rosenbrock.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2026 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 "rosenbrock.hpp"
27 #include "rosenbrock_tableau.hpp"
28 
29 namespace casadi {
30 
31  extern "C"
32  int CASADI_INTEGRATOR_ROSENBROCK_EXPORT
33  casadi_register_integrator_rosenbrock(Integrator::Plugin* plugin) {
34  plugin->creator = Rosenbrock::creator;
35  plugin->name = "rosenbrock";
36  plugin->doc = Rosenbrock::meta_doc.c_str();
37  plugin->version = CASADI_VERSION;
38  plugin->options = &Rosenbrock::options_;
39  plugin->deserialize = &Rosenbrock::deserialize;
40  return 0;
41  }
42 
43  extern "C"
44  void CASADI_INTEGRATOR_ROSENBROCK_EXPORT casadi_load_integrator_rosenbrock() {
46  }
47 
48  Rosenbrock::Rosenbrock(const std::string& name, const Function& dae, double t0,
49  const std::vector<double>& tout) : RuntimeIntegrator(name, dae, t0, tout) {
50  }
51 
53  clear_mem();
54  }
55 
58  {{"init_tol",
59  {OT_DOUBLE,
60  "Tolerance on the scaled Newton step when making the algebraic states "
61  "consistent [1e-3]"}},
62  {"init_max_iter",
63  {OT_INT,
64  "Maximum number of Newton iterations for consistent algebraic states [20]"}}
65  }
66  };
67 
68  void Rosenbrock::init(const Dict& opts) {
69  // Call the base class init
71 
72  // Default options
73  init_tol_ = 1e-3;
74  init_max_iter_ = 20;
75 
76  // Read options
77  for (auto&& op : opts) {
78  if (op.first == "init_tol") {
79  init_tol_ = op.second;
80  } else if (op.first == "init_max_iter") {
81  init_max_iter_ = op.second;
82  }
83  }
84 
85  // Reverse communication problem
86  set_iteration_matrix();
87  set_coefficients();
88  set_ivp_prob();
90  set_rosenbrock_prob();
91 
92  // Work vectors of the kernel, and of the nominal integration
94  casadi_int sz_arg = 0, sz_res = 0, sz_iw = 0, sz_w = 0;
95  casadi_rosenbrock_work(&pr_, &sz_arg, &sz_res, &sz_iw, &sz_w);
96  if (replay_) casadi_rosenbrock_work(&pr_nom_, &sz_arg, &sz_res, &sz_iw, &sz_w);
97  alloc_w(sz_w, true);
98  }
99 
100  void Rosenbrock::set_iteration_matrix() {
101  // Iteration matrix M/(h gamma) - J of the nominal [x; z]
102  sp_iter_ = blockcat(sp_jac_[IVP_ODE_X] + Sparsity::diag(nx1_),
103  sp_jac_[IVP_ODE_Z], sp_jac_[IVP_ALG_X], sp_jac_[IVP_ALG_Z]);
104  // Positions of the nonzeros of the blocks ODE_X, ALG_X, ODE_Z, ALG_Z, then the x-diagonal
105  iter_map_.clear();
106  const casadi_int blk[4] = {IVP_ODE_X, IVP_ALG_X, IVP_ODE_Z, IVP_ALG_Z};
107  for (casadi_int k = 0; k < 4; ++k) {
108  std::vector<casadi_int> r = sp_jac_[blk[k]].get_row(), c = sp_jac_[blk[k]].get_col();
109  casadi_int r_off = blk[k] == IVP_ALG_X || blk[k] == IVP_ALG_Z ? nx1_ : 0;
110  casadi_int c_off = blk[k] == IVP_ODE_Z || blk[k] == IVP_ALG_Z ? nx1_ : 0;
111  for (size_t i = 0; i < r.size(); ++i) {
112  iter_map_.push_back(sp_iter_.get_nz(r[i] + r_off, c[i] + c_off));
113  }
114  }
115  for (casadi_int i = 0; i < nx1_; ++i) iter_map_.push_back(sp_iter_.get_nz(i, i));
116  // Symbolic sparse QR factorizations
117  sp_iter_.qr_sparse(sp_iter_v_, sp_iter_r_, iter_prinv_, iter_pc_);
118  if (nz1_ > 0) sp_jac_[IVP_ALG_Z].qr_sparse(sp_alg_v_, sp_alg_r_, alg_prinv_, alg_pc_);
119  }
120 
121  void Rosenbrock::set_coefficients() {
122  // Coefficients in original form (rosenbrock_tableau.hpp), Gamma lower triangular
123  using namespace rosenbrock_tableau;
124  const casadi_int s = n_stages;
125  // Inverse of Gamma = (gamma_ij), lower triangular, in long double
126  std::vector<long double> Gi(s * s, 0);
127  for (casadi_int j = 0; j < s; ++j) {
128  Gi[j * s + j] = 1 / gamma;
129  for (casadi_int i = j + 1; i < s; ++i) {
130  long double acc = 0;
131  for (casadi_int k = j; k < i; ++k) acc += gamma_ij[i][k] * Gi[k * s + j];
132  Gi[i * s + j] = -acc / gamma;
133  }
134  }
135  // a = A Gamma^-1, c = diag(1/gamma) - Gamma^-1, m = Gamma^-T b, m_err = Gamma^-T (b - bhat)
136  a_.assign(s * s, 0);
137  c_.assign(s * s, 0);
138  alpha_.assign(s, 0);
139  gamma_sum_.assign(s, 0);
140  m_.assign(s, 0);
141  m_err_.assign(s, 0);
142  for (casadi_int i = 0; i < s; ++i) {
143  long double alpha_i = 0, gamma_i = gamma;
144  for (casadi_int j = 0; j < i; ++j) {
145  alpha_i += alpha[i][j];
146  gamma_i += gamma_ij[i][j];
147  long double aij = 0;
148  for (casadi_int k = j; k < i; ++k) aij += alpha[i][k] * Gi[k * s + j];
149  a_[i * s + j] = static_cast<double>(aij);
150  c_[i * s + j] = static_cast<double>(-Gi[i * s + j]);
151  }
152  alpha_[i] = static_cast<double>(alpha_i);
153  gamma_sum_[i] = static_cast<double>(gamma_i);
154  long double mi = 0, mhi = 0;
155  for (casadi_int k = i; k < s; ++k) {
156  mi += b[k] * Gi[k * s + i];
157  mhi += bhat[k] * Gi[k * s + i];
158  }
159  m_[i] = static_cast<double>(mi);
160  m_err_[i] = static_cast<double>(mi - mhi);
161  }
163  // Dense output weights in the same form: m(theta) = Gamma^-T b(theta)
164  n_dense_ = n_dense;
165  m_dense_.assign(n_dense * s, 0);
166  for (casadi_int q = 0; q < n_dense; ++q) {
167  for (casadi_int i = 0; i < s; ++i) {
168  long double mi = 0;
169  for (casadi_int k = i; k < s; ++k) mi += b_dense[q][k] * Gi[k * s + i];
170  m_dense_[q * s + i] = static_cast<double>(mi);
171  }
172  }
173  }
174 
175  void Rosenbrock::set_rosenbrock_prob() {
176  // Problem structure of the method
177  pr_.ivp = &p_;
178  pr_.gamma = static_cast<double>(rosenbrock_tableau::gamma);
179  pr_.c = get_ptr(c_);
180  pr_.gamma_sum = get_ptr(gamma_sum_);
181  pr_.sp_iter = sp_iter_;
182  pr_.iter_map = get_ptr(iter_map_);
183  pr_.sp_iter_v = sp_iter_v_;
184  pr_.sp_iter_r = sp_iter_r_;
185  pr_.iter_prinv = get_ptr(iter_prinv_);
186  pr_.iter_pc = get_ptr(iter_pc_);
187  pr_.sp_alg_v = nz1_ > 0 ? static_cast<const casadi_int*>(sp_alg_v_) : nullptr;
188  pr_.sp_alg_r = nz1_ > 0 ? static_cast<const casadi_int*>(sp_alg_r_) : nullptr;
189  pr_.alg_prinv = get_ptr(alg_prinv_);
190  pr_.alg_pc = get_ptr(alg_pc_);
191  pr_.init_tol = init_tol_;
192  pr_.init_max_iter = init_max_iter_;
193  casadi_rosenbrock_setup(&pr_);
194  // Method of the original integrator, if its steps are replayed
195  if (replay_) {
196  pr_nom_ = static_cast<const Rosenbrock*>(root())->pr_;
197  pr_nom_.ivp = &p_nom_;
198  casadi_rosenbrock_setup(&pr_nom_);
199  }
200  }
201 
202  void Rosenbrock::set_work(void* mem, const double**& arg, double**& res,
203  casadi_int*& iw, double*& w) const {
204  auto m = static_cast<RosenbrockMemory*>(mem);
205  RuntimeIntegrator::set_work(mem, arg, res, iw, w);
206  m->r.prob = &pr_;
207  casadi_rosenbrock_set_work(&m->r, &arg, &res, &iw, &w);
208  if (replay_) {
209  m->r_nom.prob = &pr_nom_;
210  casadi_rosenbrock_set_work(&m->r_nom, &arg, &res, &iw, &w);
211  m->r.nom = &m->r_nom;
212  }
213  }
214 
216  auto m = static_cast<RosenbrockMemory*>(mem);
217  return casadi_rosenbrock(&m->d, &m->r);
218  }
219 
222  codegen_rosenbrock_prob(g, "pe", "p");
223  g << "e.prob = &pe;\n";
224  g << "casadi_rosenbrock_set_work(&e, &arg, &res, &iw, &w);\n";
225  if (replay_) {
226  static_cast<const Rosenbrock*>(root())->codegen_rosenbrock_prob(g, "pen", "pn");
227  g << "en.prob = &pen;\n";
228  g << "casadi_rosenbrock_set_work(&en, &arg, &res, &iw, &w);\n";
229  g << "e.nom = &en;\n";
230  }
231  }
232 
233  void Rosenbrock::codegen_rosenbrock_prob(CodeGenerator& g, const std::string& pe,
234  const std::string& p) const {
235  // Problem structure of the method in variable pe, as set_rosenbrock_prob
236  auto set = [&](const std::string& field, const std::string& value) -> void {
237  g << pe << "." << field << " = " << value << ";\n";
238  };
239  set("ivp", "&" + p);
240  set("gamma", g.constant(pr_.gamma));
241  set("c", g.constant(c_));
242  set("gamma_sum", g.constant(gamma_sum_));
243  set("sp_iter", g.sparsity(sp_iter_));
244  set("iter_map", g.constant(iter_map_));
245  set("sp_iter_v", g.sparsity(sp_iter_v_));
246  set("sp_iter_r", g.sparsity(sp_iter_r_));
247  set("iter_prinv", g.constant(iter_prinv_));
248  set("iter_pc", g.constant(iter_pc_));
249  set("sp_alg_v", nz1_ > 0 ? g.sparsity(sp_alg_v_) : "0");
250  set("sp_alg_r", nz1_ > 0 ? g.sparsity(sp_alg_r_) : "0");
251  set("alg_prinv", nz1_ > 0 ? g.constant(alg_prinv_) : "0");
252  set("alg_pc", nz1_ > 0 ? g.constant(alg_pc_) : "0");
253  set("init_tol", g.constant(pr_.init_tol));
254  set("init_max_iter", str(pr_.init_max_iter));
255  g << "casadi_rosenbrock_setup(&" << pe << ");\n";
256  }
257 
259  s.version("Rosenbrock", 1);
260  s.unpack("Rosenbrock::init_tol", init_tol_);
261  s.unpack("Rosenbrock::init_max_iter", init_max_iter_);
262  set_iteration_matrix();
263  set_coefficients();
264  set_ivp_prob();
266  set_rosenbrock_prob();
267  }
268 
271  s.version("Rosenbrock", 1);
272  s.pack("Rosenbrock::init_tol", init_tol_);
273  s.pack("Rosenbrock::init_max_iter", init_max_iter_);
274  }
275 
276 } // namespace casadi
Helper class for C code generation.
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
std::string sparsity(const Sparsity &sp, bool canonical=true)
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
size_t sz_res() const
Get required length of res field.
size_t sz_w() const
Get required length of w field.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
size_t sz_arg() const
Get required length of arg field.
size_t sz_iw() const
Get required length of iw field.
Function object.
Definition: function.hpp:126
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
void clear_mem()
Clear all memory (called from destructor)
'rosenbrock' plugin for Integrator
Definition: rosenbrock.hpp:69
int kernel(RuntimeIntegratorMemory *m) const override
Call the kernel of the method: returns 1 while a task is requested.
Definition: rosenbrock.cpp:215
static const std::string meta_doc
A documentation string.
Definition: rosenbrock.hpp:111
static Integrator * creator(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Create a new integrator.
Definition: rosenbrock.hpp:77
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: rosenbrock.cpp:269
~Rosenbrock() override
Destructor.
Definition: rosenbrock.cpp:52
Rosenbrock(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Constructor.
Definition: rosenbrock.cpp:48
void init(const Dict &opts) override
Initialize.
Definition: rosenbrock.cpp:68
void codegen_method(CodeGenerator &g) const override
Generate code for the problem and data of the method, variables pe and e.
Definition: rosenbrock.cpp:220
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Definition: rosenbrock.cpp:202
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize into MX.
Definition: rosenbrock.hpp:117
static const Options options_
Options.
Definition: rosenbrock.hpp:93
Base class of one-step integrators driven by a reverse communication kernel.
void set_ivp_prob()
Shared problem structure, from the method coefficients.
std::vector< double > m_dense_
std::vector< double > alpha_
std::vector< Sparsity > sp_jac_
void set_nominal_prob()
Problem of the nominal integration whose steps are replayed, if any.
casadi_ivp_prob< double > p_nom_
const RuntimeIntegrator * root() const
Integrator at the start of the chain of derivatives.
void init(const Dict &opts) override
Initialize: functions of the kernel tasks.
casadi_ivp_prob< double > p_
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
std::vector< double > m_err_
void alloc_ivp_work()
Work vectors of the shared kernel data.
static const Options options_
Options.
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
casadi_int get_nz(casadi_int rr, casadi_int cc) const
Nonzero index of entry (rr, cc), or -1 if it is a structural zero.
Definition: sparsity.cpp:248
static Sparsity diag(casadi_int nrow)
Create a diagonal sparsity pattern.
Definition: sparsity.hpp:723
void qr_sparse(Sparsity &V, Sparsity &R, std::vector< casadi_int > &prinv, std::vector< casadi_int > &pc, bool amd=true) const
Symbolic QR factorization.
Definition: sparsity.cpp:664
const long double alpha[8][8]
const long double gamma_ij[8][8]
The casadi namespace.
Definition: archiver.cpp:28
void CASADI_INTEGRATOR_ROSENBROCK_EXPORT casadi_load_integrator_rosenbrock()
Definition: rosenbrock.cpp:44
int CASADI_INTEGRATOR_ROSENBROCK_EXPORT casadi_register_integrator_rosenbrock(Integrator::Plugin *plugin)
Definition: rosenbrock.cpp:33
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
Options metadata for a class.
Definition: options.hpp:40
const casadi_int * sp_alg_r
const casadi_int * sp_iter_v
const casadi_int * iter_pc
const casadi_int * sp_iter
const casadi_ivp_prob< T1 > * ivp
const casadi_int * alg_pc
const casadi_int * iter_prinv
const casadi_int * sp_iter_r
const casadi_int * alg_prinv
const casadi_int * iter_map
const casadi_int * sp_alg_v