13#ifndef OPTIMIST_SOLVER_HH
14#define OPTIMIST_SOLVER_HH
41 template <
typename Real, Integer SolInDim, Integer SolOutDim,
typename DerivedSolver>
46 static_assert(SolInDim >
static_cast<Integer>(0) && SolOutDim >
static_cast<Integer>(0),
47 "Negative-dimensional optimization problem? Are you serious?");
53 using InputType =
typename std::conditional_t<SolInDim == 1, Real, Eigen::Vector<Real, SolInDim>>;
54 using OutputType =
typename std::conditional_t<SolOutDim == 1, Real, Eigen::Vector<Real, SolOutDim>>;
60 using FirstDerivativeType = std::conditional_t<SolInDim == 1 && SolOutDim == 1, Real, Eigen::Matrix<Real, SolOutDim, SolInDim>>;
62 std::conditional_t<SolInDim == 1 || SolOutDim == 1, Eigen::Matrix<Real, SolInDim, SolInDim>,
63 std::vector<Eigen::Matrix<Real, SolInDim, SolInDim>>>>;
107 if constexpr (SolInDim == 1) {
108 this->m_lower_bound = -INFTY;
109 this->m_upper_bound = INFTY;
110 }
else if constexpr (SolInDim > 1) {
111 this->m_lower_bound.setConstant(-INFTY);
112 this->m_upper_bound.setConstant(INFTY);
124 this->solve_impl(function, x_ini, x_sol);
137 this->solve_impl(function, first_derivative, x_ini, x_sol);
151 this->solve_impl(function, first_derivative, second_derivative, x_ini, x_sol);
165 #define CMD "Optimist::Solver::bounds(...): "
167 if constexpr (SolInDim == 1) {
169 CMD "invalid or degenarate bounds detected.");
170 }
else if constexpr (SolInDim > 1) {
171 OPTIMIST_ASSERT((this->m_upper_bound - t_lower_bound).minCoeff() <= 0.0,
172 CMD "invalid or degenarate bounds detected.");
174 this->m_lower_bound = t_lower_bound;
190 #define CMD "Optimist::Solver::bounds(...): "
192 if constexpr (SolInDim == 1) {
194 CMD "invalid or degenarate bounds detected.");
195 }
else if constexpr (SolInDim > 1) {
196 OPTIMIST_ASSERT((t_upper_bound - this->m_lower_bound).minCoeff() <= 0.0,
197 CMD "invalid or degenarate bounds detected.");
199 this->m_upper_bound = t_upper_bound;
211 #define CMD "Optimist::Solver::bounds(...): "
213 if constexpr (SolInDim == 1) {
215 CMD "invalid or degenarate bounds detected.");
216 }
else if constexpr (SolInDim > 1) {
218 CMD "invalid or degenarate bounds detected.");
220 this->m_lower_bound = t_lower_bound;
221 this->m_upper_bound = t_upper_bound;
250 OPTIMIST_ASSERT(!std::isnan(t_max_function_evaluations) && std::isfinite(t_max_function_evaluations),
251 "Optimist::Solver::max_function_evaluations(...): invalid input detected.");
282 "Optimist::Solver::max_first_derivative_evaluations(...): invalid input detected.");
306 "Optimist::Solver::max_second_derivative_evaluations(...): invalid input detected.");
327 OPTIMIST_ASSERT(!std::isnan(t_max_iterations) && std::isfinite(t_max_iterations),
328 "Optimist::Solver::max_iterations(...): invalid input detected.");
345 !std::isnan(t_alpha) && std::isfinite(t_alpha) && 0.0 <= t_alpha && t_alpha <= 1.0,
346 "Optimist::Solver::alpha(...): invalid input detected.");
368 OPTIMIST_ASSERT(!std::isnan(t_max_relaxations) && std::isfinite(t_max_relaxations),
369 "Optimist::Solver::max_relaxations(...): invalid input detected.");
386 !std::isnan(t_tolerance) && std::isfinite(t_tolerance) && t_tolerance > 0.0,
387 "Optimist::Solver::tolerance(...): invalid input detected.");
479 #define CMD "Optimist::Solver::solve(...): "
481 static_assert(DerivedSolver::requires_function,
482 CMD "the solver requires a function.");
483 return static_cast<DerivedSolver *
>(
this)->
solve(function,
nullptr,
nullptr, x_ini, x_sol);
498 #define CMD "Optimist::Solver::solve(...): "
500 static_assert(DerivedSolver::requires_function,
501 CMD "the solver requires a function.");
502 static_assert(DerivedSolver::requires_first_derivative,
503 CMD "the solver requires the first derivative.");
504 return static_cast<DerivedSolver *
>(
this)->
solve(function, first_derivative,
nullptr, x_ini, x_sol);
520 #define CMD "Optimist::Solver::solve(...): "
522 static_assert(DerivedSolver::requires_function,
523 CMD "the solver requires the function.");
524 static_assert(DerivedSolver::requires_first_derivative,
525 CMD "the solver requires the first derivative.");
526 static_assert(DerivedSolver::requires_second_derivative,
527 CMD "the solver requires the second derivative.");
528 return static_cast<DerivedSolver *
>(
this)->
solve(function, first_derivative, second_derivative,
543 template <Integer FunInDim, Integer FunOutDim,
typename DerivedFunction>
547 #define CMD "Optimist::Solver::rootfind(...): "
549 static_assert(SolInDim == FunInDim,
550 CMD "solver input dimension must be equal to the function input dimension.");
551 static_assert(SolOutDim == FunOutDim || SolOutDim == 1,
552 CMD "solver output dimension must be equal to the function output dimension or 1.");
553 static_assert(!(SolInDim == 1 && DerivedSolver::is_optimizer),
554 CMD "one-dimensional optimizers do not support root-finding problems.");
555 return this->
solve(function, x_ini, x_sol, SolOutDim != FunOutDim || (SolInDim > 1 && SolOutDim == 1));
569 template <Integer FunInDim, Integer FunOutDim,
typename DerivedFunction>
573 #define CMD "Optimist::Solver::optimize(...): "
575 static_assert(SolInDim == FunInDim,
576 CMD "solver input dimension must be equal to the function input dimension.");
577 static_assert(SolOutDim == 1,
578 CMD "solver output dimension must be equal to the function output dimension or 1.");
579 static_assert(!(SolInDim == 1 && DerivedSolver::is_rootfinder),
580 CMD "one-dimensional root-finders do not support optimization problems.");
581 return this->
solve(function, x_ini, x_sol,
true);
590 std::string
name()
const {
return static_cast<const DerivedSolver *
>(
this)->name_impl();};
603 template <Integer FunInDim, Integer FunOutDim,
typename DerivedFunction>
607 #define CMD "Optimist::Solver::solve(...): "
611 static_assert(SolInDim == FunInDim,
612 CMD "solver input dimension must be equal to the function input dimension.");
613 static_assert(SolOutDim == FunOutDim || SolOutDim == 1,
614 CMD "solver output dimension must be equal to the function output dimension or 1.");
617 auto function_wrapper = [&function, is_optimization](
const InputType & x,
OutputType & out)
619 typename FunctionType::OutputType f;
621 if (is_optimization) {
622 if constexpr (FunOutDim == 1) {out = 0.5*f*f;}
623 else if constexpr (SolOutDim != FunOutDim) {out = 0.5*f.squaredNorm();}
624 else {
OPTIMIST_ERROR(
CMD "optimization problem with inconsistent output in function.");}
626 if constexpr (SolOutDim == FunOutDim) {
629 else {
OPTIMIST_ERROR(
CMD "root-finding problem with inconsistent output in function.");}
635 typename FunctionType::FirstDerivativeType J; function.
first_derivative(x, J);
637 if (is_optimization) {
638 typename FunctionType::OutputType f; function.
evaluate(x, f);
640 if constexpr (FunInDim == 1 && FunOutDim == 1) {out = J*f;}
641 else if constexpr (SolOutDim != FunOutDim) {out = J.transpose()*f;}
642 else {
OPTIMIST_ERROR(
CMD "optimization problem inconsistent output in first derivative.");}
644 if constexpr (SolOutDim == FunOutDim) {
647 else {
OPTIMIST_ERROR(
CMD "root-finding problem with inconsistent output in first derivative.");}
655 if (is_optimization) {
656 typename FunctionType::OutputType f; function.
evaluate(x, f);
657 typename FunctionType::FirstDerivativeType J; function.
first_derivative(x, J);
659 if constexpr (FunInDim == 1 && FunOutDim == 1) {out = J*J + f*H;}
660 else if constexpr (SolOutDim != FunOutDim) {
661 out = J.transpose()*J;
662 for (
Integer i = 0; i < static_cast<Integer>(H.size()); ++i) {out += f(i)*H[i];}
664 else {
OPTIMIST_ERROR(
CMD "optimization problem with inconsistent output in second derivative.");}
666 if constexpr (SolOutDim == FunOutDim) {out = H;}
667 else {
OPTIMIST_ERROR(
CMD "root-finding problem with inconsistent output in second derivative.");}
672 if constexpr (DerivedSolver::requires_function && !DerivedSolver::requires_first_derivative &&
673 !DerivedSolver::requires_second_derivative) {
674 return static_cast<DerivedSolver *
>(
this)->
solve(function_wrapper, x_ini, x_sol);
675 }
else if constexpr (DerivedSolver::requires_function && DerivedSolver::requires_first_derivative &&
676 !DerivedSolver::requires_second_derivative) {
677 return static_cast<DerivedSolver *
>(
this)->
solve(function_wrapper, first_derivative_wrapper,
679 }
else if constexpr (DerivedSolver::requires_function && DerivedSolver::requires_first_derivative &&
680 DerivedSolver::requires_second_derivative) {
681 return static_cast<DerivedSolver *
>(
this)->
solve(function_wrapper, first_derivative_wrapper,
682 second_derivative_wrapper, x_ini, x_sol);
701 this->m_trace.clear();
713 "Optimist::" << this->
name() <<
"::evaluate_function(...): maximum allowed function evaluations reached.");
727 "Optimist::" << this->
name() <<
"::evaluate_first_derivative(...): maximum allowed first derivative evaluations reached.");
741 "Optimist::" << this->
name() <<
"::evaluate_second_derivative(...): maximum allowed second derivative evaluations reached.");
766 Real step_norm_old, step_norm_new, residuals_old, residuals_new, tau{1.0};
770 step_new = tau * step_old;
771 x_new = x_old + step_new;
775 if constexpr (SolInDim == 1 && SolOutDim == 1) {
776 residuals_old = std::abs(function_old);
777 residuals_new = std::abs(function_new);
778 step_norm_old = std::abs(step_old);
779 step_norm_new = std::abs(step_new);
781 residuals_old = function_old.norm();
782 residuals_new = function_new.norm();
783 step_norm_old = step_old.norm();
784 step_norm_new = step_new.norm();
786 if (residuals_new < residuals_old || step_norm_new < (Real(1.0)-tau/Real(2.0))*step_norm_old) {
816 << c_tl << h_78 << c_tr << std::endl
817 << v_ll <<
"Solver:" << std::setw(69) << this->
name() << v_rr << std::endl
818 << v_ll <<
"Task: " << std::setw(69) << this->
task() << v_rr << std::endl
819 << j_ll << h_7 << j_tt << h_7 << j_tt << h_7 << j_tt << h_7 << j_tt << h_7 << j_tt << h_14 << j_tt << h_23 << j_rr << std::endl
820 << v_ll <<
"#Iter" << v_lc <<
" #f" << v_lc <<
" #Df" << v_lc <<
" #DDf" << v_lc <<
" #Rlx" << v_lc
821 <<
" ║f(x)║₂ " << v_lc <<
"Additional notes" << std::setw(9) << v_rr << std::endl
822 << j_ll << h_7 << j_cc << h_7 << j_cc << h_7 << j_cc << h_7 << j_cc << h_7 << j_cc << h_14 << j_cc << h_23 << j_rr << std::endl;
844 << j_ll << h_7 << j_bb << h_7 << j_bb << h_7 << j_bb << h_7 << j_bb << h_7 << j_bb << h_14 << j_bb << h_23 << j_rr << std::endl
845 << v_ll << std::setw(40) << (this->
m_converged ?
"CONVERGED" :
"NOT CONVERGED") << std::setw(40) << v_rr << std::endl
846 << c_bl << h_78 << c_br << std::endl;
853 void info(Real residuals, std::string
const & notes =
"-")
865 << std::setw(12) << std::scientific << std::setprecision(6) << residuals << v_lc
866 << notes << std::setw(25-notes.length()) << v_rr << std::endl;
853 void info(Real residuals, std::string
const & notes =
"-") {
…}
#define OPTIMIST_ERROR(MSG)
Definition Optimist.hh:33
#define OPTIMIST_BASIC_CONSTANTS(Real)
Definition Optimist.hh:70
#define OPTIMIST_ASSERT(COND, MSG)
Definition Optimist.hh:43
Class container for the generic function.
Definition Function.hh:33
void second_derivative(const InputType &x, SecondDerivativeType &out) const
Definition Function.hh:92
void first_derivative(const InputType &x, FirstDerivativeType &out) const
Definition Function.hh:82
void evaluate(const InputType &x, OutputType &out) const
Definition Function.hh:72
Integer m_max_function_evaluations
Definition Solver.hh:80
Real m_tolerance
Definition Solver.hh:92
Integer first_derivative_evaluations() const
Definition Solver.hh:266
const TraceType & trace() const
Definition Solver.hh:457
constexpr Integer output_dimension() const
Definition Solver.hh:236
Solver(FunctionWrapper function, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:123
std::ostream * m_ostream
Definition Solver.hh:95
bool m_verbose
Definition Solver.hh:93
Solver()
Definition Solver.hh:106
Integer max_relaxations() const
Definition Solver.hh:360
bool m_converged
Definition Solver.hh:99
void ostream(std::ostream &t_ostream)
Definition Solver.hh:469
void lower_bound(const InputType &t_lower_bound)
Definition Solver.hh:164
void max_function_evaluations(Integer t_max_function_evaluations)
Definition Solver.hh:248
Integer m_function_evaluations
Definition Solver.hh:75
bool verbose_mode() const
Definition Solver.hh:401
bool m_damped
Definition Solver.hh:94
void store_trace(const InputType &x)
Definition Solver.hh:750
void task(std::string t_task)
Definition Solver.hh:445
bool rootfind(Function< Real, FunInDim, FunOutDim, DerivedFunction > const &function, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:544
InputType m_upper_bound
Definition Solver.hh:72
TraceType m_trace
Definition Solver.hh:100
bool solve(FunctionWrapper function, FirstDerivativeWrapper first_derivative, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:495
void max_iterations(Integer t_max_iterations)
Definition Solver.hh:326
void max_second_derivative_evaluations(Integer second_derivative_evaluations)
Definition Solver.hh:302
bool optimize(Function< Real, FunInDim, FunOutDim, DerivedFunction > const &function, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:570
Solver(FunctionWrapper function, FirstDerivativeWrapper first_derivative, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:134
typename std::conditional_t< SolInDim==1, Real, Eigen::Vector< Real, SolInDim > > InputType
Definition Solver.hh:53
Integer function_evaluations() const
Definition Solver.hh:242
std::string task() const
Definition Solver.hh:439
void damped_mode(bool t_damped)
Definition Solver.hh:417
std::ostream & ostream() const
Definition Solver.hh:463
Integer m_second_derivative_evaluations
Definition Solver.hh:77
void header()
Definition Solver.hh:799
Integer relaxations() const
Definition Solver.hh:354
void reset()
Definition Solver.hh:693
InputType m_lower_bound
Definition Solver.hh:71
Integer max_iterations() const
Definition Solver.hh:320
void max_relaxations(Integer t_max_relaxations)
Definition Solver.hh:366
bool converged() const
Definition Solver.hh:451
const InputType & upper_bound() const
Definition Solver.hh:183
std::string name() const
Definition Solver.hh:590
Integer max_function_evaluations() const
Definition Solver.hh:259
typename std::function< void(const InputType &, FirstDerivativeType &)> FirstDerivativeWrapper
Definition Solver.hh:67
void bounds(const InputType &t_lower_bound, const InputType &t_upper_bound)
Definition Solver.hh:209
typename std::vector< InputType > TraceType
Definition Solver.hh:57
bool damp(FunctionWrapper function, InputType const &x_old, InputType const &function_old, InputType const &step_old, InputType &x_new, InputType &function_new, InputType &step_new)
Definition Solver.hh:763
std::conditional_t< SolInDim==1 &&SolOutDim==1, Real, std::conditional_t< SolInDim==1||SolOutDim==1, Eigen::Matrix< Real, SolInDim, SolInDim >, std::vector< Eigen::Matrix< Real, SolInDim, SolInDim > > > > SecondDerivativeType
Definition Solver.hh:61
void enable_damped_mode()
Definition Solver.hh:428
Integer max_second_derivative_evaluations() const
Definition Solver.hh:296
Integer max_first_derivative_evaluations() const
Definition Solver.hh:272
typename std::function< void(const InputType &, SecondDerivativeType &)> SecondDerivativeWrapper
Definition Solver.hh:68
typename std::function< void(const InputType &, OutputType &)> FunctionWrapper
Definition Solver.hh:66
Integer m_first_derivative_evaluations
Definition Solver.hh:76
Integer m_max_second_derivative_evaluations
Definition Solver.hh:82
bool damped_mode() const
Definition Solver.hh:423
void tolerance(Real t_tolerance)
Definition Solver.hh:384
void enable_verbose_mode()
Definition Solver.hh:406
Integer m_max_relaxations
Definition Solver.hh:89
const InputType & lower_bound() const
Definition Solver.hh:158
Real tolerance() const
Definition Solver.hh:377
constexpr Integer input_dimension() const
Definition Solver.hh:230
Integer iterations() const
Definition Solver.hh:314
void evaluate_first_derivative(FirstDerivativeWrapper function, const InputType &x, FirstDerivativeType &out)
Definition Solver.hh:724
void alpha(Real t_alpha)
Definition Solver.hh:342
void bottom()
Definition Solver.hh:829
void evaluate_function(FunctionWrapper function, const InputType &x, OutputType &out)
Definition Solver.hh:710
std::string m_task
Definition Solver.hh:98
Solver(FunctionWrapper function, FirstDerivativeWrapper first_derivative, SecondDerivativeWrapper second_derivative, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:148
Real m_alpha
Definition Solver.hh:87
Integer second_derivative_evaluations() const
Definition Solver.hh:290
void verbose_mode(bool t_verbose)
Definition Solver.hh:395
void upper_bound(const InputType &t_upper_bound)
Definition Solver.hh:189
void max_first_derivative_evaluations(Integer first_derivative_evaluations)
Definition Solver.hh:278
std::conditional_t< SolInDim==1 &&SolOutDim==1, Real, Eigen::Matrix< Real, SolOutDim, SolInDim > > FirstDerivativeType
Definition Solver.hh:60
Integer m_max_first_derivative_evaluations
Definition Solver.hh:81
Real alpha() const
Definition Solver.hh:336
void info(Real residuals, std::string const ¬es="-")
Definition Solver.hh:853
Integer m_iterations
Definition Solver.hh:85
Integer m_max_iterations
Definition Solver.hh:86
Integer m_relaxations
Definition Solver.hh:88
bool solve(FunctionWrapper function, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:477
bool solve(Function< Real, FunInDim, FunOutDim, DerivedFunction > const &function, const InputType &x_ini, InputType &x_sol, bool is_optimization)
Definition Solver.hh:604
void disable_damped_mode()
Definition Solver.hh:433
bool solve(FunctionWrapper function, FirstDerivativeWrapper first_derivative, SecondDerivativeWrapper second_derivative, const InputType &x_ini, InputType &x_sol)
Definition Solver.hh:517
typename std::conditional_t< SolOutDim==1, Real, Eigen::Vector< Real, SolOutDim > > OutputType
Definition Solver.hh:54
void disable_verbose_mode()
Definition Solver.hh:411
void evaluate_second_derivative(SecondDerivativeWrapper function, const InputType &x, SecondDerivativeType &out)
Definition Solver.hh:738
Namespace for the Optimist library.
Definition Optimist.hh:87
static std::string table_vertical_line()
Retrieve the Unicode character for the vertical line of a table.
Definition Optimist.hh:173
static std::string table_bottom_right_corner()
Retrieve the Unicode character for the bottom-right corner of a table.
Definition Optimist.hh:119
static std::string table_top_left_corner()
Retrieve the Unicode character for the top-left corner of a table.
Definition Optimist.hh:101
static std::string table_bottom_left_corner()
Retrieve the Unicode character for the bottom-left corner of a table.
Definition Optimist.hh:113
OPTIMIST_DEFAULT_INTEGER_TYPE Integer
The Integer type as used for the API.
Definition Optimist.hh:95
static std::string table_center_cross()
Retrieve the Unicode character for the center cross of a table.
Definition Optimist.hh:149
static std::string table_bottom_junction()
Retrieve the Unicode character for the bottom junction of a table.
Definition Optimist.hh:143
static std::string table_top_junction()
Retrieve the Unicode character for the top junction of a table.
Definition Optimist.hh:137
static std::string table_top_right_corner()
Retrieve the Unicode character for the top-right corner of a table.
Definition Optimist.hh:107
static std::string table_horizontal_line()
Retrieve the Unicode character for the horizontal line of a table.
Definition Optimist.hh:155
static std::string table_right_junction()
Retrieve the Unicode character for the right junction of a table.
Definition Optimist.hh:131
static std::string table_left_junction()
Retrieve the Unicode character for the left junction of a table.
Definition Optimist.hh:125