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