| 155 | // Specifies a solver implementation (of a given order) for a given function |
| 156 | template <typename FunctionTypeT, typename StateTypeT> |
| 157 | class Solver { |
| 158 | public: |
| 159 | using StateType = StateTypeT; |
| 160 | using FunctionType = FunctionTypeT; |
| 161 | |
| 162 | using ProgressType = Progress<FunctionType, StateType>; |
| 163 | using CallbackType = std::function<void( |
| 164 | const FunctionType& func, const StateType&, const ProgressType&)>; |
| 165 | |
| 166 | ProgressType stopping_progress; |
| 167 | |
| 168 | public: |
| 169 | explicit Solver(const ProgressType& stopping_progress = |
| 170 | DefaultStoppingSolverProgress<FunctionType, StateType>()) |
| 171 | : stopping_progress(stopping_progress), |
| 172 | step_callback_(NoOpCallback<FunctionType, StateType>()) {} |
| 173 | |
| 174 | virtual ~Solver() = default; |
| 175 | |
| 176 | void SetCallback(CallbackType callback) { step_callback_ = callback; } |
| 177 | |
| 178 | virtual void InitializeSolver(const FunctionType& /*function*/, |
| 179 | const StateType& /*initial_state*/) = 0; |
| 180 | |
| 181 | virtual std::tuple<StateType, Progress<FunctionType, StateType>> Minimize( |
| 182 | const FunctionType& function, const StateType& function_state) { |
| 183 | // Solver state during the optimization. |
| 184 | ProgressType solver_state; |
| 185 | // Evaluate `function` once at the user-supplied starting point so the |
| 186 | // `(value, gradient)` invariant on `FunctionState` is established. For |
| 187 | // `AugmentedLagrangeState` and other non-FunctionState types this |
| 188 | // `if constexpr` branch is skipped and we keep the caller's state as-is. |
| 189 | StateType current_function_state = function_state; |
| 190 | if constexpr (IsFunctionState<StateType>::value) { |
| 191 | current_function_state = StateType(function, function_state.x); |
| 192 | } |
| 193 | |
| 194 | this->InitializeSolver(function, function_state); |
| 195 | |
| 196 | do { |
| 197 | this->step_callback_(function, current_function_state, solver_state); |
| 198 | |
| 199 | StateType previous_function_state = current_function_state; |
| 200 | current_function_state = this->OptimizationStep( |
| 201 | function, previous_function_state, solver_state); |
| 202 | |
| 203 | // Re-establish the `(value, gradient)` invariant on |
| 204 | // `current_function_state`. Solvers that already return a populated |
| 205 | // state from their line search leave `gradient.size() == x.size()` |
| 206 | // -- in that case skip the rebuild so the optimization loop pays no |
| 207 | // redundant evaluation. Unmigrated solvers return a state with an |
| 208 | // empty `gradient`; rebuild to keep `Update` and the callback |
| 209 | // consistent. |
| 210 | if constexpr (IsFunctionState<StateType>::value) { |
| 211 | if (current_function_state.gradient.size() != |
| 212 | current_function_state.x.size()) { |
| 213 | current_function_state = |
| 214 | StateType(function, current_function_state.x); |
nothing calls this directly
no outgoing calls
no test coverage detected