proxsuite 0.6.7
The Advanced Proximal Optimization Toolbox
Loading...
Searching...
No Matches
helpers.hpp
Go to the documentation of this file.
1//
2// Copyright (c) 2022 INRIA
3//
5#ifndef PROXSUITE_PROXQP_SPARSE_HELPERS_HPP
6#define PROXSUITE_PROXQP_SPARSE_HELPERS_HPP
7
8#include <Eigen/Sparse>
10
13#include <iostream>
14namespace proxsuite {
15namespace proxqp {
16namespace sparse {
17
18template<typename T, typename I>
19T
22 sparse::Vec<T>& rhs,
23 sparse::Vec<T>& err_v,
24 T power_iteration_accuracy,
25 isize nb_power_iteration)
26{
27 // computes maximal eigen value of the bottom right matrix of the LDLT
28 isize dim = H.rows();
29 rhs.setZero();
30 // stores eigenvector
31 rhs.array() += 1. / std::sqrt(dim);
32 // stores Hx
33 dw.setZero();
34 // detail::noalias_symhiv_add(
35 // detail::vec_mut(dw), H, detail::vec_mut(rhs));
36 dw = H * rhs;
37 T eig = 0;
38 for (isize i = 0; i < nb_power_iteration; i++) {
39
40 rhs = dw / dw.norm();
41 dw.setZero();
42 // detail::noalias_symhiv_add(
43 // detail::vec_mut(dw), H, detail::vec_mut(rhs));
44 dw = H * rhs;
45 // calculate associated eigenvalue
46 eig = rhs.dot(dw);
47 // calculate associated error
48 err_v = dw - eig * rhs;
49 T err = proxsuite::proxqp::dense::infty_norm(err_v);
50 // std::cout << "power iteration max: i " << i << " err " << err <<
51 // std::endl;
52 if (err <= power_iteration_accuracy) {
53 break;
54 }
55 }
56 return eig;
57}
58template<typename T, typename I>
59T
62 sparse::Vec<T>& rhs,
63 sparse::Vec<T>& err_v,
64 T max_eigen_value,
65 T power_iteration_accuracy,
66 isize nb_power_iteration)
67{
68 // performs power iteration on the matrix: max_eigen_value I - H
69 // estimates then the minimal eigenvalue with: minimal_eigenvalue =
70 // max_eigen_value - eig
71 isize dim = H.rows();
72 rhs.setZero();
73 // stores eigenvector
74 rhs.array() += 1. / std::sqrt(dim);
75 // stores Hx
76 dw.setZero();
77 // does not work below with full symmetry...
78 // detail::noalias_symhiv_add(
79 // detail::vec_mut(dw), H, detail::vec_mut(rhs));
80 dw.noalias() = H * rhs;
81 dw.array() *= (-1.);
82 dw += max_eigen_value * rhs;
83 T eig = 0;
84 for (isize i = 0; i < nb_power_iteration; i++) {
85
86 rhs = dw / dw.norm();
87 dw.setZero();
88 // idem
89 // detail::noalias_symhiv_add(
90 // detail::vec_mut(dw), H, detail::vec_mut(rhs));
91 dw.noalias() = H * rhs;
92 dw.array() *= (-1.);
93 dw += max_eigen_value * rhs;
94 // calculate associated eigenvalue
95 eig = rhs.dot(dw);
96 // calculate associated error
97 err_v = dw - eig * rhs;
98 T err = proxsuite::proxqp::dense::infty_norm(err_v);
99 // std::cout << "power iteration min: i " << i << " err " << err <<
100 // std::endl;
101 if (err <= power_iteration_accuracy) {
102 break;
103 }
104 }
105 T minimal_eigenvalue = max_eigen_value - eig;
106 return minimal_eigenvalue;
107}
109
116template<typename T, typename I>
117T
119 T power_iteration_accuracy,
120 isize nb_power_iteration)
121{
123 (!H.isApprox(H.transpose(), std::numeric_limits<T>::epsilon())),
124 std::invalid_argument,
125 "H is not symmetric.");
127 H.rows(),
128 H.cols(),
129 "H has a number of rows different of the number of columns.");
130 isize dim = H.rows();
131 T res(0.);
132 sparse::Vec<T> dw(dim);
133 sparse::Vec<T> rhs(dim);
134 sparse::Vec<T> err(dim);
135 T dominant_eigen_value = power_iteration<T, I>(
136 H, dw, rhs, err, power_iteration_accuracy, nb_power_iteration);
137 T min_eigenvalue =
138 min_eigen_value_via_modified_power_iteration<T, I>(H,
139 dw,
140 rhs,
141 err,
142 dominant_eigen_value,
143 power_iteration_accuracy,
144 nb_power_iteration);
145 // std::cout << "dominant_eigen_value " << dominant_eigen_value<< "
146 // min_eigenvalue " << min_eigenvalue << std::endl;
147 res = std::min(min_eigenvalue, dominant_eigen_value);
148 return res;
149}
151
157template<typename T>
158void
160 optional<T> manual_minimal_H_eigenvalue,
161 Results<T>& results,
162 Settings<T>& settings)
163{
164 if (manual_minimal_H_eigenvalue != nullopt) {
166 manual_minimal_H_eigenvalue.value();
167 results.info.minimal_H_eigenvalue_estimate =
169 }
170 settings.default_rho += std::abs(results.info.minimal_H_eigenvalue_estimate);
171 results.info.rho = settings.default_rho;
172}
181template<typename T, typename I>
182void
184 Results<T>& results,
185 Workspace<T, I>& work,
186 optional<T> rho_new,
187 optional<T> mu_eq_new,
188 optional<T> mu_in_new)
189{
190 if (rho_new != nullopt) {
191 settings.default_rho = rho_new.value();
192 results.info.rho = rho_new.value();
194 }
195 if (mu_eq_new != nullopt) {
196 settings.default_mu_eq = mu_eq_new.value();
197 results.info.mu_eq = mu_eq_new.value();
198 results.info.mu_eq_inv = T(1) / results.info.mu_eq;
200 }
201 if (mu_in_new != nullopt) {
202 settings.default_mu_in = mu_in_new.value();
203 results.info.mu_in = mu_in_new.value();
204 results.info.mu_in_inv = T(1) / results.info.mu_in;
206 }
207}
217template<typename T, typename I>
218void
220 optional<VecRef<T>> y_wm,
221 optional<VecRef<T>> z_wm,
222 Results<T>& results,
223 Settings<T>& settings,
224 Model<T, I>& model)
225{
226 if (x_wm == nullopt && y_wm == nullopt && z_wm == nullopt)
227 return;
228
230
231 // first check problem dimensions
232 if (x_wm != nullopt) {
234 x_wm.value().rows(),
235 model.dim,
236 "the dimension wrt primal variable x for warm start is not valid.");
237 }
238
239 if (y_wm != nullopt) {
240 PROXSUITE_CHECK_ARGUMENT_SIZE(y_wm.value().rows(),
241 model.n_eq,
242 "the dimension wrt equality constrained "
243 "variables for warm start is not valid.");
244 }
245
246 if (z_wm != nullopt) {
248 z_wm.value().rows(),
249 model.n_in,
250 "the dimension wrt inequality constrained variables for warm start "
251 "is not valid.");
252 }
253
254 if (x_wm != nullopt) {
255 results.x = x_wm.value().eval();
256 }
257
258 if (y_wm != nullopt) {
259 results.y = y_wm.value().eval();
260 }
261
262 if (z_wm != nullopt) {
263 results.z = z_wm.value().eval();
264 }
265}
266
280template<typename T, typename I, typename P>
281void
283 Results<T>& results,
284 Model<T, I>& data,
285 Workspace<T, I>& work,
286 Settings<T>& settings,
287 P& precond,
288 PreconditionerStatus& preconditioner_status)
289{
290 isize n = qp.H.nrows();
291 isize n_eq = qp.AT.ncols();
292 isize n_in = qp.CT.ncols();
293
294 if (results.x.rows() != n) {
295 results.x.resize(n);
296 results.x.setZero();
297 }
298 if (results.y.rows() != n_eq) {
299 results.y.resize(n_eq);
300 results.y.setZero();
301 }
302 if (results.z.rows() != n_in) {
303 results.z.resize(n_in);
304 results.z.setZero();
305 }
306 if (work.active_inequalities.len() != n_in) {
307 work.active_inequalities.resize(n_in);
308 for (isize i = 0; i < n_in; ++i) {
309 work.active_inequalities[i] = false;
310 }
311 }
312 if (work.active_set_up.rows() != n_in) {
313 work.active_set_up.resize(n_in);
314 for (isize i = 0; i < n_in; ++i) {
315 work.active_set_up[i] = false;
316 }
317 }
318 if (work.active_set_low.rows() != n_in) {
319 work.active_set_low.resize(n_in);
320 for (isize i = 0; i < n_in; ++i) {
321 work.active_set_low[i] = false;
322 }
323 }
324 bool execute_preconditioner_or_not = false;
325 switch (preconditioner_status) {
327 execute_preconditioner_or_not = true;
328 break;
330 execute_preconditioner_or_not = false;
331 break;
333 // keep previous one
334 execute_preconditioner_or_not = false;
335 break;
336 }
337 // performs scaling according to options chosen + stored model value
338 work.setup_impl(
339 qp,
340 data,
341 settings,
342 execute_preconditioner_or_not,
343 precond,
344 P::scale_qp_in_place_req(proxsuite::linalg::veg::Tag<T>{}, n, n_eq, n_in));
345 switch (settings.initial_guess) { // the following is used when initiliazing
346 // the Qp object or updating it
348
351 } else {
352 results.cleanup(settings);
353 }
354 break;
355 }
357 // keep solutions but restart workspace and results
358
360 results.cleanup_statistics();
361 } else {
362 results.cold_start(settings);
363 }
364 break;
365 }
367
370 } else {
371 results.cleanup(settings);
372 }
373 break;
374 }
376
379 } else {
380 results.cleanup(settings);
381 }
382 break;
383 }
385 // keep workspace and results solutions except statistics
386
387 results.cleanup_statistics(); // always keep prox parameters (changed or
388 // previous ones)
389 break;
390 }
391 }
392 // if user chose Automatic as sparse backend, store in results which backend
393 // of SparseCholesky or MatrixFree had been used
395 if (work.internal.do_ldlt) {
396 results.info.sparse_backend = SparseBackend::SparseCholesky;
397 } else {
398 results.info.sparse_backend = SparseBackend::MatrixFree;
399 }
400 }
401 // if user selected a specfic sparse backend, store it in results
402 else {
403 results.info.sparse_backend = settings.sparse_backend;
404 }
405}
412template<typename T, typename I>
413auto
416{
417 if (a.nrows() != b.nrows())
418 return false;
419 if (a.ncols() != b.ncols())
420 return false;
421 for (usize j = 0; j < static_cast<usize>(a.ncols()); ++j) {
422 usize n_elems(a.col_end(j) - a.col_start(j));
423 usize n_elems_to_compare(b.col_end(j) - b.col_start(j));
424 if (n_elems != n_elems_to_compare)
425 return false;
426 for (usize p = 0; p < n_elems; ++p) {
427 isize i_a = a.row_indices()[a.col_start(j) + p];
428 isize i_b = b.row_indices()[b.col_start(j) + p];
429 if (i_a != i_b)
430 return false;
431 }
432 }
433 return true;
434}
441template<typename T, typename I>
442void
445{
446 // assume same sparsity structure for a and b
447 // copy b into a
448 for (usize j = 0; j < static_cast<usize>(a.ncols()); ++j) {
449 auto a_start = a.values_mut() + a.col_start(j);
450 auto b_start = b.values() + b.col_start(j);
451
452 usize n_elems = static_cast<usize>(a.col_end(j) - a.col_start(j));
453
454 for (usize p = 0; p < n_elems; ++p) {
455 a_start[p] = b_start[p];
456 }
457 }
458}
459
460} // namespace sparse
461} // namespace proxqp
462} // namespace proxsuite
463
464#endif /* end of include guard PROXSUITE_PROXQP_SPARSE_HELPERS_HPP */
TL_OPTIONAL_11_CONSTEXPR T & value() &
#define PROXSUITE_CHECK_ARGUMENT_SIZE(size, expected_size, message)
Definition macros.hpp:28
#define PROXSUITE_THROW_PRETTY(condition, exception, message)
Definition macros.hpp:18
void warm_start(optional< VecRef< T > > x_wm, optional< VecRef< T > > y_wm, optional< VecRef< T > > z_wm, Results< T > &results, Settings< T > &settings, Model< T, I > &model)
Definition helpers.hpp:219
T estimate_minimal_eigen_value_of_symmetric_matrix(SparseMat< T, I > &H, T power_iteration_accuracy, isize nb_power_iteration)
Definition helpers.hpp:118
void update_proximal_parameters(Settings< T > &settings, Results< T > &results, Workspace< T, I > &work, optional< T > rho_new, optional< T > mu_eq_new, optional< T > mu_in_new)
Definition helpers.hpp:183
Eigen::SparseMatrix< T, Eigen::ColMajor, I > SparseMat
Definition fwd.hpp:31
void copy(proxsuite::linalg::sparse::MatMut< T, I > a, proxsuite::linalg::sparse::MatRef< T, I > b)
Definition helpers.hpp:443
Eigen::Ref< Eigen::Matrix< T, DYN, 1 > const > VecRef
Definition fwd.hpp:34
auto have_same_structure(proxsuite::linalg::sparse::MatRef< T, I > a, proxsuite::linalg::sparse::MatRef< T, I > b) -> bool
Definition helpers.hpp:414
T min_eigen_value_via_modified_power_iteration(SparseMat< T, I > &H, sparse::Vec< T > &dw, sparse::Vec< T > &rhs, sparse::Vec< T > &err_v, T max_eigen_value, T power_iteration_accuracy, isize nb_power_iteration)
Definition helpers.hpp:60
Eigen::Matrix< T, DYN, 1 > Vec
Definition fwd.hpp:38
T power_iteration(SparseMat< T, I > &H, sparse::Vec< T > &dw, sparse::Vec< T > &rhs, sparse::Vec< T > &err_v, T power_iteration_accuracy, isize nb_power_iteration)
Definition helpers.hpp:20
void qp_setup(QpView< T, I > qp, Results< T > &results, Model< T, I > &data, Workspace< T, I > &work, Settings< T > &settings, P &precond, PreconditionerStatus &preconditioner_status)
Definition helpers.hpp:282
void update_default_rho_with_minimal_Hessian_eigen_value(optional< T > manual_minimal_H_eigenvalue, Results< T > &results, Settings< T > &settings)
Definition helpers.hpp:159
constexpr nullopt_t nullopt
Definition optional.hpp:42
VEG_NODISCARD VEG_INLINE auto len() const VEG_NOEXCEPT -> isize
Definition vec.hpp:970
This class stores all the results of PROXQP solvers with sparse and dense backends.
Definition results.hpp:68
sparse::Vec< T > z
Definition results.hpp:74
void cold_start(optional< Settings< T > > settings=nullopt)
Definition results.hpp:175
void cleanup(optional< Settings< T > > settings=nullopt)
Definition results.hpp:149
sparse::Vec< T > y
Definition results.hpp:73
sparse::Vec< T > x
Definition results.hpp:72
void cleanup_all_except_prox_parameters()
Definition results.hpp:195
This class defines the settings of PROXQP solvers with sparse and dense backends.
Definition settings.hpp:89
InitialGuessStatus initial_guess
Definition settings.hpp:123
This class stores the model of the QP problem.
Definition model.hpp:23
proxsuite::linalg::sparse::MatRef< T, I > AT
Definition views.hpp:32
proxsuite::linalg::sparse::MatRef< T, I > CT
Definition views.hpp:34
proxsuite::linalg::sparse::MatRef< T, I > H
Definition views.hpp:30
This class defines the workspace of the sparse solver.
proxsuite::linalg::veg::Vec< bool > active_inequalities
void setup_impl(const QpView< T, I > qp, Model< T, I > &data, const Settings< T > &settings, bool execute_or_not, P &precond, proxsuite::linalg::veg::dynstack::StackReq precond_req)
struct proxsuite::proxqp::sparse::Workspace::@16 internal