Optimist  0.0.0
A C++ library for optimization
Loading...
Searching...
No Matches
SolverBase.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The Optimist project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * davide.stocco@unitn.it enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef OPTIMIST_SOLVER_HH
14#define OPTIMIST_SOLVER_HH
15
16#include "Optimist.hh"
17#include "Optimist/Function.hh"
18
19namespace Optimist {
20
21 /*\
22 | ____ _ ____
23 | / ___| ___ | |_ _____ _ __| __ ) __ _ ___ ___
24 | \___ \ / _ \| \ \ / / _ \ '__| _ \ / _` / __|/ _ \
25 | ___) | (_) | |\ V / __/ | | |_) | (_| \__ \ __/
26 | |____/ \___/|_| \_/ \___|_| |____/ \__,_|___/\___|
27 |
28 \*/
29
40 template <typename Input, typename Output, typename DerivedSolver>
41 requires std::is_same<typename TypeTrait<Input>::Scalar,
42 typename TypeTrait<Output>::Scalar>::value &&
43 (TypeTrait<Input>::IsScalar || TypeTrait<Input>::IsEigen) &&
44 (TypeTrait<Output>::IsScalar || TypeTrait<Output>::IsEigen) &&
45 (!TypeTrait<Input>::IsFixed || TypeTrait<Input>::Dimension > 0) &&
46 (!TypeTrait<Output>::IsFixed ||
47 TypeTrait<Output>::Dimension > 0) &&
48 (!(TypeTrait<Input>::IsEigen && TypeTrait<Output>::IsEigen) ||
49 (TypeTrait<Input>::IsFixed && TypeTrait<Output>::IsFixed) ||
50 (TypeTrait<Input>::IsDynamic && TypeTrait<Output>::IsDynamic) ||
51 (TypeTrait<Input>::IsSparse && TypeTrait<Output>::IsSparse))
52 class SolverBase {
53 public:
54 // Input and output types
57 using Scalar = typename InputTrait::Scalar;
58
59 // Input and output must be non-zero dimensional
60 static_assert(InputTrait::Dimension != 0,
61 "Input must be non-zero dimensional");
62 static_assert(OutputTrait::Dimension != 0,
63 "Output must be non-zero dimensional");
64
65 // Input and output must have the same scalar type
66 static_assert(std::is_same<typename InputTrait::Scalar,
67 typename OutputTrait::Scalar>::value,
68 "Input and output scalar types must be the same.");
69
70 // If both input and output are eigen types they be both fixed-size,
71 // dynamic-size, or sparse
72 static_assert(!(InputTrait::IsEigen && OutputTrait::IsEigen) ||
73 (InputTrait::IsFixed && OutputTrait::IsFixed) ||
74 (InputTrait::IsDynamic && OutputTrait::IsDynamic) ||
75 (InputTrait::IsSparse && OutputTrait::IsSparse),
76 "Input and output Eigen types must be both fixed-size, "
77 "dynamic-size, or sparse.");
78
79 // Derivative types
80 using FirstDerivative = std::conditional_t<
81 InputTrait::IsEigen || OutputTrait::IsEigen,
82 std::conditional_t<InputTrait::IsSparse || OutputTrait::IsSparse,
83 Eigen::SparseMatrix<Scalar>,
84 Eigen::Matrix<Scalar,
85 OutputTrait::Dimension,
86 InputTrait::Dimension>>,
87 Scalar>;
88 using SecondDerivative = std::conditional_t<
89 InputTrait::IsEigen || OutputTrait::IsEigen,
90 std::conditional_t<InputTrait::IsSparse || OutputTrait::IsSparse,
91 std::vector<Eigen::SparseMatrix<Scalar>>,
92 std::vector<Eigen::Matrix<Scalar,
93 OutputTrait::Dimension,
94 InputTrait::Dimension>>>,
95 Scalar>;
96
98
99 protected:
100 // Bounds (may not be used)
103
104 // Evaluations
110
111 // Maximum allowed evaluations
118
119 // Iterations and relaxations
126
127 // Settings
129 SQRT_EPSILON};
130 bool m_verbose{false};
131 bool m_damped{true};
132 std::ostream *m_ostream{&std::cout};
133
134 // Convergence output flag and trace
135 std::string m_task{"Undefined"};
136 bool m_converged{false};
137
138 public:
143
151 template <typename FunctionLambda>
152 SolverBase(FunctionLambda &&function, const Input &x_ini, Input &x_sol)
153 : SolverBase() {
154 static_cast<const DerivedSolver *>(this)->solve_impl(
155 std::forward<FunctionLambda>(function),
156 x_ini,
157 x_sol);
158 }
159
169 template <typename FunctionLambda, typename FirstDerivativeLambda>
170 SolverBase(FunctionLambda &&function,
171 FirstDerivativeLambda &&first_derivative,
172 const Input &x_ini,
173 Input &x_sol)
174 : SolverBase() {
175 static_cast<const DerivedSolver *>(this)->solve_impl(
176 std::forward<FunctionLambda>(function),
177 std::forward<FirstDerivativeLambda>(first_derivative),
178 x_ini,
179 x_sol);
180 }
181
193 template <typename FunctionLambda,
194 typename FirstDerivativeLambda,
195 typename SecondDerivativeLambda>
196 SolverBase(FunctionLambda &&function,
197 FirstDerivativeLambda &&first_derivative,
198 SecondDerivativeLambda &&second_derivative,
199 const Input &x_ini,
200 Input &x_sol)
201 : SolverBase() {
202 static_cast<const DerivedSolver *>(this)->solve_impl(
203 std::forward<FunctionLambda>(function),
204 std::forward<FirstDerivativeLambda>(first_derivative),
205 std::forward<SecondDerivativeLambda>(second_derivative),
206 x_ini,
207 x_sol);
208 }
209
214 void reset_bounds(const Integer n = InputTrait::IsDynamic
215 ? 0
216 : InputTrait::Dimension) {
217#define CMD "Optimist::Solver::reset_bounds(...): "
218
219 if constexpr (InputTrait::IsScalar) {
220 this->m_lower_bound = -INFTY;
221 this->m_upper_bound = +INFTY;
222 } else if constexpr (InputTrait::IsFixed) {
223 this->m_lower_bound.setConstant(-INFTY);
224 this->m_upper_bound.setConstant(+INFTY);
225 } else if constexpr (InputTrait::IsDynamic) {
226 this->m_lower_bound.resize(n);
227 this->m_lower_bound.setConstant(-INFTY);
228 this->m_upper_bound.resize(n);
229 this->m_upper_bound.setConstant(+INFTY);
230 } else if constexpr (InputTrait::IsSparse) {
231 this->m_lower_bound.resize(n);
232 this->m_lower_bound.reserve(n);
233 this->m_upper_bound.resize(n);
234 this->m_upper_bound.reserve(n);
235 std::vector<Eigen::Triplet<Scalar>> triplets;
236 triplets.reserve(n);
237 for (Integer i{0}; i < n; ++i) {
238 triplets.emplace_back(i, 0, -INFTY);
239 }
240 this->m_lower_bound.setFromTriplets(triplets.begin(), triplets.end());
241 triplets.clear();
242 triplets.reserve(n);
243 for (Integer i{0}; i < n; ++i) {
244 triplets.emplace_back(i, 0, +INFTY);
245 }
246 this->m_upper_bound.setFromTriplets(triplets.begin(), triplets.end());
247 } else {
248 OPTIMIST_ERROR(CMD "unsupported input type for bounds reset.");
249 }
250
251#undef CMD
252 }
253
258 const Input &lower_bound() const {
259 return this->m_lower_bound;
260 }
261
266 void lower_bound(const Input &t_lower_bound) {
267#define CMD "Optimist::Solver::bounds(...): "
268
269 if constexpr (InputTrait::IsEigen) {
270 OPTIMIST_ASSERT((this->m_upper_bound - t_lower_bound).minCoeff() <= 0.0,
271 CMD "invalid or degenarate bounds detected.");
272 } else {
273 OPTIMIST_ASSERT(this->m_upper_bound > t_lower_bound,
274 CMD "invalid or degenarate bounds detected.");
275 }
276 this->m_lower_bound = t_lower_bound;
277
278#undef CMD
279 }
280
285 const Input &upper_bound() const {
286 return this->m_upper_bound;
287 }
288
293 void upper_bound(const Input &t_upper_bound) {
294#define CMD "Optimist::Solver::bounds(...): "
295
296 if constexpr (InputTrait::IsEigen) {
297 OPTIMIST_ASSERT((t_upper_bound - this->m_lower_bound).minCoeff() <= 0.0,
298 CMD "invalid or degenarate bounds detected.");
299 } else {
300 OPTIMIST_ASSERT(t_upper_bound > this->m_lower_bound,
301 CMD "invalid or degenarate bounds detected.");
302 }
303 this->m_upper_bound = t_upper_bound;
304
305#undef CMD
306 }
307
313 void bounds(const Input &t_lower_bound, const Input &t_upper_bound) {
314#define CMD "Optimist::Solver::bounds(...): "
315
316 if constexpr (InputTrait::IsEigen) {
317 OPTIMIST_ASSERT((t_upper_bound - t_lower_bound).minCoeff() <= 0.0,
318 CMD "invalid or degenarate bounds detected.");
319 } else {
320 OPTIMIST_ASSERT(t_upper_bound > t_lower_bound,
321 CMD "invalid or degenarate bounds detected.");
322 }
323 this->m_lower_bound = t_lower_bound;
324 this->m_upper_bound = t_upper_bound;
325
326#undef CMD
327 }
328
333 constexpr Integer input_dimension() const {
334 return InputTrait::Dimension;
335 }
336
341 constexpr Integer output_dimension() const {
342 return OutputTrait::Dimension;
343 }
344
350 return this->m_function_evaluations;
351 }
352
358 void max_function_evaluations(const Integer t_max_function_evaluations) {
360 t_max_function_evaluations > 0,
361 "Optimist::Solver::max_function_evaluations(...): invalid "
362 "input detected.");
363 this->m_max_function_evaluations = t_max_function_evaluations;
364 }
365
371 return this->m_max_function_evaluations;
372 }
373
374 protected:
380 return this->m_first_derivative_evaluations;
381 }
382
388 return this->m_max_first_derivative_evaluations;
389 }
390
397 const Integer t_first_derivative_evaluations) {
399 t_first_derivative_evaluations > 0,
400 "Optimist::Solver::max_first_derivative_evaluations(...): "
401 "invalid input detected.");
402 this->m_max_first_derivative_evaluations = t_first_derivative_evaluations;
403 }
404
410 return this->m_second_derivative_evaluations;
411 }
412
418 return this->m_max_second_derivative_evaluations;
419 }
420
427 const Integer t_second_derivative_evaluations) {
429 t_second_derivative_evaluations > 0,
430 "Optimist::Solver::max_second_derivative_evaluations(...): "
431 "invalid input detected.");
432 this->m_max_second_derivative_evaluations =
433 t_second_derivative_evaluations;
434 }
435
436 public:
441 return this->m_iterations;
442 }
443
449 return this->max_iterations;
450 }
451
456 void max_iterations(const Integer t_max_iterations) {
458 t_max_iterations > 0,
459 "Optimist::Solver::max_iterations(...): invalid input detected.");
460 this->m_max_iterations = t_max_iterations;
461 }
462
467 Scalar alpha() const {
468 return this->m_alpha;
469 }
470
475 void alpha(Scalar t_alpha) {
476 OPTIMIST_ASSERT(!std::isnan(t_alpha) && std::isfinite(t_alpha) &&
477 0.0 <= t_alpha && t_alpha <= 1.0,
478 "Optimist::Solver::alpha(...): invalid input detected.");
479 this->m_alpha = t_alpha;
480 }
481
487 return this->m_relaxations;
488 }
489
495 return this->max_relaxations;
496 }
497
502 void max_relaxations(const Integer t_max_relaxations) {
504 t_max_relaxations > 0,
505 "Optimist::Solver::max_relaxations(...): invalid input detected.");
506 this->m_max_relaxations = t_max_relaxations;
507 }
508
514 return this->m_tolerance;
515 }
516
523 void tolerance(Scalar t_tolerance) {
525 !std::isnan(t_tolerance) && std::isfinite(t_tolerance) &&
526 t_tolerance > 0,
527 "Optimist::Solver::tolerance(...): invalid input detected.");
528 this->m_tolerance = t_tolerance;
529 }
530
535 void verbose_mode(bool t_verbose) {
536 this->m_verbose = t_verbose;
537 }
538
543 bool verbose_mode() const {
544 return this->m_verbose;
545 }
546
551 this->m_verbose = true;
552 }
553
558 this->m_verbose = false;
559 }
560
565 void damped_mode(bool t_damped) {
566 this->m_damped = t_damped;
567 }
568
573 bool damped_mode() const {
574 return this->m_damped;
575 }
576
581 this->m_damped = true;
582 }
583
588 this->m_damped = false;
589 }
590
595 std::string task() const {
596 return this->m_task;
597 }
598
603 void task(std::string t_task) {
604 this->m_task = t_task;
605 }
606
611 bool converged() const {
612 return this->m_converged;
613 }
614
619 std::ostream &ostream() const {
620 return *this->m_ostream;
621 }
622
627 void ostream(std::ostream &t_ostream) {
628 this->m_ostream = &t_ostream;
629 }
630
640 template <typename FunctionLambda>
641 bool solve(FunctionLambda &&function, const Input &x_ini, Input &x_sol) {
642#define CMD "Optimist::Solver::solve(...): "
643
644 static_assert(DerivedSolver::RequiresFunction,
645 CMD "the solver requires a function.");
646 return static_cast<DerivedSolver *>(this)->solve_impl(
647 std::forward<FunctionLambda>(function),
648 nullptr,
649 nullptr,
650 x_ini,
651 x_sol);
652
653#undef CMD
654 }
655
667 template <typename FunctionLambda, typename FirstDerivativeLambda>
668 bool solve(FunctionLambda &&function,
669 FirstDerivativeLambda &&first_derivative,
670 const Input &x_ini,
671 Input &x_sol) {
672#define CMD "Optimist::Solver::solve(...): "
673
674 static_assert(DerivedSolver::RequiresFunction,
675 CMD "the solver requires a function.");
676 static_assert(DerivedSolver::RequiresFirstDerivative,
677 CMD "the solver requires the first derivative.");
678 return static_cast<DerivedSolver *>(this)->solve_impl(
679 std::forward<FunctionLambda>(function),
680 std::forward<FirstDerivativeLambda>(first_derivative),
681 nullptr,
682 x_ini,
683 x_sol);
684
685#undef CMD
686 }
687
701 template <typename FunctionLambda,
702 typename FirstDerivativeLambda,
703 typename SecondDerivativeLambda>
704 bool solve(FunctionLambda &&function,
705 FirstDerivativeLambda &&first_derivative,
706 SecondDerivativeLambda &&second_derivative,
707 const Input &x_ini,
708 Input &x_sol) {
709#define CMD "Optimist::Solver::solve(...): "
710
711 static_assert(DerivedSolver::RequiresFunction,
712 CMD "the solver requires the function.");
713 static_assert(DerivedSolver::RequiresFirstDerivative,
714 CMD "the solver requires the first derivative.");
715 static_assert(DerivedSolver::RequiresSecondDerivative,
716 CMD "the solver requires the second derivative.");
717 return static_cast<DerivedSolver *>(this)->solve_impl(
718 std::forward<FunctionLambda>(function),
719 std::forward<FirstDerivativeLambda>(first_derivative),
720 std::forward<SecondDerivativeLambda>(second_derivative),
721 x_ini,
722 x_sol);
723
724#undef CMD
725 }
726
736 template <typename FunctionInput,
737 typename FunctionOutput,
738 typename DerivedFunction>
739 // TODO: make requires clauses to avoid invalid combinations
742 &function,
743 const Input &x_ini,
744 Input &x_sol) {
745#define CMD "Optimist::Solver::rootfind(...): "
746
747 using FunctionInputTrait = TypeTrait<FunctionInput>;
748 using FunctionOutputTrait = TypeTrait<FunctionOutput>;
749
750 static_assert(InputTrait::Dimension == FunctionInputTrait::Dimension,
751 CMD
752 "solver input dimension must be equal to the function "
753 "input dimension.");
754 static_assert(OutputTrait::Dimension == FunctionOutputTrait::Dimension ||
755 OutputTrait::Dimension == 1,
756 CMD
757 "solver output dimension must be equal to the function "
758 "output dimension or 1.");
759 static_assert(
760 !(InputTrait::IsScalar && DerivedSolver::IsOptimizer),
761 CMD
762 "one-dimensional optimizers do not support root-finding problems.");
763 return this->solve(
764 function,
765 x_ini,
766 x_sol,
767 (OutputTrait::Dimension != FunctionOutputTrait::Dimension) ||
768 (InputTrait::IsEigen && FunctionOutputTrait::IsScalar));
769#undef CMD
770 }
771
781 template <typename FunctionInput,
782 typename FunctionOutput,
783 typename DerivedFunction>
786 &function,
787 const Input &x_ini,
788 Input &x_sol) {
789#define CMD "Optimist::Solver::optimize(...): "
790
791 using FunctionInputTrait = TypeTrait<FunctionInput>;
792
793 static_assert(InputTrait::Dimension == FunctionInputTrait::Dimension,
794 CMD
795 "solver input dimension must be equal to the function "
796 "input dimension.");
797 static_assert(OutputTrait::Dimension == 1,
798 CMD
799 "solver output dimension must be equal to the function "
800 "output dimension or 1.");
801 // static_assert(!(InputTrait::Dimension == 1 &&
802 // DerivedSolver::IsRootFinder),
803 // CMD "one-dimensional root-finders do not support optimization
804 // problems.");
805 return this->solve(function, x_ini, x_sol, true);
806
807#undef CMD
808 }
809
814 constexpr std::string name() const {
815 return static_cast<const DerivedSolver *>(this)->name_impl();
816 };
817
818 protected:
829 template <typename FunctionInput,
830 typename FunctionOutput,
831 typename DerivedFunction>
832 // TODO: make requires clauses to avoid invalid combinations
833 bool solve(
835 &function,
836 const Input &x_ini,
837 Input &x_sol,
838 bool is_optimization) {
839#define CMD "Optimist::Solver::solve(...): "
840
841 using FunctionType =
843 using FunctionInputTrait = TypeTrait<FunctionInput>;
844 using FunctionOutputTrait = TypeTrait<FunctionOutput>;
845
846 static_assert(InputTrait::Dimension == FunctionInputTrait::Dimension,
847 CMD
848 "solver input dimension must be equal to the function "
849 "input dimension.");
850 static_assert(OutputTrait::Dimension == FunctionOutputTrait::Dimension ||
851 OutputTrait::Dimension == 1,
852 CMD
853 "solver output dimension must be equal to the function "
854 "output dimension or a scalar.");
855
856 // Lambda generators for function and derivatives
857 auto function_lambda = [&function, is_optimization](const Input &x,
858 Output &out) -> bool {
859 bool success{false};
860 FunctionOutput f;
861 success = function.evaluate(x, f);
863 success,
864 CMD "function evaluation failed during function computation.");
865
866 if (is_optimization) {
867 if constexpr (FunctionOutputTrait::Dimension == 1) {
868 out = 0.5 * f * f;
869 } else if constexpr (OutputTrait::Dimension !=
870 FunctionOutputTrait::Dimension) {
871 out = 0.5 * f.squaredNorm();
872 } else {
874 CMD
875 "optimization problem with inconsistent output in function.");
876 }
877 } else {
878 if constexpr (OutputTrait::Dimension ==
879 FunctionOutputTrait::Dimension) {
880 out = f;
881 } else {
883 CMD
884 "root-finding problem with inconsistent output in function.");
885 }
886 }
887 return success;
888 };
889
890 auto first_derivative_lambda = [&function, is_optimization, this](
891 const Input &x,
892 FirstDerivative &out) -> bool {
893 bool success{false};
894 typename FunctionType::FirstDerivative J;
895 success = function.first_derivative(x, J);
896 OPTIMIST_ASSERT(success,
897 CMD
898 "first derivative evaluation failed during "
899 "first derivative computation.");
900
901 if (is_optimization) {
902 bool success{false};
903 FunctionOutput f;
904 success = function.evaluate(x, f);
905 OPTIMIST_ASSERT(success,
906 CMD
907 "function evaluation failed during first derivative "
908 "computation.");
909 this->m_function_evaluations++;
910 if constexpr (FunctionInputTrait::IsScalar &&
911 FunctionOutputTrait::IsScalar) {
912 out = J * f;
913 } else if constexpr (OutputTrait::Dimension !=
914 FunctionOutputTrait::Dimension) {
915 out = J.transpose() * f;
916 } else {
918 "optimization problem inconsistent output in first "
919 "derivative.");
920 }
921 } else {
922 if constexpr (OutputTrait::Dimension ==
923 FunctionOutputTrait::Dimension) {
924 out = J;
925 } else {
927 "root-finding problem with inconsistent output in "
928 "first derivative.");
929 }
930 }
931 return success;
932 };
933
934 auto second_derivative_lambda = [&function, is_optimization, this](
935 const Input &x,
936 SecondDerivative &out) -> bool {
937 bool success{false};
938 typename FunctionType::SecondDerivative H;
939 success = function.second_derivative(x, H);
941 success,
942 CMD
943 "function evaluation failed during second derivative computation.");
944
945 if (is_optimization) {
946 FunctionOutput f;
947 success = function.evaluate(x, f);
948 this->m_function_evaluations++;
949 OPTIMIST_ASSERT(success,
950 CMD
951 "function evaluation failed during second derivative "
952 "computation.");
953 typename FunctionType::FirstDerivative J;
954 success = function.first_derivative(x, J);
955 this->m_first_derivative_evaluations++;
956 OPTIMIST_ASSERT(success,
957 CMD
958 "first derivative evaluation failed "
959 "during second derivative computation.");
960 if constexpr (FunctionInputTrait::IsScalar &&
961 FunctionOutputTrait::IsScalar) {
962 out = J * J + f * H;
963 } else if constexpr (OutputTrait::Dimension !=
964 FunctionOutputTrait::Dimension) {
965 out = J.transpose() * J;
966 for (Integer i{0}; i < static_cast<Integer>(H.size()); ++i) {
967 out += f(i) * H[i];
968 }
969 } else {
971 "optimization problem with inconsistent output in "
972 "second derivative.");
973 }
974 } else {
975 if constexpr (OutputTrait::Dimension ==
976 FunctionOutputTrait::Dimension) {
977 out = H;
978 } else {
980 "root-finding problem with inconsistent output in "
981 "second derivative.");
982 }
983 }
984 return success;
985 };
986
987 // Select solver method based on derivative requirements
988 if constexpr (DerivedSolver::RequiresFunction &&
989 !DerivedSolver::RequiresFirstDerivative &&
990 !DerivedSolver::RequiresSecondDerivative) {
991 return static_cast<DerivedSolver *>(this)->solve_impl(function_lambda,
992 x_ini,
993 x_sol);
994 } else if constexpr (DerivedSolver::RequiresFunction &&
995 DerivedSolver::RequiresFirstDerivative &&
996 !DerivedSolver::RequiresSecondDerivative) {
997 return static_cast<DerivedSolver *>(this)->solve_impl(
998 function_lambda,
999 first_derivative_lambda,
1000 x_ini,
1001 x_sol);
1002 } else if constexpr (DerivedSolver::RequiresFunction &&
1003 DerivedSolver::RequiresFirstDerivative &&
1004 DerivedSolver::RequiresSecondDerivative) {
1005 return static_cast<DerivedSolver *>(this)->solve_impl(
1006 function_lambda,
1007 first_derivative_lambda,
1008 second_derivative_lambda,
1009 x_ini,
1010 x_sol);
1011 } else {
1013 "no matching function signature found for the solver.");
1014 }
1015
1016#undef CMD
1017 }
1018
1023 this->m_function_evaluations = 0;
1024 this->m_first_derivative_evaluations = 0;
1025 this->m_second_derivative_evaluations = 0;
1026 this->m_iterations = 0;
1027 this->m_relaxations = 0;
1028 this->m_converged = false;
1029 }
1030
1039 template <typename FunctionLambda>
1040 bool evaluate_function(FunctionLambda &&function,
1041 const Input &x,
1042 Output &out) {
1044 this->m_function_evaluations < this->m_max_function_evaluations,
1045 "Optimist::" << this->name()
1046 << "::evaluate_function(...): maximum allowed "
1047 "function evaluations reached.");
1048 ++this->m_function_evaluations;
1049 return function(x, out);
1050 }
1051
1060 template <typename FirstDerivativeLambda>
1061 bool evaluate_first_derivative(FirstDerivativeLambda &&function,
1062 const Input &x,
1063 FirstDerivative &out) {
1065 this->m_first_derivative_evaluations <
1066 this->m_max_first_derivative_evaluations,
1067 "Optimist::" << this->name()
1068 << "::evaluate_first_derivative(...): maximum allowed "
1069 "first derivative evaluations reached.");
1070 ++this->m_first_derivative_evaluations;
1071 return function(x, out);
1072 }
1073
1081 template <typename SecondDerivativeLambda>
1082 bool evaluate_second_derivative(SecondDerivativeLambda &&function,
1083 const Input &x,
1084 SecondDerivative &out) {
1086 this->m_second_derivative_evaluations <
1087 this->m_max_second_derivative_evaluations,
1088 "Optimist::" << this->name()
1089 << "::evaluate_second_derivative(...): maximum allowed "
1090 "second derivative evaluations reached.");
1091 ++this->m_second_derivative_evaluations;
1092 return function(x, out);
1093 }
1094
1108 template <typename FunctionLambda>
1109 bool damp(FunctionLambda &&function,
1110 const Input &x_old,
1111 const Input &function_old,
1112 const Input &step_old,
1113 Input &x_new,
1114 Input &function_new,
1115 Input &step_new) {
1116#define CMD "Optimist::Solver::damp(...): "
1117
1118 Scalar step_norm_old, step_norm_new, residuals_old, residuals_new,
1119 tau{1.0};
1120 for (this->m_relaxations = 0;
1121 this->m_relaxations < this->m_max_relaxations;
1122 ++this->m_relaxations) {
1123 // Update point
1124 step_new = tau * step_old;
1125 x_new = x_old + step_new;
1126 bool success{
1127 this->evaluate_function(std::forward<FunctionLambda>(function),
1128 x_new,
1129 function_new)};
1130 OPTIMIST_ASSERT(success,
1131 CMD "function evaluation failed during damping.");
1132
1133 // Compute step norms
1134 if constexpr (InputTrait::IsEigen) {
1135 step_norm_old = step_old.norm();
1136 step_norm_new = step_new.norm();
1137 } else {
1138 step_norm_old = std::abs(step_old);
1139 step_norm_new = std::abs(step_new);
1140 }
1141
1142 // Compute residual norms
1143 if constexpr (OutputTrait::IsEigen) {
1144 residuals_old = function_old.norm();
1145 residuals_new = function_new.norm();
1146 } else {
1147 residuals_old = std::abs(function_old);
1148 residuals_new = std::abs(function_new);
1149 }
1150
1151 // Check relaxation
1152 if (residuals_new < residuals_old ||
1153 step_norm_new < (Scalar(1.0) - tau / Scalar(2.0)) * step_norm_old) {
1154 return true;
1155 } else {
1156 tau *= this->m_alpha;
1157 }
1158 }
1159 return false;
1160
1161#undef CMD
1162 }
1163
1168 void header() {
1169 static constexpr std::string c_tl{table_top_left_corner()};
1170 static constexpr std::string c_tr{table_top_right_corner()};
1171 static constexpr std::string h_7{table_horizontal_line<7>()};
1172 static std::string h_14{table_horizontal_line<14>()};
1173 static std::string h_23{table_horizontal_line<23>()};
1174 static std::string h_78{table_horizontal_line<78>()};
1175 static constexpr std::string v_ll{table_vertical_line() + " "};
1176 static constexpr std::string v_rr{" " + table_vertical_line()};
1177 static constexpr std::string v_lc{" " + table_vertical_line() + " "};
1178 static constexpr std::string j_tt{table_top_junction()};
1179 static constexpr std::string j_cc{table_center_cross()};
1180 static constexpr std::string j_ll{table_left_junction()};
1181 static constexpr std::string j_rr{table_right_junction()};
1182
1183 *this->m_ostream << c_tl << h_78 << c_tr << std::endl
1184 << v_ll << "Solver:" << std::setw(69) << this->name()
1185 << v_rr << std::endl
1186 << v_ll << "Task: " << std::setw(69) << this->task()
1187 << v_rr << std::endl
1188 << j_ll << h_7 << j_tt << h_7 << j_tt << h_7 << j_tt
1189 << h_7 << j_tt << h_7 << j_tt << h_14 << j_tt << h_23
1190 << j_rr << std::endl
1191 << v_ll << "#Iter" << v_lc << " #f" << v_lc << " #Df"
1192 << v_lc << " #DDf" << v_lc << " #Rlx" << v_lc
1193 << " ║f(x)║₂ " << v_lc << "Additional notes"
1194 << std::setw(9) << v_rr << std::endl
1195 << j_ll << h_7 << j_cc << h_7 << j_cc << h_7 << j_cc
1196 << h_7 << j_cc << h_7 << j_cc << h_14 << j_cc << h_23
1197 << j_rr << std::endl;
1198 }
1199
1204 void bottom() {
1205 static constexpr std::string c_bl{table_bottom_left_corner()};
1206 static constexpr std::string c_br{table_bottom_right_corner()};
1207 static std::string h_7{table_horizontal_line<7>()};
1208 static std::string h_14{table_horizontal_line<14>()};
1209 static std::string h_23{table_horizontal_line<23>()};
1210 static std::string h_78{table_horizontal_line<78>()};
1211 static constexpr std::string v_ll{table_vertical_line() + " "};
1212 static constexpr std::string v_rr{" " + table_vertical_line()};
1213 static constexpr std::string j_ll{table_left_junction()};
1214 static constexpr std::string j_rr{table_right_junction()};
1215 static constexpr std::string j_bb{table_bottom_junction()};
1216
1217 *this->m_ostream << j_ll << h_7 << j_bb << h_7 << j_bb << h_7 << j_bb
1218 << h_7 << j_bb << h_7 << j_bb << h_14 << j_bb << h_23
1219 << j_rr << std::endl
1220 << v_ll << std::setw(40)
1221 << (this->m_converged ? "CONVERGED" : "NOT CONVERGED")
1222 << std::setw(40) << v_rr << std::endl
1223 << c_bl << h_78 << c_br << std::endl;
1224 }
1225
1230 void info(Scalar residuals, const std::string &notes = "-") {
1231 static constexpr std::string v_rr{" " + table_vertical_line()};
1232 static constexpr std::string v_ll{table_vertical_line() + " "};
1233 static constexpr std::string v_lc{" " + table_vertical_line() + " "};
1234
1235 *this->m_ostream << v_ll << std::setw(5) << this->m_iterations << v_lc
1236 << std::setw(5) << this->m_function_evaluations << v_lc
1237 << std::setw(5) << this->m_first_derivative_evaluations
1238 << v_lc << std::setw(5)
1239 << this->m_second_derivative_evaluations << v_lc
1240 << std::setw(5) << this->m_relaxations << v_lc
1241 << std::setw(12) << std::scientific
1242 << std::setprecision(6) << residuals << v_lc << notes
1243 << std::setw(25 - notes.length()) << v_rr << std::endl;
1244 }
1245
1246 }; // class Solver
1247
1248} // namespace Optimist
1249
1250#endif // OPTIMIST_SOLVER_HH
#define OPTIMIST_ERROR(MSG)
Definition Optimist.hh:37
#define OPTIMIST_BASIC_CONSTANTS(Scalar)
Definition Optimist.hh:69
#define OPTIMIST_ASSERT(COND, MSG)
Definition Optimist.hh:47
#define CMD
Class container for the generic function.
Definition Function.hh:48
bool second_derivative(const Input &x, SecondDerivative &out) const
Definition Function.hh:123
bool evaluate(const Input &x, Output &out) const
Definition Function.hh:101
bool first_derivative(const Input &x, FirstDerivative &out) const
Definition Function.hh:111
void max_second_derivative_evaluations(const Integer t_second_derivative_evaluations)
Definition SolverBase.hh:426
Integer first_derivative_evaluations() const
Definition SolverBase.hh:379
void max_iterations(const Integer t_max_iterations)
Definition SolverBase.hh:456
void disable_damped_mode()
Definition SolverBase.hh:587
bool damp(FunctionLambda &&function, const Input &x_old, const Input &function_old, const Input &step_old, Input &x_new, Input &function_new, Input &step_new)
Definition SolverBase.hh:1109
void damped_mode(bool t_damped)
Definition SolverBase.hh:565
void info(Scalar residuals, const std::string &notes="-")
Definition SolverBase.hh:1230
bool solve(const FunctionBase< FunctionInput, FunctionOutput, DerivedFunction > &function, const Input &x_ini, Input &x_sol, bool is_optimization)
Definition SolverBase.hh:833
Integer m_function_evaluations
Definition SolverBase.hh:105
void alpha(Scalar t_alpha)
Definition SolverBase.hh:475
void reset_bounds(const Integer n=InputTrait::IsDynamic ? 0 :InputTrait::Dimension)
Definition SolverBase.hh:214
void lower_bound(const Input &t_lower_bound)
Definition SolverBase.hh:266
void upper_bound(const Input &t_upper_bound)
Definition SolverBase.hh:293
void max_relaxations(const Integer t_max_relaxations)
Definition SolverBase.hh:502
SolverBase(FunctionLambda &&function, FirstDerivativeLambda &&first_derivative, SecondDerivativeLambda &&second_derivative, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:196
void max_function_evaluations(const Integer t_max_function_evaluations)
Definition SolverBase.hh:358
Scalar alpha() const
Definition SolverBase.hh:467
constexpr Integer input_dimension() const
Definition SolverBase.hh:333
std::conditional_t< InputTrait::IsEigen||OutputTrait::IsEigen, std::conditional_t< InputTrait::IsSparse||OutputTrait::IsSparse, Eigen::SparseMatrix< Scalar >, Eigen::Matrix< Scalar, OutputTrait::Dimension, InputTrait::Dimension > >, Scalar > FirstDerivative
Definition SolverBase.hh:80
bool evaluate_function(FunctionLambda &&function, const Input &x, Output &out)
Definition SolverBase.hh:1040
bool evaluate_first_derivative(FirstDerivativeLambda &&function, const Input &x, FirstDerivative &out)
Definition SolverBase.hh:1061
void enable_verbose_mode()
Definition SolverBase.hh:550
Integer function_evaluations() const
Definition SolverBase.hh:349
constexpr Integer output_dimension() const
Definition SolverBase.hh:341
bool m_damped
Definition SolverBase.hh:131
void ostream(std::ostream &t_ostream)
Definition SolverBase.hh:627
Integer relaxations() const
Definition SolverBase.hh:486
bool converged() const
Definition SolverBase.hh:611
void task(std::string t_task)
Definition SolverBase.hh:603
void tolerance(Scalar t_tolerance)
Definition SolverBase.hh:523
const Input & upper_bound() const
Definition SolverBase.hh:285
std::ostream * m_ostream
Definition SolverBase.hh:132
bool evaluate_second_derivative(SecondDerivativeLambda &&function, const Input &x, SecondDerivative &out)
Definition SolverBase.hh:1082
std::ostream & ostream() const
Definition SolverBase.hh:619
Integer m_max_iterations
Definition SolverBase.hh:121
Integer max_iterations() const
Definition SolverBase.hh:448
bool optimize(const FunctionBase< FunctionInput, FunctionOutput, DerivedFunction > &function, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:784
Scalar m_tolerance
Definition SolverBase.hh:128
bool solve(FunctionLambda &&function, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:641
Integer m_max_function_evaluations
Definition SolverBase.hh:112
typename InputTrait::Scalar Scalar
Definition SolverBase.hh:57
Integer max_first_derivative_evaluations() const
Definition SolverBase.hh:387
constexpr std::string name() const
Definition SolverBase.hh:814
Scalar tolerance() const
Definition SolverBase.hh:513
const Input & lower_bound() const
Definition SolverBase.hh:258
void header()
Definition SolverBase.hh:1168
Integer max_function_evaluations() const
Definition SolverBase.hh:370
Integer second_derivative_evaluations() const
Definition SolverBase.hh:409
bool rootfind(const FunctionBase< FunctionInput, FunctionOutput, DerivedFunction > &function, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:740
void disable_verbose_mode()
Definition SolverBase.hh:557
Input m_lower_bound
Definition SolverBase.hh:101
Integer max_second_derivative_evaluations() const
Definition SolverBase.hh:417
TypeTrait< Input > InputTrait
Definition SolverBase.hh:55
std::conditional_t< InputTrait::IsEigen||OutputTrait::IsEigen, std::conditional_t< InputTrait::IsSparse||OutputTrait::IsSparse, std::vector< Eigen::SparseMatrix< Scalar > >, std::vector< Eigen::Matrix< Scalar, OutputTrait::Dimension, InputTrait::Dimension > > >, Scalar > SecondDerivative
Definition SolverBase.hh:88
Integer m_max_second_derivative_evaluations
Definition SolverBase.hh:116
bool damped_mode() const
Definition SolverBase.hh:573
std::string m_task
Definition SolverBase.hh:135
void reset_counters()
Definition SolverBase.hh:1022
void bounds(const Input &t_lower_bound, const Input &t_upper_bound)
Definition SolverBase.hh:313
void enable_damped_mode()
Definition SolverBase.hh:580
bool solve(FunctionLambda &&function, FirstDerivativeLambda &&first_derivative, SecondDerivativeLambda &&second_derivative, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:704
Integer m_iterations
Definition SolverBase.hh:120
bool solve(FunctionLambda &&function, FirstDerivativeLambda &&first_derivative, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:668
Integer m_max_relaxations
Definition SolverBase.hh:124
Integer max_relaxations() const
Definition SolverBase.hh:494
TypeTrait< Output > OutputTrait
Definition SolverBase.hh:56
Integer iterations() const
Definition SolverBase.hh:440
std::string task() const
Definition SolverBase.hh:595
SolverBase(FunctionLambda &&function, FirstDerivativeLambda &&first_derivative, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:170
void max_first_derivative_evaluations(const Integer t_first_derivative_evaluations)
Definition SolverBase.hh:396
Input m_upper_bound
Definition SolverBase.hh:102
Integer m_relaxations
Definition SolverBase.hh:123
SolverBase()
Definition SolverBase.hh:142
SolverBase(FunctionLambda &&function, const Input &x_ini, Input &x_sol)
Definition SolverBase.hh:152
Integer m_second_derivative_evaluations
Definition SolverBase.hh:108
Integer m_max_first_derivative_evaluations
Definition SolverBase.hh:114
Integer m_first_derivative_evaluations
Definition SolverBase.hh:106
bool verbose_mode() const
Definition SolverBase.hh:543
void verbose_mode(bool t_verbose)
Definition SolverBase.hh:535
Scalar m_alpha
Definition SolverBase.hh:122
bool m_verbose
Definition SolverBase.hh:130
bool m_converged
Definition SolverBase.hh:136
void bottom()
Definition SolverBase.hh:1204
Namespace for the Optimist library.
Definition Optimist.hh:89
static constexpr std::string table_top_right_corner()
Retrieve the Unicode character for the top-right corner of a table.
Definition Optimist.hh:258
static constexpr std::string table_top_junction()
Retrieve the Unicode character for the top junction of a table.
Definition Optimist.hh:300
static constexpr std::string table_center_cross()
Retrieve the Unicode character for the center cross of a table.
Definition Optimist.hh:316
static constexpr std::string table_top_left_corner()
Retrieve the Unicode character for the top-left corner of a table.
Definition Optimist.hh:250
static constexpr std::string table_vertical_line()
Retrieve the Unicode character for the vertical line of a table.
Definition Optimist.hh:347
static constexpr std::string table_bottom_junction()
Retrieve the Unicode character for the bottom junction of a table.
Definition Optimist.hh:308
OPTIMIST_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Optimist.hh:97
static constexpr std::string table_left_junction()
Retrieve the Unicode character for the left junction of a table.
Definition Optimist.hh:284
static constexpr std::string table_bottom_right_corner()
Retrieve the Unicode character for the bottom-right corner of a table.
Definition Optimist.hh:276
static constexpr std::string table_right_junction()
Retrieve the Unicode character for the right junction of a table.
Definition Optimist.hh:292
static constexpr std::string table_bottom_left_corner()
Retrieve the Unicode character for the bottom-left corner of a table.
Definition Optimist.hh:267
static constexpr std::string table_horizontal_line()
Retrieve the Unicode character for the horizontal line of a table.
Definition Optimist.hh:324
Definition Optimist.hh:113