Updates state from function information. For plain `FunctionState`, the `(value, gradient)` invariant means we can read the numbers we need directly from the two state arguments instead of calling `function()` again. The `AugmentedLagrangeState` branch still re-evaluates because the augmented-Lagrangian objective is a *composite* built from `function`, the multipliers, and the penalty, none of w
| 151 | // penalty, none of which are stored as a cached `(value, gradient)` on |
| 152 | // the state itself. |
| 153 | void Update(const FunctionType& function, |
| 154 | const StateType& previous_function_state, |
| 155 | const StateType& current_function_state, |
| 156 | const Progress<FunctionType, StateType>& stop_progress) { |
| 157 | const VectorType& current_x = current_function_state.x; |
| 158 | const VectorType& previous_x = previous_function_state.x; |
| 159 | VectorType previous_gradient, current_gradient; |
| 160 | ScalarType previous_value; |
| 161 | ScalarType current_value; |
| 162 | if constexpr (StateType::IsConstrained) { |
| 163 | // Augmented-Lagrangian path: value/gradient live in the composite |
| 164 | // objective, not on the state, so we still evaluate here. |
| 165 | const auto previous_function = cppoptlib::function::ToAugmentedLagrangian( |
| 166 | function, previous_function_state.multiplier_state, |
| 167 | previous_function_state.penalty_state); |
| 168 | const auto current_function = cppoptlib::function::ToAugmentedLagrangian( |
| 169 | function, current_function_state.multiplier_state, |
| 170 | current_function_state.penalty_state); |
| 171 | |
| 172 | previous_value = previous_function(previous_x, &previous_gradient); |
| 173 | current_value = current_function(current_x, ¤t_gradient); |
| 174 | } else if constexpr (FunctionType::Differentiability >= |
| 175 | cppoptlib::function::DifferentiabilityMode::First) { |
| 176 | // FunctionState path: read cached (value, gradient) populated by the |
| 177 | // solver/line search -- no redundant function() calls. |
| 178 | previous_value = previous_function_state.value; |
| 179 | current_value = current_function_state.value; |
| 180 | previous_gradient = previous_function_state.gradient; |
| 181 | current_gradient = current_function_state.gradient; |
| 182 | } else { |
| 183 | // None-mode FunctionState: only value is tracked. |
| 184 | previous_value = previous_function_state.value; |
| 185 | current_value = current_function_state.value; |
| 186 | } |
| 187 | |
| 188 | num_iterations++; |
| 189 | f_delta = std::abs(current_value - previous_value); |
| 190 | x_delta = (current_x - previous_x).template lpNorm<Eigen::Infinity>(); |
| 191 | |
| 192 | // Compute gradient norm if the function supports differentiation |
| 193 | if constexpr (FunctionType::Differentiability >= |
| 194 | cppoptlib::function::DifferentiabilityMode::First) { |
| 195 | gradient_norm = current_gradient.template lpNorm<Eigen::Infinity>(); |
| 196 | } |
| 197 | // Compute Hessian condition number if the function supports second-order |
| 198 | // derivatives. Gated on `!StateType::IsConstrained` so |
| 199 | // constrained-solver state types (`AugmentedLagrangeState`) skip it: |
| 200 | // their `FunctionType` is `ConstrainedOptimizationProblem`, which |
| 201 | // has no `operator()` -- the solver evaluates objective and |
| 202 | // constraints separately via the struct's member fields. |
| 203 | if constexpr (!StateType::IsConstrained && |
| 204 | FunctionType::Differentiability == |
| 205 | cppoptlib::function::DifferentiabilityMode::Second) { |
| 206 | MatrixType current_hessian; |
| 207 | function(current_x, nullptr, ¤t_hessian); |
| 208 | condition_hessian = |
| 209 | current_hessian.norm() * current_hessian.inverse().norm(); |
| 210 | } |
no test coverage detected