scpgen.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 
26 #include "scpgen.hpp"
27 #include "casadi/core/core.hpp"
28 #include <ctime>
29 #include <iomanip>
30 #include <fstream>
31 #include <cmath>
32 #include <cfloat>
33 #include <cstdlib>
34 
35 namespace casadi {
36 
37  extern "C"
38  int CASADI_NLPSOL_SCPGEN_EXPORT
39  casadi_register_nlpsol_scpgen(Nlpsol::Plugin* plugin) {
40  plugin->creator = Scpgen::creator;
41  plugin->name = "scpgen";
42  plugin->doc = Scpgen::meta_doc.c_str();
43  plugin->version = CASADI_VERSION;
44  plugin->options = &Scpgen::options_;
45  return 0;
46  }
47 
48  extern "C"
49  void CASADI_NLPSOL_SCPGEN_EXPORT casadi_load_nlpsol_scpgen() {
51  }
52 
53  Scpgen::Scpgen(const std::string& name, const Function& nlp) : Nlpsol(name, nlp) {
54  casadi_warning("SCPgen is under development");
55  }
56 
58  clear_mem();
59  }
60 
62  = {{&Nlpsol::options_},
63  {{"qpsol",
64  {OT_STRING,
65  "The QP solver to be used by the SQP method"}},
66  {"qpsol_options",
67  {OT_DICT,
68  "Options to be passed to the QP solver"}},
69  {"hessian_approximation",
70  {OT_STRING,
71  "gauss-newton|exact"}},
72  {"max_iter",
73  {OT_INT,
74  "Maximum number of SQP iterations"}},
75  {"max_iter_ls",
76  {OT_INT,
77  "Maximum number of linesearch iterations"}},
78  {"tol_pr",
79  {OT_DOUBLE,
80  "Stopping criterion for primal infeasibility"}},
81  {"tol_du",
82  {OT_DOUBLE,
83  "Stopping criterion for dual infeasability"}},
84  {"tol_reg",
85  {OT_DOUBLE,
86  "Stopping criterion for regularization"}},
87  {"tol_pr_step",
88  {OT_DOUBLE,
89  "Stopping criterion for the step size"}},
90  {"c1",
91  {OT_DOUBLE,
92  "Armijo condition, coefficient of decrease in merit"}},
93  {"beta",
94  {OT_DOUBLE,
95  "Line-search parameter, restoration factor of stepsize"}},
96  {"merit_memsize",
97  {OT_INT,
98  "Size of memory to store history of merit function values"}},
99  {"merit_start",
100  {OT_DOUBLE,
101  "Lower bound for the merit function parameter"}},
102  {"lbfgs_memory",
103  {OT_INT,
104  "Size of L-BFGS memory."}},
105  {"regularize",
106  {OT_BOOL,
107  "Automatic regularization of Lagrange Hessian."}},
108  {"print_header",
109  {OT_BOOL,
110  "Print the header with problem statistics"}},
111  {"codegen",
112  {OT_BOOL,
113  "C-code generation"}},
114  {"reg_threshold",
115  {OT_DOUBLE,
116  "Threshold for the regularization."}},
117  {"name_x",
119  "Names of the variables."}},
120  {"print_x",
121  {OT_INTVECTOR,
122  "Which variables to print."}}
123  }
124  };
125 
126  void Scpgen::init(const Dict& opts) {
127  // Call the init method of the base class
128  Nlpsol::init(opts);
129 
130  // Default options
131  max_iter_ = 50;
132  max_iter_ls_ = 1;
133  c1_ = 1e-4;
134  beta_ = 0.8;
135  lbfgs_memory_ = 10;
136  tol_pr_ = 1e-6;
137  tol_du_ = 1e-6;
138  tol_reg_ = 1e-11;
139  regularize_ = false;
140  codegen_ = false;
141  reg_threshold_ = 1e-8;
142  tol_pr_step_ = 1e-6;
143  merit_memsize_ = 4;
144  merit_start_ = 1e-8;
145  std::string hessian_approximation = "exact";
146  std::string qpsol_plugin;
147  Dict qpsol_options;
148  print_header_ = true;
149 
150  // Read user options
151  for (auto&& op : opts) {
152  if (op.first=="max_iter") {
153  max_iter_ = op.second;
154  } else if (op.first=="max_iter_ls") {
155  max_iter_ls_ = op.second;
156  } else if (op.first=="c1") {
157  c1_ = op.second;
158  } else if (op.first=="beta") {
159  beta_ = op.second;
160  } else if (op.first=="lbfgs_memory") {
161  lbfgs_memory_ = op.second;
162  } else if (op.first=="tol_pr") {
163  tol_pr_ = op.second;
164  } else if (op.first=="tol_du") {
165  tol_du_ = op.second;
166  } else if (op.first=="tol_reg") {
167  tol_reg_ = op.second;
168  } else if (op.first=="regularize") {
169  regularize_ = op.second;
170  } else if (op.first=="codegen") {
171  codegen_ = op.second;
172  } else if (op.first=="reg_threshold") {
173  reg_threshold_ = op.second;
174  } else if (op.first=="tol_pr_step") {
175  tol_pr_step_ = op.second;
176  } else if (op.first=="merit_memsize") {
177  merit_memsize_ = op.second;
178  } else if (op.first=="merit_start") {
179  merit_start_ = op.second;
180  } else if (op.first=="hessian_approximation") {
181  hessian_approximation = op.second.to_string();
182  } else if (op.first=="name_x") {
183  name_x_ = op.second;
184  } else if (op.first=="print_x") {
185  print_x_ = op.second;
186  } else if (op.first=="qpsol") {
187  qpsol_plugin = op.second.to_string();
188  } else if (op.first=="qpsol_options") {
189  qpsol_options = op.second;
190  } else if (op.first=="print_header") {
191  print_header_ = op.second;
192  }
193  }
194 
195  // Gauss-Newton Hessian?
196  gauss_newton_ = hessian_approximation == "gauss-newton";
197 
198  // Name the components
199  if (name_x_.empty()) {
200  name_x_.resize(nx_);
201  for (casadi_int i=0; i<nx_; ++i) {
202  std::stringstream ss;
203  ss << "x" << i;
204  name_x_[i] = ss.str();
205  }
206  } else {
207  casadi_assert_dev(name_x_.size()==nx_);
208  }
209 
210  // Generate lifting functions
211  Function fg = oracle();
212  Function vdef_fcn, vinit_fcn;
213  fg.generate_lifted(vdef_fcn, vinit_fcn);
214  vinit_fcn_ = vinit_fcn;
215  alloc(vinit_fcn_);
216 
217  // Extract the expressions
218  std::vector<MX> vdef_in = vdef_fcn.mx_in();
219  std::vector<MX> vdef_out = vdef_fcn(vdef_in);
220 
221  // Get the dimensions
222  MX x = vdef_in.at(0);
223  MX p = vdef_in.at(1);
224  v_.resize(vdef_in.size()-2);
225  for (casadi_int i=0; i<v_.size(); ++i) {
226  v_[i].v = vdef_in.at(i+2);
227  v_[i].v_def = vdef_out.at(i+2);
228  v_[i].n = v_[i].v.nnz();
229  }
230 
231  // Scalar objective function
232  MX f;
233 
234  // Multipliers
235  MX g_lam;
236 
237  // Definition of the lifted dual variables
238  MX p_defL, gL_defL;
239 
240  if (gauss_newton_) {
241  // Least square objective
242  f = dot(vdef_out[0], vdef_out[0])/2;
243  gL_defL = vdef_out[0];
244  ngn_ = gL_defL.nnz();
245  } else {
246  // Scalar objective function
247  f = vdef_out[0];
248 
249  // Lagrange multipliers corresponding to the definition of the dependent variables
250  std::stringstream ss;
251  casadi_int i=0;
252  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
253  ss.str(std::string());
254  ss << "lam_x" << i++;
255  it->v_lam = MX::sym(ss.str(), it->v.sparsity());
256  }
257 
258  // Lagrange multipliers for the nonlinear constraints
259  g_lam = MX::sym("g_lam", ng_);
260 
261  if (verbose_) {
262  uout() << "Allocated intermediate variables." << std::endl;
263  }
264 
265  // Adjoint sweep to get the definitions of the lifted dual variables
266  // (Equation 3.8 in Albersmeyer2010)
267  std::vector<std::vector<MX> > aseed(1), asens(1);
268  aseed[0].push_back(1.0);
269  aseed[0].push_back(g_lam);
270  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
271  aseed[0].push_back(it->v_lam);
272  }
273  vdef_fcn->call_reverse(vdef_in, vdef_out, aseed, asens, true, false);
274  i=0;
275 
276  gL_defL = asens[0].at(i++);
277  if (gL_defL.is_null()) gL_defL = MX::zeros(x.sparsity()); // Needed?
278 
279  p_defL = asens[0].at(i++);
280  if (p_defL.is_null()) p_defL = MX::zeros(p.sparsity()); // Needed?
281 
282  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
283  it->v_defL = asens[0].at(i++);
284  if (it->v_defL.is_null()) {
285  it->v_defL = MX::zeros(it->v.sparsity());
286  }
287  }
288 
289  if (verbose_) {
290  uout() << "Generated the gradient of the Lagrangian." << std::endl;
291  }
292  }
293 
294  // Residual function
295 
296  // Inputs
297  std::vector<MX> res_fcn_in;
298  casadi_int n=0;
299  res_fcn_in.push_back(x); res_x_ = n++;
300  res_fcn_in.push_back(p); res_p_ = n++;
301  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
302  res_fcn_in.push_back(it->v); it->res_var = n++;
303  }
304  if (!gauss_newton_) {
305  res_fcn_in.push_back(g_lam); res_g_lam_ = n++;
306  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
307  res_fcn_in.push_back(it->v_lam); it->res_lam = n++;
308  }
309  }
310 
311  // Outputs
312  std::vector<MX> res_fcn_out;
313  n=0;
314  res_fcn_out.push_back(f); res_f_ = n++;
315  res_fcn_out.push_back(gL_defL); res_gl_ = n++;
316  res_fcn_out.push_back(vdef_out[1]); res_g_ = n++;
317  res_fcn_out.push_back(p_defL); res_p_d_ = n++;
318  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
319  res_fcn_out.push_back(it->v_def - it->v); it->res_d = n++;
320  }
321 
322  if (!gauss_newton_) {
323  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
324  res_fcn_out.push_back(it->v_defL - it->v_lam); it->res_lam_d = n++;
325  }
326  }
327 
328  // Generate function
329  Function res_fcn("res_fcn", res_fcn_in, res_fcn_out);
330  if (verbose_) {
331  uout() << "Generated residual function ( " << res_fcn.n_nodes() << " nodes)." << std::endl;
332  }
333 
334  // Declare difference vector d and substitute out p and v
335  std::stringstream ss;
336  casadi_int i=0;
337  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
338  ss.str(std::string());
339  ss << "d" << i++;
340  it->d = MX::sym(ss.str(), it->v.sparsity());
341  it->d_def = it->v_def - it->d;
342  }
343 
344  // Declare difference vector lam_d and substitute out lam
345  if (!gauss_newton_) {
346  casadi_int i=0;
347  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
348  ss.str(std::string());
349  ss << "d_lam" << i++;
350  it->d_lam = MX::sym(ss.str(), it->v.sparsity());
351  it->d_defL = it->v_defL - it->d_lam;
352  }
353  }
354 
355  // Variables to be substituted and their definitions
356  std::vector<MX> svar, sdef;
357  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
358  svar.push_back(it->v);
359  sdef.push_back(it->d_def);
360  }
361  if (!gauss_newton_) {
362  for (std::vector<Var>::reverse_iterator it=v_.rbegin(); it!=v_.rend(); ++it) {
363  svar.push_back(it->v_lam);
364  sdef.push_back(it->d_defL);
365  }
366  }
367 
368  std::vector<MX> ex(4);
369  ex[0] = f;
370  ex[1] = vdef_out[1];
371  ex[2] = gL_defL;
372  ex[3] = p_defL;
373 
374  substitute_inplace(svar, sdef, ex, false);
375  i=0;
376  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
377  it->d_def = sdef[i++];
378  }
379  if (!gauss_newton_) {
380  for (std::vector<Var>::reverse_iterator it=v_.rbegin(); it!=v_.rend(); ++it) {
381  it->d_defL = sdef[i++];
382  }
383  }
384 
385  MX f_z = ex[0];
386  MX g_z = ex[1];
387  MX gL_z = ex[2];
388  MX p_z = ex[3];
389 
390  // Modified function inputs
391  std::vector<MX> mfcn_in;
392  n=0;
393  mfcn_in.push_back(p); mod_p_ = n++;
394  mfcn_in.push_back(x); mod_x_ = n++;
395  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
396  mfcn_in.push_back(it->d); it->mod_var = n++;
397  }
398 
399  // Modified function outputs
400  n=0;
401  std::vector<MX> mfcn_out;
402  mfcn_out.push_back(g_z); mod_g_ = n++;
403 
404  // Add multipliers to function inputs
405  if (!gauss_newton_) {
406  n = mfcn_in.size();
407  mfcn_in.push_back(g_lam); mod_g_lam_ = n++;
408  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
409  mfcn_in.push_back(it->d_lam); it->mod_lam = n++;
410  }
411  }
412 
413  // Add gradient of the Lagrangian
414  n = mfcn_out.size();
415  mfcn_out.push_back(f_z); mod_f_ = n++;
416  mfcn_out.push_back(gL_z); mod_gl_ = n++;
417 
418  // Jacobian of the constraints
419  MX jac = MX::jacobian(mfcn_out[mod_g_], mfcn_in[mod_x_]);
420  if (verbose_) casadi_message("Formed Jacobian of the constraints.");
421 
422  // Hessian of the Lagrangian
423  MX hes = MX::jacobian(mfcn_out[mod_gl_], mfcn_in[mod_x_], {{"symmetric", !gauss_newton_}});
424  if (gauss_newton_) {
425  if (verbose_) casadi_message("Formed square root of Gauss-Newton Hessian.");
426  } else {
427  if (verbose_) casadi_message("Formed Hessian of the Lagrangian.");
428  }
429 
430  // Matrices in the reduced QP
431  n=0;
432  std::vector<MX> mat_out;
433  mat_out.push_back(jac); mat_jac_ = n++;
434  mat_out.push_back(hes); mat_hes_ = n++;
435  Function mat_fcn("mat_fcn", mfcn_in, mat_out);
436 
437  // Definition of intermediate variables
438  n = mfcn_out.size();
439  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
440  mfcn_out.push_back(it->d_def); it->mod_def = n++;
441  if (!gauss_newton_) {
442  mfcn_out.push_back(it->d_defL); it->mod_defL = n++;
443  }
444  }
445 
446  // Modifier function
447  Function mfcn("mfcn", mfcn_in, mfcn_out);
448 
449  // Directional derivative of Z
450  std::vector<std::vector<MX> > mfcn_fwdSeed(1, mfcn_in), mfcn_fwdSens(1, mfcn_out);
451 
452  // Linearization in the d-direction (see Equation (2.12) in Alberspeyer2010)
453  std::fill(mfcn_fwdSeed[0].begin(), mfcn_fwdSeed[0].end(), MX());
454  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
455  mfcn_fwdSeed[0][it->mod_var] = it->d;
456  if (!gauss_newton_) {
457  mfcn_fwdSeed[0][it->mod_lam] = it->d_lam;
458  }
459  }
460  mfcn->call_forward(mfcn_in, mfcn_out, mfcn_fwdSeed, mfcn_fwdSens, true, false);
461 
462  // Vector(s) b in Lifted Newton
463  MX b_gf = densify(mfcn_fwdSens[0][mod_gl_]);
464  MX b_g = densify(mfcn_fwdSens[0][mod_g_]);
465 
466  // Tangent function
467  std::vector<MX> vec_fcn_out;
468  n=0;
469  vec_fcn_out.push_back(b_gf); vec_gf_ = n++;
470  vec_fcn_out.push_back(b_g); vec_g_ = n++;
471  casadi_assert_dev(n==vec_fcn_out.size());
472 
473  Function vec_fcn("vec_fcn", mfcn_in, vec_fcn_out);
474  if (verbose_) {
475  uout() << "Generated linearization function ( " << vec_fcn.n_nodes()
476  << " nodes)." << std::endl;
477  }
478 
479  // Expression a + A*du in Lifted Newton (Section 2.1 in Alberspeyer2010)
480  MX du = MX::sym("du", nx_); // Step in u
481  MX g_dlam; // Step lambda_g
482  if (!gauss_newton_) {
483  g_dlam = MX::sym("g_dlam", g_lam.sparsity());
484  }
485 
486  // Interpret the Jacobian-vector multiplication as a forward directional derivative
487  std::fill(mfcn_fwdSeed[0].begin(), mfcn_fwdSeed[0].end(), MX());
488  mfcn_fwdSeed[0][mod_x_] = du;
489  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
490  mfcn_fwdSeed[0][it->mod_var] = -it->d;
491  }
492  if (!gauss_newton_) {
493  mfcn_fwdSeed[0][mod_g_lam_] = g_dlam;
494  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
495  mfcn_fwdSeed[0][it->mod_lam] = -it->d_lam;
496  }
497  }
498  mfcn->call_forward(mfcn_in, mfcn_out, mfcn_fwdSeed, mfcn_fwdSens, true, false);
499 
500  // Step expansion function inputs
501  n = mfcn_in.size();
502  mfcn_in.push_back(du); mod_du_ = n++;
503  if (!gauss_newton_) {
504  mfcn_in.push_back(g_dlam); mod_dlam_g_ = n++;
505  }
506 
507  // Step expansion function outputs
508  std::vector<MX> exp_fcn_out;
509  n=0;
510  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
511  exp_fcn_out.push_back(mfcn_fwdSens[0][it->mod_def]); it->exp_def = n++;
512  }
513 
514  if (!gauss_newton_) {
515  for (std::vector<Var>::iterator it=v_.begin(); it!=v_.end(); ++it) {
516  exp_fcn_out.push_back(mfcn_fwdSens[0][it->mod_defL]); it->exp_defL = n++;
517  }
518  }
519 
520  // Step expansion function
521  Function exp_fcn("exp_fcn", mfcn_in, exp_fcn_out);
522  if (verbose_) {
523  uout() << "Generated step expansion function ( " << exp_fcn.n_nodes() << " nodes)."
524  << std::endl;
525  }
526 
527  // Generate c code and load as DLL
528  if (codegen_) {
529  // Name of temporary file
530  std::string cname = temporary_file("tmp_casadi_scpgen", ".c");
531 
532  // Codegen the functions
533  CodeGenerator gen(cname);
534  gen.add(res_fcn);
535  gen.add(mat_fcn);
536  gen.add(vec_fcn);
537  gen.add(exp_fcn);
538 
539  // Generate code
540  if (verbose_) {
541  uout() << "Generating \"" << cname << "\"" << std::endl;
542  }
543  std::string name = cname.substr(0, cname.find_first_of('.'));
544  gen.generate();
545 
546  // Complile and run
547  if (verbose_) {
548  uout() << "Starting compilation" << std::endl;
549  }
550  time_t time1 = time(nullptr);
552  time_t time2 = time(nullptr);
553  double comp_time = difftime(time2, time1);
554  if (verbose_) {
555  uout() << "Compilation completed after " << comp_time << " s." << std::endl;
556  }
557 
558  // Load the generated code
559  res_fcn_ = external("res_fcn", compiler_);
560  mat_fcn_ = external("mat_fcn", compiler_);
561  vec_fcn_ = external("vec_fcn", compiler_);
562  exp_fcn_ = external("exp_fcn", compiler_);
563  } else {
564  mat_fcn_ = mat_fcn;
565  res_fcn_ = res_fcn;
566  vec_fcn_ = vec_fcn;
567  exp_fcn_ = exp_fcn;
568  }
569 
570  // Allocate a QP solver
572  spH_ = mtimes(spL_.T(), spL_);
574  casadi_assert(!qpsol_plugin.empty(), "'qpsol' option has not been set");
575  qpsol_ = conic("qpsol", qpsol_plugin, {{"h", spH_}, {"a", spA_}},
576  qpsol_options);
577  if (verbose_) {
578  uout() << "Allocated QP solver." << std::endl;
579  }
580 
581  if (verbose_) {
582  uout() << "NLP preparation completed" << std::endl;
583  }
584 
585  // Header
586  if (print_header_) {
587  uout() << "-------------------------------------------" << std::endl;
588  uout() << "This is casadi::SCPgen." << std::endl;
589  if (gauss_newton_) {
590  uout() << "Using Gauss-Newton Hessian" << std::endl;
591  } else {
592  uout() << "Using exact Hessian" << std::endl;
593  }
594 
595  // Count the total number of variables
596  casadi_int n_lifted = 0;
597  for (std::vector<Var>::const_iterator i=v_.begin(); i!=v_.end(); ++i) {
598  n_lifted += i->n;
599  }
600 
601  uout()
602  << std::endl
603  << "Number of reduced variables: "
604  << std::setw(9) << nx_ << std::endl
605  << "Number of reduced constraints: "
606  << std::setw(9) << ng_ << std::endl
607  << "Number of lifted variables/constraints: "
608  << std::setw(9) << n_lifted << std::endl
609  << "Number of parameters: "
610  << std::setw(9) << np_ << std::endl
611  << "Total number of variables: "
612  << std::setw(9) << (nx_+n_lifted) << std::endl
613  << "Total number of constraints: "
614  << std::setw(9) << (ng_+n_lifted) << std::endl
615  << std::endl;
616 
617  uout()
618  << "Iteration options:" << std::endl
619  << "{ \"max_iter\":" << max_iter_ << ", "
620  << "\"max_iter_ls\":" << max_iter_ls_ << ", "
621  << "\"c1\":" << c1_ << ", "
622  << "\"beta\":" << beta_ << ", "
623  << "\"merit_memsize\":" << merit_memsize_ << ", "
624  << "\"merit_start\":" << merit_start_ << ", "
625  << "\"regularize\":" << regularize_ << ", "
626  << std::endl << " "
627  << "\"tol_pr\":" << tol_pr_ << ", "
628  << "\"tol_du\":" << tol_du_ << ", "
629  << "\"tol_reg\":" << tol_reg_ << ", "
630  << "\"reg_threshold\":" << reg_threshold_ << "}" << std::endl
631  << std::endl;
632  }
633 
634  // Allocate memory, nonlfted problem
635  alloc_w(nx_, true); // dxk_
636  alloc_w(nx_+ng_, true); // dlam
637  alloc_w(nx_, true); // gfk_
638  alloc_w(nx_, true); // gL_
639  if (gauss_newton_) {
640  alloc_w(ngn_, true); // b_gn_
641  }
642 
643  // Allocate memory, lifted problem
644  for (casadi_int i=0; i<v_.size(); ++i) {
645  casadi_int n = v_[i].n;
646  alloc_w(n, true); // dx
647  alloc_w(n, true); // x0
648  alloc_w(n, true); // x
649  alloc_w(n, true); // res
650  if (!gauss_newton_) {
651  alloc_w(n, true); // lam
652  alloc_w(n, true); // dlam
653  alloc_w(n, true); // resL
654  }
655  }
656 
657  // Allocate QP
658  alloc_w(spH_.nnz(), true); // qpH_
659  alloc_w(spA_.nnz(), true); // qpA_
660  alloc_w(ng_, true); // qpB_
661  alloc_w(nx_, true); // qpH_times_du_
662  if (gauss_newton_) {
663  alloc_w(spL_.nnz(), true); // qpL_
664  alloc_w(ngn_, true); // qpG_
665  } else {
666  alloc_w(nx_, true); // qpG_
667  }
668 
669  // QP solver
670  alloc_w(nx_+ng_, true); // lbdz
671  alloc_w(nx_+ng_, true); // ubdz
672 
673  // Line-search memory
674  alloc_w(merit_memsize_, true);
675 
676  // Temporary work vectors
677  alloc(mat_fcn_);
678  alloc(res_fcn_);
679  alloc(vec_fcn_);
680  alloc(exp_fcn_);
681  if (gauss_newton_) {
682  alloc_w(ngn_); // casadi_mul to get GN Hessian
683  }
684  alloc(qpsol_);
685  }
686 
687  int Scpgen::init_mem(void* mem) const {
688  if (Nlpsol::init_mem(mem)) return 1;
689  auto m = static_cast<ScpgenMemory*>(mem);
690 
691  // Lifted memory
692  m->lifted_mem.resize(v_.size());
693  for (casadi_int i=0; i<v_.size(); ++i) {
694  m->lifted_mem[i].n = v_[i].n;
695  }
696 
697  return 0;
698  }
699 
700  void Scpgen::set_work(void* mem, const double**& arg, double**& res,
701  casadi_int*& iw, double*& w) const {
702  auto m = static_cast<ScpgenMemory*>(mem);
703 
704  // Set work in base classes
705  Nlpsol::set_work(mem, arg, res, iw, w);
706 
707  // Get work vectors, nonlifted problem
708  m->dxk = w; w += nx_;
709  m->dlam = w; w += nx_ + ng_;
710  m->gfk = w; w += nx_;
711  m->gL = w; w += nx_;
712  if (gauss_newton_) {
713  m->b_gn = w; w += ngn_;
714  }
715 
716  // Get work vectors, lifted problem
717  for (auto&& v : m->lifted_mem) {
718  v.dx = w; w += v.n;
719  v.x0 = w; w += v.n;
720  v.x = w; w += v.n;
721  v.res = w; w += v.n;
722  if (!gauss_newton_) {
723  v.lam = w; w += v.n;
724  v.dlam = w; w += v.n;
725  v.resL = w; w += v.n;
726  }
727  }
728 
729  // QP
730  m->qpH = w; w += spH_.nnz();
731  m->qpA = w; w += spA_.nnz();
732  m->qpB = w; w += ng_;
733  if (gauss_newton_) {
734  m->qpL = w; w += spL_.nnz();
735  m->qpG = w; w += ngn_;
736  } else {
737  m->qpG = w; w += nx_;
738  }
739  m->qpH_times_du = w; w += nx_;
740 
741  // QP solver
742  m->lbdz = w; w += nx_+ng_;
743  m->ubdz = w; w += nx_+ng_;
744 
745  // merit_mem
746  m->merit_mem = w; w += merit_memsize_;
747 
748  // Residual
749  for (auto&& v : m->lifted_mem) casadi_clear(v.res, v.n);
750  if (!gauss_newton_) {
751  for (auto&& v : m->lifted_mem) casadi_clear(v.resL, v.n);
752  }
753  }
754 
755  int Scpgen::solve(void* mem) const {
756  auto m = static_cast<ScpgenMemory*>(mem);
757  auto d_nlp = &m->d_nlp;
758 
759  if (!v_.empty()) {
760  // Initialize lifted variables using the generated function
761  std::fill_n(m->arg, vinit_fcn_.n_in(), nullptr);
762  m->arg[0] = d_nlp->z;
763  m->arg[1] = d_nlp->p;
764  std::fill_n(m->res, vinit_fcn_.n_out(), nullptr);
765  for (casadi_int i=0; i<v_.size(); ++i) {
766  m->res[i] = m->lifted_mem[i].x0;
767  }
768  vinit_fcn_(m->arg, m->res, m->iw, m->w, 0);
769  }
770  if (verbose_) {
771  uout() << "Passed initial guess" << std::endl;
772  }
773 
774  // Reset dual guess
775  casadi_clear(m->dlam, nx_+ng_);
776  if (!gauss_newton_) {
777  for (auto&& v : m->lifted_mem) {
778  casadi_clear(v.lam, v.n);
779  casadi_clear(v.dlam, v.n);
780  }
781  }
782 
783  // Reset line-search
784  m->merit_ind = 0;
785 
786  // Current guess for the primal solution
787  for (auto&& v : m->lifted_mem) {
788  casadi_copy(v.x0, v.n, v.x);
789  }
790 
791  // Get current time and reset timers
792  double time1 = clock();
793  m->t_eval_mat = m->t_eval_res = m->t_eval_vec = m->t_eval_exp = m->t_solve_qp = 0;
794 
795  // Initial evaluation of the residual function
796  eval_res(m);
797 
798  // Number of SQP iterations
799  m->iter_count = 0;
800 
801  // Reset last step-size
802  m->pr_step = 0;
803  m->du_step = 0;
804 
805  // Reset line-search
806  casadi_int ls_iter = 0;
807  bool ls_success = true;
808 
809  // Reset regularization
810  m->reg = 0;
811 
812  // Reset iteration message
813  m->iteration_note = nullptr;
814 
815  // MAIN OPTIMZATION LOOP
816  while (true) {
817 
818  // Evaluate the vectors in the condensed QP
819  eval_vec(m);
820 
821  // Evaluate the matrices in the condensed QP
822  eval_mat(m);
823 
824  // 1-norm of the primal infeasibility
825  double pr_inf = primalInfeasibility(m);
826 
827  // 1-norm of the dual infeasibility
828  double du_inf = dualInfeasibility(m);
829 
830  // Print header occasionally
831  if (m->iter_count % 10 == 0) printIteration(m, uout());
832 
833  // Printing information about the actual iterate
834  printIteration(m, uout(), m->iter_count, d_nlp->objective, pr_inf, du_inf, m->reg,
835  ls_iter, ls_success);
836 
837  // Checking convergence criteria
838  bool converged = pr_inf <= tol_pr_ && m->pr_step <= tol_pr_step_ && m->reg <= tol_reg_;
839  converged = converged && du_inf <= tol_du_;
840  if (converged) {
841  uout() << std::endl << "casadi::SCPgen: Convergence achieved after "
842  << m->iter_count << " iterations." << std::endl;
843  break;
844  }
845 
846  if (m->iter_count >= max_iter_) {
847  uout() << std::endl;
848  uout() << "casadi::SCPgen: Maximum number of iterations reached." << std::endl;
849  break;
850  }
851 
852  // Check if not-a-number
853  if (d_nlp->objective!=d_nlp->objective || m->pr_step != m->pr_step || pr_inf != pr_inf) {
854  uout() << "casadi::SCPgen: Aborted, nan detected" << std::endl;
855  break;
856  }
857 
858  // Start a new iteration
859  m->iter_count++;
860 
861  // Regularize the QP
862  if (regularize_) {
863  regularize(m);
864  }
865 
866  // Solve the condensed QP
867  solve_qp(m);
868 
869  // Expand the step
870  eval_exp(m);
871 
872  // Line-search to take the step
873  line_search(m, ls_iter, ls_success);
874  }
875 
876  double time2 = clock();
877  m->t_mainloop = (time2-time1)/CLOCKS_PER_SEC;
878 
879  // Store optimal value
880  uout() << "optimal cost = " << d_nlp->objective << std::endl;
881 
882  // Write timers
883  if (print_time_) {
884  uout() << std::endl;
885  uout() << "time spent in eval_mat: "
886  << std::setw(9) << m->t_eval_mat << " s." << std::endl;
887  uout() << "time spent in eval_res: "
888  << std::setw(9) << m->t_eval_res << " s." << std::endl;
889  uout() << "time spent in eval_vec: "
890  << std::setw(9) << m->t_eval_vec << " s." << std::endl;
891  uout() << "time spent in eval_exp: "
892  << std::setw(9) << m->t_eval_exp << " s." << std::endl;
893  uout() << "time spent in solve_qp: "
894  << std::setw(9) << m->t_solve_qp << " s." << std::endl;
895  uout() << "time spent in main loop: "
896  << std::setw(9) << m->t_mainloop << " s." << std::endl;
897  }
898 
899  uout() << std::endl;
900  return 0;
901  }
902 
904  auto d_nlp = &m->d_nlp;
905  // L1-norm of the primal infeasibility
906  double pr_inf = 0;
907  // Inequality constraint violations
908  pr_inf += casadi_sum_viol(nx_+ng_, d_nlp->z, d_nlp->lbz, d_nlp->ubz);
909  // Lifted variables
910  for (auto&& v : m->lifted_mem) pr_inf += casadi_norm_1(v.n, v.res);
911  return pr_inf;
912  }
913 
915  // L1-norm of the dual infeasibility
916  return casadi_norm_1(nx_, m->gL);
917  }
918 
919  void Scpgen::printIteration(ScpgenMemory* m, std::ostream &stream) const {
920  stream << std::setw(4) << "iter";
921  stream << std::setw(14) << "objective";
922  stream << std::setw(11) << "inf_pr";
923  stream << std::setw(11) << "inf_du";
924  stream << std::setw(11) << "pr_step";
925  stream << std::setw(11) << "du_step";
926  stream << std::setw(8) << "lg(rg)";
927  stream << std::setw(3) << "ls";
928  stream << ' ';
929 
930  // Print variables
931  for (std::vector<casadi_int>::const_iterator i=print_x_.begin(); i!=print_x_.end(); ++i) {
932  stream << std::setw(9) << name_x_.at(*i);
933  }
934 
935  stream << std::endl;
936  stream.unsetf(std::ios::floatfield);
937  }
938 
939  void Scpgen::printIteration(ScpgenMemory* m, std::ostream &stream, casadi_int iter, double obj,
940  double pr_inf, double du_inf, double rg, casadi_int ls_trials,
941  bool ls_success) const {
942  auto d_nlp = &m->d_nlp;
943  stream << std::setw(4) << iter;
944  stream << std::scientific;
945  stream << std::setw(14) << std::setprecision(6) << obj;
946  stream << std::setw(11) << std::setprecision(2) << pr_inf;
947  stream << std::setw(11);
948  stream << std::setprecision(2) << du_inf;
949  stream << std::setw(11) << std::setprecision(2) << m->pr_step;
950  stream << std::setw(11);
951  stream << std::setprecision(2) << m->du_step;
952  stream << std::fixed;
953  if (rg>0) {
954  stream << std::setw(8) << std::setprecision(2) << log10(rg);
955  } else {
956  stream << std::setw(8) << "-";
957  }
958  stream << std::setw(3) << ls_trials;
959  stream << (ls_success ? ' ' : 'F');
960 
961  // Print variables
962  for (std::vector<casadi_int>::const_iterator i=print_x_.begin(); i!=print_x_.end(); ++i) {
963  stream << std::setw(9) << std::setprecision(4) << d_nlp->z[*i];
964  }
965 
966  // Print note
967  if (m->iteration_note) {
968  stream << " " << m->iteration_note;
969  m->iteration_note = nullptr;
970  }
971 
972  stream.unsetf(std::ios::floatfield);
973  stream << std::endl;
974  }
975 
977  auto d_nlp = &m->d_nlp;
978  // Get current time
979  double time1 = clock();
980 
981  // Inputs
982  std::fill_n(m->arg, mat_fcn_.n_in(), nullptr);
983  m->arg[mod_p_] = d_nlp->p; // Parameters
984  m->arg[mod_x_] = d_nlp->z; // Primal step/variables
985  for (size_t i=0; i<v_.size(); ++i) {
986  m->arg[v_[i].mod_var] = m->lifted_mem[i].res;
987  }
988  if (!gauss_newton_) { // Dual steps/variables
989  m->arg[mod_g_lam_] = d_nlp->lam + nx_;
990  for (size_t i=0; i<v_.size(); ++i) {
991  m->arg[v_[i].mod_lam] = m->lifted_mem[i].resL;
992  }
993  }
994 
995  // Outputs
996  std::fill_n(m->res, mat_fcn_.n_out(), nullptr);
997  m->res[mat_jac_] = m->qpA; // Condensed Jacobian
998  m->res[mat_hes_] = gauss_newton_ ? m->qpL : m->qpH; // Condensed Hessian
999 
1000  // Calculate condensed QP matrices
1001  mat_fcn_(m->arg, m->res, m->iw, m->w, 0);
1002 
1003  if (gauss_newton_) {
1004  // Gauss-Newton Hessian
1005  casadi_clear(m->qpH, spH_.nnz());
1006  casadi_mtimes(m->qpL, spL_, m->qpL, spL_, m->qpH, spH_, m->w, true);
1007 
1008  // Gradient of the objective in Gauss-Newton
1009  casadi_clear(m->gfk, nx_);
1010  casadi_mv(m->qpL, spL_, m->b_gn, m->gfk, true);
1011  }
1012 
1013  // Calculate the gradient of the lagrangian
1014  casadi_copy(m->gfk, nx_, m->gL);
1015  casadi_axpy(nx_, 1., d_nlp->lam, m->gL);
1016  casadi_mv(m->qpA, spA_, d_nlp->lam + nx_, m->gL, true);
1017 
1018  double time2 = clock();
1019  m->t_eval_mat += (time2-time1)/CLOCKS_PER_SEC;
1020  }
1021 
1023  auto d_nlp = &m->d_nlp;
1024  // Get current time
1025  double time1 = clock();
1026 
1027  // Inputs
1028  std::fill_n(m->arg, res_fcn_.n_in(), nullptr);
1029  m->arg[res_p_] = d_nlp->p; // Parameters
1030  m->arg[res_x_] = d_nlp->z; // Non-lifted primal variables
1031  for (size_t i=0; i<v_.size(); ++i) { // Lifted primal variables
1032  m->arg[v_[i].res_var] = m->lifted_mem[i].x;
1033  }
1034  if (!gauss_newton_) {
1035  m->arg[res_g_lam_] = nullptr; // Non-lifted dual variables
1036  for (size_t i=0; i<v_.size(); ++i) { // Lifted dual variables
1037  m->arg[v_[i].res_lam] = m->lifted_mem[i].lam;
1038  }
1039  }
1040 
1041  // Outputs
1042  std::fill_n(m->res, res_fcn_.n_out(), nullptr);
1043  m->res[res_f_] = &d_nlp->objective; // Objective
1044  m->res[res_gl_] = gauss_newton_ ? m->b_gn : m->gfk; // Objective gradient
1045  m->res[res_g_] = d_nlp->z + nx_; // Constraints
1046  for (size_t i=0; i<v_.size(); ++i) {
1047  m->res[v_[i].res_d] = m->lifted_mem[i].res;
1048  if (!gauss_newton_) {
1049  m->res[v_[i].res_lam_d] = m->lifted_mem[i].resL;
1050  }
1051  }
1052  m->res[res_p_d_] = d_nlp->lam_p; // Parameter sensitivities
1053 
1054  // Evaluate residual function
1055  res_fcn_(m->arg, m->res, m->iw, m->w, 0);
1056 
1057  double time2 = clock();
1058  m->t_eval_res += (time2-time1)/CLOCKS_PER_SEC;
1059  }
1060 
1062  auto d_nlp = &m->d_nlp;
1063  // Get current time
1064  double time1 = clock();
1065 
1066  // Inputs
1067  std::fill_n(m->arg, vec_fcn_.n_in(), nullptr);
1068  m->arg[mod_p_] = d_nlp->p; // Parameters
1069  m->arg[mod_x_] = d_nlp->z; // Primal step/variables
1070  for (size_t i=0; i<v_.size(); ++i) {
1071  m->arg[v_[i].mod_var] = m->lifted_mem[i].res;
1072  }
1073  if (!gauss_newton_) {
1074  m->arg[mod_g_lam_] = nullptr; // Dual steps/variables
1075  for (size_t i=0; i<v_.size(); ++i) {
1076  m->arg[v_[i].mod_lam] = m->lifted_mem[i].resL;
1077  }
1078  }
1079 
1080  // Outputs
1081  std::fill_n(m->res, vec_fcn_.n_out(), nullptr);
1082  m->res[vec_gf_] = m->qpG;
1083  m->res[vec_g_] = m->qpB;
1084 
1085  // Calculate condensed QP vectors
1086  vec_fcn_(m->arg, m->res, m->iw, m->w, 0);
1087 
1088  // Linear offset in the reduced QP
1089  casadi_scal(ng_, -1., m->qpB);
1090  casadi_axpy(ng_, 1., d_nlp->z + nx_, m->qpB);
1091 
1092  // Gradient of the objective in the reduced QP
1093  if (gauss_newton_) {
1094  casadi_axpy(ngn_, -1., m->qpG, m->b_gn);
1095  } else {
1096  casadi_axpy(nx_, -1., m->qpG, m->gfk);
1097  }
1098 
1099  double time2 = clock();
1100  m->t_eval_vec += (time2-time1)/CLOCKS_PER_SEC;
1101  }
1102 
1104  casadi_assert_dev(nx_==2 && spH_.is_dense());
1105 
1106  // Regularization
1107  m->reg = 0;
1108 
1109  // Check the smallest eigenvalue of the Hessian
1110  double a = m->qpH[0];
1111  double b = m->qpH[2];
1112  double c = m->qpH[1];
1113  double d = m->qpH[3];
1114 
1115  // Make sure no not a numbers
1116  casadi_assert_dev(a==a && b==b && c==c && d==d);
1117 
1118  // Make sure symmetric
1119  if (b!=c) {
1120  if (fabs(b-c)>=1e-10) casadi_warning("Hessian is not symmetric: "
1121  + str(b) + " != " + str(c));
1122  m->qpH[1] = c = b;
1123  }
1124 
1125  double eig_smallest = (a+d)/2 - std::sqrt(4*b*c + (a-d)*(a-d))/2;
1126  if (eig_smallest<reg_threshold_) {
1127  // Regularization
1128  m->reg = reg_threshold_-eig_smallest;
1129  m->qpH[0] += m->reg;
1130  m->qpH[3] += m->reg;
1131  }
1132  }
1133 
1135  auto d_nlp = &m->d_nlp;
1136  // Get current time
1137  double time1 = clock();
1138 
1139  // Get bounds on step
1140  casadi_copy(d_nlp->lbz, nx_+ng_, m->lbdz);
1141  casadi_copy(d_nlp->ubz, nx_+ng_, m->ubdz);
1142  casadi_axpy(nx_, -1., d_nlp->z, m->lbdz);
1143  casadi_axpy(nx_, -1., d_nlp->z, m->ubdz);
1144  casadi_axpy(ng_, -1., m->qpB, m->lbdz + nx_);
1145  casadi_axpy(ng_, -1., m->qpB, m->ubdz + nx_);
1146 
1147  // Inputs
1148  std::fill_n(m->arg, qpsol_.n_in(), nullptr);
1149  m->arg[CONIC_H] = m->qpH;
1150  m->arg[CONIC_G] = m->gfk;
1151  m->arg[CONIC_A] = m->qpA;
1152  m->arg[CONIC_LBX] = m->lbdz;
1153  m->arg[CONIC_UBX] = m->ubdz;
1154  m->arg[CONIC_LBA] = m->lbdz + nx_;
1155  m->arg[CONIC_UBA] = m->ubdz + nx_;
1156 
1157  // Outputs
1158  std::fill_n(m->res, qpsol_.n_out(), nullptr);
1159  m->res[CONIC_X] = m->dxk; // Condensed primal step
1160  m->res[CONIC_LAM_X] = m->dlam; // Multipliers (simple bounds)
1161  m->res[CONIC_LAM_A] = m->dlam + nx_; // Multipliers (linear bounds)
1162 
1163  // Solve the QP
1164  qpsol_(m->arg, m->res, m->iw, m->w);
1165 
1166  // Calculate penalty parameter of merit function
1167  m->sigma = std::max(merit_start_, 1.01*casadi_norm_inf(nx_+ng_, m->dlam));
1168 
1169  // Calculate step in multipliers
1170  casadi_axpy(nx_ + ng_, -1., d_nlp->lam, m->dlam);
1171 
1172  double time2 = clock();
1173  m->t_solve_qp += (time2-time1)/CLOCKS_PER_SEC;
1174  }
1175 
1176  void Scpgen::line_search(ScpgenMemory* m, casadi_int& ls_iter, bool& ls_success) const {
1177  auto d_nlp = &m->d_nlp;
1178  // Make sure that we have a decent direction
1179  if (!gauss_newton_) {
1180  // Get the curvature in the step direction
1181  double gain = casadi_bilin(m->qpH, spH_, m->dxk, m->dxk);
1182  if (gain < 0) {
1183  m->iteration_note = "Hessian indefinite in the search direction";
1184  }
1185  }
1186 
1187  // Calculate L1-merit function in the actual iterate
1188  double l1_infeas = primalInfeasibility(m);
1189 
1190  // Right-hand side of Armijo condition
1191  double F_sens = casadi_dot(nx_, m->dxk, m->gfk);
1192  double L1dir = F_sens - m->sigma * l1_infeas;
1193  double L1merit = d_nlp->objective + m->sigma * l1_infeas;
1194 
1195  // Storing the actual merit function value in a list
1196  m->merit_mem[m->merit_ind] = L1merit;
1197  ++m->merit_ind %= merit_memsize_;
1198 
1199  // Calculating maximal merit function value so far
1200  double meritmax = m->merit_mem[0];
1201  for (size_t i=1; i<merit_memsize_ && i<m->iter_count; ++i) {
1202  if (meritmax < m->merit_mem[i]) meritmax = m->merit_mem[i];
1203  }
1204 
1205  // Stepsize
1206  double t = 1.0, t_prev = 0.0;
1207 
1208  // Merit function value in candidate
1209  double L1merit_cand = 0;
1210 
1211  // Reset line-search counter, success marker
1212  ls_iter = 0;
1213  ls_success = false;
1214 
1215  // Line-search
1216  //if (verbose_) casadi_message("Starting line-search");
1217 
1218  // Line-search loop
1219  while (true) {
1220  // Take the primal step
1221  double dt = t-t_prev;
1222  casadi_axpy(nx_, dt, m->dxk, d_nlp->z);
1223  for (auto&& v : m->lifted_mem) casadi_axpy(v.n, dt, v.dx, v.x);
1224 
1225  // Take the dual step
1226  casadi_axpy(nx_+ng_, dt, m->dlam, d_nlp->lam);
1227  if (!gauss_newton_) {
1228  for (auto&& v : m->lifted_mem) casadi_axpy(v.n, dt, v.dlam, v.lam);
1229  }
1230 
1231  // Evaluate residual function to get objective and constraints
1232  // (and residuals for the next iteration)
1233  eval_res(m);
1234  ls_iter++;
1235 
1236  // Calculating merit-function in candidate
1237  l1_infeas = primalInfeasibility(m);
1238  L1merit_cand = d_nlp->objective + m->sigma * l1_infeas;
1239  if (L1merit_cand <= meritmax + t * c1_ * L1dir) {
1240 
1241  // Accepting candidate
1242  ls_success = true;
1243  //if (verbose_) casadi_message("Line-search completed, candidate accepted");
1244  break;
1245  }
1246 
1247  // Line-search not successful, but we accept it.
1248  if (ls_iter == max_iter_ls_) {
1249  //if (verbose_) casadi_message("Line-search completed, maximum number of iterations");
1250  break;
1251  }
1252 
1253  // Backtracking
1254  t_prev = t;
1255  t = beta_ * t;
1256  }
1257 
1258  // Calculate primal step-size
1259  m->pr_step = casadi_norm_1(nx_, m->dxk);
1260  for (auto&& v : m->lifted_mem) m->pr_step += casadi_norm_1(v.n, v.dx);
1261  m->pr_step *= t;
1262 
1263  // Calculate the dual step-size
1264  m->du_step = casadi_norm_1(ng_, m->dlam +nx_) + casadi_norm_1(nx_, m->dlam);
1265  for (auto&& v : m->lifted_mem) m->du_step += casadi_norm_1(v.n, v.dlam);
1266  m->du_step *= t;
1267  }
1268 
1270  auto d_nlp = &m->d_nlp;
1271  // Get current time
1272  double time1 = clock();
1273 
1274  // Inputs
1275  std::fill_n(m->arg, exp_fcn_.n_in(), nullptr);
1276  m->arg[mod_p_] = d_nlp->p; // Parameter
1277  m->arg[mod_du_] = m->dxk; // Primal step
1278  m->arg[mod_x_] = d_nlp->z; // Primal variables
1279  for (size_t i=0; i<v_.size(); ++i) {
1280  m->arg[v_[i].mod_var] = m->lifted_mem[i].res;
1281  }
1282  if (!gauss_newton_) {
1283  m->arg[mod_dlam_g_] = m->dlam + nx_; // Dual variables
1284  m->arg[mod_g_lam_] = d_nlp->lam + nx_; // Dual step
1285  for (size_t i=0; i<v_.size(); ++i) {
1286  m->arg[v_[i].mod_lam] = m->lifted_mem[i].resL;
1287  }
1288  }
1289 
1290  // Outputs
1291  std::fill_n(m->res, exp_fcn_.n_out(), nullptr);
1292  for (casadi_int i=0; i<v_.size(); ++ i) {
1293  m->res[v_[i].exp_def] = m->lifted_mem[i].dx; // Expanded primal step
1294  if (!gauss_newton_) {
1295  m->res[v_[i].exp_defL] = m->lifted_mem[i].dlam; // Expanded dual step
1296  }
1297  }
1298 
1299  // Perform the step expansion
1300  exp_fcn_(m->arg, m->res, m->iw, m->w, 0);
1301 
1302  double time2 = clock();
1303  m->t_eval_exp += (time2-time1)/CLOCKS_PER_SEC;
1304  }
1305 
1306  Dict Scpgen::get_stats(void* mem) const {
1307  Dict stats = Nlpsol::get_stats(mem);
1308  auto m = static_cast<ScpgenMemory*>(mem);
1309  stats["t_eval_mat"] = m->t_eval_mat;
1310  stats["t_eval_res"] = m->t_eval_res;
1311  stats["t_eval_vec"] = m->t_eval_vec;
1312  stats["t_eval_exp"] = m->t_eval_exp;
1313  stats["t_solve_qp"] = m->t_solve_qp;
1314  stats["t_mainloop"] = m->t_mainloop;
1315  stats["iter_count"] = m->iter_count;
1316  return stats;
1317  }
1318 
1319 
1320 
1321 } // namespace casadi
Helper class for C code generation.
void add(const Function &f, bool with_jac_sparsity=false)
Add a function (name generated)
std::string generate(const std::string &prefix="")
Generate file(s)
virtual void call_forward(const std::vector< MX > &arg, const std::vector< MX > &res, const std::vector< std::vector< MX > > &fseed, std::vector< std::vector< MX > > &fsens, bool always_inline, bool never_inline) const
Forward mode AD, virtual functions overloaded in derived classes.
std::string compiler_plugin_
Just-in-time compiler.
virtual void call_reverse(const std::vector< MX > &arg, const std::vector< MX > &res, const std::vector< std::vector< MX > > &aseed, std::vector< std::vector< MX > > &asens, bool always_inline, bool never_inline) const
Reverse mode, virtual functions overloaded in derived classes.
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.
Function object.
Definition: function.hpp:60
const MX mx_in(casadi_int ind) const
Get symbolic primitives equivalent to the input expressions.
Definition: function.cpp:1584
const Sparsity & sparsity_out(casadi_int ind) const
Get sparsity of a given output.
Definition: function.cpp:1031
casadi_int n_nodes() const
Number of nodes in the algorithm.
Definition: function.cpp:1765
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:823
casadi_int n_in() const
Get the number of function inputs.
Definition: function.cpp:819
void generate_lifted(Function &vdef_fcn, Function &vinit_fcn) const
Extract the functions needed for the Lifted Newton method.
Definition: function.cpp:1701
casadi_int nnz() const
Get the number of (structural) non-zero elements.
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
static MX zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
bool is_null() const
Is a null pointer?
Importer.
Definition: importer.hpp:86
MX - Matrix expression.
Definition: mx.hpp:92
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:592
static MX jacobian(const MX &f, const MX &x, const Dict &opts=Dict())
Definition: mx.cpp:1822
NLP solver storage class.
Definition: nlpsol_impl.hpp:59
Dict get_stats(void *mem) const override
Get all statistics.
Definition: nlpsol.cpp:1162
static const Options options_
Options.
void init(const Dict &opts) override
Initialize.
Definition: nlpsol.cpp:420
casadi_int ng_
Number of constraints.
Definition: nlpsol_impl.hpp:69
int init_mem(void *mem) const override
Initalize memory block.
Definition: nlpsol.cpp:603
casadi_int np_
Number of parameters.
Definition: nlpsol_impl.hpp:72
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:795
const Function & oracle() const override
Get oracle.
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)
static const std::string meta_doc
A documentation string.
Definition: scpgen.hpp:272
void eval_mat(ScpgenMemory *m) const
Definition: scpgen.cpp:976
casadi_int mod_dlam_g_
Definition: scpgen.hpp:244
Function exp_fcn_
Step expansion.
Definition: scpgen.hpp:235
void eval_vec(ScpgenMemory *m) const
Definition: scpgen.cpp:1061
casadi_int max_iter_ls_
Definition: scpgen.hpp:197
double beta_
Definition: scpgen.hpp:196
casadi_int res_p_
Definition: scpgen.hpp:238
casadi_int res_x_
Definition: scpgen.hpp:238
int init_mem(void *mem) const override
Initalize memory block.
Definition: scpgen.cpp:687
static const Options options_
Options.
Definition: scpgen.hpp:105
Function res_fcn_
Residual function.
Definition: scpgen.hpp:224
casadi_int res_g_
Definition: scpgen.hpp:239
casadi_int mod_g_lam_
Definition: scpgen.hpp:242
Scpgen(const std::string &name, const Function &nlp)
Definition: scpgen.cpp:53
casadi_int vec_g_
Definition: scpgen.hpp:232
double merit_start_
Definition: scpgen.hpp:199
bool gauss_newton_
use Gauss-Newton Hessian
Definition: scpgen.hpp:170
void solve_qp(ScpgenMemory *m) const
Definition: scpgen.cpp:1134
bool codegen_
Enable Code generation.
Definition: scpgen.hpp:203
casadi_int res_f_
Definition: scpgen.hpp:239
casadi_int mod_f_
Definition: scpgen.hpp:243
casadi_int mat_jac_
Definition: scpgen.hpp:228
double tol_du_
Tolerance on dual infeasibility.
Definition: scpgen.hpp:182
casadi_int mat_hes_
Definition: scpgen.hpp:228
std::vector< std::string > name_x_
Definition: scpgen.hpp:260
casadi_int mod_g_
Definition: scpgen.hpp:243
casadi_int mod_gl_
Definition: scpgen.hpp:243
void line_search(ScpgenMemory *m, casadi_int &ls_iter, bool &ls_success) const
Definition: scpgen.cpp:1176
bool regularize_
Regularization.
Definition: scpgen.hpp:209
casadi_int mod_x_
Definition: scpgen.hpp:242
Sparsity spA_
Definition: scpgen.hpp:266
casadi_int merit_memsize_
Definition: scpgen.hpp:198
casadi_int res_g_lam_
Definition: scpgen.hpp:238
std::vector< Var > v_
Definition: scpgen.hpp:257
casadi_int res_gl_
Definition: scpgen.hpp:239
void regularize(ScpgenMemory *m) const
Definition: scpgen.cpp:1103
Function qpsol_
QP solver for the subproblems.
Definition: scpgen.hpp:167
Function vinit_fcn_
Generate initial guess for lifted variables.
Definition: scpgen.hpp:221
double tol_pr_step_
stopping criterion for the stepsize
Definition: scpgen.hpp:188
~Scpgen() override
Definition: scpgen.cpp:57
double tol_pr_
Tolerance on primal infeasibility.
Definition: scpgen.hpp:179
Sparsity spL_
Definition: scpgen.hpp:266
void eval_exp(ScpgenMemory *m) const
Definition: scpgen.cpp:1269
int solve(void *mem) const override
Definition: scpgen.cpp:755
casadi_int max_iter_
maximum number of sqp iterations
Definition: scpgen.hpp:173
double primalInfeasibility(ScpgenMemory *m) const
Definition: scpgen.cpp:903
double c1_
Definition: scpgen.hpp:195
casadi_int mod_du_
Definition: scpgen.hpp:244
std::vector< casadi_int > print_x_
Definition: scpgen.hpp:263
void printIteration(ScpgenMemory *m, std::ostream &stream) const
Definition: scpgen.cpp:919
void eval_res(ScpgenMemory *m) const
Definition: scpgen.cpp:1022
casadi_int lbfgs_memory_
Memory size of L-BFGS method.
Definition: scpgen.hpp:176
Function mat_fcn_
Definition: scpgen.hpp:227
Function vec_fcn_
Quadratic approximation.
Definition: scpgen.hpp:231
casadi_int mod_p_
Definition: scpgen.hpp:242
casadi_int res_p_d_
Definition: scpgen.hpp:238
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: scpgen.cpp:700
bool print_time_
Print timers.
Definition: scpgen.hpp:218
double reg_threshold_
Definition: scpgen.hpp:215
casadi_int vec_gf_
Definition: scpgen.hpp:232
static Nlpsol * creator(const std::string &name, const Function &nlp)
Create a new NLP Solver.
Definition: scpgen.hpp:99
Dict get_stats(void *mem) const override
Get all statistics.
Definition: scpgen.cpp:1306
Sparsity spH_
Definition: scpgen.hpp:266
bool print_header_
Definition: scpgen.hpp:269
casadi_int ngn_
Definition: scpgen.hpp:212
double dualInfeasibility(ScpgenMemory *m) const
Definition: scpgen.cpp:914
double tol_reg_
Tolerance on regularization.
Definition: scpgen.hpp:185
void init(const Dict &opts) override
Initialize.
Definition: scpgen.cpp:126
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
bool is_dense() const
Is dense?
Definition: sparsity.cpp:273
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
int CASADI_NLPSOL_SCPGEN_EXPORT casadi_register_nlpsol_scpgen(Nlpsol::Plugin *plugin)
Definition: scpgen.cpp:39
T1 casadi_norm_1(casadi_int n, const T1 *x)
NORM_1: ||x||_1 -> return.
@ CONIC_UBA
dense, (nc x 1)
Definition: conic.hpp:181
@ 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_LBX
dense, (n x 1)
Definition: conic.hpp:183
T1 casadi_bilin(const T1 *A, const casadi_int *sp_A, const T1 *x, const T1 *y)
std::string temporary_file(const std::string &prefix, const std::string &suffix)
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.
@ OT_STRINGVECTOR
@ 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.
void casadi_mtimes(const T1 *x, const casadi_int *sp_x, const T1 *y, const casadi_int *sp_y, T1 *z, const casadi_int *sp_z, T1 *w, casadi_int tr)
Sparse matrix-matrix multiplication: z <- z + x*y.
T1 casadi_dot(casadi_int n, const T1 *x, const T1 *y)
Inner product.
T dot(const std::vector< T > &a, const std::vector< T > &b)
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_SCPGEN_EXPORT casadi_load_nlpsol_scpgen()
Definition: scpgen.cpp:49
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.
Function external(const std::string &name, const Importer &li, const Dict &opts)
Load a just-in-time compiled external function.
Definition: external.cpp:42
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_LAM_X
The dual solution corresponding to simple bounds.
Definition: conic.hpp:207
casadi_nlpsol_data< double > d_nlp
Definition: nlpsol_impl.hpp:42
Options metadata for a class.
Definition: options.hpp:40
const char * iteration_note
Definition: scpgen.hpp:65
casadi_int merit_ind
Definition: scpgen.hpp:72
std::vector< VarMem > lifted_mem
Definition: scpgen.hpp:55
double * merit_mem
Definition: scpgen.hpp:71
casadi_int iter_count
Definition: scpgen.hpp:76