26 #include "proxqp_interface.hpp"
27 #include "casadi/core/casadi_misc.hpp"
30 using namespace proxsuite::proxqp;
35 int CASADI_CONIC_PROXQP_EXPORT
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;
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) {
64 "const proxqp options."}},
67 "Use x input to warmstart [Default: true]."}},
70 "Use y and z input to warmstart [Default: true]."}}
82 for (
auto&& op : opts) {
83 if (op.first==
"warm_start_primal") {
85 }
else if (op.first==
"warm_start_dual") {
87 }
else if (op.first==
"proxqp") {
88 const Dict& opts = op.second;
89 for (
auto&& op : opts) {
90 if (op.first==
"default_rho") {
92 }
else if (op.first==
"default_mu_eq") {
94 }
else if (op.first==
"default_mu_in") {
96 }
else if (op.first==
"eps_abs") {
98 }
else if (op.first==
"eps_rel") {
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") {
105 }
else if (op.first==
"backend") {
106 if (op.second ==
"sparse") {
108 }
else if (op.second ==
"dense") {
111 casadi_error(
"[Backend option] Please specify either sparse or dense");
114 casadi_error(
"[ProxQP settings] User-specified option "
115 "'" +
str(op.first) +
"' not recognized.");
141 m->tripletListEq.reserve(
na_);
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_);
152 m->add_stat(
"preprocessing");
153 m->add_stat(
"solver");
154 m->add_stat(
"postprocessing");
163 solve(
const double** arg,
double** res, casadi_int* iw,
double* w,
void* mem)
const {
164 typedef Eigen::Triplet<double>
T;
167 m->
fstats.at(
"preprocessing").tic();
170 double* g=w; w +=
nx_;
172 double* lbx=w; w +=
nx_;
174 double* ubx=w; w +=
nx_;
176 double* lba=w; w +=
na_;
178 double* uba=w; w +=
na_;
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_);
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;
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];
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];
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]);
226 tmp_ineq_lb_vector.push_back(m->lba_vector[k]);
227 tmp_ineq_ub_vector.push_back(m->uba_vector[k]);
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());
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());
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());
249 std::size_t n_eq = m->b_vector.size();
250 std::size_t n_ineq = m->lba_vector.size() + m->lbx_vector.size();
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])));
261 H_spa.setFromTriplets(m->tripletList.begin(), m->tripletList.end());
262 m->tripletList.clear();
266 m->tripletList.reserve(
A_.
nnz());
269 for (
int k=0; k<
A_.
nnz(); ++k) {
271 if (lhs_equals_rhs_constraint[m->row[k]]) {
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])));
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])));
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)));
300 Eigen::SparseMatrix<double> A_spa(n_eq,
nx_);
301 A_spa.setFromTriplets(m->tripletListEq.begin(), m->tripletListEq.end());
302 m->tripletListEq.clear();
304 Eigen::SparseMatrix<double> C_spa(n_ineq,
nx_);
305 C_spa.setFromTriplets(m->tripletList.begin(), m->tripletList.end());
306 m->tripletList.clear();
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;
314 m->ub_vector << m->ubx_vector;
317 if (m->lba_vector.size() > 0) {
318 m->lb_vector << m->lba_vector, m->lbx_vector;
320 m->lb_vector << m->lbx_vector;
322 m->fstats.at(
"preprocessing").toc();
325 m->fstats.at(
"solver").tic();
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,
331 C_spa, m->lb_vector, m->ub_vector);
334 m->sparse_solver.solve();
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;
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);
348 m->dense_solver.solve();
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;
356 m->fstats.at(
"solver").toc();
359 m->fstats.at(
"postprocessing").tic();
366 if (n_constraints_x > 0) {
370 uint32_t n_lam_a = m->results_z->size() - n_constraints_x;
371 casadi_assert_dev(n_lam_a ==
na_);
373 }
else if (!n_ineq) {
376 bool ineq_constraints_a = (lhs_equals_rhs_constraint == 0).
any();
377 if (!ineq_constraints_a) {
380 Eigen::VectorXd lam_a(
na_);
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);
386 lam_a[k] = m->results_z->coeff(k);
397 m->d_qp.success = m->status == QPSolverOutput::PROXQP_SOLVED;
398 if (m->d_qp.success) {
401 if (m->status == QPSolverOutput::PROXQP_MAX_ITER_REACHED) {
407 m->fstats.at(
"postprocessing").toc();
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";
429 stats[
"return_status"] = ret_status;
434 : sparse_solver(1, 0, 0),
435 dense_solver(1, 0, 0) {
444 s.
version(
"ProxqpInterface", 1);
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);
461 s.
version(
"ProxqpInterface", 1);
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);
static const Options options_
Options.
casadi_int nx_
Number of decision variables.
int init_mem(void *mem) const override
Initalize memory block.
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
Sparsity H_
Problem structure.
void init(const Dict &opts) override
Initialize.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Dict get_stats(void *mem) const override
Get all statistics.
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const final
Solve the QP.
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.
casadi_int nnz() const
Get the number of (structural) non-zeros.
casadi_int size2() const
Get the number of columns.
void get_triplet(std::vector< casadi_int > &row, std::vector< casadi_int > &col) const
Get the sparsity in sparse triplet format.
void CASADI_CONIC_PROXQP_EXPORT casadi_load_conic_proxqp()
@ CONIC_UBA
dense, (nc x 1)
@ CONIC_A
The matrix A: sparse, (nc x n) - product with x must be dense.
@ CONIC_G
The vector g: dense, (n x 1)
@ CONIC_LBA
dense, (nc x 1)
@ CONIC_UBX
dense, (n x 1)
@ CONIC_LBX
dense, (n x 1)
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.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
@ CONIC_X
The primal solution.
@ CONIC_LAM_A
The dual solution corresponding to linear bounds.
@ CONIC_COST
The optimal cost.
@ CONIC_LAM_X
The dual solution corresponding to simple bounds.
Options metadata for a class.
std::map< std::string, FStats > fstats
~ProxqpMemory()
Destructor.
ProxqpMemory()
Constructor.
proxsuite::proxqp::sparse::QP< double, long long > sparse_solver
std::vector< T > tripletList
proxsuite::proxqp::dense::QP< double > dense_solver