osqp_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, Kobe Bergmans
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 "osqp_interface.hpp"
27 #include "casadi/core/casadi_misc.hpp"
28 
29 #ifdef WITH_OSQP_V1
30 typedef OSQPInt c_int;
31 typedef OSQPFloat c_float;
32 typedef OSQPCscMatrix csc;
33 
34 struct casadiOSQPData {
35  OSQPInt n;
36  OSQPInt m;
37  OSQPCscMatrix * P;
38  // quadratic objective matrix P (size n x n).
39  OSQPCscMatrix * A;
40  OSQPFloat * q;
41  OSQPFloat * l;
42  OSQPFloat * u;
43 };
44 #endif
45 
46 namespace casadi {
47 
48  extern "C"
49  int CASADI_CONIC_OSQP_EXPORT
50  casadi_register_conic_osqp(Conic::Plugin* plugin) {
51  plugin->creator = OsqpInterface::creator;
52  plugin->name = "osqp";
53  plugin->doc = OsqpInterface::meta_doc.c_str();
54  plugin->version = CASADI_VERSION;
55  plugin->options = &OsqpInterface::options_;
56  plugin->deserialize = &OsqpInterface::deserialize;
57  return 0;
58  }
59 
60  extern "C"
61  void CASADI_CONIC_OSQP_EXPORT casadi_load_conic_osqp() {
63  }
64 
65  OsqpInterface::OsqpInterface(const std::string& name,
66  const std::map<std::string, Sparsity>& st)
67  : Conic(name, st) {
68 
69  }
70 
72  clear_mem();
73  }
74 
76  = {{&Conic::options_},
77  {{"osqp",
78  {OT_DICT,
79  "const Options to be passed to osqp."}},
80  {"warm_start_primal",
81  {OT_BOOL,
82  "Use x0 input to warmstart [Default: true]."}},
83  {"warm_start_dual",
84  {OT_BOOL,
85  "Use lam_a0 and lam_x0 input to warmstart [Default: true]."}}
86  }
87  };
88 
89  void OsqpInterface::init(const Dict& opts) {
90  // Initialize the base classes
91  Conic::init(opts);
92 
93  overrun_check_[0] = 0;
94  osqp_set_default_settings(&settings_);
95 
96  casadi_assert(overrun_check_[0]==0,
97  "OSQP interface requires OSQP version < 1.0.0. "
98  "Please downgrade your OSQP installation.");
99 
100 #ifdef WITH_OSQP_V1
101  settings_.warm_starting = false;
102 #else
103  settings_.warm_start = false;
104 #endif
105 
106  warm_start_primal_ = true;
107  warm_start_dual_ = true;
108 
109  // Read options
110  for (auto&& op : opts) {
111  if (op.first=="warm_start_primal") {
112  warm_start_primal_ = op.second;
113  } else if (op.first=="warm_start_dual") {
114  warm_start_dual_ = op.second;
115  } else if (op.first=="osqp") {
116  const Dict& opts = op.second;
117  for (auto&& op : opts) {
118  if (op.first=="rho") {
119  settings_.rho = op.second;
120  } else if (op.first=="sigma") {
121  settings_.sigma = op.second;
122  } else if (op.first=="scaling") {
123  settings_.scaling = op.second;
124  } else if (op.first=="adaptive_rho") {
125  settings_.adaptive_rho = op.second;
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;
130  //} else if (op.first=="adaptive_rho_fraction") {
131  // settings_.adaptive_rho_fraction = op.second;
132  } else if (op.first=="max_iter") {
133  settings_.max_iter = op.second;
134  } else if (op.first=="eps_abs") {
135  settings_.eps_abs = op.second;
136  } else if (op.first=="eps_rel") {
137  settings_.eps_rel = op.second;
138  } else if (op.first=="eps_prim_inf") {
139  settings_.eps_prim_inf = op.second;
140  } else if (op.first=="eps_dual_inf") {
141  settings_.eps_dual_inf = op.second;
142  } else if (op.first=="alpha") {
143  settings_.alpha = op.second;
144  } else if (op.first=="delta") {
145  settings_.delta = op.second;
146  } else if (op.first=="polish" || op.first=="polishing") {
147 #ifdef WITH_OSQP_V1
148  settings_.polishing = op.second;
149 #else
150  settings_.polish = op.second;
151 #endif
152  } else if (op.first=="polish_refine_iter") {
153  settings_.polish_refine_iter = op.second;
154  } else if (op.first=="verbose") {
155  settings_.verbose = op.second;
156  } else if (op.first=="scaled_termination") {
157  settings_.scaled_termination = op.second;
158  } else if (op.first=="check_termination") {
159  settings_.check_termination = op.second;
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.");
163  //} else if (op.first=="time_limit") {
164  // settings_.time_limit = op.second;
165  } else {
166  casadi_error("Not recognised");
167  }
168  }
169  }
170  }
171 
172  rho_initial_ = settings_.rho;
173 
174  nnzHupp_ = H_.nnz_upper();
175  nnzA_ = A_.nnz()+nx_;
176 
177  alloc_w(nnzHupp_+nnzA_, false);
178  alloc_w(2*nx_+2*na_, false);
179  }
180 
181  void OsqpInterface::deps_version_check(const std::string& stage) const {
182 #ifdef WITH_OSQP_V1
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.");
186 #endif
187  }
188 
189  int OsqpInterface::init_mem(void* mem) const {
190  if (Conic::init_mem(mem)) return 1;
191  auto m = static_cast<OsqpMemory*>(mem);
192 
193  // convert H in a upper triangular matrix. This is required by osqp v0.6.0
194  Sparsity H_triu = Sparsity::triu(H_);
195 
196  Sparsity Asp = vertcat(Sparsity::diag(nx_), A_);
197  std::vector<double> dummy(std::max(nx_+na_, std::max(Asp.nnz(), H_.nnz())));
198 
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());
203 
204  csc A;
205  A.m = nx_ + na_;
206  A.n = nx_;
207  A.nz = nnzA_;
208  A.nzmax = A.nz;
209  A.x = get_ptr(dummy);
210  A.i = get_ptr(A_row);
211  A.p = get_ptr(A_colind);
212 
213  csc H;
214  H.m = nx_;
215  H.n = nx_;
216  H.nz = H_triu.nnz_upper();
217  H.nzmax = H_triu.nnz_upper();
218  H.x = get_ptr(dummy);
219  H.i = get_ptr(H_row);
220  H.p = get_ptr(H_colind);
221 
222 #ifdef WITH_OSQP_V1
223  casadiOSQPData data;
224 #else
225  OSQPData data;
226 #endif
227  // Populate data
228  data.n = nx_;
229  data.m = nx_ + na_;
230  // csc_matrix in mem
231  data.P = &H;
232  data.q = get_ptr(dummy);
233  data.A = &A;
234  data.l = get_ptr(dummy);
235  data.u = get_ptr(dummy);
236 
237  // Setup workspace
238 #ifdef WITH_OSQP_V1
239  if (osqp_setup(&m->work, data.P, data.q, data.A,
240  data.l, data.u, data.m, data.n, &settings_)) return 1;
241 #else
242  if (osqp_setup(&m->work, &data, &settings_)) return 1;
243 #endif
244 
245  m->fstats["preprocessing"] = FStats();
246  m->fstats["solver"] = FStats();
247  m->fstats["postprocessing"] = FStats();
248  return 0;
249  }
250 
251  inline const char* return_status_string(casadi_int status) {
252  return "Unknown";
253  }
254 
256  solve(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
257  auto m = static_cast<OsqpMemory*>(mem);
258 
259  // Inputs
260  const double *a=arg[CONIC_A],
261  *h=arg[CONIC_H];
262 
263  // Outputs
264  double *x=res[CONIC_X],
265  *cost=res[CONIC_COST],
266  *lam_a=res[CONIC_LAM_A],
267  *lam_x=res[CONIC_LAM_X];
268 
269  int ret;
270 
271  // Set objective
272  if (arg[CONIC_G]) {
273 #ifdef WITH_OSQP_V1
274  ret = osqp_update_data_vec(m->work, arg[CONIC_G], nullptr, nullptr);
275 #else
276  ret = osqp_update_lin_cost(m->work, arg[CONIC_G]);
277 #endif
278  casadi_assert(ret==0, "Problem in osqp_update_lin_cost");
279  }
280 
281  // Set bounds
282  casadi_copy(arg[CONIC_LBX], nx_, w);
283  casadi_copy(arg[CONIC_LBA], na_, w+nx_);
284  casadi_copy(arg[CONIC_UBX], nx_, w+nx_+na_);
285  casadi_copy(arg[CONIC_UBA], na_, w+2*nx_+na_);
286 
287  casadi_clip_min(w, 2*(nx_+na_), -OSQP_INFTY, 0);
288  casadi_clip_max(w, 2*(nx_+na_), OSQP_INFTY, 0);
289 #ifdef WITH_OSQP_V1
290  ret = osqp_update_data_vec(m->work, nullptr, w, w+nx_+na_);
291 #else
292  ret = osqp_update_bounds(m->work, w, w+nx_+na_);
293 #endif
294  casadi_assert(ret==0, "Problem in osqp_update_bounds");
295 
296  // Project Hessian
297  casadi_tri_project(arg[CONIC_H], H_, w, false);
298 
299  // Get contraint matrix
300  const casadi_int* colind = A_.colind();
301  double* A = w + nnzHupp_;
302  // Get constraint matrix
303  casadi_int offset = 0;
304  // Loop over columns
305  for (casadi_int i=0; i<nx_; ++i) {
306  A[offset] = 1;
307  offset++;
308  casadi_int n = colind[i+1]-colind[i];
309  casadi_copy(a+colind[i], n, A+offset);
310  offset+= n;
311  }
312 
313  // Pass Hessian and constraint matrices
314 #ifdef WITH_OSQP_V1
315  ret = osqp_update_data_mat(m->work, w, nullptr, nnzHupp_, A, nullptr, nnzA_);
316 #else
317  ret = osqp_update_P_A(m->work, w, nullptr, nnzHupp_, A, nullptr, nnzA_);
318 #endif
319  casadi_assert(ret==0, "Problem in osqp_update_P_A");
320 
321 #ifdef WITH_OSQP_V1
322  osqp_cold_start(m->work);
323 #endif
324 
325  osqp_update_rho(m->work, rho_initial_);
326 
327  if (warm_start_primal_) {
328 #ifdef WITH_OSQP_V1
329  ret = osqp_warm_start(m->work, arg[CONIC_X0], nullptr);
330 #else
331  ret = osqp_warm_start_x(m->work, arg[CONIC_X0]);
332 #endif
333  casadi_assert(ret==0, "Problem in osqp_warm_start_x");
334  }
335 
336  if (warm_start_dual_) {
337  casadi_copy(arg[CONIC_LAM_X0], nx_, w);
338  casadi_copy(arg[CONIC_LAM_A0], na_, w+nx_);
339 #ifdef WITH_OSQP_V1
340  ret = osqp_warm_start(m->work, nullptr, w);
341 #else
342  ret = osqp_warm_start_y(m->work, w);
343 #endif
344  casadi_assert(ret==0, "Problem in osqp_warm_start_y");
345  }
346 
347  // Solve Problem
348  ret = osqp_solve(m->work);
349  casadi_assert(ret==0, "Problem in osqp_solve");
350 
351  casadi_copy(m->work->solution->x, nx_, res[CONIC_X]);
352  casadi_copy(m->work->solution->y, nx_, res[CONIC_LAM_X]);
353  casadi_copy(m->work->solution->y+nx_, na_, res[CONIC_LAM_A]);
354  if (res[CONIC_COST]) *res[CONIC_COST] = m->work->info->obj_val;
355 
356  m->d_qp.success = m->work->info->status_val == OSQP_SOLVED;
357  if (m->d_qp.success) {
358  m->d_qp.unified_return_status = SOLVER_RET_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) {
365  m->d_qp.unified_return_status = SOLVER_RET_INFEASIBLE;
366  } else {
367  m->d_qp.unified_return_status = SOLVER_RET_UNKNOWN;
368  }
369 
370  return 0;
371  }
372 
374  g << "osqp_cleanup(" + codegen_mem(g) + ");\n";
375  }
376 
378  Sparsity Asp = vertcat(Sparsity::diag(nx_), A_);
379  casadi_int dummy_size = std::max(nx_+na_, std::max(Asp.nnz(), H_.nnz()));
380 
381 #ifdef WITH_OSQP_V1
382  std::string osqp_csc_type = "OSQPCscMatrix";
383 #else
384  std::string osqp_csc_type = "csc";
385 #endif
386  g.local("A", osqp_csc_type);
387  g.local("dummy[" + str(dummy_size) + "]", "casadi_real");
388  g << g.clear("dummy", dummy_size) << "\n";
389 
390  #ifdef WITH_OSQP_V1
391  std::string osqp_int_type = "OSQPInt";
392  #else
393  std::string osqp_int_type = "c_int";
394  #endif
395 
396  g.constant_copy("A_row", Asp.get_row(), osqp_int_type);
397  g.constant_copy("A_colind", Asp.get_colind(), osqp_int_type);
398 
399  // convert H in a upper triangular matrix. This is required by osqp v0.6.0
400  Sparsity H_triu = Sparsity::triu(H_);
401 
402  g.constant_copy("H_row", H_triu.get_row(), osqp_int_type);
403  g.constant_copy("H_colind", H_triu.get_colind(), osqp_int_type);
404 
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";
413 
414  g.local("H", osqp_csc_type);
415  g << "H.m = " << nx_ << ";\n";
416  g << "H.n = " << nx_ << ";\n";
417  g << "H.nz = " << H_.nnz_upper() << ";\n";
418  g << "H.nzmax = " << H_.nnz_upper() << ";\n";
419  g << "H.x = dummy;\n";
420  g << "H.i = H_row;\n";
421  g << "H.p = H_colind;\n";
422 
423 #ifdef WITH_OSQP_V1
424  g << "struct\n";
425  g << "{\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";
433  g << "} data;\n";
434 #else
435  g.local("data", "OSQPData");
436 #endif
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";
444 
445  g.local("settings", "OSQPSettings");
446  g << "osqp_set_default_settings(&settings);\n";
447  g << "settings.rho = " << g.constant(settings_.rho) << ";\n";
448  g << "settings.sigma = " << g.constant(settings_.sigma) << ";\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
452  << ";\n";
453  g << "settings.adaptive_rho_tolerance = " << g.constant(settings_.adaptive_rho_tolerance)
454  << ";\n";
455  //g << "settings.adaptive_rho_fraction = " << settings_.adaptive_rho_fraction << ";\n";
456  g << "settings.max_iter = " << settings_.max_iter << ";\n";
457  g << "settings.eps_abs = " << g.constant(settings_.eps_abs) << ";\n";
458  g << "settings.eps_rel = " << g.constant(settings_.eps_rel) << ";\n";
459  g << "settings.eps_prim_inf = " << g.constant(settings_.eps_prim_inf) << ";\n";
460  g << "settings.eps_dual_inf = " << g.constant(settings_.eps_dual_inf) << ";\n";
461  g << "settings.alpha = " << g.constant(settings_.alpha) << ";\n";
462  g << "settings.delta = " << g.constant(settings_.delta) << ";\n";
463 #ifdef WITH_OSQP_V1
464  g << "settings.polishing = " << settings_.polishing << ";\n";
465 #else
466  g << "settings.polish = " << settings_.polish << ";\n";
467 #endif
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";
472 #ifdef WITH_OSQP_V1
473  g << "settings.warm_starting = " << settings_.warm_starting << ";\n";
474 #else
475  g << "settings.warm_start = " << settings_.warm_start << ";\n";
476 #endif
477  //g << "settings.time_limit = " << settings_.time_limit << ";\n";
478 #ifdef WITH_OSQP_V1
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";
481 #else
482  g << "return osqp_setup(&" + codegen_mem(g) + ", &data, &settings)!=0;\n";
483 #endif
484  }
485 
487  g.add_include("osqp/osqp.h");
489 
490 #ifdef WITH_OSQP_V1
491  g.local("work", "OSQPSolver", "*");
492 #else
493  g.local("work", "OSQPWorkspace", "*");
494 #endif
495  g.init_local("work", codegen_mem(g));
496 
497  g.comment("Set objective");
498  g.copy_default(g.arg(CONIC_G), nx_, "w", "0", false);
499 #ifdef WITH_OSQP_V1
500  g << "if (osqp_update_data_vec(work, w, 0, 0)) return 1;\n";
501 #else
502  g << "if (osqp_update_lin_cost(work, w)) return 1;\n";
503 #endif
504  g.comment("Set bounds");
505  g.copy_default(g.arg(CONIC_LBX), nx_, "w", "-casadi_inf", false);
506  g.copy_default(g.arg(CONIC_LBA), na_, "w+"+str(nx_), "-casadi_inf", false);
507  g.copy_default(g.arg(CONIC_UBX), nx_, "w+"+str(nx_+na_), "casadi_inf", false);
508  g.copy_default(g.arg(CONIC_UBA), na_, "w+"+str(2*nx_+na_), "casadi_inf", false);
509 
510  g.comment("Convert C++ infinity values to OSQP infinity");
511  g << g.clip_min("w", 2*(nx_+na_), "-OSQP_INFTY", "0") << "\n";
512  g << g.clip_max("w", 2*(nx_+na_), "OSQP_INFTY", "0") << "\n";
513 #ifdef WITH_OSQP_V1
514  g << "if (osqp_update_data_vec(work, 0, w, w+" + str(nx_+na_)+ ")) return 1;\n";
515 #else
516  g << "if (osqp_update_bounds(work, w, w+" + str(nx_+na_)+ ")) return 1;\n";
517 #endif
518  g.comment("Project Hessian");
519  g << g.tri_project(g.arg(CONIC_H), H_, "w", false);
520 
521  g.comment("Get constraint matrix");
522  std::string A_colind = g.constant(A_.get_colind());
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";
528  g << "w[" + str(nnzHupp_) + "+offset] = 1;\n";
529  g << "offset++;\n";
530  g << "n = " + A_colind + "[i+1]-" + A_colind + "[i];\n";
531  g << "casadi_copy(" << g.arg(CONIC_A) << "+" + A_colind + "[i], n, "
532  "w+offset+" + str(nnzHupp_) + ");\n";
533  g << "offset+= n;\n";
534  g << "}\n";
535 
536  g.comment("Pass Hessian and constraint matrices");
537 #ifdef WITH_OSQP_V1
538  g << "if (osqp_update_data_mat(work, w, 0, " + str(nnzHupp_) + ", w+" + str(nnzHupp_) +
539  ", 0, " + str(nnzA_) + ")) return 1;\n";
540 #else
541  g << "if (osqp_update_P_A(work, w, 0, " + str(nnzHupp_) + ", w+" + str(nnzHupp_) +
542  ", 0, " + str(nnzA_) + ")) return 1;\n";
543 #endif
544 
545  #ifdef WITH_OSQP_V1
546  g << "osqp_cold_start(work);\n";
547  #endif
548 
549  g << "osqp_update_rho(work, " << g.constant(rho_initial_) << ");\n";
550 
551  if (warm_start_primal_) {
552  #ifdef WITH_OSQP_V1
553  g << "if (osqp_warm_start(work, " + g.arg(CONIC_X0) + ", OSQP_NULL)) return 1;\n";
554  #else
555  g << "if (osqp_warm_start_x(work, " + g.arg(CONIC_X0) + ")) return 1;\n";
556  #endif
557  }
558 
559  if (warm_start_dual_) {
560  g.copy_default(g.arg(CONIC_LAM_X0), nx_, "w", "0", false);
561  g.copy_default(g.arg(CONIC_LAM_A0), na_, "w+"+str(nx_), "0", false);
562  #ifdef WITH_OSQP_V1
563  g << "if (osqp_warm_start(work, OSQP_NULL, w)) return 1;\n";
564  #else
565  g << "if (osqp_warm_start_y(work, w)) return 1;\n";
566  #endif
567  }
568 
569  g << "if (osqp_solve(work)) return 1;\n";
570 
571  g.copy_check("&work->info->obj_val", 1, g.res(CONIC_COST), false, true);
572  g.copy_check("work->solution->x", nx_, g.res(CONIC_X), false, true);
573  g.copy_check("work->solution->y", nx_, g.res(CONIC_LAM_X), false, true);
574  g.copy_check("work->solution->y+" + str(nx_), na_, g.res(CONIC_LAM_A), false, true);
575 
576  g << "if (work->info->status_val != OSQP_SOLVED) {\n";
577  if (error_on_fail_) {
578  g << "return -1000;\n";
579  } else {
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";
586  g << "return " << SOLVER_RET_INFEASIBLE << ";\n";
587  g << "} else {\n";
588  g << "return " << SOLVER_RET_UNKNOWN << ";\n";
589  g << "}\n";
590  }
591  g << "}\n";
592 
593  g << "return 0;\n";
594  }
595 
596  Dict OsqpInterface::get_stats(void* mem) const {
597  Dict stats = Conic::get_stats(mem);
598  auto m = static_cast<OsqpMemory*>(mem);
599  stats["return_status"] = m->work->info->status;
600  return stats;
601  }
602 
604  // Initialize so a teardown before a successful osqp_setup (e.g. failed
605  // init_mem) does not osqp_cleanup() an uninitialized pointer (heap smash).
606  work = nullptr;
607  }
608 
610  if (work) osqp_cleanup(work);
611  }
612 
614  int version = s.version("OsqpInterface", 1, 2);
615  s.unpack("OsqpInterface::nnzHupp", nnzHupp_);
616  s.unpack("OsqpInterface::nnzA", nnzA_);
617  s.unpack("OsqpInterface::warm_start_primal", warm_start_primal_);
618  s.unpack("OsqpInterface::warm_start_dual", warm_start_dual_);
619 
620  osqp_set_default_settings(&settings_);
621  s.unpack("OsqpInterface::settings::rho", settings_.rho);
622  s.unpack("OsqpInterface::settings::sigma", settings_.sigma);
623  s.unpack("OsqpInterface::settings::scaling", settings_.scaling);
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);
627  //s.unpack("OsqpInterface::settings::adaptive_rho_fraction", settings_.adaptive_rho_fraction);
628  s.unpack("OsqpInterface::settings::max_iter", settings_.max_iter);
629  s.unpack("OsqpInterface::settings::eps_abs", settings_.eps_abs);
630  s.unpack("OsqpInterface::settings::eps_rel", settings_.eps_rel);
631  s.unpack("OsqpInterface::settings::eps_prim_inf", settings_.eps_prim_inf);
632  s.unpack("OsqpInterface::settings::eps_dual_inf", settings_.eps_dual_inf);
633  s.unpack("OsqpInterface::settings::alpha", settings_.alpha);
634  s.unpack("OsqpInterface::settings::delta", settings_.delta);
635 #ifdef WITH_OSQP_V1
636  s.unpack("OsqpInterface::settings::polish", settings_.polishing);
637 #else
638  s.unpack("OsqpInterface::settings::polish", settings_.polish);
639 #endif
640  s.unpack("OsqpInterface::settings::polish_refine_iter", settings_.polish_refine_iter);
641  s.unpack("OsqpInterface::settings::verbose", settings_.verbose);
642  s.unpack("OsqpInterface::settings::scaled_termination", settings_.scaled_termination);
643  s.unpack("OsqpInterface::settings::check_termination", settings_.check_termination);
644 #ifdef WITH_OSQP_V1
645  s.unpack("OsqpInterface::settings::warm_start", settings_.warm_starting);
646 #else
647  s.unpack("OsqpInterface::settings::warm_start", settings_.warm_start);
648 #endif
649  if (version>=2) {
650  s.unpack("OsqpInterface::rho_initial", rho_initial_);
651  } else {
652  rho_initial_ = settings_.rho;
653  }
654  //s.unpack("OsqpInterface::settings::time_limit", settings_.time_limit);
655  }
656 
659  s.version("OsqpInterface", 2);
660  s.pack("OsqpInterface::nnzHupp", nnzHupp_);
661  s.pack("OsqpInterface::nnzA", nnzA_);
662  s.pack("OsqpInterface::warm_start_primal", warm_start_primal_);
663  s.pack("OsqpInterface::warm_start_dual", warm_start_dual_);
664  s.pack("OsqpInterface::settings::rho", settings_.rho);
665  s.pack("OsqpInterface::settings::sigma", settings_.sigma);
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);
670  //s.pack("OsqpInterface::settings::adaptive_rho_fraction", settings_.adaptive_rho_fraction);
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);
676  s.pack("OsqpInterface::settings::alpha", settings_.alpha);
677  s.pack("OsqpInterface::settings::delta", settings_.delta);
678 #ifdef WITH_OSQP_V1
679  s.pack("OsqpInterface::settings::polish", settings_.polishing);
680 #else
681  s.pack("OsqpInterface::settings::polish", settings_.polish);
682 #endif
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);
687 #ifdef WITH_OSQP_V1
688  s.pack("OsqpInterface::settings::warm_start", settings_.warm_starting);
689 #else
690  s.pack("OsqpInterface::settings::warm_start", settings_.warm_start);
691 #endif
692  s.pack("OsqpInterface::rho_initial", rho_initial_);
693  //s.pack("OsqpInterface::settings::time_limit", settings_.time_limit);
694  }
695 
696 } // namespace casadi
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.
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:173
int init_mem(void *mem) const override
Initalize memory block.
Definition: conic.cpp:466
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
Definition: conic_impl.hpp:176
Sparsity H_
Problem structure.
Definition: conic_impl.hpp:170
void init(const Dict &opts) override
Initialize.
Definition: conic.cpp:415
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: conic.cpp:753
Dict get_stats(void *mem) const override
Get all statistics.
Definition: conic.cpp:726
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.
General sparsity class.
Definition: sparsity.hpp:106
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
Definition: sparsity.hpp:190
static Sparsity triu(const Sparsity &x, bool includeDiagonal=true)
Enlarge matrix.
Definition: sparsity.cpp:999
casadi_int nnz_upper(bool strictly=false) const
Number of non-zeros in the upper triangular half,.
Definition: sparsity.cpp:356
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
std::vector< casadi_int > get_colind() const
Get the column index for each column.
Definition: sparsity.cpp:364
std::vector< casadi_int > get_row() const
Get the row for each non-zero entry.
Definition: sparsity.cpp:372
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
The casadi namespace.
Definition: archiver.cpp:28
@ CONIC_UBA
dense, (nc x 1)
Definition: conic.hpp:181
@ CONIC_X0
dense, (n x 1)
Definition: conic.hpp:187
@ 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_LAM_A0
dense
Definition: conic.hpp:191
@ CONIC_LBX
dense, (n x 1)
Definition: conic.hpp:183
@ CONIC_LAM_X0
dense
Definition: conic.hpp:189
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.
@ SOLVER_RET_INFEASIBLE
@ 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
~OsqpMemory()
Destructor.
OSQPWorkspace * work
OsqpMemory()
Constructor.