kinsol_interface.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "kinsol_interface.hpp"
27 
28 namespace casadi {
29 
30  extern "C"
31  int CASADI_ROOTFINDER_KINSOL_EXPORT
32  casadi_register_rootfinder_kinsol(Rootfinder::Plugin* plugin) {
33  plugin->creator = KinsolInterface::creator;
34  plugin->name = "kinsol";
35  plugin->doc = KinsolInterface::meta_doc.c_str();
36  plugin->version = CASADI_VERSION;
37  plugin->options = &KinsolInterface::options_;
38  return 0;
39  }
40 
41  extern "C"
42  void CASADI_ROOTFINDER_KINSOL_EXPORT casadi_load_rootfinder_kinsol() {
44  }
45 
65  KinsolInterface::KinsolInterface(const std::string& name, const Function& f)
66  : Rootfinder(name, f) {
67 
68  u_scale_ = nullptr;
69  f_scale_ = nullptr;
70 
71  // Default options
72  exact_jac_ = true;
74  max_iter_ = 0;
75  print_level_ = 0;
76  maxl_ = 0;
77  upper_bandwidth_ = -1;
78  lower_bandwidth_ = -1;
79  use_preconditioner_ = false;
80  abstol_ = 1e-6;
81  }
82 
84  if (u_scale_) N_VDestroy_Serial(u_scale_);
85  if (f_scale_) N_VDestroy_Serial(f_scale_);
86  clear_mem();
87  }
88 
91  {{"max_iter",
92  {OT_INT,
93  "Maximum number of Newton iterations. Putting 0 sets the default value of KinSol."}},
94  {"print_level",
95  {OT_INT,
96  "Verbosity level"}},
97  {"abstol",
98  {OT_DOUBLE,
99  "Stopping criterion tolerance"}},
100  {"linear_solver_type",
101  {OT_STRING,
102  "dense|banded|iterative|user_defined"}},
103  {"upper_bandwidth",
104  {OT_INT,
105  "Upper bandwidth for banded linear solvers"}},
106  {"lower_bandwidth",
107  {OT_INT,
108  "Lower bandwidth for banded linear solvers"}},
109  {"max_krylov",
110  {OT_INT,
111  "Maximum Krylov space dimension"}},
112  {"exact_jacobian",
113  {OT_BOOL,
114  "Use exact Jacobian information"}},
115  {"iterative_solver",
116  {OT_STRING,
117  "gmres|bcgstab|tfqmr"}},
118  {"f_scale",
120  "Equation scaling factors"}},
121  {"u_scale",
123  "Variable scaling factors"}},
124  {"pretype",
125  {OT_STRING,
126  "Type of preconditioner"}},
127  {"use_preconditioner",
128  {OT_BOOL,
129  "Precondition an iterative solver"}},
130  {"strategy",
131  {OT_STRING,
132  "Globalization strategy"}},
133  {"disable_internal_warnings",
134  {OT_BOOL,
135  "Disable KINSOL internal warning messages"}}
136  }
137  };
138 
139  void KinsolInterface::init(const Dict& opts) {
140  // Initialize the base classes
141  Rootfinder::init(opts);
142 
143  // Default (temporary) options
144  std::string strategy = "none";
145  std::vector<double> u_scale;
146  std::vector<double> f_scale;
147  std::string linear_solver_type = "dense";
148  std::string iterative_solver = "gmres";
149 
150  // Read options
151  for (auto&& op : opts) {
152  if (op.first=="strategy") {
153  strategy = op.second.to_string();
154  } else if (op.first=="exact_jacobian") {
155  exact_jac_ = op.second;
156  } else if (op.first=="u_scale") {
157  u_scale = op.second;
158  } else if (op.first=="f_scale") {
159  f_scale = op.second;
160  } else if (op.first=="disable_internal_warnings") {
161  disable_internal_warnings_ = op.second;
162  } else if (op.first=="max_iter") {
163  max_iter_ = op.second;
164  } else if (op.first=="print_level") {
165  print_level_ = op.second;
166  } else if (op.first=="linear_solver_type") {
167  linear_solver_type = op.second.to_string();
168  } else if (op.first=="max_krylov") {
169  maxl_ = op.second;
170  } else if (op.first=="upper_bandwidth") {
171  upper_bandwidth_ = op.second;
172  } else if (op.first=="lower_bandwidth") {
173  lower_bandwidth_ = op.second;
174  } else if (op.first=="iterative_solver") {
175  iterative_solver = op.second.to_string();
176  } else if (op.first=="use_preconditioner") {
177  use_preconditioner_ = op.second;
178  } else if (op.first=="abstol") {
179  abstol_ = op.second;
180  }
181  }
182 
183  // Get globalization strategy
184  if (strategy=="linesearch") {
185  strategy_ = KIN_LINESEARCH;
186  } else {
187  casadi_assert_dev(strategy=="none");
188  strategy_ = KIN_NONE;
189  }
190 
191  // Allocate N_Vectors
192  if (u_scale_) N_VDestroy_Serial(u_scale_);
193  if (f_scale_) N_VDestroy_Serial(f_scale_);
194  u_scale_ = N_VNew_Serial(n_);
195  f_scale_ = N_VNew_Serial(n_);
196 
197  // Set scaling factors on variables
198  if (!u_scale.empty()) {
199  casadi_assert_dev(u_scale.size()==NV_LENGTH_S(u_scale_));
200  std::copy(u_scale.begin(), u_scale.end(), NV_DATA_S(u_scale_));
201  } else {
202  N_VConst(1.0, u_scale_);
203  }
204 
205  // Set scaling factors on equations
206  if (!f_scale.empty()) {
207  casadi_assert_dev(f_scale.size()==NV_LENGTH_S(f_scale_));
208  std::copy(f_scale.begin(), f_scale.end(), NV_DATA_S(f_scale_));
209  } else {
210  N_VConst(1.0, f_scale_);
211  }
212 
213  // Type of linear solver
214  if (linear_solver_type=="dense") {
216  if (exact_jac_) {
217  // For storing Jacobian nonzeros
218  alloc_w(sp_jac_.nnz(), true);
219  }
220  } else if (linear_solver_type=="banded") {
222  casadi_assert_dev(upper_bandwidth_>=0);
223  casadi_assert_dev(lower_bandwidth_>=0);
224  if (exact_jac_) {
225  // For storing Jacobian nonzeros
226  alloc_w(sp_jac_.nnz(), true);
227  }
228  } else if (linear_solver_type=="iterative") {
230  if (iterative_solver=="gmres") {
232  } else if (iterative_solver=="bcgstab") {
234  } else if (iterative_solver=="tfqmr") {
236  } else {
237  casadi_error("KINSOL: Unknown sparse solver");
238  }
239  if (exact_jac_) {
240  get_jtimes();
241  }
242  } else if (linear_solver_type=="user_defined") {
244 
245  // Form the Jacobian-times-vector function
246  get_jtimes();
247 
248  // Allocate space for Jacobian
249  alloc_w(sp_jac_.nnz(), true);
250  } else {
251  casadi_error("Unknown linear solver");
252  }
253  }
254 
255  void KinsolInterface::set_work(void* mem, const double**& arg, double**& res,
256  casadi_int*& iw, double*& w) const {
257  Rootfinder::set_work(mem, arg, res, iw, w);
258  auto m = static_cast<KinsolMemory*>(mem);
259  m->jac = w; w += sp_jac_.nnz();
260  }
261 
263  std::vector<std::string> jtimes_in = oracle_.name_in();
264  jtimes_in.push_back("fwd:" + oracle_.name_in(iin_));
265  std::vector<std::string> jtimes_out = {"fwd:" + oracle_.name_out(iout_)};
266  jtimes_ = oracle_.factory("jtimes", jtimes_in, jtimes_out);
267  alloc(jtimes_);
268  }
269 
270  Dict KinsolInterface::get_stats(void* mem) const {
271  Dict stats = Rootfinder::get_stats(mem);
272  auto m = static_cast<KinsolMemory*>(mem);
273  stats["return_status"] = m->return_status;
274  return stats;
275  }
276 
277  int KinsolInterface::solve(void* mem) const {
278  auto m = static_cast<KinsolMemory*>(mem);
279 
280  // Get the initial guess
281  casadi_copy(m->iarg[iin_], nnz_in(iin_), NV_DATA_S(m->u));
282 
283  // Solve the nonlinear system of equations
284  int flag = KINSol(m->mem, m->u, strategy_, u_scale_, f_scale_);
285  char* flagname = KINGetReturnFlagName(flag);
286  m->return_status = flagname;
287  free(flagname); // NOLINT
288  m->success = flag>= KIN_SUCCESS;
289  if (flag<KIN_SUCCESS) kinsol_error("KINSol", flag, error_on_fail_);
290  if (flag==KIN_MAXITER_REACHED) m->unified_return_status = SOLVER_RET_LIMITED;
291 
292  // Warn if not successful return
293  if (verbose_) {
294  if (flag!=KIN_SUCCESS) kinsol_error("KINSol", flag, false);
295  }
296 
297  // Get the solution
298  casadi_copy(NV_DATA_S(m->u), nnz_out(iout_), m->ires[iout_]);
299 
300  // Evaluate auxiliary outputs
301  if (n_out_>0) {
302  // Evaluate f_
303  std::copy_n(m->iarg, n_in_, m->arg);
304  m->arg[iin_] = NV_DATA_S(m->u);
305  std::copy_n(m->ires, n_out_, m->res);
306  m->res[iout_] = nullptr;
307  oracle_(m->arg, m->res, m->iw, m->w, 0);
308  }
309  return 0;
310  }
311 
312  void KinsolInterface::func(KinsolMemory& m, N_Vector u, N_Vector fval) const {
313  // Get nonzeros
314  const double *u_data = NV_DATA_S(u);
315  double *f_data = NV_DATA_S(fval);
316 
317  // Evaluate f_
318  std::copy_n(m.iarg, n_in_, m.arg);
319  m.arg[iin_] = u_data;
320  std::fill_n(m.res, n_out_, nullptr);
321  m.res[iout_] = f_data;
322  oracle_(m.arg, m.res, m.iw, m.w, 0);
323 
324  // Make sure that all entries of the linear system are valid
325  for (int k=0; k<n_; ++k) {
326  try {
327  casadi_assert(!isnan(f_data[k]),
328  "Nonzero " + str(k) + " is not-a-number");
329  casadi_assert(!isinf(f_data[k]),
330  "Nonzero " + str(k) + " is infinite");
331  } catch(std::exception& ex) {
332  std::stringstream ss;
333  ss << ex.what() << std::endl;
334  if (verbose_) {
335  uout() << "u = " << std::vector<double>(u_data, u_data + n_) << std::endl;
336  uout() << "f = " << std::vector<double>(f_data, f_data + n_) << std::endl;
337  }
338  throw CasadiException(ss.str());
339  }
340  }
341  }
342 
343  int KinsolInterface::func_wrapper(N_Vector u, N_Vector fval, void *user_data) {
344  try {
345  casadi_assert_dev(user_data);
346  auto this_ = static_cast<KinsolMemory*>(user_data);
347  this_->self.func(*this_, u, fval);
348  return 0;
349  } catch(std::exception& e) {
350  uerr() << "func failed: " << e.what() << std::endl;
351  return 1;
352  }
353  }
354 
355  int KinsolInterface::djac_wrapper(long N, N_Vector u, N_Vector fu, DlsMat J,
356  void *user_data, N_Vector tmp1, N_Vector tmp2) {
357  try {
358  casadi_assert_dev(user_data);
359  auto this_ = static_cast<KinsolMemory*>(user_data);
360  this_->self.djac(*this_, N, u, fu, J, tmp1, tmp2);
361  return 0;
362  } catch(std::exception& e) {
363  uerr() << "djac failed: " << e.what() << std::endl;;
364  return 1;
365  }
366  }
367 
368  void KinsolInterface::djac(KinsolMemory& m, long N, N_Vector u, N_Vector fu, DlsMat J,
369  N_Vector tmp1, N_Vector tmp2) const {
370  // Evaluate jac_
371  std::copy_n(m.iarg, n_in_, m.arg);
372  m.arg[iin_] = NV_DATA_S(u);
373  std::fill_n(m.res, n_out_+1, nullptr);
374  m.res[0] = m.jac;
375  calc_function(&m, "jac_g_x");
376 
377  // Get sparsity and non-zero elements
378  const casadi_int* colind = sp_jac_.colind();
379  casadi_int ncol = sp_jac_.size2();
380  const casadi_int* row = sp_jac_.row();
381 
382  // Loop over columns
383  for (casadi_int cc=0; cc<ncol; ++cc) {
384  // Loop over non-zero entries
385  for (casadi_int el=colind[cc]; el<colind[cc+1]; ++el) {
386  // Get row
387  casadi_int rr = row[el];
388 
389  // Set the element
390  DENSE_ELEM(J, rr, cc) = m.jac[el];
391  }
392  }
393  }
394 
395  int KinsolInterface::bjac_wrapper(long N, long mupper, long mlower, N_Vector u,
396  N_Vector fu, DlsMat J, void *user_data,
397  N_Vector tmp1, N_Vector tmp2) {
398  try {
399  casadi_assert_dev(user_data);
400  auto this_ = static_cast<KinsolMemory*>(user_data);
401  this_->self.bjac(*this_, N, mupper, mlower, u, fu, J, tmp1, tmp2);
402  return 0;
403  } catch(std::exception& e) {
404  uerr() << "bjac failed: " << e.what() << std::endl;;
405  return 1;
406  }
407  }
408 
409  void KinsolInterface::bjac(KinsolMemory& m, long N, long mupper, long mlower, N_Vector u,
410  N_Vector fu, DlsMat J, N_Vector tmp1, N_Vector tmp2) const {
411  // Evaluate jac_
412  std::copy_n(m.iarg, n_in_, m.arg);
413  m.arg[iin_] = NV_DATA_S(u);
414  std::fill_n(m.res, n_out_+1, nullptr);
415  m.res[0] = m.jac;
416  calc_function(&m, "jac_g_x");
417 
418  // Get sparsity and non-zero elements
419  const casadi_int* colind = sp_jac_.colind();
420  casadi_int ncol = sp_jac_.size2();
421  const casadi_int* row = sp_jac_.row();
422 
423  // Loop over cols
424  for (casadi_int cc=0; cc<ncol; ++cc) {
425  // Loop over non-zero entries
426  for (casadi_int el=colind[cc]; el<colind[cc+1]; ++el) {
427  // Get row
428  casadi_int rr = row[el];
429 
430  // Set the element
431  if (rr-cc>=-mupper && rr-cc<=mlower) {
432  BAND_ELEM(J, rr, cc) = m.jac[el];
433  }
434  }
435  }
436  }
437 
438  int KinsolInterface::jtimes_wrapper(N_Vector v, N_Vector Jv, N_Vector u, int* new_u,
439  void *user_data) {
440  try {
441  casadi_assert_dev(user_data);
442  auto this_ = static_cast<KinsolMemory*>(user_data);
443  this_->self.jtimes(*this_, v, Jv, u, new_u);
444  return 0;
445  } catch(std::exception& e) {
446  uerr() << "jtimes failed: " << e.what() << std::endl;;
447  return 1;
448  }
449  }
450 
451  void KinsolInterface::jtimes(KinsolMemory& m, N_Vector v, N_Vector Jv,
452  N_Vector u, int* new_u) const {
453  // Evaluate f_fwd_
454  std::copy_n(m.iarg, n_in_, m.arg);
455  m.arg[iin_] = NV_DATA_S(u);
456  m.arg[n_in_] = NV_DATA_S(v);
457  m.res[0] = NV_DATA_S(Jv);
458  jtimes_(m.arg, m.res, m.iw, m.w, 0);
459  }
460 
462  psetup_wrapper(N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale,
463  void* user_data, N_Vector tmp1, N_Vector tmp2) {
464  try {
465  casadi_assert_dev(user_data);
466  auto this_ = static_cast<KinsolMemory*>(user_data);
467  this_->self.psetup(*this_, u, uscale, fval, fscale, tmp1, tmp2);
468  return 0;
469  } catch(std::exception& e) {
470  uerr() << "psetup failed: " << e.what() << std::endl;;
471  return 1;
472  }
473  }
474 
476  psetup(KinsolMemory& m, N_Vector u, N_Vector uscale, N_Vector fval,
477  N_Vector fscale, N_Vector tmp1, N_Vector tmp2) const {
478  // Evaluate jac_
479  std::copy_n(m.iarg, n_in_, m.arg);
480  m.arg[iin_] = NV_DATA_S(u);
481  std::fill_n(m.res, n_out_+1, nullptr);
482  m.res[0] = m.jac;
483  if (calc_function(&m, "jac_g_x")) casadi_error("Jacobian calculation failed");
484 
485  // Get sparsity and non-zero elements
486  //const int* colind = sp_jac_.colind();
487  //int ncol = sp_jac_.size2();
488  //const int* row = sp_jac_.row();
489 
490  // Factorize the linear system
491  if (linsol_.nfact(m.jac)) casadi_error("'nfact' failed");
492  }
493 
494  int KinsolInterface::psolve_wrapper(N_Vector u, N_Vector uscale, N_Vector fval,
495  N_Vector fscale, N_Vector v, void* user_data, N_Vector tmp) {
496  try {
497  casadi_assert_dev(user_data);
498  auto this_ = static_cast<KinsolMemory*>(user_data);
499  this_->self.psolve(*this_, u, uscale, fval, fscale, v, tmp);
500  return 0;
501  } catch(std::exception& e) {
502  uerr() << "psolve failed: " << e.what() << std::endl;;
503  return 1;
504  }
505  }
506 
507  void KinsolInterface::psolve(KinsolMemory& m, N_Vector u, N_Vector uscale, N_Vector fval,
508  N_Vector fscale, N_Vector v, N_Vector tmp) const {
509  // Solve the factorized system
510  if (linsol_.solve(m.jac, NV_DATA_S(v))) casadi_error("'solve' failed");
511  }
512 
513  int KinsolInterface::lsetup(KINMem kin_mem) {
514  try {
515  auto m = to_mem(kin_mem->kin_lmem);
516  auto& s = m->self;
517 
518  N_Vector u = kin_mem->kin_uu;
519  N_Vector uscale = kin_mem->kin_uscale;
520  N_Vector fval = kin_mem->kin_fval;
521  N_Vector fscale = kin_mem->kin_fscale;
522  N_Vector tmp1 = kin_mem->kin_vtemp1;
523  N_Vector tmp2 = kin_mem->kin_vtemp2;
524  s.psetup(*m, u, uscale, fval, fscale, tmp1, tmp2);
525 
526  return 0;
527  } catch(std::exception& e) {
528  uerr() << "lsetup failed: " << e.what() << std::endl;;
529  return -1;
530  }
531  }
532 
534  lsolve(KINMem kin_mem, N_Vector x, N_Vector b, double *sJpnorm, double *sFdotJp) {
535  try {
536  auto m = to_mem(kin_mem->kin_lmem);
537  auto& s = m->self;
538 
539  // Get vectors
540  N_Vector u = kin_mem->kin_uu;
541  N_Vector uscale = kin_mem->kin_uscale;
542  N_Vector fval = kin_mem->kin_fval;
543  N_Vector fscale = kin_mem->kin_fscale;
544  N_Vector tmp1 = kin_mem->kin_vtemp1;
545  //N_Vector tmp2 = kin_mem->kin_vtemp2;
546 
547  // Solve the linear system
548  N_VScale(1.0, b, x);
549  s.psolve(*m, u, uscale, fval, fscale, x, tmp1);
550 
551  // Calculate residuals without accessing SPILS-specific linear solver memory
552  int new_u = TRUE;
553  int flag = jtimes_wrapper(x, b, u, &new_u, m);
554  if (flag) return flag;
555  *sJpnorm = N_VWL2Norm(b, fscale);
556  N_VProd(b, fscale, b);
557  N_VProd(b, fscale, b);
558  *sFdotJp = N_VDotProd(fval, b);
559 
560  return 0;
561  } catch(std::exception& e) {
562  uerr() << "lsolve failed: " << e.what() << std::endl;;
563  return -1;
564  }
565  }
566 
567  void KinsolInterface::ehfun(int error_code, const char *module, const char *function,
568  char *msg, void *eh_data) {
569  try {
570  auto m = to_mem(eh_data);
571  auto& s = m->self;
572  if (!s.disable_internal_warnings_) {
573  uerr() << msg << std::endl;
574  }
575  } catch(std::exception& e) {
576  uerr() << "ehfun failed: " << e.what() << std::endl;
577  }
578  }
579 
580  void KinsolInterface::ihfun(const char *module, const char *function, char *msg, void *ih_data) {
581  try {
582  // auto m = to_mem(ih_data);
583  // auto& s = m->self;
584  uout() << "[" << module << "] " << function << "\n " << msg << std::endl;
585  } catch(std::exception& e) {
586  uout() << "ihfun failed: " << e.what() << std::endl;
587  }
588  }
589 
590  void KinsolInterface::kinsol_error(const std::string& module, int flag, bool fatal) const {
591  // Get the error message
592  const char *id, *msg;
593  switch (flag) {
594  case KIN_SUCCESS:
595  id = "KIN_SUCCES";
596  msg = "KINSol succeeded; the scaled norm of F(u) is less than fnormtol";
597  break;
598  case KIN_INITIAL_GUESS_OK:
599  id = "KIN_INITIAL_GUESS_OK";
600  msg = "The guess u = u0 satisfied the system F(u) = 0 within the tolerances specified.";
601  break;
602  case KIN_STEP_LT_STPTOL:
603  id = "KIN_STEP_LT_STPTOL";
604  msg = "KINSol stopped based on scaled step length. This "
605  "means that the current iterate may be an approximate solution of the "
606  "given nonlinear system, but it is also quite possible that the algorithm"
607  " is 'stalled' (making insufficient progress) near an invalid solution, "
608  "or that the scalar scsteptol is too large.";
609  break;
610  case KIN_MEM_NULL:
611  id = "KIN_MEM_NULL";
612  msg = "The kinsol memory block pointer was NULL.";
613  break;
614  case KIN_ILL_INPUT:
615  id = "KIN_ILL_INPUT";
616  msg = "An input parameter was invalid.";
617  break;
618  case KIN_NO_MALLOC:
619  id = "KIN_NO_MALLOC";
620  msg = "The kinsol memory was not allocated by a call to KINCreate.";
621  break;
622  case KIN_LINESEARCH_NONCONV:
623  id = "KIN_LINESEARCH_NONCONV";
624  msg = "The line search algorithm was unable to find "
625  "an iterate sufficiently distinct from the current iterate, or could not"
626  " find an iterate satisfying the sufficient decrease condition. Failure"
627  " to satisfy the sufficient decrease condition could mean the current "
628  "iterate is 'close' to an approximate solution of the given nonlinear "
629  "system, the difference approximation of the matrix-vector product J(u)v"
630  " is inaccurate, or the real scalar scsteptol is too large.";
631  break;
632  case KIN_MAXITER_REACHED:
633  id = "KIN_MAXITER_REACHED";
634  msg = "The maximum number of nonlinear iterations "
635  "has been reached.";
636  break;
637  case KIN_MXNEWT_5X_EXCEEDED:
638  id = "KIN_MXNEWT_5X_EXCEEDED";
639  msg = "Five consecutive steps have been taken that "
640  "satisfy the inequality || D_u p ||_L2 > 0.99 mxnewtstep, where p "
641  "denotes the current step and mxnewtstep is a scalar upper bound on the "
642  "scaled step length. Such a failure may mean that || D_F F(u)||_L2 "
643  "asymptotes from above to a positive value, or the real scalar "
644  "mxnewtstep is too small.";
645  break;
646  case KIN_LINESEARCH_BCFAIL:
647  id = "KIN_LINESEARCH_BCFAIL";
648  msg = "The line search algorithm was unable to satisfy "
649  "the “beta-condition” for MXNBCF +1 nonlinear iterations (not necessarily "
650  "consecutive), which may indicate the algorithm is making poor progress.";
651  break;
652  case KIN_LINSOLV_NO_RECOVERY:
653  id = "KIN_LINSOLV_NO_RECOVERY";
654  msg = "The user-supplied routine psolve encountered a"
655  " recoverable error, but the preconditioner is already current.";
656  break;
657  case KIN_LINIT_FAIL:
658  id = "KIN_LINIT_FAIL";
659  msg = "The linear solver initialization routine (linit) encountered an error.";
660  break;
661  case KIN_LSETUP_FAIL:
662  id = "KIN_LSETUP_FAIL";
663  msg = "The user-supplied routine pset (used to set up the "
664  "preconditioner data) encountered an unrecoverable error.";
665  break;
666  case KIN_LSOLVE_FAIL:
667  id = "KIN_LSOLVE_FAIL";
668  msg = "Either the user-supplied routine psolve "
669  "(used to to solve the preconditioned linear system) encountered an "
670  "unrecoverable error, or the linear solver routine (lsolve) "
671  "encountered an error condition.";
672  break;
673  case KIN_SYSFUNC_FAIL:
674  id = "KIN_SYSFUNC_FAIL";
675  msg = "The system function failed in an unrecoverable manner.";
676  break;
677  case KIN_FIRST_SYSFUNC_ERR:
678  id = "KIN_FIRST_SYSFUNC_ERR";
679  msg = "The system function failed recoverably at the first call.";
680  break;
681  case KIN_REPTD_SYSFUNC_ERR:
682  id = "KIN_REPTD_SYSFUNC_ERR";
683  msg = "The system function had repeated recoverable errors. "
684  "No recovery is possible.";
685  break;
686  default:
687  id = "N/A";
688  msg = nullptr;
689  }
690 
691  // Construct message
692  std::stringstream ss;
693  if (msg==nullptr) {
694  ss << "Unknown " << (fatal? "error" : "warning") <<" (" << flag << ")"
695  " from module \"" << module << "\".";
696  } else {
697  ss << "Module \"" << module << "\" returned flag \"" << id << "\"." << std::endl;
698  ss << "The description of this flag is: " << std::endl;
699  ss << "\"" << msg << "\"" << std::endl;
700  }
701  ss << "Consult KINSOL documentation for more information.";
702  if (fatal) {
703  casadi_error("nlpsol process failed. "
704  "Set 'error_on_fail' option to false to ignore this error. "
705  + ss.str());
706  } else {
707  casadi_warning(ss.str());
708  }
709  }
710 
712  this->u = nullptr;
713  this->mem = nullptr;
714  }
715 
717  if (this->u) N_VDestroy_Serial(this->u);
718  if (this->mem) KINFree(&this->mem);
719  }
720 
721  int KinsolInterface::init_mem(void* mem) const {
722  if (Rootfinder::init_mem(mem)) return 1;
723  auto m = static_cast<KinsolMemory*>(mem);
724 
725  // Current solution
726  m->u = N_VNew_Serial(n_);
727 
728  // Create KINSOL memory block
729  m->mem = KINCreate();
730 
731  // KINSOL bugfix
732  KINMem kin_mem = KINMem(m->mem);
733  kin_mem->kin_inexact_ls = FALSE;
734 
735  // Set optional inputs
736  int flag = KINSetUserData(m->mem, m);
737  casadi_assert(flag==KIN_SUCCESS, "KINSetUserData");
738 
739  // Set error handler function
740  flag = KINSetErrHandlerFn(m->mem, ehfun, m);
741  casadi_assert(flag==KIN_SUCCESS, "KINSetErrHandlerFn");
742 
743  // Set error handler function
744  flag = KINSetInfoHandlerFn(m->mem, ihfun, m);
745  casadi_assert(flag==KIN_SUCCESS, "KINSetInfoHandlerFn");
746 
747  // Printing
748  flag = KINSetPrintLevel(m->mem, print_level_);
749  casadi_assert(flag==KIN_SUCCESS, "KINSetPrintLevel");
750 
751  // Initialize KINSOL
752  flag = KINInit(m->mem, func_wrapper, m->u);
753  casadi_assert_dev(flag==KIN_SUCCESS);
754 
755  // Setting maximum number of Newton iterations
756  flag = KINSetMaxNewtonStep(m->mem, max_iter_);
757  casadi_assert_dev(flag==KIN_SUCCESS);
758 
759  // Set constraints
760  if (!u_c_.empty()) {
761  N_Vector domain = N_VNew_Serial(n_);
762  std::copy(u_c_.begin(), u_c_.end(), NV_DATA_S(domain));
763 
764  // Pass to KINSOL
765  flag = KINSetConstraints(m->mem, domain);
766  casadi_assert_dev(flag==KIN_SUCCESS);
767 
768  // Free the temporary vector
769  N_VDestroy_Serial(domain);
770  }
771 
772  switch (linear_solver_type_) {
774  // Dense Jacobian
775  flag = KINDense(m->mem, n_);
776  casadi_assert(flag==KIN_SUCCESS, "KINDense");
777 
778  if (exact_jac_) {
779  flag = KINDlsSetDenseJacFn(m->mem, djac_wrapper);
780  casadi_assert(flag==KIN_SUCCESS, "KINDlsSetDenseJacFn");
781  }
782  break;
784  // Banded Jacobian
785  flag = KINBand(m->mem, n_, upper_bandwidth_, lower_bandwidth_);
786  casadi_assert(flag==KIN_SUCCESS, "KINBand");
787 
788  if (exact_jac_) {
789  flag = KINDlsSetBandJacFn(m->mem, bjac_wrapper);
790  casadi_assert(flag==KIN_SUCCESS, "KINDlsBandJacFn");
791  }
792  break;
794  // Attach the sparse solver
795  switch (iterative_solver_) {
797  flag = KINSpgmr(m->mem, maxl_);
798  casadi_assert(flag==KIN_SUCCESS, "KINSpgmr");
799  break;
801  flag = KINSpbcg(m->mem, maxl_);
802  casadi_assert(flag==KIN_SUCCESS, "KINSpbcg");
803  break;
805  flag = KINSptfqmr(m->mem, maxl_);
806  casadi_assert(flag==KIN_SUCCESS, "KINSptfqmr");
807  break;
808  }
809 
810  // Attach functions for Jacobian information
811  if (exact_jac_) {
812  flag = KINSpilsSetJacTimesVecFn(m->mem, jtimes_wrapper);
813  casadi_assert(flag==KIN_SUCCESS, "KINSpilsSetJacTimesVecFn");
814  }
815 
816  // Add a preconditioner
817  if (use_preconditioner_) {
818  flag = KINSpilsSetPreconditioner(m->mem, psetup_wrapper, psolve_wrapper);
819  casadi_assert_dev(flag==KIN_SUCCESS);
820  }
821  break;
823  // Set fields in the IDA memory
824  KINMem kin_mem = KINMem(m->mem);
825  kin_mem->kin_lmem = m;
826  kin_mem->kin_lsetup = lsetup;
827  kin_mem->kin_lsolve = lsolve;
828  kin_mem->kin_setupNonNull = TRUE;
829  break;
830  }
831 
832  // Set stop criterion
833  flag = KINSetFuncNormTol(m->mem, abstol_);
834  casadi_assert_dev(flag==KIN_SUCCESS);
835  return 0;
836  }
837 
838 } // namespace casadi
Casadi exception class.
Definition: exception.hpp:77
size_t n_in_
Number of inputs and outputs.
casadi_int nnz_in() const
Number of input/output nonzeros.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
casadi_int nnz_out() const
Number of input/output nonzeros.
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:126
const std::vector< std::string > & name_in() const
Get the names of all inputs, in order.
Definition: function.cpp:1113
Function factory(const std::string &name, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const AuxOut &aux=AuxOut(), const Dict &opts=Dict()) const
Build a function computing derivatives by naming inputs and outputs.
Definition: function.cpp:2009
const std::vector< std::string > & name_out() const
Get the names of all outputs, in order.
Definition: function.cpp:1117
'kinsol' plugin for Rootfinder
void func(KinsolMemory &m, N_Vector u, N_Vector fval) const
Callback functions (to be updated)
void bjac(KinsolMemory &m, long N, long mupper, long mlower, N_Vector u, N_Vector fu, DlsMat J, N_Vector tmp1, N_Vector tmp2) const
static const std::string meta_doc
A documentation string.
static const Options options_
Options.
static KinsolMemory * to_mem(void *mem)
Cast to memory object.
void jtimes(KinsolMemory &m, N_Vector v, N_Vector Jv, N_Vector u, int *new_u) const
static int psolve_wrapper(N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, N_Vector v, void *user_data, N_Vector tmp)
int init_mem(void *mem) const override
Initalize memory block.
casadi_int strategy_
Globalization strategy.
static Rootfinder * creator(const std::string &name, const Function &f)
Create a new Rootfinder.
static void ehfun(int error_code, const char *module, const char *function, char *msg, void *eh_data)
static int jtimes_wrapper(N_Vector v, N_Vector Jv, N_Vector u, int *new_u, void *user_data)
void psolve(KinsolMemory &m, N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, N_Vector v, N_Vector tmp) const
IterativeSolver iterative_solver_
static void ihfun(const char *module, const char *function, char *msg, void *ih_data)
static int func_wrapper(N_Vector u, N_Vector fval, void *user_data)
Wrappers to callback functions.
static int djac_wrapper(long N, N_Vector u, N_Vector fu, DlsMat J, void *user_data, N_Vector tmp1, N_Vector tmp2)
void kinsol_error(const std::string &module, int flag, bool fatal=true) const
void psetup(KinsolMemory &m, N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, N_Vector tmp1, N_Vector tmp2) const
~KinsolInterface() override
Destructor.
int solve(void *mem) const override
Solve the system of equations and calculate derivatives.
KinsolInterface(const std::string &name, const Function &f)
Constructor.
static int lsetup(KINMem kin_mem)
Callback functions (updated)
void init(const Dict &opts) override
Initialize stage.
static int psetup_wrapper(N_Vector u, N_Vector uscale, N_Vector fval, N_Vector fscale, void *user_data, N_Vector tmp1, N_Vector tmp2)
static int bjac_wrapper(long N, long mupper, long mlower, N_Vector u, N_Vector fu, DlsMat J, void *user_data, N_Vector tmp1, N_Vector tmp2)
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
void djac(KinsolMemory &m, long N, N_Vector u, N_Vector fu, DlsMat J, N_Vector tmp1, N_Vector tmp2) const
Dict get_stats(void *mem) const override
Get all statistics.
static int lsolve(KINMem kin_mem, N_Vector x, N_Vector b, double *sJpnorm, double *sFdotJp)
void nfact(const DM &A) const
Numeric factorization of the linear system.
Definition: linsol.cpp:133
DM solve(const DM &A, const DM &B, bool tr=false) const
Solve the linear system A*X = B (or A'*X = B with tr true)
Definition: linsol.cpp:73
Function oracle_
Oracle: Used to generate other functions.
int calc_function(OracleMemory *m, const std::string &fcn, const double *const *arg=nullptr, int thread_id=0) const
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool error_on_fail_
Throw an exception on failure?
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
Internal class.
int init_mem(void *mem) const override
Initalize memory block.
Definition: rootfinder.cpp:274
casadi_int n_
Number of equations.
std::vector< casadi_int > u_c_
Constraints on decision variables.
Dict get_stats(void *mem) const override
Get all statistics.
Definition: rootfinder.cpp:570
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: rootfinder.cpp:301
casadi_int iin_
Indices of the input and output that correspond to the actual root-finding.
static const Options options_
Options.
Linsol linsol_
Linear solver.
void init(const Dict &opts) override
Initialize.
Definition: rootfinder.cpp:200
casadi_int nnz() const
Number of structural nonzeros.
Definition: sparsity.cpp:150
casadi_int size2() const
Number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:166
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:170
The casadi namespace.
Definition: archiver.cpp:28
std::ostream & uerr()
void CASADI_ROOTFINDER_KINSOL_EXPORT casadi_load_rootfinder_kinsol()
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
@ OT_DOUBLEVECTOR
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
int CASADI_ROOTFINDER_KINSOL_EXPORT casadi_register_rootfinder_kinsol(Rootfinder::Plugin *plugin)
std::ostream & uout()
@ SOLVER_RET_LIMITED
void * mem
KINSOL memory block.
KinsolMemory(const KinsolInterface &s)
Constructor.
const KinsolInterface & self
Function object.
N_Vector u
Variable.
Options metadata for a class.
Definition: options.hpp:40