mirror of https://github.com/CGAL/cgal
Improvements and fixes for OSQP_quadratic_program_traits
- Fix wrong array sizes - Fix to_CSC matrix conversion - Rework the reserve() / insertion / size mechanisms - Enable passing some options via solve()
This commit is contained in:
parent
267a6412ac
commit
f99d0c3480
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2020 GeometryFactory SARL (France).
|
||||
// Copyright (c) 2020-2021 GeometryFactory SARL (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org)
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
|
||||
//
|
||||
// Author(s) : Dmitry Anisimov
|
||||
// Mael Rouxel-Labbé
|
||||
//
|
||||
|
||||
#ifndef CGAL_OSQP_QUADRATIC_PROGRAM_TRAITS_H
|
||||
|
|
@ -43,96 +44,129 @@ namespace CGAL {
|
|||
\cgalModels `QuadraticProgramTraits`
|
||||
*/
|
||||
template<typename FT>
|
||||
class OSQP_quadratic_program_traits {
|
||||
// row, col, value
|
||||
using Triplet = std::tuple<std::size_t, std::size_t, FT>;
|
||||
class OSQP_quadratic_program_traits
|
||||
{
|
||||
using Triplet = std::tuple<std::size_t, std::size_t, FT>; // row, col, value
|
||||
|
||||
private:
|
||||
std::size_t n; // number of variables
|
||||
std::size_t m; // number of constraints
|
||||
|
||||
std::vector<Triplet> P_vec, A_vec;
|
||||
std::vector<FT> q_vec, l_vec, u_vec;
|
||||
|
||||
public:
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
void reserve_P(const std::size_t k) {
|
||||
P_vec.reserve(k);
|
||||
}
|
||||
/// Default constructor
|
||||
OSQP_quadratic_program_traits() : n(0), m(0) { }
|
||||
|
||||
void reserve_q(const std::size_t n) {
|
||||
/// Constructor
|
||||
/// \param n the number of variables
|
||||
OSQP_quadratic_program_traits(const std::size_t n)
|
||||
: n(n), m(0)
|
||||
{
|
||||
// 6*n, just guessing that this is some kind of sparse problem on a nice 2D mesh
|
||||
P_vec.reserve(6 * n);
|
||||
q_vec.reserve(n);
|
||||
}
|
||||
|
||||
void reserve_A(const std::size_t k) {
|
||||
A_vec.reserve(k);
|
||||
}
|
||||
/// Constructor
|
||||
/// \param n the number of variables
|
||||
/// \param m the number of constraints
|
||||
OSQP_quadratic_program_traits(const std::size_t n, const std::size_t m)
|
||||
: n(n), m(m)
|
||||
{
|
||||
P_vec.reserve(6 * n);
|
||||
q_vec.reserve(n);
|
||||
|
||||
void reserve_l(const std::size_t m) {
|
||||
A_vec.reserve(m);
|
||||
l_vec.reserve(m);
|
||||
}
|
||||
|
||||
void reserve_u(const std::size_t m) {
|
||||
u_vec.reserve(m);
|
||||
}
|
||||
|
||||
void set_P(const std::size_t i, const std::size_t j, const FT value) {
|
||||
P_vec.push_back(std::make_tuple(i, j, value));
|
||||
public:
|
||||
/// \cond SKIP_IN_MANUAL
|
||||
|
||||
void set_P(const std::size_t i, const std::size_t j, const FT value)
|
||||
{
|
||||
if(j < i)
|
||||
return;
|
||||
if(j >= n) // no need to test i since j >= i
|
||||
n = j+1;
|
||||
|
||||
P_vec.emplace_back(i, j, value);
|
||||
}
|
||||
|
||||
void set_q(const std::size_t, const FT value) {
|
||||
q_vec.push_back(value);
|
||||
void set_q(const std::size_t i, const FT value)
|
||||
{
|
||||
if(i >= n)
|
||||
n = i+1;
|
||||
if(i >= q_vec.size())
|
||||
q_vec.resize(n);
|
||||
|
||||
q_vec[i] = value;
|
||||
}
|
||||
|
||||
void set_r(const FT) {
|
||||
// is not used here
|
||||
}
|
||||
|
||||
void set_A(const std::size_t i, const std::size_t j, const FT value) {
|
||||
A_vec.push_back(std::make_tuple(i, j, value));
|
||||
void set_A(const std::size_t i, const std::size_t j, const FT value)
|
||||
{
|
||||
if(i >= m)
|
||||
m = i+1;
|
||||
if(j >= n)
|
||||
n = j+1;
|
||||
|
||||
A_vec.emplace_back(i, j, value);
|
||||
}
|
||||
|
||||
void set_l(const std::size_t, const FT value) {
|
||||
l_vec.push_back(value);
|
||||
void set_l(const std::size_t i, const FT value)
|
||||
{
|
||||
if(i >= m)
|
||||
m = i+1;
|
||||
if(i >= l_vec.size())
|
||||
l_vec.resize(m);
|
||||
|
||||
l_vec[i] = value;
|
||||
}
|
||||
|
||||
void set_u(const std::size_t, const FT value) {
|
||||
u_vec.push_back(value);
|
||||
}
|
||||
void set_u(const std::size_t i, const FT value)
|
||||
{
|
||||
if(i >= m)
|
||||
m = i+1;
|
||||
if(i >= u_vec.size())
|
||||
u_vec.resize(m);
|
||||
|
||||
std::size_t P_size() const {
|
||||
return P_vec.size();
|
||||
}
|
||||
|
||||
std::size_t q_size() const {
|
||||
return q_vec.size();
|
||||
}
|
||||
|
||||
std::size_t A_size() const {
|
||||
return A_vec.size();
|
||||
}
|
||||
|
||||
std::size_t l_size() const {
|
||||
return l_vec.size();
|
||||
}
|
||||
|
||||
std::size_t u_size() const {
|
||||
return u_vec.size();
|
||||
u_vec[i] = value;
|
||||
}
|
||||
|
||||
template<typename OutIterator>
|
||||
bool solve(OutIterator solution) {
|
||||
bool solve(OutIterator solution,
|
||||
const FT eps_abs = 1e-10,
|
||||
const FT eps_rel = 1e-15,
|
||||
const bool verbose = false)
|
||||
{
|
||||
std::cout << "num variables = " << n << std::endl;
|
||||
std::cout << "num constraints = " << m << std::endl;
|
||||
|
||||
const std::size_t num_cols = std::get<1>(P_vec.back()) + 1;
|
||||
CGAL_precondition(q_vec.size() == num_cols);
|
||||
CGAL_precondition(l_vec.size() == u_vec.size());
|
||||
CGAL_precondition(n >= 1 && m >= 0);
|
||||
|
||||
CGAL_precondition(q_vec.size() == n);
|
||||
CGAL_precondition(l_vec.size() == m && l_vec.size() == m);
|
||||
|
||||
const c_int P_nnz = static_cast<c_int>(P_vec.size());
|
||||
c_float P_x[P_nnz];
|
||||
c_int P_i[P_nnz];
|
||||
c_int P_p[P_nnz + 1];
|
||||
c_int P_p[n + 1];
|
||||
set_matrix_from_triplets("P", P_vec, P_x, P_i, P_p);
|
||||
// std::cout << "P_nnz: " << P_nnz << std::endl;
|
||||
std::cout << "P_nnz: " << P_nnz << std::endl;
|
||||
|
||||
const c_int A_nnz = static_cast<c_int>(A_vec.size());
|
||||
c_float A_x[A_nnz];
|
||||
c_int A_i[A_nnz];
|
||||
c_int A_p[P_nnz + 1];
|
||||
c_int A_p[n + 1];
|
||||
set_matrix_from_triplets("A", A_vec, A_x, A_i, A_p);
|
||||
// std::cout << "A_nnz: " << A_nnz << std::endl;
|
||||
std::cout << "A_nnz: " << A_nnz << std::endl;
|
||||
|
||||
const c_int q_size = static_cast<c_int>(q_vec.size());
|
||||
const c_int l_size = static_cast<c_int>(l_vec.size());
|
||||
|
|
@ -144,43 +178,45 @@ namespace CGAL {
|
|||
set_qlu_data(q_x, l_x, u_x);
|
||||
|
||||
// Problem settings.
|
||||
OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));
|
||||
OSQPSettings *settings = (OSQPSettings *) malloc(sizeof(OSQPSettings));
|
||||
CGAL_assertion(settings);
|
||||
|
||||
// Structures.
|
||||
OSQPWorkspace *work;
|
||||
OSQPData *data;
|
||||
OSQPData *data = (OSQPData *) malloc(sizeof(OSQPData));
|
||||
CGAL_assertion(data);
|
||||
|
||||
// Populate data.
|
||||
const c_int n = q_size; // number of variables
|
||||
const c_int m = l_size; // number of constraints
|
||||
// std::cout << "n: " << n << std::endl;
|
||||
// std::cout << "m: " << m << std::endl;
|
||||
data->n = static_cast<c_int>(n);
|
||||
data->m = static_cast<c_int>(m);
|
||||
data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
|
||||
CGAL_assertion(data->P);
|
||||
|
||||
data = (OSQPData *)c_malloc(sizeof(OSQPData));
|
||||
data->n = n;
|
||||
data->m = m;
|
||||
data->P = csc_matrix(n, n, P_nnz, P_x, P_i, P_p);
|
||||
data->q = q_x;
|
||||
data->A = csc_matrix(m, n, A_nnz, A_x, A_i, A_p);
|
||||
data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
|
||||
CGAL_assertion(data->A);
|
||||
|
||||
data->l = l_x;
|
||||
data->u = u_x;
|
||||
|
||||
// Set solver settings.
|
||||
osqp_set_default_settings(settings);
|
||||
settings->eps_rel = 1.0e-15;
|
||||
settings->verbose = false;
|
||||
settings->eps_abs = eps_abs;
|
||||
settings->eps_rel = eps_rel;
|
||||
settings->verbose = verbose;
|
||||
|
||||
// Set workspace.
|
||||
osqp_setup(&work, data, settings);
|
||||
|
||||
// Solve problem.
|
||||
const int exitflag = osqp_solve(work);
|
||||
const bool success = exitflag == 0 ? true : false;
|
||||
const bool success = (exitflag == 0);
|
||||
|
||||
// Create solution.
|
||||
const c_float *x = work->solution->x;
|
||||
for (c_int i = 0; i < n; ++i) {
|
||||
const FT value = static_cast<FT>(x[i]);
|
||||
for(c_int i=0; i<n; ++i)
|
||||
{
|
||||
const FT value{x[i]};
|
||||
*(++solution) = value;
|
||||
}
|
||||
|
||||
|
|
@ -195,66 +231,78 @@ namespace CGAL {
|
|||
}
|
||||
/// \endcond
|
||||
|
||||
private:
|
||||
std::vector<Triplet> P_vec, A_vec;
|
||||
std::vector<FT> q_vec, l_vec, u_vec;
|
||||
|
||||
void set_matrix_from_triplets(
|
||||
const std::string /* name */,
|
||||
public:
|
||||
// Based on the code in scipy, function coo_tocsr()
|
||||
void set_matrix_from_triplets(const std::string name,
|
||||
const std::vector<Triplet>& triplets,
|
||||
c_float *M_x, c_int *M_i, c_int *M_p) const {
|
||||
c_float *M_x,
|
||||
c_int *M_i,
|
||||
c_int *M_p) const
|
||||
{
|
||||
const std::size_t nnz = triplets.size();
|
||||
|
||||
M_p[0] = 0;
|
||||
std::size_t count = 0;
|
||||
const std::size_t num_cols = std::get<1>(triplets.back());
|
||||
std::size_t ref_col = 0;
|
||||
for (ref_col = 0; ref_col <= num_cols; ++ref_col) {
|
||||
std::size_t num_rows = 0;
|
||||
for (std::size_t i = 0; i < triplets.size(); ++i) {
|
||||
const std::size_t row = std::get<0>(triplets[i]);
|
||||
const std::size_t col = std::get<1>(triplets[i]);
|
||||
const double value = CGAL::to_double(std::get<2>(triplets[i]));
|
||||
// Compute the number of non-zero entries in each column of the sparse matrix A
|
||||
std::fill(M_p, M_p + n, 0);
|
||||
for(std::size_t k=0; k<nnz; ++k)
|
||||
M_p[std::get<1>(triplets[k])]++;
|
||||
|
||||
if (col == ref_col) {
|
||||
M_i[count] = row; M_x[count] = value;
|
||||
++count; ++num_rows;
|
||||
// Fill M_p
|
||||
c_int cumsum = 0;
|
||||
for(std::size_t j=0; j<n; ++j)
|
||||
{
|
||||
const c_int tmp = M_p[j];
|
||||
M_p[j] = cumsum;
|
||||
cumsum += tmp;
|
||||
}
|
||||
M_p[n] = nnz;
|
||||
|
||||
// Write Ai, Ax into M_i, M_x
|
||||
for(std::size_t k=0; k<nnz; ++k)
|
||||
{
|
||||
const c_int col = static_cast<c_int>(std::get<1>(triplets[k]));
|
||||
const c_int dest = M_p[col];
|
||||
|
||||
M_i[dest] = static_cast<c_int>(std::get<0>(triplets[k]));
|
||||
M_x[dest] = c_float(CGAL::to_double(std::get<2>(triplets[k])));
|
||||
|
||||
M_p[col]++;
|
||||
}
|
||||
M_p[ref_col + 1] = M_p[ref_col] + num_rows;
|
||||
|
||||
c_int last = 0;
|
||||
for(std::size_t j=0; j<=n; ++j)
|
||||
{
|
||||
const c_int tmp = M_p[j];
|
||||
M_p[j] = last;
|
||||
last = tmp;
|
||||
}
|
||||
|
||||
// std::cout << name + "_x: ";
|
||||
// for (std::size_t i = 0; i < count; ++i)
|
||||
// for(std::size_t i=0; i<nnz; ++i)
|
||||
// std::cout << M_x[i] << " ";
|
||||
// std::cout << std::endl;
|
||||
|
||||
// std::cout << name + "_i: ";
|
||||
// for (std::size_t i = 0; i < count; ++i)
|
||||
// for(std::size_t i=0; i<nnz; ++i)
|
||||
// std::cout << M_i[i] << " ";
|
||||
// std::cout << std::endl;
|
||||
|
||||
// std::cout << name + "_p: ";
|
||||
// for (std::size_t i = 0; i <= ref_col; ++i)
|
||||
// for(std::size_t i=0; i<(n+1); ++i)
|
||||
// std::cout << M_p[i] << " ";
|
||||
// std::cout << std::endl;
|
||||
}
|
||||
|
||||
void set_qlu_data(
|
||||
c_float *q_x, c_float *l_x, c_float *u_x) const {
|
||||
|
||||
CGAL_assertion(q_vec.size() > 1);
|
||||
CGAL_assertion(l_vec.size() == u_vec.size());
|
||||
const std::size_t n = q_vec.size(); // number of variables
|
||||
const std::size_t m = l_vec.size(); // number of constraints
|
||||
|
||||
CGAL_assertion(n > 1);
|
||||
void set_qlu_data(c_float *q_x,
|
||||
c_float *l_x,
|
||||
c_float *u_x) const
|
||||
{
|
||||
for(std::size_t i=0; i<n; ++i)
|
||||
q_x[i] = CGAL::to_double(q_vec[i]);
|
||||
q_x[i] = c_float(CGAL::to_double(q_vec[i]));
|
||||
|
||||
CGAL_assertion(m >= 0);
|
||||
for (std::size_t i = 0; i < m; ++i) {
|
||||
l_x[i] = CGAL::to_double(l_vec[i]);
|
||||
u_x[i] = CGAL::to_double(u_vec[i]);
|
||||
for(std::size_t i=0; i<m; ++i)
|
||||
{
|
||||
l_x[i] = c_float(CGAL::to_double(l_vec[i]));
|
||||
u_x[i] = c_float(CGAL::to_double(u_vec[i]));
|
||||
}
|
||||
|
||||
// std::cout << "q_x: ";
|
||||
|
|
|
|||
Loading…
Reference in New Issue