Program Listing for File solver.hpp
↰ Return to documentation for file (/tmp/ws/src/proxsuite/include/proxsuite/proxqp/sparse/solver.hpp
)
//
// Copyright (c) 2022 INRIA
//
#ifndef PROXSUITE_PROXQP_SPARSE_SOLVER_HPP
#define PROXSUITE_PROXQP_SPARSE_SOLVER_HPP
#include <chrono>
#include <cmath>
#include <proxsuite/linalg/dense/core.hpp>
#include <proxsuite/linalg/sparse/core.hpp>
#include <proxsuite/linalg/sparse/factorize.hpp>
#include <proxsuite/linalg/sparse/update.hpp>
#include <proxsuite/linalg/sparse/rowmod.hpp>
#include <proxsuite/proxqp/dense/views.hpp>
#include <proxsuite/proxqp/settings.hpp>
#include <proxsuite/linalg/veg/vec.hpp>
#include "proxsuite/proxqp/results.hpp"
#include "proxsuite/proxqp/sparse/fwd.hpp"
#include "proxsuite/proxqp/sparse/views.hpp"
#include "proxsuite/proxqp/sparse/model.hpp"
#include "proxsuite/proxqp/sparse/workspace.hpp"
#include "proxsuite/proxqp/sparse/utils.hpp"
#include "proxsuite/proxqp/sparse/preconditioner/ruiz.hpp"
#include "proxsuite/proxqp/sparse/preconditioner/identity.hpp"
#include <iostream>
#include <iomanip>
#include <Eigen/IterativeLinearSolvers>
#include <unsupported/Eigen/IterativeSolvers>
namespace proxsuite {
namespace proxqp {
namespace sparse {
template<typename T, typename I>
void
ldl_solve(VectorViewMut<T> sol,
VectorView<T> rhs,
isize n_tot,
proxsuite::linalg::sparse::MatMut<T, I> ldl,
Eigen::MINRES<detail::AugmentedKkt<T, I>,
Eigen::Upper | Eigen::Lower,
Eigen::IdentityPreconditioner>& iterative_solver,
bool do_ldlt,
proxsuite::linalg::veg::dynstack::DynStackMut stack,
T* ldl_values,
I* perm,
I* ldl_col_ptrs,
I const* perm_inv)
{
LDLT_TEMP_VEC_UNINIT(T, work_, n_tot, stack);
auto rhs_e = rhs.to_eigen();
auto sol_e = sol.to_eigen();
auto zx = proxsuite::linalg::sparse::util::zero_extend;
if (do_ldlt) {
for (isize i = 0; i < n_tot; ++i) {
work_[i] = rhs_e[isize(zx(perm[i]))];
}
proxsuite::linalg::sparse::dense_lsolve<T, I>( //
{ proxsuite::linalg::sparse::from_eigen, work_ },
ldl.as_const());
for (isize i = 0; i < n_tot; ++i) {
work_[i] /= ldl_values[isize(zx(ldl_col_ptrs[i]))];
}
proxsuite::linalg::sparse::dense_ltsolve<T, I>( //
{ proxsuite::linalg::sparse::from_eigen, work_ },
ldl.as_const());
for (isize i = 0; i < n_tot; ++i) {
sol_e[i] = work_[isize(zx(perm_inv[i]))];
}
} else {
work_ = iterative_solver.solve(rhs_e);
sol_e = work_;
}
}
template<typename T, typename I>
void
ldl_iter_solve_noalias(
VectorViewMut<T> sol,
VectorView<T> rhs,
VectorView<T> init_guess,
Results<T> const& results,
Model<T, I> const& data,
isize n_tot,
proxsuite::linalg::sparse::MatMut<T, I> ldl,
Eigen::MINRES<detail::AugmentedKkt<T, I>,
Eigen::Upper | Eigen::Lower,
Eigen::IdentityPreconditioner>& iterative_solver,
bool do_ldlt,
proxsuite::linalg::veg::dynstack::DynStackMut stack,
T* ldl_values,
I* perm,
I* ldl_col_ptrs,
I const* perm_inv,
Settings<T> const& settings,
proxsuite::linalg::sparse::MatMut<T, I> kkt_active,
proxsuite::linalg::veg::SliceMut<bool> active_constraints)
{
auto rhs_e = rhs.to_eigen();
auto sol_e = sol.to_eigen();
if (init_guess.dim == sol.dim) {
sol_e = init_guess.to_eigen();
} else {
sol_e.setZero();
}
LDLT_TEMP_VEC_UNINIT(T, err, n_tot, stack);
T prev_err_norm = std::numeric_limits<T>::infinity();
for (isize solve_iter = 0; solve_iter < settings.nb_iterative_refinement;
++solve_iter) {
auto err_x = err.head(data.dim);
auto err_y = err.segment(data.dim, data.n_eq);
auto err_z = err.tail(data.n_in);
auto sol_x = sol_e.head(data.dim);
auto sol_y = sol_e.segment(data.dim, data.n_eq);
auto sol_z = sol_e.tail(data.n_in); // removed active set condition
err = -rhs_e;
if (solve_iter > 0) {
T mu_eq_neg = -results.info.mu_eq;
T mu_in_neg = -results.info.mu_in;
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL:
// mu_in_neg = -settings.alpha_gpdal*results.info.mu_in;
// break;
// case MeritFunctionType::PDAL:
// mu_in_neg = -results.info.mu_in;
// break;
// }
detail::noalias_symhiv_add(err, kkt_active.to_eigen(), sol_e);
err_x += results.info.rho * sol_x;
err_y += mu_eq_neg * sol_y;
for (isize i = 0; i < data.n_in; ++i) {
err_z[i] += (active_constraints[i] ? mu_in_neg : T(1)) * sol_z[i];
}
}
T err_norm = infty_norm(err);
if (err_norm > prev_err_norm / T(2)) {
break;
}
prev_err_norm = err_norm;
ldl_solve({ proxqp::from_eigen, err },
{ proxqp::from_eigen, err },
n_tot,
ldl,
iterative_solver,
do_ldlt,
stack,
ldl_values,
perm,
ldl_col_ptrs,
perm_inv);
sol_e -= err;
}
}
template<typename T, typename I>
void
ldl_solve_in_place(
VectorViewMut<T> rhs,
VectorView<T> init_guess,
Results<T> const& results,
Model<T, I> const& data,
isize n_tot,
proxsuite::linalg::sparse::MatMut<T, I> ldl,
Eigen::MINRES<detail::AugmentedKkt<T, I>,
Eigen::Upper | Eigen::Lower,
Eigen::IdentityPreconditioner>& iterative_solver,
bool do_ldlt,
proxsuite::linalg::veg::dynstack::DynStackMut stack,
T* ldl_values,
I* perm,
I* ldl_col_ptrs,
I const* perm_inv,
Settings<T> const& settings,
proxsuite::linalg::sparse::MatMut<T, I> kkt_active,
proxsuite::linalg::veg::SliceMut<bool> active_constraints)
{
LDLT_TEMP_VEC_UNINIT(T, tmp, n_tot, stack);
ldl_iter_solve_noalias({ proxqp::from_eigen, tmp },
rhs.as_const(),
init_guess,
results,
data,
n_tot,
ldl,
iterative_solver,
do_ldlt,
stack,
ldl_values,
perm,
ldl_col_ptrs,
perm_inv,
settings,
kkt_active,
active_constraints);
rhs.to_eigen() = tmp;
}
template<typename T, typename I>
auto
inner_reconstructed_matrix(proxsuite::linalg::sparse::MatMut<T, I> ldl)
-> DMat<T>
{
auto ldl_dense = ldl.to_eigen().toDense();
auto l = DMat<T>(ldl_dense.template triangularView<Eigen::UnitLower>());
auto lt = l.transpose();
auto d = ldl_dense.diagonal().asDiagonal();
auto mat = DMat<T>(l * d * lt);
return mat;
}
template<typename T, typename I>
auto
reconstructed_matrix(proxsuite::linalg::sparse::MatMut<T, I> ldl,
I const* perm_inv,
isize n_tot) -> DMat<T>
{
auto mat = inner_reconstructed_matrix(ldl);
auto mat_backup = mat;
for (isize i = 0; i < n_tot; ++i) {
for (isize j = 0; j < n_tot; ++j) {
mat(i, j) = mat_backup(perm_inv[i], perm_inv[j]);
}
}
return mat;
}
template<typename T, typename I>
auto
reconstruction_error(proxsuite::linalg::sparse::MatMut<T, I> ldl,
I const* perm_inv,
Results<T> const& results,
Model<T, I> const& data,
isize n_tot,
proxsuite::linalg::sparse::MatMut<T, I> kkt_active,
proxsuite::linalg::veg::SliceMut<bool> active_constraints)
-> DMat<T>
{
T mu_eq_neg = -results.info.mu_eq;
T mu_in_neg = -results.info.mu_in;
auto diff = DMat<T>(
reconstructed_matrix(ldl, perm_inv, n_tot) -
DMat<T>(
DMat<T>(kkt_active.to_eigen()).template selfadjointView<Eigen::Upper>()));
diff.diagonal().head(data.dim).array() -= results.info.rho;
diff.diagonal().segment(data.dim, data.n_eq).array() -= mu_eq_neg;
for (isize i = 0; i < data.n_in; ++i) {
diff.diagonal()[data.dim + data.n_eq + i] -=
active_constraints[i] ? mu_in_neg : T(1);
}
return diff;
}
template<typename T>
struct PrimalDualGradResult
{
T a;
T b;
T grad;
VEG_REFLECT(PrimalDualGradResult, a, b, grad);
};
template<typename T, typename I, typename P>
void
qp_solve(Results<T>& results,
Model<T, I>& data,
const Settings<T>& settings,
Workspace<T, I>& work,
P& precond)
{
if (settings.compute_timings) {
work.timer.stop();
work.timer.start();
}
if (work.internal
.dirty) // the following is used when a solve has already been executed
// (and without any intermediary model update)
{
proxsuite::linalg::sparse::MatMut<T, I> kkt_unscaled =
data.kkt_mut_unscaled();
auto kkt_top_n_rows = detail::top_rows_mut_unchecked(
proxsuite::linalg::veg::unsafe, kkt_unscaled, data.dim);
proxsuite::linalg::sparse::MatMut<T, I> H_unscaled =
detail::middle_cols_mut(kkt_top_n_rows, 0, data.dim, data.H_nnz);
proxsuite::linalg::sparse::MatMut<T, I> AT_unscaled =
detail::middle_cols_mut(kkt_top_n_rows, data.dim, data.n_eq, data.A_nnz);
proxsuite::linalg::sparse::MatMut<T, I> CT_unscaled =
detail::middle_cols_mut(
kkt_top_n_rows, data.dim + data.n_eq, data.n_in, data.C_nnz);
SparseMat<T, I> H_triu =
H_unscaled.to_eigen().template triangularView<Eigen::Upper>();
sparse::QpView<T, I> qp = {
{ proxsuite::linalg::sparse::from_eigen, H_triu },
{ proxsuite::linalg::sparse::from_eigen, data.g },
{ proxsuite::linalg::sparse::from_eigen, AT_unscaled.to_eigen() },
{ proxsuite::linalg::sparse::from_eigen, data.b },
{ proxsuite::linalg::sparse::from_eigen, CT_unscaled.to_eigen() },
{ proxsuite::linalg::sparse::from_eigen, data.l },
{ proxsuite::linalg::sparse::from_eigen, data.u }
};
switch (settings.initial_guess) { // the following is used when one solve
// has already been executed
case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: {
results.cleanup(settings);
break;
}
case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: {
// keep solutions but restart workspace and results
results.cold_start(settings);
precond.scale_primal_in_place(
{ proxsuite::proxqp::from_eigen, results.x });
precond.scale_dual_in_place_eq(
{ proxsuite::proxqp::from_eigen, results.y });
precond.scale_dual_in_place_in(
{ proxsuite::proxqp::from_eigen, results.z });
break;
}
case InitialGuessStatus::NO_INITIAL_GUESS: {
results.cleanup(settings);
break;
}
case InitialGuessStatus::WARM_START: {
results.cold_start(settings); // because there was already a solve,
// precond was already computed if set so
precond.scale_primal_in_place(
{ proxsuite::proxqp::from_eigen,
results.x }); // it contains the value given in entry for warm start
precond.scale_dual_in_place_eq(
{ proxsuite::proxqp::from_eigen, results.y });
precond.scale_dual_in_place_in(
{ proxsuite::proxqp::from_eigen, results.z });
break;
}
case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: {
// keep workspace and results solutions except statistics
results.cleanup_statistics();
precond.scale_primal_in_place(
{ proxsuite::proxqp::from_eigen, results.x });
precond.scale_dual_in_place_eq(
{ proxsuite::proxqp::from_eigen, results.y });
precond.scale_dual_in_place_in(
{ proxsuite::proxqp::from_eigen, results.z });
break;
}
}
work.setup_impl(
qp,
data,
settings,
false,
precond,
P::scale_qp_in_place_req(
proxsuite::linalg::veg::Tag<T>{}, data.dim, data.n_eq, data.n_in));
} else {
// the following is used for a first solve after initializing or updating
// the Qp object
switch (settings.initial_guess) {
case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: {
break;
}
case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: {
precond.scale_primal_in_place(
{ proxsuite::proxqp::from_eigen,
results.x }); // meaningful for when there is an upate of the model
// and one wants to warm start with previous result
precond.scale_dual_in_place_eq(
{ proxsuite::proxqp::from_eigen, results.y });
precond.scale_dual_in_place_in(
{ proxsuite::proxqp::from_eigen, results.z });
break;
}
case InitialGuessStatus::NO_INITIAL_GUESS: {
break;
}
case InitialGuessStatus::WARM_START: {
precond.scale_primal_in_place(
{ proxsuite::proxqp::from_eigen, results.x });
precond.scale_dual_in_place_eq(
{ proxsuite::proxqp::from_eigen, results.y });
precond.scale_dual_in_place_in(
{ proxsuite::proxqp::from_eigen, results.z });
break;
}
case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: {
precond.scale_primal_in_place(
{ proxsuite::proxqp::from_eigen,
results.x }); // meaningful for when there is an upate of the model
// and one wants to warm start with previous result
precond.scale_dual_in_place_eq(
{ proxsuite::proxqp::from_eigen, results.y });
precond.scale_dual_in_place_in(
{ proxsuite::proxqp::from_eigen, results.z });
break;
}
}
}
if (settings.verbose) {
sparse::print_setup_header(settings, results, data);
}
using namespace proxsuite::linalg::veg::literals;
namespace util = proxsuite::linalg::sparse::util;
auto zx = util::zero_extend;
proxsuite::linalg::veg::dynstack::DynStackMut stack = work.stack_mut();
isize n = data.dim;
isize n_eq = data.n_eq;
isize n_in = data.n_in;
isize n_tot = n + n_eq + n_in;
VectorViewMut<T> x{ proxqp::from_eigen, results.x };
VectorViewMut<T> y{ proxqp::from_eigen, results.y };
VectorViewMut<T> z{ proxqp::from_eigen, results.z };
proxsuite::linalg::sparse::MatMut<T, I> kkt = data.kkt_mut();
auto kkt_top_n_rows =
detail::top_rows_mut_unchecked(proxsuite::linalg::veg::unsafe, kkt, n);
proxsuite::linalg::sparse::MatMut<T, I> H_scaled =
detail::middle_cols_mut(kkt_top_n_rows, 0, n, data.H_nnz);
proxsuite::linalg::sparse::MatMut<T, I> AT_scaled =
detail::middle_cols_mut(kkt_top_n_rows, n, n_eq, data.A_nnz);
proxsuite::linalg::sparse::MatMut<T, I> CT_scaled =
detail::middle_cols_mut(kkt_top_n_rows, n + n_eq, n_in, data.C_nnz);
auto& g_scaled_e = work.internal.g_scaled;
auto& b_scaled_e = work.internal.b_scaled;
auto& l_scaled_e = work.internal.l_scaled;
auto& u_scaled_e = work.internal.u_scaled;
QpViewMut<T, I> qp_scaled = {
H_scaled,
{ proxsuite::linalg::sparse::from_eigen, g_scaled_e },
AT_scaled,
{ proxsuite::linalg::sparse::from_eigen, b_scaled_e },
CT_scaled,
{ proxsuite::linalg::sparse::from_eigen, l_scaled_e },
{ proxsuite::linalg::sparse::from_eigen, u_scaled_e },
};
T const dual_feasibility_rhs_2 = infty_norm(data.g);
// auto ldl_col_ptrs = work.ldl_col_ptrs_mut();
auto ldl_col_ptrs = work.internal.ldl.col_ptrs.ptr_mut();
proxsuite::linalg::veg::Tag<I> itag;
proxsuite::linalg::veg::Tag<T> xtag;
bool do_ldlt = work.internal.do_ldlt;
isize ldlt_ntot = do_ldlt ? n_tot : 0;
auto _perm = stack.make_new_for_overwrite(itag, ldlt_ntot);
I* perm_inv = work.internal.ldl.perm_inv.ptr_mut();
I* perm = _perm.ptr_mut();
if (do_ldlt) {
// compute perm from perm_inv
for (isize i = 0; i < n_tot; ++i) {
perm[isize(zx(perm_inv[i]))] = I(i);
}
}
I* kkt_nnz_counts = work.internal.kkt_nnz_counts.ptr_mut();
auto& iterative_solver = *work.internal.matrix_free_solver.get();
isize C_active_nnz = 0;
switch (settings.initial_guess) {
case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: {
// H and A are always active
for (usize j = 0; j < usize(n + n_eq); ++j) {
kkt_nnz_counts[isize(j)] = I(kkt.col_end(j) - kkt.col_start(j));
}
// ineq constraints initially inactive
for (isize j = 0; j < n_in; ++j) {
kkt_nnz_counts[n + n_eq + j] = 0;
work.active_inequalities[j] = false;
}
break;
}
case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: {
// keep solutions + restart workspace and results except rho and mu : done
// in setup
// H and A are always active
for (usize j = 0; j < usize(n + n_eq); ++j) {
kkt_nnz_counts[isize(j)] = I(kkt.col_end(j) - kkt.col_start(j));
}
// keep constraints inactive from previous solution
for (isize j = 0; j < n_in; ++j) {
if (results.z(j) != 0) {
kkt_nnz_counts[n + n_eq + j] = I(kkt.col_end(usize(n + n_eq + j)) -
kkt.col_start(usize(n + n_eq + j)));
work.active_inequalities[j] = true;
C_active_nnz += kkt_nnz_counts[n + n_eq + j];
} else {
kkt_nnz_counts[n + n_eq + j] = 0;
work.active_inequalities[j] = false;
}
}
break;
}
case InitialGuessStatus::NO_INITIAL_GUESS: {
// already set to zero in the setup
// H and A are always active
for (usize j = 0; j < usize(n + n_eq); ++j) {
kkt_nnz_counts[isize(j)] = I(kkt.col_end(j) - kkt.col_start(j));
}
// ineq constraints initially inactive
for (isize j = 0; j < n_in; ++j) {
kkt_nnz_counts[n + n_eq + j] = 0;
work.active_inequalities[j] = false;
}
break;
}
case InitialGuessStatus::WARM_START: {
// keep previous solution
// H and A are always active
for (usize j = 0; j < usize(n + n_eq); ++j) {
kkt_nnz_counts[isize(j)] = I(kkt.col_end(j) - kkt.col_start(j));
}
// keep constraints inactive from previous solution
for (isize j = 0; j < n_in; ++j) {
if (results.z(j) != 0) {
kkt_nnz_counts[n + n_eq + j] = I(kkt.col_end(usize(n + n_eq + j)) -
kkt.col_start(usize(n + n_eq + j)));
work.active_inequalities[j] = true;
C_active_nnz += kkt_nnz_counts[n + n_eq + j];
} else {
kkt_nnz_counts[n + n_eq + j] = 0;
work.active_inequalities[j] = false;
}
}
break;
}
case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: {
// keep workspace and results solutions except statistics
// H and A are always active
for (usize j = 0; j < usize(n + n_eq); ++j) {
kkt_nnz_counts[isize(j)] = I(kkt.col_end(j) - kkt.col_start(j));
}
// keep constraints inactive from previous solution
for (isize j = 0; j < n_in; ++j) {
if (results.z(j) != 0) {
kkt_nnz_counts[n + n_eq + j] = I(kkt.col_end(usize(n + n_eq + j)) -
kkt.col_start(usize(n + n_eq + j)));
work.active_inequalities[j] = true;
C_active_nnz += kkt_nnz_counts[n + n_eq + j];
} else {
kkt_nnz_counts[n + n_eq + j] = 0;
work.active_inequalities[j] = false;
}
}
break;
}
}
proxsuite::linalg::sparse::MatMut<T, I> kkt_active = {
proxsuite::linalg::sparse::from_raw_parts,
n_tot,
n_tot,
data.H_nnz + data.A_nnz + C_active_nnz,
kkt.col_ptrs_mut(),
kkt_nnz_counts,
kkt.row_indices_mut(),
kkt.values_mut(),
};
I* etree = work.internal.ldl.etree.ptr_mut();
I* ldl_nnz_counts = work.internal.ldl.nnz_counts.ptr_mut();
I* ldl_row_indices = work.internal.ldl.row_indices.ptr_mut();
T* ldl_values = work.internal.ldl.values.ptr_mut();
proxsuite::linalg::veg::SliceMut<bool> active_constraints =
work.active_inequalities.as_mut();
proxsuite::linalg::sparse::MatMut<T, I> ldl = {
proxsuite::linalg::sparse::from_raw_parts,
n_tot,
n_tot,
0,
ldl_col_ptrs,
do_ldlt ? ldl_nnz_counts : nullptr, // si do_ldlt est vrai do ldl_nnz_counts
ldl_row_indices,
ldl_values,
};
T bcl_eta_ext_init = pow(T(0.1), settings.alpha_bcl);
T bcl_eta_ext = bcl_eta_ext_init;
T bcl_eta_in(1);
T eps_in_min = std::min(settings.eps_abs, T(1e-9));
auto x_e = x.to_eigen();
auto y_e = y.to_eigen();
auto z_e = z.to_eigen();
sparse::refactorize<T, I>(
work, results, settings, kkt_active, active_constraints, data, stack, xtag);
switch (settings.initial_guess) {
case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: {
LDLT_TEMP_VEC_UNINIT(T, rhs, n_tot, stack);
LDLT_TEMP_VEC_UNINIT(T, no_guess, 0, stack);
rhs.head(n) = -g_scaled_e;
rhs.segment(n, n_eq) = b_scaled_e;
rhs.segment(n + n_eq, n_in).setZero();
ldl_solve_in_place({ proxqp::from_eigen, rhs },
{ proxqp::from_eigen, no_guess },
results,
data,
n_tot,
ldl,
iterative_solver,
do_ldlt,
stack,
ldl_values,
perm,
ldl_col_ptrs,
perm_inv,
settings,
kkt_active,
active_constraints);
x_e = rhs.head(n);
y_e = rhs.segment(n, n_eq);
z_e = rhs.segment(n + n_eq, n_in);
break;
}
case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: {
// keep solutions but restart workspace and results
break;
}
case InitialGuessStatus::NO_INITIAL_GUESS: {
// already set to zero in the setup
break;
}
case InitialGuessStatus::WARM_START: {
// keep previous solution
break;
}
case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: {
// keep workspace and results solutions except statistics
break;
}
}
T rhs_duality_gap(0);
for (isize iter = 0; iter < settings.max_iter; ++iter) {
results.info.iter_ext += 1;
if (iter == settings.max_iter) {
break;
}
T new_bcl_mu_eq = results.info.mu_eq;
T new_bcl_mu_in = results.info.mu_in;
T new_bcl_mu_eq_inv = results.info.mu_eq_inv;
T new_bcl_mu_in_inv = results.info.mu_in_inv;
{
T primal_feasibility_eq_rhs_0;
T primal_feasibility_in_rhs_0;
T dual_feasibility_rhs_0(0);
T dual_feasibility_rhs_1(0);
T dual_feasibility_rhs_3(0);
LDLT_TEMP_VEC_UNINIT(T, primal_residual_eq_scaled, n_eq, stack);
LDLT_TEMP_VEC_UNINIT(T, primal_residual_in_scaled_lo, n_in, stack);
LDLT_TEMP_VEC_UNINIT(T, primal_residual_in_scaled_up, n_in, stack);
LDLT_TEMP_VEC_UNINIT(T, dual_residual_scaled, n, stack);
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
auto is_primal_feasible = [&](T primal_feasibility_lhs) -> bool {
T rhs_pri = settings.eps_abs;
if (settings.eps_rel != 0) {
rhs_pri +=
settings.eps_rel * std::max({ primal_feasibility_eq_rhs_0,
primal_feasibility_in_rhs_0 });
}
return primal_feasibility_lhs <= rhs_pri;
};
auto is_dual_feasible = [&](T dual_feasibility_lhs) -> bool {
T rhs_dua = settings.eps_abs;
if (settings.eps_rel != 0) {
rhs_dua += settings.eps_rel * std::max({
dual_feasibility_rhs_0,
dual_feasibility_rhs_1,
dual_feasibility_rhs_2,
dual_feasibility_rhs_3,
});
}
return dual_feasibility_lhs <= rhs_dua;
};
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
VEG_BIND( // ?
auto,
(primal_feasibility_lhs, dual_feasibility_lhs),
detail::unscaled_primal_dual_residual(work,
results,
primal_residual_eq_scaled,
primal_residual_in_scaled_lo,
primal_residual_in_scaled_up,
dual_residual_scaled,
primal_feasibility_eq_rhs_0,
primal_feasibility_in_rhs_0,
dual_feasibility_rhs_0,
dual_feasibility_rhs_1,
dual_feasibility_rhs_3,
rhs_duality_gap,
precond,
data,
qp_scaled.as_const(),
detail::vec_mut(x_e),
detail::vec_mut(y_e),
detail::vec_mut(z_e),
stack));
/*put in debug mode
if (settings.verbose) {
std::cout << "-------- outer iteration: " << iter << " primal
residual "
<< primal_feasibility_lhs
<< " dual residual "
<< dual_feasibility_lhs <<
" mu_in " << results.info.mu_in
<< " bcl_eta_ext " <<
bcl_eta_ext << " bcl_eta_in "
<< bcl_eta_in <<
std::endl;
}
*/
if (settings.verbose) {
LDLT_TEMP_VEC_UNINIT(T, tmp, n, stack);
tmp.setZero();
detail::noalias_symhiv_add(tmp, qp_scaled.H.to_eigen(), x_e);
precond.unscale_dual_residual_in_place({ proxqp::from_eigen, tmp });
precond.unscale_primal_in_place({ proxqp::from_eigen, x_e });
precond.unscale_dual_in_place_eq({ proxqp::from_eigen, y_e });
precond.unscale_dual_in_place_in({ proxqp::from_eigen, z_e });
tmp *= 0.5;
tmp += data.g;
results.info.objValue = (tmp).dot(x_e);
std::cout << "\033[1;32m[outer iteration " << iter + 1 << "]\033[0m"
<< std::endl;
std::cout << std::scientific << std::setw(2) << std::setprecision(2)
<< "| primal residual=" << primal_feasibility_lhs
<< " | dual residual=" << dual_feasibility_lhs
<< " | duality gap=" << results.info.duality_gap
<< " | mu_in=" << results.info.mu_in
<< " | rho=" << results.info.rho << std::endl;
results.info.pri_res = primal_feasibility_lhs;
results.info.dua_res = dual_feasibility_lhs;
precond.scale_primal_in_place(VectorViewMut<T>{ from_eigen, x_e });
precond.scale_dual_in_place_eq(VectorViewMut<T>{ from_eigen, y_e });
precond.scale_dual_in_place_in(VectorViewMut<T>{ from_eigen, z_e });
}
if (is_primal_feasible(primal_feasibility_lhs) &&
is_dual_feasible(dual_feasibility_lhs)) {
if (settings.check_duality_gap) {
if (std::fabs(results.info.duality_gap) <=
settings.eps_duality_gap_abs +
settings.eps_duality_gap_rel * rhs_duality_gap) {
results.info.pri_res = primal_feasibility_lhs;
results.info.dua_res = dual_feasibility_lhs;
results.info.status = QPSolverOutput::PROXQP_SOLVED;
break;
}
} else {
results.info.pri_res = primal_feasibility_lhs;
results.info.dua_res = dual_feasibility_lhs;
results.info.status = QPSolverOutput::PROXQP_SOLVED;
break;
}
}
LDLT_TEMP_VEC_UNINIT(T, x_prev_e, n, stack);
LDLT_TEMP_VEC_UNINIT(T, y_prev_e, n_eq, stack);
LDLT_TEMP_VEC_UNINIT(T, z_prev_e, n_in, stack);
LDLT_TEMP_VEC(T, dw_prev, n_tot, stack);
x_prev_e = x_e;
y_prev_e = y_e;
z_prev_e = z_e;
// Cx + 1/mu_in * z_prev
primal_residual_in_scaled_up += results.info.mu_in * z_prev_e;
// switch (settings.merit_function_type) { NOT activated for the moment
// case MeritFunctionType::GPDAL:
// primal_residual_in_scaled_up +=
// (settings.alpha_gpdal - 1.) * results.info.mu_in * results.z;
// break;
// case MeritFunctionType::PDAL:
// break;
// }
primal_residual_in_scaled_lo = primal_residual_in_scaled_up;
// Cx - l + 1/mu_in * z_prev
primal_residual_in_scaled_lo -= l_scaled_e;
// Cx - u + 1/mu_in * z_prev
primal_residual_in_scaled_up -= u_scaled_e;
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
auto primal_dual_newton_semi_smooth = [&]() -> void {
for (isize iter_inner = 0; iter_inner < settings.max_iter_in;
++iter_inner) {
LDLT_TEMP_VEC_UNINIT(T, dw, n_tot, stack);
if (iter_inner == settings.max_iter_in - 1) {
results.info.iter += settings.max_iter_in;
break;
}
// primal_dual_semi_smooth_newton_step
{
LDLT_TEMP_VEC_UNINIT(bool, new_active_constraints, n_in, stack);
auto rhs = dw;
work.active_set_low.array() =
primal_residual_in_scaled_lo.array() <= 0;
work.active_set_up.array() =
primal_residual_in_scaled_up.array() >= 0;
new_active_constraints = work.active_set_low || work.active_set_up;
// active set change
if (n_in > 0) {
bool removed = false;
bool added = false;
for (isize i = 0; i < n_in; ++i) {
bool was_active = active_constraints[i];
bool is_active = new_active_constraints[i];
isize idx = n + n_eq + i;
usize col_nnz =
zx(kkt.col_end(usize(idx))) - zx(kkt.col_start(usize(idx)));
if (is_active && !was_active) {
added = true;
kkt_active.nnz_per_col_mut()[idx] = I(col_nnz);
kkt_active._set_nnz(kkt_active.nnz() + isize(col_nnz));
if (do_ldlt) {
proxsuite::linalg::sparse::VecRef<T, I> new_col{
proxsuite::linalg::sparse::from_raw_parts,
n_tot,
isize(col_nnz),
kkt.row_indices() + zx(kkt.col_start(usize(idx))),
kkt.values() + zx(kkt.col_start(usize(idx))),
};
T mu_in_neg = -results.info.mu_in;
// switch (settings.merit_function_type)
// {
// case MeritFunctionType::GPDAL:
// mu_in_neg = -settings.alpha_gpdal * results.info.mu_in;
// break;
// case MeritFunctionType::PDAL:
// mu_in_neg = -results.info.mu_in;
// break;
// }
ldl = proxsuite::linalg::sparse::add_row(
ldl, etree, perm_inv, idx, new_col, mu_in_neg, stack);
}
active_constraints[i] = new_active_constraints[i];
} else if (!is_active && was_active) {
removed = true;
kkt_active.nnz_per_col_mut()[idx] = 0;
kkt_active._set_nnz(kkt_active.nnz() - isize(col_nnz));
if (do_ldlt) {
ldl = proxsuite::linalg::sparse::delete_row(
ldl, etree, perm_inv, idx, stack);
}
active_constraints[i] = new_active_constraints[i];
}
}
if (!do_ldlt) {
if (removed || added) {
refactorize(work,
results,
settings,
kkt_active,
active_constraints,
data,
stack,
xtag);
}
}
}
rhs.setZero();
rhs.head(n) = -dual_residual_scaled;
rhs.segment(n, n_eq) = -primal_residual_eq_scaled;
for (isize i = 0; i < n_in; ++i) {
if (work.active_set_up(i)) {
rhs(n + n_eq + i) =
results.info.mu_in * z_e(i) - primal_residual_in_scaled_up(i);
} else if (work.active_set_low(i)) {
rhs(n + n_eq + i) =
results.info.mu_in * z_e(i) - primal_residual_in_scaled_lo(i);
} else {
rhs(n + n_eq + i) = -z_e(i);
rhs.head(n) += z_e(i) * CT_scaled.to_eigen().col(i);
}
}
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL:
// for (isize i = 0; i < n_in; ++i) {
// if (work.active_set_up(i)) {
// rhs(n + n_eq + i) =
// -primal_residual_in_scaled_up(i) +
// z_e(i) * results.info.mu_in * settings.alpha_gpdal;
// } else if (work.active_set_low(i)) {
// rhs(n + n_eq + i) =
// -primal_residual_in_scaled_lo(i) +
// z_e(i) * results.info.mu_in * settings.alpha_gpdal;
// } else {
// rhs(n + n_eq + i) = -z_e(i);
// rhs.head(n) += z_e(i) * CT_scaled.to_eigen().col(i);
// }
// }
// break;
// case MeritFunctionType::PDAL:
// for (isize i = 0; i < n_in; ++i) {
// if (work.active_set_up(i)) {
// rhs(n + n_eq + i) = results.info.mu_in * z_e(i) -
// primal_residual_in_scaled_up(i);
// } else if (work.active_set_low(i)) {
// rhs(n + n_eq + i) = results.info.mu_in * z_e(i) -
// primal_residual_in_scaled_lo(i);
// } else {
// rhs(n + n_eq + i) = -z_e(i);
// rhs.head(n) += z_e(i) * CT_scaled.to_eigen().col(i);
// }
// }
// break;
// }
ldl_solve_in_place(
{ proxqp::from_eigen, rhs },
{ proxqp::from_eigen,
dw_prev }, // todo: MAJ dw_prev avec dw pour avoir meilleur
// guess sur les solve in place
results,
data,
n_tot,
ldl,
iterative_solver,
do_ldlt,
stack,
ldl_values,
perm,
ldl_col_ptrs,
perm_inv,
settings,
kkt_active,
active_constraints);
}
auto dx = dw.head(n);
auto dy = dw.segment(n, n_eq);
auto dz = dw.segment(n + n_eq, n_in);
LDLT_TEMP_VEC(T, Hdx, n, stack);
LDLT_TEMP_VEC(T, Adx, n_eq, stack);
LDLT_TEMP_VEC(T, Cdx, n_in, stack);
LDLT_TEMP_VEC(T, ATdy, n, stack);
LDLT_TEMP_VEC(T, CTdz, n, stack);
detail::noalias_symhiv_add(Hdx, H_scaled.to_eigen(), dx);
detail::noalias_gevmmv_add(Adx, ATdy, AT_scaled.to_eigen(), dx, dy);
detail::noalias_gevmmv_add(Cdx, CTdz, CT_scaled.to_eigen(), dx, dz);
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL:
// Cdx.noalias() +=
// (settings.alpha_gpdal - 1.) * results.info.mu_in * dz;
// break;
// case MeritFunctionType::PDAL:
// break;
// }
T alpha = 1;
// primal dual line search
if (n_in > 0) {
auto primal_dual_gradient_norm =
[&](T alpha_cur) -> PrimalDualGradResult<T> {
LDLT_TEMP_VEC_UNINIT(T, Cdx_active, n_in, stack);
LDLT_TEMP_VEC_UNINIT(T, active_part_z, n_in, stack);
{
LDLT_TEMP_VEC_UNINIT(T, tmp_lo, n_in, stack);
LDLT_TEMP_VEC_UNINIT(T, tmp_up, n_in, stack);
auto zero = Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_in);
tmp_lo = primal_residual_in_scaled_lo + alpha_cur * Cdx;
tmp_up = primal_residual_in_scaled_up + alpha_cur * Cdx;
Cdx_active =
(tmp_lo.array() < 0 || tmp_up.array() > 0).select(Cdx, zero);
active_part_z = (tmp_lo.array() < 0)
.select(primal_residual_in_scaled_lo, zero) +
(tmp_up.array() > 0)
.select(primal_residual_in_scaled_up, zero);
}
T a = dx.dot(Hdx) + //
results.info.rho * dx.squaredNorm() + //
results.info.mu_eq_inv * Adx.squaredNorm() +
+results.info.mu_in_inv * Cdx_active.squaredNorm() +
results.info.nu * results.info.mu_eq *
(results.info.mu_eq_inv * Adx - dy).squaredNorm() +
results.info.nu * results.info.mu_in *
(results.info.mu_in_inv * Cdx_active - dz).squaredNorm();
T b =
x_e.dot(Hdx) + //
(results.info.rho * (x_e - x_prev_e) + g_scaled_e).dot(dx) + //
Adx.dot(results.info.mu_eq_inv * primal_residual_eq_scaled +
y_e) + //
results.info.mu_in_inv * Cdx_active.dot(active_part_z) + //
results.info.nu * primal_residual_eq_scaled.dot(
results.info.mu_eq_inv * Adx - dy) + //
results.info.nu *
(active_part_z - results.info.mu_in * z_e)
.dot(results.info.mu_in_inv * Cdx_active - dz);
return {
a,
b,
a * alpha_cur + b,
};
};
// auto gpdal_derivative_results =
// [&](T alpha_cur) -> PrimalDualGradResult<T> {
// LDLT_TEMP_VEC_UNINIT(T, Cdx_active, n_in, stack);
// LDLT_TEMP_VEC_UNINIT(T, active_part_z, n_in, stack);
// {
// LDLT_TEMP_VEC_UNINIT(T, tmp_lo, n_in, stack);
// LDLT_TEMP_VEC_UNINIT(T, tmp_up, n_in, stack);
// auto zero = Eigen::Matrix<T, Eigen::Dynamic, 1>::Zero(n_in);
// tmp_lo = primal_residual_in_scaled_lo + alpha_cur * Cdx;
// tmp_up = primal_residual_in_scaled_up + alpha_cur * Cdx;
// Cdx_active =
// (tmp_lo.array() < 0 || tmp_up.array() > 0).select(Cdx,
// zero);
// active_part_z = (tmp_lo.array() < 0)
// .select(primal_residual_in_scaled_lo, zero)
// +
// (tmp_up.array() > 0)
// .select(primal_residual_in_scaled_up,
// zero);
// }
// T a = dx.dot(Hdx) + //
// results.info.rho * dx.squaredNorm() + //
// results.info.mu_eq_inv * Adx.squaredNorm() +
// +results.info.mu_in_inv * Cdx_active.squaredNorm() /
// settings.alpha_gpdal + results.info.mu_eq_inv *
// (Adx - results.info.mu_eq *dy).squaredNorm() +
// results.info.mu_in * (1. - settings.alpha_gpdal) *
// (dz).squaredNorm();
// T b =
// x_e.dot(Hdx) + // (results.info.rho * (x_e - x_prev_e) +
// g_scaled_e).dot(dx) + // results.info.mu_eq_inv * Adx.dot(
// primal_residual_eq_scaled +
// y_e * results.info.mu_eq) + //
// results.info.mu_in_inv * Cdx_active.dot(active_part_z) /
// settings.alpha_gpdal + //
// results.info.mu_eq_inv * primal_residual_eq_scaled.dot( Adx -
// dy * results.info.mu_eq) + //
// (z_e).dot(dz) * results.info.mu_in *
// (1. - settings.alpha_gpdal);
// return {
// a,
// b,
// a * alpha_cur + b,
// };
// };
LDLT_TEMP_VEC_UNINIT(T, alphas, 2 * n_in, stack);
isize alphas_count = 0;
const T machine_eps = std::numeric_limits<T>::epsilon();
for (isize i = 0; i < n_in; ++i) {
T alpha_candidates[2] = {
-primal_residual_in_scaled_lo(i) / (Cdx(i) + machine_eps),
-primal_residual_in_scaled_up(i) / (Cdx(i) + machine_eps),
};
for (auto alpha_candidate : alpha_candidates) {
if (alpha_candidate > machine_eps) {
alphas[alphas_count] = alpha_candidate;
++alphas_count;
}
}
}
std::sort(alphas.data(), alphas.data() + alphas_count);
alphas_count =
std::unique(alphas.data(), alphas.data() + alphas_count) -
alphas.data();
if (alphas_count > 0) { //&& alphas[0] <= 1
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 < alphas_count; ++i) {
T alpha_cur = alphas[i];
T gr = primal_dual_gradient_norm(alpha_cur).grad;
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL:
// gr = gpdal_derivative_results(alpha_cur).grad;
// break;
// case MeritFunctionType::PDAL:
// gr = primal_dual_gradient_norm(alpha_cur).grad;
// break;
// }
if (gr < 0) {
alpha_last_neg = alpha_cur;
last_neg_grad = gr;
} else {
first_pos_grad = gr;
alpha_first_pos = alpha_cur;
break;
}
}
if (alpha_last_neg == 0) {
last_neg_grad =
primal_dual_gradient_norm(alpha_last_neg).grad;
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL:
// last_neg_grad =
// gpdal_derivative_results(alpha_last_neg).grad;
// break;
// case MeritFunctionType::PDAL:
// last_neg_grad =
// primal_dual_gradient_norm(alpha_last_neg).grad;
// break;
// }
}
if (alpha_first_pos == infty) {
auto res = primal_dual_gradient_norm(2 * alpha_last_neg + 1);
alpha = -res.b / res.a;
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL: {
// auto res =
// gpdal_derivative_results(2 * alpha_last_neg + 1);
// alpha = -res.b / res.a;
// } break;
// case MeritFunctionType::PDAL: {
// auto res =
// primal_dual_gradient_norm(2 * alpha_last_neg + 1);
// alpha = -res.b / res.a;
// } break;
// }
} else {
alpha = alpha_last_neg -
last_neg_grad * (alpha_first_pos - alpha_last_neg) /
(first_pos_grad - last_neg_grad);
}
}
} else {
auto res = primal_dual_gradient_norm(T(0));
alpha = -res.b / res.a;
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL: {
// auto res = gpdal_derivative_results(T(0));
// alpha = -res.b / res.a;
// } break;
// case MeritFunctionType::PDAL: {
// auto res = primal_dual_gradient_norm(T(0));
// alpha = -res.b / res.a;
// } break;
// }
}
}
if (alpha * infty_norm(dw) < T(1e-11) && iter_inner > 0) {
results.info.iter += iter_inner + 1;
return;
}
x_e += alpha * dx;
y_e += alpha * dy;
z_e += alpha * dz;
dual_residual_scaled +=
alpha * (Hdx + ATdy + CTdz + results.info.rho * dx);
primal_residual_eq_scaled += alpha * (Adx - results.info.mu_eq * dy);
primal_residual_in_scaled_lo += alpha * Cdx;
primal_residual_in_scaled_up += alpha * Cdx;
T err_in = std::max({
(infty_norm(helpers::negative_part(primal_residual_in_scaled_lo) +
helpers::positive_part(primal_residual_in_scaled_up) -
results.info.mu_in * z_e)),
(infty_norm(primal_residual_eq_scaled)),
(infty_norm(dual_residual_scaled)),
});
// switch (settings.merit_function_type) {
// case MeritFunctionType::GPDAL:
// err_in = std::max({
// (infty_norm(
// helpers::negative_part(primal_residual_in_scaled_lo) +
// helpers::positive_part(primal_residual_in_scaled_up) -
// settings.alpha_gpdal * results.info.mu_in * z_e)),
// (infty_norm(primal_residual_eq_scaled)),
// (infty_norm(dual_residual_scaled)),
// });
// break;
// case MeritFunctionType::PDAL:
// err_in = std::max({
// (infty_norm(
// helpers::negative_part(primal_residual_in_scaled_lo) +
// helpers::positive_part(primal_residual_in_scaled_up) -
// results.info.mu_in * z_e)),
// (infty_norm(primal_residual_eq_scaled)),
// (infty_norm(dual_residual_scaled)),
// });
// break;
// }
/* put in debug mode
if (settings.verbose) {
std::cout << "--inner iter " << iter_inner << " iner error "
<< err_in << " alpha "
<< alpha << " infty_norm(dw) "
<< infty_norm(dw) <<
std::endl;
}
*/
if (settings.verbose) {
std::cout << "\033[1;34m[inner iteration " << iter_inner + 1
<< "]\033[0m" << std::endl;
std::cout << std::scientific << std::setw(2) << std::setprecision(2)
<< "| inner residual=" << err_in << " | alpha=" << alpha
<< std::endl;
}
if (err_in <= bcl_eta_in) {
results.info.iter += iter_inner + 1;
return;
}
// compute primal and dual infeasibility criteria
bool is_primal_infeasible = proxsuite::proxqp::sparse::detail::
global_primal_residual_infeasibility(
VectorViewMut<T>{ from_eigen, ATdy },
VectorViewMut<T>{ from_eigen, CTdz },
VectorViewMut<T>{ from_eigen, dy },
VectorViewMut<T>{ from_eigen, dz },
qp_scaled.as_const(),
settings,
precond);
bool is_dual_infeasible = proxsuite::proxqp::sparse::detail::
global_dual_residual_infeasibility(
VectorViewMut<T>{ from_eigen, Adx },
VectorViewMut<T>{ from_eigen, Cdx },
VectorViewMut<T>{ from_eigen, Hdx },
VectorViewMut<T>{ from_eigen, dx },
qp_scaled.as_const(),
settings,
data,
precond);
if (is_primal_infeasible) {
results.info.status = QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE;
dw_prev = dw;
break;
} else if (is_dual_infeasible) {
results.info.status = QPSolverOutput::PROXQP_DUAL_INFEASIBLE;
dw_prev = dw;
break;
}
}
};
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
primal_dual_newton_semi_smooth();
if (results.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE ||
results.info.status == QPSolverOutput::PROXQP_DUAL_INFEASIBLE) {
// certificate of infeasibility
results.x = dw_prev.head(data.dim);
results.y = dw_prev.segment(data.dim, data.n_eq);
results.z = dw_prev.tail(data.n_in);
break;
}
// VEG bind : met le résultat tuple de unscaled_primal_dual_residual dans
// (primal_feasibility_lhs_new, dual_feasibility_lhs_new) en guessant leur
// type via auto
VEG_BIND(
auto,
(primal_feasibility_lhs_new, dual_feasibility_lhs_new),
detail::unscaled_primal_dual_residual(work,
results,
primal_residual_eq_scaled,
primal_residual_in_scaled_lo,
primal_residual_in_scaled_up,
dual_residual_scaled,
primal_feasibility_eq_rhs_0,
primal_feasibility_in_rhs_0,
dual_feasibility_rhs_0,
dual_feasibility_rhs_1,
dual_feasibility_rhs_3,
rhs_duality_gap,
precond,
data,
qp_scaled.as_const(),
detail::vec_mut(x_e),
detail::vec_mut(y_e),
detail::vec_mut(z_e),
stack));
if (is_primal_feasible(primal_feasibility_lhs_new) &&
is_dual_feasible(dual_feasibility_lhs_new)) {
if (settings.check_duality_gap) {
if (std::fabs(results.info.duality_gap) <=
settings.eps_duality_gap_abs +
settings.eps_duality_gap_rel * rhs_duality_gap) {
results.info.pri_res = primal_feasibility_lhs_new;
results.info.dua_res = dual_feasibility_lhs_new;
results.info.status = QPSolverOutput::PROXQP_SOLVED;
break;
}
} else {
results.info.pri_res = primal_feasibility_lhs_new;
results.info.dua_res = dual_feasibility_lhs_new;
results.info.status = QPSolverOutput::PROXQP_SOLVED;
break;
}
}
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
auto bcl_update = [&]() -> void {
if (primal_feasibility_lhs_new <= bcl_eta_ext ||
iter > settings.safe_guard) {
bcl_eta_ext *= pow(results.info.mu_in, settings.beta_bcl);
bcl_eta_in = std::max(bcl_eta_in * results.info.mu_in, eps_in_min);
} else {
y_e = y_prev_e;
z_e = z_prev_e;
new_bcl_mu_in = std::max(
results.info.mu_in * settings.mu_update_factor, settings.mu_min_in);
new_bcl_mu_eq = std::max(
results.info.mu_eq * settings.mu_update_factor, settings.mu_min_eq);
new_bcl_mu_in_inv =
std::min(results.info.mu_in_inv * settings.mu_update_inv_factor,
settings.mu_max_in_inv);
new_bcl_mu_eq_inv =
std::min(results.info.mu_eq_inv * settings.mu_update_inv_factor,
settings.mu_max_eq_inv);
bcl_eta_ext =
bcl_eta_ext_init * pow(new_bcl_mu_in, settings.alpha_bcl);
bcl_eta_in = std::max(new_bcl_mu_in, eps_in_min);
}
};
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
bcl_update();
VEG_BIND(
auto,
(_, dual_feasibility_lhs_new_2),
detail::unscaled_primal_dual_residual(work,
results,
primal_residual_eq_scaled,
primal_residual_in_scaled_lo,
primal_residual_in_scaled_up,
dual_residual_scaled,
primal_feasibility_eq_rhs_0,
primal_feasibility_in_rhs_0,
dual_feasibility_rhs_0,
dual_feasibility_rhs_1,
dual_feasibility_rhs_3,
rhs_duality_gap,
precond,
data,
qp_scaled.as_const(),
detail::vec_mut(x_e),
detail::vec_mut(y_e),
detail::vec_mut(z_e),
stack));
proxsuite::linalg::veg::unused(_);
if (primal_feasibility_lhs_new >= primal_feasibility_lhs && //
dual_feasibility_lhs_new_2 >= dual_feasibility_lhs && //
results.info.mu_in <= T(1.E-5)) {
new_bcl_mu_in = settings.cold_reset_mu_in;
new_bcl_mu_eq = settings.cold_reset_mu_eq;
new_bcl_mu_in_inv = settings.cold_reset_mu_in_inv;
new_bcl_mu_eq_inv = settings.cold_reset_mu_eq_inv;
}
}
if (results.info.mu_in != new_bcl_mu_in ||
results.info.mu_eq != new_bcl_mu_eq) {
{
++results.info.mu_updates;
}
/*
refactorize(
work,
results,
kkt_active,
active_constraints,
data,
stack,
xtag);
*/
if (work.internal.do_ldlt) {
isize w_values = 1; // un seul elt non nul
T alpha = 0;
for (isize j = 0; j < n_eq + n_in; ++j) {
I row_index = I(j + n);
if (j < n_eq) {
alpha = results.info.mu_eq - new_bcl_mu_eq;
} else {
if (!work.active_inequalities[j - n_eq]) {
continue;
}
alpha = results.info.mu_in - new_bcl_mu_in;
// switch (settings.merit_function_type)
// {
// case MeritFunctionType::GPDAL:
// alpha = settings.alpha_gpdal * (results.info.mu_in -
// new_bcl_mu_in); break;
// case MeritFunctionType::PDAL:
// alpha = results.info.mu_in - new_bcl_mu_in;
// break;
// }
}
T value = 1;
proxsuite::linalg::sparse::VecRef<T, I> w{
proxsuite::linalg::veg::from_raw_parts,
n + n_eq + n_in,
w_values,
&row_index, // &: adresse de row index
&value,
};
ldl = rank1_update(ldl, etree, perm_inv, w, alpha, stack);
}
} else {
refactorize(work,
results,
settings,
kkt_active,
active_constraints,
data,
stack,
xtag);
}
}
results.info.mu_eq = new_bcl_mu_eq;
results.info.mu_in = new_bcl_mu_in;
results.info.mu_eq_inv = new_bcl_mu_eq_inv;
results.info.mu_in_inv = new_bcl_mu_in_inv;
}
LDLT_TEMP_VEC_UNINIT(T, tmp, n, stack);
tmp.setZero();
detail::noalias_symhiv_add(tmp, qp_scaled.H.to_eigen(), x_e);
precond.unscale_dual_residual_in_place({ proxqp::from_eigen, tmp });
precond.unscale_primal_in_place({ proxqp::from_eigen, x_e });
precond.unscale_dual_in_place_eq({ proxqp::from_eigen, y_e });
precond.unscale_dual_in_place_in({ proxqp::from_eigen, z_e });
tmp *= 0.5;
tmp += data.g;
results.info.objValue = (tmp).dot(x_e);
if (settings.compute_timings) {
results.info.solve_time = work.timer.elapsed().user; // in nanoseconds
results.info.run_time = results.info.solve_time + results.info.setup_time;
}
if (settings.verbose) {
std::cout << "-------------------SOLVER STATISTICS-------------------"
<< std::endl;
std::cout << "outer iter: " << results.info.iter_ext << std::endl;
std::cout << "total iter: " << results.info.iter << std::endl;
std::cout << "mu updates: " << results.info.mu_updates << std::endl;
std::cout << "rho updates: " << results.info.rho_updates << std::endl;
std::cout << "objective: " << results.info.objValue << std::endl;
switch (results.info.status) {
case QPSolverOutput::PROXQP_SOLVED: {
std::cout << "status: "
<< "Solved" << std::endl;
break;
}
case QPSolverOutput::PROXQP_MAX_ITER_REACHED: {
std::cout << "status: "
<< "Maximum number of iterations reached" << std::endl;
break;
}
case QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE: {
std::cout << "status: "
<< "Primal infeasible" << std::endl;
break;
}
case QPSolverOutput::PROXQP_DUAL_INFEASIBLE: {
std::cout << "status: "
<< "Dual infeasible" << std::endl;
break;
}
default: {
assert(false && "Should never happened");
break;
}
}
if (settings.compute_timings)
std::cout << "run time: " << results.info.solve_time << std::endl;
std::cout << "--------------------------------------------------------"
<< std::endl;
}
assert(!std::isnan(results.info.pri_res));
assert(!std::isnan(results.info.dua_res));
assert(!std::isnan(results.info.duality_gap));
work.set_dirty();
}
} // namespace sparse
} // namespace proxqp
} // namespace proxsuite
#endif /* end of include guard PROXSUITE_PROXQP_SPARSE_SOLVER_HPP */