Program Listing for File ruiz.hpp
↰ Return to documentation for file (/tmp/ws/src/proxsuite/include/proxsuite/proxqp/dense/preconditioner/ruiz.hpp
)
//
// Copyright (c) 2022 INRIA
//
#ifndef PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP
#define PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP
#include "proxsuite/proxqp/dense/views.hpp"
#include "proxsuite/proxqp/dense/fwd.hpp"
#include <proxsuite/linalg/dense/core.hpp>
#include <proxsuite/proxqp/settings.hpp>
#include <ostream>
#include <Eigen/Core>
namespace proxsuite {
namespace proxqp {
enum struct Symmetry
{
general,
lower,
upper,
};
namespace dense {
namespace detail {
template<typename T>
auto
ruiz_scale_qp_in_place( //
VectorViewMut<T> delta_,
std::ostream* logger_ptr,
QpViewBoxMut<T> qp,
T epsilon,
isize max_iter,
Symmetry sym,
ProblemType problem_type,
proxsuite::linalg::veg::dynstack::DynStackMut stack) -> T
{
T c(1);
auto S = delta_.to_eigen();
auto H = qp.H.to_eigen();
auto g = qp.g.to_eigen();
auto A = qp.A.to_eigen();
auto b = qp.b.to_eigen();
auto C = qp.C.to_eigen();
auto u = qp.u.to_eigen();
auto l = qp.l.to_eigen();
static constexpr T machine_eps = std::numeric_limits<T>::epsilon();
/*
* compute equilibration parameters and scale in place the qp following
* algorithm
*
* modified: removed g in gamma computation
*/
isize n = qp.H.rows;
isize n_eq = qp.A.rows;
isize n_in = qp.C.rows;
T gamma = T(1);
LDLT_TEMP_VEC(T, delta, n + n_eq + n_in, stack);
i64 iter = 1;
while (infty_norm((1 - delta.array()).matrix()) > epsilon) {
if (logger_ptr != nullptr) {
*logger_ptr //
<< "j : " //
<< iter //
<< " ; error : " //
<< infty_norm((1 - delta.array()).matrix()) //
<< "\n\n";
}
if (iter == max_iter) {
break;
} else {
++iter;
}
// normalization vector
{
switch (problem_type) {
case ProblemType::LP:
for (isize k = 0; k < n; ++k) {
T aux = sqrt(std::max({
n_eq > 0 ? infty_norm(A.col(k)) : T(0),
n_in > 0 ? infty_norm(C.col(k)) : T(0),
}));
if (aux == T(0)) {
delta(k) = T(1);
} else {
delta(k) = T(1) / (aux + machine_eps);
}
}
break;
case ProblemType::QP:
for (isize k = 0; k < n; ++k) {
switch (sym) {
case Symmetry::upper: { // upper triangular part
T aux = sqrt(std::max({
infty_norm(H.col(k).head(k)),
infty_norm(H.row(k).tail(n - k)),
n_eq > 0 ? infty_norm(A.col(k)) : T(0),
n_in > 0 ? infty_norm(C.col(k)) : T(0),
}));
if (aux == T(0)) {
delta(k) = T(1);
} else {
delta(k) = T(1) / (aux + machine_eps);
}
break;
}
case Symmetry::lower: { // lower triangular part
T aux = sqrt(std::max({
infty_norm(H.col(k).head(k)),
infty_norm(H.col(k).tail(n - k)),
n_eq > 0 ? infty_norm(A.col(k)) : T(0),
n_in > 0 ? infty_norm(C.col(k)) : T(0),
}));
if (aux == T(0)) {
delta(k) = T(1);
} else {
delta(k) = T(1) / (aux + machine_eps);
}
break;
}
case Symmetry::general: {
T aux = sqrt(std::max({
infty_norm(H.col(k)),
n_eq > 0 ? infty_norm(A.col(k)) : T(0),
n_in > 0 ? infty_norm(C.col(k)) : T(0),
}));
if (aux == T(0)) {
delta(k) = T(1);
} else {
delta(k) = T(1) / (aux + machine_eps);
}
break;
}
}
}
break;
}
for (isize k = 0; k < n_eq; ++k) {
T aux = sqrt(infty_norm(A.row(k)));
if (aux == T(0)) {
delta(n + k) = T(1);
} else {
delta(n + k) = T(1) / (aux + machine_eps);
}
}
for (isize k = 0; k < n_in; ++k) {
T aux = sqrt(infty_norm(C.row(k)));
if (aux == T(0)) {
delta(k + n + n_eq) = T(1);
} else {
delta(k + n + n_eq) = T(1) / (aux + machine_eps);
}
}
}
{
// normalize A and C
A = delta.segment(n, n_eq).asDiagonal() * A * delta.head(n).asDiagonal();
C = delta.tail(n_in).asDiagonal() * C * delta.head(n).asDiagonal();
// normalize vectors
g.array() *= delta.head(n).array();
b.array() *= delta.middleRows(n, n_eq).array();
u.array() *= delta.tail(n_in).array();
l.array() *= delta.tail(n_in).array();
// normalize H
switch (problem_type) {
case ProblemType::LP:
break;
case ProblemType::QP:
switch (sym) {
case Symmetry::upper: {
// upper triangular part
for (isize j = 0; j < n; ++j) {
H.col(j).head(j + 1) *= delta(j);
}
// normalisation des lignes
for (isize i = 0; i < n; ++i) {
H.row(i).tail(n - i) *= delta(i);
}
break;
}
case Symmetry::lower: {
// lower triangular part
for (isize j = 0; j < n; ++j) {
H.col(j).tail(n - j) *= delta(j);
}
// normalisation des lignes
for (isize i = 0; i < n; ++i) {
H.row(i).head(i + 1) *= delta(i);
}
break;
}
case Symmetry::general: {
// all matrix
H = delta.head(n).asDiagonal() * H * delta.head(n).asDiagonal();
break;
}
default:
break;
}
// additional normalization for the cost function
switch (sym) {
case Symmetry::upper: {
// upper triangular part
T tmp = T(0);
for (isize j = 0; j < n; ++j) {
tmp += proxqp::dense::infty_norm(H.row(j).tail(n - j));
}
gamma = 1 / std::max(tmp / T(n), T(1));
break;
}
case Symmetry::lower: {
// lower triangular part
T tmp = T(0);
for (isize j = 0; j < n; ++j) {
tmp += proxqp::dense::infty_norm(H.col(j).tail(n - j));
}
gamma = 1 / std::max(tmp / T(n), T(1));
break;
}
case Symmetry::general: {
// all matrix
gamma =
1 / std::max(
T(1),
(H.colwise().template lpNorm<Eigen::Infinity>()).mean());
break;
}
default:
break;
}
H *= gamma;
break;
}
g *= gamma;
S.array() *= delta.array(); // coefficientwise product
c *= gamma;
}
}
return c;
}
} // namespace detail
namespace preconditioner {
template<typename T>
struct RuizEquilibration
{
Vec<T> delta;
T c;
isize dim;
T epsilon;
i64 max_iter;
Symmetry sym;
std::ostream* logger_ptr = nullptr;
explicit RuizEquilibration(isize dim_,
isize n_eq_in,
T epsilon_ = T(1e-3),
i64 max_iter_ = 10,
Symmetry sym_ = Symmetry::general,
std::ostream* logger = nullptr)
: delta(Vec<T>::Ones(dim_ + n_eq_in))
, c(1)
, dim(dim_)
, epsilon(epsilon_)
, max_iter(max_iter_)
, sym(sym_)
, logger_ptr(logger)
{
}
void print() const
{
// CHANGE: endl to newline
*logger_ptr << " delta : " << delta << "\n\n";
*logger_ptr << " c : " << c << "\n\n";
}
static auto scale_qp_in_place_req(proxsuite::linalg::veg::Tag<T> tag,
isize n,
isize n_eq,
isize n_in)
-> proxsuite::linalg::veg::dynstack::StackReq
{
return proxsuite::linalg::dense::temp_vec_req(tag, n + n_eq + n_in);
}
// H_new = c * head @ H @ head
// A_new = tail @ A @ head
// g_new = c * head @ g
// b_new = tail @ b
void scale_qp_in_place(QpViewBoxMut<T> qp,
bool execute_preconditioner,
const isize max_iter,
const T epsilon,
const ProblemType problem_type,
proxsuite::linalg::veg::dynstack::DynStackMut stack)
{
if (execute_preconditioner) {
delta.setOnes();
c = detail::ruiz_scale_qp_in_place({ proxqp::from_eigen, delta },
logger_ptr,
qp,
epsilon,
max_iter,
sym,
problem_type,
stack);
} else {
auto H = qp.H.to_eigen();
auto g = qp.g.to_eigen();
auto A = qp.A.to_eigen();
auto b = qp.b.to_eigen();
auto C = qp.C.to_eigen();
auto u = qp.u.to_eigen();
auto l = qp.l.to_eigen();
isize n = qp.H.rows;
isize n_eq = qp.A.rows;
isize n_in = qp.C.rows;
// normalize A and C
A = delta.segment(n, n_eq).asDiagonal() * A * delta.head(n).asDiagonal();
C = delta.tail(n_in).asDiagonal() * C * delta.head(n).asDiagonal();
// normalize H
switch (sym) {
case Symmetry::upper: {
// upper triangular part
for (isize j = 0; j < n; ++j) {
H.col(j).head(j + 1) *= delta(j);
}
// normalisation des lignes
for (isize i = 0; i < n; ++i) {
H.row(i).tail(n - i) *= delta(i);
}
break;
}
case Symmetry::lower: {
// lower triangular part
for (isize j = 0; j < n; ++j) {
H.col(j).tail(n - j) *= delta(j);
}
// normalisation des lignes
for (isize i = 0; i < n; ++i) {
H.row(i).head(i + 1) *= delta(i);
}
break;
}
case Symmetry::general: {
// all matrix
H = delta.head(n).asDiagonal() * H * delta.head(n).asDiagonal();
break;
}
default:
break;
}
// normalize vectors
g.array() *= delta.head(n).array();
b.array() *= delta.segment(n, n_eq).array();
l.array() *= delta.tail(n_in).array();
u.array() *= delta.tail(n_in).array();
g *= c;
H *= c;
}
}
void scale_qp(const QpViewBox<T> qp,
QpViewBoxMut<T> scaled_qp,
bool execute_preconditioner,
VectorViewMut<T> tmp_delta_preallocated) const
{
/*
* scaled_qp is scaled, whereas first qp is not
* the procedure computes as well equilibration parameters using default
* parameters
*/
scaled_qp.H.to_eigen() = qp.H.to_eigen();
scaled_qp.A.to_eigen() = qp.A.to_eigen();
scaled_qp.C.to_eigen() = qp.C.to_eigen();
scaled_qp.g.to_eigen() = qp.g.to_eigen();
scaled_qp.b.to_eigen() = qp.b.to_eigen();
scaled_qp.d.to_eigen() = qp.d.to_eigen();
scale_qp_in_place(scaled_qp,
execute_preconditioner,
tmp_delta_preallocated,
epsilon,
max_iter);
}
// modifies variables in place
void scale_primal_in_place(VectorViewMut<T> primal) const
{
primal.to_eigen().array() /= delta.array().head(dim);
}
void scale_dual_in_place(VectorViewMut<T> dual) const
{
dual.to_eigen().array() = dual.as_const().to_eigen().array() /
delta.tail(delta.size() - dim).array() * c;
}
void scale_dual_in_place_eq(VectorViewMut<T> dual) const
{
dual.to_eigen().array() =
dual.as_const().to_eigen().array() /
delta.middleRows(dim, dual.to_eigen().size()).array() * c;
}
void scale_dual_in_place_in(VectorViewMut<T> dual) const
{
dual.to_eigen().array() = dual.as_const().to_eigen().array() /
delta.tail(dual.to_eigen().size()).array() * c;
}
void unscale_primal_in_place(VectorViewMut<T> primal) const
{
primal.to_eigen().array() *= delta.array().head(dim);
}
void unscale_dual_in_place(VectorViewMut<T> dual) const
{
dual.to_eigen().array() = dual.as_const().to_eigen().array() *
delta.tail(delta.size() - dim).array() / c;
}
void unscale_dual_in_place_eq(VectorViewMut<T> dual) const
{
dual.to_eigen().array() =
dual.as_const().to_eigen().array() *
delta.middleRows(dim, dual.to_eigen().size()).array() / c;
}
void unscale_dual_in_place_in(VectorViewMut<T> dual) const
{
dual.to_eigen().array() = dual.as_const().to_eigen().array() *
delta.tail(dual.to_eigen().size()).array() / c;
}
// modifies residuals in place
void scale_primal_residual_in_place(VectorViewMut<T> primal) const
{
primal.to_eigen().array() *= delta.tail(delta.size() - dim).array();
}
void scale_primal_residual_in_place_eq(VectorViewMut<T> primal_eq) const
{
primal_eq.to_eigen().array() *=
delta.middleRows(dim, primal_eq.to_eigen().size()).array();
}
void scale_primal_residual_in_place_in(VectorViewMut<T> primal_in) const
{
primal_in.to_eigen().array() *=
delta.tail(primal_in.to_eigen().size()).array();
}
void scale_dual_residual_in_place(VectorViewMut<T> dual) const
{
dual.to_eigen().array() *= delta.head(dim).array() * c;
}
void unscale_primal_residual_in_place(VectorViewMut<T> primal) const
{
primal.to_eigen().array() /= delta.tail(delta.size() - dim).array();
}
void unscale_primal_residual_in_place_eq(VectorViewMut<T> primal_eq) const
{
primal_eq.to_eigen().array() /=
delta.middleRows(dim, primal_eq.to_eigen().size()).array();
}
void unscale_primal_residual_in_place_in(VectorViewMut<T> primal_in) const
{
primal_in.to_eigen().array() /=
delta.tail(primal_in.to_eigen().size()).array();
}
void unscale_dual_residual_in_place(VectorViewMut<T> dual) const
{
dual.to_eigen().array() /= delta.head(dim).array() * c;
}
};
} // namespace preconditioner
} // namespace dense
} // namespace proxqp
} // namespace proxsuite
#endif /* end of include guard PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP */