Sleipnir C++ API
Loading...
Searching...
No Matches
feasibility_restoration.hpp
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6#include <cmath>
7#include <span>
8#include <tuple>
9#include <utility>
10
11#include <Eigen/Core>
12#include <gch/small_vector.hpp>
13
14#include "sleipnir/optimization/solver/exit_status.hpp"
15#include "sleipnir/optimization/solver/interior_point_matrix_callbacks.hpp"
16#include "sleipnir/optimization/solver/iteration_info.hpp"
17#include "sleipnir/optimization/solver/options.hpp"
18#include "sleipnir/optimization/solver/sqp_matrix_callbacks.hpp"
19#include "sleipnir/optimization/solver/util/append_as_triplets.hpp"
20#include "sleipnir/optimization/solver/util/lagrange_multiplier_estimate.hpp"
21
22namespace slp {
23
24template <typename Scalar>
25ExitStatus interior_point(
26 const InteriorPointMatrixCallbacks<Scalar>& matrix_callbacks,
27 std::span<std::function<bool(const IterationInfo<Scalar>& info)>>
28 iteration_callbacks,
29 const Options& options, bool in_feasibility_restoration,
30#ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
31 const Eigen::ArrayX<bool>& bound_constraint_mask,
32#endif
33 Eigen::Vector<Scalar, Eigen::Dynamic>& x,
34 Eigen::Vector<Scalar, Eigen::Dynamic>& s,
35 Eigen::Vector<Scalar, Eigen::Dynamic>& y,
36 Eigen::Vector<Scalar, Eigen::Dynamic>& z, Scalar& μ);
37
46template <typename Scalar>
47std::tuple<Eigen::Vector<Scalar, Eigen::Dynamic>,
48 Eigen::Vector<Scalar, Eigen::Dynamic>>
49compute_p_n(const Eigen::Vector<Scalar, Eigen::Dynamic>& c, Scalar ρ,
50 Scalar μ) {
51 // From equation (33) of [2]:
52 // ______________________
53 // μ − ρ c(x) /(μ − ρ c(x))² μ c(x)
54 // n = −−−−−−−−−− + / (−−−−−−−−−−) + −−−−−− (1)
55 // 2ρ √ ( 2ρ ) 2ρ
56 //
57 // The quadratic formula:
58 // ________
59 // -b + √b² - 4ac
60 // x = −−−−−−−−−−−−−− (2)
61 // 2a
62 //
63 // Rearrange (1) to fit the quadratic formula better:
64 // _________________________
65 // μ - ρ c(x) + √(μ - ρ c(x))² + 2ρ μ c(x)
66 // n = −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
67 // 2ρ
68 //
69 // Solve for coefficients:
70 //
71 // a = ρ (3)
72 // b = ρ c(x) - μ (4)
73 //
74 // -4ac = 2ρ μ c(x)
75 // -4(ρ)c = 2ρ μ c(x)
76 // -4c = 2μ c(x)
77 // c = -μ c(x)/2 (5)
78 //
79 // p = c(x) + n (6)
80
81 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
82
83 using std::sqrt;
84
85 DenseVector p{c.rows()};
86 DenseVector n{c.rows()};
87 for (int row = 0; row < p.rows(); ++row) {
88 Scalar _a = ρ;
89 Scalar _b = ρ * c[row] - μ;
90 Scalar _c = -μ * c[row] / Scalar(2);
91
92 n[row] = (-_b + sqrt(_b * _b - Scalar(4) * _a * _c)) / (Scalar(2) * _a);
93 p[row] = c[row] + n[row];
94 }
95
96 return {std::move(p), std::move(n)};
97}
98
113template <typename Scalar>
114ExitStatus feasibility_restoration(
115 const SQPMatrixCallbacks<Scalar>& matrix_callbacks,
116 std::span<std::function<bool(const IterationInfo<Scalar>& info)>>
117 iteration_callbacks,
118 const Options& options, Eigen::Vector<Scalar, Eigen::Dynamic>& x,
119 Eigen::Vector<Scalar, Eigen::Dynamic>& y) {
120 // Feasibility restoration
121 //
122 // min ρ Σ (pₑ + nₑ) + ζ/2 (x - xᵣ)ᵀDᵣ(x - xᵣ)
123 // x
124 // pₑ,nₑ
125 //
126 // s.t. cₑ(x) - pₑ + nₑ = 0
127 // pₑ ≥ 0
128 // nₑ ≥ 0
129 //
130 // where ρ = 1000, ζ = √μ where μ is the barrier parameter, xᵣ is original
131 // iterate before feasibility restoration, and Dᵣ is a scaling matrix defined
132 // by
133 //
134 // Dᵣ = diag(min(1, 1/xᵣ[i]²) for i in x.rows())
135
136 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
137 using DiagonalMatrix = Eigen::DiagonalMatrix<Scalar, Eigen::Dynamic>;
138 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
139 using SparseVector = Eigen::SparseVector<Scalar>;
140
141 using std::sqrt;
142
143 const auto& matrices = matrix_callbacks;
144 const auto& num_vars = matrices.num_decision_variables;
145 const auto& num_eq = matrices.num_equality_constraints;
146
147 constexpr Scalar ρ(1e3);
148 const Scalar μ(options.tolerance / 10.0);
149
150 const DenseVector c_e = matrices.c_e(x);
151
152 Scalar fr_μ = std::max(μ, c_e.template lpNorm<Eigen::Infinity>());
153 const Scalar ζ = sqrt(fr_μ);
154
155 const auto& x_r = x;
156 const auto [p_e_0, n_e_0] = compute_p_n(c_e, ρ, fr_μ);
157
158 // Dᵣ = diag(min(1, 1/xᵣ[i]²) for i in x.rows())
159 const DiagonalMatrix D_r =
160 x.cwiseSquare().cwiseInverse().cwiseMin(Scalar(1)).asDiagonal();
161
162 DenseVector fr_x{num_vars + 2 * num_eq};
163 fr_x << x, p_e_0, n_e_0;
164
165 DenseVector fr_s = DenseVector::Ones(2 * num_eq);
166
167 DenseVector fr_y = DenseVector::Zero(num_eq);
168
169 // Force the duals to start with perfect complementarity with the slacks
170 DenseVector fr_z{2 * num_eq};
171 fr_z << fr_μ * p_e_0.cwiseInverse(), fr_μ * n_e_0.cwiseInverse();
172
173 // Inherit the parent problem's scaling for the constraints, and use no
174 // scaling for the cost function since it has changed. The new rows introduced
175 // are not scaled.
176 const ProblemScaling<Scalar> fr_scaling{Scalar(1), matrices.scaling.c_e,
177 DenseVector::Ones(2 * num_eq)};
178
179 InteriorPointMatrixCallbacks<Scalar> fr_matrix_callbacks{
180 static_cast<int>(fr_x.rows()),
181 static_cast<int>(fr_y.rows()),
182 static_cast<int>(fr_z.rows()),
183 [&](const DenseVector& x_p) -> Scalar {
184 auto x = x_p.segment(0, num_vars);
185
186 // Cost function
187 //
188 // ρ Σ (pₑ + nₑ) + ζ/2 (x - xᵣ)ᵀDᵣ(x - xᵣ)
189
190 auto diff = x - x_r;
191 return ρ * x_p.segment(num_vars, 2 * num_eq).array().sum() +
192 ζ / Scalar(2) * diff.transpose() * D_r * diff;
193 },
194 [&](const DenseVector& x_p) -> SparseVector {
195 auto x = x_p.segment(0, num_vars);
196
197 // Cost function gradient
198 //
199 // [ζDᵣ(x − xᵣ)]
200 // [ ρ ]
201 // [ ρ ]
202 DenseVector g{x_p.rows()};
203 g.segment(0, num_vars) = ζ * D_r * (x - x_r);
204 g.segment(num_vars, 2 * num_eq).setConstant(ρ);
205 return g.sparseView();
206 },
207 [&](const DenseVector& x_p, const DenseVector& y_p,
208 [[maybe_unused]] const DenseVector& z_p) -> SparseMatrix {
209 auto x = x_p.segment(0, num_vars);
210 const auto& y = y_p;
211
212 // Cost function Hessian
213 //
214 // [ζDᵣ 0 0]
215 // [ 0 0 0]
216 // [ 0 0 0]
217 gch::small_vector<Eigen::Triplet<Scalar>> triplets;
218 triplets.reserve(x_p.rows());
219 append_as_triplets(triplets, 0, 0, {SparseMatrix{ζ * D_r}});
220 SparseMatrix d2f_dx2{x_p.rows(), x_p.rows()};
221 d2f_dx2.setFromSortedTriplets(triplets.begin(), triplets.end());
222
223 // Constraint part of original problem's Lagrangian Hessian
224 //
225 // −∇ₓₓ²yᵀcₑ(x)
226 auto H_c = matrices.H_c(x, y);
227 H_c.resize(x_p.rows(), x_p.rows());
228
229 // Lagrangian Hessian
230 //
231 // [ζDᵣ 0 0]
232 // [ 0 0 0] − ∇ₓₓ²yᵀcₑ(x)
233 // [ 0 0 0]
234 return d2f_dx2 + H_c;
235 },
236 [&](const DenseVector& x_p, [[maybe_unused]] const DenseVector& y_p,
237 [[maybe_unused]] const DenseVector& z_p) -> SparseMatrix {
238 return SparseMatrix{x_p.rows(), x_p.rows()};
239 },
240 [&](const DenseVector& x_p) -> DenseVector {
241 auto x = x_p.segment(0, num_vars);
242 auto p_e = x_p.segment(num_vars, num_eq);
243 auto n_e = x_p.segment(num_vars + num_eq, num_eq);
244
245 // Equality constraints
246 //
247 // cₑ(x) - pₑ + nₑ = 0
248 return matrices.c_e(x) - p_e + n_e;
249 },
250 [&](const DenseVector& x_p) -> SparseMatrix {
251 auto x = x_p.segment(0, num_vars);
252
253 // Equality constraint Jacobian
254 //
255 // [Aₑ −I I]
256
257 SparseMatrix A_e = matrices.A_e(x);
258
259 gch::small_vector<Eigen::Triplet<Scalar>> triplets;
260 triplets.reserve(A_e.nonZeros() + 2 * num_eq);
261
262 append_as_triplets(triplets, 0, 0, {A_e});
263 append_diagonal_as_triplets(
264 triplets, 0, num_vars,
265 DenseVector::Constant(num_eq, Scalar(-1)).eval());
266 append_diagonal_as_triplets(
267 triplets, 0, num_vars + num_eq,
268 DenseVector::Constant(num_eq, Scalar(1)).eval());
269
270 SparseMatrix A_e_p{A_e.rows(), x_p.rows()};
271 A_e_p.setFromSortedTriplets(triplets.begin(), triplets.end());
272 return A_e_p;
273 },
274 [&](const DenseVector& x_p) -> DenseVector {
275 // Inequality constraints
276 //
277 // pₑ ≥ 0
278 // nₑ ≥ 0
279 return x_p.segment(num_vars, 2 * num_eq);
280 },
281 [&](const DenseVector& x_p) -> SparseMatrix {
282 // Inequality constraint Jacobian
283 //
284 // [0 I 0]
285 // [0 0 I]
286
287 gch::small_vector<Eigen::Triplet<Scalar>> triplets;
288 triplets.reserve(2 * num_eq);
289
290 append_diagonal_as_triplets(
291 triplets, 0, num_vars,
292 DenseVector::Constant(2 * num_eq, Scalar(1)).eval());
293
294 SparseMatrix A_i_p{2 * num_eq, x_p.rows()};
295 A_i_p.setFromSortedTriplets(triplets.begin(), triplets.end());
296 return A_i_p;
297 },
298 fr_scaling};
299
300 auto status = interior_point<Scalar>(fr_matrix_callbacks, iteration_callbacks,
301 options, true,
302#ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
303 {},
304#endif
305 fr_x, fr_s, fr_y, fr_z, fr_μ);
306
307 x = fr_x.segment(0, x.rows());
308
309 if (status == ExitStatus::CALLBACK_REQUESTED_STOP) {
310 auto g = matrices.g(x);
311 auto A_e = matrices.A_e(x);
312
313 y = lagrange_multiplier_estimate(g, A_e);
314
315 return ExitStatus::SUCCESS;
316 } else if (status == ExitStatus::SUCCESS) {
317 return ExitStatus::LOCALLY_INFEASIBLE;
318 } else {
319 return ExitStatus::FEASIBILITY_RESTORATION_FAILED;
320 }
321}
322
341template <typename Scalar>
342ExitStatus feasibility_restoration(
343 const InteriorPointMatrixCallbacks<Scalar>& matrix_callbacks,
344 std::span<std::function<bool(const IterationInfo<Scalar>& info)>>
345 iteration_callbacks,
346 const Options& options, Eigen::Vector<Scalar, Eigen::Dynamic>& x,
347 Eigen::Vector<Scalar, Eigen::Dynamic>& s,
348 Eigen::Vector<Scalar, Eigen::Dynamic>& y,
349 Eigen::Vector<Scalar, Eigen::Dynamic>& z, Scalar μ) {
350 // Feasibility restoration
351 //
352 // min ρ Σ (pₑ + nₑ + pᵢ + nᵢ) + ζ/2 (x - xᵣ)ᵀDᵣ(x - xᵣ)
353 // x
354 // pₑ,nₑ
355 // pᵢ,nᵢ
356 //
357 // s.t. cₑ(x) - pₑ + nₑ = 0
358 // cᵢ(x) - pᵢ + nᵢ ≥ 0
359 // pₑ ≥ 0
360 // nₑ ≥ 0
361 // pᵢ ≥ 0
362 // nᵢ ≥ 0
363 //
364 // where ρ = 1000, ζ = √μ where μ is the barrier parameter, xᵣ is original
365 // iterate before feasibility restoration, and Dᵣ is a scaling matrix defined
366 // by
367 //
368 // Dᵣ = diag(min(1, 1/xᵣ[i]²) for i in x.rows())
369
370 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
371 using DiagonalMatrix = Eigen::DiagonalMatrix<Scalar, Eigen::Dynamic>;
372 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
373 using SparseVector = Eigen::SparseVector<Scalar>;
374
375 using std::sqrt;
376
377 const auto& matrices = matrix_callbacks;
378 const auto& num_vars = matrices.num_decision_variables;
379 const auto& num_eq = matrices.num_equality_constraints;
380 const auto& num_ineq = matrices.num_inequality_constraints;
381
382 constexpr Scalar ρ(1e3);
383
384 const DenseVector c_e = matrices.c_e(x);
385 const DenseVector c_i = matrices.c_i(x);
386
387 Scalar fr_μ = std::max({μ, c_e.template lpNorm<Eigen::Infinity>(),
388 (c_i - s).template lpNorm<Eigen::Infinity>()});
389 const Scalar ζ = sqrt(fr_μ);
390
391 const auto& x_r = x;
392 const auto [p_e_0, n_e_0] = compute_p_n(c_e, ρ, fr_μ);
393 const auto [p_i_0, n_i_0] = compute_p_n((c_i - s).eval(), ρ, fr_μ);
394
395 // Dᵣ = diag(min(1, 1/xᵣ[i]²) for i in x.rows())
396 const DiagonalMatrix D_r =
397 x.cwiseSquare().cwiseInverse().cwiseMin(Scalar(1)).asDiagonal();
398
399 DenseVector fr_x{num_vars + 2 * num_eq + 2 * num_ineq};
400 fr_x << x, p_e_0, n_e_0, p_i_0, n_i_0;
401
402 DenseVector fr_s{s.rows() + 2 * num_eq + 2 * num_ineq};
403 fr_s.segment(0, s.rows()) = s;
404 fr_s.segment(s.rows(), 2 * num_eq + 2 * num_ineq).setOnes();
405
406 DenseVector fr_y = DenseVector::Zero(c_e.rows());
407
408 // Force the duals to start with perfect complementarity with the slacks
409 DenseVector fr_z{c_i.rows() + 2 * num_eq + 2 * num_ineq};
410 fr_z << fr_μ * s.cwiseInverse(), fr_μ * p_e_0.cwiseInverse(),
411 fr_μ * n_e_0.cwiseInverse(), fr_μ * p_i_0.cwiseInverse(),
412 fr_μ * n_i_0.cwiseInverse();
413
414 // Inherit the parent problem's scaling for the constraints, and use no
415 // scaling for the cost function since it has changed. The new rows introduced
416 // are not scaled.
417 DenseVector fr_d_c_i{c_i.rows() + 2 * num_eq + 2 * num_ineq};
418 fr_d_c_i << matrices.scaling.c_i,
419 DenseVector::Ones(2 * num_eq + 2 * num_ineq);
420 const ProblemScaling<Scalar> fr_scaling{Scalar(1), matrices.scaling.c_e,
421 fr_d_c_i};
422
423 InteriorPointMatrixCallbacks<Scalar> fr_matrix_callbacks{
424 static_cast<int>(fr_x.rows()),
425 static_cast<int>(fr_y.rows()),
426 static_cast<int>(fr_z.rows()),
427 [&](const DenseVector& x_p) -> Scalar {
428 auto x = x_p.segment(0, num_vars);
429
430 // Cost function
431 //
432 // ρ Σ (pₑ + nₑ + pᵢ + nᵢ) + ζ/2 (x - xᵣ)ᵀDᵣ(x - xᵣ)
433 auto diff = x - x_r;
434 return ρ * x_p.segment(num_vars, 2 * num_eq + 2 * num_ineq)
435 .array()
436 .sum() +
437 ζ / Scalar(2) * diff.transpose() * D_r * diff;
438 },
439 [&](const DenseVector& x_p) -> SparseVector {
440 auto x = x_p.segment(0, num_vars);
441
442 // Cost function gradient
443 //
444 // [ζDᵣ(x − xᵣ)]
445 // [ ρ ]
446 // [ ρ ]
447 // [ ρ ]
448 // [ ρ ]
449 DenseVector g{x_p.rows()};
450 g.segment(0, num_vars) = ζ * D_r * (x - x_r);
451 g.segment(num_vars, 2 * num_eq + 2 * num_ineq).setConstant(ρ);
452 return g.sparseView();
453 },
454 [&](const DenseVector& x_p, const DenseVector& y_p,
455 const DenseVector& z_p) -> SparseMatrix {
456 auto x = x_p.segment(0, num_vars);
457 const auto& y = y_p;
458 auto z = z_p.segment(0, num_ineq);
459
460 // Cost function Hessian
461 //
462 // [ζDᵣ 0 0 0 0]
463 // [ 0 0 0 0 0]
464 // [ 0 0 0 0 0]
465 // [ 0 0 0 0 0]
466 // [ 0 0 0 0 0]
467 gch::small_vector<Eigen::Triplet<Scalar>> triplets;
468 triplets.reserve(x_p.rows());
469 append_as_triplets(triplets, 0, 0, {SparseMatrix{ζ * D_r}});
470 SparseMatrix d2f_dx2{x_p.rows(), x_p.rows()};
471 d2f_dx2.setFromSortedTriplets(triplets.begin(), triplets.end());
472
473 // Constraint part of original problem's Lagrangian Hessian
474 //
475 // −∇ₓₓ²yᵀcₑ(x) − ∇ₓₓ²zᵀcᵢ(x)
476 auto H_c = matrices.H_c(x, y, z);
477 H_c.resize(x_p.rows(), x_p.rows());
478
479 // Lagrangian Hessian
480 //
481 // [ζDᵣ 0 0 0 0]
482 // [ 0 0 0 0 0]
483 // [ 0 0 0 0 0] − ∇ₓₓ²yᵀcₑ(x) − ∇ₓₓ²zᵀcᵢ(x)
484 // [ 0 0 0 0 0]
485 // [ 0 0 0 0 0]
486 return d2f_dx2 + H_c;
487 },
488 [&](const DenseVector& x_p, [[maybe_unused]] const DenseVector& y_p,
489 [[maybe_unused]] const DenseVector& z_p) -> SparseMatrix {
490 return SparseMatrix{x_p.rows(), x_p.rows()};
491 },
492 [&](const DenseVector& x_p) -> DenseVector {
493 auto x = x_p.segment(0, num_vars);
494 auto p_e = x_p.segment(num_vars, num_eq);
495 auto n_e = x_p.segment(num_vars + num_eq, num_eq);
496
497 // Equality constraints
498 //
499 // cₑ(x) - pₑ + nₑ = 0
500 return matrices.c_e(x) - p_e + n_e;
501 },
502 [&](const DenseVector& x_p) -> SparseMatrix {
503 auto x = x_p.segment(0, num_vars);
504
505 // Equality constraint Jacobian
506 //
507 // [Aₑ −I I 0 0]
508
509 SparseMatrix A_e = matrices.A_e(x);
510
511 gch::small_vector<Eigen::Triplet<Scalar>> triplets;
512 triplets.reserve(A_e.nonZeros() + 2 * num_eq);
513
514 append_as_triplets(triplets, 0, 0, {A_e});
515 append_diagonal_as_triplets(
516 triplets, 0, num_vars,
517 DenseVector::Constant(num_eq, Scalar(-1)).eval());
518 append_diagonal_as_triplets(
519 triplets, 0, num_vars + num_eq,
520 DenseVector::Constant(num_eq, Scalar(1)).eval());
521
522 SparseMatrix A_e_p{A_e.rows(), x_p.rows()};
523 A_e_p.setFromSortedTriplets(triplets.begin(), triplets.end());
524 return A_e_p;
525 },
526 [&](const DenseVector& x_p) -> DenseVector {
527 auto x = x_p.segment(0, num_vars);
528 auto p_i = x_p.segment(num_vars + 2 * num_eq, num_ineq);
529 auto n_i = x_p.segment(num_vars + 2 * num_eq + num_ineq, num_ineq);
530
531 // Inequality constraints
532 //
533 // cᵢ(x) - pᵢ + nᵢ ≥ 0
534 // pₑ ≥ 0
535 // nₑ ≥ 0
536 // pᵢ ≥ 0
537 // nᵢ ≥ 0
538 DenseVector c_i_p{c_i.rows() + 2 * num_eq + 2 * num_ineq};
539 c_i_p.segment(0, num_ineq) = matrices.c_i(x) - p_i + n_i;
540 c_i_p.segment(p_i.rows(), 2 * num_eq + 2 * num_ineq) =
541 x_p.segment(num_vars, 2 * num_eq + 2 * num_ineq);
542 return c_i_p;
543 },
544 [&](const DenseVector& x_p) -> SparseMatrix {
545 auto x = x_p.segment(0, num_vars);
546
547 // Inequality constraint Jacobian
548 //
549 // [Aᵢ 0 0 −I I]
550 // [0 I 0 0 0]
551 // [0 0 I 0 0]
552 // [0 0 0 I 0]
553 // [0 0 0 0 I]
554
555 SparseMatrix A_i = matrices.A_i(x);
556
557 gch::small_vector<Eigen::Triplet<Scalar>> triplets;
558 triplets.reserve(A_i.nonZeros() + 2 * num_eq + 4 * num_ineq);
559
560 // Column 0
561 append_as_triplets(triplets, 0, 0, {A_i});
562
563 // Columns 1 and 2
564 append_diagonal_as_triplets(
565 triplets, num_ineq, num_vars,
566 DenseVector::Constant(2 * num_eq, Scalar(1)).eval());
567
568 SparseMatrix I_ineq{
569 DenseVector::Constant(num_ineq, Scalar(1)).asDiagonal()};
570
571 // Column 3
572 SparseMatrix Z_col3{2 * num_eq, num_ineq};
573 append_as_triplets(triplets, 0, num_vars + 2 * num_eq,
574 {(-I_ineq).eval(), Z_col3, I_ineq});
575
576 // Column 4
577 SparseMatrix Z_col4{2 * num_eq + num_ineq, num_ineq};
578 append_as_triplets(triplets, 0, num_vars + 2 * num_eq + num_ineq,
579 {I_ineq, Z_col4, I_ineq});
580
581 SparseMatrix A_i_p{2 * num_eq + 3 * num_ineq, x_p.rows()};
582 A_i_p.setFromSortedTriplets(triplets.begin(), triplets.end());
583 return A_i_p;
584 },
585 fr_scaling};
586
587 auto status = interior_point<Scalar>(fr_matrix_callbacks, iteration_callbacks,
588 options, true,
589#ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
590 {},
591#endif
592 fr_x, fr_s, fr_y, fr_z, fr_μ);
593
594 x = fr_x.segment(0, x.rows());
595 s = fr_s.segment(0, s.rows());
596
597 if (status == ExitStatus::CALLBACK_REQUESTED_STOP) {
598 auto g = matrices.g(x);
599 auto A_e = matrices.A_e(x);
600 auto A_i = matrices.A_i(x);
601
602 auto [y_estimate, z_estimate] =
603 lagrange_multiplier_estimate(g, A_e, A_i, s, μ);
604 y = y_estimate;
605 z = z_estimate;
606
607 return ExitStatus::SUCCESS;
608 } else if (status == ExitStatus::SUCCESS) {
609 return ExitStatus::LOCALLY_INFEASIBLE;
610 } else {
611 return ExitStatus::FEASIBILITY_RESTORATION_FAILED;
612 }
613}
614
615} // namespace slp
616
617#include "sleipnir/optimization/solver/interior_point.hpp"