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