Sleipnir C++ API
Loading...
Searching...
No Matches
kkt_error.hpp
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6
7#include <Eigen/Core>
8#include <Eigen/SparseCore>
9
10#include "sleipnir/optimization/solver/util/problem_scaling.hpp"
11
12// See docs/algorithms.md#Works_cited for citation definitions
13
14namespace slp {
15
17enum class KKTErrorType {
19 INF_NORM_SCALED,
21 ONE_NORM
22};
23
29template <typename Scalar, KKTErrorType T>
30Scalar kkt_error(const Eigen::Vector<Scalar, Eigen::Dynamic>& g) {
31 // The KKT conditions from docs/algorithms.md:
32 //
33 // ∇f = 0
34
35 if constexpr (T == KKTErrorType::INF_NORM_SCALED) {
36 return g.template lpNorm<Eigen::Infinity>();
37 } else if constexpr (T == KKTErrorType::ONE_NORM) {
38 return g.template lpNorm<1>();
39 }
40}
41
50template <typename Scalar, KKTErrorType T>
51Scalar kkt_error(const Eigen::Vector<Scalar, Eigen::Dynamic>& g,
52 const Eigen::SparseMatrix<Scalar>& A_e,
53 const Eigen::Vector<Scalar, Eigen::Dynamic>& c_e,
54 const Eigen::Vector<Scalar, Eigen::Dynamic>& y) {
55 // The KKT conditions from docs/algorithms.md:
56 //
57 // ∇f − Aₑᵀy = 0
58 // cₑ = 0
59
60 if constexpr (T == KKTErrorType::INF_NORM_SCALED) {
61 // See equation (5) of [2].
62
63 // s_d = max(sₘₐₓ, ‖y‖₁ / m) / sₘₐₓ
64 constexpr Scalar s_max(100);
65 Scalar s_d =
66 std::max(s_max, y.template lpNorm<1>() / Scalar(y.rows())) / s_max;
67
68 // ‖∇f − Aₑᵀy‖_∞ / s_d
69 // ‖cₑ‖_∞
70 return std::max(
71 {(g - A_e.transpose() * y).template lpNorm<Eigen::Infinity>() / s_d,
72 c_e.template lpNorm<Eigen::Infinity>()});
73 } else if constexpr (T == KKTErrorType::ONE_NORM) {
74 return (g - A_e.transpose() * y).template lpNorm<1>() +
75 c_e.template lpNorm<1>();
76 }
77}
78
92template <typename Scalar, KKTErrorType T>
93Scalar kkt_error(const Eigen::Vector<Scalar, Eigen::Dynamic>& g,
94 const Eigen::SparseMatrix<Scalar>& A_e,
95 const Eigen::Vector<Scalar, Eigen::Dynamic>& c_e,
96 const Eigen::SparseMatrix<Scalar>& A_i,
97 const Eigen::Vector<Scalar, Eigen::Dynamic>& c_i,
98 const Eigen::Vector<Scalar, Eigen::Dynamic>& s,
99 const Eigen::Vector<Scalar, Eigen::Dynamic>& y,
100 const Eigen::Vector<Scalar, Eigen::Dynamic>& z, Scalar μ) {
101 // The KKT conditions from docs/algorithms.md:
102 //
103 // ∇f − Aₑᵀy − Aᵢᵀz = 0
104 // Sz − μe = 0
105 // cₑ = 0
106 // cᵢ − s = 0
107
108 if constexpr (T == KKTErrorType::INF_NORM_SCALED) {
109 // See equation (5) of [2].
110
111 // s_d = max(sₘₐₓ, (‖y‖₁ + ‖z‖₁) / (m + n)) / sₘₐₓ
112 constexpr Scalar s_max(100);
113 Scalar s_d =
114 std::max(s_max, (y.template lpNorm<1>() + z.template lpNorm<1>()) /
115 Scalar(y.rows() + z.rows())) /
116 s_max;
117
118 // s_c = max(sₘₐₓ, ‖z‖₁ / n) / sₘₐₓ
119 Scalar s_c =
120 std::max(s_max, z.template lpNorm<1>() / Scalar(z.rows())) / s_max;
121
122 const auto S = s.asDiagonal();
123 const Eigen::Vector<Scalar, Eigen::Dynamic> μe =
124 Eigen::Vector<Scalar, Eigen::Dynamic>::Constant(s.rows(), μ);
125
126 // ‖∇f − Aₑᵀy − Aᵢᵀz‖_∞ / s_d
127 // ‖Sz − μe‖_∞ / s_c
128 // ‖cₑ‖_∞
129 // ‖cᵢ − s‖_∞
130 return std::max({(g - A_e.transpose() * y - A_i.transpose() * z)
131 .template lpNorm<Eigen::Infinity>() /
132 s_d,
133 (S * z - μe).template lpNorm<Eigen::Infinity>() / s_c,
134 c_e.template lpNorm<Eigen::Infinity>(),
135 (c_i - s).template lpNorm<Eigen::Infinity>()});
136 } else if constexpr (T == KKTErrorType::ONE_NORM) {
137 const auto S = s.asDiagonal();
138 const Eigen::Vector<Scalar, Eigen::Dynamic> μe =
139 Eigen::Vector<Scalar, Eigen::Dynamic>::Constant(s.rows(), μ);
140
141 return (g - A_e.transpose() * y - A_i.transpose() * z)
142 .template lpNorm<1>() +
143 (S * z - μe).template lpNorm<1>() + c_e.template lpNorm<1>() +
144 (c_i - s).template lpNorm<1>();
145 }
146}
147
154template <typename Scalar, KKTErrorType T>
155Scalar unscaled_kkt_error(const ProblemScaling<Scalar>& scaling,
156 const Eigen::Vector<Scalar, Eigen::Dynamic>& g) {
157 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
158
159 if (scaling.is_identity()) {
160 return kkt_error<Scalar, T>(g);
161 }
162
163 const DenseVector g_unscaled = (Scalar(1) / scaling.f) * g;
164
165 return kkt_error<Scalar, T>(g_unscaled);
166}
167
177template <typename Scalar, KKTErrorType T>
178Scalar unscaled_kkt_error(const ProblemScaling<Scalar>& scaling,
179 const Eigen::Vector<Scalar, Eigen::Dynamic>& g,
180 const Eigen::SparseMatrix<Scalar>& A_e,
181 const Eigen::Vector<Scalar, Eigen::Dynamic>& c_e,
182 const Eigen::Vector<Scalar, Eigen::Dynamic>& y) {
183 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
184 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
185
186 if (scaling.is_identity()) {
187 return kkt_error<Scalar, T>(g, A_e, c_e, y);
188 }
189
190 const Scalar inv_d_f = Scalar(1) / scaling.f;
191 const DenseVector inv_d_c_e = scaling.c_e.cwiseInverse();
192
193 const DenseVector g_unscaled = inv_d_f * g;
194 const SparseMatrix A_e_unscaled = inv_d_c_e.asDiagonal() * A_e;
195 const DenseVector c_e_unscaled = inv_d_c_e.cwiseProduct(c_e);
196 const DenseVector y_unscaled = scaling.c_e.cwiseProduct(y) * inv_d_f;
197
198 return kkt_error<Scalar, T>(g_unscaled, A_e_unscaled, c_e_unscaled,
199 y_unscaled);
200}
201
216template <typename Scalar, KKTErrorType T>
217Scalar unscaled_kkt_error(const ProblemScaling<Scalar>& scaling,
218 const Eigen::Vector<Scalar, Eigen::Dynamic>& g,
219 const Eigen::SparseMatrix<Scalar>& A_e,
220 const Eigen::Vector<Scalar, Eigen::Dynamic>& c_e,
221 const Eigen::SparseMatrix<Scalar>& A_i,
222 const Eigen::Vector<Scalar, Eigen::Dynamic>& c_i,
223 const Eigen::Vector<Scalar, Eigen::Dynamic>& s,
224 const Eigen::Vector<Scalar, Eigen::Dynamic>& y,
225 const Eigen::Vector<Scalar, Eigen::Dynamic>& z,
226 Scalar μ) {
227 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
228 using SparseMatrix = Eigen::SparseMatrix<Scalar>;
229
230 if (scaling.is_identity()) {
231 return kkt_error<Scalar, T>(g, A_e, c_e, A_i, c_i, s, y, z, μ);
232 }
233
234 const Scalar inv_d_f = Scalar(1) / scaling.f;
235 const DenseVector inv_d_c_e = scaling.c_e.cwiseInverse();
236 const DenseVector inv_d_c_i = scaling.c_i.cwiseInverse();
237
238 const DenseVector g_unscaled = inv_d_f * g;
239 const SparseMatrix A_e_unscaled = inv_d_c_e.asDiagonal() * A_e;
240 const DenseVector c_e_unscaled = inv_d_c_e.cwiseProduct(c_e);
241 const SparseMatrix A_i_unscaled = inv_d_c_i.asDiagonal() * A_i;
242 const DenseVector c_i_unscaled = inv_d_c_i.cwiseProduct(c_i);
243 const DenseVector s_unscaled = inv_d_c_i.cwiseProduct(s);
244 const DenseVector y_unscaled = scaling.c_e.cwiseProduct(y) * inv_d_f;
245 const DenseVector z_unscaled = scaling.c_i.cwiseProduct(z) * inv_d_f;
246 const Scalar μ_unscaled = inv_d_f * μ;
247
248 return kkt_error<Scalar, T>(g_unscaled, A_e_unscaled, c_e_unscaled,
249 A_i_unscaled, c_i_unscaled, s_unscaled,
250 y_unscaled, z_unscaled, μ_unscaled);
251}
252
253} // namespace slp