11#include <Eigen/SparseCore>
12#include <gch/small_vector.hpp>
14#include "sleipnir/optimization/solver/exit_status.hpp"
15#include "sleipnir/optimization/solver/iteration_info.hpp"
16#include "sleipnir/optimization/solver/newton_matrix_callbacks.hpp"
17#include "sleipnir/optimization/solver/options.hpp"
18#include "sleipnir/optimization/solver/util/all_finite.hpp"
19#include "sleipnir/optimization/solver/util/filter.hpp"
20#include "sleipnir/optimization/solver/util/kkt_error.hpp"
21#include "sleipnir/optimization/solver/util/regularized_ldlt.hpp"
22#include "sleipnir/util/assert.hpp"
23#include "sleipnir/util/print_diagnostics.hpp"
24#include "sleipnir/util/profiler.hpp"
25#include "sleipnir/util/scope_exit.hpp"
26#include "sleipnir/util/symbol_exports.hpp"
50template <
typename Scalar>
52 const NewtonMatrixCallbacks<Scalar>& matrix_callbacks,
53 std::span<std::function<
bool(
const IterationInfo<Scalar>& info)>>
55 const Options& options, Eigen::Vector<Scalar, Eigen::Dynamic>& x) {
56 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
57 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
58 using SparseVector = Eigen::SparseVector<Scalar>;
62 const auto solve_start_time = std::chrono::steady_clock::now();
64 gch::small_vector<SolveProfiler> solve_profilers;
65 solve_profilers.emplace_back(
"solver");
66 solve_profilers.emplace_back(
"↳ setup");
67 solve_profilers.emplace_back(
"↳ iteration");
68 solve_profilers.emplace_back(
" ↳ feasibility check");
69 solve_profilers.emplace_back(
" ↳ callbacks");
70 solve_profilers.emplace_back(
" ↳ KKT matrix decomp");
71 solve_profilers.emplace_back(
" ↳ KKT system solve");
72 solve_profilers.emplace_back(
" ↳ line search");
73 solve_profilers.emplace_back(
" ↳ f(x)");
74 solve_profilers.emplace_back(
" ↳ ∇f(x)");
75 solve_profilers.emplace_back(
" ↳ ∇²ₓₓL");
77 auto& solver_prof = solve_profilers[0];
78 auto& setup_prof = solve_profilers[1];
79 auto& inner_iter_prof = solve_profilers[2];
80 auto& feasibility_check_prof = solve_profilers[3];
81 auto& iter_callbacks_prof = solve_profilers[4];
82 auto& kkt_matrix_decomp_prof = solve_profilers[5];
83 auto& kkt_system_solve_prof = solve_profilers[6];
84 auto& line_search_prof = solve_profilers[7];
87#ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
88 auto& f_prof = solve_profilers[8];
89 auto& g_prof = solve_profilers[9];
90 auto& H_prof = solve_profilers[10];
92 NewtonMatrixCallbacks<Scalar> matrices{
93 matrix_callbacks.num_decision_variables,
94 [&](
const DenseVector& x) -> Scalar {
95 ScopedProfiler prof{f_prof};
96 return matrix_callbacks.f(x);
98 [&](
const DenseVector& x) -> SparseVector {
99 ScopedProfiler prof{g_prof};
100 return matrix_callbacks.g(x);
102 [&](
const DenseVector& x) -> SparseMatrix {
103 ScopedProfiler prof{H_prof};
104 return matrix_callbacks.H(x);
106 matrix_callbacks.scaling};
108 const auto& matrices = matrix_callbacks;
114 Scalar f = matrices.f(x);
115 SparseVector g = matrices.g(x);
116 SparseMatrix H = matrices.H(x);
119 slp_assert(g.rows() == matrices.num_decision_variables);
120 slp_assert(H.rows() == matrices.num_decision_variables);
121 slp_assert(H.cols() == matrices.num_decision_variables);
128 if (!isfinite(f) || !all_finite(g) || !all_finite(H)) {
129 return ExitStatus::NONFINITE_INITIAL_GUESS;
134 Filter<Scalar> filter;
136 RegularizedLDLT<Scalar> solver{
138 H.nonZeros() < 0.25 * H.size(), matrices.num_decision_variables, 0};
141 constexpr Scalar α_reduction_factor(0.5);
142 constexpr Scalar α_min(1e-20);
145 Scalar E_0 = unscaled_kkt_error<Scalar, KKTErrorType::INF_NORM_SCALED>(
146 matrices.scaling, g);
151 scope_exit exit{[&] {
152 if (options.diagnostics) {
154 if (iterations > 0) {
155 print_bottom_iteration_diagnostics();
157 print_solver_diagnostics(solve_profilers);
161 while (E_0 > Scalar(options.tolerance)) {
162 ScopedProfiler inner_iter_profiler{inner_iter_prof};
163 ScopedProfiler feasibility_check_profiler{feasibility_check_prof};
166 if (x.template lpNorm<Eigen::Infinity>() > Scalar(1e10) || !x.allFinite()) {
167 return ExitStatus::DIVERGING_ITERATES;
170 feasibility_check_profiler.stop();
171 ScopedProfiler iter_callbacks_profiler{iter_callbacks_prof};
174 for (
const auto& callback : iteration_callbacks) {
175 if (callback({iterations, x, {}, {}, {}, g, H, {}, {}})) {
176 return ExitStatus::CALLBACK_REQUESTED_STOP;
180 iter_callbacks_profiler.stop();
181 ScopedProfiler kkt_matrix_decomp_profiler{kkt_matrix_decomp_prof};
188 kkt_matrix_decomp_profiler.stop();
189 ScopedProfiler kkt_system_solve_profiler{kkt_system_solve_prof};
191 DenseVector p_x = solver.solve(-g);
193 kkt_system_solve_profiler.stop();
194 ScopedProfiler line_search_profiler{line_search_prof};
196 constexpr Scalar α_max(1);
202 trial_x = x + α * p_x;
204 trial_f = matrices.f(trial_x);
207 if (!isfinite(trial_f)) {
209 α *= α_reduction_factor;
212 return ExitStatus::LINE_SEARCH_FAILED;
218 if (filter.try_add(FilterEntry{f}, FilterEntry{trial_f}, p_x, g, α)) {
224 α *= α_reduction_factor;
229 Scalar current_kkt_error = kkt_error<Scalar, KKTErrorType::ONE_NORM>(g);
231 trial_x = x + α_max * p_x;
233 Scalar next_kkt_error =
234 kkt_error<Scalar, KKTErrorType::ONE_NORM>(matrices.g(trial_x));
237 if (next_kkt_error <= Scalar(0.999) * current_kkt_error) {
238 trial_f = matrices.f(trial_x);
244 return ExitStatus::LINE_SEARCH_FAILED;
248 line_search_profiler.stop();
260 E_0 = unscaled_kkt_error<Scalar, KKTErrorType::INF_NORM_SCALED>(
261 matrices.scaling, g);
263 inner_iter_profiler.stop();
265 if (options.diagnostics) {
266 print_iteration_diagnostics(
267 iterations, IterationType::NORMAL,
268 inner_iter_profiler.current_duration(), E_0, f, Scalar(0), Scalar(0),
269 Scalar(0), solver.hessian_regularization(),
270 solver.constraint_jacobian_regularization(),
271 p_x.template lpNorm<Eigen::Infinity>(), Scalar(1), α, α_max,
272 α_reduction_factor, Scalar(1));
278 if (iterations >= options.max_iterations) {
279 return ExitStatus::MAX_ITERATIONS_EXCEEDED;
283 if (std::chrono::steady_clock::now() - solve_start_time > options.timeout) {
284 return ExitStatus::TIMEOUT;
288 return ExitStatus::SUCCESS;
291extern template SLEIPNIR_DLLEXPORT ExitStatus
292newton(
const NewtonMatrixCallbacks<double>& matrix_callbacks,
293 std::span<std::function<
bool(
const IterationInfo<double>& info)>>
295 const Options& options, Eigen::Vector<double, Eigen::Dynamic>& x);