casadi_erk.hpp
1 //
2 // MIT No Attribution
3 //
4 // Copyright (C) 2010-2026 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of this
7 // software and associated documentation files (the "Software"), to deal in the Software
8 // without restriction, including without limitation the rights to use, copy, modify,
9 // merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
14 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
15 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
16 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
17 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 //
19 
20 // Explicit Runge-Kutta integrator for y' = F(t, y), y = [x; q], see casadi_ivp.hpp
21 // Stages U_i = h F(t0 + alpha_i h, y0 + sum_{j<i} a_ij U_j), y1 = y0 + sum_i m_i U_i
22 // Fixed or adaptive steps; with the first-same-as-last property, the last stage of a step is the
23 // first of the next
24 
25 // C-REPLACE "casadi_ivp_prob<T1>" "struct casadi_ivp_prob"
26 // C-REPLACE "casadi_ivp_data<T1>" "struct casadi_ivp_data"
27 
28 // SYMBOL "erk_prob"
29 template<typename T1>
31  // Last stage at t0 + h, y1 (first same as last)
32  int fsal;
33 };
34 // C-REPLACE "casadi_erk_prob<T1>" "struct casadi_erk_prob"
35 
36 // SYMBOL "erk_next_t"
37 typedef enum {
38  // Phases: each one consumes the result of the previously requested task
39  ERK_STEP_BEGIN = IVP_NUM_ENTRY,
40  ERK_STEP_SIZE,
41  ERK_STEP_INITIAL_SIZE,
42  ERK_STEP_REPLAY,
43  ERK_STAGE,
44  ERK_STEP_CHECK,
45  ERK_STEP_EVENT,
46  ERK_BACK_STAGE,
47  ERK_BACK_SPREAD
48 } casadi_erk_next_t;
49 
50 // C-REPLACE "casadi_erk_data<T1>" "struct casadi_erk_data"
51 // SYMBOL "erk_data"
52 template<typename T1>
54  // Problem structure
56  // Method data of the nominal integration whose steps are replayed (or null)
58  // Stage holding h_f F(t, y) for the next step (-1: none), h_f
59  casadi_int f_stage;
60  T1 f_scale;
61 };
62 
63 // SYMBOL "erk"
64 template<typename T1>
65 int casadi_erk(casadi_ivp_data<T1>* d, casadi_erk_data<T1>* e);
66 
67 // SYMBOL "erk_stage_request"
68 template<typename T1>
69 int casadi_erk_stage_request(casadi_ivp_data<T1>* d) {
70  // Right-hand side at the argument of the current stage
71  const casadi_ivp_prob<T1>* p = d->prob;
72  casadi_ivp_stage_argument(d, d->stage);
73  d->t_arg = d->t0 + p->alpha[d->stage] * d->h;
74  d->y_arg = d->y_stage;
75  d->res = d->U + d->stage * p->ny;
76  return casadi_ivp_request(d, IVP_EVAL_RHS, ERK_STAGE);
77 }
78 
79 // SYMBOL "erk_step_start"
80 template<typename T1>
81 int casadi_erk_step_start(casadi_ivp_data<T1>* d, casadi_erk_data<T1>* e) {
82  // Step of size h from t, reusing F(t, y) if available
83  const casadi_ivp_prob<T1>* p = d->prob;
84  d->t0 = d->t;
85  casadi_copy(d->y, p->ny, d->y0);
86  d->stage = 0;
87  if (e->f_stage >= 0) {
88  if (e->f_stage > 0) casadi_copy(d->U + e->f_stage * p->ny, p->ny, d->U);
89  casadi_scal(p->ny, d->h / e->f_scale, d->U);
90  e->f_stage = -1;
91  d->stage = 1;
92  }
93  return casadi_erk_stage_request(d);
94 }
95 
96 // SYMBOL "erk_forward"
97 template<typename T1>
98 int casadi_erk_forward(casadi_ivp_data<T1>* d, casadi_erk_data<T1>* e) {
99  // Forward integration; returns 1 if a task is requested, 0 when done or failed
100  const casadi_ivp_prob<T1>* p = d->prob;
101  int ret;
102  switch (d->next) {
103  case IVP_ADVANCE:
104  if (d->reset) {
105  // New state or controls: F(t, y) is not known
106  casadi_ivp_begin_run(d);
107  e->f_stage = -1;
108  }
109  d->next = ERK_STEP_BEGIN;
110  return casadi_erk_forward(d, e);
111  case ERK_STEP_BEGIN:
112  if (d->fail) return casadi_ivp_stop(d, IVP_TAPE_FAILED);
113  if (d->t >= d->t_out) {
114  if (!d->pause) casadi_ivp_output(d);
115  return casadi_ivp_stop(d, IVP_SUCCESS);
116  }
117  if (d->jump) {
118  // State changed by an event: restart
119  d->reset = 1;
120  d->next = IVP_ADVANCE;
121  return casadi_erk_forward(d, e);
122  }
123  if (casadi_ivp_step_limit(d)) return casadi_ivp_stop(d, IVP_MAX_NUM_STEPS);
124  if ((ret = casadi_ivp_reserve(d, IVP_RECORD_STEP, ERK_STEP_BEGIN)) >= 0) return ret;
125  casadi_copy(d->u, p->nu, d->u_step);
126  if (d->nom) {
127  // Step of the nominal integration
128  d->next = ERK_STEP_REPLAY;
129  return casadi_erk_forward(d, e);
130  }
131  if (d->prob->step_size > 0) {
132  d->h = d->run_h;
133  return casadi_erk_step_start(d, e);
134  }
135  return casadi_ivp_request_control(d, IVP_CONTROL_SIZE, ERK_STEP_SIZE);
136  case ERK_STEP_SIZE:
137  if (d->h > 0) return casadi_erk_step_start(d, e);
138  // Estimate from the right-hand side at the initial point
139  d->t_arg = d->t;
140  d->y_arg = d->y;
141  d->res = d->U;
142  return casadi_ivp_request(d, IVP_EVAL_RHS, ERK_STEP_INITIAL_SIZE);
143  case ERK_STEP_INITIAL_SIZE:
144  if (d->fail) return casadi_ivp_stop(d, IVP_EVAL_FAILED);
145  d->n_rhs++;
146  e->f_stage = 0;
147  e->f_scale = 1;
148  return casadi_ivp_request_control(d, IVP_CONTROL_INIT, ERK_STEP_SIZE);
149  case ERK_STEP_REPLAY:
150  // Advance the nominal integration by one step; its tasks are handed to the caller
151  if (casadi_erk(d->nom, e->nom)) {
152  d->active = d->nom;
153  return 1;
154  }
155  d->active = d;
156  if (d->nom->status != IVP_SUCCESS) return casadi_ivp_stop(d, d->nom->status);
157  if (d->nom->next == IVP_DONE) return casadi_ivp_stop(d, IVP_EVAL_FAILED);
158  d->h = d->nom->h;
159  return casadi_erk_step_start(d, e);
160  case ERK_STAGE:
161  // F(t0, y0), if evaluated, holds for a retry from the same point
162  e->f_stage = d->stage > 0 ? 0 : -1;
163  e->f_scale = d->h;
164  if (d->fail) {
165  // Evaluation failed: retry with a smaller step, if adaptive
166  if (casadi_ivp_prescribed(d)) return casadi_ivp_stop(d, IVP_EVAL_FAILED);
167  return casadi_ivp_request_control(d, IVP_CONTROL_FAIL, ERK_STEP_CHECK);
168  }
169  d->n_rhs++;
170  casadi_scal(p->ny, d->h, d->U + d->stage * p->ny);
171  if (++d->stage < p->n_stages) return casadi_erk_stage_request(d);
172  if (e->prob->fsal) {
173  // y1 is the argument of the last stage
174  casadi_copy(d->y_stage, p->ny, d->y);
175  } else {
176  // y = y0 + sum_i m_i U_i
177  casadi_copy(d->y0, p->ny, d->y);
178  casadi_mv_dense(d->U, p->ny, p->n_stages, p->m, d->y, 0);
179  }
180  if (!casadi_ivp_prescribed(d)) {
181  return casadi_ivp_request_control(d, IVP_CONTROL_TEST, ERK_STEP_CHECK);
182  }
183  d->accepted = 1;
184  d->next = ERK_STEP_CHECK;
185  return casadi_erk_forward(d, e);
186  case ERK_STEP_CHECK:
187  if (d->status != IVP_SUCCESS) return casadi_ivp_stop(d, d->status);
188  if (!d->accepted) {
189  // Retry from t0
190  d->next = ERK_STEP_BEGIN;
191  return casadi_erk_forward(d, e);
192  }
193  e->f_stage = e->prob->fsal ? p->n_stages - 1 : -1;
194  casadi_ivp_accept(d);
195  if (p->ne > 0) return casadi_ivp_request(d, IVP_EVENT, ERK_STEP_EVENT);
196  d->next = ERK_STEP_EVENT;
197  return casadi_erk_forward(d, e);
198  case ERK_STEP_EVENT:
199  if (d->status != IVP_SUCCESS) return casadi_ivp_stop(d, d->status);
200  d->next = ERK_STEP_BEGIN;
201  if (d->pause) {
202  // Hand the step to the integration that replays it
203  d->status = IVP_SUCCESS;
204  return 0;
205  }
206  return casadi_erk_forward(d, e);
207  default:
208  return 0;
209  }
210 }
211 
212 // SYMBOL "erk_backward"
213 template<typename T1>
214 int casadi_erk_backward(casadi_ivp_data<T1>* d, casadi_erk_data<T1>* e) {
215  // Discrete adjoint of the taped steps; returns 1 if a task is requested, 0 when done
216  const casadi_ivp_prob<T1>* p = d->prob;
217  casadi_int i, j, s = p->n_stages;
218  T1* rUi;
219  switch (d->next) {
220  case IVP_RETREAT:
221  if (!casadi_ivp_back_begin(d)) return 0;
222  if (d->record == IVP_RECORD_EVENT) return casadi_ivp_request(d, IVP_EVENT_ADJ, IVP_RETREAT);
223  casadi_ivp_back_step_begin(d);
224  d->next = ERK_BACK_STAGE;
225  return casadi_erk_backward(d, e);
226  case ERK_BACK_STAGE:
227  if (d->stage == 0) {
228  // Next record
229  d->t = d->t0;
230  d->next = IVP_RETREAT;
231  return casadi_erk_backward(d, e);
232  }
233  // Previous stage: U_i = h F at the stage argument
234  i = --d->stage;
235  rUi = d->rU + i * p->nr;
236  casadi_scal(p->nr, d->h, rUi);
237  casadi_ivp_stage_argument(d, i);
238  casadi_ivp_clear_vjp(d);
239  d->t_arg = d->t0 + p->alpha[i] * d->h;
240  d->y_arg = d->y_stage;
241  d->w = rUi;
242  return casadi_ivp_request(d, IVP_EVAL_VJP, ERK_BACK_SPREAD);
243  case ERK_BACK_SPREAD:
244  // Adjoint of y0 + sum_j a_ij U_j to y0 and earlier stages
245  if (d->fail) return casadi_ivp_stop(d, IVP_EVAL_FAILED);
246  d->n_vjp++;
247  i = d->stage;
248  casadi_ivp_add_vjp(d, d->ry);
249  for (j = 0; j < i; ++j) {
250  if (p->a[i * s + j] != 0) casadi_axpy(p->nrx, p->a[i * s + j], d->vjp_x, d->rU + j * p->nr);
251  }
252  d->next = ERK_BACK_STAGE;
253  return casadi_erk_backward(d, e);
254  case IVP_IMPULSE:
255  // Adjoint seed of the output at t_seed
256  if (casadi_ivp_impulse_begin(d)) {
257  casadi_ivp_seed_stages(d);
258  return casadi_ivp_stop(d, IVP_SUCCESS);
259  }
260  return 0;
261  default:
262  return 0;
263  }
264 }
265 
266 template<typename T1>
267 int casadi_erk(casadi_ivp_data<T1>* d, casadi_erk_data<T1>* e) {
268  // Reverse communication: returns 1 while a task in d->task is to be carried out
269  if (d->next == IVP_IMPULSE || d->next == IVP_RETREAT || d->next >= ERK_BACK_STAGE) {
270  return casadi_erk_backward(d, e);
271  }
272  return casadi_erk_forward(d, e);
273 }
void casadi_scal(casadi_int n, T1 alpha, T1 *x)
SCAL: x <- alpha*x.
casadi_erk_data< T1 > * nom
Definition: casadi_erk.hpp:57
casadi_int f_stage
Definition: casadi_erk.hpp:59
const casadi_erk_prob< T1 > * prob
Definition: casadi_erk.hpp:55
casadi_ivp_data< T1 > * active
Definition: casadi_ivp.hpp:191
const T1 * w
Definition: casadi_ivp.hpp:206
casadi_ivp_flag_t status
Definition: casadi_ivp.hpp:201
casadi_ivp_record_t record
Definition: casadi_ivp.hpp:245
const casadi_ivp_prob< T1 > * prob
Definition: casadi_ivp.hpp:189
casadi_int n_rhs
Definition: casadi_ivp.hpp:267
const T1 * y_arg
Definition: casadi_ivp.hpp:206
casadi_int next
Definition: casadi_ivp.hpp:200
const T1 * u
Definition: casadi_ivp.hpp:214
casadi_ivp_data< T1 > * nom
Definition: casadi_ivp.hpp:193
casadi_int stage
Definition: casadi_ivp.hpp:221
casadi_int n_vjp
Definition: casadi_ivp.hpp:267
const T1 * m
Definition: casadi_ivp.hpp:118
casadi_int ny
Definition: casadi_ivp.hpp:145
const T1 * alpha
Definition: casadi_ivp.hpp:118
casadi_int nr
Definition: casadi_ivp.hpp:145
casadi_int nrx
Definition: casadi_ivp.hpp:145
const T1 * a
Definition: casadi_ivp.hpp:118
casadi_int ne
Definition: casadi_ivp.hpp:140
casadi_int n_stages
Definition: casadi_ivp.hpp:117
casadi_int nu
Definition: casadi_ivp.hpp:145