Program Listing for File linesearch.hpp
↰ Return to documentation for file (/tmp/ws/src/proxsuite/include/proxsuite/proxqp/dense/linesearch.hpp
)
//
// Copyright (c) 2022 INRIA
//
#ifndef PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP
#define PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP
#include "proxsuite/proxqp/dense/views.hpp"
#include "proxsuite/proxqp/dense/model.hpp"
#include "proxsuite/proxqp/results.hpp"
#include "proxsuite/proxqp/dense/workspace.hpp"
#include "proxsuite/proxqp/settings.hpp"
#include <cmath>
namespace proxsuite {
namespace proxqp {
namespace dense {
namespace linesearch {
template<typename T>
struct PrimalDualDerivativeResult
{
T a;
T b;
T grad;
VEG_REFLECT(PrimalDualDerivativeResult, a, b, grad);
};
template<typename T>
auto
gpdal_derivative_results(const Model<T>& qpmodel,
Results<T>& qpresults,
Workspace<T>& qpwork,
const Settings<T>& qpsettings,
T alpha) -> PrimalDualDerivativeResult<T>
{
/*
* the function computes the first derivative of phi(alpha) at outer step k
* and inner step l
*
* phi(alpha) = f(x_l+alpha dx) + rho/2 |x_l + alpha dx - x_k|**2
* + mu_eq_inv/2 (|A(x_l+alpha dx)-d+y_k * mu_eq|**2)
* + mu_eq_inv * nu /2 (|A(x_l+alpha dx)-d+y_k * mu_eq -
* (y_l+alpha dy)
* |**2)
* + mu_in_inv/2 ( | [C(x_l+alpha dx) - u + z_k * mu_in]_+ |**2
* +| [C(x_l+alpha dx) - l + z_k * mu_in]_- |**2
* )
* + mu_in_inv * nu / 2 ( | [C(x_l+alpha dx) - u +
* z_k
* * mu_in]_+
* + [C(x_l+alpha dx) - l + z_k * mu_in]_- - (z+alpha dz) * mu_in |**2 with
* f(x) = 0.5 * x^THx + g^Tx phi is a second order polynomial in alpha. Below
* are computed its coefficients a0 and b0 in order to compute the desired
* gradient a0 * alpha + b0
*/
qpwork.primal_residual_in_scaled_up_plus_alphaCdx =
qpwork.primal_residual_in_scaled_up + qpwork.Cdx * alpha;
qpwork.primal_residual_in_scaled_low_plus_alphaCdx =
qpwork.primal_residual_in_scaled_low + qpwork.Cdx * alpha;
T a(qpwork.dw_aug.head(qpmodel.dim).dot(qpwork.Hdx) +
qpresults.info.mu_eq_inv * (qpwork.Adx).squaredNorm() +
qpresults.info.rho *
qpwork.dw_aug.head(qpmodel.dim)
.squaredNorm()); // contains now: a = dx.dot(H.dot(dx)) + rho *
// norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2
qpwork.err.segment(qpmodel.dim, qpmodel.n_eq) =
qpwork.Adx -
qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq) * qpresults.info.mu_eq;
a +=
qpwork.err.segment(qpmodel.dim, qpmodel.n_eq).squaredNorm() *
qpresults.info
.mu_eq_inv; // contains now: a = dx.dot(H.dot(dx)) + rho * norm(dx)**2 +
// (mu_eq_inv) * norm(Adx)**2 + mu_eq_inv * norm(Adx-dy*mu_eq)**2
qpwork.err.head(qpmodel.dim) =
qpresults.info.rho * (qpresults.x - qpwork.x_prev) + qpwork.g_scaled;
T b(qpresults.x.dot(qpwork.Hdx) +
(qpwork.err.head(qpmodel.dim)).dot(qpwork.dw_aug.head(qpmodel.dim)) +
qpresults.info.mu_eq_inv *
(qpwork.Adx)
.dot(qpwork.primal_residual_eq_scaled +
qpresults.y * qpresults.info.mu_eq)); // contains now: b =
// dx.dot(H.dot(x) +
// rho*(x-xe) + g) +
// mu_eq_inv * Adx.dot(res_eq)
qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq) =
qpwork.primal_residual_eq_scaled;
b += qpresults.info.mu_eq_inv *
qpwork.err.segment(qpmodel.dim, qpmodel.n_eq)
.dot(qpwork.rhs.segment(
qpmodel.dim,
qpmodel.n_eq)); // contains now: b = dx.dot(H.dot(x) + rho*(x-xe)
// + g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
// (Adx-dy*mu_eq).dot(res_eq-y*mu_eq)
// derive Cdx_act
qpwork.err.tail(qpmodel.n_in) =
((qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.)) ||
(qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.)))
.select(qpwork.Cdx,
Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(qpmodel.n_in));
a += qpresults.info.mu_in_inv * qpwork.err.tail(qpmodel.n_in).squaredNorm() /
qpsettings.alpha_gpdal; // contains now: a = dx.dot(H.dot(dx)) + rho *
// norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2 + nu*mu_eq_inv *
// norm(Adx-dy*mu_eq)**2 +
// norm(dw_act)**2 / (mu_in * (alpha_gpdal))
a += qpresults.info.mu_in * (1. - qpsettings.alpha_gpdal) *
qpwork.dw_aug.tail(qpmodel.n_in).squaredNorm();
// add norm(z)**2 * mu_in * (1-alpha)
// derive vector [w-u]_+ + [w-l]--
qpwork.active_part_z =
(qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.))
.select(qpwork.primal_residual_in_scaled_up,
Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(qpmodel.n_in)) +
(qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.))
.select(qpwork.primal_residual_in_scaled_low,
Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(qpmodel.n_in));
b +=
qpresults.info.mu_in_inv *
qpwork.active_part_z.dot(qpwork.err.tail(qpmodel.n_in)) /
qpsettings.alpha_gpdal; // contains now: b = dx.dot(H.dot(x) + rho*(x-xe) +
// g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
// (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) + mu_in
// * dw_act.dot([w-u]_+ + [w-l]--) / alpha_gpdal
// contains now b = dx.dot(H.dot(x) + rho*(x-xe) + g) + mu_eq_inv *
// Adx.dot(res_eq) + nu*mu_eq_inv * (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) +
// mu_in_inv
// * Cdx_act.dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]--) + nu*mu_in_inv
// (Cdx_act-dz*mu_in).dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]-- - z*mu_in)
b += qpresults.info.mu_in * (1. - qpsettings.alpha_gpdal) *
qpwork.dw_aug.tail(qpmodel.n_in).dot(qpresults.z);
return {
a,
b,
a * alpha + b,
};
}
template<typename T>
auto
primal_dual_derivative_results(const Model<T>& qpmodel,
Results<T>& qpresults,
Workspace<T>& qpwork,
T alpha) -> PrimalDualDerivativeResult<T>
{
/*
* the function computes the first derivative of phi(alpha) at outer step k
* and inner step l
*
* phi(alpha) = f(x_l+alpha dx) + rho/2 |x_l + alpha dx - x_k|**2
* + mu_eq_inv/2 (|A(x_l+alpha dx)-d+y_k * mu_eq|**2)
* + mu_eq_inv * nu /2 (|A(x_l+alpha dx)-d+y_k * mu_eq -
* (y_l+alpha dy)
* |**2)
* + mu_in_inv/2 ( | [C(x_l+alpha dx) - u + z_k * mu_in]_+ |**2
* +| [C(x_l+alpha dx) - l + z_k * mu_in]_- |**2
* )
* + mu_in_inv * nu / 2 ( | [C(x_l+alpha dx) - u +
* z_k
* * mu_in]_+
* + [C(x_l+alpha dx) - l + z_k * mu_in]_- - (z+alpha dz) * mu_in |**2 with
* f(x) = 0.5 * x^THx + g^Tx phi is a second order polynomial in alpha. Below
* are computed its coefficients a0 and b0 in order to compute the desired
* gradient a0 * alpha + b0
*/
qpwork.primal_residual_in_scaled_up_plus_alphaCdx =
qpwork.primal_residual_in_scaled_up + qpwork.Cdx * alpha;
qpwork.primal_residual_in_scaled_low_plus_alphaCdx =
qpwork.primal_residual_in_scaled_low + qpwork.Cdx * alpha;
T a(qpwork.dw_aug.head(qpmodel.dim).dot(qpwork.Hdx) +
qpresults.info.mu_eq_inv * (qpwork.Adx).squaredNorm() +
qpresults.info.rho *
qpwork.dw_aug.head(qpmodel.dim)
.squaredNorm()); // contains now: a = dx.dot(H.dot(dx)) + rho *
// norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2
qpwork.err.segment(qpmodel.dim, qpmodel.n_eq) =
qpwork.Adx -
qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq) * qpresults.info.mu_eq;
a += qpwork.err.segment(qpmodel.dim, qpmodel.n_eq).squaredNorm() *
qpresults.info.mu_eq_inv *
qpresults.info
.nu; // contains now: a = dx.dot(H.dot(dx)) + rho * norm(dx)**2 +
// (mu_eq_inv) * norm(Adx)**2 + nu*mu_eq_inv * norm(Adx-dy*mu_eq)**2
qpwork.err.head(qpmodel.dim) =
qpresults.info.rho * (qpresults.x - qpwork.x_prev) + qpwork.g_scaled;
T b(qpresults.x.dot(qpwork.Hdx) +
(qpwork.err.head(qpmodel.dim)).dot(qpwork.dw_aug.head(qpmodel.dim)) +
qpresults.info.mu_eq_inv *
(qpwork.Adx)
.dot(qpwork.primal_residual_eq_scaled +
qpresults.y * qpresults.info.mu_eq)); // contains now: b =
// dx.dot(H.dot(x) +
// rho*(x-xe) + g) +
// mu_eq_inv * Adx.dot(res_eq)
qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq) =
qpwork.primal_residual_eq_scaled;
b += qpresults.info.nu * qpresults.info.mu_eq_inv *
qpwork.err.segment(qpmodel.dim, qpmodel.n_eq)
.dot(qpwork.rhs.segment(
qpmodel.dim,
qpmodel.n_eq)); // contains now: b = dx.dot(H.dot(x) + rho*(x-xe)
// + g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
// (Adx-dy*mu_eq).dot(res_eq-y*mu_eq)
// derive Cdx_act
qpwork.err.tail(qpmodel.n_in) =
((qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.)) ||
(qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.)))
.select(qpwork.Cdx,
Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(qpmodel.n_in));
a += qpresults.info.mu_in_inv *
qpwork.err.tail(qpmodel.n_in)
.squaredNorm(); // contains now: a = dx.dot(H.dot(dx)) + rho *
// norm(dx)**2 + (mu_eq_inv) * norm(Adx)**2 + nu*mu_eq_inv *
// norm(Adx-dy*mu_eq)**2 + mu_in *
// norm(Cdx_act)**2
// derive vector [Cx-u+ze/mu]_+ + [Cx-l+ze/mu]--
qpwork.active_part_z =
(qpwork.primal_residual_in_scaled_up_plus_alphaCdx.array() > T(0.))
.select(qpwork.primal_residual_in_scaled_up,
Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(qpmodel.n_in)) +
(qpwork.primal_residual_in_scaled_low_plus_alphaCdx.array() < T(0.))
.select(qpwork.primal_residual_in_scaled_low,
Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(qpmodel.n_in));
b += qpresults.info.mu_in_inv *
qpwork.active_part_z.dot(qpwork.err.tail(
qpmodel.n_in)); // contains now: b = dx.dot(H.dot(x) + rho*(x-xe) +
// g) + mu_eq_inv * Adx.dot(res_eq) + nu*mu_eq_inv *
// (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) + mu_in
// * Cdx_act.dot([Cx-u+ze/mu]_+ + [Cx-l+ze*mu_in]--)
// derive Cdx_act - dz*mu_in
qpwork.err.tail(qpmodel.n_in) -=
qpwork.dw_aug.tail(qpmodel.n_in) * qpresults.info.mu_in;
// derive [Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]-- -z*mu_in
qpwork.active_part_z -= qpresults.z * qpresults.info.mu_in;
// contains now a = dx.dot(H.dot(dx)) + rho * norm(dx)**2 + (mu_eq_inv) *
// norm(Adx)**2 + nu*mu_eq_inv * norm(Adx-dy*mu_eq)**2 + mu_in_inv *
// norm(Cdx_act)**2 + nu*mu_in_inv * norm(Cdx_act-dz*mu_in)**2
a += qpresults.info.nu * qpresults.info.mu_in_inv *
qpwork.err.tail(qpmodel.n_in).squaredNorm();
// contains now b = dx.dot(H.dot(x) + rho*(x-xe) + g) + mu_eq_inv *
// Adx.dot(res_eq) + nu*mu_eq_inv * (Adx-dy*mu_eq).dot(res_eq-y*mu_eq) +
// mu_in_inv
// * Cdx_act.dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]--) + nu*mu_in_inv
// (Cdx_act-dz*mu_in).dot([Cx-u+ze*mu_in]_+ + [Cx-l+ze*mu_in]-- - z*mu_in)
b += qpresults.info.nu * qpresults.info.mu_in_inv *
qpwork.err.tail(qpmodel.n_in).dot(qpwork.active_part_z);
return {
a,
b,
a * alpha + b,
};
}
template<typename T>
void
primal_dual_ls(const Model<T>& qpmodel,
Results<T>& qpresults,
Workspace<T>& qpwork,
const Settings<T>& qpsettings)
{
/*
* The algorithm performs the following step
*
* 1/
* 1.1/ Store solutions of equations
* C(x+alpha dx) - l + ze/mu_in = 0
* C(x+alpha dx) - u + ze/mu_in = 0
*
* 1.2/ Sort the alpha
* 2/
* 2.1
* For each positive alpha compute the first derivative of
* phi(alpha) = [proximal primal dual augmented lagrangian of the subproblem
* evaluated at x_k + alpha dx, y_k + alpha dy, z_k + alpha dz] using function
* "gradient_norm" By construction for alpha = 0, phi'(alpha) <= 0 and
* phi'(alpha) goes to infinity with alpha hence it cancels uniquely at one
* optimal alpha*
*
* while phi'(alpha)<=0 store the derivative (noted last_grad_neg) and
* alpha (last_alpha_neg)
* the first time phi'(alpha) > 0 store the derivative (noted
* first_grad_pos) and alpha (first_alpha_pos), and break the loo
*
* 2.2
* If first_alpha_pos corresponds to the first positive alpha of previous
* loop, then do
* last_alpha_neg = 0
* last_grad_neg = phi'(0)
*
* 2.3
* the optimal alpha is within the interval
* [last_alpha_neg,first_alpha_pos] and can be computed exactly as phi' is
* an affine function in alph
* alpha* = alpha_last_neg
* - last_neg_grad * (alpha_first_pos - alpha_last_neg) /
* (first_pos_grad - last_neg_grad);
*/
const T machine_eps = std::numeric_limits<T>::epsilon();
qpwork.alpha = T(1);
T alpha_(1.);
qpwork.alphas.clear();
// 1.1 add solutions of equations C(x+alpha dx)-l +ze/mu_in = 0 and C(x+alpha
// dx)-u +ze/mu_in = 0
for (isize i = 0; i < qpmodel.n_in; i++) {
if (qpwork.Cdx(i) != 0.) {
alpha_ =
-qpwork.primal_residual_in_scaled_up(i) / (qpwork.Cdx(i) + machine_eps);
if (alpha_ > machine_eps) {
qpwork.alphas.push(alpha_);
}
alpha_ = -qpwork.primal_residual_in_scaled_low(i) /
(qpwork.Cdx(i) + machine_eps);
if (alpha_ > machine_eps) {
qpwork.alphas.push(alpha_);
}
}
}
isize n_alpha = qpwork.alphas.len();
// 1.2 sort the alphas
std::sort(qpwork.alphas.ptr_mut(), qpwork.alphas.ptr_mut() + n_alpha);
isize new_len = std::unique( //
qpwork.alphas.ptr_mut(),
qpwork.alphas.ptr_mut() + n_alpha) -
qpwork.alphas.ptr_mut();
qpwork.alphas.resize(new_len);
n_alpha = qpwork.alphas.len();
if (n_alpha == 0) { //
switch (qpsettings.merit_function_type) {
case MeritFunctionType::GPDAL: {
auto res = gpdal_derivative_results(
qpmodel, qpresults, qpwork, qpsettings, T(0));
qpwork.alpha = -res.b / res.a;
} break;
case MeritFunctionType::PDAL: {
auto res =
primal_dual_derivative_results(qpmodel, qpresults, qpwork, T(0));
qpwork.alpha = -res.b / res.a;
} break;
}
return;
}
auto infty = std::numeric_limits<T>::infinity();
T last_neg_grad = 0;
T alpha_last_neg = 0;
T first_pos_grad = 0;
T alpha_first_pos = infty;
for (isize i = 0; i < n_alpha; ++i) {
alpha_ = qpwork.alphas[i];
/*
* 2.1
* For each positive alpha compute the first derivative of
* phi(alpha) = [proximal augmented lagrangian of the
* subproblem evaluated at x_k + alpha dx]
*
* (By construction for alpha = 0, phi'(alpha) <= 0 and
* phi'(alpha) goes to infinity with alpha hence it cancels
* uniquely at one optimal alpha*
*
* while phi'(alpha)<=0 store the derivative (noted
* last_grad_neg) and alpha (last_alpha_neg
* the first time phi'(alpha) > 0 store the derivative
* (noted first_grad_pos) and alpha (first_alpha_pos), and
* break the loop
*/
T gr(0);
switch (qpsettings.merit_function_type) {
case MeritFunctionType::GPDAL:
gr = gpdal_derivative_results(
qpmodel, qpresults, qpwork, qpsettings, alpha_)
.grad;
break;
case MeritFunctionType::PDAL:
gr = primal_dual_derivative_results(qpmodel, qpresults, qpwork, alpha_)
.grad;
break;
}
if (gr < T(0)) {
alpha_last_neg = alpha_;
last_neg_grad = gr;
} else {
first_pos_grad = gr;
alpha_first_pos = alpha_;
break;
}
}
/*
* 2.2
* If first_alpha_pos corresponds to the first positive alpha of
* previous loop, then do
* last_alpha_neg = 0 and last_grad_neg = phi'(0) using function
* "gradient_norm"
*/
if (alpha_last_neg == T(0)) {
switch (qpsettings.merit_function_type) {
case MeritFunctionType::GPDAL:
last_neg_grad =
gpdal_derivative_results(
qpmodel, qpresults, qpwork, qpsettings, alpha_last_neg)
.grad;
break;
case MeritFunctionType::PDAL:
last_neg_grad = primal_dual_derivative_results(
qpmodel, qpresults, qpwork, alpha_last_neg)
.grad;
break;
}
}
if (alpha_first_pos == infty) {
/*
* 2.3
* the optimal alpha is within the interval
* [last_alpha_neg, +∞)
*/
switch (qpsettings.merit_function_type) {
case MeritFunctionType::GPDAL: {
PrimalDualDerivativeResult<T> res = gpdal_derivative_results(
qpmodel, qpresults, qpwork, qpsettings, 2 * alpha_last_neg + 1);
auto& a = res.a;
auto& b = res.b;
// grad = a * alpha + b
// grad = 0 => alpha = -b/a
qpwork.alpha = -b / a;
} break;
case MeritFunctionType::PDAL: {
PrimalDualDerivativeResult<T> res = primal_dual_derivative_results(
qpmodel, qpresults, qpwork, 2 * alpha_last_neg + 1);
auto& a = res.a;
auto& b = res.b;
// grad = a * alpha + b
// grad = 0 => alpha = -b/a
qpwork.alpha = -b / a;
} break;
}
} else {
/*
* 2.3
* the optimal alpha is within the interval
* [last_alpha_neg,first_alpha_pos] and can be computed exactly as phi'
* is an affine function in alpha
*/
qpwork.alpha = std::abs(alpha_last_neg -
last_neg_grad * (alpha_first_pos - alpha_last_neg) /
(first_pos_grad - last_neg_grad));
}
}
template<typename T>
void
active_set_change(const Model<T>& qpmodel,
Results<T>& qpresults,
Workspace<T>& qpwork)
{
/*
* arguments
* 1/ new_active_set : a vector which contains new active set of the
* problem, namely if
* new_active_set_u = Cx_k-u +z_k*mu_in>= 0
* new_active_set_l = Cx_k-l +z_k*mu_in<=
* then new_active_set = new_active_set_u OR new_active_set_
*
* 2/ current_bijection_map : a vector for which each entry corresponds to
* the current row of C of the current factorization
*
* for example, naming C_initial the initial C matrix of the problem, and
* C_current the one of the current factorization, the
* C_initial[i,:] = C_current[current_bijection_mal[i],:] for all
*
* 3/ n_c : the current number of active_inequalities
* This algorithm ensures that for all new version of C_current in the LDLT
* factorization all row index i < n_c correspond to current active indexes
* (all other correspond to inactive rows
*
* To do so,
* 1/ for initialization
* 1.1/ new_bijection_map = current_bijection_map
* 1.2/ n_c_f = n_
*
* 2/ All active indexes of the current bijection map (i.e
* current_bijection_map(i) < n_c by assumption) which are not active
* anymore in the new active set (new_active_set(i)=false are put at the
* end of new_bijection_map, i.
*
* 2.1/ for all j if new_bijection_map(j) > new_bijection_map(i), then
* new_bijection_map(j)-=1
* 2.2/ n_c_f -=1
* 2.3/ new_bijection_map(i) = n_in-1
*
* 3/ All active indexe of the new active set (new_active_set(i) == true)
* which are not active in the new_bijection_map (new_bijection_map(i) >=
* n_c_f) are put at the end of the current version of C, i.e
* 3.1/ if new_bijection_map(j) < new_bijection_map(i) &&
* new_bijection_map(j) >= n_c_f then new_bijection_map(j)+=1
* 3.2/ new_bijection_map(i) = n_c_f
* 3.3/ n_c_f +=1
*
* It returns finally the new_bijection_map, for which
* new_bijection_map(n_in) = n_c_f
*/
qpwork.dw_aug.setZero();
isize n_c_f = qpwork.n_c;
qpwork.new_bijection_map = qpwork.current_bijection_map;
// suppression pour le nouvel active set, ajout dans le nouvel unactive set
proxsuite::linalg::veg::dynstack::DynStackMut stack{
proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut()
};
{
auto _planned_to_delete = stack.make_new_for_overwrite(
proxsuite::linalg::veg::Tag<isize>{}, isize(qpmodel.n_in));
isize* planned_to_delete = _planned_to_delete.ptr_mut();
isize planned_to_delete_count = 0;
for (isize i = 0; i < qpmodel.n_in; i++) {
if (qpwork.current_bijection_map(i) < qpwork.n_c) {
if (!qpwork.active_inequalities(i)) {
// delete current_bijection_map(i)
planned_to_delete[planned_to_delete_count] =
qpwork.current_bijection_map(i) + qpmodel.dim + qpmodel.n_eq;
++planned_to_delete_count;
for (isize j = 0; j < qpmodel.n_in; j++) {
if (qpwork.new_bijection_map(j) > qpwork.new_bijection_map(i)) {
qpwork.new_bijection_map(j) -= 1;
}
}
n_c_f -= 1;
qpwork.new_bijection_map(i) = qpmodel.n_in - 1;
}
}
}
std::sort(planned_to_delete, planned_to_delete + planned_to_delete_count);
qpwork.ldl.delete_at(planned_to_delete, planned_to_delete_count, stack);
if (planned_to_delete_count > 0) {
qpwork.constraints_changed = true;
}
}
// ajout au nouvel active set, suppression pour le nouvel unactive set
{
auto _planned_to_add = stack.make_new_for_overwrite(
proxsuite::linalg::veg::Tag<isize>{}, qpmodel.n_in);
auto planned_to_add = _planned_to_add.ptr_mut();
isize planned_to_add_count = 0;
T mu_in_neg(-qpresults.info.mu_in);
isize n_c = n_c_f;
for (isize i = 0; i < qpmodel.n_in; i++) {
if (qpwork.active_inequalities(i)) {
if (qpwork.new_bijection_map(i) >= n_c_f) {
// add at the end
planned_to_add[planned_to_add_count] = i;
++planned_to_add_count;
for (isize j = 0; j < qpmodel.n_in; j++) {
if (qpwork.new_bijection_map(j) < qpwork.new_bijection_map(i) &&
qpwork.new_bijection_map(j) >= n_c_f) {
qpwork.new_bijection_map(j) += 1;
}
}
qpwork.new_bijection_map(i) = n_c_f;
n_c_f += 1;
}
}
}
{
isize n = qpmodel.dim;
isize n_eq = qpmodel.n_eq;
LDLT_TEMP_MAT_UNINIT(
T, new_cols, n + n_eq + n_c_f, planned_to_add_count, stack);
for (isize k = 0; k < planned_to_add_count; ++k) {
isize index = planned_to_add[k];
auto col = new_cols.col(k);
col.head(n) = (qpwork.C_scaled.row(index));
col.tail(n_eq + n_c_f).setZero();
col[n + n_eq + n_c + k] = mu_in_neg;
}
qpwork.ldl.insert_block_at(n + n_eq + n_c, new_cols, stack);
}
if (planned_to_add_count > 0) {
qpwork.constraints_changed = true;
}
}
qpwork.n_c = n_c_f;
qpwork.current_bijection_map = qpwork.new_bijection_map;
qpwork.dw_aug.setZero();
}
} // namespace linesearch
} // namespace dense
} // namespace proxqp
} // namespace proxsuite
#endif /* end of include guard PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP */