26 #include "osqp_interface.hpp"
27 #include "casadi/core/casadi_misc.hpp"
30 typedef OSQPInt c_int;
31 typedef OSQPFloat c_float;
32 typedef OSQPCscMatrix csc;
34 struct casadiOSQPData {
49 int CASADI_CONIC_OSQP_EXPORT
52 plugin->name =
"osqp";
54 plugin->version = CASADI_VERSION;
66 const std::map<std::string, Sparsity>& st)
79 "const Options to be passed to osqp."}},
82 "Use x0 input to warmstart [Default: true]."}},
85 "Use lam_a0 and lam_x0 input to warmstart [Default: true]."}}
97 "OSQP interface requires OSQP version < 1.0.0. "
98 "Please downgrade your OSQP installation.");
110 for (
auto&& op : opts) {
111 if (op.first==
"warm_start_primal") {
113 }
else if (op.first==
"warm_start_dual") {
115 }
else if (op.first==
"osqp") {
116 const Dict& opts = op.second;
117 for (
auto&& op : opts) {
118 if (op.first==
"rho") {
120 }
else if (op.first==
"sigma") {
122 }
else if (op.first==
"scaling") {
124 }
else if (op.first==
"adaptive_rho") {
126 }
else if (op.first==
"adaptive_rho_interval") {
127 settings_.adaptive_rho_interval = op.second;
128 }
else if (op.first==
"adaptive_rho_tolerance") {
129 settings_.adaptive_rho_tolerance = op.second;
132 }
else if (op.first==
"max_iter") {
134 }
else if (op.first==
"eps_abs") {
136 }
else if (op.first==
"eps_rel") {
138 }
else if (op.first==
"eps_prim_inf") {
140 }
else if (op.first==
"eps_dual_inf") {
142 }
else if (op.first==
"alpha") {
144 }
else if (op.first==
"delta") {
146 }
else if (op.first==
"polish" || op.first==
"polishing") {
152 }
else if (op.first==
"polish_refine_iter") {
153 settings_.polish_refine_iter = op.second;
154 }
else if (op.first==
"verbose") {
156 }
else if (op.first==
"scaled_termination") {
157 settings_.scaled_termination = op.second;
158 }
else if (op.first==
"check_termination") {
160 }
else if (op.first==
"warm_start" || op.first==
"warm_starting") {
161 casadi_error(
"OSQP's warm_start option is impure and therefore disabled. "
162 "Use CasADi options 'warm_start_primal' and 'warm_start_dual' instead.");
166 casadi_error(
"Not recognised");
183 casadi_assert(
version_ge(osqp_version(),
"1.0.0"),
184 "OSQP interface requires OSQP version >= 1.0.0. "
185 "Please update your OSQP installation.");
197 std::vector<double> dummy(std::max(
nx_+
na_, std::max(Asp.
nnz(),
H_.
nnz())));
199 std::vector<c_int> A_row = vector_static_cast<c_int>(Asp.
get_row());
200 std::vector<c_int> A_colind = vector_static_cast<c_int>(Asp.
get_colind());
201 std::vector<c_int> H_row = vector_static_cast<c_int>(H_triu.
get_row());
202 std::vector<c_int> H_colind = vector_static_cast<c_int>(H_triu.
get_colind());
239 if (osqp_setup(&m->work, data.P, data.q, data.A,
240 data.l, data.u, data.m, data.n, &
settings_))
return 1;
242 if (osqp_setup(&m->work, &data, &
settings_))
return 1;
245 m->fstats[
"preprocessing"] =
FStats();
246 m->fstats[
"solver"] =
FStats();
247 m->fstats[
"postprocessing"] =
FStats();
256 solve(
const double** arg,
double** res, casadi_int* iw,
double* w,
void* mem)
const {
274 ret = osqp_update_data_vec(m->work, arg[
CONIC_G],
nullptr,
nullptr);
276 ret = osqp_update_lin_cost(m->work, arg[
CONIC_G]);
278 casadi_assert(ret==0,
"Problem in osqp_update_lin_cost");
287 casadi_clip_min(w, 2*(
nx_+
na_), -OSQP_INFTY, 0);
288 casadi_clip_max(w, 2*(
nx_+
na_), OSQP_INFTY, 0);
290 ret = osqp_update_data_vec(m->work,
nullptr, w, w+
nx_+
na_);
292 ret = osqp_update_bounds(m->work, w, w+
nx_+
na_);
294 casadi_assert(ret==0,
"Problem in osqp_update_bounds");
297 casadi_tri_project(arg[
CONIC_H],
H_, w,
false);
300 const casadi_int* colind =
A_.
colind();
303 casadi_int offset = 0;
305 for (casadi_int i=0; i<
nx_; ++i) {
308 casadi_int n = colind[i+1]-colind[i];
315 ret = osqp_update_data_mat(m->work, w,
nullptr,
nnzHupp_, A,
nullptr,
nnzA_);
317 ret = osqp_update_P_A(m->work, w,
nullptr,
nnzHupp_, A,
nullptr,
nnzA_);
319 casadi_assert(ret==0,
"Problem in osqp_update_P_A");
322 osqp_cold_start(m->work);
329 ret = osqp_warm_start(m->work, arg[
CONIC_X0],
nullptr);
331 ret = osqp_warm_start_x(m->work, arg[
CONIC_X0]);
333 casadi_assert(ret==0,
"Problem in osqp_warm_start_x");
340 ret = osqp_warm_start(m->work,
nullptr, w);
342 ret = osqp_warm_start_y(m->work, w);
344 casadi_assert(ret==0,
"Problem in osqp_warm_start_y");
348 ret = osqp_solve(m->work);
349 casadi_assert(ret==0,
"Problem in osqp_solve");
356 m->d_qp.success = m->work->info->status_val == OSQP_SOLVED;
357 if (m->d_qp.success) {
359 }
else if (m->work->info->status_val == OSQP_PRIMAL_INFEASIBLE ||
360 m->work->info->status_val == OSQP_MAX_ITER_REACHED ||
361 m->work->info->status_val == OSQP_DUAL_INFEASIBLE ||
362 m->work->info->status_val == OSQP_NON_CVX ||
363 m->work->info->status_val == OSQP_PRIMAL_INFEASIBLE_INACCURATE ||
364 m->work->info->status_val == OSQP_DUAL_INFEASIBLE_INACCURATE) {
379 casadi_int dummy_size = std::max(
nx_+
na_, std::max(Asp.
nnz(),
H_.
nnz()));
382 std::string osqp_csc_type =
"OSQPCscMatrix";
384 std::string osqp_csc_type =
"csc";
386 g.
local(
"A", osqp_csc_type);
387 g.
local(
"dummy[" +
str(dummy_size) +
"]",
"casadi_real");
388 g << g.
clear(
"dummy", dummy_size) <<
"\n";
391 std::string osqp_int_type =
"OSQPInt";
393 std::string osqp_int_type =
"c_int";
405 g.
local(
"A", osqp_csc_type);
406 g <<
"A.m = " <<
nx_ +
na_ <<
";\n";
407 g <<
"A.n = " <<
nx_ <<
";\n";
408 g <<
"A.nz = " <<
nnzA_ <<
";\n";
409 g <<
"A.nzmax = " <<
nnzA_ <<
";\n";
410 g <<
"A.x = dummy;\n";
411 g <<
"A.i = A_row;\n";
412 g <<
"A.p = A_colind;\n";
414 g.
local(
"H", osqp_csc_type);
415 g <<
"H.m = " <<
nx_ <<
";\n";
416 g <<
"H.n = " <<
nx_ <<
";\n";
419 g <<
"H.x = dummy;\n";
420 g <<
"H.i = H_row;\n";
421 g <<
"H.p = H_colind;\n";
426 g <<
" OSQPInt n;\n";
427 g <<
" OSQPInt m;\n";
428 g <<
" OSQPCscMatrix * P;\n";
429 g <<
" OSQPCscMatrix * A;\n";
430 g <<
" OSQPFloat * q;\n";
431 g <<
" OSQPFloat * l;\n";
432 g <<
" OSQPFloat * u;\n";
435 g.
local(
"data",
"OSQPData");
437 g <<
"data.n = " <<
nx_ <<
";\n";
438 g <<
"data.m = " <<
nx_ +
na_ <<
";\n";
439 g <<
"data.P = &H;\n";
440 g <<
"data.q = dummy;\n";
441 g <<
"data.A = &A;\n";
442 g <<
"data.l = dummy;\n";
443 g <<
"data.u = dummy;\n";
445 g.
local(
"settings",
"OSQPSettings");
446 g <<
"osqp_set_default_settings(&settings);\n";
449 g <<
"settings.scaling = " <<
settings_.scaling <<
";\n";
450 g <<
"settings.adaptive_rho = " <<
settings_.adaptive_rho <<
";\n";
451 g <<
"settings.adaptive_rho_interval = " <<
settings_.adaptive_rho_interval
453 g <<
"settings.adaptive_rho_tolerance = " << g.
constant(
settings_.adaptive_rho_tolerance)
456 g <<
"settings.max_iter = " <<
settings_.max_iter <<
";\n";
464 g <<
"settings.polishing = " <<
settings_.polishing <<
";\n";
466 g <<
"settings.polish = " <<
settings_.polish <<
";\n";
468 g <<
"settings.polish_refine_iter = " <<
settings_.polish_refine_iter <<
";\n";
469 g <<
"settings.verbose = " <<
settings_.verbose <<
";\n";
470 g <<
"settings.scaled_termination = " <<
settings_.scaled_termination <<
";\n";
471 g <<
"settings.check_termination = " <<
settings_.check_termination <<
";\n";
473 g <<
"settings.warm_starting = " <<
settings_.warm_starting <<
";\n";
475 g <<
"settings.warm_start = " <<
settings_.warm_start <<
";\n";
479 g <<
"return osqp_setup(&" +
codegen_mem(g) +
", data.P, data.q, data.A, "
480 <<
"data.l, data.u, data.m, data.n, &settings)!=0;\n";
482 g <<
"return osqp_setup(&" +
codegen_mem(g) +
", &data, &settings)!=0;\n";
491 g.
local(
"work",
"OSQPSolver",
"*");
493 g.
local(
"work",
"OSQPWorkspace",
"*");
500 g <<
"if (osqp_update_data_vec(work, w, 0, 0)) return 1;\n";
502 g <<
"if (osqp_update_lin_cost(work, w)) return 1;\n";
510 g.
comment(
"Convert C++ infinity values to OSQP infinity");
514 g <<
"if (osqp_update_data_vec(work, 0, w, w+" +
str(
nx_+
na_)+
")) return 1;\n";
516 g <<
"if (osqp_update_bounds(work, w, w+" +
str(
nx_+
na_)+
")) return 1;\n";
521 g.
comment(
"Get constraint matrix");
523 g.
local(
"offset",
"casadi_int");
524 g.
local(
"n",
"casadi_int");
525 g.
local(
"i",
"casadi_int");
526 g <<
"offset = 0;\n";
527 g <<
"for (i=0; i< " <<
nx_ <<
"; ++i) {\n";
530 g <<
"n = " + A_colind +
"[i+1]-" + A_colind +
"[i];\n";
531 g <<
"casadi_copy(" << g.
arg(
CONIC_A) <<
"+" + A_colind +
"[i], n, "
533 g <<
"offset+= n;\n";
536 g.
comment(
"Pass Hessian and constraint matrices");
539 ", 0, " +
str(
nnzA_) +
")) return 1;\n";
542 ", 0, " +
str(
nnzA_) +
")) return 1;\n";
546 g <<
"osqp_cold_start(work);\n";
553 g <<
"if (osqp_warm_start(work, " + g.
arg(
CONIC_X0) +
", OSQP_NULL)) return 1;\n";
555 g <<
"if (osqp_warm_start_x(work, " + g.
arg(
CONIC_X0) +
")) return 1;\n";
563 g <<
"if (osqp_warm_start(work, OSQP_NULL, w)) return 1;\n";
565 g <<
"if (osqp_warm_start_y(work, w)) return 1;\n";
569 g <<
"if (osqp_solve(work)) return 1;\n";
576 g <<
"if (work->info->status_val != OSQP_SOLVED) {\n";
578 g <<
"return -1000;\n";
580 g <<
"if (work->info->status_val == OSQP_PRIMAL_INFEASIBLE || ";
581 g <<
"work->info->status_val == OSQP_MAX_ITER_REACHED || ";
582 g <<
"work->info->status_val == OSQP_DUAL_INFEASIBLE || ";
583 g <<
"work->info->status_val == OSQP_PRIMAL_INFEASIBLE_INACCURATE || ";
584 g <<
"work->info->status_val == OSQP_DUAL_INFEASIBLE_INACCURATE || ";
585 g <<
"work->info->status_val == OSQP_NON_CVX) {\n";
599 stats[
"return_status"] = m->
work->info->status;
614 int version = s.
version(
"OsqpInterface", 1, 2);
624 s.
unpack(
"OsqpInterface::settings::adaptive_rho",
settings_.adaptive_rho);
625 s.
unpack(
"OsqpInterface::settings::adaptive_rho_interval",
settings_.adaptive_rho_interval);
626 s.
unpack(
"OsqpInterface::settings::adaptive_rho_tolerance",
settings_.adaptive_rho_tolerance);
631 s.
unpack(
"OsqpInterface::settings::eps_prim_inf",
settings_.eps_prim_inf);
632 s.
unpack(
"OsqpInterface::settings::eps_dual_inf",
settings_.eps_dual_inf);
640 s.
unpack(
"OsqpInterface::settings::polish_refine_iter",
settings_.polish_refine_iter);
642 s.
unpack(
"OsqpInterface::settings::scaled_termination",
settings_.scaled_termination);
643 s.
unpack(
"OsqpInterface::settings::check_termination",
settings_.check_termination);
645 s.
unpack(
"OsqpInterface::settings::warm_start",
settings_.warm_starting);
666 s.
pack(
"OsqpInterface::settings::scaling",
settings_.scaling);
667 s.
pack(
"OsqpInterface::settings::adaptive_rho",
settings_.adaptive_rho);
668 s.
pack(
"OsqpInterface::settings::adaptive_rho_interval",
settings_.adaptive_rho_interval);
669 s.
pack(
"OsqpInterface::settings::adaptive_rho_tolerance",
settings_.adaptive_rho_tolerance);
671 s.
pack(
"OsqpInterface::settings::max_iter",
settings_.max_iter);
672 s.
pack(
"OsqpInterface::settings::eps_abs",
settings_.eps_abs);
673 s.
pack(
"OsqpInterface::settings::eps_rel",
settings_.eps_rel);
674 s.
pack(
"OsqpInterface::settings::eps_prim_inf",
settings_.eps_prim_inf);
675 s.
pack(
"OsqpInterface::settings::eps_dual_inf",
settings_.eps_dual_inf);
679 s.
pack(
"OsqpInterface::settings::polish",
settings_.polishing);
683 s.
pack(
"OsqpInterface::settings::polish_refine_iter",
settings_.polish_refine_iter);
684 s.
pack(
"OsqpInterface::settings::verbose",
settings_.verbose);
685 s.
pack(
"OsqpInterface::settings::scaled_termination",
settings_.scaled_termination);
686 s.
pack(
"OsqpInterface::settings::check_termination",
settings_.check_termination);
688 s.
pack(
"OsqpInterface::settings::warm_start",
settings_.warm_starting);
690 s.
pack(
"OsqpInterface::settings::warm_start",
settings_.warm_start);
Helper class for C code generation.
std::string clip_min(const std::string &x, casadi_int n, const std::string &min, const std::string &mask)
Codegen clip_min: Clips the smaller entries in a vector than min to the min.
std::string arg(casadi_int i) const
Refer to argument.
void comment(const std::string &s)
Write a comment line (ignored if not verbose)
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
std::string res(casadi_int i) const
Refer to resuly.
void init_local(const std::string &name, const std::string &def)
Specify the default value for a local variable.
std::string clip_max(const std::string &x, casadi_int n, const std::string &min, const std::string &mask)
Codegen clip_max: Clips the larger entries in a vector than max to the max.
void add_include(const std::string &new_include, bool relative_path=false, const std::string &use_ifdef=std::string())
Add an include file optionally using a relative path "..." instead of an absolute path <....
void constant_copy(const std::string &var_name, const std::vector< casadi_int > &v, const std::string &type="casadi_int")
Represent an array constant; adding it when new.
void copy_check(const std::string &arg, std::size_t n, const std::string &res, bool check_lhs=true, bool check_rhs=true)
std::string tri_project(const std::string &arg, const Sparsity &sp_arg, const std::string &res, bool lower)
Project triangular part.
void copy_default(const std::string &arg, std::size_t n, const std::string &res, const std::string &def, bool check_rhs=true)
std::string clear(const std::string &res, std::size_t n)
Create a fill operation.
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
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.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
std::string codegen_mem(CodeGenerator &g, const std::string &index="mem") const
Get thread-local memory object.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
void codegen_free_mem(CodeGenerator &g) const override
Codegen free_mem.
void codegen_init_mem(CodeGenerator &g) const override
Codegen alloc_mem.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Dict get_stats(void *mem) const override
Get all statistics.
void codegen_body(CodeGenerator &g) const override
Generate code for the function body.
~OsqpInterface() override
Destructor.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
int init_mem(void *mem) const override
Initalize memory block.
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
OsqpInterface(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new Solver.
void init(const Dict &opts) override
Initialize.
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Solve the QP.
static const std::string meta_doc
A documentation string.
double overrun_check_[10000]
void deps_version_check(const std::string &stage) const override
static const Options options_
const Options
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool error_on_fail_
Throw an exception on failure?
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
static Sparsity triu(const Sparsity &x, bool includeDiagonal=true)
Enlarge matrix.
casadi_int nnz_upper(bool strictly=false) const
Number of non-zeros in the upper triangular half,.
casadi_int nnz() const
Get the number of (structural) non-zeros.
std::vector< casadi_int > get_colind() const
Get the column index for each column.
std::vector< casadi_int > get_row() const
Get the row for each non-zero entry.
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
@ 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)
int CASADI_CONIC_OSQP_EXPORT casadi_register_conic_osqp(Conic::Plugin *plugin)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
void CASADI_CONIC_OSQP_EXPORT casadi_load_conic_osqp()
bool version_ge(const std::string &version_left, const std::string &version_right)
Compare versions: returns true if version_left >= version_right.
@ 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.