proxqp_interface.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "proxqp_interface.hpp"
27 #include "casadi/core/casadi_misc.hpp"
28 
29 
30 using namespace proxsuite::proxqp;
31 
32 namespace casadi {
33 
34  extern "C"
35  int CASADI_CONIC_PROXQP_EXPORT
36  casadi_register_conic_proxqp(Conic::Plugin* plugin) {
37  plugin->creator = ProxqpInterface::creator;
38  plugin->name = "proxqp";
39  plugin->doc = ProxqpInterface::meta_doc.c_str();
40  plugin->version = CASADI_VERSION;
41  plugin->options = &ProxqpInterface::options_;
42  plugin->deserialize = &ProxqpInterface::deserialize;
43  return 0;
44  }
45 
46  extern "C"
47  void CASADI_CONIC_PROXQP_EXPORT casadi_load_conic_proxqp() {
48  Conic::registerPlugin(casadi_register_conic_proxqp);
49  }
50 
51  ProxqpInterface::ProxqpInterface(const std::string& name,
52  const std::map<std::string, Sparsity>& st)
53  : Conic(name, st), sparse_backend(true), max_iter(0.0) {
54  }
55 
57  clear_mem();
58  }
59 
61  = {{&Conic::options_},
62  {{"proxqp",
63  {OT_DICT,
64  "const proxqp options."}},
65  {"warm_start_primal",
66  {OT_BOOL,
67  "Use x input to warmstart [Default: true]."}},
68  {"warm_start_dual",
69  {OT_BOOL,
70  "Use y and z input to warmstart [Default: true]."}}
71  }
72  };
73 
74  void ProxqpInterface::init(const Dict& opts) {
75  // Initialize the base classes
76  Conic::init(opts);
77 
78  warm_start_primal_ = true;
79  warm_start_dual_ = true;
80 
81  // Read options
82  for (auto&& op : opts) {
83  if (op.first=="warm_start_primal") {
84  warm_start_primal_ = op.second;
85  } else if (op.first=="warm_start_dual") {
86  warm_start_dual_ = op.second;
87  } else if (op.first=="proxqp") {
88  const Dict& opts = op.second;
89  for (auto&& op : opts) {
90  if (op.first=="default_rho") {
91  settings_.default_rho = op.second;
92  } else if (op.first=="default_mu_eq") {
93  settings_.default_mu_eq = op.second;
94  } else if (op.first=="default_mu_in") {
95  settings_.default_mu_in = op.second;
96  } else if (op.first=="eps_abs") {
97  settings_.eps_abs = op.second;
98  } else if (op.first=="eps_rel") {
99  settings_.eps_rel = op.second;
100  } else if (op.first=="max_iter") {
101  settings_.max_iter = static_cast<double>(op.second);
102  max_iter = static_cast<double>(op.second);
103  } else if (op.first=="verbose") {
104  settings_.verbose = op.second;
105  } else if (op.first=="backend") {
106  if (op.second == "sparse") {
107  sparse_backend = true;
108  } else if (op.second == "dense") {
109  sparse_backend = false;
110  } else {
111  casadi_error("[Backend option] Please specify either sparse or dense");
112  }
113  } else {
114  casadi_error("[ProxQP settings] User-specified option "
115  "'" + str(op.first) + "' not recognized.");
116  }
117  }
118  }
119  }
120 
121  // Allocate memory for problem
122  nA_ = nnz_in(CONIC_A);
123  nH_ = nnz_in(CONIC_H);
124 
125  // Allocate work vectors
126  alloc_w(nx_, true); // g
127  alloc_w(nx_, true); // lbx
128  alloc_w(nx_, true); // ubx
129  alloc_w(na_, true); // lba
130  alloc_w(na_, true); // uba
131  alloc_w(nH_, true); // H
132  alloc_w(nA_, true); // A
133 
134  }
135 
136  int ProxqpInterface::init_mem(void* mem) const {
137  if (Conic::init_mem(mem)) return 1;
138  auto m = static_cast<ProxqpMemory*>(mem);
139 
140  m->tripletList.reserve(H_.nnz());
141  m->tripletListEq.reserve(na_);
142 
143  m->g_vector.resize(nx_);
144  m->uba_vector.resize(na_);
145  m->lba_vector.resize(na_);
146  m->ubx_vector.resize(na_);
147  m->lbx_vector.resize(na_);
148  m->ub_vector.resize(na_);
149  m->lb_vector.resize(na_);
150  m->b_vector.resize(na_);
151 
152  m->add_stat("preprocessing");
153  m->add_stat("solver");
154  m->add_stat("postprocessing");
155  return 0;
156  }
157 
158  inline const char* return_status_string(casadi_int status) {
159  return "Unknown";
160  }
161 
163  solve(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
164  typedef Eigen::Triplet<double> T;
165 
166  auto m = static_cast<ProxqpMemory*>(mem);
167  m->fstats.at("preprocessing").tic();
168 
169  // Get problem data
170  double* g=w; w += nx_;
171  casadi_copy(arg[CONIC_G], nx_, g);
172  double* lbx=w; w += nx_;
173  casadi_copy(arg[CONIC_LBX], nx_, lbx);
174  double* ubx=w; w += nx_;
175  casadi_copy(arg[CONIC_UBX], nx_, ubx);
176  double* lba=w; w += na_;
177  casadi_copy(arg[CONIC_LBA], na_, lba);
178  double* uba=w; w += na_;
179  casadi_copy(arg[CONIC_UBA], na_, uba);
180  double* H=w; w += nnz_in(CONIC_H);
181  casadi_copy(arg[CONIC_H], nnz_in(CONIC_H), H);
182  double* A=w; w += nnz_in(CONIC_A);
183  casadi_copy(arg[CONIC_A], nnz_in(CONIC_A), A);
184 
185  m->g_vector = Eigen::Map<const Eigen::VectorXd>(g, nx_);
186  m->uba_vector = Eigen::Map<Eigen::VectorXd>(uba, na_);
187  m->lba_vector = Eigen::Map<Eigen::VectorXd>(lba, na_);
188  m->ubx_vector = Eigen::Map<Eigen::VectorXd>(ubx, nx_);
189  m->lbx_vector = Eigen::Map<Eigen::VectorXd>(lbx, nx_);
190 
191  // Use lhs_equals_rhs_constraint to split double-sided bounds into one-sided
192  // bound for equality constraints and double-sided for inequality constraints
193  const Eigen::Array<bool, Eigen::Dynamic, 1>
194  lhs_equals_rhs_constraint = (m->uba_vector.array() == m->lba_vector.array()).eval();
195  std::vector<unsigned int> number_of_prev_equality(lhs_equals_rhs_constraint.size(), 0);
196  std::vector<unsigned int> number_of_prev_inequality(lhs_equals_rhs_constraint.size(), 0);
197  std::vector<double> tmp_eq_vector;
198  std::vector<double> tmp_ineq_lb_vector;
199  std::vector<double> tmp_ineq_ub_vector;
200  {
201 
202  // number_of_prev_equality and number_of_prev
203  // inequality are two vectors that contains the number of
204  // equality and inequality that can be found before the current index
205  // number_of_prev_equality[i] = number of equality that can be found before index i
206  // number_of_prev_inequality[i] = number of inequality that can be found before index i
207  // For instance:
208  // equality and inequality [i, e, e, e, i, i, i, e]
209  // lhs_equals_rgs_contraint [f, t, t, t, f, f, f, t]
210  // number_of_prev_equality [0, 0, 1, 3, 3, 3, 3, 3]
211  // number_of_prev_inequality [0, 1, 1, 1, 1, 2, 3, 4]
212  for (std::size_t k=1; k<lhs_equals_rhs_constraint.size(); ++k) {
213  if (lhs_equals_rhs_constraint[k-1]) {
214  number_of_prev_equality[k] = number_of_prev_equality[k-1] + 1;
215  number_of_prev_inequality[k] = number_of_prev_inequality[k-1];
216  } else {
217  number_of_prev_inequality[k] = number_of_prev_inequality[k-1] + 1;
218  number_of_prev_equality[k] = number_of_prev_equality[k-1];
219  }
220  }
221 
222  for (std::size_t k=0; k<lhs_equals_rhs_constraint.size(); ++k) {
223  if (lhs_equals_rhs_constraint[k]) {
224  tmp_eq_vector.push_back(m->lba_vector[k]);
225  } else {
226  tmp_ineq_lb_vector.push_back(m->lba_vector[k]);
227  tmp_ineq_ub_vector.push_back(m->uba_vector[k]);
228  }
229  }
230 
231  m->b_vector.resize(tmp_eq_vector.size());
232  if (tmp_eq_vector.size() > 0) {
233  m->b_vector = Eigen::Map<Eigen::VectorXd>(
234  get_ptr(tmp_eq_vector), tmp_eq_vector.size());
235  }
236 
237  m->lba_vector.resize(tmp_ineq_lb_vector.size());
238  if (tmp_ineq_lb_vector.size() > 0) {
239  m->lba_vector = Eigen::Map<Eigen::VectorXd>(
240  get_ptr(tmp_ineq_lb_vector), tmp_ineq_lb_vector.size());
241  }
242 
243  m->uba_vector.resize(tmp_ineq_ub_vector.size());
244  if (tmp_ineq_ub_vector.size() > 0) {
245  m->uba_vector = Eigen::Map<Eigen::VectorXd>(
246  get_ptr(tmp_ineq_ub_vector), tmp_ineq_ub_vector.size());
247  }
248  }
249  std::size_t n_eq = m->b_vector.size();
250  std::size_t n_ineq = m->lba_vector.size() + m->lbx_vector.size();
251 
252  // Convert H_ from casadi::Sparsity to Eigen::SparseMatrix
253  H_.get_triplet(m->row, m->col);
254  for (int k=0; k<H_.nnz(); ++k) {
255  m->tripletList.push_back(T(
256  static_cast<double>(m->row[k]),
257  static_cast<double>(m->col[k]),
258  static_cast<double>(H[k])));
259  }
260  Eigen::SparseMatrix<double> H_spa(H_.size1(), H_.size2());
261  H_spa.setFromTriplets(m->tripletList.begin(), m->tripletList.end());
262  m->tripletList.clear();
263 
264  // Convert A_ from casadi Sparsity to Eigen::SparseMatrix and split
265  // in- and equality constraints into different matrices
266  m->tripletList.reserve(A_.nnz());
267  A_.get_triplet(m->row, m->col);
268 
269  for (int k=0; k<A_.nnz(); ++k) {
270  // Detect equality constraint
271  if (lhs_equals_rhs_constraint[m->row[k]]) {
272  // Equality constraint the row[k] is decreased
273  // by the number of previous inequality constraints
274  m->tripletListEq.push_back(T(
275  static_cast<double>(m->row[k] - number_of_prev_inequality[m->row[k]]),
276  static_cast<double>(m->col[k]),
277  static_cast<double>(A[k])));
278  } else {
279  // Inequality constraint the row[k] is decreased
280  // by the number of previous equality constraints
281  m->tripletList.push_back(T(
282  static_cast<double>(m->row[k] - number_of_prev_equality[m->row[k]]),
283  static_cast<double>(m->col[k]),
284  static_cast<double>(A[k])));
285  }
286  }
287 
288  // Handle constraints on decision variable x in inequality constraint matrix C
289  uint32_t n_constraints_x = 0;
290  if (m->ubx_vector.size() > 0 || m->lbx_vector.size() > 0) {
291  for (int k=0; k<nx_; ++k) {
292  m->tripletList.push_back(T(
293  static_cast<double>(m->lba_vector.size() + k),
294  static_cast<double>(k),
295  static_cast<double>(1.0)));
296  ++n_constraints_x;
297  }
298  }
299 
300  Eigen::SparseMatrix<double> A_spa(n_eq, nx_);
301  A_spa.setFromTriplets(m->tripletListEq.begin(), m->tripletListEq.end());
302  m->tripletListEq.clear();
303 
304  Eigen::SparseMatrix<double> C_spa(n_ineq, nx_);
305  C_spa.setFromTriplets(m->tripletList.begin(), m->tripletList.end());
306  m->tripletList.clear();
307 
308  // Get stacked lower and upper inequality bounds
309  m->ub_vector.resize(n_ineq);
310  m->lb_vector.resize(n_ineq);
311  if (m->uba_vector.size() > 0) {
312  m->ub_vector << m->uba_vector, m->ubx_vector;
313  } else {
314  m->ub_vector << m->ubx_vector;
315  }
316 
317  if (m->lba_vector.size() > 0) {
318  m->lb_vector << m->lba_vector, m->lbx_vector;
319  } else {
320  m->lb_vector << m->lbx_vector;
321  }
322  m->fstats.at("preprocessing").toc();
323 
324  // Solve Problem
325  m->fstats.at("solver").tic();
326 
327  if (sparse_backend) {
328  m->sparse_solver = proxsuite::proxqp::sparse::QP<double, long long> (nx_, n_eq, n_ineq);
329  m->sparse_solver.init(H_spa, m->g_vector,
330  A_spa, m->b_vector,
331  C_spa, m->lb_vector, m->ub_vector);
332  m->sparse_solver.settings = settings_;
333 
334  m->sparse_solver.solve();
335 
336  m->results_x = std::make_unique<Eigen::VectorXd>(m->sparse_solver.results.x);
337  m->results_y = std::make_unique<Eigen::VectorXd>(m->sparse_solver.results.y);
338  m->results_z = std::make_unique<Eigen::VectorXd>(m->sparse_solver.results.z);
339  m->objValue = m->sparse_solver.results.info.objValue;
340  m->status = m->sparse_solver.results.info.status;
341  } else {
342  m->dense_solver = proxsuite::proxqp::dense::QP<double> (nx_, n_eq, n_ineq);
343  m->dense_solver.init(Eigen::MatrixXd(H_spa), m->g_vector,
344  Eigen::MatrixXd(A_spa), m->b_vector,
345  Eigen::MatrixXd(C_spa), m->lb_vector, m->ub_vector);
346  m->dense_solver.settings = settings_;
347 
348  m->dense_solver.solve();
349 
350  m->results_x = std::make_unique<Eigen::VectorXd>(m->dense_solver.results.x);
351  m->results_y = std::make_unique<Eigen::VectorXd>(m->dense_solver.results.y);
352  m->results_z = std::make_unique<Eigen::VectorXd>(m->dense_solver.results.z);
353  m->objValue = m->dense_solver.results.info.objValue;
354  m->status = m->dense_solver.results.info.status;
355  }
356  m->fstats.at("solver").toc();
357 
358  // Post-processing to retrieve the results
359  m->fstats.at("postprocessing").tic();
360  casadi_copy(m->results_x->data(), nx_, res[CONIC_X]);
361 
362  // Copy back the multipliers.
363  // Note, casadi has LAM_X (multipliers for constraints on variable x) and
364  // LAM_A (multipliers for in- and equality constraints) while proxqp has results_y
365  // (equality multipliers) and results_z (inequality multipliers).
366  if (n_constraints_x > 0) {
367  casadi_copy(m->results_z->tail(n_constraints_x).data(), n_constraints_x, res[CONIC_LAM_X]);
368  }
369  if (!n_eq) {
370  uint32_t n_lam_a = m->results_z->size() - n_constraints_x;
371  casadi_assert_dev(n_lam_a == na_);
372  casadi_copy(m->results_z->head(n_lam_a).data(), n_lam_a, res[CONIC_LAM_A]);
373  } else if (!n_ineq) {
374  casadi_copy(m->results_z->data(), na_, res[CONIC_LAM_A]);
375  } else {
376  bool ineq_constraints_a = (lhs_equals_rhs_constraint == 0).any();
377  if (!ineq_constraints_a) {
378  casadi_copy(m->results_y->data(), na_, res[CONIC_LAM_A]);
379  } else {
380  Eigen::VectorXd lam_a(na_);
381 
382  for (int k=0; k<lhs_equals_rhs_constraint.size(); ++k) {
383  if (lhs_equals_rhs_constraint[k]) {
384  lam_a[k] = m->results_y->coeff(k);
385  } else {
386  lam_a[k] = m->results_z->coeff(k);
387  }
388  }
389  casadi_copy(lam_a.data(), na_, res[CONIC_LAM_A]);
390  }
391  }
392 
393  if (res[CONIC_COST]) {
394  *res[CONIC_COST] = m->objValue;
395  }
396 
397  m->d_qp.success = m->status == QPSolverOutput::PROXQP_SOLVED;
398  if (m->d_qp.success) {
399  m->d_qp.unified_return_status = SOLVER_RET_SUCCESS;
400  } else {
401  if (m->status == QPSolverOutput::PROXQP_MAX_ITER_REACHED) {
402  m->d_qp.unified_return_status = SOLVER_RET_LIMITED;
403  } else { // primal or dual infeasibility
404  m->d_qp.unified_return_status = SOLVER_RET_UNKNOWN;
405  }
406  }
407  m->fstats.at("postprocessing").toc();
408 
409  return 0;
410  }
411 
412 
413  Dict ProxqpInterface::get_stats(void* mem) const {
414 
415  Dict stats = Conic::get_stats(mem);
416  auto m = static_cast<ProxqpMemory*>(mem);
417 
418  std::string ret_status;
419  if (m->status == QPSolverOutput::PROXQP_SOLVED) {
420  ret_status = "PROXQP_SOLVED";
421  } else if (m->status == QPSolverOutput::PROXQP_MAX_ITER_REACHED) {
422  ret_status = "PROXQP_MAX_ITER_REACHED";
423  } else if (m->status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) {
424  ret_status = "PROXQP_PRIMAL_INFEASIBLE";
425  } else if (m->status == QPSolverOutput::PROXQP_DUAL_INFEASIBLE) {
426  ret_status = "PROXQP_DUAL_INFEASIBLE";
427  }
428 
429  stats["return_status"] = ret_status;
430  return stats;
431  }
432 
434  : sparse_solver(1, 0, 0),
435  dense_solver(1, 0, 0) {
436  }
437 
439  this->sparse_solver.cleanup();
440  this->dense_solver.cleanup();
441  }
442 
444  s.version("ProxqpInterface", 1);
445  s.unpack("ProxqpInterface::warm_start_primal", warm_start_primal_);
446  s.unpack("ProxqpInterface::warm_start_dual", warm_start_dual_);
447 
448  s.unpack("ProxqpInterface::settings::default_rho", settings_.default_rho);
449  s.unpack("ProxqpInterface::settings::default_mu_eq", settings_.default_mu_eq);
450  s.unpack("ProxqpInterface::settings::default_mu_in", settings_.default_mu_in);
451  s.unpack("ProxqpInterface::settings::eps_abs", settings_.eps_abs);
452  s.unpack("ProxqpInterface::settings::eps_rel", settings_.eps_rel);
453  s.unpack("ProxqpInterface::settings::max_iter", max_iter);
454  settings_.max_iter = isize(max_iter);
455  s.unpack("ProxqpInterface::settings::verbose", settings_.verbose);
456  s.unpack("ProxqpInterface::settings::sparse_backend", sparse_backend);
457  }
458 
461  s.version("ProxqpInterface", 1);
462  s.pack("ProxqpInterface::warm_start_primal", warm_start_primal_);
463  s.pack("ProxqpInterface::warm_start_dual", warm_start_dual_);
464  s.pack("ProxqpInterface::settings::default_rho", settings_.default_rho);
465  s.pack("ProxqpInterface::settings::default_mu_eq", settings_.default_mu_eq);
466  s.pack("ProxqpInterface::settings::default_mu_in", settings_.default_mu_in);
467  s.pack("ProxqpInterface::settings::eps_abs", settings_.eps_abs);
468  s.pack("ProxqpInterface::settings::eps_rel", settings_.eps_rel);
469  s.pack("ProxqpInterface::settings::max_iter", static_cast<double>(settings_.max_iter));
470  s.pack("ProxqpInterface::settings::verbose", settings_.verbose);
471  s.pack("ProxqpInterface::settings::sparse_backend", sparse_backend);
472  }
473 
474 } // namespace casadi
Internal class.
Definition: conic_impl.hpp:44
static const Options options_
Options.
Definition: conic_impl.hpp:83
casadi_int nx_
Number of decision variables.
Definition: conic_impl.hpp:169
int init_mem(void *mem) const override
Initalize memory block.
Definition: conic.cpp:451
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
Definition: conic_impl.hpp:172
Sparsity H_
Problem structure.
Definition: conic_impl.hpp:166
void init(const Dict &opts) override
Initialize.
Definition: conic.cpp:412
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: conic.cpp:738
Dict get_stats(void *mem) const override
Get all statistics.
Definition: conic.cpp:711
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const final
Solve the QP.
Definition: conic.cpp:532
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
casadi_int nnz_in() const
Number of input/output nonzeros.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
void clear_mem()
Clear all memory (called from destructor)
static const Options options_
const Options
Dict get_stats(void *mem) const override
Get all statistics.
proxsuite::proxqp::Settings< double > settings_
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Solve the QP.
int init_mem(void *mem) const override
Initalize memory block.
~ProxqpInterface() override
Destructor.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void init(const Dict &opts) override
Initialize.
ProxqpInterface(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new Solver.
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
casadi_int size1() const
Get the number of rows.
Definition: sparsity.cpp:124
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
void get_triplet(std::vector< casadi_int > &row, std::vector< casadi_int > &col) const
Get the sparsity in sparse triplet format.
Definition: sparsity.cpp:385
The casadi namespace.
Definition: archiver.cpp:28
void CASADI_CONIC_PROXQP_EXPORT casadi_load_conic_proxqp()
@ CONIC_UBA
dense, (nc x 1)
Definition: conic.hpp:181
@ CONIC_A
The matrix A: sparse, (nc x n) - product with x must be dense.
Definition: conic.hpp:177
@ CONIC_G
The vector g: dense, (n x 1)
Definition: conic.hpp:175
@ CONIC_LBA
dense, (nc x 1)
Definition: conic.hpp:179
@ CONIC_UBX
dense, (n x 1)
Definition: conic.hpp:185
@ CONIC_H
Definition: conic.hpp:173
@ CONIC_LBX
dense, (n x 1)
Definition: conic.hpp:183
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
const char * return_status_string(Bonmin::TMINLP::SolverReturn status)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
int CASADI_CONIC_PROXQP_EXPORT casadi_register_conic_proxqp(Conic::Plugin *plugin)
bool any(const std::vector< bool > &v)
Check if any arguments are true.
Definition: casadi_misc.cpp:84
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
@ SOLVER_RET_LIMITED
@ SOLVER_RET_SUCCESS
@ SOLVER_RET_UNKNOWN
@ CONIC_X
The primal solution.
Definition: conic.hpp:201
@ CONIC_LAM_A
The dual solution corresponding to linear bounds.
Definition: conic.hpp:205
@ CONIC_COST
The optimal cost.
Definition: conic.hpp:203
@ CONIC_LAM_X
The dual solution corresponding to simple bounds.
Definition: conic.hpp:207
Options metadata for a class.
Definition: options.hpp:40
std::map< std::string, FStats > fstats
proxsuite::proxqp::sparse::QP< double, long long > sparse_solver
std::vector< T > tripletList
proxsuite::proxqp::dense::QP< double > dense_solver