proxsuite 0.6.7
The Advanced Proximal Optimization Toolbox
Loading...
Searching...
No Matches
ruiz.hpp
Go to the documentation of this file.
1//
2// Copyright (c) 2022 INRIA
3//
7#ifndef PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP
8#define PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP
9
14#include <ostream>
15#include <iostream>
16#include <Eigen/Core>
17
18namespace proxsuite {
19namespace proxqp {
20enum struct Symmetry
21{
22 general,
23 lower,
24 upper,
25};
26namespace dense {
27namespace detail {
28
29template<typename T>
30auto
32 VectorViewMut<T> delta_,
33 std::ostream* logger_ptr,
35 T epsilon,
36 isize max_iter,
37 bool preconditioning_for_infeasible_problems,
38 Symmetry sym,
40 const bool box_constraints,
42{
43 T c(1);
44 auto S = delta_.to_eigen();
45 auto H = qp.H.to_eigen();
46 auto g = qp.g.to_eigen();
47 auto A = qp.A.to_eigen();
48 auto b = qp.b.to_eigen();
49 auto C = qp.C.to_eigen();
50 auto u = qp.u.to_eigen();
51 auto l = qp.l.to_eigen();
52
53 auto l_box = qp.l_box.to_eigen();
54 auto u_box = qp.u_box.to_eigen();
55 auto i_scaled = qp.I.to_eigen();
56 i_scaled.setOnes();
57
58 static constexpr T machine_eps = std::numeric_limits<T>::epsilon();
59 /*
60 * compute equilibration parameters and scale in place the qp following
61 * algorithm
62 *
63 * modified: removed g in gamma computation
64 */
65
66 isize n = qp.H.rows;
67 isize n_eq = qp.A.rows;
68 isize n_in = qp.C.rows;
69 isize n_constraints(n_in);
70 if (box_constraints) {
71 n_constraints += n;
72 }
73
74 T gamma = T(1);
75 LDLT_TEMP_VEC(T, delta, n + n_eq + n_constraints, stack);
76 i64 iter = 1;
77 while (infty_norm((1 - delta.array()).matrix()) > epsilon) {
78
79 if (logger_ptr != nullptr) {
80 *logger_ptr //
81 << "j : " //
82 << iter //
83 << " ; error : " //
84 << infty_norm((1 - delta.array()).matrix()) //
85 << "\n\n";
86 }
87 if (iter == max_iter) {
88 break;
89 } else {
90 ++iter;
91 }
92 // normalization vector
93 {
94 switch (HessianType) {
96 for (isize k = 0; k < n; ++k) {
97 T aux = sqrt(std::max({ n_eq > 0 ? infty_norm(A.col(k)) : T(0),
98 n_in > 0 ? infty_norm(C.col(k)) : T(0),
99 box_constraints ? i_scaled[k] : T(0) }));
100 if (aux == T(0)) {
101 delta(k) = T(1);
102 } else {
103 delta(k) = T(1) / (aux + machine_eps);
104 }
105 }
106 break;
108 for (isize k = 0; k < n; ++k) {
109 switch (sym) {
110 case Symmetry::upper: { // upper triangular part
111 T aux =
112 sqrt(std::max({ infty_norm(H.col(k).head(k)),
113 infty_norm(H.row(k).tail(n - k)),
114 n_eq > 0 ? infty_norm(A.col(k)) : T(0),
115 n_in > 0 ? infty_norm(C.col(k)) : T(0),
116 box_constraints ? i_scaled[k] : T(0) }));
117 if (aux == T(0)) {
118 delta(k) = T(1);
119 } else {
120 delta(k) = T(1) / (aux + machine_eps);
121 }
122 break;
123 }
124 case Symmetry::lower: { // lower triangular part
125
126 T aux =
127 sqrt(std::max({ infty_norm(H.col(k).head(k)),
128 infty_norm(H.col(k).tail(n - k)),
129 n_eq > 0 ? infty_norm(A.col(k)) : T(0),
130 n_in > 0 ? infty_norm(C.col(k)) : T(0),
131 box_constraints ? i_scaled[k] : T(0) }));
132 if (aux == T(0)) {
133 delta(k) = T(1);
134 } else {
135 delta(k) = T(1) / (aux + machine_eps);
136 }
137 break;
138 }
139 case Symmetry::general: {
140
141 T aux =
142 sqrt(std::max({ infty_norm(H.col(k)),
143 n_eq > 0 ? infty_norm(A.col(k)) : T(0),
144 n_in > 0 ? infty_norm(C.col(k)) : T(0),
145 box_constraints ? i_scaled[k] : T(0) }));
146 if (aux == T(0)) {
147 delta(k) = T(1);
148 } else {
149 delta(k) = T(1) / (aux + machine_eps);
150 }
151 break;
152 }
153 }
154 }
155 break;
157 for (isize k = 0; k < n; ++k) {
158 T aux = sqrt(std::max({ std::abs(H(k, k)),
159 n_eq > 0 ? infty_norm(A.col(k)) : T(0),
160 n_in > 0 ? infty_norm(C.col(k)) : T(0),
161 box_constraints ? i_scaled[k] : T(0) }));
162 if (aux == T(0)) {
163 delta(k) = T(1);
164 } else {
165 delta(k) = T(1) / (aux + machine_eps);
166 }
167 }
168 break;
169 }
170 if (preconditioning_for_infeasible_problems) {
171 delta.tail(n_eq + n_constraints).setOnes();
172 } else {
173 for (isize k = 0; k < n_eq; ++k) {
174 T aux = sqrt(infty_norm(A.row(k)));
175 if (aux == T(0)) {
176 delta(n + k) = T(1);
177 } else {
178 delta(n + k) = T(1) / (aux + machine_eps);
179 }
180 }
181 for (isize k = 0; k < n_in; ++k) {
182 T aux = sqrt(infty_norm(C.row(k)));
183 if (aux == T(0)) {
184 delta(k + n + n_eq) = T(1);
185 } else {
186 delta(k + n + n_eq) = T(1) / (aux + machine_eps);
187 }
188 }
189 if (box_constraints) {
190 for (isize k = 0; k < n; ++k) {
191 delta(k + n + n_eq + n_in) = T(1) / sqrt(i_scaled[k] + machine_eps);
192 }
193 }
194 }
195 // removed as non deterministic when using avx
196 // https://gitlab.com/libeigen/eigen/-/issues/1728
197 // if (preconditioning_for_infeasible_problems) {
198 // T mean = delta.segment(n, n_eq_in).mean();
199 // delta.segment(n,n_eq_in).setConstant(mean);
200 // }
201 }
202 {
203
204 // normalize A and C
205 A = delta.segment(n, n_eq).asDiagonal() * A * delta.head(n).asDiagonal();
206 C = delta.segment(n + n_eq, n_in).asDiagonal() * C *
207 delta.head(n).asDiagonal();
208 if (box_constraints) {
209 i_scaled.array() *= delta.head(n).array();
210 i_scaled.array() *= delta.tail(n).array();
211 u_box.array() *= delta.tail(n).array();
212 l_box.array() *= delta.tail(n).array();
213 }
214 // normalize vectors
215 g.array() *= delta.head(n).array();
216 b.array() *= delta.segment(n, n_eq).array();
217 u.array() *= delta.segment(n + n_eq, n_in).array();
218 l.array() *= delta.segment(n + n_eq, n_in).array();
219
220 // normalize H
221 switch (HessianType) {
223 break;
225 switch (sym) {
226 case Symmetry::upper: {
227 // upper triangular part
228 for (isize j = 0; j < n; ++j) {
229 H.col(j).head(j + 1) *= delta(j);
230 }
231 // normalisation des lignes
232 for (isize i = 0; i < n; ++i) {
233 H.row(i).tail(n - i) *= delta(i);
234 }
235 break;
236 }
237 case Symmetry::lower: {
238 // lower triangular part
239 for (isize j = 0; j < n; ++j) {
240 H.col(j).tail(n - j) *= delta(j);
241 }
242 // normalisation des lignes
243 for (isize i = 0; i < n; ++i) {
244 H.row(i).head(i + 1) *= delta(i);
245 }
246 break;
247 }
248 case Symmetry::general: {
249 // all matrix
250 H = delta.head(n).asDiagonal() * H * delta.head(n).asDiagonal();
251 break;
252 }
253 default:
254 break;
255 }
256 // additional normalization for the cost function
257 switch (sym) {
258 case Symmetry::upper: {
259 // upper triangular part
260 T tmp = T(0);
261 for (isize j = 0; j < n; ++j) {
262 tmp += proxqp::dense::infty_norm(H.row(j).tail(n - j));
263 }
264 gamma = 1 / std::max(tmp / T(n), T(1));
265 break;
266 }
267 case Symmetry::lower: {
268 // lower triangular part
269 T tmp = T(0);
270 for (isize j = 0; j < n; ++j) {
271 tmp += proxqp::dense::infty_norm(H.col(j).tail(n - j));
272 }
273 gamma = 1 / std::max(tmp / T(n), T(1));
274 break;
275 }
276 case Symmetry::general: {
277 // all matrix
278 gamma =
279 1 / std::max(
280 T(1),
281 (H.colwise().template lpNorm<Eigen::Infinity>()).mean());
282 break;
283 }
284 default:
285 break;
286 }
287 break;
289 // H = delta.head(n).asDiagonal() * H.asDiagonal() *
290 // delta.head(n).asDiagonal();
291 H.diagonal().array() *=
292 delta.head(n)
293 .array(); //* H.asDiagonal() * delta.head(n).asDiagonal();
294 H.diagonal().array() *=
295 delta.head(n)
296 .array(); //* H.asDiagonal() * delta.head(n).asDiagonal();
297 gamma =
298 1 /
299 std::max(T(1),
300 (H.diagonal().template lpNorm<Eigen::Infinity>()) / T(n));
301 H *= gamma;
302 break;
303 }
304 g *= gamma;
305
306 S.array() *= delta.array(); // coefficientwise product
307 c *= gamma;
308 }
309 }
310 return c;
311}
312} // namespace detail
313
314namespace preconditioner {
315
316template<typename T>
318{
320 T c;
321 isize dim;
322 isize n_eq;
323 isize n_in;
327
328 std::ostream* logger_ptr = nullptr;
340 explicit RuizEquilibration(isize dim_,
341 isize n_eq_,
342 isize n_in_,
343 bool box_constraints,
344 T epsilon_ = T(1e-3),
345 i64 max_iter_ = 10,
347 std::ostream* logger = nullptr)
348 : delta(Vec<T>::Ones(dim_ + n_eq_ + n_in_ + (box_constraints ? dim_ : 0)))
349 , c(1)
350 , dim(dim_)
351 , n_eq(n_eq_)
352 , n_in(n_in_)
353 , epsilon(epsilon_)
354 , max_iter(max_iter_)
355 , sym(sym_)
356 , logger_ptr(logger)
357 {
358 }
362 void print() const
363 {
364 // CHANGE: endl to newline
365 *logger_ptr << " delta : " << delta << "\n\n";
366 *logger_ptr << " c : " << c << "\n\n";
367 }
376 isize n,
377 isize n_eq,
378 isize n_in,
379 bool box_constraints)
381 {
382 if (box_constraints) {
383 return proxsuite::linalg::dense::temp_vec_req(tag, 2 * n + n_eq + n_in);
384 } else {
386 }
387 }
388
389 // H_new = c * head @ H @ head
390 // A_new = tail @ A @ head
391 // g_new = c * head @ g
392 // b_new = tail @ b
404 bool execute_preconditioner,
405 bool preconditioning_for_infeasible_problems,
406 const isize max_iter,
407 const T epsilon,
409 const bool box_constraints,
411 {
412 if (execute_preconditioner) {
413 delta.setOnes();
414 c =
415 detail::ruiz_scale_qp_in_place({ proxqp::from_eigen, delta },
417 qp,
418 epsilon,
419 max_iter,
420 preconditioning_for_infeasible_problems,
421 sym,
423 box_constraints,
424 stack);
425 } else {
426
427 auto H = qp.H.to_eigen();
428 auto g = qp.g.to_eigen();
429 auto A = qp.A.to_eigen();
430 auto b = qp.b.to_eigen();
431 auto C = qp.C.to_eigen();
432 auto u = qp.u.to_eigen();
433 auto l = qp.l.to_eigen();
434 auto l_box = qp.l_box.to_eigen();
435 auto u_box = qp.u_box.to_eigen();
436 auto i_scaled = qp.I.to_eigen(); // it is a vector
437 isize n = qp.H.rows;
438 isize n_eq = qp.A.rows;
439 isize n_in = qp.C.rows;
440
441 // normalize A and C
442 A = delta.segment(n, n_eq).asDiagonal() * A * delta.head(n).asDiagonal();
443 C = delta.segment(n + n_eq, n_in).asDiagonal() * C *
444 delta.head(n).asDiagonal();
445
446 // normalize H
447 switch (HessianType) {
449 switch (sym) {
450 case Symmetry::upper: {
451 // upper triangular part
452 for (isize j = 0; j < n; ++j) {
453 H.col(j).head(j + 1) *= delta(j);
454 }
455 // normalisation des lignes
456 for (isize i = 0; i < n; ++i) {
457 H.row(i).tail(n - i) *= delta(i);
458 }
459 break;
460 }
461 case Symmetry::lower: {
462 // lower triangular part
463 for (isize j = 0; j < n; ++j) {
464 H.col(j).tail(n - j) *= delta(j);
465 }
466 // normalisation des lignes
467 for (isize i = 0; i < n; ++i) {
468 H.row(i).head(i + 1) *= delta(i);
469 }
470 break;
471 }
472 case Symmetry::general: {
473 // all matrix
474 H = delta.head(n).asDiagonal() * H * delta.head(n).asDiagonal();
475 break;
476 }
477 default:
478 break;
479 }
480 break;
481
483 break;
485 // H = delta.head(n).asDiagonal() * H.asDiagonal() *
486 // delta.head(n).asDiagonal();
487 H.diagonal().array() *=
488 delta.head(n)
489 .array(); //* H.asDiagonal() * delta.head(n).asDiagonal();
490 H.diagonal().array() *=
491 delta.head(n)
492 .array(); //* H.asDiagonal() * delta.head(n).asDiagonal();
493 break;
494 }
495
496 // normalize vectors
497 g.array() *= delta.head(n).array();
498 b.array() *= delta.segment(n, n_eq).array();
499 l.array() *= delta.segment(n + n_eq, n_in).array();
500 u.array() *= delta.segment(n + n_eq, n_in).array();
501
502 if (box_constraints) {
503 u_box.array() *= delta.tail(n).array();
504 l_box.array() *= delta.tail(n).array();
505 i_scaled.array() *= delta.tail(n).array();
506 i_scaled.array() *= delta.head(n).array();
507 }
508
509 g *= c;
510 H *= c;
511 }
512 }
513 // modifies variables in place
519 {
520 primal.to_eigen().array() /= delta.array().head(dim);
521 }
528 {
529 dual.to_eigen().array() = dual.as_const().to_eigen().array() /
530 delta.tail(delta.size() - dim).array() * c;
531 }
537 {
538 dual.to_eigen().array() = dual.as_const().to_eigen().array() /
539 delta.middleRows(dim, n_eq).array() * c;
540 }
546 {
547 dual.to_eigen().array() = dual.as_const().to_eigen().array() /
548 delta.segment(dim + n_eq, n_in).array() * c;
549 }
555 {
556 primal.to_eigen().array() *= delta.array().head(dim);
557 }
563 {
564 dual.to_eigen().array() = dual.as_const().to_eigen().array() *
565 delta.tail(delta.size() - dim).array() / c;
566 }
572 {
573 dual.to_eigen().array() =
574 delta.tail(dim).array() * dual.as_const().to_eigen().array() / c;
575 }
581 {
582 dual.to_eigen().array() =
583 dual.as_const().to_eigen().array() / delta.tail(dim).array() * c;
584 }
590 {
591 dual.to_eigen().array() = dual.as_const().to_eigen().array() *
592 delta.middleRows(dim, n_eq).array() / c;
593 }
599 {
600 dual.to_eigen().array() = dual.as_const().to_eigen().array() *
601 delta.segment(dim + n_eq, n_in).array() / c;
602 }
603 // modifies residuals in place
610 {
611 primal.to_eigen().array() *= delta.tail(delta.size() - dim).array();
612 }
613
619 {
620 primal_eq.to_eigen().array() *= delta.middleRows(dim, n_eq).array();
621 }
627 {
628 primal_in.to_eigen().array() *= delta.segment(dim + n_eq, n_in).array();
629 }
635 {
636 primal_in.to_eigen().array() *= delta.tail(dim).array();
637 }
643 {
644 dual.to_eigen().array() *= delta.head(dim).array() * c;
645 }
652 {
653 primal.to_eigen().array() /= delta.tail(delta.size() - dim).array();
654 }
660 {
661 primal.to_eigen().array() /= delta.tail(dim).array();
662 }
668 {
669 primal_eq.to_eigen().array() /= delta.middleRows(dim, n_eq).array();
670 }
676 {
677 primal_in.to_eigen().array() /= delta.middleRows(dim + n_eq, n_in).array();
678 }
684 {
685 primal_in.to_eigen().array() /= delta.tail(dim).array();
686 }
692 {
693 dual.to_eigen().array() /= delta.head(dim).array() * c;
694 }
695};
696
697template<typename T>
698bool
700{
701 bool value =
702 // ruiz1.delta == ruiz2.delta &&
703 ruiz1.c == ruiz2.c
704 // ruiz1.dim == ruiz2.dim
705 // ruiz1.epsilon == ruiz2.epsilon &&
706 // ruiz1.max_iter == ruiz2.max_iter &&
707 // ruiz1.sym == ruiz2.sym &&
708 // ruiz1.logger_ptr == ruiz2.logger_ptr
709 ;
710 return value;
711}
712
713template<typename T>
714bool
716{
717 return !(ruiz1 == ruiz2);
718}
719
720} // namespace preconditioner
721} // namespace dense
722} // namespace proxqp
723} // namespace proxsuite
724
725#endif /* end of include guard PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP */
#define LDLT_TEMP_VEC(Type, Name, Rows, Stack)
Definition core.hpp:74
auto temp_vec_req(proxsuite::linalg::veg::Tag< T >, isize rows) noexcept -> proxsuite::linalg::veg::dynstack::StackReq
Definition core.hpp:862
auto ruiz_scale_qp_in_place(VectorViewMut< T > delta_, std::ostream *logger_ptr, QpViewBoxMut< T > qp, T epsilon, isize max_iter, bool preconditioning_for_infeasible_problems, Symmetry sym, HessianType HessianType, const bool box_constraints, proxsuite::linalg::veg::dynstack::DynStackMut stack) -> T
Definition ruiz.hpp:31
bool operator!=(const RuizEquilibration< T > &ruiz1, const RuizEquilibration< T > &ruiz2)
Definition ruiz.hpp:715
bool operator==(const RuizEquilibration< T > &ruiz1, const RuizEquilibration< T > &ruiz2)
Definition ruiz.hpp:699
Eigen::Matrix< T, DYN, 1 > Vec
Definition fwd.hpp:24
VEG_INLINE auto to_eigen() const noexcept -> detail::EigenMatMapMut< T, L >
Definition views.hpp:1014
VEG_INLINE auto to_eigen() const -> detail::VecMapMut< T >
Definition views.hpp:677
VEG_INLINE auto as_const() const noexcept -> VectorView< T >
Definition views.hpp:656
MatrixViewMut< Scalar, layout > C
Definition views.hpp:1404
VectorViewMut< Scalar > u_box
Definition views.hpp:1409
MatrixViewMut< Scalar, layout > A
Definition views.hpp:1402
MatrixViewMut< Scalar, layout > H
Definition views.hpp:1399
VectorViewMut< Scalar > l_box
Definition views.hpp:1408
void unscale_primal_residual_in_place(VectorViewMut< T > primal) const
Definition ruiz.hpp:651
void unscale_primal_in_place(VectorViewMut< T > primal) const
Definition ruiz.hpp:554
static auto scale_qp_in_place_req(proxsuite::linalg::veg::Tag< T > tag, isize n, isize n_eq, isize n_in, bool box_constraints) -> proxsuite::linalg::veg::dynstack::StackReq
Definition ruiz.hpp:375
void scale_primal_residual_in_place_eq(VectorViewMut< T > primal_eq) const
Definition ruiz.hpp:618
void scale_box_dual_in_place_in(VectorViewMut< T > dual) const
Definition ruiz.hpp:580
void unscale_dual_in_place_in(VectorViewMut< T > dual) const
Definition ruiz.hpp:598
void unscale_primal_residual_in_place_in(VectorViewMut< T > primal_in) const
Definition ruiz.hpp:675
RuizEquilibration(isize dim_, isize n_eq_, isize n_in_, bool box_constraints, T epsilon_=T(1e-3), i64 max_iter_=10, Symmetry sym_=Symmetry::general, std::ostream *logger=nullptr)
Definition ruiz.hpp:340
void scale_primal_in_place(VectorViewMut< T > primal) const
Definition ruiz.hpp:518
void unscale_dual_in_place_eq(VectorViewMut< T > dual) const
Definition ruiz.hpp:589
void unscale_primal_residual_in_place_eq(VectorViewMut< T > primal_eq) const
Definition ruiz.hpp:667
void unscale_box_primal_residual_in_place(VectorViewMut< T > primal) const
Definition ruiz.hpp:659
void scale_dual_in_place(VectorViewMut< T > dual) const
Definition ruiz.hpp:527
void scale_primal_residual_in_place(VectorViewMut< T > primal) const
Definition ruiz.hpp:609
void unscale_dual_in_place(VectorViewMut< T > dual) const
Definition ruiz.hpp:562
void unscale_dual_residual_in_place(VectorViewMut< T > dual) const
Definition ruiz.hpp:691
void scale_dual_residual_in_place(VectorViewMut< T > dual) const
Definition ruiz.hpp:642
void scale_dual_in_place_in(VectorViewMut< T > dual) const
Definition ruiz.hpp:545
void scale_primal_residual_in_place_in(VectorViewMut< T > primal_in) const
Definition ruiz.hpp:626
void scale_dual_in_place_eq(VectorViewMut< T > dual) const
Definition ruiz.hpp:536
void scale_box_primal_residual_in_place_in(VectorViewMut< T > primal_in) const
Definition ruiz.hpp:634
void unscale_box_dual_in_place_in(VectorViewMut< T > dual) const
Definition ruiz.hpp:571
void scale_qp_in_place(QpViewBoxMut< T > qp, bool execute_preconditioner, bool preconditioning_for_infeasible_problems, const isize max_iter, const T epsilon, const HessianType &HessianType, const bool box_constraints, proxsuite::linalg::veg::dynstack::DynStackMut stack)
Definition ruiz.hpp:403
void unscale_box_primal_residual_in_place_in(VectorViewMut< T > primal_in) const
Definition ruiz.hpp:683