hpmpc_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 "hpmpc_interface.hpp"
26 #include <numeric>
27 
28 namespace casadi {
29 
30  extern "C"
31  int CASADI_CONIC_HPMPC_EXPORT
32  casadi_register_conic_hpmpc(Conic::Plugin* plugin) {
33  plugin->creator = HpmpcInterface::creator;
34  plugin->name = "hpmpc";
35  plugin->doc = HpmpcInterface::meta_doc.c_str();
36  plugin->version = CASADI_VERSION;
37  plugin->options = &HpmpcInterface::options_;
38  return 0;
39  }
40 
41  extern "C"
42  void CASADI_CONIC_HPMPC_EXPORT casadi_load_conic_hpmpc() {
44  }
45 
46  HpmpcInterface::HpmpcInterface(const std::string& name,
47  const std::map<std::string, Sparsity>& st)
48  : Conic(name, st) {
49  }
50 
52  clear_mem();
53  }
54 
56  = {{&Conic::options_},
57  {{"N",
58  {OT_INT,
59  "OCP horizon"}},
60  {"nx",
61  {OT_INTVECTOR,
62  "Number of states, length N+1"}},
63  {"nu",
64  {OT_INTVECTOR,
65  "Number of controls, length N"}},
66  {"ng",
67  {OT_INTVECTOR,
68  "Number of non-dynamic constraints, length N+1"}},
69  {"mu0",
70  {OT_DOUBLE,
71  "Max element in cost function as estimate of max multiplier"}},
72  {"max_iter",
73  {OT_INT,
74  "Max number of iterations"}},
75  {"tol",
76  {OT_DOUBLE,
77  "Tolerance in the duality measure"}},
78  {"warm_start",
79  {OT_BOOL,
80  "Use warm-starting"}},
81  {"inf",
82  {OT_DOUBLE,
83  "HPMPC cannot handle infinities. Infinities will be replaced by this option's value."}},
84  {"print_level",
85  {OT_INT,
86  "Amount of diagnostic printing [Default: 1]."}},
87  {"target",
88  {OT_STRING,
89  "hpmpc target"}},
90  {"blasfeo_target",
91  {OT_STRING,
92  "hpmpc target"}}}
93  };
94 
95  void HpmpcInterface::init(const Dict& opts) {
96  Conic::init(opts);
97 
98  // Default options
99  mu0_ = 1;
100  max_iter_ = 1000;
101  print_level_ = 1;
102  tol_ = 1e-8;
103  warm_start_ = false;
104  inf_ = 1e6;
105  target_ = "C99_4X4";
106  blasfeo_target_ = "GENERIC";
107  casadi_int struct_cnt=0;
108  // Read options
109  for (auto&& op : opts) {
110  if (op.first=="N") {
111  N_ = op.second;
112  struct_cnt++;
113  } else if (op.first=="nx") {
114  nxs_ = op.second;
115  struct_cnt++;
116  } else if (op.first=="nu") {
117  nus_ = op.second;
118  struct_cnt++;
119  } else if (op.first=="ng") {
120  ngs_ = op.second;
121  struct_cnt++;
122  } else if (op.first=="mu0") {
123  mu0_ = op.second;
124  } else if (op.first=="max_iter") {
125  max_iter_ = op.second;
126  } else if (op.first=="tol") {
127  tol_ = op.second;
128  } else if (op.first=="warm_start") {
129  warm_start_ = op.second;
130  } else if (op.first=="target") {
131  target_ = static_cast<std::string>(op.second);
132  } else if (op.first=="blasfeo_target") {
133  blasfeo_target_ = static_cast<std::string>(op.second);
134  } else if (op.first=="print_level") {
135  print_level_ = op.second;
136  } else if (op.first=="inf") {
137  inf_ = op.second;
138  }
139  }
140 
141  bool detect_structure = struct_cnt==0;
142  casadi_assert(struct_cnt==0 || struct_cnt==4,
143  "You must either set all of N, nx, nu, ng; "
144  "or set none at all (automatic detection).");
145 
146  const std::vector<casadi_int>& nx = nxs_;
147  const std::vector<casadi_int>& ng = ngs_;
148  const std::vector<casadi_int>& nu = nus_;
149 
150  if (detect_structure) {
151  /* General strategy: look for the xk+1 diagonal part in A
152  */
153 
154  // Find the right-most column for each row in A -> A_skyline
155  // Find the second-to-right-most column -> A_skyline2
156  // Find the left-most column -> A_bottomline
157  Sparsity AT = A_.T();
158  std::vector<casadi_int> A_skyline;
159  std::vector<casadi_int> A_skyline2;
160  std::vector<casadi_int> A_bottomline;
161  for (casadi_int i=0;i<AT.size2();++i) {
162  casadi_int pivot = AT.colind()[i+1];
163  A_bottomline.push_back(AT.row()[AT.colind()[i]]);
164  if (pivot>AT.colind()[i]) {
165  A_skyline.push_back(AT.row()[pivot-1]);
166  if (pivot>AT.colind()[i]+1) {
167  A_skyline2.push_back(AT.row()[pivot-2]);
168  } else {
169  A_skyline2.push_back(-1);
170  }
171  } else {
172  A_skyline.push_back(-1);
173  A_skyline2.push_back(-1);
174  }
175  }
176 
177  /*
178  Loop over the right-most columns of A:
179  they form the diagonal part due to xk+1 in gap constraints.
180  detect when the diagonal pattern is broken -> new stage
181  */
182  casadi_int pivot = 0; // Current right-most element
183  casadi_int start_pivot = pivot; // First right-most element that started the stage
184  casadi_int cg = 0; // Counter for non-gap-closing constraints
185  for (casadi_int i=0;i<na_;++i) { // Loop over all rows
186  bool commit = false; // Set true to jump to the stage
187  if (A_skyline[i]>pivot+1) { // Jump to a diagonal in the future
188  nus_.push_back(A_skyline[i]-pivot-1); // Size of jump equals number of states
189  commit = true;
190  } else if (A_skyline[i]==pivot+1) { // Walking the diagonal
191  if (A_skyline2[i]<start_pivot) { // Free of below-diagonal entries?
192  pivot++;
193  } else {
194  nus_.push_back(0); // We cannot but conclude that we arrived at a new stage
195  commit = true;
196  }
197  } else { // non-gap-closing constraint detected
198  cg++;
199  }
200 
201  if (commit) {
202  nxs_.push_back(pivot-start_pivot+1);
203  ngs_.push_back(cg); cg=0;
204  start_pivot = A_skyline[i];
205  pivot = A_skyline[i];
206  }
207  }
208  nxs_.push_back(pivot-start_pivot+1);
209 
210  // Correction for k==0
211  nxs_[0] = A_skyline[0];
212  nus_[0] = 0;
213  ngs_.erase(ngs_.begin());
214  casadi_int cN=0;
215  for (casadi_int i=na_-1;i>=0;--i) {
216  if (A_bottomline[i]<start_pivot) break;
217  cN++;
218  }
219  ngs_.push_back(cg-cN);
220  ngs_.push_back(cN);
221 
222  N_ = nus_.size();
223  if (verbose_) {
224  casadi_message("Detected structure: N " + str(N_) + ", nx " + str(nx) + ", "
225  "nu " + str(nu) + ", ng " + str(ng) + ".");
226  }
227  }
228 
229  casadi_assert_dev(nx.size()==N_+1);
230  casadi_assert_dev(nu.size()==N_);
231  casadi_assert_dev(ng.size()==N_+1);
232 
233  casadi_assert(nx_ == std::accumulate(nx.begin(), nx.end(), 0) + // NOLINT
234  std::accumulate(nu.begin(), nu.end(), 0),
235  "sum(nx)+sum(nu) = must equal total size of variables (" + str(nx_) + "). "
236  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", "
237  "nu " + str(nu) + ", ng " + str(ng) + ".");
238  casadi_assert(na_ == std::accumulate(nx.begin()+1, nx.end(), 0) + // NOLINT
239  std::accumulate(ng.begin(), ng.end(), 0),
240  "sum(nx+1)+sum(ng) = must equal total size of constraints (" + str(na_) + "). "
241  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", "
242  "nu " + str(nu) + ", ng " + str(ng) + ".");
243  // Load library HPMPC when applicable
244  std::string searchpath;
245 
246 #ifdef HPMPC_DLOPEN
247 
248  std::string libname = "casadi_hpmpc_" + target_ + "_" + blasfeo_target_;
249  DL_HANDLE_TYPE handle = load_library(libname, searchpath, true);
250 
251  std::string work_size_name = "hpmpc_d_ip_ocp_hard_tv_work_space_size_bytes";
252 
253 #ifdef _WIN32
254  hpmpc_d_ip_ocp_hard_tv_work_space_size_bytes =
255  (Work_size)GetProcAddress(handle, TEXT(work_size_name.c_str()));
256 #else // _WIN32
257  // Reset error
258  dlerror();
259 
260  // Load creator
261  hpmpc_d_ip_ocp_hard_tv_work_space_size_bytes = (Work_size)dlsym(handle, work_size_name.c_str());
262 #endif // _WIN32
263 
264  casadi_assert(hpmpc_d_ip_ocp_hard_tv_work_space_size_bytes!=nullptr,
265  "HPMPC interface: symbol \"" + work_size_name + "\" found in " + searchpath + ".");
266 
267  std::string ocp_solve_name = "fortran_order_d_ip_ocp_hard_tv";
268 
269 #ifdef _WIN32
270  fortran_order_d_ip_ocp_hard_tv =
271  (Ocp_solve)GetProcAddress(handle, TEXT(ocp_solve_name.c_str()));
272 #else // _WIN32
273  // Reset error
274  dlerror();
275 
276  // Load creator
277  fortran_order_d_ip_ocp_hard_tv = (Ocp_solve)dlsym(handle, ocp_solve_name.c_str());
278 #endif // _WIN32
279 
280  casadi_assert(fortran_order_d_ip_ocp_hard_tv!=nullptr,
281  "HPMPC interface: symbol \"" + ocp_solve_name + "\" found in " + searchpath + ".");
282 #endif
283 
284  /* Disassemble A input into:
285  A B I
286  C D
287  A B I
288  C D
289  */
290  casadi_int offset_r = 0, offset_c = 0;
291  for (casadi_int k=0;k<N_;++k) { // Loop over blocks
292  A_blocks.push_back({offset_r, offset_c, nx[k+1], nx[k]});
293  B_blocks.push_back({offset_r, offset_c+nx[k], nx[k+1], nu[k]});
294  C_blocks.push_back({offset_r+nx[k+1], offset_c, ng[k], nx[k]});
295  D_blocks.push_back({offset_r+nx[k+1], offset_c+nx[k], ng[k], nu[k]});
296 
297  offset_c+= nx[k]+nu[k];
298  if (k+1<N_)
299  I_blocks.push_back({offset_r, offset_c, nx[k+1], nx[k+1]});
300  else
301  I_blocks.push_back({offset_r, offset_c, nx[k+1], nx[k+1]});
302  offset_r+= nx[k+1]+ng[k];
303  }
304 
305  C_blocks.push_back({offset_r, offset_c, ng[N_], nx[N_]});
306 
311  Isp_ = blocksparsity(na_, nx_, I_blocks, true);
312 
313  Sparsity total = Asp_ + Bsp_ + Csp_ + Dsp_ + Isp_;
314  casadi_assert((A_ + total).nnz() == total.nnz(),
315  "HPMPC: specified structure of A does not correspond to what the interface can handle. "
316  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", nu " + str(nu) + ", "
317  "ng " + str(ng) + ".");
318  casadi_assert_dev(total.nnz() == Asp_.nnz() + Bsp_.nnz() + Csp_.nnz() + Dsp_.nnz()
319  + Isp_.nnz());
320 
321  /* Disassemble H input into:
322  Q S'
323  S R
324  Q S'
325  S R
326 
327  Multiply by 2
328  */
329  casadi_int offset = 0;
330  for (casadi_int k=0;k<N_;++k) { // Loop over blocks
331  R_blocks.push_back({offset+nx[k], offset+nx[k], nu[k], nu[k]});
332  S_blocks.push_back({offset+nx[k], offset, nu[k], nx[k]});
333  Q_blocks.push_back({offset, offset, nx[k], nx[k]});
334  offset+= nx[k]+nu[k];
335  }
336  Q_blocks.push_back({offset, offset, nx[N_], nx[N_]});
337 
341 
342  total = Rsp_ + Ssp_ + Qsp_ + Ssp_.T();
343  casadi_assert((H_ + total).nnz() == total.nnz(),
344  "HPMPC: specified structure of H does not correspond to what the interface can handle. "
345  "Structure is: N " + str(N_) + ", nx " + str(nx) + ", nu " + str(nu) + ", "
346  "ng " + str(ng) + ".");
347  casadi_assert_dev(total.nnz() == Rsp_.nnz() + 2*Ssp_.nnz() + Qsp_.nnz());
348 
349  /* Disassemble LBA/UBA input into:
350  b
351  lg/ug
352 
353  b
354  lg/ug
355  */
356  offset = 0;
357 
358  for (casadi_int k=0;k<N_;++k) {
359  b_blocks.push_back({offset, 0, nx[k+1], 1}); offset+= nx[k+1];
360  lug_blocks.push_back({offset, 0, ng[k], 1}); offset+= ng[k];
361  }
362  lug_blocks.push_back({offset, 0, ng[N_], 1});
363 
366  total = bsp_ + lugsp_;
367  casadi_assert_dev(total.nnz() == bsp_.nnz() + lugsp_.nnz());
368  casadi_assert_dev(total.nnz() == na_);
369 
370  /* Disassemble G/X0 input into:
371  r/u
372  q/x
373 
374  r/u
375  q/x
376  */
377  offset = 0;
378 
379  for (casadi_int k=0;k<N_;++k) {
380  x_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
381  u_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
382  }
383  x_blocks.push_back({offset, 0, nx[N_], 1});
384 
387  total = usp_ + xsp_;
388  casadi_assert_dev(total.nnz() == usp_.nnz() + xsp_.nnz());
389  casadi_assert_dev(total.nnz() == nx_);
390 
391  std::vector< Block > theirs_u_blocks, theirs_x_blocks;
392  offset = 0;
393 
394  for (casadi_int k=0;k<N_;++k) {
395  theirs_u_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
396  theirs_x_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
397  }
398  theirs_x_blocks.push_back({offset, 0, nx[N_], 1});
399 
400  theirs_usp_ = blocksparsity(nx_, 1, theirs_u_blocks);
401  theirs_xsp_ = blocksparsity(nx_, 1, theirs_x_blocks);
402  total = theirs_usp_ + theirs_xsp_;
403  casadi_assert_dev(total.nnz() == theirs_usp_.nnz() + theirs_xsp_.nnz());
404  casadi_assert_dev(total.nnz() == nx_);
405 
406  offset = 0;
407  std::vector< Block > lamg_gap_blocks;
408  for (casadi_int k=0;k<N_;++k) {
409  lamg_gap_blocks.push_back({offset, 0, nx[k+1], 1});offset+= nx[k+1] + ng[k];
410  }
411  lamg_gapsp_ = blocksparsity(na_, 1, lamg_gap_blocks);
413 
414  offset = 0;
415 
416  for (casadi_int k=0;k<N_;++k) {
417  lam_ul_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
418  lam_xl_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
419  lam_uu_blocks.push_back({offset, 0, nu[k], 1}); offset+= nu[k];
420  lam_xu_blocks.push_back({offset, 0, nx[k], 1}); offset+= nx[k];
421  lam_cl_blocks.push_back({offset, 0, ng[k], 1}); offset+= ng[k];
422  lam_cu_blocks.push_back({offset, 0, ng[k], 1}); offset+= ng[k];
423  }
424  lam_xl_blocks.push_back({offset, 0, nx[N_], 1}); offset+= nx[N_];
425  lam_xu_blocks.push_back({offset, 0, nx[N_], 1}); offset+= nx[N_];
426  lam_cl_blocks.push_back({offset, 0, ng[N_], 1}); offset+= ng[N_];
427  lam_cu_blocks.push_back({offset, 0, ng[N_], 1}); offset+= ng[N_];
428 
429  lam_ulsp_ = blocksparsity(offset, 1, lam_ul_blocks);
430  lam_uusp_ = blocksparsity(offset, 1, lam_uu_blocks);
431  lam_xlsp_ = blocksparsity(offset, 1, lam_xl_blocks);
432  lam_xusp_ = blocksparsity(offset, 1, lam_xu_blocks);
433  lam_clsp_ = blocksparsity(offset, 1, lam_cl_blocks);
434  lam_cusp_ = blocksparsity(offset, 1, lam_cu_blocks);
435 
436  pisp_ = Sparsity::dense(std::accumulate(nx.begin()+1, nx.end(), 0), 1); // NOLINT
437 
439  casadi_assert_dev(total.nnz() == lam_ulsp_.nnz() + lam_uusp_.nnz() + lam_xlsp_.nnz() +
440  lam_xusp_.nnz() + lam_clsp_.nnz() + lam_cusp_.nnz());
441  casadi_assert_dev(total.nnz() == offset);
442 
443  theirs_Xsp_ = Sparsity::dense(std::accumulate(nx.begin(), nx.end(), 0), 1); // NOLINT
444  theirs_Usp_ = Sparsity::dense(std::accumulate(nu.begin(), nu.end(), 0), 1); // NOLINT
445 
446  }
447 
448 
449  int HpmpcInterface::init_mem(void* mem) const {
450  if (Conic::init_mem(mem)) return 1;
451  auto m = static_cast<HpmpcMemory*>(mem);
452 
453  init_vector(m->nx, nxs_);
454  init_vector(m->nu, nus_); m->nu.push_back(0);
455  init_vector(m->ng, ngs_);
456 
457  const std::vector<int>& nx = m->nx;
458  const std::vector<int>& nu = m->nu;
459  const std::vector<int>& ng = m->ng;
460  const std::vector<int>& nb = m->nb;
461 
462  m->nb.resize(N_+1);
463  casadi_int offset = 0;
464  for (casadi_int k=0;k<N_;++k) m->nb[k] = nx[k]+nu[k];
465  m->nb[N_] = nx[N_];
466 
467  m->A.resize(Asp_.nnz());
468  m->B.resize(Bsp_.nnz());
469  m->C.resize(Csp_.nnz());
470  m->D.resize(Dsp_.nnz());
471  m->R.resize(Rsp_.nnz());
472  m->I.resize(Isp_.nnz());
473  m->S.resize(Ssp_.nnz());
474  m->Q.resize(Qsp_.nnz());
475  m->b.resize(bsp_.nnz());
476  m->b2.resize(bsp_.nnz());
477  m->x.resize(xsp_.nnz());m->q.resize(xsp_.nnz());
478  m->u.resize(usp_.nnz());m->r.resize(usp_.nnz());
479  m->lg.resize(lugsp_.nnz());
480  m->ug.resize(lugsp_.nnz());
481  m->lb.resize(nx_);m->ub.resize(nx_);
482  m->pi.resize(pisp_.nnz());
483 
484  offset = 0;
485  for (casadi_int k=0;k<N_+1;++k) offset+=m->nx[k]+nu[k];
486  m->hidxb.resize(offset);
487 
488  offset = 0;
489  for (casadi_int k=0;k<N_;++k) offset+=ng[k]+nx[k]+nu[k];
490  offset+=ng[N_]+nx[N_];
491  m->lam.resize(2*offset);
492 
493  // Allocate double* work vectors
494  blockptr(m->As, m->A, A_blocks);
495  blockptr(m->Bs, m->B, B_blocks);
496  blockptr(m->Cs, m->C, C_blocks);
497  blockptr(m->Ds, m->D, D_blocks);
498  blockptr(m->Is, m->I, I_blocks, true);
499  blockptr(m->Rs, m->R, R_blocks);
500  blockptr(m->Qs, m->Q, Q_blocks);
501  blockptr(m->Ss, m->S, S_blocks);
502  blockptr(m->us, m->u, u_blocks);
503  blockptr(m->xs, m->x, x_blocks);
504  blockptr(m->rs, m->r, u_blocks);
505  blockptr(m->qs, m->q, x_blocks);
506  blockptr(m->lgs, m->lg, lug_blocks);
507  blockptr(m->ugs, m->ug, lug_blocks);
508  blockptr(m->bs, m->b, b_blocks);
509 
510  m->pis.resize(N_);
511  offset = 0;
512  for (casadi_int k=0;k<N_;++k) {
513  m->pis[k] = get_ptr(m->pi)+offset;
514  offset+=nx[k+1];
515  }
516 
517  m->lbs.resize(N_+1);
518  m->ubs.resize(N_+1);
519  offset = 0;
520  for (casadi_int k=0;k<N_+1;++k) {
521  m->lbs[k] = get_ptr(m->lb)+offset;
522  m->ubs[k] = get_ptr(m->ub)+offset;
523  offset+=nu[k]+nx[k];
524  }
525 
526  m->lams.resize(N_+1);
527  offset = 0;
528  for (casadi_int k=0;k<N_+1;++k) {
529  m->lams[k] = get_ptr(m->lam)+offset;
530  offset+=2*(ng[k]+nb[k]);
531  }
532 
533  m->hidxbs.resize(N_+1);
534  offset = 0;
535  for (casadi_int k=0;k<N_+1;++k) {
536  m->hidxbs[k] = get_ptr(m->hidxb)+offset;
537  for (casadi_int i=0;i<m->nb[k];++i) m->hidxbs[k][i] = i;
538  offset+=nb[k];
539  }
540 
541  // Allocate extra workspace as per HPMPC request
542  int workspace_size = hpmpc_d_ip_ocp_hard_tv_work_space_size_bytes(
543  N_, get_ptr(m->nx), get_ptr(m->nu), get_ptr(m->nb), get_ptr(m->hidxbs), get_ptr(m->ng), N_);
544  m->workspace.resize(workspace_size);
545  m->stats.resize(max_iter_*5);
546 
547  m->pv.resize(2*(nx_+na_));
548 
549  m->res.resize(4);
550 
551  m->add_stat("preprocessing");
552  m->add_stat("solver");
553  m->add_stat("postprocessing");
554  return 0;
555  }
556 
557  void HpmpcInterface::mproject(double factor, const double* x, const casadi_int* sp_x,
558  double* y, const casadi_int* sp_y, double* w) {
559  casadi_int ncol_y = sp_y[1];
560  const casadi_int *colind_y = sp_y+2;
561  casadi_project(x, sp_x, y, sp_y, w);
562  casadi_scal(colind_y[ncol_y], factor, y);
563  }
564 
565  void HpmpcInterface::dense_transfer(double factor, const double* x,
566  const casadi_int* sp_x, double* y,
567  const casadi_int* sp_y, double* w) {
568  CASADI_PREFIX(sparsify)(x, w, sp_x, false);
569  casadi_int nrow_y = sp_y[0];
570  casadi_int ncol_y = sp_y[1];
571  const casadi_int *colind_y = sp_y+2, *row_y = sp_y + 2 + ncol_y+1;
572  /* Loop over columns of y */
573  casadi_int i, el;
574  for (i=0; i<ncol_y; ++i) {
575  for (el=colind_y[i]; el<colind_y[i+1]; ++el) y[nrow_y*i + row_y[el]] += factor*(*w++);
576  }
577  }
578 
580  solve(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
581  auto m = static_cast<HpmpcMemory*>(mem);
582  m->fstats.at("preprocessing").tic();
583 
584  double* pv = get_ptr(m->pv);
585 
586  // Dissect A matrix
587  casadi_project(arg[CONIC_A], A_, get_ptr(m->A), Asp_, pv);
588  casadi_project(arg[CONIC_A], A_, get_ptr(m->B), Bsp_, pv);
589  casadi_project(arg[CONIC_A], A_, get_ptr(m->C), Csp_, pv);
590  casadi_project(arg[CONIC_A], A_, get_ptr(m->D), Dsp_, pv);
591  casadi_project(arg[CONIC_A], A_, get_ptr(m->I), Isp_, pv);
592 
593  // Dissect H matrix; definition of HPMPC lacks a factor 2
594  mproject(0.5, arg[CONIC_H], H_, get_ptr(m->R), Rsp_, pv);
595  mproject(0.5, arg[CONIC_H], H_, get_ptr(m->S), Ssp_, pv);
596  mproject(0.5, arg[CONIC_H], H_, get_ptr(m->Q), Qsp_, pv);
597 
598  // Dissect LBA/UBA
599  mproject(-1.0, arg[CONIC_LBA], sparsity_in_.at(CONIC_LBA), get_ptr(m->b), bsp_, pv);
600  mproject(-1.0, arg[CONIC_UBA], sparsity_in_.at(CONIC_UBA), get_ptr(m->b2), bsp_, pv);
601  casadi_assert_dev(std::equal(m->b.begin(), m->b.end(), m->b2.begin()));
604 
605  // Dissect LBX/UBX input
606  std::fill(m->lb.begin(), m->lb.end(), 0);
607  std::fill(m->ub.begin(), m->ub.end(), 0);
608 
609  dense_transfer(1.0, arg[CONIC_LBX], xsp_, get_ptr(m->lb), theirs_xsp_, pv);
610  dense_transfer(1.0, arg[CONIC_UBX], xsp_, get_ptr(m->ub), theirs_xsp_, pv);
611  dense_transfer(1.0, arg[CONIC_LBX], usp_, get_ptr(m->lb), theirs_usp_, pv);
612  dense_transfer(1.0, arg[CONIC_UBX], usp_, get_ptr(m->ub), theirs_usp_, pv);
613 
614  // Dissect G
615  mproject(0.5, arg[CONIC_G], sparsity_in_.at(CONIC_G), get_ptr(m->r), usp_, pv);
616  mproject(0.5, arg[CONIC_G], sparsity_in_.at(CONIC_G), get_ptr(m->q), xsp_, pv);
617 
618  // Dissect X0
619  casadi_project(arg[CONIC_X0], sparsity_in_.at(CONIC_X0), get_ptr(m->u), usp_, pv);
620  casadi_project(arg[CONIC_X0], sparsity_in_.at(CONIC_X0), get_ptr(m->x), xsp_, pv);
621 
622  m->iter_count = -1;
623 
624  // Deal with non-unity I block
625  for (casadi_int k=0;k<N_;++k) {
626  casadi_int n_row = m->nx[k+1];
627  for (casadi_int i=0;i<n_row;++i) {
628  double f = -1/m->Is[k][i];
629  m->bs[k][i]*=f;
630  for (casadi_int j=0;j<m->nx[k];++j) m->As[k][i+j*n_row]*=f;
631  for (casadi_int j=0;j<m->nu[k];++j) m->Bs[k][i+j*n_row]*=f;
632  }
633  }
634 
635  // replace infinities
636  for (casadi_int i=0;i<m->lb.size();++i) {
637  if (m->lb[i]==-std::numeric_limits<double>::infinity()) m->lb[i] = -inf_;
638  }
639  for (casadi_int i=0;i<m->ub.size();++i) {
640  if (m->ub[i]==std::numeric_limits<double>::infinity()) m->ub[i] = inf_;
641  }
642  for (casadi_int i=0;i<m->lg.size();++i) {
643  if (m->lg[i]==-std::numeric_limits<double>::infinity()) m->lg[i] = -inf_;
644  }
645  for (casadi_int i=0;i<m->ug.size();++i) {
646  if (m->ug[i]==std::numeric_limits<double>::infinity()) m->ug[i] = inf_;
647  }
648 
649  m->fstats.at("preprocessing").toc();
650  m->fstats.at("solver").tic();
651 
652 
653  std::fill(m->pi.begin(), m->pi.end(), 0);
654  std::fill(m->lam.begin(), m->lam.end(), 0);
655 
656  if (arg[CONIC_LAM_A0]) {
657  dense_transfer(0.5, arg[CONIC_LAM_A0], lamg_gapsp_, get_ptr(m->pi), pisp_, pv);
658  // Deal with non-unity I block
659  for (casadi_int k=0;k<N_;++k) {
660  casadi_int n_row = m->nx[k+1];
661  for (casadi_int i=0;i<n_row;++i) {
662  double f = -m->Is[k][i];
663  m->pis[k][i]*=f;
664  }
665  }
666 
667  dense_transfer(0.5, arg[CONIC_LAM_A0], lamg_csp_, get_ptr(m->lam), lam_cusp_, pv);
668  }
669 
670  if (arg[CONIC_LAM_X0]) {
671  dense_transfer(0.5, arg[CONIC_LAM_X0], usp_, get_ptr(m->lam), lam_uusp_, pv);
672  dense_transfer(0.5, arg[CONIC_LAM_X0], xsp_, get_ptr(m->lam), lam_xusp_, pv);
673  }
674 
675  m->return_status =
676  fortran_order_d_ip_ocp_hard_tv(&m->iter_count, max_iter_, mu0_, tol_, N_, get_ptr(m->nx),
677  get_ptr(m->nu), get_ptr(m->nb), get_ptr(m->hidxbs), get_ptr(m->ng), N_, warm_start_,
678  get_ptr(m->As), get_ptr(m->Bs), get_ptr(m->bs), get_ptr(m->Qs), get_ptr(m->Ss),
679  get_ptr(m->Rs), get_ptr(m->qs), get_ptr(m->rs), get_ptr(m->lbs), get_ptr(m->ubs),
680  get_ptr(m->Cs), get_ptr(m->Ds), get_ptr(m->lgs), get_ptr(m->ugs), get_ptr(m->xs),
681  get_ptr(m->us), get_ptr(m->pis), get_ptr(m->lams), get_ptr(m->res),
682  get_ptr(m->workspace), get_ptr(m->stats));
683 
684  m->fstats.at("solver").toc();
685  m->fstats.at("postprocessing").tic();
686  if (print_level_>0) {
687  uout() << "HPMPC finished after " << m->iter_count << " iterations." << std::endl;
688  uout() << "return status: " << m->return_status << std::endl;
689  uout() << "residuals: " << m->res << std::endl;
690  }
691  m->d_qp.success = m->return_status==0;
692 
693  std::fill(res[CONIC_X], res[CONIC_X]+nx_, 0);
694  dense_transfer(1.0, get_ptr(m->x), theirs_Xsp_, res[CONIC_X], xsp_, pv);
695  dense_transfer(1.0, get_ptr(m->u), theirs_Usp_, res[CONIC_X], usp_, pv);
696 
697  std::fill(res[CONIC_LAM_X], res[CONIC_LAM_X]+nx_, 0);
698  std::fill(res[CONIC_LAM_A], res[CONIC_LAM_A]+na_, 0);
699 
700  // Deal with non-unity I block
701  for (casadi_int k=0;k<N_;++k) {
702  casadi_int n_row = m->nx[k+1];
703  for (casadi_int i=0;i<n_row;++i) {
704  double f = -1/m->Is[k][i];
705  m->pis[k][i]*=f;
706  }
707  }
708 
709  dense_transfer(2.0, get_ptr(m->pi), pisp_, res[CONIC_LAM_A], lamg_gapsp_, pv);
710  dense_transfer(2.0, get_ptr(m->lam), lam_cusp_, res[CONIC_LAM_A], lamg_csp_, pv);
711  dense_transfer(-2.0, get_ptr(m->lam), lam_clsp_, res[CONIC_LAM_A], lamg_csp_, pv);
712 
713  dense_transfer(-2.0, get_ptr(m->lam), lam_ulsp_, res[CONIC_LAM_X], usp_, pv);
714  dense_transfer(2.0, get_ptr(m->lam), lam_uusp_, res[CONIC_LAM_X], usp_, pv);
715  dense_transfer(-2.0, get_ptr(m->lam), lam_xlsp_, res[CONIC_LAM_X], xsp_, pv);
716  dense_transfer(2.0, get_ptr(m->lam), lam_xusp_, res[CONIC_LAM_X], xsp_, pv);
717 
718  // Construct f
719  double f = casadi_dot(nx_, arg[CONIC_G], res[CONIC_X]);
720  f += 0.5*casadi_bilin(arg[CONIC_H], H_, res[CONIC_X], res[CONIC_X]);
721 
722  if (res[CONIC_COST]) res[CONIC_COST][0] = f;
723 
724  m->fstats.at("postprocessing").toc();
725 
726  return 0;
727  }
728 
729  Dict HpmpcInterface::get_stats(void* mem) const {
730  Dict stats = Conic::get_stats(mem);
731  auto m = static_cast<HpmpcMemory*>(mem);
732  stats["return_status"] = m->return_status;
733  stats["iter_count"] = m->iter_count;
734  stats["res"] = m->res;
735  return stats;
736  }
737 
739  }
740 
742 
743  }
744 
745  Sparsity HpmpcInterface::blocksparsity(casadi_int rows, casadi_int cols,
746  const std::vector<Block>& blocks, bool eye) {
747  DM r(rows, cols);
748  for (auto && b : blocks) {
749  if (eye) {
750  r(range(b.offset_r, b.offset_r+b.rows),
751  range(b.offset_c, b.offset_c+b.cols)) = DM::eye(b.rows);
752  casadi_assert_dev(b.rows==b.cols);
753  } else {
754  r(range(b.offset_r, b.offset_r+b.rows),
755  range(b.offset_c, b.offset_c+b.cols)) = DM::zeros(b.rows, b.cols);
756  }
757  }
758  return r.sparsity();
759  }
760  void HpmpcInterface::blockptr(std::vector<double *>& vs, std::vector<double>& v,
761  const std::vector<Block>& blocks, bool eye) {
762  casadi_int N = blocks.size();
763  vs.resize(N);
764  casadi_int offset=0;
765  for (casadi_int k=0;k<N;++k) {
766  vs[k] = get_ptr(v)+offset;
767  if (eye) {
768  casadi_assert_dev(blocks[k].rows==blocks[k].cols);
769  offset+=blocks[k].rows;
770  } else {
771  offset+=blocks[k].rows*blocks[k].cols;
772  }
773  }
774  }
775 } // namespace casadi
Internal class.
Definition: conic_impl.hpp:44
static const Options options_
Options.
Definition: conic_impl.hpp:83
casadi_int nx_
Number of decision variables.
Definition: conic_impl.hpp:169
int init_mem(void *mem) const override
Initalize memory block.
Definition: conic.cpp:451
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
Definition: conic_impl.hpp:172
Sparsity H_
Problem structure.
Definition: conic_impl.hpp:166
void init(const Dict &opts) override
Initialize.
Definition: conic.cpp:412
Dict get_stats(void *mem) const override
Get all statistics.
Definition: conic.cpp:711
std::vector< Sparsity > sparsity_in_
Input and output sparsity.
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.
HpmpcInterface()
Constructor.
static const Options options_
Options.
std::vector< Block > Q_blocks
std::vector< Block > lam_ul_blocks
std::vector< Block > R_blocks
std::vector< Block > A_blocks
static void mproject(double factor, const double *x, const casadi_int *sp_x, double *y, const casadi_int *sp_y, double *w)
Helper function.
std::vector< Block > I_blocks
static const std::string meta_doc
A documentation string.
Dict get_stats(void *mem) const override
Get all statistics.
std::vector< Block > b_blocks
std::vector< casadi_int > nxs_
std::vector< Block > lam_cl_blocks
std::vector< Block > C_blocks
std::vector< Block > u_blocks
~HpmpcInterface() override
Destructor.
std::vector< Block > lug_blocks
std::vector< Block > lam_uu_blocks
static void dense_transfer(double factor, const double *x, const casadi_int *sp_x, double *y, const casadi_int *sp_y, double *w)
std::vector< Block > lam_cu_blocks
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically.
static Sparsity blocksparsity(casadi_int rows, casadi_int cols, const std::vector< Block > &blocks, bool eye=false)
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
std::vector< Block > B_blocks
std::vector< casadi_int > nus_
std::vector< Block > lam_xu_blocks
std::vector< Block > S_blocks
std::vector< casadi_int > ngs_
void init(const Dict &opts) override
Initialize.
std::vector< Block > D_blocks
std::vector< Block > lam_xl_blocks
std::vector< Block > x_blocks
int init_mem(void *mem) const override
Initalize memory block.
static void blockptr(std::vector< double * > &vs, std::vector< double > &v, const std::vector< Block > &blocks, bool eye=false)
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 handle_t load_library(const std::string &libname, std::string &resultpath, bool global)
Load a library dynamically.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
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
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.
int CASADI_CONIC_HPMPC_EXPORT casadi_register_conic_hpmpc(Conic::Plugin *plugin)
void init_vector(std::vector< S > &d, const std::vector< D > &s)
@ 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
T1 casadi_bilin(const T1 *A, const casadi_int *sp_A, const T1 *x, const T1 *y)
void casadi_project(const T1 *x, const casadi_int *sp_x, T1 *y, const casadi_int *sp_y, T1 *w)
Sparse copy: y <- x, w work vector (length >= number of rows)
@ 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.
T1 casadi_dot(casadi_int n, const T1 *x, const T1 *y)
Inner product.
void casadi_scal(casadi_int n, T1 alpha, T1 *x)
SCAL: x <- alpha*x.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
void CASADI_CONIC_HPMPC_EXPORT casadi_load_conic_hpmpc()
std::ostream & uout()
@ 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
HpmpcMemory()
Constructor.
Options metadata for a class.
Definition: options.hpp:40
std::map< std::string, FStats > fstats