proxsuite 0.6.7
The Advanced Proximal Optimization Toolbox
Loading...
Searching...
No Matches
linesearch.hpp
Go to the documentation of this file.
1//
2// Copyright (c) 2022 INRIA
3//
5#ifndef PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP
6#define PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP
7
13#include <cmath>
14namespace proxsuite {
15namespace proxqp {
16namespace dense {
17namespace linesearch {
21
30template<typename T>
49template<typename T>
50auto
52 Results<T>& qpresults,
53 Workspace<T>& qpwork,
54 const Settings<T>& qpsettings,
55 isize n_constraints,
57{
58
59 /*
60 * the function computes the first derivative of phi(alpha) at outer step k
61 * and inner step l
62 *
63 * phi(alpha) = f(x_l+alpha dx) + rho/2 |x_l + alpha dx - x_k|**2
64 * + mu_eq_inv/2 (|A(x_l+alpha dx)-d+y_k * mu_eq|**2)
65 * + mu_eq_inv * nu /2 (|A(x_l+alpha dx)-d+y_k * mu_eq -
66 * (y_l+alpha dy)
67 * |**2)
68 * + mu_in_inv/2 ( | [C(x_l+alpha dx) - u + z_k * mu_in]_+ |**2
69 * +| [C(x_l+alpha dx) - l + z_k * mu_in]_- |**2
70 * )
71 * + mu_in_inv * nu / 2 ( | [C(x_l+alpha dx) - u +
72 * z_k
73 * * mu_in]_+
74 * + [C(x_l+alpha dx) - l + z_k * mu_in]_- - (z+alpha dz) * mu_in |**2 with
75 * f(x) = 0.5 * x^THx + g^Tx phi is a second order polynomial in alpha. Below
76 * are computed its coefficients a0 and b0 in order to compute the desired
77 * gradient a0 * alpha + b0
78 */
79
80 qpwork.primal_residual_in_scaled_up_plus_alphaCdx =
81 qpwork.primal_residual_in_scaled_up + qpwork.Cdx * alpha;
82 qpwork.primal_residual_in_scaled_low_plus_alphaCdx =
83 qpresults.si + qpwork.Cdx * alpha;
84
85 T a(qpwork.dw_aug.head(qpmodel.dim).dot(qpwork.Hdx) +
86 qpresults.info.mu_eq_inv * (qpwork.Adx).squaredNorm() +
87 qpresults.info.rho *
88 qpwork.dw_aug.head(qpmodel.dim)
89 .squaredNorm()); // contains now: a = dx.dot(H.dot(dx)) + rho *
90 // norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2
91
92 qpwork.err.segment(qpmodel.dim, qpmodel.n_eq) =
93 qpwork.Adx -
94 qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq) * qpresults.info.mu_eq;
95 a +=
96 qpwork.err.segment(qpmodel.dim, qpmodel.n_eq).squaredNorm() *
97 qpresults.info
98 .mu_eq_inv; // contains now: a = dx.dot(H.dot(dx)) + rho * norm(dx)**2 +
99 // (mu_eq_inv) * norm(Adx)**2 + mu_eq_inv * norm(Adx-dy*mu_eq)**2
100 qpwork.err.head(qpmodel.dim) =
101 qpresults.info.rho * (qpresults.x - qpwork.x_prev) + qpwork.g_scaled;
102 T b(qpresults.x.dot(qpwork.Hdx) +
103 (qpwork.err.head(qpmodel.dim)).dot(qpwork.dw_aug.head(qpmodel.dim)) +
104 qpresults.info.mu_eq_inv *
105 (qpwork.Adx)
106 .dot(qpresults.se +
107 qpresults.y * qpresults.info.mu_eq)); // contains now: b =
108 // dx.dot(H.dot(x) +
109 // rho*(x-xe) + g) +
110 // mu_eq_inv * Adx.dot(res_eq)
111
112 qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq) = qpresults.se;
113 b += qpresults.info.mu_eq_inv *
114 qpwork.err.segment(qpmodel.dim, qpmodel.n_eq)
115 .dot(qpwork.rhs.segment(
116 qpmodel.dim,
117 qpmodel.n_eq)); // contains now: b = dx.dot(H.dot(x) + rho*(x-xe)
118 // + g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
119 // (Adx-dy*mu_eq).dot(res_eq-y*mu_eq)
120
121 // derive Cdx_act
122 qpwork.err.tail(n_constraints) =
123 ((qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.)) ||
124 (qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.)))
125 .select(qpwork.Cdx,
126 Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_constraints));
127
128 a += qpresults.info.mu_in_inv * qpwork.err.tail(n_constraints).squaredNorm() /
129 qpsettings.alpha_gpdal; // contains now: a = dx.dot(H.dot(dx)) + rho *
130 // norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2 + nu*mu_eq_inv *
131 // norm(Adx-dy*mu_eq)**2 +
132 // norm(dw_act)**2 / (mu_in * (alpha_gpdal))
133 a += qpresults.info.mu_in * (1. - qpsettings.alpha_gpdal) *
134 qpwork.dw_aug.tail(n_constraints).squaredNorm();
135 // add norm(z)**2 * mu_in * (1-alpha)
136
137 // derive vector [w-u]_+ + [w-l]--
138 qpwork.active_part_z =
139 (qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.))
140 .select(qpwork.primal_residual_in_scaled_up,
141 Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_constraints)) +
142 (qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.))
143 .select(qpresults.si,
144 Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_constraints));
145
146 b +=
147 qpresults.info.mu_in_inv *
148 qpwork.active_part_z.dot(qpwork.err.tail(n_constraints)) /
149 qpsettings.alpha_gpdal; // contains now: b = dx.dot(H.dot(x) + rho*(x-xe) +
150 // g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
151 // (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) + mu_in
152 // * dw_act.dot([w-u]_+ + [w-l]--) / alpha_gpdal
153
154 // contains now b = dx.dot(H.dot(x) + rho*(x-xe) + g) + mu_eq_inv *
155 // Adx.dot(res_eq) + nu*mu_eq_inv * (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) +
156 // mu_in_inv
157 // * Cdx_act.dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]--) + nu*mu_in_inv
158 // (Cdx_act-dz*mu_in).dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]-- - z*mu_in)
159 b += qpresults.info.mu_in * (1. - qpsettings.alpha_gpdal) *
160 qpwork.dw_aug.tail(n_constraints).dot(qpresults.z);
161
162 return {
163 a,
164 b,
165 a * alpha + b,
166 };
167}
178template<typename T>
179auto
181 Results<T>& qpresults,
182 Workspace<T>& qpwork,
183 isize n_constraints,
185{
186
187 /*
188 * the function computes the first derivative of phi(alpha) at outer step k
189 * and inner step l
190 *
191 * phi(alpha) = f(x_l+alpha dx) + rho/2 |x_l + alpha dx - x_k|**2
192 * + mu_eq_inv/2 (|A(x_l+alpha dx)-d+y_k * mu_eq|**2)
193 * + mu_eq_inv * nu /2 (|A(x_l+alpha dx)-d+y_k * mu_eq -
194 * (y_l+alpha dy)
195 * |**2)
196 * + mu_in_inv/2 ( | [C(x_l+alpha dx) - u + z_k * mu_in]_+ |**2
197 * +| [C(x_l+alpha dx) - l + z_k * mu_in]_- |**2
198 * )
199 * + mu_in_inv * nu / 2 ( | [C(x_l+alpha dx) - u +
200 * z_k
201 * * mu_in]_+
202 * + [C(x_l+alpha dx) - l + z_k * mu_in]_- - (z+alpha dz) * mu_in |**2 with
203 * f(x) = 0.5 * x^THx + g^Tx phi is a second order polynomial in alpha. Below
204 * are computed its coefficients a0 and b0 in order to compute the desired
205 * gradient a0 * alpha + b0
206 */
207
208 qpwork.primal_residual_in_scaled_up_plus_alphaCdx =
209 qpwork.primal_residual_in_scaled_up + qpwork.Cdx * alpha;
210 qpwork.primal_residual_in_scaled_low_plus_alphaCdx =
211 qpresults.si + qpwork.Cdx * alpha;
212
213 T a(qpwork.dw_aug.head(qpmodel.dim).dot(qpwork.Hdx) +
214 qpresults.info.mu_eq_inv * (qpwork.Adx).squaredNorm() +
215 qpresults.info.rho *
216 qpwork.dw_aug.head(qpmodel.dim)
217 .squaredNorm()); // contains now: a = dx.dot(H.dot(dx)) + rho *
218 // norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2
219
220 qpwork.err.segment(qpmodel.dim, qpmodel.n_eq) =
221 qpwork.Adx -
222 qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq) * qpresults.info.mu_eq;
223 a += qpwork.err.segment(qpmodel.dim, qpmodel.n_eq).squaredNorm() *
224 qpresults.info.mu_eq_inv *
225 qpresults.info
226 .nu; // contains now: a = dx.dot(H.dot(dx)) + rho * norm(dx)**2 +
227 // (mu_eq_inv) * norm(Adx)**2 + nu*mu_eq_inv * norm(Adx-dy*mu_eq)**2
228 qpwork.err.head(qpmodel.dim) =
229 qpresults.info.rho * (qpresults.x - qpwork.x_prev) + qpwork.g_scaled;
230 // T b(qpresults.x.dot(qpwork.Hdx) +
231 // (qpwork.err.head(qpmodel.dim)).dot(qpwork.dw_aug.head(qpmodel.dim)) +
232 // qpresults.info.mu_eq_inv *
233 // (qpwork.Adx)
234 // .dot(qpwork.primal_residual_eq_scaled +
235 // qpresults.y * qpresults.info.mu_eq)); // contains now: b =
236 // // dx.dot(H.dot(x) +
237 // // rho*(x-xe) + g) +
238
239 T b(qpresults.x.dot(qpwork.Hdx) +
240 (qpwork.err.head(qpmodel.dim)).dot(qpwork.dw_aug.head(qpmodel.dim)) +
241 qpresults.info.mu_eq_inv *
242 (qpwork.Adx)
243 .dot(qpresults.se +
244 qpresults.y * qpresults.info.mu_eq)); // contains now: b =
245 // dx.dot(H.dot(x) +
246 // rho*(x-xe) + g) +
247 // mu_eq_inv * Adx.dot(res_eq)
248 qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq) = qpresults.se;
249 b += qpresults.info.nu * qpresults.info.mu_eq_inv *
250 qpwork.err.segment(qpmodel.dim, qpmodel.n_eq)
251 .dot(qpwork.rhs.segment(
252 qpmodel.dim,
253 qpmodel.n_eq)); // contains now: b = dx.dot(H.dot(x) + rho*(x-xe)
254 // + g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
255 // (Adx-dy*mu_eq).dot(res_eq-y*mu_eq)
256
257 // derive Cdx_act
258 qpwork.err.tail(n_constraints) =
259 ((qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.)) ||
260 (qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.)))
261 .select(qpwork.Cdx,
262 Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_constraints));
263
264 a += qpresults.info.mu_in_inv *
265 qpwork.err.tail(n_constraints)
266 .squaredNorm(); // contains now: a = dx.dot(H.dot(dx)) + rho *
267 // norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2 + nu*mu_eq_inv *
268 // norm(Adx-dy*mu_eq)**2 + mu_in *
269 // norm(Cdx_act)**2
270
271 // derive vector [Cx-u+ze/mu]_+ + [Cx-l+ze/mu]--
272 qpwork.active_part_z =
273 (qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.))
274 .select(qpwork.primal_residual_in_scaled_up,
275 Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_constraints)) +
276 (qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.))
277 .select(qpresults.si,
278 Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_constraints));
279
280 b += qpresults.info.mu_in_inv *
281 qpwork.active_part_z.dot(qpwork.err.tail(
282 n_constraints)); // contains now: b = dx.dot(H.dot(x) + rho*(x-xe) +
283 // g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
284 // (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) + mu_in
285 // * Cdx_act.dot([Cx-u+ze/mu]_+ + [Cx-l+ze*mu_in]--)
286
287 // derive Cdx_act - dz*mu_in
288 qpwork.err.tail(n_constraints) -=
289 qpwork.dw_aug.tail(n_constraints) * qpresults.info.mu_in;
290 // derive [Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]-- -z*mu_in
291 qpwork.active_part_z -= qpresults.z * qpresults.info.mu_in;
292
293 // contains now a = dx.dot(H.dot(dx)) + rho * norm(dx)**2 + (mu_eq_inv) *
294 // norm(Adx)**2 + nu*mu_eq_inv * norm(Adx-dy*mu_eq)**2 + mu_in_inv *
295 // norm(Cdx_act)**2 + nu*mu_in_inv * norm(Cdx_act-dz*mu_in)**2
296 a += qpresults.info.nu * qpresults.info.mu_in_inv *
297 qpwork.err.tail(n_constraints).squaredNorm();
298 // contains now b = dx.dot(H.dot(x) + rho*(x-xe) + g) + mu_eq_inv *
299 // Adx.dot(res_eq) + nu*mu_eq_inv * (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) +
300 // mu_in_inv
301 // * Cdx_act.dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]--) + nu*mu_in_inv
302 // (Cdx_act-dz*mu_in).dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]-- - z*mu_in)
303 b += qpresults.info.nu * qpresults.info.mu_in_inv *
304 qpwork.err.tail(n_constraints).dot(qpwork.active_part_z);
305
306 return {
307 a,
308 b,
309 a * alpha + b,
310 };
311}
320template<typename T>
321void
323 Results<T>& qpresults,
324 Workspace<T>& qpwork,
325 const Settings<T>& qpsettings,
326 const isize n_constraints)
327{
328
329 /*
330 * The algorithm performs the following step
331 *
332 * 1/
333 * 1.1/ Store solutions of equations
334 * C(x+alpha dx) - l + ze/mu_in = 0
335 * C(x+alpha dx) - u + ze/mu_in = 0
336 *
337 * 1.2/ Sort the alpha
338 * 2/
339 * 2.1
340 * For each positive alpha compute the first derivative of
341 * phi(alpha) = [proximal primal dual augmented lagrangian of the subproblem
342 * evaluated at x_k + alpha dx, y_k + alpha dy, z_k + alpha dz] using function
343 * "gradient_norm" By construction for alpha = 0, phi'(alpha) <= 0 and
344 * phi'(alpha) goes to infinity with alpha hence it cancels uniquely at one
345 * optimal alpha*
346 *
347 * while phi'(alpha)<=0 store the derivative (noted last_grad_neg) and
348 * alpha (last_alpha_neg)
349 * the first time phi'(alpha) > 0 store the derivative (noted
350 * first_grad_pos) and alpha (first_alpha_pos), and break the loo
351 *
352 * 2.2
353 * If first_alpha_pos corresponds to the first positive alpha of previous
354 * loop, then do
355 * last_alpha_neg = 0
356 * last_grad_neg = phi'(0)
357 *
358 * 2.3
359 * the optimal alpha is within the interval
360 * [last_alpha_neg,first_alpha_pos] and can be computed exactly as phi' is
361 * an affine function in alph
362 * alpha* = alpha_last_neg
363 * - last_neg_grad * (alpha_first_pos - alpha_last_neg) /
364 * (first_pos_grad - last_neg_grad);
365 */
366
367 const T machine_eps = std::numeric_limits<T>::epsilon();
368
369 qpwork.alpha = T(1);
370 T alpha_(1.);
371
372 qpwork.alphas.clear();
373
375 // 1.1 add solutions of equations C(x+alpha dx)-l +ze/mu_in = 0 and C(x+alpha
376 // dx)-u +ze/mu_in = 0
377
378 for (isize i = 0; i < n_constraints; i++) {
379
380 if (qpwork.Cdx(i) != 0.) {
381 alpha_ =
382 -qpwork.primal_residual_in_scaled_up(i) / (qpwork.Cdx(i) + machine_eps);
383 if (alpha_ > machine_eps) {
384 qpwork.alphas.push(alpha_);
385 }
386 alpha_ = -qpresults.si(i) / (qpwork.Cdx(i) + machine_eps);
387 if (alpha_ > machine_eps) {
388 qpwork.alphas.push(alpha_);
389 }
390 }
391 }
392
393 isize n_alpha = qpwork.alphas.len();
394
395 // 1.2 sort the alphas
396
397 std::sort(qpwork.alphas.ptr_mut(), qpwork.alphas.ptr_mut() + n_alpha);
398 isize new_len = std::unique( //
399 qpwork.alphas.ptr_mut(),
400 qpwork.alphas.ptr_mut() + n_alpha) -
401 qpwork.alphas.ptr_mut();
402 qpwork.alphas.resize(new_len);
403
404 n_alpha = qpwork.alphas.len();
405 if (n_alpha == 0) { //
406 switch (qpsettings.merit_function_type) {
408 auto res = gpdal_derivative_results(
409 qpmodel, qpresults, qpwork, qpsettings, n_constraints, T(0));
410 qpwork.alpha = -res.b / res.a;
411 } break;
414 qpmodel, qpresults, qpwork, n_constraints, T(0));
415 qpwork.alpha = -res.b / res.a;
416 } break;
417 }
418 return;
419 }
421 auto infty = std::numeric_limits<T>::infinity();
422
423 T last_neg_grad = 0;
424 T alpha_last_neg = 0;
425 T first_pos_grad = 0;
426 T alpha_first_pos = infty;
427 for (isize i = 0; i < n_alpha; ++i) {
428 alpha_ = qpwork.alphas[i];
429
430 /*
431 * 2.1
432 * For each positive alpha compute the first derivative of
433 * phi(alpha) = [proximal augmented lagrangian of the
434 * subproblem evaluated at x_k + alpha dx]
435 *
436 * (By construction for alpha = 0, phi'(alpha) <= 0 and
437 * phi'(alpha) goes to infinity with alpha hence it cancels
438 * uniquely at one optimal alpha*
439 *
440 * while phi'(alpha)<=0 store the derivative (noted
441 * last_grad_neg) and alpha (last_alpha_neg
442 * the first time phi'(alpha) > 0 store the derivative
443 * (noted first_grad_pos) and alpha (first_alpha_pos), and
444 * break the loop
445 */
446 T gr(0);
447 switch (qpsettings.merit_function_type) {
450 qpmodel, qpresults, qpwork, qpsettings, n_constraints, alpha_)
451 .grad;
452 break;
455 qpmodel, qpresults, qpwork, n_constraints, alpha_)
456 .grad;
457 break;
458 }
459
460 if (gr < T(0)) {
461 alpha_last_neg = alpha_;
462 last_neg_grad = gr;
463 } else {
464 first_pos_grad = gr;
465 alpha_first_pos = alpha_;
466 break;
467 }
468 }
469
470 /*
471 * 2.2
472 * If first_alpha_pos corresponds to the first positive alpha of
473 * previous loop, then do
474 * last_alpha_neg = 0 and last_grad_neg = phi'(0) using function
475 * "gradient_norm"
476 */
477 if (alpha_last_neg == T(0)) {
478 switch (qpsettings.merit_function_type) {
480 last_neg_grad = gpdal_derivative_results(qpmodel,
481 qpresults,
482 qpwork,
483 qpsettings,
484 n_constraints,
485 alpha_last_neg)
486 .grad;
487 break;
489 last_neg_grad =
491 qpmodel, qpresults, qpwork, n_constraints, alpha_last_neg)
492 .grad;
493 break;
494 }
495 }
496 if (alpha_first_pos == infty) {
497 /*
498 * 2.3
499 * the optimal alpha is within the interval
500 * [last_alpha_neg, +∞)
501 */
502 switch (qpsettings.merit_function_type) {
506 qpresults,
507 qpwork,
508 qpsettings,
509 n_constraints,
510 2 * alpha_last_neg + 1);
511 auto& a = res.a;
512 auto& b = res.b;
513 // grad = a * alpha + b
514 // grad = 0 => alpha = -b/a
515 qpwork.alpha = -b / a;
516 } break;
519 qpmodel, qpresults, qpwork, n_constraints, 2 * alpha_last_neg + 1);
520 auto& a = res.a;
521 auto& b = res.b;
522 // grad = a * alpha + b
523 // grad = 0 => alpha = -b/a
524 qpwork.alpha = -b / a;
525 } break;
526 }
527 } else {
528 /*
529 * 2.3
530 * the optimal alpha is within the interval
531 * [last_alpha_neg,first_alpha_pos] and can be computed exactly as phi'
532 * is an affine function in alpha
533 */
534 qpwork.alpha = std::abs(alpha_last_neg -
535 last_neg_grad * (alpha_first_pos - alpha_last_neg) /
536 (first_pos_grad - last_neg_grad));
537 }
538}
539
549template<typename T>
550void
552 Results<T>& qpresults,
553 const DenseBackend& dense_backend,
554 const isize n_constraints,
555 Workspace<T>& qpwork)
556{
557
558 /*
559 * arguments
560 * 1/ new_active_set : a vector which contains new active set of the
561 * problem, namely if
562 * new_active_set_u = Cx_k-u +z_k*mu_in>= 0
563 * new_active_set_l = Cx_k-l +z_k*mu_in<=
564 * then new_active_set = new_active_set_u OR new_active_set_
565 *
566 * 2/ current_bijection_map : a vector for which each entry corresponds to
567 * the current row of C of the current factorization
568 *
569 * for example, naming C_initial the initial C matrix of the problem, and
570 * C_current the one of the current factorization, the
571 * C_initial[i,:] = C_current[current_bijection_mal[i],:] for all
572 *
573 * 3/ n_c : the current number of active_inequalities
574 * This algorithm ensures that for all new version of C_current in the LDLT
575 * factorization all row index i < n_c correspond to current active indexes
576 * (all other correspond to inactive rows
577 *
578 * To do so,
579 * 1/ for initialization
580 * 1.1/ new_bijection_map = current_bijection_map
581 * 1.2/ n_c_f = n_
582 *
583 * 2/ All active indexes of the current bijection map (i.e
584 * current_bijection_map(i) < n_c by assumption) which are not active
585 * anymore in the new active set (new_active_set(i)=false are put at the
586 * end of new_bijection_map, i.
587 *
588 * 2.1/ for all j if new_bijection_map(j) > new_bijection_map(i), then
589 * new_bijection_map(j)-=1
590 * 2.2/ n_c_f -=1
591 * 2.3/ new_bijection_map(i) = n_in-1
592 *
593 * 3/ All active indexe of the new active set (new_active_set(i) == true)
594 * which are not active in the new_bijection_map (new_bijection_map(i) >=
595 * n_c_f) are put at the end of the current version of C, i.e
596 * 3.1/ if new_bijection_map(j) < new_bijection_map(i) &&
597 * new_bijection_map(j) >= n_c_f then new_bijection_map(j)+=1
598 * 3.2/ new_bijection_map(i) = n_c_f
599 * 3.3/ n_c_f +=1
600 *
601 * It returns finally the new_bijection_map, for which
602 * new_bijection_map(n_in) = n_c_f
603 */
604
605 isize n_c_f = qpwork.n_c;
607
608 // suppression pour le nouvel active set, ajout dans le nouvel unactive set
609
612 };
613 {
614 auto _planned_to_delete = stack.make_new_for_overwrite(
615 proxsuite::linalg::veg::Tag<isize>{}, isize(n_constraints));
616 isize* planned_to_delete = _planned_to_delete.ptr_mut();
617 isize planned_to_delete_count = 0;
618
619 for (isize i = 0; i < n_constraints; i++) {
620 if (qpwork.current_bijection_map(i) < qpwork.n_c) {
621 if (!qpwork.active_inequalities(i)) {
622 // delete current_bijection_map(i)
623
624 planned_to_delete[planned_to_delete_count] =
625 qpwork.current_bijection_map(i) + qpmodel.dim + qpmodel.n_eq;
626 ++planned_to_delete_count;
627
628 for (isize j = 0; j < n_constraints; j++) {
629 if (qpwork.new_bijection_map(j) > qpwork.new_bijection_map(i)) {
630 qpwork.new_bijection_map(j) -= 1;
631 }
632 }
633 n_c_f -= 1;
634 qpwork.new_bijection_map(i) = n_constraints - 1;
635 }
636 }
637 }
638 std::sort(planned_to_delete, planned_to_delete + planned_to_delete_count);
639 switch (dense_backend) {
641 qpwork.ldl.delete_at(planned_to_delete, planned_to_delete_count, stack);
642 break;
644 // for (isize i=0; i < planned_to_delete_count; i++){
645 // isize index = planned_to_delete[i] - (qpmodel.dim + qpmodel.n_eq);
646 // if (index >= qpmodel.n_in){
647 // // bow constraint
648 // qpwork.kkt(index-qpmodel.n_in,index-qpmodel.n_in) -=
649 // qpresults.info.mu_in_inv *
650 // std::pow(qpwork.i_scaled[index-qpmodel.n_in],2) ;
651 // }else{
652 // // generic ineq constraints
653 // qpwork.kkt.noalias() -= qpresults.info.mu_in_inv *
654 // qpwork.C_scaled.row(index).transpose() *
655 // qpwork.C_scaled.row(index) ;
656 // }
657 // }
659 T, new_cols, qpmodel.dim, planned_to_delete_count, stack);
660 qpwork.dw_aug.head(planned_to_delete_count).setOnes();
661 T mu_in_inv_neg(-qpresults.info.mu_in_inv);
662 qpwork.dw_aug.head(planned_to_delete_count).array() *= mu_in_inv_neg;
663 for (isize i = 0; i < planned_to_delete_count; ++i) {
664 isize index = planned_to_delete[i] - (qpmodel.dim + qpmodel.n_eq);
665 auto col = new_cols.col(i);
666 if (index >= qpmodel.n_in) {
667 // box constraint
668 col.setZero();
669 col[index - qpmodel.n_in] = qpwork.i_scaled[index - qpmodel.n_in];
670 } else {
671 // generic ineq constraints
672 col = qpwork.C_scaled.row(index);
673 }
674 }
675 qpwork.ldl.rank_r_update(
676 new_cols, qpwork.dw_aug.head(planned_to_delete_count), stack);
677 } break;
679 break;
680 }
681 if (planned_to_delete_count > 0) {
682 qpwork.constraints_changed = true;
683 }
684 }
685 // ajout au nouvel active set, suppression pour le nouvel unactive set
686
687 {
688 auto _planned_to_add = stack.make_new_for_overwrite(
689 proxsuite::linalg::veg::Tag<isize>{}, n_constraints);
690 auto planned_to_add = _planned_to_add.ptr_mut();
691
692 isize planned_to_add_count = 0;
693 T mu_in_neg(-qpresults.info.mu_in);
694
695 isize n_c = n_c_f;
696 for (isize i = 0; i < n_constraints; i++) {
697 if (qpwork.active_inequalities(i)) {
698 if (qpwork.new_bijection_map(i) >= n_c_f) {
699 // add at the end
700 planned_to_add[planned_to_add_count] = i;
701 ++planned_to_add_count;
702
703 for (isize j = 0; j < n_constraints; j++) {
704 if (qpwork.new_bijection_map(j) < qpwork.new_bijection_map(i) &&
705 qpwork.new_bijection_map(j) >= n_c_f) {
706 qpwork.new_bijection_map(j) += 1;
707 }
708 }
709 qpwork.new_bijection_map(i) = n_c_f;
710 n_c_f += 1;
711 }
712 }
713 }
714 {
715 switch (dense_backend) {
717 isize n = qpmodel.dim;
718 isize n_eq = qpmodel.n_eq;
720 T, new_cols, n + n_eq + n_c_f, planned_to_add_count, stack);
721
722 for (isize k = 0; k < planned_to_add_count; ++k) {
723 isize index = planned_to_add[k];
724 auto col = new_cols.col(k);
725 if (index >= qpmodel.n_in) {
726 col.head(n).setZero();
727 // I_scaled = ED which is the diagonal matrix
728 col[index - qpmodel.n_in] = qpwork.i_scaled[index - qpmodel.n_in];
729 } else {
730 col.head(n) = (qpwork.C_scaled.row(index));
731 }
732 col.tail(n_eq + n_c_f).setZero();
733 col[n + n_eq + n_c + k] = mu_in_neg;
734 }
735 qpwork.ldl.insert_block_at(n + n_eq + n_c, new_cols, stack);
736 } break;
738 // too slow
739 // for (isize i=0; i < planned_to_add_count; ++i){
740 // isize index = planned_to_add[i];
741 // if (index >= qpmodel.n_in){
742 // // bow constraint
743 // qpwork.kkt(index-qpmodel.n_in,index-qpmodel.n_in) +=
744 // qpresults.info.mu_in_inv *
745 // std::pow(qpwork.i_scaled[index-qpmodel.n_in],2) ;
746 // }else{
747 // // generic ineq constraints
748 // qpwork.kkt.noalias() += qpresults.info.mu_in_inv *
749 // qpwork.C_scaled.row(index).transpose() *
750 // qpwork.C_scaled.row(index) ;
751 // }
752 // }
754 T, new_cols, qpmodel.dim, planned_to_add_count, stack);
755 qpwork.dw_aug.head(planned_to_add_count).setOnes();
756 qpwork.dw_aug.head(planned_to_add_count).array() *=
757 qpresults.info.mu_in_inv;
758 for (isize i = 0; i < planned_to_add_count; ++i) {
759 isize index = planned_to_add[i];
760 auto col = new_cols.col(i);
761 if (index >= qpmodel.n_in) {
762 // box constraint
763 col.setZero();
764 col[index - qpmodel.n_in] = qpwork.i_scaled[index - qpmodel.n_in];
765 } else {
766 // generic ineq constraints
767 col.head(qpmodel.dim) = qpwork.C_scaled.row(index);
768 }
769 }
770 qpwork.ldl.rank_r_update(
771 new_cols, qpwork.dw_aug.head(planned_to_add_count), stack);
772 }
773 // qpwork.ldl.factorize(qpwork.kkt.transpose(), stack);
774 break;
776 break;
777 }
778 }
779 if (planned_to_add_count > 0) {
780 qpwork.constraints_changed = true;
781 }
782 }
783
784 qpwork.n_c = n_c_f;
786}
787
788} // namespace linesearch
789} // namespace dense
790} // namespace proxqp
791} // namespace proxsuite
792
793#endif /* end of include guard PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP */
#define LDLT_TEMP_MAT_UNINIT(Type, Name, Rows, Cols, Stack)
Definition core.hpp:81
auto gpdal_derivative_results(const Model< T > &qpmodel, Results< T > &qpresults, Workspace< T > &qpwork, const Settings< T > &qpsettings, isize n_constraints, T alpha) -> PrimalDualDerivativeResult< T >
void active_set_change(const Model< T > &qpmodel, Results< T > &qpresults, const DenseBackend &dense_backend, const isize n_constraints, Workspace< T > &qpwork)
auto primal_dual_derivative_results(const Model< T > &qpmodel, Results< T > &qpresults, Workspace< T > &qpwork, isize n_constraints, T alpha) -> PrimalDualDerivativeResult< T >
void primal_dual_ls(const Model< T > &qpmodel, Results< T > &qpresults, Workspace< T > &qpwork, const Settings< T > &qpsettings, const isize n_constraints)
VEG_NODISCARD VEG_INLINE auto as_mut() VEG_NOEXCEPT -> SliceMut< T >
Definition vec.hpp:957
This class stores all the results of PROXQP solvers with sparse and dense backends.
Definition results.hpp:68
sparse::Vec< T > si
Definition results.hpp:77
This class defines the settings of PROXQP solvers with sparse and dense backends.
Definition settings.hpp:89
MeritFunctionType merit_function_type
Definition settings.hpp:137
This class stores the model of the QP problem.
Definition model.hpp:24
This class defines the workspace of the dense solver.
Definition workspace.hpp:26
proxsuite::linalg::veg::Vec< unsigned char > ldl_stack
Definition workspace.hpp:30
proxsuite::linalg::veg::Vec< T > alphas
Definition workspace.hpp:70
proxsuite::linalg::dense::Ldlt< T > ldl
Definition workspace.hpp:29
This class stores the results of the primal-dual line-search.