Sleipnir C++ API
Loading...
Searching...
No Matches
lagrange_multiplier_estimate.hpp
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6#include <utility>
7
8#include <Eigen/Core>
9#include <Eigen/SparseCholesky>
10#include <Eigen/SparseCore>
11#include <gch/small_vector.hpp>
12
13#include "sleipnir/optimization/solver/util/append_as_triplets.hpp"
14
15namespace slp {
16
20template <typename Scalar>
23 Eigen::Vector<Scalar, Eigen::Dynamic> y;
25 Eigen::Vector<Scalar, Eigen::Dynamic> z;
26};
27
33template <typename Scalar>
34Eigen::Vector<Scalar, Eigen::Dynamic> lagrange_multiplier_estimate(
35 const Eigen::SparseVector<Scalar>& g,
36 const Eigen::SparseMatrix<Scalar>& A_e) {
37 // Lagrange multiplier estimates
38 //
39 // ∇f − Aₑᵀy = 0
40 // Aₑᵀy = ∇f
41 // y = (AₑAₑᵀ)⁻¹Aₑ∇f
42 return Eigen::SimplicialLDLT<Eigen::SparseMatrix<Scalar>>{A_e *
43 A_e.transpose()}
44 .solve(A_e * g);
45}
46
55template <typename Scalar>
56LagrangeMultiplierEstimate<Scalar> lagrange_multiplier_estimate(
57 const Eigen::SparseVector<Scalar>& g,
58 const Eigen::SparseMatrix<Scalar>& A_e,
59 const Eigen::SparseMatrix<Scalar>& A_i,
60 const Eigen::Vector<Scalar, Eigen::Dynamic>& s, Scalar μ) {
61 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
62 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
63
64 // Lagrange multiplier estimates
65 //
66 // ∇f − Aₑᵀy − Aᵢᵀz = 0
67 // Sz − μe = 0
68 //
69 // Aₑᵀy + Aᵢᵀz = ∇f
70 // −Sz = −μe
71 //
72 // [Aₑᵀ Aᵢᵀ][y] = [ ∇f]
73 // [ 0 −S ][z] [−μe]
74 //
75 // [Aₑ 0]ᵀ[y] = [ ∇f]
76 // [Aᵢ −S] [z] [−μe]
77 //
78 // Let  = [Aₑ 0]
79 // [Aᵢ −S]
80 //
81 // Âᵀ[y] = [ ∇f]
82 // [z] [−μe]
83 //
84 // [y] = (ÂÂᵀ)⁻¹Â[ ∇f]
85 // [z] [−μe]
86
87 gch::small_vector<Eigen::Triplet<Scalar>> triplets;
88
89 // Â = [Aₑ 0]
90 // [Aᵢ −S]
91 triplets.reserve(A_e.nonZeros() + A_i.nonZeros() + s.rows());
92 append_as_triplets(triplets, 0, 0, {A_e, A_i});
93 append_diagonal_as_triplets(triplets, A_e.rows(), A_i.cols(), (-s).eval());
94 SparseMatrix A_hat{A_e.rows() + A_i.rows(), A_e.cols() + s.rows()};
95 A_hat.setFromSortedTriplets(triplets.begin(), triplets.end());
96
97 // lhs = ÂÂᵀ
98 SparseMatrix lhs = A_hat * A_hat.transpose();
99
100 // rhs = Â[ ∇f]
101 // [−μe]
102 DenseVector rhs_temp{g.rows() + s.rows()};
103 rhs_temp.segment(0, g.rows()) = g;
104 rhs_temp.segment(g.rows(), s.rows()).setConstant(-μ);
105 DenseVector rhs = A_hat * rhs_temp;
106
107 Eigen::SimplicialLDLT<SparseMatrix> yz_estimator{lhs};
108 DenseVector sol = yz_estimator.solve(rhs);
109 DenseVector y = sol.segment(0, A_e.rows());
110 DenseVector z = sol.segment(A_e.rows(), s.rows());
111
112 // A requirement for the convergence proof is that the primal-dual barrier
113 // term Hessian Σₖ₊₁ does not deviate arbitrarily much from the primal barrier
114 // term Hessian μSₖ₊₁⁻².
115 //
116 // Σₖ₊₁ = μSₖ₊₁⁻²
117 // Sₖ₊₁⁻¹Zₖ₊₁ = μSₖ₊₁⁻²
118 // Zₖ₊₁ = μSₖ₊₁⁻¹
119 //
120 // We ensure this by resetting
121 //
122 // zₖ₊₁ = clamp(zₖ₊₁, 1/κ_Σ μ/sₖ₊₁, κ_Σ μ/sₖ₊₁)
123 //
124 // for some fixed κ_Σ ≥ 1 after each step. See equation (16) of [2].
125 for (int row = 0; row < z.rows(); ++row) {
126 constexpr Scalar κ_Σ(1e10);
127 z[row] = std::clamp(z[row], Scalar(1) / κ_Σ * μ / s[row], κ_Σ * μ / s[row]);
128 }
129
130 return {std::move(y), std::move(z)};
131}
132
133} // namespace slp
Definition lagrange_multiplier_estimate.hpp:21
Eigen::Vector< Scalar, Eigen::Dynamic > y
Equality constraint dual estimate.
Definition lagrange_multiplier_estimate.hpp:23
Eigen::Vector< Scalar, Eigen::Dynamic > z
Inequality constraint dual estimate.
Definition lagrange_multiplier_estimate.hpp:25