sqpmethod.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 #include "sqpmethod.hpp"
26 
27 #include "casadi/core/casadi_misc.hpp"
28 #include "casadi/core/calculus.hpp"
29 #include "casadi/core/conic.hpp"
30 #include "casadi/core/conic_impl.hpp"
31 #include "casadi/core/convexify.hpp"
32 
33 #include <ctime>
34 #include <iomanip>
35 #include <fstream>
36 #include <cmath>
37 #include <cfloat>
38 
39 namespace casadi {
40 
41 extern "C"
42 int CASADI_NLPSOL_SQPMETHOD_EXPORT
43  casadi_register_nlpsol_sqpmethod(Nlpsol::Plugin* plugin) {
44  plugin->creator = Sqpmethod::creator;
45  plugin->name = "sqpmethod";
46  plugin->doc = Sqpmethod::meta_doc.c_str();
47  plugin->version = CASADI_VERSION;
48  plugin->options = &Sqpmethod::options_;
49  plugin->deserialize = &Sqpmethod::deserialize;
50  return 0;
51 }
52 
53 extern "C"
54 void CASADI_NLPSOL_SQPMETHOD_EXPORT casadi_load_nlpsol_sqpmethod() {
56 }
57 
58 Sqpmethod::Sqpmethod(const std::string& name, const Function& nlp)
59  : Nlpsol(name, nlp) {
60 }
61 
63  clear_mem();
64 }
65 
67 = {{&Nlpsol::options_},
68  {{"qpsol",
69  {OT_STRING,
70  "The QP solver to be used by the SQP method [qpoases]"}},
71  {"qpsol_options",
72  {OT_DICT,
73  "Options to be passed to the QP solver"}},
74  {"hessian_approximation",
75  {OT_STRING,
76  "limited-memory|exact"}},
77  {"max_iter",
78  {OT_INT,
79  "Maximum number of SQP iterations"}},
80  {"min_iter",
81  {OT_INT,
82  "Minimum number of SQP iterations"}},
83  {"max_iter_ls",
84  {OT_INT,
85  "Maximum number of linesearch iterations"}},
86  {"tol_pr",
87  {OT_DOUBLE,
88  "Stopping criterion for primal infeasibility"}},
89  {"tol_du",
90  {OT_DOUBLE,
91  "Stopping criterion for dual infeasability"}},
92  {"c1",
93  {OT_DOUBLE,
94  "Armijo condition, coefficient of decrease in merit"}},
95  {"beta",
96  {OT_DOUBLE,
97  "Line-search parameter, restoration factor of stepsize"}},
98  {"merit_memory",
99  {OT_INT,
100  "Size of memory to store history of merit function values"}},
101  {"lbfgs_memory",
102  {OT_INT,
103  "Size of L-BFGS memory."}},
104  {"print_header",
105  {OT_BOOL,
106  "Print the header with problem statistics"}},
107  {"print_iteration",
108  {OT_BOOL,
109  "Print the iterations"}},
110  {"print_status",
111  {OT_BOOL,
112  "Print a status message after solving"}},
113  {"min_step_size",
114  {OT_DOUBLE,
115  "The size (inf-norm) of the step size should not become smaller than this."}},
116  {"hess_lag",
117  {OT_FUNCTION,
118  "Function for calculating the Hessian of the Lagrangian (autogenerated by default)"}},
119  {"jac_fg",
120  {OT_FUNCTION,
121  "Function for calculating the gradient of the objective and Jacobian of the constraints "
122  "(autogenerated by default)"}},
123  {"convexify_strategy",
124  {OT_STRING,
125  "NONE|regularize|eigen-reflect|eigen-clip. "
126  "Strategy to convexify the Lagrange Hessian before passing it to the solver."}},
127  {"convexify_margin",
128  {OT_DOUBLE,
129  "When using a convexification strategy, make sure that "
130  "the smallest eigenvalue is at least this (default: 1e-7)."}},
131  {"max_iter_eig",
132  {OT_DOUBLE,
133  "Maximum number of iterations to compute an eigenvalue decomposition (default: 50)."}},
134  {"elastic_mode",
135  {OT_BOOL,
136  "Enable the elastic mode which is used when the QP is infeasible (default: false)."}},
137  {"gamma_0",
138  {OT_DOUBLE,
139  "Starting value for the penalty parameter of elastic mode (default: 1)."}},
140  {"gamma_max",
141  {OT_DOUBLE,
142  "Maximum value for the penalty parameter of elastic mode (default: 1e20)."}},
143  {"gamma_1_min",
144  {OT_DOUBLE,
145  "Minimum value for gamma_1 (default: 1e-5)."}},
146  {"second_order_corrections",
147  {OT_BOOL,
148  "Enable second order corrections. "
149  "These are used when a step is considered bad by the merit function and constraint norm "
150  "(default: false)."}},
151  {"init_feasible",
152  {OT_BOOL,
153  "Initialize the QP subproblems with a feasible initial value (default: false)."}}
154  }
155 };
156 
157 void Sqpmethod::init(const Dict& opts) {
158  // Call the init method of the base class
159  Nlpsol::init(opts);
160 
161  // Default options
162  min_iter_ = 0;
163  max_iter_ = 50;
164  max_iter_ls_ = 3;
165  c1_ = 1e-4;
166  beta_ = 0.8;
167  merit_memsize_ = 4;
168  lbfgs_memory_ = 10;
169  tol_pr_ = 1e-6;
170  tol_du_ = 1e-6;
171  std::string hessian_approximation = "exact";
172  min_step_size_ = 1e-10;
173  std::string qpsol_plugin = "qpoases";
174  Dict qpsol_options;
175  print_header_ = true;
176  print_iteration_ = true;
177  print_status_ = true;
178  elastic_mode_ = false;
179  gamma_0_ = 1;
180  gamma_max_ = 1e20;
181  gamma_1_min_ = 1e-5;
182  so_corr_ = false;
183  init_feasible_ = false;
184 
185  std::string convexify_strategy = "none";
186  double convexify_margin = 1e-7;
187  casadi_int max_iter_eig = 200;
188 
189  // Read user options
190  for (auto&& op : opts) {
191  if (op.first=="max_iter") {
192  max_iter_ = op.second;
193  } else if (op.first=="min_iter") {
194  min_iter_ = op.second;
195  } else if (op.first=="max_iter_ls") {
196  max_iter_ls_ = op.second;
197  } else if (op.first=="c1") {
198  c1_ = op.second;
199  } else if (op.first=="beta") {
200  beta_ = op.second;
201  } else if (op.first=="merit_memory") {
202  merit_memsize_ = op.second;
203  } else if (op.first=="lbfgs_memory") {
204  lbfgs_memory_ = op.second;
205  } else if (op.first=="tol_pr") {
206  tol_pr_ = op.second;
207  } else if (op.first=="tol_du") {
208  tol_du_ = op.second;
209  } else if (op.first=="hessian_approximation") {
210  hessian_approximation = op.second.to_string();
211  } else if (op.first=="min_step_size") {
212  min_step_size_ = op.second;
213  } else if (op.first=="qpsol") {
214  qpsol_plugin = op.second.to_string();
215  } else if (op.first=="qpsol_options") {
216  qpsol_options = op.second;
217  } else if (op.first=="print_header") {
218  print_header_ = op.second;
219  } else if (op.first=="print_iteration") {
220  print_iteration_ = op.second;
221  } else if (op.first=="print_status") {
222  print_status_ = op.second;
223  } else if (op.first=="hess_lag") {
224  Function f = op.second;
225  casadi_assert_dev(f.n_in()==4);
226  casadi_assert_dev(f.n_out()==1);
227  set_function(f, "nlp_hess_l");
228  } else if (op.first=="jac_fg") {
229  Function f = op.second;
230  casadi_assert_dev(f.n_in()==2);
231  casadi_assert_dev(f.n_out()==4);
232  set_function(f, "nlp_jac_fg");
233  } else if (op.first=="convexify_strategy") {
234  convexify_strategy = op.second.to_string();
235  } else if (op.first=="convexify_margin") {
236  convexify_margin = op.second;
237  } else if (op.first=="max_iter_eig") {
238  max_iter_eig = op.second;
239  } else if (op.first=="elastic_mode") {
240  elastic_mode_ = op.second;
241  } else if (op.first=="gamma_0") {
242  gamma_0_ = op.second;
243  } else if (op.first=="gamma_max") {
244  gamma_max_ = op.second;
245  } else if (op.first=="gamma_1_min") {
246  gamma_1_min_ = op.second;
247  } else if (op.first=="second_order_corrections") {
248  so_corr_ = op.second;
249  } else if (op.first=="init_feasible") {
250  init_feasible_ = op.second;
251  }
252  }
253 
254  if (elastic_mode_) {
255  auto it = qpsol_options.find("error_on_fail");
256  if (it==qpsol_options.end()) {
257  qpsol_options["error_on_fail"] = false;
258  } else {
259  casadi_assert(!it->second,
260  "QP solver with setting error_on_fail is incompatible with elastic mode sqpmethod.");
261  }
262  }
263 
264  // Use exact Hessian?
265  exact_hessian_ = hessian_approximation =="exact";
266 
267  convexify_ = false;
268 
269  // Get/generate required functions
270  if (max_iter_ls_ || so_corr_) create_function("nlp_fg", {"x", "p"}, {"f", "g"});
271  // First order derivative information
272 
273  if (!has_function("nlp_jac_fg")) {
274  create_function("nlp_jac_fg", {"x", "p"},
275  {"f", "grad:f:x", "g", "jac:g:x"});
276  }
277  Asp_ = get_function("nlp_jac_fg").sparsity_out(3);
278 
279  if (exact_hessian_) {
280  if (!has_function("nlp_hess_l")) {
281  create_function("nlp_hess_l", {"x", "p", "lam:f", "lam:g"},
282  {"hess:gamma:x:x"}, {{"gamma", {"f", "g"}}});
283  }
284  Hsp_ = get_function("nlp_hess_l").sparsity_out(0);
285  casadi_assert(Hsp_.is_symmetric(), "Hessian must be symmetric");
286  if (convexify_strategy!="none") {
287  convexify_ = true;
288  Dict opts;
289  opts["strategy"] = convexify_strategy;
290  opts["margin"] = convexify_margin;
291  opts["max_iter_eig"] = max_iter_eig;
292  opts["verbose"] = verbose_;
294  }
295  } else {
297  }
298 
299  casadi_assert(!qpsol_plugin.empty(), "'qpsol' option has not been set");
300  qpsol_ = conic("qpsol", qpsol_plugin, {{"h", Hsp_}, {"a", Asp_}},
301  qpsol_options);
302  alloc(qpsol_);
303 
304  if (elastic_mode_) {
305  // Generate sparsity patterns for elastic mode
306  Sparsity Hsp_ela = Sparsity(Hsp_);
307  Sparsity Asp_ela = Sparsity(Asp_);
308 
309  std::vector<casadi_int> n_v = range(nx_);
310  Hsp_ela.enlarge(2*ng_ + nx_, 2*ng_ + nx_, n_v, n_v);
311 
312  Sparsity dsp = Sparsity::diag(ng_, ng_);
313  Asp_ela.appendColumns(dsp);
314  Asp_ela.appendColumns(dsp);
315 
316  // Allocate QP solver for elastic mode
317  Dict qpsol_ela_options = Dict(qpsol_options);
318 
319  casadi_assert(!qpsol_plugin.empty(), "'qpsol' option has not been set");
320  qpsol_ela_ = conic("qpsol_ela", qpsol_plugin, {{"h", Hsp_ela}, {"a", Asp_ela}},
321  qpsol_ela_options);
322  alloc(qpsol_ela_);
323  }
324 
325 
326  // BFGS?
327  if (!exact_hessian_) {
328  alloc_w(2*nx_); // casadi_bfgs
329  }
330 
331  // Header
332  if (print_header_) {
333  print("-------------------------------------------\n");
334  print("This is casadi::Sqpmethod.\n");
335  if (exact_hessian_) {
336  print("Using exact Hessian\n");
337  } else {
338  print("Using limited memory BFGS Hessian approximation\n");
339  }
340  print("Number of variables: %9d\n", nx_);
341  print("Number of constraints: %9d\n", ng_);
342  print("Number of nonzeros in constraint Jacobian: %9d\n", Asp_.nnz());
343  print("Number of nonzeros in Lagrangian Hessian: %9d\n", Hsp_.nnz());
344  print("\n");
345  }
346 
347 
348  set_sqpmethod_prob();
349  // Allocate memory
350  casadi_int sz_w, sz_iw;
351  casadi_sqpmethod_work(&p_, &sz_iw, &sz_w);
352  alloc_iw(sz_iw, true);
353  alloc_w(sz_w, true);
354  if (convexify_) {
357  }
358 }
359 
360 void Sqpmethod::set_sqpmethod_prob() {
361  p_.sp_h = Hsp_;
362  p_.sp_a = Asp_;
365  p_.nlp = &p_nlp_;
367  p_.so_corr = so_corr_;
368 }
369 
370 void Sqpmethod::set_work(void* mem, const double**& arg, double**& res,
371  casadi_int*& iw, double*& w) const {
372  auto m = static_cast<SqpmethodMemory*>(mem);
373 
374  // Set work in base classes
375  Nlpsol::set_work(mem, arg, res, iw, w);
376 
377  m->d.prob = &p_;
378  casadi_sqpmethod_set_work(&m->d, &arg, &res, &iw, &w);
379 
380  m->iter_count = -1;
381 }
382 
383 int Sqpmethod::init_mem(void* mem) const {
384  if (Nlpsol::init_mem(mem)) return 1;
385  auto m = static_cast<SqpmethodMemory*>(mem);
386 
387  if (convexify_) m->add_stat("convexify");
388  m->add_stat("BFGS");
389  m->add_stat("QP");
390  m->add_stat("linesearch");
391  m->mem_qp = qpsol_->checkout();
392  return 0;
393 }
394 
395 void Sqpmethod::free_mem(void* mem) const {
396  auto m = static_cast<SqpmethodMemory*>(mem);
397  if (m->mem_qp >= 0) qpsol_.release(m->mem_qp);
398  delete static_cast<SqpmethodMemory*>(mem);
399 }
400 
401 int Sqpmethod::solve(void* mem) const {
402  auto m = static_cast<SqpmethodMemory*>(mem);
403  auto d_nlp = &m->d_nlp;
404  auto d = &m->d;
405 
406  // Number of SQP iterations
407  m->iter_count = 0;
408 
409  // Number of line-search iterations
410  casadi_int ls_iter = 0;
411 
412  // Last linesearch successfull
413  bool ls_success = true;
414 
415  // Last second order correction successfull
416  bool so_succes = false;
417 
418  // Reset
419  m->merit_ind = 0;
420  m->sigma = 0.; // NOTE: Move this into the main optimization loop
421  m->reg = 0;
422 
423  // Default stepsize
424  double t = 0;
425 
426  // For seeds
427  const double one = 1.;
428 
429  // Info for printing
430  std::string info = "";
431 
432  // gamma_1
433  double gamma_1 = 0.0; // Fix may be used uninitialized warning
434 
435  // ela_it
436  casadi_int ela_it = -1;
437 
438  casadi_clear(d->dx, nx_);
439 
440  // MAIN OPTIMIZATION LOOP
441  while (true) {
442  // Evaluate f, g and first order derivative information
443  m->arg[0] = d_nlp->z;
444  m->arg[1] = d_nlp->p;
445  m->res[0] = &d_nlp->objective;
446  m->res[1] = d->gf;
447  m->res[2] = d_nlp->z + nx_;
448  m->res[3] = d->Jk;
449  switch (calc_function(m, "nlp_jac_fg")) {
450  case -1:
451  m->return_status = "Non_Regular_Sensitivities";
452  m->unified_return_status = SOLVER_RET_NAN;
453  if (print_status_)
454  print("MESSAGE(sqpmethod): No regularity of sensitivities at current point.\n");
455  return 1;
456  case 0:
457  break;
458  default:
459  return 1;
460  }
461  // Evaluate the gradient of the Lagrangian
462  casadi_copy(d->gf, nx_, d->gLag);
463  casadi_mv(d->Jk, Asp_, d_nlp->lam+nx_, d->gLag, true);
464  casadi_axpy(nx_, 1., d_nlp->lam, d->gLag);
465 
466  // Primal infeasability
467  double pr_inf = casadi_max_viol(nx_+ng_, d_nlp->z, d_nlp->lbz, d_nlp->ubz);
468 
469  // inf-norm of Lagrange gradient
470  double du_inf = casadi_norm_inf(nx_, d->gLag);
471 
472  // inf-norm of step
473  double dx_norminf = casadi_norm_inf(nx_, d->dx);
474 
475  // Printing information about the actual iterate
476  if (print_iteration_) {
477  if (m->iter_count % 10 == 0) print_iteration();
478  print_iteration(m->iter_count, d_nlp->objective, pr_inf, du_inf, dx_norminf,
479  m->reg, ls_iter, ls_success, so_succes, info);
480  info = "";
481  so_succes = false;
482  }
483 
484  // Callback function
485  if (callback(m)) {
486  if (print_status_) print("WARNING(sqpmethod): Aborted by callback...\n");
487  m->return_status = "User_Requested_Stop";
488  break;
489  }
490 
491  // Checking convergence criteria
492  if (m->iter_count >= min_iter_ && pr_inf < tol_pr_ && du_inf < tol_du_) {
493  if (print_status_)
494  print("MESSAGE(sqpmethod): Convergence achieved after %d iterations\n", m->iter_count);
495  m->return_status = "Solve_Succeeded";
496  m->success = true;
497  break;
498  }
499 
500  if (m->iter_count >= max_iter_) {
501  if (print_status_) print("MESSAGE(sqpmethod): Maximum number of iterations reached.\n");
502  m->return_status = "Maximum_Iterations_Exceeded";
503  m->unified_return_status = SOLVER_RET_LIMITED;
504  break;
505  }
506 
507  if (m->iter_count >= 1 && m->iter_count >= min_iter_ && dx_norminf <= min_step_size_) {
508  if (print_status_) print("MESSAGE(sqpmethod): Search direction becomes too small without "
509  "convergence criteria being met.\n");
510  m->return_status = "Search_Direction_Becomes_Too_Small";
511  break;
512  }
513 
514  if (exact_hessian_) {
515  // Update/reset exact Hessian
516  m->arg[0] = d_nlp->z;
517  m->arg[1] = d_nlp->p;
518  m->arg[2] = &one;
519  m->arg[3] = d_nlp->lam + nx_;
520  m->res[0] = d->Bk;
521  if (calc_function(m, "nlp_hess_l")) return 1;
522  if (convexify_) {
523  ScopedTiming tic(m->fstats.at("convexify"));
524  if (casadi_convexify_eval(&convexify_data_.config, d->Bk, d->Bk, m->iw, m->w)) return 1;
525  }
526  } else if (m->iter_count==0) {
527  ScopedTiming tic(m->fstats.at("BFGS"));
528  // Initialize BFGS
529  casadi_fill(d->Bk, Hsp_.nnz(), 1.);
530  casadi_bfgs_reset(Hsp_, d->Bk);
531  } else {
532  ScopedTiming tic(m->fstats.at("BFGS"));
533  // Update BFGS
534  if (m->iter_count % lbfgs_memory_ == 0) casadi_bfgs_reset(Hsp_, d->Bk);
535  // Update the Hessian approximation
536  casadi_bfgs(Hsp_, d->Bk, d->dx, d->gLag, d->gLag_old, m->w);
537  }
538 
539  // Formulate the QP
540  casadi_copy(d_nlp->lbz, nx_+ng_, d->lbdz);
541  casadi_axpy(nx_+ng_, -1., d_nlp->z, d->lbdz);
542  casadi_copy(d_nlp->ubz, nx_+ng_, d->ubdz);
543  casadi_axpy(nx_+ng_, -1., d_nlp->z, d->ubdz);
544 
545  // Initial guess
546  casadi_copy(d_nlp->lam, nx_+ng_, d->dlam);
547  casadi_clear(d->dx, nx_);
548 
549  // Make initial guess feasable
550  /*if (init_feasible_) {
551  for (casadi_int i = 0; i < nx_; ++i) {
552  if (d->lbdz[i] > 0) d->dx[i] = d->lbdz[i];
553  else if (d->ubdz[i] < 0) d->dx[i] = d->ubdz[i];
554  }
555  }*/
556 
557  // Increase counter
558  m->iter_count++;
559 
560  // Solve the QP
561  int ret = solve_QP(m, d->Bk, d->gf, d->lbdz, d->ubdz, d->Jk, d->dx, d->dlam, 0);
562 
563  // Elastic mode calculations
564  if (elastic_mode_) {
565  if (ret == 0) {
566  ela_it = -1;
567  } else if (ret == SOLVER_RET_INFEASIBLE) {
568  if (ela_it == -1) {
569  ela_it = 0;
570  gamma_1 = calc_gamma_1(m);
571  }
572  ret = solve_elastic_mode(m, &ela_it, gamma_1, ls_iter, ls_success, so_succes,
573  pr_inf, du_inf, dx_norminf, &info, 0);
574 
575  if (ret == SOLVER_RET_INFEASIBLE) continue;
576  } else if (ela_it == -1) {
577  double pi_inf = casadi_norm_inf(ng_, d->dlam+nx_);
578  gamma_1 = calc_gamma_1(m);
579 
580  if (pi_inf > gamma_1) {
581  ela_it = 0;
582  ret = solve_elastic_mode(m, &ela_it, gamma_1, ls_iter, ls_success, so_succes,
583  pr_inf, du_inf, dx_norminf, &info, 0);
584  if (ret == SOLVER_RET_INFEASIBLE) continue;
585  }
586  }
587  }
588 
589  // Detecting indefiniteness
590  double gain = casadi_bilin(d->Bk, Hsp_, d->dx, d->dx);
591  if (gain < 0) {
592  if (print_status_) print("WARNING(sqpmethod): Indefinite Hessian detected\n");
593  }
594 
595  // Pre calculatations for second order corrections and linesearch
596  double l1_infeas, l1;
597  if (so_corr_ || (max_iter_ls_>0)) {
598  // Calculate penalty parameter of merit function
599  m->sigma = std::fmax(m->sigma, 1.01*casadi_norm_inf(nx_+ng_, d->dlam));
600 
601  // Calculate L1-merit function in the actual iterate
602  l1_infeas = casadi_sum_viol(nx_+ng_, d_nlp->z, d_nlp->lbz, d_nlp->ubz);
603  l1 = d_nlp->objective + m->sigma * l1_infeas;
604  }
605 
606  // Pre calculations for second order corrections
607  double l1_infeas_cand, l1_cand, fk_cand;
608  l1_infeas_cand = 0;
609  if (so_corr_) {
610  // Take candidate step
611  casadi_copy(d_nlp->z, nx_, d->z_cand);
612  casadi_axpy(nx_, 1., d->dx, d->z_cand);
613 
614  // Evaluating objective and constraints
615  m->arg[0] = d->z_cand;
616  m->arg[1] = d_nlp->p;
617  m->res[0] = &fk_cand;
618  m->res[1] = d->z_cand + nx_;
619  if (calc_function(m, "nlp_fg")) {
620  l1_cand = -inf; // Make sure the second order corrections are not used!
621  } else {
622  l1_infeas_cand = casadi_sum_viol(nx_+ng_, d->z_cand, d_nlp->lbz, d_nlp->ubz);
623  l1_cand = fk_cand + m->sigma*l1_infeas_cand;
624  }
625  }
626 
627  if (so_corr_ && l1_cand > l1 && l1_infeas_cand > l1_infeas) {
628  // Copy in case of a fail
629  casadi_copy(d->dx, nx_, d->temp_sol);
630  casadi_copy(d->dlam, nx_+ng_, d->temp_sol+nx_);
631 
632  // Add gradient times proposal step to bounds
633  casadi_clear(d->lbdz, nx_+ng_);
634  casadi_mv(d->Jk, Asp_, d->dx, d->lbdz+nx_, false);
635  casadi_copy(d->lbdz, nx_+ng_, d->ubdz);
636 
637  // Add bounds
638  casadi_axpy(nx_+ng_, 1., d_nlp->lbz, d->lbdz);
639  casadi_axpy(nx_+ng_, 1., d_nlp->ubz, d->ubdz);
640 
641  // Subtract constraints in candidate step from bounds
642  casadi_axpy(nx_, -1., d_nlp->z, d->lbdz);
643  casadi_axpy(ng_, -1., d->z_cand+nx_, d->lbdz+nx_);
644  casadi_axpy(nx_, -1., d_nlp->z, d->ubdz);
645  casadi_axpy(ng_, -1., d->z_cand+nx_, d->ubdz+nx_);
646 
647  int ret = SOLVER_RET_INFEASIBLE;
648  // Second order corrections without elastic mode (ela_it is -1 if elastic mode is turned off)
649  if (ela_it == -1) {
650  // Initial guess
651  casadi_copy(d_nlp->lam, nx_+ng_, d->dlam);
652  casadi_clear(d->dx, nx_);
653 
654  // Make initial guess feasible
655  if (init_feasible_) {
656  for (casadi_int i = 0; i < nx_; ++i) {
657  if (d->lbdz[i] > 0) d->dx[i] = d->lbdz[i];
658  else if (d->ubdz[i] < 0) d->dx[i] = d->ubdz[i];
659  }
660  }
661 
662  // Solve the QP
663  ret = solve_QP(m, d->Bk, d->gf, d->lbdz, d->ubdz, d->Jk,
664  d->dx, d->dlam, 1);
665  }
666 
667  if (elastic_mode_ && (ret == SOLVER_RET_INFEASIBLE || ela_it != -1)) {
668  // Second order corrections in elastic mode
669  if (ela_it == -1) {
670  ela_it = 0;
671  gamma_1 = calc_gamma_1(m);
672  }
673 
674  ret = solve_elastic_mode(m, &ela_it, gamma_1, ls_iter, ls_success, so_succes,
675  pr_inf, du_inf, dx_norminf, &info, 1);
676 
677  }
678 
679  // Fallback on previous solution if the second order correction failed
680  if (ret != 0) {
681  casadi_copy(d->temp_sol, nx_, d->dx);
682  casadi_copy(d->temp_sol+nx_, nx_+ng_, d->dlam);
683  so_succes = false;
684  } else {
685  // Check if corrected step is better than the original one using the merit function
686  double l1_cand_norm = l1_cand;
687  double l1_cand_soc;
688 
689  // Take candidate step
690  casadi_copy(d_nlp->z, nx_, d->z_cand);
691  casadi_axpy(nx_, 1., d->dx, d->z_cand);
692 
693  // Evaluating objective and constraints
694  m->arg[0] = d->z_cand;
695  m->arg[1] = d_nlp->p;
696  m->res[0] = &fk_cand;
697  m->res[1] = d->z_cand + nx_;
698  if (calc_function(m, "nlp_fg")) {
699  l1_cand_soc = inf; // Make sure the second order corrections are not used!
700  } else {
701  l1_infeas_cand = casadi_sum_viol(nx_+ng_, d->z_cand, d_nlp->lbz, d_nlp->ubz);
702  l1_cand_soc = fk_cand + m->sigma*l1_infeas_cand;
703  }
704 
705  if (l1_cand_norm < l1_cand_soc) {
706  // Copy normal step if merit function increases
707  casadi_copy(d->temp_sol, nx_, d->dx);
708  casadi_copy(d->temp_sol+nx_, nx_+ng_, d->dlam);
709  so_succes = false;
710  } else {
711  so_succes = true;
712  }
713  }
714  }
715 
716  if (max_iter_ls_>0) { // max_iter_ls_== 0 disables line-search
717  // Line-search
718  if (verbose_) print("Starting line-search\n");
719  ScopedTiming tic(m->fstats.at("linesearch"));
720 
721  // Reset line-search counter, success marker
722  ls_iter = 0;
723  ls_success = true;
724 
725  // Stepsize
726  t = 1.0;
727 
728  // Right-hand side of Armijo condition
729  double tl1 = casadi_dot(nx_, d->dx, d->gf) - m->sigma*l1_infeas;
730 
731  // Storing the actual merit function value in a list
732  d->merit_mem[m->merit_ind] = l1;
733  m->merit_ind++;
734  m->merit_ind %= merit_memsize_;
735  // Calculating maximal merit function value so far
736  //double meritmax = casadi_vfmax(d->merit_mem+1,
737  // std::min(merit_memsize_, static_cast<casadi_int>(m->iter_count))-1, d->merit_mem[0]);
738 
739  // Line-search loop
740  while (true) {
741  // Increase counter
742  ls_iter++;
743 
744  // Candidate step
745  casadi_copy(d_nlp->z, nx_, d->z_cand);
746  casadi_axpy(nx_, t, d->dx, d->z_cand);
747 
748  // Evaluating objective and constraints
749  if (!so_corr_ || !so_succes) {
750  m->arg[0] = d->z_cand;
751  m->arg[1] = d_nlp->p;
752  m->res[0] = &fk_cand;
753  m->res[1] = d->z_cand + nx_;
754  if (calc_function(m, "nlp_fg")) {
755  // Avoid infinite recursion
756  if (ls_iter == max_iter_ls_) {
757  ls_success = false;
758  l1_infeas = nan;
759  break;
760  }
761  // line-search failed, skip iteration
762  t = beta_ * t;
763  continue;
764  }
765  }
766 
767  // Calculating merit-function in candidate
768  l1_cand = fk_cand + m->sigma*casadi_sum_viol(nx_+ng_, d->z_cand, d_nlp->lbz, d_nlp->ubz);
769  if (l1_cand <= l1 + t * c1_ * tl1) {
770  break;
771  }
772 
773  // Line-search not successful, but we accept it.
774  if (ls_iter == max_iter_ls_) {
775  ls_success = false;
776  break;
777  }
778 
779  // Backtracking
780  t = beta_ * t;
781  }
782 
783  // Candidate accepted, update dual variables
784  casadi_scal(nx_ + ng_, 1-t, d_nlp->lam);
785  casadi_axpy(nx_ + ng_, t, d->dlam, d_nlp->lam);
786 
787  casadi_scal(nx_, t, d->dx);
788  } else {
789  // Full step
790  casadi_copy(d->dlam, nx_ + ng_, d_nlp->lam);
791  }
792 
793  // Take step
794  casadi_axpy(nx_, 1., d->dx, d_nlp->z);
795 
796  if (!exact_hessian_) {
797  // Evaluate the gradient of the Lagrangian with the old x but new lam (for BFGS)
798  casadi_copy(d->gf, nx_, d->gLag_old);
799  casadi_mv(d->Jk, Asp_, d_nlp->lam+nx_, d->gLag_old, true);
800  casadi_axpy(nx_, 1., d_nlp->lam, d->gLag_old);
801  }
802 
803  // If linesearch failed enter elastic mode
804  if (!ls_success && elastic_mode_ && (max_iter_ls_>0) && ela_it == -1) {
805  ela_it = 0;
806  gamma_1 = calc_gamma_1(m);;
807  }
808  }
809 
810  return 0;
811 }
812 
814  print("%4s %14s %9s %9s %9s %7s %2s %7s\n", "iter", "objective", "inf_pr",
815  "inf_du", "||d||", "lg(rg)", "ls", "info");
816 }
817 
818 void Sqpmethod::print_iteration(casadi_int iter, double obj,
819  double pr_inf, double du_inf,
820  double dx_norm, double rg,
821  casadi_int ls_trials, bool ls_success,
822  bool so_succes, std::string info) const {
823  print("%4d %14.6e %9.2e %9.2e %9.2e ", iter, obj, pr_inf, du_inf, dx_norm);
824  if (rg>0) {
825  print("%7.2f ", log10(rg));
826  } else {
827  print("%7s ", "-");
828  }
829 
830  print("%2d", ls_trials);
831  if (!ls_success) {
832  print("F");
833  } else {
834  print(" ");
835  }
836 
837  if (so_succes) {
838  print(" - SOC");
839  }
840 
841  print(" - ");
842  print(info.c_str());
843  print("\n");
844 }
845 
846 int Sqpmethod::solve_QP(SqpmethodMemory* m, const double* H, const double* g,
847  const double* lbdz, const double* ubdz, const double* A,
848  double* x_opt, double* dlam, int mode) const {
849  ScopedTiming tic(m->fstats.at("QP"));
850  // Inputs
851  std::fill_n(m->arg, qpsol_.n_in(), nullptr);
852  m->arg[CONIC_H] = H;
853  m->arg[CONIC_G] = g;
854  m->arg[CONIC_X0] = x_opt;
855  m->arg[CONIC_LAM_X0] = dlam;
856  m->arg[CONIC_LAM_A0] = dlam + nx_;
857  m->arg[CONIC_LBX] = lbdz;
858  m->arg[CONIC_UBX] = ubdz;
859  m->arg[CONIC_A] = A;
860  m->arg[CONIC_LBA] = lbdz+nx_;
861  m->arg[CONIC_UBA] = ubdz+nx_;
862 
863  // Outputs
864  std::fill_n(m->res, qpsol_.n_out(), nullptr);
865  m->res[CONIC_X] = x_opt;
866  m->res[CONIC_LAM_X] = dlam;
867  m->res[CONIC_LAM_A] = dlam + nx_;
868  double cost;
869  m->res[CONIC_COST] = &cost;
870 
871  // Solve the QP
872  qpsol_(m->arg, m->res, m->iw, m->w, m->mem_qp);
873  auto m_qpsol = static_cast<ConicMemory*>(qpsol_->memory(m->mem_qp));
874 
875  // Check if the QP was infeasible for elastic mode
876  if (!m_qpsol->d_qp.success) {
877  if ((elastic_mode_ && m_qpsol->d_qp.unified_return_status == SOLVER_RET_INFEASIBLE)
878  || (mode == 1)) {
879  return SOLVER_RET_INFEASIBLE;
880  }
881  }
882 
883  if (verbose_) print("QP solved\n");
884  return 0;
885 }
886 
887 int Sqpmethod::solve_ela_QP(SqpmethodMemory* m, const double* H, const double* g,
888  const double* lbdz, const double* ubdz, const double* A,
889  double* x_opt, double* dlam) const {
890  ScopedTiming tic(m->fstats.at("QP"));
891  // Inputs
892  std::fill_n(m->arg, qpsol_ela_.n_in(), nullptr);
893  m->arg[CONIC_H] = H;
894  m->arg[CONIC_G] = g;
895  m->arg[CONIC_X0] = x_opt;
896  m->arg[CONIC_LAM_X0] = dlam;
897  m->arg[CONIC_LAM_A0] = dlam + nx_ + 2*ng_;
898  m->arg[CONIC_LBX] = lbdz;
899  m->arg[CONIC_UBX] = ubdz;
900  m->arg[CONIC_A] = A;
901  m->arg[CONIC_LBA] = lbdz + nx_ + 2*ng_;
902  m->arg[CONIC_UBA] = ubdz + nx_ + 2*ng_;
903 
904  // Outputs
905  std::fill_n(m->res, qpsol_ela_.n_out(), nullptr);
906  m->res[CONIC_X] = x_opt;
907  m->res[CONIC_LAM_X] = dlam;
908  m->res[CONIC_LAM_A] = dlam + nx_ + 2*ng_;
909  double cost;
910  m->res[CONIC_COST] = &cost;
911 
912  // Solve the QP
913  qpsol_ela_(m->arg, m->res, m->iw, m->w, 0);
914  auto m_qpsol_ela = static_cast<ConicMemory*>(qpsol_ela_->memory(0));
915 
916  // Check if the QP was infeasible
917  if (!m_qpsol_ela->d_qp.success) {
918  if (m_qpsol_ela->d_qp.unified_return_status == SOLVER_RET_INFEASIBLE) {
919  return SOLVER_RET_INFEASIBLE;
920  }
921  }
922 
923  if (verbose_) print("Elastic QP solved\n");
924 
925  return 0;
926 }
927 
929  casadi_int* ela_it, double gamma_1,
930  casadi_int ls_iter, bool ls_success, bool so_succes, double pr_inf,
931  double du_inf, double dx_norminf, std::string* info, int mode) const {
932  auto d_nlp = &m->d_nlp;
933  auto d = &m->d;
934 
935  if (mode != 0 && mode != 1) casadi_error("Wrong mode provided to solve_elastic_mode.");
936 
937  double gamma = 0.;
938 
939  if (mode == 0) (*ela_it)++;
940 
941  // Temp datastructs for data copy
942  double *temp_1, *temp_2;
943 
944  // Make larger jacobian (has 2 extra diagonal matrices with -1 and 1 respectively)
945  temp_1 = d->Jk + Asp_.nnz();
946  casadi_fill(temp_1, ng_, -1.);
947  temp_1 += ng_;
948  casadi_fill(temp_1, ng_, 1.);
949 
950  // Initialize bounds
951  temp_1 = d->lbdz + nx_;
952  temp_2 = d->lbdz + nx_+2*ng_;
953  casadi_copy(temp_1, ng_, temp_2);
954  casadi_clear(temp_1, 2*ng_);
955 
956  temp_1 = d->ubdz + nx_;
957  temp_2 = d->ubdz + nx_+2*ng_;
958  casadi_copy(temp_1, ng_, temp_2);
959  casadi_fill(temp_1, 2*ng_, inf);
960 
961  if (*ela_it > 1) {
962  gamma = pow(10, *ela_it * (*ela_it - 1) / 2) * gamma_1;
963  } else {
964  gamma = gamma_1;
965  }
966 
967  if (gamma > gamma_max_) {
968  casadi_error("Error in elastic mode of QP solver."
969  "Gamma became larger than gamma_max.");
970  }
971 
972  if (mode == 0 && print_iteration_) {
973  ls_iter = 0;
974  ls_success = true;
975  print_iteration(m->iter_count, d_nlp->objective, pr_inf, du_inf, dx_norminf,
976  m->reg, ls_iter, ls_success, so_succes, *info);
977  }
978 
979  // Make larger gradient (has gamma for slack variables)
980  temp_1 = d->gf + nx_;
981  casadi_fill(temp_1, 2 * ng_, gamma);
982 
983  // Initial guess
984  casadi_clear(d->dlam, nx_ + 3 * ng_);
985  casadi_copy(d_nlp->lam, nx_, d->dlam);
986  casadi_copy(d_nlp->lam + nx_, ng_, d->dlam + nx_ + 2 * ng_);
987  casadi_clear(d->dx, nx_ + 2 * ng_);
988 
989  // Make initial guess feasible on x values
990  if (init_feasible_) {
991  for (casadi_int i = 0; i < nx_; ++i) {
992  if (d->lbdz[i] > 0) d->dx[i] = d->lbdz[i];
993  else if (d->ubdz[i] < 0) d->dx[i] = d->ubdz[i];
994  }
995 
996  // Make initial guess feasible on constraints by altering slack variables
997  casadi_mv(d->Jk, Asp_, d->dx, d->temp_mem, false);
998  for (casadi_int i = 0; i < ng_; ++i) {
999  if (d->ubdz[nx_+2*ng_+i]-d->temp_mem[i] < 0) {
1000  d->dx[nx_+i] = -d->ubdz[nx_+2*ng_+i]+d->temp_mem[i];
1001  }
1002 
1003  if (d->lbdz[nx_+2*ng_+i]-d->temp_mem[i] > 0) {
1004  d->dx[nx_+ng_+i] = d->lbdz[nx_+2*ng_+i]-d->temp_mem[i];
1005  }
1006  }
1007  }
1008 
1009  // Solve the QP
1010  int ret = solve_ela_QP(m, d->Bk, d->gf, d->lbdz, d->ubdz, d->Jk, d->dx, d->dlam);
1011 
1012  if (mode == 0) *info = "Elastic mode QP (gamma = " + str(gamma) + ")";
1013 
1014  // Copy constraint dlam to the right place
1015  casadi_copy(d->dlam+nx_+2*ng_, ng_, d->dlam+nx_);
1016 
1017  return ret;
1018 }
1019 
1021  auto d = &m->d;
1022  return std::max(gamma_0_*casadi_norm_inf(nx_, d->gf), gamma_1_min_);
1023 }
1024 
1027 
1028  if (max_iter_ls_ || so_corr_) g.add_dependency(get_function("nlp_fg"));
1029  g.add_dependency(get_function("nlp_jac_fg"));
1030  if (exact_hessian_) g.add_dependency(get_function("nlp_hess_l"));
1031  if (calc_f_ || calc_g_ || calc_lam_x_ || calc_lam_p_)
1032  g.add_dependency(get_function("nlp_grad"));
1036 }
1037 
1040  codegen_body_enter(g);
1041  // From nlpsol
1042 
1043  g.local("d", "struct casadi_sqpmethod_data*");
1044  g.init_local("d", "&" + codegen_mem(g));
1045  g.local("p", "struct casadi_sqpmethod_prob");
1046 
1047  g << "d->prob = &p;\n";
1048  g << "p.sp_h = " << g.sparsity(Hsp_) << ";\n";
1049  g << "p.sp_a = " << g.sparsity(Asp_) << ";\n";
1050  g << "p.merit_memsize = " << merit_memsize_ << ";\n";
1051  g << "p.max_iter_ls = " << max_iter_ls_ << ";\n";
1052  g << "p.nlp = &p_nlp;\n";
1053  g << "p.elastic_mode = " << elastic_mode_ << ";\n";
1054  g << "p.so_corr = " << so_corr_ << ";\n";
1055  g << "casadi_sqpmethod_set_work(d, &arg, &res, &iw, &w);\n";
1056 
1057  if (elastic_mode_) {
1058  g.local("gamma_1", "double");
1059  g.local("ela_it", "casadi_int");
1060  g.init_local("ela_it", "-1");
1061  g.local("temp_norm", "double");
1062  }
1063  g.local("ret", "int");
1064 
1065  g.local("iter_count", "casadi_int");
1066  g.init_local("iter_count", "0");
1067  if (max_iter_ls_ || so_corr_) {
1068  //g.local("merit_ind", "casadi_int");
1069  //g.init_local("merit_ind", "0");
1070  g.local("sigma", "casadi_real");
1071  g.init_local("sigma", "0.0");
1072  }
1073  if (max_iter_ls_) {
1074  g.local("ls_iter", "casadi_int");
1075  g.init_local("ls_iter", "0");
1076  g.local("t", "casadi_real");
1077  g.init_local("t", "0.0");
1078  }
1079 
1080  if (elastic_mode_ && max_iter_ls_) {
1081  g.local("ls_success", "casadi_int");
1082  g.init_local("ls_success", "1");
1083  }
1084 
1085  g << g.clear("d->dx", nx_) << "\n";
1086  g.comment("MAIN OPTIMIZATION LOOP");
1087  g << "while (1) {\n";
1088  g.comment("Evaluate f, g and first order derivative information");
1089  g << "d->arg[0] = d_nlp.z;\n";
1090  g << "d->arg[1] = d_nlp.p;\n";
1091  g << "d->res[0] = &d_nlp.objective;\n";
1092  g << "d->res[1] = d->gf;\n";
1093  g << "d->res[2] = d_nlp.z+" + str(nx_) + ";\n";
1094  g << "d->res[3] = d->Jk;\n";
1095  std::string nlp_jac_fg = g(get_function("nlp_jac_fg"), "d->arg", "d->res", "d->iw", "d->w");
1096  g << "if (" + nlp_jac_fg + ") return 1;\n";
1097  g.comment("Evaluate the gradient of the Lagrangian");
1098  g << g.copy("d->gf", nx_, "d->gLag") << "\n";
1099  g << g.mv("d->Jk", Asp_, "d_nlp.lam+"+str(nx_), "d->gLag", true) << "\n";
1100  g << g.axpy(nx_, "1.0", "d_nlp.lam", "d->gLag") << "\n";
1101  g.comment("Primal infeasability");
1102  g.local("pr_inf", "casadi_real");
1103  g << "pr_inf = " << g.max_viol(nx_+ng_, "d_nlp.z", "d_nlp.lbz", "d_nlp.ubz") << ";\n";
1104  g.comment("inf-norm of lagrange gradient");
1105  g.local("du_inf", "casadi_real");
1106  g << "du_inf = " << g.norm_inf(nx_, "d->gLag") << ";\n";
1107  g.comment("inf-norm of step");
1108  g.local("dx_norminf", "casadi_real");
1109  g << "dx_norminf = " << g.norm_inf(nx_, "d->dx") << ";\n";
1110  g.comment("Checking convergence criteria");
1111  g << "if (iter_count >= " << min_iter_ << " && pr_inf < " << tol_pr_ <<
1112  " && du_inf < " << tol_du_ << ") break;\n";
1113  g << "if (iter_count >= " << max_iter_ << ") break;\n";
1114  g << "if (iter_count >= 1 && iter_count >= " << min_iter_ << " && dx_norminf <= " <<
1115  min_step_size_ << ") break;\n";
1116  if (exact_hessian_) {
1117  g.comment("Update/reset exact Hessian");
1118  g << "d->arg[0] = d_nlp.z;\n";
1119  g << "d->arg[1] = d_nlp.p;\n";
1120  g.local("one", "const casadi_real");
1121  g.init_local("one", "1");
1122  g << "d->arg[2] = &one;\n";
1123  g << "d->arg[3] = d_nlp.lam+" + str(nx_) + ";\n";
1124  g << "d->res[0] = d->Bk;\n";
1125  std::string nlp_hess_l = g(get_function("nlp_hess_l"), "d->arg", "d->res", "d->iw", "d->w");
1126  g << "if (" + nlp_hess_l + ") return 1;\n";
1127 
1128  if (convexify_) {
1129  std::string ret = g.convexify_eval(convexify_data_, "d->Bk", "d->Bk", "d->iw", "d->w");
1130  g << "if (" << ret << ") return 1;\n";
1131  }
1132  } else {
1133  g << "if (iter_count==0) {\n";
1134  g.comment("Initialize BFGS");
1135  g << g.fill("d->Bk", Hsp_.nnz(), "1.") << "\n";
1136  g << "casadi_bfgs_reset(p.sp_h, d->Bk);\n";
1137  g << "} else {\n";
1138  g.comment("Update BFGS");
1139  g << "if (iter_count % " << lbfgs_memory_ << "==0) ";
1140  g << "casadi_bfgs_reset(p.sp_h, d->Bk);\n";
1141  g.comment("Update the Hessian approximation");
1142  g << "casadi_bfgs(p.sp_h, d->Bk, d->dx, d->gLag, d->gLag_old, d->w);\n";
1143  g << "}\n";
1144  }
1145 
1146  g.comment("Formulate the QP");
1147  g << g.copy("d_nlp.lbz", nx_+ng_, "d->lbdz") << "\n";
1148  g << g.axpy(nx_+ng_, "-1.0", "d_nlp.z", "d->lbdz") << "\n";
1149  g << g.copy("d_nlp.ubz", nx_+ng_, "d->ubdz") << "\n";
1150  g << g.axpy(nx_+ng_, "-1.0", "d_nlp.z", "d->ubdz") << "\n";
1151  g.comment("Initial guess");
1152  g << g.copy("d_nlp.lam", nx_+ng_, "d->dlam") << "\n";
1153  g << g.clear("d->dx", nx_) << "\n";
1154 
1155  if (init_feasible_) {
1156  g.comment("Make initial guess feasible");
1157  g << "for (casadi_int i = 0; i < " << nx_ << "; ++i) {\n";
1158  g << "if (d->lbdz[i] > 0) d->dx[i] = d->lbdz[i];\n";
1159  g << "else if (d->ubdz[i] < 0) d->dx[i] = d->ubdz[i];\n";
1160  g << "}\n";
1161  }
1162 
1163  g.comment("Increase counter");
1164  g << "iter_count++;\n";
1165  g.comment("Solve the QP");
1166  codegen_qp_solve(g, "d->Bk", "d->gf", "d->lbdz", "d->ubdz", "d->Jk", "d->dx", "d->dlam", 0);
1167 
1168  if (elastic_mode_) {
1169  g.comment("Elastic mode calculations");
1170  g << "if (ret == " << 0 << ") {\n";
1171  g << "ela_it = -1;\n";
1172  g << "} else if (ret == " << SOLVER_RET_INFEASIBLE << ") {\n";
1173  g << "if (ela_it == -1) {\n";
1174  g << "ela_it = 0;\n";
1176  g << "}\n";
1178  g << "if (ret == " << SOLVER_RET_INFEASIBLE << ") continue;\n";
1179 
1180  g << "} else if (ela_it == -1) {\n";
1181  g << "double pi_inf = " << g.norm_inf(ng_, "d->dlam+" + str(nx_)) << ";\n";
1183  g << "if (pi_inf > gamma_1) {\n";
1184  g << "ela_it = 0;\n";
1186  g << "if (ret == " << SOLVER_RET_INFEASIBLE << ") continue;\n";
1187  g << "}\n";
1188  g << "}\n";
1189  }
1190 
1191  if (max_iter_ls_ > 0 || so_corr_) {
1192  g.comment("Calculate penalty parameter of merit function");
1193  g << "sigma = " << g.fmax("sigma", "(1.01*" + g.norm_inf(nx_+ng_, "d->dlam")+")") << ";\n";
1194  g.comment("Calculate L1-merit function in the actual iterate");
1195  g.local("l1_infeas", "casadi_real");
1196  g << "l1_infeas = " << g.sum_viol(nx_+ng_, "d_nlp.z", "d_nlp.lbz", "d_nlp.ubz") << ";\n";
1197  g.local("l1", "casadi_real");
1198  g << "l1 = d_nlp.objective + sigma * l1_infeas;\n";
1199  }
1200  if (so_corr_) {
1201  g.local("l1_infeas_cand", "casadi_real");
1202  g.init_local("l1_infeas_cand", "0");
1203  g.local("l1_cand", "casadi_real");
1204  g.local("fk_cand", "casadi_real");
1205  g.comment("Take candidate step");
1206  g << g.copy("d_nlp.z", nx_, "d->z_cand") << ";\n";
1207  g << g.axpy(nx_, "1.", "d->dx", "d->z_cand") << ";\n";
1208 
1209  g.comment("Evaluate objective and constraints");
1210  g << "d->arg[0] = d->z_cand;\n;";
1211  g << "d->arg[1] = d_nlp.p;\n;";
1212  g << "d->res[0] = &fk_cand;\n;";
1213  g << "d->res[1] = d->z_cand+" + str(nx_) + ";\n;";
1214  std::string nlp_fg = g(get_function("nlp_fg"), "d->arg", "d->res", "d->iw", "d->w");
1215  g << "if (" << nlp_fg << ") {\n";
1216  g << "l1_cand = -casadi_inf;\n";
1217  g << "} else {\n";
1218  g << "l1_infeas_cand = " << g.sum_viol(nx_+ng_, "d->z_cand", "d_nlp.lbz", "d_nlp.ubz") << ";\n";
1219  g << "l1_cand = fk_cand + sigma*l1_infeas_cand;\n";
1220  g << "}\n";
1221 
1222  g << "if (l1_cand > l1 && l1_infeas_cand > l1_infeas) {\n";
1223  g.comment("Copy in case of fail");
1224  g << g.copy("d->dx", nx_, "d->temp_sol") << "\n";
1225  g << g.copy("d->dlam", nx_+ng_, "d->temp_sol+"+str(nx_)) << "\n";
1226 
1227  g.comment("Add gradient times proposal step to bounds");
1228  g << g.clear("d->lbdz", nx_+ng_) << ";\n";
1229  g << g.mv("d->Jk", Asp_, "d->dx", "d->lbdz+" + str(nx_), false) << ";\n";
1230  g << g.copy("d->lbdz", nx_+ng_, "d->ubdz") << ";\n";
1231 
1232  g.comment("Add bounds");
1233  g << g.axpy(nx_+ng_, "1.", "d_nlp.lbz", "d->lbdz") << ";\n";
1234  g << g.axpy(nx_+ng_, "1.", "d_nlp.ubz", "d->ubdz") << ";\n";
1235 
1236  g.comment("Subtract constraints in candidate step from bounds");
1237  g << g.axpy(nx_, "-1.", "d_nlp.z", "d->lbdz") << ";\n";
1238  g << g.axpy(ng_, "-1", "d->z_cand+" + str(nx_), "d->lbdz+" + str(nx_)) << ";\n";
1239  g << g.axpy(nx_, "-1.", "d_nlp.z", "d->ubdz") << ";\n";
1240  g << g.axpy(ng_, "-1.", "d->z_cand+" + str(nx_), "d->ubdz+" + str(nx_)) << ";\n";
1241 
1242  if (!elastic_mode_) {
1243  g.comment("Initial guess");
1244  g << g.copy("d_nlp.lam", nx_+ng_, "d->dlam") << "\n";
1245  g << g.clear("d->dx", nx_);
1246 
1247  if (init_feasible_) {
1248  g.comment("Make initial guess feasible");
1249  g << "for (casadi_int i = 0; i < " << nx_ << "; ++i) {\n";
1250  g << "if (d->lbdz[i] > 0) d->dx[i] = d->lbdz[i];\n";
1251  g << "else if (d->ubdz[i] < 0) d->dx[i] = d->ubdz[i];\n";
1252  g << "}\n";
1253  }
1254 
1255  codegen_qp_solve(g, "d->Bk", "d->gf", "d->lbdz", "d->ubdz", "d->Jk", "d->dx", "d->dlam", 1);
1256  } else {
1257  g << "ret = " << SOLVER_RET_INFEASIBLE << ";\n";
1258  g.comment("Second order corrections without elastic mode");
1259  g << "if (ela_it == -1) {\n";
1260 
1261  g.comment("Initial guess");
1262  g << g.copy("d_nlp.lam", nx_+ng_, "d->dlam") << "\n";
1263  g << g.clear("d->dx", nx_);
1264 
1265  if (init_feasible_) {
1266  g.comment("Make initial guess feasible");
1267  g << "for (casadi_int i = 0; i < " << nx_ << "; ++i) {\n";
1268  g << "if (d->lbdz[i] > 0) d->dx[i] = d->lbdz[i];\n";
1269  g << "else if (d->ubdz[i] < 0) d->dx[i] = d->ubdz[i];\n";
1270  g << "}\n";
1271  }
1272 
1273  codegen_qp_solve(g, "d->Bk", "d->gf", "d->lbdz", "d->ubdz", "d->Jk", "d->dx", "d->dlam", 1);
1274  g << "}\n";
1275 
1276  g.comment("Second order corrections in elastic mode");
1277  g << "if (ret == " << SOLVER_RET_INFEASIBLE << " || ela_it != -1) {\n";
1278  g << "if (ela_it == -1) {\n";
1279  g << "ela_it = 0;\n";
1281  g << "}\n";
1283  g << "}\n";
1284  }
1285  g << "}\n";
1286  g.comment("Fallback on previous solution if the second order correction failed");
1287  g << "if (ret != " << 0 << ") {\n";
1288  g << g.copy("d->temp_sol", nx_, "d->dx") << "\n";
1289  g << g.copy("d->temp_sol+"+str(nx_), nx_+ng_, "d->dlam") << "\n";
1290  g << "} else {\n";
1291  g.comment("Check if corrected step is better than the original one using the merit function");
1292  g << "double l1_cand_norm = l1_cand;\n";
1293  g << "double l1_cand_soc;\n";
1294 
1295  g.comment("Take candidate step");
1296  g << g.copy("d_nlp.z", nx_, "d->z_cand") << "\n";
1297  g << g.axpy(nx_, "1.", "d->dx", "d->z_cand") << "\n";
1298 
1299  g.comment("Evaluate objective and constraints");
1300  g << "d->arg[0] = d->z_cand;\n;";
1301  g << "d->arg[1] = d_nlp.p;\n;";
1302  g << "d->res[0] = &fk_cand;\n;";
1303  g << "d->res[1] = d->z_cand+" + str(nx_) + ";\n;";
1304  nlp_fg = g(get_function("nlp_fg"), "d->arg", "d->res", "d->iw", "d->w");
1305  g << "if (" << nlp_fg << ") {\n";
1306  g << "l1_cand_soc = casadi_inf;\n";
1307  g << "} else {\n";
1308  g << "l1_infeas_cand = " << g.sum_viol(nx_+ng_, "d->z_cand", "d_nlp.lbz", "d_nlp.ubz") << ";\n";
1309  g << "l1_cand_soc = fk_cand + sigma*l1_infeas_cand;\n";
1310  g << "}\n";
1311 
1312  g << "if (l1_cand_norm < l1_cand_soc) {\n";
1313  g.comment("Copy normal step if merit function increases");
1314  g << g.copy("d->temp_sol", nx_, "d->dx") << "\n";
1315  g << g.copy("d->temp_sol+"+str(nx_), nx_+ng_, "d->dlam") << "\n";
1316  g << "}\n";
1317 
1318  g << "}\n";
1319  }
1320 
1321  if (max_iter_ls_) {
1322  g.comment("Detecting indefiniteness");
1323  g.comment("Right-hand side of Armijo condition");
1324  g.local("F_sens", "casadi_real");
1325  g << "F_sens = " << g.dot(nx_, "d->dx", "d->gf") << ";\n";
1326  g.local("tl1", "casadi_real");
1327  g << "tl1 = F_sens - sigma * l1_infeas;\n";
1328  /*g.comment("Storing the actual merit function value in a list");
1329  g << "d->merit_mem[merit_ind] = l1;\n";
1330  g << "merit_ind++;\n";
1331  g << "merit_ind %= " << merit_memsize_ << ";\n";
1332  g.comment("Calculating maximal merit function value so far");
1333  g.local("meritmax", "casadi_real");
1334  g << "meritmax = " << g.vfmax("d->merit_mem+1", g.min(str(merit_memsize_),
1335  "iter_count")+"-1", "d->merit_mem[0]") << "\n";*/
1336  g.comment("Stepsize");
1337  g << "t = 1.0;\n";
1338  g.local("fk_cand", "casadi_real");
1339  g.comment("Merit function value in candidate");
1340  g.local("l1_cand", "casadi_real");
1341  g << "l1_cand = 0.0;\n";
1342  g.comment("Reset line-search counter, success marker");
1343  g << "ls_iter = 0;\n";
1344  if (elastic_mode_ && max_iter_ls_) g << "ls_success = 1;\n";
1345  g.comment("Line-search loop");
1346  g << "while (1) {\n";
1347  g.comment(" Increase counter");
1348  g << "ls_iter++;\n";
1349 
1350  g.comment("Candidate step");
1351  g << g.copy("d_nlp.z", nx_, "d->z_cand") << "\n";
1352  g << g.axpy(nx_, "t", "d->dx", "d->z_cand") << "\n";
1353  g.comment("Evaluating objective and constraints");
1354  g << "d->arg[0] = d->z_cand;\n";
1355  g << "d->arg[1] = d_nlp.p;\n";
1356  g << "d->res[0] = &fk_cand;\n";
1357  g << "d->res[1] = d->z_cand+" + str(nx_) + ";\n";
1358  std::string nlp_fg = g(get_function("nlp_fg"), "d->arg", "d->res", "d->iw", "d->w");
1359  g << "if (" << nlp_fg << ") {\n";
1360  g.comment("Avoid infinite recursion");
1361  g << "if (ls_iter == " << max_iter_ls_ << ") {\n";
1362  if (elastic_mode_ && max_iter_ls_) g << "ls_success = 0;\n";
1363  g << "break;\n";
1364  g << "}\n";
1365  g.comment("line-search failed, skip iteration");
1366  g << "t = " << beta_ << "* t;\n";
1367  g << "continue;\n";
1368  g << "}\n";
1369 
1370  g.comment("Calculating merit-function in candidate");
1371  g << "l1_cand = fk_cand + sigma * "
1372  << g.sum_viol(nx_+ng_, "d->z_cand", "d_nlp.lbz", "d_nlp.ubz") + ";\n";
1373  g << "if (l1_cand <= l1 + t * " << c1_ << "* tl1) {\n";
1374  g << "break;\n";
1375  g << "}\n";
1376  g.comment("Line-search not successful, but we accept it.");
1377  g << "if (ls_iter == " << max_iter_ls_ << ") {\n";
1378  if (elastic_mode_ && max_iter_ls_) g << "ls_success = 0;\n";
1379  g << "break;\n";
1380  g << "}\n";
1381  g.comment("Backtracking");
1382  g << "t = " << beta_ << "* t;\n";
1383  g << "}\n";
1384  g.comment("Candidate accepted, update dual variables");
1385  g << g.scal(nx_+ng_, "1-t", "d_nlp.lam") << "\n";
1386  g << g.axpy(nx_+ng_, "t", "d->dlam", "d_nlp.lam") << "\n";
1387  g << g.scal(nx_, "t", "d->dx") << "\n";
1388  } else {
1389  g.comment("Full step");
1390  g << g.copy("d->dlam", nx_ + ng_, "d_nlp.lam") << "\n";
1391  }
1392 
1393  g.comment("Take step");
1394  g << g.axpy(nx_, "1.0", "d->dx", "d_nlp.z") << "\n";
1395 
1396  if (!exact_hessian_) {
1397  g.comment("Evaluate the gradient of the Lagrangian with the old x but new lam (for BFGS)");
1398  g << g.copy("d->gf", nx_, "d->gLag_old") << "\n";
1399  g << g.mv("d->Jk", Asp_, "d_nlp.lam+"+str(nx_), "d->gLag_old", true) << "\n";
1400  g << g.axpy(nx_, "1.0", "d_nlp.lam", "d->gLag_old") << "\n";
1401  }
1402 
1403  if (elastic_mode_ && max_iter_ls_ > 0) {
1404  g.comment("If linesearch failed enter elastic mode");
1405  g << "if (ls_success == 0 && ela_it == -1) {\n";
1406  g << "ela_it = 0;\n";
1408  g << "}\n";
1409  }
1410  g << "}\n";
1411  codegen_body_exit(g);
1412 }
1413 void Sqpmethod::codegen_qp_solve(CodeGenerator& cg, const std::string& H, const std::string& g,
1414  const std::string& lbdz, const std::string& ubdz,
1415  const std::string& A, const std::string& x_opt, const std::string& dlam, int mode) const {
1416  for (casadi_int i=0;i<qpsol_.n_in();++i) cg << "d->arg[" << i << "] = 0;\n";
1417  cg << "d->arg[" << CONIC_H << "] = " << H << ";\n";
1418  cg << "d->arg[" << CONIC_G << "] = " << g << ";\n";
1419  cg << "d->arg[" << CONIC_X0 << "] = " << x_opt << ";\n";
1420  cg << "d->arg[" << CONIC_LAM_X0 << "] = " << dlam << ";\n";
1421  cg << "d->arg[" << CONIC_LAM_A0 << "] = " << dlam << "+" << nx_ << ";\n";
1422  cg << "d->arg[" << CONIC_LBX << "] = " << lbdz << ";\n";
1423  cg << "d->arg[" << CONIC_UBX << "] = " << ubdz << ";\n";
1424  cg << "d->arg[" << CONIC_A << "] = " << A << ";\n";
1425  cg << "d->arg[" << CONIC_LBA << "] = " << lbdz << "+" << nx_ << ";\n";
1426  cg << "d->arg[" << CONIC_UBA << "] = " << ubdz << "+" << nx_ << ";\n";
1427  for (casadi_int i=0;i<qpsol_.n_out();++i) cg << "d->res[" << i << "] = 0;\n";
1428  cg << "d->res[" << CONIC_X << "] = " << x_opt << ";\n";
1429  cg << "d->res[" << CONIC_LAM_X << "] = " << dlam << ";\n";
1430  cg << "d->res[" << CONIC_LAM_A << "] = " << dlam << "+" << nx_ << ";\n";
1431  std::string flag = cg(qpsol_, "d->arg", "d->res", "d->iw", "d->w");
1432  cg << "ret = " << flag << ";\n";
1433  cg << "if (ret == -1000) return -1000;\n"; // equivalent to raise Exception
1434 }
1435 
1436 void Sqpmethod::codegen_qp_ela_solve(CodeGenerator& cg, const std::string& H,
1437  const std::string& g, const std::string& lbdz, const std::string& ubdz,
1438  const std::string& A, const std::string& x_opt, const std::string& dlam) const {
1439  for (casadi_int i=0;i<qpsol_ela_.n_in();++i) cg << "d->arg[" << i << "] = 0;\n";
1440  cg << "d->arg[" << CONIC_H << "] = " << H << ";\n";
1441  cg << "d->arg[" << CONIC_G << "] = " << g << ";\n";
1442  cg << "d->arg[" << CONIC_X0 << "] = " << x_opt << ";\n";
1443  cg << "d->arg[" << CONIC_LAM_X0 << "] = " << dlam << ";\n";
1444  cg << "d->arg[" << CONIC_LAM_A0 << "] = " << dlam << "+" << nx_+2*ng_ << ";\n";
1445  cg << "d->arg[" << CONIC_LBX << "] = " << lbdz << ";\n";
1446  cg << "d->arg[" << CONIC_UBX << "] = " << ubdz << ";\n";
1447  cg << "d->arg[" << CONIC_A << "] = " << A << ";\n";
1448  cg << "d->arg[" << CONIC_LBA << "] = " << lbdz << "+" << nx_+2*ng_ << ";\n";
1449  cg << "d->arg[" << CONIC_UBA << "] = " << ubdz << "+" << nx_+2*ng_ << ";\n";
1450  for (casadi_int i=0;i<qpsol_.n_out();++i) cg << "d->res[" << i << "] = 0;\n";
1451  cg << "d->res[" << CONIC_X << "] = " << x_opt << ";\n";
1452  cg << "d->res[" << CONIC_LAM_X << "] = " << dlam << ";\n";
1453  cg << "d->res[" << CONIC_LAM_A << "] = " << dlam << "+" << nx_+2*ng_ << ";\n";
1454  std::string flag = cg(qpsol_ela_, "d->arg", "d->res", "d->iw", "d->w");
1455  cg << "ret = " << flag << ";\n";
1456  cg << "if (ret == -1000) return -1000;\n"; // equivalent to raise Exception
1457 }
1458 
1460  cg << "double gamma = 0.;\n";
1461 
1462  if (mode == 0) cg << "ela_it++;\n";
1463 
1464  cg.comment("Temp datastructs for data copy");
1465  cg << "double *temp_1, *temp_2;\n";
1466 
1467  cg.comment("Make larger jacobian (has 2 extra diagonal matrices with -1 and 1 respectively)");
1468  cg << "temp_1 = d->Jk + " << Asp_.nnz() << ";\n";
1469  cg << cg.fill("temp_1", ng_, "-1.") << ";\n";
1470  cg << "temp_1 += " << ng_ << ";\n";
1471  cg << cg.fill("temp_1", ng_, "1.") << ";\n";
1472 
1473  cg.comment("Initialize bounds");
1474  cg << "temp_1 = d->lbdz + " << nx_ << ";\n";
1475  cg << "temp_2 = d->lbdz + " << nx_ + 2*ng_ << ";\n";
1476  cg << cg.copy("temp_1", ng_, "temp_2") << ";\n";
1477  cg << cg.clear("temp_1", 2*ng_) << ";\n";
1478  cg << "temp_1 = d->ubdz + " << nx_ << ";\n";
1479  cg << "temp_2 = d->ubdz + " << nx_ + 2*ng_ << ";\n";
1480  cg << cg.copy("temp_1", ng_, "temp_2") << ";\n";
1481  cg << cg.fill("temp_1", 2*ng_, cg.constant(inf)) << ";\n";
1482 
1483  cg << "if (ela_it > 1) {\n";
1484  cg << "gamma = pow(10, ela_it*(ela_it-1)/2)*gamma_1;\n";
1485  cg << "} else {\n";
1486  cg << "gamma = gamma_1;\n";
1487  cg << "}\n";
1488  cg << "if (gamma > " << gamma_max_ << ") " << "return -1" << ";\n";
1489 
1490  cg.comment("Make larger gradient (has gamma for slack variables)");
1491  cg << "temp_1 = d->gf + " << nx_ << ";\n";
1492  cg << cg.fill("temp_1", 2*ng_, "gamma") << ";\n";
1493 
1494  cg.comment("Initial guess");
1495  cg << cg.clear("d->dlam", nx_+3*ng_) << "\n";
1496  cg << cg.copy("d_nlp.lam", nx_, "d->dlam") << "\n";
1497  cg << cg.copy("d_nlp.lam+"+str(nx_), ng_, "d->dlam+"+str(nx_+2*ng_)) << "\n";
1498  cg << cg.clear("d->dx", nx_+2*ng_);
1499 
1500  if (init_feasible_) {
1501  cg.comment("Make initial guess feasible on x values");
1502  cg << "for (casadi_int i = 0; i < " << nx_ << "; ++i) {\n";
1503  cg << "if (d->lbdz[i] > 0) d->dx[i] = d->lbdz[i];\n";
1504  cg << "else if (d->ubdz[i] < 0) d->dx[i] = d->ubdz[i];\n";
1505  cg << "}\n";
1506 
1507  cg.comment("Make initial guess feasible on constraints by altering slack variables");
1508  cg << cg.mv("d->Jk", Asp_, "d->dx", "d->temp_mem", false) << "\n";
1509  cg << "for (casadi_int i = 0; i < " << ng_ << "; ++i) {\n";
1510  cg << "if (d->ubdz[" << nx_+2*ng_ << "+i]-d->temp_mem[i] < 0) {\n";
1511  cg << "d->dx[" << nx_ << "+i] = -d->ubdz[" << nx_+2*ng_ << "+i]+d->temp_mem[i];\n";
1512  cg << "}\n";
1513 
1514  cg << "if (d->lbdz[" << nx_+2*ng_ << "+i]-d->temp_mem[i] > 0) {\n";
1515  cg << "d->dx[" << nx_+ng_ << "+i] = d->lbdz[" << nx_+2*ng_ << "+i]-d->temp_mem[i];\n";
1516  cg << "}\n";
1517  cg << "}\n";
1518  }
1519 
1520  cg.comment("Solve the QP");
1521  codegen_qp_ela_solve(cg, "d->Bk", "d->gf", "d->lbdz", "d->ubdz", "d->Jk", "d->dx", "d->dlam");
1522 
1523  cg.comment("Copy constraint dlam to the right place");
1524  cg << cg.copy("d->dlam+"+str(nx_+2*ng_), ng_, "d->dlam+"+str(nx_)) << "\n";
1525 
1526 }
1527 
1529  cg << "temp_norm = " << gamma_0_ << "*" << cg.norm_inf(nx_, "d->gf") << ";\n";
1530  cg << "gamma_1 = " << cg.fmax("temp_norm", str(gamma_1_min_)) << ";\n";
1531 }
1532 
1533 Dict Sqpmethod::get_stats(void* mem) const {
1534  Dict stats = Nlpsol::get_stats(mem);
1535  auto m = static_cast<SqpmethodMemory*>(mem);
1536  stats["return_status"] = m->return_status;
1537  stats["iter_count"] = m->iter_count;
1538  return stats;
1539 }
1540 
1542  int version = s.version("Sqpmethod", 1, 3);
1543  s.unpack("Sqpmethod::qpsol", qpsol_);
1544  if (version>=3) {
1545  s.unpack("Sqpmethod::qpsol_ela", qpsol_ela_);
1546  }
1547  s.unpack("Sqpmethod::exact_hessian", exact_hessian_);
1548  s.unpack("Sqpmethod::max_iter", max_iter_);
1549  s.unpack("Sqpmethod::min_iter", min_iter_);
1550  s.unpack("Sqpmethod::lbfgs_memory", lbfgs_memory_);
1551  s.unpack("Sqpmethod::tol_pr_", tol_pr_);
1552  s.unpack("Sqpmethod::tol_du_", tol_du_);
1553  s.unpack("Sqpmethod::min_step_size_", min_step_size_);
1554  s.unpack("Sqpmethod::c1", c1_);
1555  s.unpack("Sqpmethod::beta", beta_);
1556  s.unpack("Sqpmethod::max_iter_ls_", max_iter_ls_);
1557  s.unpack("Sqpmethod::merit_memsize_", merit_memsize_);
1558  s.unpack("Sqpmethod::beta", beta_);
1559  s.unpack("Sqpmethod::print_header", print_header_);
1560  s.unpack("Sqpmethod::print_iteration", print_iteration_);
1561  s.unpack("Sqpmethod::print_status", print_status_);
1562 
1563  if (version>=3) {
1564  s.unpack("Sqpmethod::elastic_mode", elastic_mode_);
1565  s.unpack("Sqpmethod::gamma_0", gamma_0_);
1566  s.unpack("Sqpmethod::gamma_max", gamma_max_);
1567  s.unpack("Sqpmethod::gamma_1_min", gamma_1_min_);
1568  s.unpack("Sqpmethod::init_feasible", init_feasible_);
1569  s.unpack("Sqpmethod::so_corr", so_corr_);
1570  } else {
1571  elastic_mode_ = false;
1572  gamma_0_ = 0;
1573  gamma_max_ = 0;
1574  gamma_1_min_ = 0;
1575  init_feasible_ = false;
1576  so_corr_ = false;
1577  }
1578 
1579  s.unpack("Sqpmethod::Hsp", Hsp_);
1580  if (version==1) {
1581  Sparsity Hrsp;
1582  s.unpack("Sqpmethod::Hrsp", Hrsp);
1583  }
1584  s.unpack("Sqpmethod::Asp", Asp_);
1585  if (version==1) {
1586  double convexify_margin;
1587  s.unpack("Sqpmethod::convexify_margin", convexify_margin);
1588  char convexify_strategy;
1589  s.unpack("Sqpmethod::convexify_strategy", convexify_strategy);
1590  casadi_assert(convexify_strategy==0, "deserializtion failed.");
1591  bool Hsp_project;
1592  s.unpack("Sqpmethod::Hsp_project", Hsp_project);
1593  bool scc_transform;
1594  s.unpack("Sqpmethod::scc_transform", scc_transform);
1595  std::vector<casadi_int> scc_offset;
1596  s.unpack("Sqpmethod::scc_offset", scc_offset);
1597  std::vector<casadi_int> scc_mapping;
1598  s.unpack("Sqpmethod::scc_mapping", scc_mapping);
1599  casadi_int max_iter_eig;
1600  s.unpack("Sqpmethod::max_iter_eig", max_iter_eig);
1601  casadi_int block_size;
1602  s.unpack("Sqpmethod::block_size", block_size);
1603  Sparsity scc_sp;
1604  s.unpack("Sqpmethod::scc_sp", scc_sp);
1605  convexify_ = false;
1606  }
1607  if (version>=2) {
1608  s.unpack("Sqpmethod::convexify", convexify_);
1609  if (convexify_) Convexify::deserialize(s, "Sqpmethod::", convexify_data_);
1610  }
1611  set_sqpmethod_prob();
1612 }
1613 
1616  s.version("Sqpmethod", 3);
1617  s.pack("Sqpmethod::qpsol", qpsol_);
1618  s.pack("Sqpmethod::qpsol_ela", qpsol_ela_);
1619  s.pack("Sqpmethod::exact_hessian", exact_hessian_);
1620  s.pack("Sqpmethod::max_iter", max_iter_);
1621  s.pack("Sqpmethod::min_iter", min_iter_);
1622  s.pack("Sqpmethod::lbfgs_memory", lbfgs_memory_);
1623  s.pack("Sqpmethod::tol_pr_", tol_pr_);
1624  s.pack("Sqpmethod::tol_du_", tol_du_);
1625  s.pack("Sqpmethod::min_step_size_", min_step_size_);
1626  s.pack("Sqpmethod::c1", c1_);
1627  s.pack("Sqpmethod::beta", beta_);
1628  s.pack("Sqpmethod::max_iter_ls_", max_iter_ls_);
1629  s.pack("Sqpmethod::merit_memsize_", merit_memsize_);
1630  s.pack("Sqpmethod::beta", beta_);
1631  s.pack("Sqpmethod::print_header", print_header_);
1632  s.pack("Sqpmethod::print_iteration", print_iteration_);
1633  s.pack("Sqpmethod::print_status", print_status_);
1634 
1635  s.pack("Sqpmethod::elastic_mode", elastic_mode_);
1636  s.pack("Sqpmethod::gamma_0", gamma_0_);
1637  s.pack("Sqpmethod::gamma_max", gamma_max_);
1638  s.pack("Sqpmethod::gamma_1_min", gamma_1_min_);
1639 
1640  s.pack("Sqpmethod::init_feasible", init_feasible_);
1641  s.pack("Sqpmethod::so_corr", so_corr_);
1642 
1643  s.pack("Sqpmethod::Hsp", Hsp_);
1644  s.pack("Sqpmethod::Asp", Asp_);
1645  s.pack("Sqpmethod::convexify", convexify_);
1646  if (convexify_) Convexify::serialize(s, "Sqpmethod::", convexify_data_);
1647 }
1648 
1649 } // namespace casadi
Helper class for C code generation.
std::string fill(const std::string &res, std::size_t n, const std::string &v)
Create a fill operation.
std::string axpy(casadi_int n, const std::string &a, const std::string &x, const std::string &y)
Codegen axpy: y += a*x.
std::string add_dependency(const Function &f)
Add a function dependency.
std::string copy(const std::string &arg, std::size_t n, const std::string &res)
Create a copy operation.
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.
std::string scal(casadi_int n, const std::string &alpha, const std::string &x)
What does scal do??
std::string sum_viol(casadi_int n, const std::string &x, const std::string &lb, const std::string &ub)
sum_viol
std::string mv(const std::string &x, const Sparsity &sp_x, const std::string &y, const std::string &z, bool tr)
Codegen sparse matrix-vector multiplication.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
std::string norm_inf(casadi_int n, const std::string &x)
norm_inf
void init_local(const std::string &name, const std::string &def)
Specify the default value for a local variable.
std::string dot(casadi_int n, const std::string &x, const std::string &y)
Codegen inner product.
std::string max_viol(casadi_int n, const std::string &x, const std::string &lb, const std::string &ub)
max_viol
std::string convexify_eval(const ConvexifyData &d, const std::string &Hin, const std::string &Hout, const std::string &iw, const std::string &w)
convexify
std::string sparsity(const Sparsity &sp, bool canonical=true)
std::string fmax(const std::string &x, const std::string &y)
fmax
std::string clear(const std::string &res, std::size_t n)
Create a fill operation.
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
static void serialize(SerializingStream &s, const std::string &prefix, const ConvexifyData &d)
Definition: convexify.cpp:112
static Sparsity setup(ConvexifyData &d, const Sparsity &H, const Dict &opts=Dict(), bool inplace=true)
Definition: convexify.cpp:167
static MXNode * deserialize(DeserializingStream &s)
Deserialize without type information.
Definition: convexify.hpp:105
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
std::string codegen_mem(CodeGenerator &g, const std::string &index="mem") const
Get thread-local memory object.
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.
void alloc(const Function &f, bool persistent=false, int num_threads=1)
Ensure work vectors long enough to evaluate function.
size_t sz_iw() const
Get required length of iw field.
Function object.
Definition: function.hpp:60
void release(int mem) const
Release a memory object.
Definition: function.cpp:1974
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:975
casadi_int n_in() const
Get the number of function inputs.
Definition: function.cpp:971
NLP solver storage class.
Definition: nlpsol_impl.hpp:59
void codegen_body_exit(CodeGenerator &g) const override
Generate code for the function body.
Definition: nlpsol.cpp:1368
bool calc_lam_p_
Options.
Definition: nlpsol_impl.hpp:97
Dict get_stats(void *mem) const override
Get all statistics.
Definition: nlpsol.cpp:1251
static const Options options_
Options.
void codegen_body_enter(CodeGenerator &g) const override
Generate code for the function body.
Definition: nlpsol.cpp:1268
void codegen_declarations(CodeGenerator &g) const override
Generate code for the declarations of the C function.
Definition: nlpsol.cpp:1348
void init(const Dict &opts) override
Initialize.
Definition: nlpsol.cpp:499
casadi_int ng_
Number of constraints.
Definition: nlpsol_impl.hpp:69
int init_mem(void *mem) const override
Initalize memory block.
Definition: nlpsol.cpp:692
casadi_nlpsol_prob< double > p_nlp_
Definition: nlpsol_impl.hpp:63
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: nlpsol.cpp:1409
bool calc_f_
Options.
Definition: nlpsol_impl.hpp:97
bool calc_g_
Options.
Definition: nlpsol_impl.hpp:97
bool calc_lam_x_
Options.
Definition: nlpsol_impl.hpp:97
int callback(NlpsolMemory *m) const
Definition: nlpsol.cpp:1210
casadi_int nx_
Number of variables.
Definition: nlpsol_impl.hpp:66
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: nlpsol.cpp:884
void set_function(const Function &fcn, const std::string &fname, bool jit=false)
Function create_function(const Function &oracle, const std::string &fname, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const Function::AuxOut &aux=Function::AuxOut(), const Dict &opts=Dict())
int calc_function(OracleMemory *m, const std::string &fcn, const double *const *arg=nullptr, int thread_id=0) const
std::vector< std::string > get_function() const override
Get list of dependency functions.
bool has_function(const std::string &fname) const override
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
void print(const char *fmt,...) const
C-style formatted printing during evaluation.
int checkout() const
Checkout a memory object.
void * memory(int ind) const
Memory objects.
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)
void pack(const Sparsity &e)
Serializes an object to the output stream.
General sparsity class.
Definition: sparsity.hpp:106
void enlarge(casadi_int nrow, casadi_int ncol, const std::vector< casadi_int > &rr, const std::vector< casadi_int > &cc, bool ind1=false)
Enlarge matrix.
Definition: sparsity.cpp:545
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
Definition: sparsity.hpp:190
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern *.
Definition: sparsity.cpp:1028
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
void appendColumns(const Sparsity &sp)
Append another sparsity patten horizontally.
Definition: sparsity.cpp:500
bool is_symmetric() const
Is symmetric?
Definition: sparsity.cpp:317
void codegen_qp_ela_solve(CodeGenerator &cg, const std::string &H, const std::string &g, const std::string &lbdz, const std::string &ubdz, const std::string &A, const std::string &x_opt, const std::string &dlam) const
Definition: sqpmethod.cpp:1436
double calc_gamma_1(SqpmethodMemory *m) const
Definition: sqpmethod.cpp:1020
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into MX.
Definition: sqpmethod.hpp:242
Function qpsol_
QP solver for the subproblems.
Definition: sqpmethod.hpp:116
bool init_feasible_
Initialize feasible qp's.
Definition: sqpmethod.hpp:146
void print_iteration() const
Print iteration header.
Definition: sqpmethod.cpp:813
casadi_int lbfgs_memory_
Memory size of L-BFGS method.
Definition: sqpmethod.hpp:131
virtual int solve_QP(SqpmethodMemory *m, const double *H, const double *g, const double *lbdz, const double *ubdz, const double *A, double *x_opt, double *dlam, int mode) const
Definition: sqpmethod.cpp:846
static Nlpsol * creator(const std::string &name, const Function &nlp)
Create a new NLP Solver.
Definition: sqpmethod.hpp:80
casadi_int min_iter_
Definition: sqpmethod.hpp:128
virtual int solve_ela_QP(SqpmethodMemory *m, const double *H, const double *g, const double *lbdz, const double *ubdz, const double *A, double *x_opt, double *dlam) const
Definition: sqpmethod.cpp:887
void codegen_qp_solve(CodeGenerator &cg, const std::string &H, const std::string &g, const std::string &lbdz, const std::string &ubdz, const std::string &A, const std::string &x_opt, const std::string &dlam, int mode) const
Definition: sqpmethod.cpp:1413
~Sqpmethod() override
Definition: sqpmethod.cpp:62
double gamma_0_
Initial and maximum penalty parameter for elastic mode.
Definition: sqpmethod.hpp:143
bool elastic_mode_
Elastic mode.
Definition: sqpmethod.hpp:140
int solve(void *mem) const override
Definition: sqpmethod.cpp:401
casadi_sqpmethod_prob< double > p_
Definition: sqpmethod.hpp:113
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: sqpmethod.cpp:370
ConvexifyData convexify_data_
Data for convexification.
Definition: sqpmethod.hpp:166
static const Options options_
Options.
Definition: sqpmethod.hpp:86
virtual int solve_elastic_mode(SqpmethodMemory *m, casadi_int *ela_it, double gamma_1, casadi_int ls_iter, bool ls_success, bool so_succes, double pr_inf, double du_inf, double dx_norminf, std::string *info, int mode) const
Definition: sqpmethod.cpp:928
void codegen_solve_elastic_mode(CodeGenerator &cg, int mode) const
Definition: sqpmethod.cpp:1459
Dict get_stats(void *mem) const override
Get all statistics.
Definition: sqpmethod.cpp:1533
int init_mem(void *mem) const override
Initalize memory block.
Definition: sqpmethod.cpp:383
casadi_int merit_memsize_
Definition: sqpmethod.hpp:153
void free_mem(void *mem) const override
Free memory block.
Definition: sqpmethod.cpp:395
double min_step_size_
Minimum step size allowed.
Definition: sqpmethod.hpp:137
casadi_int max_iter_
Maximum, minimum number of SQP iterations.
Definition: sqpmethod.hpp:128
bool convexify_
convexify?
Definition: sqpmethod.hpp:169
casadi_int max_iter_ls_
Definition: sqpmethod.hpp:152
bool exact_hessian_
Exact Hessian?
Definition: sqpmethod.hpp:122
void codegen_body(CodeGenerator &g) const override
Generate code for the function body.
Definition: sqpmethod.cpp:1038
void codegen_calc_gamma_1(CodeGenerator &cg) const
Definition: sqpmethod.cpp:1528
void init(const Dict &opts) override
Initialize.
Definition: sqpmethod.cpp:157
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: sqpmethod.cpp:1614
static const std::string meta_doc
A documentation string.
Definition: sqpmethod.hpp:235
void codegen_declarations(CodeGenerator &g) const override
Generate code for the declarations of the C function.
Definition: sqpmethod.cpp:1025
Sqpmethod(const std::string &name, const Function &nlp)
Definition: sqpmethod.cpp:58
Function qpsol_ela_
QP solver for elastic mode subproblems.
Definition: sqpmethod.hpp:119
double tol_pr_
Tolerance of primal and dual infeasibility.
Definition: sqpmethod.hpp:134
Function conic(const std::string &name, const std::string &solver, const SpDict &qp, const Dict &opts)
Definition: conic.cpp:44
The casadi namespace.
Definition: archiver.cpp:28
T1 casadi_max_viol(casadi_int n, const T1 *x, const T1 *lb, const T1 *ub)
Largest bound violation.
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
@ 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)
T1 casadi_sum_viol(casadi_int n, const T1 *x, const T1 *lb, const T1 *ub)
Sum of bound violations.
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
void casadi_fill(T1 *x, casadi_int n, T1 alpha)
FILL: x <- alpha.
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
const double inf
infinity
Definition: calculus.hpp:50
int CASADI_NLPSOL_SQPMETHOD_EXPORT casadi_register_nlpsol_sqpmethod(Nlpsol::Plugin *plugin)
Definition: sqpmethod.cpp:43
T1 casadi_dot(casadi_int n, const T1 *x, const T1 *y)
Inner product.
const double nan
Not a number.
Definition: calculus.hpp:53
void casadi_scal(casadi_int n, T1 alpha, T1 *x)
SCAL: x <- alpha*x.
void casadi_axpy(casadi_int n, T1 alpha, const T1 *x, T1 *y)
AXPY: y <- a*x + y.
void CASADI_NLPSOL_SQPMETHOD_EXPORT casadi_load_nlpsol_sqpmethod()
Definition: sqpmethod.cpp:54
T1 casadi_norm_inf(casadi_int n, const T1 *x)
void casadi_clear(T1 *x, casadi_int n)
CLEAR: x <- 0.
void casadi_mv(const T1 *x, const casadi_int *sp_x, const T1 *y, T1 *z, casadi_int tr)
Sparse matrix-vector multiplication: z <- z + x*y.
@ SOLVER_RET_NAN
@ SOLVER_RET_INFEASIBLE
@ SOLVER_RET_LIMITED
@ 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
casadi_convexify_config< double > config
Definition: mx.hpp:61
casadi_int sz_iw
Definition: mx.hpp:62
casadi_int sz_w
Definition: mx.hpp:63
casadi_nlpsol_data< double > d_nlp
Definition: nlpsol_impl.hpp:42
Options metadata for a class.
Definition: options.hpp:40
std::map< std::string, FStats > fstats
void add_stat(const std::string &s)
int iter_count
Iteration count.
Definition: sqpmethod.hpp:61
const char * return_status
Last return status.
Definition: sqpmethod.hpp:58
casadi_sqpmethod_data< double > d
Definition: sqpmethod.hpp:46
double reg
Hessian regularization.
Definition: sqpmethod.hpp:51
const casadi_nlpsol_prob< T1 > * nlp
const casadi_int * sp_h
const casadi_int * sp_a