8#include <tinyopt/optimizers/optimizer.h>
9#include <tinyopt/optimizers/options.h>
11#include <tinyopt/optimizers/optimizers.h>
13#include "tinyopt/log.h"
19template <
typename T,
typename Func>
20inline Summary Optimize(T &x,
const Func &func,
const Options &options = {}) {
22 using Scalar = std::conditional_t<
23 std::is_scalar_v<typename traits::params_trait<T>::Scalar>,
24 typename traits::params_trait<T>::Scalar,
26 static_assert(std::is_scalar_v<Scalar>);
27 constexpr Index Dims = traits::params_trait<T>::Dims;
30 constexpr bool isDense =
31 std::is_invocable_v<Func, const T &> ||
32 std::is_invocable_v<Func, const T &, Vector<Scalar, Dims> &> ||
33 std::is_invocable_v<Func, const T &, Vector<Scalar, Dims> &, Matrix<Scalar, Dims, Dims> &>;
35 using Hessian_t = std::conditional_t<isDense, Matrix<Scalar, Dims, Dims>, SparseMatrix<Scalar>>;
36#if defined(TINYOPT_ENABLE_GRADIENT_DESCENT) || defined(TINYOPT_ENABLE_CONJUGATE_GRADIENT) || \
37 defined(TINYOPT_ENABLE_BFGS) || defined(TINYOPT_ENABLE_LBFGS)
38 using Gradient_t = std::conditional_t<isDense, Vector<Scalar, Dims>, SparseMatrix<Scalar>>;
41 constexpr bool secondOrderValid = !std::is_invocable_v<Func, const T &, Vector<Scalar, Dims> &>;
44#if defined(TINYOPT_ENABLE_GRADIENT_DESCENT) || defined(TINYOPT_ENABLE_CONJUGATE_GRADIENT) || \
45 defined(TINYOPT_ENABLE_BFGS) || defined(TINYOPT_ENABLE_LBFGS)
46 constexpr bool firstOrderAllowed = !secondOrderValid;
49 switch (options.solver_type) {
51#if defined(TINYOPT_ENABLE_GAUSS_NEWTON)
52 case Options::Solver::GaussNewton:
53 if constexpr (secondOrderValid) {
55 return optimizer.Optimize(x, func);
57 throw std::invalid_argument(
58 "Error: GaussNewton can't be used on this gradient only function");
61#if defined(TINYOPT_ENABLE_DOGLEG)
62 case Options::Solver::DogLeg:
63 if constexpr (secondOrderValid) {
64 dl::Optimizer<Hessian_t> optimizer(options);
65 return optimizer.Optimize(x, func);
67 throw std::invalid_argument(
"Error: DogLeg can't be used on this gradient only function");
70 case Options::Solver::LevenbergMarquardt:
71 if constexpr (secondOrderValid) {
73 return optimizer.Optimize(x, func);
75 throw std::invalid_argument(
76 "Error: LevenbergMarquardt can't be used on this gradient only function");
79#if defined(TINYOPT_ENABLE_GRADIENT_DESCENT)
80 case Options::Solver::GradientDescent:
81 if constexpr (std::is_invocable_v<Func, const T &>) {
82 using ReturnType = std::invoke_result_t<Func, T>;
83 if constexpr (traits::is_scalar_v<ReturnType>) {
85 return optimizer.Optimize(x, func);
87 throw std::invalid_argument(
88 "Error: cost function must return a scalar for Gradient Descent");
90 }
else if constexpr (firstOrderAllowed) {
92 return optimizer.Optimize(x, func);
95#if defined(TINYOPT_ENABLE_CONJUGATE_GRADIENT)
96 case Options::Solver::ConjugateGradient:
97 if constexpr (std::is_invocable_v<Func, const T &>) {
98 using ReturnType = std::invoke_result_t<Func, T>;
99 if constexpr (traits::is_scalar_v<ReturnType>) {
100 cg::Optimizer<Gradient_t> optimizer(options);
101 return optimizer.Optimize(x, func);
103 throw std::invalid_argument(
104 "Error: cost function must return a scalar for Conjugate Gradient");
106 }
else if constexpr (firstOrderAllowed) {
107 cg::Optimizer<Gradient_t> optimizer(options);
108 return optimizer.Optimize(x, func);
111#if defined(TINYOPT_ENABLE_BFGS)
112 case Options::Solver::BFGS:
113 if constexpr (std::is_invocable_v<Func, const T &>) {
114 using ReturnType = std::invoke_result_t<Func, T>;
115 if constexpr (traits::is_scalar_v<ReturnType>) {
116 bfgs::Optimizer<Gradient_t> optimizer(options);
117 return optimizer.Optimize(x, func);
119 throw std::invalid_argument(
"Error: cost function must return a scalar for BFGS");
121 }
else if constexpr (firstOrderAllowed) {
122 bfgs::Optimizer<Gradient_t> optimizer(options);
123 return optimizer.Optimize(x, func);
126#if defined(TINYOPT_ENABLE_LBFGS)
127 case Options::Solver::LBFGS:
128 if constexpr (std::is_invocable_v<Func, const T &>) {
129 using ReturnType = std::invoke_result_t<Func, T>;
130 if constexpr (traits::is_scalar_v<ReturnType>) {
131 lbfgs::Optimizer<Gradient_t> optimizer(options);
132 return optimizer.Optimize(x, func);
134 throw std::invalid_argument(
"Error: cost function must return a scalar for L-BFGS");
136 }
else if constexpr (firstOrderAllowed) {
137 lbfgs::Optimizer<Gradient_t> optimizer(options);
138 return optimizer.Optimize(x, func);
142 TINYOPT_LOG(
"❌ Error: Unknown solver type {}", (
int)options.solver_type);
143 throw std::invalid_argument(
"Error: Unknown solver type");
147template <
typename T,
typename U,
typename... Rest,
typename Func>
148 requires(!std::is_same_v<std::remove_cvref_t<Func>,
Options>)
149inline Summary Optimize(T &x, U &y, Rest &...rest,
const Func &func,
const Options &options = {}) {
151 auto wrapped = detail::make_packed_adapter<Func, T, U, Rest...>(func);
152 return Optimize(pack, wrapped, options);
Optimizer_< solvers::SolverGD< Gradient_t > > Optimizer
Gradient Descent Optimizer.
Definition gd.h:24
Optimizer_< solvers::SolverGN< Hessian_t > > Optimizer
Gauss-Newton Optimizer.
Definition gn.h:24
Optimizer_< solvers::SolverLM< Hessian_t > > Optimizer
Levenberg-Marquardt Optimizer.
Definition lm.h:25
Definition params_pack.h:12
Definition parameter_traits.h:15