12#include <Eigen/SparseCore>
13#include <gch/small_vector.hpp>
15#include "sleipnir/optimization/solver/exit_status.hpp"
16#include "sleipnir/optimization/solver/iteration_info.hpp"
17#include "sleipnir/optimization/solver/newton_matrix_callbacks.hpp"
18#include "sleipnir/optimization/solver/options.hpp"
19#include "sleipnir/optimization/solver/util/error_estimate.hpp"
20#include "sleipnir/optimization/solver/util/filter.hpp"
21#include "sleipnir/optimization/solver/util/kkt_error.hpp"
22#include "sleipnir/optimization/solver/util/regularized_ldlt.hpp"
23#include "sleipnir/util/assert.hpp"
24#include "sleipnir/util/print_diagnostics.hpp"
25#include "sleipnir/util/scope_exit.hpp"
26#include "sleipnir/util/scoped_profiler.hpp"
27#include "sleipnir/util/solve_profiler.hpp"
28#include "sleipnir/util/symbol_exports.hpp"
52template <
typename Scalar>
54 const NewtonMatrixCallbacks<Scalar>& matrix_callbacks,
55 std::span<std::function<
bool(
const IterationInfo<Scalar>& info)>>
57 const Options& options, Eigen::Vector<Scalar, Eigen::Dynamic>& x) {
58 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
59 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
60 using SparseVector = Eigen::SparseVector<Scalar>;
64 const auto solve_start_time = std::chrono::steady_clock::now();
66 gch::small_vector<SolveProfiler> solve_profilers;
67 solve_profilers.emplace_back(
"solver");
68 solve_profilers.emplace_back(
" ↳ setup");
69 solve_profilers.emplace_back(
" ↳ iteration");
70 solve_profilers.emplace_back(
" ↳ feasibility ✓");
71 solve_profilers.emplace_back(
" ↳ iter callbacks");
72 solve_profilers.emplace_back(
" ↳ KKT matrix decomp");
73 solve_profilers.emplace_back(
" ↳ KKT system solve");
74 solve_profilers.emplace_back(
" ↳ line search");
75 solve_profilers.emplace_back(
" ↳ next iter prep");
76 solve_profilers.emplace_back(
" ↳ f(x)");
77 solve_profilers.emplace_back(
" ↳ ∇f(x)");
78 solve_profilers.emplace_back(
" ↳ ∇²ₓₓL");
80 auto& solver_prof = solve_profilers[0];
81 auto& setup_prof = solve_profilers[1];
82 auto& inner_iter_prof = solve_profilers[2];
83 auto& feasibility_check_prof = solve_profilers[3];
84 auto& iter_callbacks_prof = solve_profilers[4];
85 auto& kkt_matrix_decomp_prof = solve_profilers[5];
86 auto& kkt_system_solve_prof = solve_profilers[6];
87 auto& line_search_prof = solve_profilers[7];
88 auto& next_iter_prep_prof = solve_profilers[8];
91#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
92 auto& f_prof = solve_profilers[9];
93 auto& g_prof = solve_profilers[10];
94 auto& H_prof = solve_profilers[11];
96 NewtonMatrixCallbacks<Scalar> matrices{
97 [&](
const DenseVector& x) -> Scalar {
98 ScopedProfiler prof{f_prof};
99 return matrix_callbacks.f(x);
101 [&](
const DenseVector& x) -> SparseVector {
102 ScopedProfiler prof{g_prof};
103 return matrix_callbacks.g(x);
105 [&](
const DenseVector& x) -> SparseMatrix {
106 ScopedProfiler prof{H_prof};
107 return matrix_callbacks.H(x);
110 const auto& matrices = matrix_callbacks;
116 Scalar f = matrices.f(x);
118 int num_decision_variables = x.rows();
120 SparseVector g = matrices.g(x);
121 SparseMatrix H = matrices.H(x);
124 slp_assert(g.rows() == num_decision_variables);
125 slp_assert(H.rows() == num_decision_variables);
126 slp_assert(H.cols() == num_decision_variables);
130 return ExitStatus::NONFINITE_INITIAL_COST_OR_CONSTRAINTS;
135 Filter<Scalar> filter;
137 RegularizedLDLT<Scalar> solver{num_decision_variables, 0};
140 constexpr Scalar α_reduction_factor(0.5);
141 constexpr Scalar α_min(1e-20);
144 Scalar E_0 = std::numeric_limits<Scalar>::infinity();
149 scope_exit exit{[&] {
150 if (options.diagnostics) {
152 if (iterations > 0) {
153 print_bottom_iteration_diagnostics();
155 print_solver_diagnostics(solve_profilers);
159 while (E_0 > Scalar(options.tolerance)) {
160 ScopedProfiler inner_iter_profiler{inner_iter_prof};
161 ScopedProfiler feasibility_check_profiler{feasibility_check_prof};
164 if (x.template lpNorm<Eigen::Infinity>() > Scalar(1e10) || !x.allFinite()) {
165 return ExitStatus::DIVERGING_ITERATES;
168 feasibility_check_profiler.stop();
169 ScopedProfiler iter_callbacks_profiler{iter_callbacks_prof};
172 for (
const auto& callback : iteration_callbacks) {
173 if (callback({iterations, x, g, H, SparseMatrix{}, SparseMatrix{}})) {
174 return ExitStatus::CALLBACK_REQUESTED_STOP;
178 iter_callbacks_profiler.stop();
179 ScopedProfiler kkt_matrix_decomp_profiler{kkt_matrix_decomp_prof};
186 kkt_matrix_decomp_profiler.stop();
187 ScopedProfiler kkt_system_solve_profiler{kkt_system_solve_prof};
189 DenseVector p_x = solver.solve(-g);
191 kkt_system_solve_profiler.stop();
192 ScopedProfiler line_search_profiler{line_search_prof};
194 constexpr Scalar α_max(1);
200 DenseVector trial_x = x + α * p_x;
202 Scalar trial_f = matrices.f(trial_x);
205 if (!isfinite(trial_f)) {
207 α *= α_reduction_factor;
210 return ExitStatus::LINE_SEARCH_FAILED;
216 if (filter.try_add(FilterEntry{trial_f}, α)) {
222 α *= α_reduction_factor;
227 Scalar current_kkt_error = kkt_error<Scalar>(g);
229 DenseVector trial_x = x + α_max * p_x;
231 Scalar next_kkt_error = kkt_error<Scalar>(matrices.g(trial_x));
234 if (next_kkt_error <= Scalar(0.999) * current_kkt_error) {
241 return ExitStatus::LINE_SEARCH_FAILED;
245 line_search_profiler.stop();
255 ScopedProfiler next_iter_prep_profiler{next_iter_prep_prof};
258 E_0 = error_estimate<Scalar>(g);
260 next_iter_prep_profiler.stop();
261 inner_iter_profiler.stop();
263 if (options.diagnostics) {
264 print_iteration_diagnostics(iterations, IterationType::NORMAL,
265 inner_iter_profiler.current_duration(), E_0,
266 f, Scalar(0), Scalar(0), Scalar(0),
267 solver.hessian_regularization(), α, α_max,
268 α_reduction_factor, Scalar(1));
274 if (iterations >= options.max_iterations) {
275 return ExitStatus::MAX_ITERATIONS_EXCEEDED;
279 if (std::chrono::steady_clock::now() - solve_start_time > options.timeout) {
280 return ExitStatus::TIMEOUT;
284 return ExitStatus::SUCCESS;
287extern template SLEIPNIR_DLLEXPORT ExitStatus
288newton(
const NewtonMatrixCallbacks<double>& matrix_callbacks,
289 std::span<std::function<
bool(
const IterationInfo<double>& info)>>
291 const Options& options, Eigen::Vector<double, Eigen::Dynamic>& x);