hpipm_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 #include "hpipm_interface.hpp"
26 #include <numeric>
27 #include <cstring>
28 
29 #include <hpipm_runtime_str.h>
30 
31 namespace casadi {
32 
33  extern "C"
34  int CASADI_CONIC_HPIPM_EXPORT
35  casadi_register_conic_hpipm(Conic::Plugin* plugin) {
36  plugin->creator = HpipmInterface::creator;
37  plugin->name = "hpipm";
38  plugin->doc = HpipmInterface::meta_doc.c_str();
39  plugin->version = CASADI_VERSION;
40  plugin->options = &HpipmInterface::options_;
41  return 0;
42  }
43 
44  extern "C"
45  void CASADI_CONIC_HPIPM_EXPORT casadi_load_conic_hpipm() {
47  }
48 
49  HpipmInterface::HpipmInterface(const std::string& name,
50  const std::map<std::string, Sparsity>& st)
51  : Conic(name, st) {
52  }
53 
55  clear_mem();
56  }
57 
59  = {{&Conic::options_},
60  {{"N",
61  {OT_INT,
62  "OCP horizon"}},
63  {"nx",
64  {OT_INTVECTOR,
65  "Number of states, length N+1"}},
66  {"nu",
67  {OT_INTVECTOR,
68  "Number of controls, length N"}},
69  {"ng",
70  {OT_INTVECTOR,
71  "Number of non-dynamic constraints, length N+1"}},
72  {"inf",
73  {OT_DOUBLE,
74  "Replace infinities by this amount [default: 1e8]"}},
75  {"hpipm",
76  {OT_DICT,
77  "Options to be passed to hpipm"}}}
78  };
79 
80  void HpipmInterface::init(const Dict& opts) {
81  Conic::init(opts);
82 
83  hpipm_mode mode = ROBUST;
84  casadi_int struct_cnt=0;
85 
86  d_ocp_qp_ipm_arg_set_default(mode, &hpipm_options_);
87 
88  inf_ = 1e8;
89 
90  // Read options
91  for (auto&& op : opts) {
92  if (op.first=="N") {
93  N_ = op.second;
94  struct_cnt++;
95  } else if (op.first=="nx") {
96  nxs_ = op.second;
97  struct_cnt++;
98  } else if (op.first=="nu") {
99  nus_ = op.second;
100  struct_cnt++;
101  } else if (op.first=="ng") {
102  ngs_ = op.second;
103  struct_cnt++;
104  } else if (op.first=="inf") {
105  inf_ = op.second;
106  } else if (op.first=="hpipm") {
107  Dict hopts = op.second;
108  auto it = hopts.find("mode");
109  if (it!=hopts.end()) {
110  if (it->second=="speed_abs") {
111  mode = SPEED_ABS;
112  } else if (it->second=="speed") {
113  mode = SPEED;
114  } else if (it->second=="balance") {
115  mode = BALANCE;
116  } else if (it->second=="robust") {
117  mode = ROBUST;
118  } else {
119  casadi_error("Unknown mode. Choose from speed_abs, speedm balance, robust.");
120  }
121  }
122 
123  d_ocp_qp_ipm_arg_set_default(mode, &hpipm_options_);
124 
125  for (auto&& op : hopts) {
126  if (op.first=="mu0") {
127  hpipm_options_.mu0 = op.second;
128  } else if (op.first=="alpha_min") {
129  hpipm_options_.alpha_min = op.second;
130  } else if (op.first=="res_g_max") {
131  hpipm_options_.res_g_max = op.second;
132  } else if (op.first=="res_b_max") {
133  hpipm_options_.res_b_max = op.second;
134  } else if (op.first=="res_d_max") {
135  hpipm_options_.res_d_max = op.second;
136  } else if (op.first=="res_m_max") {
137  hpipm_options_.res_m_max = op.second;
138  } else if (op.first=="iter_max") {
139  hpipm_options_.iter_max = op.second;
140  } else if (op.first=="stat_max") {
141  hpipm_options_.stat_max = op.second;
142  } else if (op.first=="pred_corr") {
143  hpipm_options_.pred_corr = op.second;
144  } else if (op.first=="cond_pred_corr") {
145  hpipm_options_.cond_pred_corr = op.second;
146  } else if (op.first=="itref_pred_max") {
147  hpipm_options_.itref_pred_max = op.second;
148  } else if (op.first=="itref_corr_max") {
149  hpipm_options_.itref_corr_max = op.second;
150  } else if (op.first=="reg_prim") {
151  hpipm_options_.reg_prim = op.second;
152  } else if (op.first=="lq_fact") {
153  hpipm_options_.lq_fact = op.second;
154  } else if (op.first=="lam_min") {
155  hpipm_options_.lam_min = op.second;
156  } else if (op.first=="t_min") {
157  hpipm_options_.t_min = op.second;
158  } else if (op.first=="warm_start") {
159  hpipm_options_.warm_start = op.second;
160  } else if (op.first=="abs_form") {
161  hpipm_options_.abs_form = op.second;
162  } else if (op.first=="comp_dual_sol_eq") {
163  hpipm_options_.comp_dual_sol_eq = op.second;
164  } else if (op.first=="comp_res_exit") {
165  hpipm_options_.comp_res_exit = op.second;
166  } else if (op.first=="mode") {
167  // already covered
168  } else {
169  casadi_error("Unknown option.");
170  }
171  }
172  }
173  }
174 
175  bool detect_structure = struct_cnt==0;
176  casadi_assert(struct_cnt==0 || struct_cnt==4,
177  "You must either set all of N, nx, nu, ng; "
178  "or set none at all (automatic detection).");
179 
180  const std::vector<int>& nx = nxs_;
181  const std::vector<int>& ng = ngs_;
182  const std::vector<int>& nu = nus_;
183 
184  Sparsity lamg_csp_, lam_ulsp_, lam_uusp_, lam_xlsp_, lam_xusp_, lam_clsp_;
185 
186  if (detect_structure) {
187  /* General strategy: look for the xk+1 diagonal part in A
188  */
189 
190  // Find the right-most column for each row in A -> A_skyline
191  // Find the second-to-right-most column -> A_skyline2
192  // Find the left-most column -> A_bottomline
193  Sparsity AT = A_.T();
194  std::vector<casadi_int> A_skyline;
195  std::vector<casadi_int> A_skyline2;
196  std::vector<casadi_int> A_bottomline;
197 
198  std::vector<casadi_int> AT_colind = AT.get_colind();
199  std::vector<casadi_int> AT_row = AT.get_row();
200  for (casadi_int i=0;i<AT.size2();++i) {
201  casadi_int pivot = AT.colind()[i+1];
202  if (pivot>AT_colind.at(i)) {
203  A_bottomline.push_back(AT_row.at(AT_colind.at(i)));
204  } else {
205  A_bottomline.push_back(-1);
206  }
207  if (pivot>AT.colind()[i]) {
208  A_skyline.push_back(AT.row()[pivot-1]);
209  if (pivot>AT.colind()[i]+1) {
210  A_skyline2.push_back(AT.row()[pivot-2]);
211  } else {
212  A_skyline2.push_back(-1);
213  }
214  } else {
215  A_skyline.push_back(-1);
216  A_skyline2.push_back(-1);
217  }
218  }
219 
220  /*
221  Loop over the right-most columns of A:
222  they form the diagonal part due to xk+1 in gap constraints.
223  detect when the diagonal pattern is broken -> new stage
224  */
225  casadi_int pivot = 0; // Current right-most element
226  casadi_int start_pivot = pivot; // First right-most element that started the stage
227  casadi_int cg = 0; // Counter for non-gap-closing constraints
228  for (casadi_int i=0;i<na_;++i) { // Loop over all rows
229  bool commit = false; // Set true to jump to the stage
230  if (A_skyline[i]>pivot+1) { // Jump to a diagonal in the future
231  nus_.push_back(A_skyline[i]-pivot-1); // Size of jump equals number of states
232  commit = true;
233  } else if (A_skyline[i]==pivot+1) { // Walking the diagonal
234  if (A_skyline2[i]<start_pivot) { // Free of below-diagonal entries?
235  pivot++;
236  } else {
237  nus_.push_back(0); // We cannot but conclude that we arrived at a new stage
238  commit = true;
239  }
240  } else { // non-gap-closing constraint detected
241  cg++;
242  }
243 
244  if (commit) {
245  nxs_.push_back(pivot-start_pivot+1);
246  ngs_.push_back(cg); cg=0;
247  start_pivot = A_skyline[i];
248  pivot = A_skyline[i];
249  }
250  }
251  nxs_.push_back(pivot-start_pivot+1);
252 
253  // Correction for k==0
254  nxs_[0] = A_skyline[0];
255  nus_[0] = 0;
256  ngs_.erase(ngs_.begin());
257  casadi_int cN=0;
258  for (casadi_int i=na_-1;i>=0;--i) {
259  if (A_bottomline[i]<start_pivot) break;
260  cN++;
261  }
262  ngs_.push_back(cg-cN);
263  ngs_.push_back(cN);
264 
265  N_ = nus_.size();
266  if (N_>1) {
267  if (nus_[0]==0 && nxs_[1]+nus_[1]==nxs_[0]) {
268  nxs_[0] = nxs_[1];
269  nus_[0] = nus_[1];
270  }
271  }
272  nus_.push_back(0);
273  }
274  if (verbose_) {
275  casadi_message("Using structure: N " + str(N_) + ", nx " + str(nx) + ", "
276  "nu " + str(nu) + ", ng " + str(ng) + ".");
277  }
278 
279  casadi_assert_dev(nx.size()==N_+1);
280  casadi_assert_dev(nu.size()==N_+1);
281  casadi_assert_dev(ng.size()==N_+1);
282 
283  casadi_assert(nx_ == std::accumulate(nx.begin(), nx.end(), 0) + // NOLINT
284  std::accumulate(nu.begin(), nu.end(), 0),
285  "sum(nx)+sum(nu) = must equal total size of variables (" + str(nx_) + "). "
286  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", "
287  "nu " + str(nu) + ", ng " + str(ng) + ".");
288  casadi_assert(na_ == std::accumulate(nx.begin()+1, nx.end(), 0) + // NOLINT
289  std::accumulate(ng.begin(), ng.end(), 0),
290  "sum(nx+1)+sum(ng) = must equal total size of constraints (" + str(na_) + "). "
291  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", "
292  "nu " + str(nu) + ", ng " + str(ng) + ".");
293  // Load library HPIPM when applicable
294  std::string searchpath;
295 
296  /* Disassemble A input into:
297  A B I
298  C D
299  A B I
300  C D
301  */
302  casadi_int offset_r = 0, offset_c = 0;
303  for (casadi_int k=0;k<N_;++k) { // Loop over blocks
304  A_blocks.push_back({offset_r, offset_c, nx[k+1], nx[k]});
305  B_blocks.push_back({offset_r, offset_c+nx[k], nx[k+1], nu[k]});
306  C_blocks.push_back({offset_r+nx[k+1], offset_c, ng[k], nx[k]});
307  D_blocks.push_back({offset_r+nx[k+1], offset_c+nx[k], ng[k], nu[k]});
308 
309  offset_c+= nx[k]+nu[k];
310  if (k+1<N_)
311  I_blocks.push_back({offset_r, offset_c, nx[k+1], nx[k+1]});
312  else
313  I_blocks.push_back({offset_r, offset_c, nx[k+1], nx[k+1]});
314  offset_r+= nx[k+1]+ng[k];
315  }
316 
317  C_blocks.push_back({offset_r, offset_c, ng[N_], nx[N_]});
318  D_blocks.push_back({offset_r, offset_c+nx[N_], ng[N_], nu[N_]});
319 
324  Isp_ = blocksparsity(na_, nx_, I_blocks, true);
325 
326  Sparsity total = Asp_ + Bsp_ + Csp_ + Dsp_ + Isp_;
327 
328  casadi_assert((A_ + total).nnz() == total.nnz(),
329  "HPIPM: specified structure of A does not correspond to what the interface can handle. "
330  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", nu " + str(nu) + ", "
331  "ng " + str(ng) + ".");
332  casadi_assert_dev(total.nnz() == Asp_.nnz() + Bsp_.nnz() + Csp_.nnz() + Dsp_.nnz()
333  + Isp_.nnz());
334 
335  /* Disassemble H input into:
336  Q S'
337  S R
338  Q S'
339  S R
340 
341  Multiply by 2
342  */
343  casadi_int offset = 0;
344  for (casadi_int k=0;k<N_+1;++k) { // Loop over blocks
345  R_blocks.push_back({offset+nx[k], offset+nx[k], nu[k], nu[k]});
346  S_blocks.push_back({offset+nx[k], offset, nu[k], nx[k]});
347  Q_blocks.push_back({offset, offset, nx[k], nx[k]});
348  offset+= nx[k]+nu[k];
349  }
350 
354 
355  total = Rsp_ + Ssp_ + Qsp_ + Ssp_.T();
356  casadi_assert((H_ + total).nnz() == total.nnz(),
357  "HPIPM: specified structure of H does not correspond to what the interface can handle. "
358  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", nu " + str(nu) + ", "
359  "ng " + str(ng) + ".");
360  casadi_assert_dev(total.nnz() == Rsp_.nnz() + 2*Ssp_.nnz() + Qsp_.nnz());
361 
362  /* Disassemble LBA/UBA input into:
363  b
364  lg/ug
365 
366  b
367  lg/ug
368  */
369  offset = 0;
370 
371  for (casadi_int k=0;k<N_;++k) {
372  b_blocks.push_back({offset, 0, nx[k+1], 1}); offset+= nx[k+1];
373  lug_blocks.push_back({offset, 0, ng[k], 1}); offset+= ng[k];
374  }
375  lug_blocks.push_back({offset, 0, ng[N_], 1});
376 
379  total = bsp_ + lugsp_;
380  casadi_assert_dev(total.nnz() == bsp_.nnz() + lugsp_.nnz());
381  casadi_assert_dev(total.nnz() == na_);
382 
383  /* Disassemble G/X0 input into:
384  r/u
385  q/x
386 
387  r/u
388  q/x
389  */
390  offset = 0;
391 
392  for (casadi_int k=0;k<N_+1;++k) {
393  x_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
394  u_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
395  }
396 
399  total = usp_ + xsp_;
400  casadi_assert_dev(total.nnz() == usp_.nnz() + xsp_.nnz());
401  casadi_assert_dev(total.nnz() == nx_);
402 
403  std::vector< casadi_hpipm_block > theirs_u_blocks, theirs_x_blocks;
404  offset = 0;
405 
406  for (casadi_int k=0;k<N_;++k) {
407  theirs_u_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
408  theirs_x_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
409  }
410  theirs_x_blocks.push_back({offset, 0, nx[N_], 1});
411 
412  theirs_usp_ = blocksparsity(nx_, 1, theirs_u_blocks);
413  theirs_xsp_ = blocksparsity(nx_, 1, theirs_x_blocks);
414  total = theirs_usp_ + theirs_xsp_;
415  casadi_assert_dev(total.nnz() == theirs_usp_.nnz() + theirs_xsp_.nnz());
416  casadi_assert_dev(total.nnz() == nx_);
417 
418  offset = 0;
419  std::vector< casadi_hpipm_block > lamg_gap_blocks;
420  for (casadi_int k=0;k<N_;++k) {
421  lamg_gap_blocks.push_back({offset, 0, nx[k+1], 1});offset+= nx[k+1] + ng[k];
422  }
423  lamg_gapsp_ = blocksparsity(na_, 1, lamg_gap_blocks);
424  lamg_csp_ = lamg_gapsp_.pattern_inverse();
425 
426  offset = 0;
427 
428  for (casadi_int k=0;k<N_;++k) {
429  lam_ul_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
430  lam_xl_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
431  lam_uu_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
432  lam_xu_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
433  lam_cl_blocks.push_back({offset, 0, ng[k], 1}); offset+= ng[k];
434  lam_cu_blocks.push_back({offset, 0, ng[k], 1}); offset+= ng[k];
435  }
436  lam_xl_blocks.push_back({offset, 0, nx[N_], 1}); offset+= nx[N_];
437  lam_xu_blocks.push_back({offset, 0, nx[N_], 1}); offset+= nx[N_];
438  lam_cl_blocks.push_back({offset, 0, ng[N_], 1}); offset+= ng[N_];
439  lam_cu_blocks.push_back({offset, 0, ng[N_], 1}); offset+= ng[N_];
440 
441  lam_ulsp_ = blocksparsity(offset, 1, lam_ul_blocks);
442  lam_uusp_ = blocksparsity(offset, 1, lam_uu_blocks);
443  lam_xlsp_ = blocksparsity(offset, 1, lam_xl_blocks);
444  lam_xusp_ = blocksparsity(offset, 1, lam_xu_blocks);
445  lam_clsp_ = blocksparsity(offset, 1, lam_cl_blocks);
446  lam_cusp_ = blocksparsity(offset, 1, lam_cu_blocks);
447 
448  pisp_ = Sparsity::dense(std::accumulate(nx.begin()+1, nx.end(), 0), 1); // NOLINT
449 
450  total = lam_ulsp_ + lam_uusp_ + lam_xlsp_ + lam_xusp_ + lam_clsp_ + lam_cusp_;
451  casadi_assert_dev(total.nnz() == lam_ulsp_.nnz() + lam_uusp_.nnz() + lam_xlsp_.nnz() +
452  lam_xusp_.nnz() + lam_clsp_.nnz() + lam_cusp_.nnz());
453  casadi_assert_dev(total.nnz() == offset);
454 
455  theirs_Xsp_ = Sparsity::dense(std::accumulate(nx.begin(), nx.end(), 0), 1); // NOLINT
456  theirs_Usp_ = Sparsity::dense(std::accumulate(nu.begin(), nu.end(), 0), 1); // NOLINT
457 
458  nus_.push_back(0);
459  zeros_.resize(N_+1, 0);
460 
461  set_hpipm_prob();
462 
463  // Allocate memory
464  casadi_int sz_arg, sz_res, sz_w, sz_iw;
465  casadi_hpipm_work(&p_, &sz_arg, &sz_res, &sz_iw, &sz_w);
466 
467  alloc_arg(sz_arg, true);
468  alloc_res(sz_res, true);
469  alloc_iw(sz_iw, true);
470  alloc_w(sz_w, true);
471 
472 
473 
474  }
475 
476  std::vector<casadi_int> hpipm_blocks_pack(const std::vector<casadi_hpipm_block>& blocks) {
477  size_t N = blocks.size();
478  std::vector<casadi_int> ret(4*N);
479  casadi_int* r = get_ptr(ret);
480  for (casadi_int i=0;i<N;++i) {
481  *r++ = blocks[i].offset_r;
482  *r++ = blocks[i].offset_c;
483  *r++ = blocks[i].rows;
484  *r++ = blocks[i].cols;
485  }
486  return ret;
487  }
488 
489 
490  void codegen_unpack_block(CodeGenerator& g, const std::string& name,
491  const std::vector<casadi_hpipm_block>& blocks) {
492  std::string n = "block_" + name + "[" + str(blocks.size()) + "]";
493  g.local(n, "static struct casadi_hpipm_block");
494  g << "p." << name << " = block_" + name + ";\n";
495  g << "casadi_hpipm_unpack_blocks(" << blocks.size()
496  << ", p." << name
497  << ", " << g.constant(hpipm_blocks_pack(blocks)) << ");\n";
498  }
499 
500  void codegen_local(CodeGenerator& g, const std::string& name, const std::vector<int>& v) {
501  std::string n = name + "[]";
502  g.local(n, "static const int");
503  std::stringstream init;
504  init << "{";
505  for (casadi_int i=0;i<v.size();++i) {
506  init << v[i];
507  if (i<v.size()-1) init << ", ";
508  }
509  init << "}";
510  g.init_local(n, init.str());
511  }
512 
514  g << "p.qp = &p_qp;\n";
515  codegen_local(g, "nx", nxs_);
516  codegen_local(g, "nu", nus_);
517  codegen_local(g, "ng", ngs_);
518  codegen_local(g, "zeros", zeros_);
519  g << "p.nx = nx;\n";
520  g << "p.nu = nu;\n";
521  g << "p.ng = ng;\n";
522 
523  g << "p.nbx = nx;\n";
524  g << "p.nbu = nu;\n";
525  g << "p.ns = zeros;\n";
526  g << "p.nsbx = zeros;\n";
527  g << "p.nsbu = zeros;\n";
528  g << "p.nsg = zeros;\n";
529 
530  g << "p.sp_x = " << g.sparsity(sparsity_in(CONIC_X0)) << ";\n";
531  g << "p.sp_ba = " << g.sparsity(sparsity_in(CONIC_LBA)) << ";\n";
532 
533  g << "p.Asp = " << g.sparsity(Asp_) << ";\n";
534  g << "p.Bsp = " << g.sparsity(Bsp_) << ";\n";
535  g << "p.Csp = " << g.sparsity(Csp_) << ";\n";
536  g << "p.Dsp = " << g.sparsity(Dsp_) << ";\n";
537 
538  g << "p.Rsp = " << g.sparsity(Rsp_) << ";\n";
539  g << "p.Isp = " << g.sparsity(Isp_) << ";\n";
540  g << "p.Ssp = " << g.sparsity(Ssp_) << ";\n";
541  g << "p.Qsp = " << g.sparsity(Qsp_) << ";\n";
542 
543  g << "p.bsp = " << g.sparsity(bsp_) << ";\n";
544  g << "p.xsp = " << g.sparsity(xsp_) << ";\n";
545  g << "p.usp = " << g.sparsity(usp_) << ";\n";
546 
547  g << "p.pisp = " << g.sparsity(pisp_) << ";\n";
548 
549  g << "p.theirs_xsp = " << g.sparsity(theirs_xsp_) << ";\n";
550  g << "p.theirs_usp = " << g.sparsity(theirs_usp_) << ";\n";
551  g << "p.theirs_Xsp = " << g.sparsity(theirs_Xsp_) << ";\n";
552  g << "p.theirs_Usp = " << g.sparsity(theirs_Usp_) << ";\n";
553 
554  g << "p.lamg_gapsp = " << g.sparsity(lamg_gapsp_) << ";\n";
555  g << "p.lugsp = " << g.sparsity(lugsp_) << ";\n";
556 
557  g << "p.N = " << N_ << ";\n";
558 
559  g << "p.hpipm_options.mu0 = " << hpipm_options_.mu0 << ";\n";
560  g << "p.hpipm_options.alpha_min = " << hpipm_options_.alpha_min << ";\n";
561  g << "p.hpipm_options.res_g_max = " << hpipm_options_.res_g_max << ";\n";
562  g << "p.hpipm_options.res_b_max = " << hpipm_options_.res_b_max << ";\n";
563  g << "p.hpipm_options.res_d_max = " << hpipm_options_.res_d_max << ";\n";
564  g << "p.hpipm_options.res_m_max = " << hpipm_options_.res_m_max << ";\n";
565  g << "p.hpipm_options.reg_prim = " << hpipm_options_.reg_prim << ";\n";
566  g << "p.hpipm_options.lam_min = " << hpipm_options_.lam_min << ";\n";
567  g << "p.hpipm_options.t_min = " << hpipm_options_.t_min << ";\n";
568  g << "p.hpipm_options.tau_min = " << hpipm_options_.tau_min << ";\n";
569  g << "p.hpipm_options.iter_max = " << hpipm_options_.iter_max << ";\n";
570  g << "p.hpipm_options.stat_max = " << hpipm_options_.stat_max << ";\n";
571  g << "p.hpipm_options.pred_corr = " << hpipm_options_.pred_corr << ";\n";
572  g << "p.hpipm_options.cond_pred_corr = " << hpipm_options_.cond_pred_corr << ";\n";
573  g << "p.hpipm_options.itref_pred_max = " << hpipm_options_.itref_pred_max << ";\n";
574  g << "p.hpipm_options.itref_corr_max = " << hpipm_options_.itref_corr_max << ";\n";
575  g << "p.hpipm_options.warm_start = " << hpipm_options_.warm_start << ";\n";
576  g << "p.hpipm_options.square_root_alg = " << hpipm_options_.square_root_alg << ";\n";
577  g << "p.hpipm_options.lq_fact = " << hpipm_options_.lq_fact << ";\n";
578  g << "p.hpipm_options.abs_form = " << hpipm_options_.abs_form << ";\n";
579  g << "p.hpipm_options.comp_dual_sol_eq = " << hpipm_options_.comp_dual_sol_eq << ";\n";
580  g << "p.hpipm_options.comp_res_exit = " << hpipm_options_.comp_res_exit << ";\n";
581  g << "p.hpipm_options.comp_res_pred = " << hpipm_options_.comp_res_pred << ";\n";
582  g << "p.hpipm_options.split_step = " << hpipm_options_.split_step << ";\n";
583  g << "p.hpipm_options.var_init_scheme = " << hpipm_options_.var_init_scheme << ";\n";
584  g << "p.hpipm_options.t_lam_min = " << hpipm_options_.t_lam_min << ";\n";
585  g << "p.hpipm_options.mode = " << hpipm_options_.mode << ";\n";
586  g << "p.hpipm_options.memsize = 0;\n";
587 
588 
589  g << "p.inf = " << inf_ << ";\n";
590 
595 
600 
602  codegen_unpack_block(g, "lug", lug_blocks);
605 
606 
607  codegen_unpack_block(g, "lam_ul", lam_ul_blocks);
608  codegen_unpack_block(g, "lam_xl", lam_xl_blocks);
609  codegen_unpack_block(g, "lam_uu", lam_uu_blocks);
610  codegen_unpack_block(g, "lam_xu", lam_xu_blocks);
611  codegen_unpack_block(g, "lam_cl", lam_cl_blocks);
612  codegen_unpack_block(g, "lam_cu", lam_cu_blocks);
613 
614  g << "casadi_hpipm_setup(&p);\n";
615 
616  }
617 
619  p_.qp = &p_qp_;
620  p_.nx = get_ptr(nxs_);
621  p_.nu = get_ptr(nus_);
622  p_.ng = get_ptr(ngs_);
623 
624  p_.nbx = get_ptr(nxs_);
625  p_.nbu = get_ptr(nus_);
626  p_.ns = get_ptr(zeros_);
627  p_.nsbx = get_ptr(zeros_);
628  p_.nsbu = get_ptr(zeros_);
629  p_.nsg = get_ptr(zeros_);
630 
633 
634  p_.Asp = Asp_;
635  p_.Bsp = Bsp_;
636  p_.Csp = Csp_;
637  p_.Dsp = Dsp_;
638 
639  p_.Rsp = Rsp_;
640  p_.Isp = Isp_;
641  p_.Ssp = Ssp_;
642  p_.Qsp = Qsp_;
643 
644  p_.bsp = bsp_;
645 
646  p_.xsp = xsp_;
647  p_.usp = usp_;
648 
649  p_.pisp = pisp_;
650 
655 
657  p_.lugsp = lugsp_;
658 
659  p_.N = N_;
661 
662  p_.inf = inf_;
663 
664  p_.A = get_ptr(A_blocks);
665  p_.B = get_ptr(B_blocks);
666  p_.C = get_ptr(C_blocks);
667  p_.D = get_ptr(D_blocks);
668 
669  p_.R = get_ptr(R_blocks);
670  p_.I = get_ptr(I_blocks);
671  p_.S = get_ptr(S_blocks);
672  p_.Q = get_ptr(Q_blocks);
673 
674  p_.b = get_ptr(b_blocks);
676  p_.u = get_ptr(u_blocks);
677  p_.x = get_ptr(x_blocks);
678 
685 
686  casadi_hpipm_setup(&p_);
687  }
688 
689  int HpipmInterface::init_mem(void* mem) const {
690  if (Conic::init_mem(mem)) return 1;
691  auto m = static_cast<HpipmMemory*>(mem);
692 
693  m->add_stat("preprocessing");
694  m->add_stat("solver");
695  m->add_stat("postprocessing");
696  return 0;
697  }
698 
700  void HpipmInterface::set_work(void* mem, const double**& arg, double**& res,
701  casadi_int*& iw, double*& w) const {
702 
703  auto m = static_cast<HpipmMemory*>(mem);
704 
705  Conic::set_work(mem, arg, res, iw, w);
706 
707  m->d.prob = &p_;
708  m->d.qp = &m->d_qp;
709  casadi_hpipm_set_work(&m->d, &arg, &res, &iw, &w);
710  }
711 
713  solve(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
714  auto m = static_cast<HpipmMemory*>(mem);
715 
716  // Statistics
717  m->fstats.at("solver").tic();
718 
719  casadi_hpipm_solve(&m->d, arg, res, iw, w);
720 
721  m->fstats.at("solver").toc();
722 
723  m->d_qp.success = m->d.return_status==0;
724 
725  if (verbose_) {
726  uout() << "HPIPM finished after " << m->d.iter_count << " iterations." << std::endl;
727  uout() << "return status: " << m->d.return_status << std::endl;
728  uout() << "HPIPM residuals: " << m->d.res_stat << ", " <<
729  m->d.res_eq << ", " << m->d.res_ineq << ", " <<
730  m->d.res_comp << std::endl;
731  }
732 
733  return 0;
734  }
735 
736  Dict HpipmInterface::get_stats(void* mem) const {
737  Dict stats = Conic::get_stats(mem);
738  auto m = static_cast<HpipmMemory*>(mem);
739 
740  stats["return_status"] = m->d.return_status;
741  stats["iter_count"] = m->d.iter_count;
742  return stats;
743  }
744 
746  }
747 
749  }
750 
751  Sparsity HpipmInterface::blocksparsity(casadi_int rows, casadi_int cols,
752  const std::vector<casadi_hpipm_block>& blocks, bool eye) {
753  DM r(rows, cols);
754  for (auto && b : blocks) {
755  if (eye) {
756  r(range(b.offset_r, b.offset_r+b.rows),
757  range(b.offset_c, b.offset_c+b.cols)) = DM::eye(b.rows);
758  casadi_assert_dev(b.rows==b.cols);
759  } else {
760  r(range(b.offset_r, b.offset_r+b.rows),
761  range(b.offset_c, b.offset_c+b.cols)) = DM::zeros(b.rows, b.cols);
762  }
763  }
764  return r.sparsity();
765  }
766  void HpipmInterface::blockptr(std::vector<double *>& vs, std::vector<double>& v,
767  const std::vector<casadi_hpipm_block>& blocks, bool eye) {
768  casadi_int N = blocks.size();
769  vs.resize(N);
770  casadi_int offset=0;
771  for (casadi_int k=0;k<N;++k) {
772  vs[k] = get_ptr(v)+offset;
773  if (eye) {
774  casadi_assert_dev(blocks[k].rows==blocks[k].cols);
775  offset+=blocks[k].rows;
776  } else {
777  offset+=blocks[k].rows*blocks[k].cols;
778  }
779  }
780  }
781 
782 
784  qp_codegen_body(g);
796  g.add_include("blasfeo_d_aux_ext_dep.h");
797  g.add_include("hpipm_d_ocp_qp_ipm.h");
798  g.add_include("hpipm_d_ocp_qp_dim.h");
799  g.add_include("hpipm_d_ocp_qp.h");
800  g.add_include("hpipm_d_ocp_qp_sol.h");
801  g.add_include("hpipm_d_ocp_qp_utils.h");
802  g.add_include("stdlib.h");
803  g.add_include("string.h");
804 
805  g.auxiliaries << g.sanitize_source(hpipm_runtime_str, {"casadi_real"});
806 
807 
808  g.local("d", "struct casadi_hpipm_data");
809  g.local("p", "struct casadi_hpipm_prob");
810 
811  set_hpipm_prob(g);
812 
813  // Setup data structure (corresponds to set_work)
814  g << "d.prob = &p;\n";
815  g << "d.qp = &d_qp;\n";
816  g << "casadi_hpipm_set_work(&d, &arg, &res, &iw, &w);\n";
817 
818  g << "casadi_hpipm_solve(&d, arg, res, iw, w);\n";
819 
820  g << "if (d.return_status!=0) {\n";
821  if (error_on_fail_) {
822  g << "return -1000;\n";
823  } else {
824  g << "return -1;\n";
825  }
826  g << "}\n";
827  g << "return 0;\n";
828  }
829 
831  s.version("HpipmInterface", 1);
832  }
833 
836 
837  s.version("HpipmInterface", 1);
838  }
839 
840 } // namespace casadi
Helper class for C code generation.
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 sanitize_source(const std::string &src, const std::vector< std::string > &inst, bool add_shorthand=true)
Sanitize source files for codegen.
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 <....
std::string sparsity(const Sparsity &sp, bool canonical=true)
std::stringstream auxiliaries
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: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
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: conic.cpp:458
Dict get_stats(void *mem) const override
Get all statistics.
Definition: conic.cpp:711
casadi_qp_prob< double > p_qp_
Definition: conic_impl.hpp:47
void qp_codegen_body(CodeGenerator &g) const
Generate code for the function body.
Definition: conic.cpp:789
Helper class for Serialization.
void version(const std::string &name, int v)
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
void alloc_res(size_t sz_res, bool persistent=false)
Ensure required length of res field.
const Sparsity & sparsity_in(casadi_int ind) const
Input/output sparsity.
void alloc_arg(size_t sz_arg, bool persistent=false)
Ensure required length of arg field.
size_t sz_res() const
Get required length of res field.
size_t sz_w() const
Get required length of w field.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
size_t sz_arg() const
Get required length of arg field.
size_t sz_iw() const
Get required length of iw field.
static MatType zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
static Sparsity blocksparsity(casadi_int rows, casadi_int cols, const std::vector< casadi_hpipm_block > &blocks, bool eye=false)
std::vector< casadi_hpipm_block > D_blocks
Dict get_stats(void *mem) const override
Get all statistics.
HpipmInterface()
Constructor.
std::vector< casadi_hpipm_block > lam_cl_blocks
std::vector< casadi_hpipm_block > C_blocks
static void blockptr(std::vector< double * > &vs, std::vector< double > &v, const std::vector< casadi_hpipm_block > &blocks, bool eye=false)
void codegen_body(CodeGenerator &g) const override
Generate code for the function body.
static const std::string meta_doc
A documentation string.
std::vector< casadi_hpipm_block > I_blocks
std::vector< int > nxs_
std::vector< int > ngs_
std::vector< casadi_hpipm_block > lam_uu_blocks
~HpipmInterface() override
Destructor.
std::vector< int > nus_
std::vector< casadi_hpipm_block > u_blocks
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
std::vector< casadi_hpipm_block > Q_blocks
std::vector< casadi_hpipm_block > lam_ul_blocks
static const Options options_
Options.
std::vector< casadi_hpipm_block > lam_cu_blocks
casadi_hpipm_prob< double > p_
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically.
std::vector< casadi_hpipm_block > lug_blocks
std::vector< casadi_hpipm_block > B_blocks
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
std::vector< casadi_hpipm_block > b_blocks
std::vector< int > zeros_
std::vector< casadi_hpipm_block > lam_xu_blocks
std::vector< casadi_hpipm_block > x_blocks
std::vector< casadi_hpipm_block > lam_xl_blocks
std::vector< casadi_hpipm_block > S_blocks
std::vector< casadi_hpipm_block > R_blocks
int init_mem(void *mem) const override
Initalize memory block.
d_ocp_qp_ipm_arg hpipm_options_
void init(const Dict &opts) override
Initialize.
std::vector< casadi_hpipm_block > A_blocks
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
static Matrix< double > eye(casadi_int n)
create an n-by-n identity matrix
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?
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
General sparsity class.
Definition: sparsity.hpp:106
Sparsity pattern_inverse() const
Take the inverse of a sparsity pattern; flip zeros and non-zeros.
Definition: sparsity.cpp:466
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern *.
Definition: sparsity.cpp:1012
Sparsity T() const
Transpose the matrix.
Definition: sparsity.cpp:394
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
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
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
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
void CASADI_CONIC_HPIPM_EXPORT casadi_load_conic_hpipm()
@ CONIC_X0
dense, (n x 1)
Definition: conic.hpp:187
@ CONIC_LBA
dense, (nc x 1)
Definition: conic.hpp:179
@ OT_INTVECTOR
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.
std::vector< casadi_int > hpipm_blocks_pack(const std::vector< casadi_hpipm_block > &blocks)
int CASADI_CONIC_HPIPM_EXPORT casadi_register_conic_hpipm(Conic::Plugin *plugin)
void codegen_local(CodeGenerator &g, const std::string &name, const std::vector< int > &v)
void codegen_unpack_block(CodeGenerator &g, const std::string &name, const std::vector< casadi_ocp_block > &blocks)
std::ostream & uout()
casadi_hpipm_data< double > d
HpipmMemory()
Constructor.
Options metadata for a class.
Definition: options.hpp:40
std::map< std::string, FStats > fstats
void add_stat(const std::string &s)
const casadi_int * Csp
const casadi_int * theirs_Usp
casadi_hpipm_block * lam_xu
casadi_hpipm_block * lam_ul
casadi_hpipm_block * lam_uu
const casadi_int * pisp
casadi_hpipm_block * C
const casadi_int * lugsp
const casadi_int * usp
casadi_hpipm_block * S
casadi_hpipm_block * Q
casadi_hpipm_block * I
const casadi_int * Asp
const casadi_int * theirs_xsp
const casadi_int * Ssp
const casadi_int * theirs_Xsp
const casadi_int * lamg_gapsp
const casadi_int * Bsp
const casadi_int * Rsp
const casadi_int * xsp
casadi_hpipm_block * lug
casadi_hpipm_block * D
const casadi_int * Dsp
const casadi_int * bsp
casadi_hpipm_block * R
casadi_hpipm_block * lam_cl
casadi_hpipm_block * B
casadi_hpipm_block * u
casadi_hpipm_block * lam_cu
const casadi_int * Qsp
const casadi_int * theirs_usp
const casadi_int * sp_ba
casadi_hpipm_block * A
casadi_hpipm_block * b
const casadi_int * sp_x
struct d_ocp_qp_ipm_arg hpipm_options
const casadi_int * Isp
casadi_hpipm_block * lam_xl
casadi_hpipm_block * x
const casadi_qp_prob< T1 > * qp