26 #include "runtime_integrator.hpp"
27 #include "casadi_misc.hpp"
32 const std::vector<double>& tout) :
Integrator(name, dae, t0, tout) {
40 {{
"control_group_size",
42 "Number of output intervals in each control group, over which the controls, one "
43 "column each, are constant: a run of steps [default: 1 for each output interval]"}},
46 "Fixed steps: each run of steps, over a control group or up to the end without "
47 "controls, is divided into equal steps of at most step_size [0: adaptive steps]"}},
50 "Absolute tolerance [1e-8]"}},
53 "Relative tolerance [1e-6]"}},
56 "Adaptive steps: maximum number of steps per output interval [10000]"}},
59 "Adaptive steps: initial step size [default: estimated]"}},
62 "Adaptive steps: maximum step size [default: unbounded]"}},
65 "Adaptive steps: minimum step size [1e-14]"}},
68 "Adaptive steps: include the quadratures in the error control [true]"}},
71 "Adaptive steps: capacity, in steps, of a tape in the work vector, for adjoint "
72 "sensitivities; needed for code generation [0: tape of unbounded size]. "
73 "With fixed steps, the tape is sized exactly."}}
81 auto cgs = opts.find(
"control_group_size");
82 if (cgs != opts.end()) {
84 for (casadi_int n : cgs->second.to_int_vector()) {
85 casadi_assert(n > 0,
"'control_group_size' entries must be positive");
88 casadi_assert(
group_start_.back() ==
nt(),
"'control_group_size' must sum to the number "
110 for (
auto&& op : opts) {
111 if (op.first ==
"step_size") {
113 }
else if (op.first ==
"abstol") {
115 }
else if (op.first ==
"reltol") {
117 }
else if (op.first ==
"max_num_steps") {
119 }
else if (op.first ==
"step0") {
121 }
else if (op.first ==
"max_step_size") {
123 }
else if (op.first ==
"min_step_size") {
125 }
else if (op.first ==
"quad_err_con") {
127 }
else if (op.first ==
"max_tape_steps") {
131 casadi_assert(
step_size_ >= 0,
"'step_size' must be nonnegative");
140 casadi_assert_dev(d !=
nullptr);
162 std::vector<MX> in = jump.
mx_in(), out = jump(in), seed;
163 for (casadi_int i = 0; i < jump.
n_out(); ++i) {
167 std::vector<MX> arg = in;
168 arg.insert(arg.end(), out.begin(), out.end());
169 arg.insert(arg.end(), seed.begin(), seed.end());
171 in.insert(in.end(), seed.begin(), seed.end());
182 {
"jac:ode:x",
"jac:alg:x",
"jac:quad:x",
"jac:ode:z",
"jac:alg:z",
"jac:quad:z",
183 "jac:ode:t",
"jac:alg:t",
"jac:quad:t"});
211 auto arg = [](
const Function& f, casadi_int i,
const MX& v) ->
MX {
214 auto dae = [&](
const MX& tt,
const MX& xx,
const MX& zz) -> std::vector<MX> {
222 z_solve =
rootfinder(
"z_consistent",
"fast_newton", alg,
223 Dict{{
"abstol", 1e-14}, {
"abstolStep", 1e-14}, {
"max_iter", 50}});
225 auto consistent = [&](
const MX& tt,
const MX& xx,
const MX& zz) ->
MX {
226 return nz1_ == 0 ? zz : z_solve(std::vector<MX>{zz, tt, xx, p, u}).at(0);
229 auto rates = [&](
const MX& tt,
const MX& xx,
const MX& zz, std::vector<MX>& r) ->
void {
230 std::vector<MX> r0 = dae(tt, xx, consistent(tt, xx, zz));
232 std::vector<MX> r1 = dae(tt + s, x1, consistent(tt + s, x1, zz));
239 MX x_s = x + s * r[0] + 0.5 * s * s * r[2];
243 MX s1 = -gv[0] / gv[1];
244 MX ss = s1 - 0.5 * gv[2] * s1 * s1 / gv[1];
246 std::vector<double> index(
ne_);
247 for (casadi_int i = 0; i < ne_; ++i) index[i] = static_cast<double>(i);
248 MX x_ev = x + ss * r[0] + 0.5 * ss * ss * r[2];
249 MX q_ev = q + ss * r[1] + 0.5 * ss * ss * r[3];
251 arg(tr,
EVENT_T, t + ss), x_ev, consistent(t + ss, x_ev, z), arg(tr,
EVENT_P, p),
256 MX x_post = post[
EVENT_POST_X] - ss * r[0] + 0.5 * ss * ss * r[2];
257 MX q_post = q_ev - ss * r[1] + 0.5 * ss * ss * r[3];
258 return Function(
"jumpF", {t, x, z, q, p, u, w}, {x_post, z_post, q_post},
259 {
"t",
"x",
"z",
"q",
"p",
"u",
"w"}, {
"post_x",
"post_z",
"post_q"});
284 for (casadi_int k = 0; k < IVP_NUM_JAC; ++k) {
309 casadi_ivp_setup(&
p_);
310 calls_ = task_calls(
"");
318 for (
auto&& calls : r->task_calls(
"")) {
319 for (
auto&& c : calls) {
320 std::string fname = c.fname +
"_nom";
327 calls_nom_ =
root()->task_calls(
"_nom");
330 casadi_ivp_setup(&
p_nom_);
348 for (casadi_int j = 0; j <
ngroup(); ++j) {
353 if (
nadj_ == 0 || n_step == 0)
return 0;
355 n_init =
nz_ > 0 ?
nt() : 0;
363 return n_step * (ny * (
static_cast<casadi_int
>(
alpha_.size()) + 1) +
nu_ + 5)
364 + (n_init + n_event) * (ny +
nu_ + 5);
368 casadi_int*& iw,
double*& w)
const {
372 casadi_ivp_set_work(&m->d, &arg, &res, &iw, &w);
375 casadi_ivp_set_work(&m->d_nom, &arg, &res, &iw, &w);
376 m->d.nom = &m->d_nom;
383 casadi_ivp_clear_stats(&m->d);
391 if (a == &m->
d ? task(m) : eval_task(m, a, calls_nom_)) a->
fail = 1;
402 casadi_ivp_reset(d, m->
t, m->u, first_call);
408 casadi_ivp_advance(d, m->t_next, m->
t_stop,
nrx_ > 0);
411 +
" steps is full at t = " +
str(d->
t) +
", increase max_tape_steps");
412 casadi_assert(d->
status != IVP_TOO_MANY_EVENTS,
name_ +
": more than max_events = "
414 casadi_assert(d->
status != IVP_EVENT_FAILED,
name_ +
": event near t = " +
str(d->
t_ev)
415 +
" not located within event_acceptable_tol");
416 if (d->
status != IVP_SUCCESS) {
418 casadi_warning(
name_ +
": integration failed at t = " +
str(d->
t) +
", status "
419 +
str(
static_cast<casadi_int
>(d->
status)));
431 casadi_ivp_reset_backward(&m->d);
435 const double* adj_x,
const double* adj_z,
const double* adj_q)
const {
437 double* seed = m->
d.
seed;
441 casadi_ivp_impulse(&m->d, m->t);
443 casadi_assert(m->d.status == IVP_SUCCESS,
name_ +
": adjoint seed at t = "
444 +
str(m->t) +
" failed, status " +
str(
static_cast<casadi_int
>(m->d.status)));
458 casadi_int* iw,
double* w,
void* mem)
const {
476 for (casadi_int j = 0; j <
ngroup(); ++j) {
477 set_u(m, u ? u + j *
nu_ :
nullptr);
481 m->t_next =
tout_[m->k];
484 get_x(m, xf ? xf + m->k *
nx_ :
nullptr);
485 get_z(m, zf ? zf + m->k *
nz_ :
nullptr);
486 get_q(m, qf ? qf + m->k *
nq_ :
nullptr);
492 for (casadi_int j =
ngroup(); j-- > 0; ) {
498 adj_zf ? adj_zf + m->k *
nrz_ :
nullptr, adj_qf ? adj_qf + m->k *
nrp_ :
nullptr);
499 m->t_next = m->k > 0 ?
tout_[m->k - 1] :
t0_;
513 void groups_to_intervals(
const std::vector<casadi_int>& gs,
const bvec_t* g,
bvec_t* k,
515 for (
size_t j = 0; j + 1 < gs.size(); ++j) {
516 for (casadi_int i = gs[j]; i < gs[j + 1]; ++i) std::copy_n(g + j * n, n, k + i * n);
520 void intervals_to_groups(
const std::vector<casadi_int>& gs,
const bvec_t* k,
bvec_t* g,
522 for (
size_t j = 0; j + 1 < gs.size(); ++j) {
523 for (casadi_int i = gs[j]; i < gs[j + 1]; ++i) {
524 for (casadi_int l = 0; l < n; ++l) g[j * n + l] |= k[i * n + l];
531 casadi_int* iw,
bvec_t* w,
void* mem)
const {
552 casadi_int* iw,
bvec_t* w,
void* mem)
const {
559 std::fill_n(u_k,
nu_ *
nt(), 0);
574 double* adj_x,
double* adj_p,
double* adj_u)
const {
578 casadi_ivp_retreat(d, m->
t, m->t_next);
580 casadi_assert(d->
status == IVP_SUCCESS,
581 name_ +
": adjoint sweep failed, status " +
str(
static_cast<casadi_int
>(d->
status)));
589 if (d->
task == IVP_TAPE_RESERVE) {
598 return eval_task(m, d, calls_);
602 const std::vector<std::vector<TaskCall>>& calls)
const {
604 if (d->
task == IVP_STEP_CONTROL) {
605 casadi_ivp_control(d);
608 if (d->
task == IVP_EVENT) {
609 while (casadi_ivp_event(d)) {
610 if (eval_task(m, d, calls)) d->
fail = 1;
614 if (d->
task == IVP_EVENT_ADJ) {
615 while (casadi_ivp_event_adj(d)) {
616 if (eval_task(m, d, calls)) d->
fail = 1;
620 for (
const TaskCall& c : calls.at(d->
task)) {
621 std::fill_n(m->arg, c.n_in,
nullptr);
622 std::fill_n(m->res, c.n_out,
nullptr);
623 for (
const Slot& e : c.arg) m->arg[e.i] = slot_ptr(m, d, e);
624 for (
const Slot& e : c.res) m->res[e.i] = slot_ptr(m, d, e);
631 const Slot& e)
const {
635 case SRC_T:
b = &d->
t_arg;
break;
636 case SRC_Y:
b =
const_cast<double*
>(d->
y_arg);
break;
637 case SRC_P:
b = m->p;
break;
638 case SRC_U:
b = d->
u_step;
break;
639 case SRC_RES:
b = d->
res;
break;
640 case SRC_W:
b =
const_cast<double*
>(d->
w);
break;
641 case SRC_JAC:
b = d->
jac;
break;
642 case SRC_JAC_FWD:
b = d->
jac_fwd;
break;
643 case SRC_JAC_ADJ:
b = d->
jac_adj;
break;
645 case SRC_VJP_X:
b = d->
vjp_x;
break;
646 case SRC_VJP_Z:
b = d->
vjp_z;
break;
647 case SRC_VJP_P:
b = d->
vjp_p;
break;
648 case SRC_VJP_U:
b = d->
vjp_u;
break;
649 case SRC_EV_ONE:
b = &d->
ev_one;
break;
650 case SRC_EV_W:
b = d->
ev_w;
break;
651 case SRC_YDOT:
b = d->
ydot;
break;
652 case SRC_EDOT:
b = d->
edot;
break;
657 std::string RuntimeIntegrator::slot_expr(
const std::string& d,
const Slot& e) {
661 case SRC_T:
b =
"&" + d +
".t_arg";
break;
662 case SRC_Y:
b = d +
".y_arg";
break;
663 case SRC_P:
b =
"pv";
break;
664 case SRC_U:
b = d +
".u_step";
break;
665 case SRC_RES:
b = d +
".res";
break;
666 case SRC_W:
b = d +
".w";
break;
667 case SRC_JAC:
b = d +
".jac";
break;
668 case SRC_JAC_FWD:
b = d +
".jac_fwd";
break;
669 case SRC_JAC_ADJ:
b = d +
".jac_adj";
break;
670 case SRC_JAC_ADJ_FWD:
b = d +
".jac_adj_fwd";
break;
671 case SRC_VJP_X:
b = d +
".vjp_x";
break;
672 case SRC_VJP_Z:
b = d +
".vjp_z";
break;
673 case SRC_VJP_P:
b = d +
".vjp_p";
break;
674 case SRC_VJP_U:
b = d +
".vjp_u";
break;
675 case SRC_EV_ONE:
b =
"&" + d +
".ev_one";
break;
676 case SRC_EV_W:
b = d +
".ev_w";
break;
677 case SRC_YDOT:
b = d +
".ydot";
break;
678 case SRC_EDOT:
b = d +
".edot";
break;
680 return e.off == 0 ?
b :
b +
"+" +
str(e.off);
683 std::vector<std::vector<RuntimeIntegrator::TaskCall>> RuntimeIntegrator::task_calls(
684 const std::string& suffix)
const {
686 std::vector<std::vector<TaskCall>> calls(IVP_TAPE_RESERVE);
687 static_assert(
static_cast<int>(
BDYN_U) ==
static_cast<int>(
DYN_U)
688 &&
static_cast<int>(
BDYN_T) ==
static_cast<int>(
DYN_T),
689 "Backward DAE inputs start with t, x, z, p, u");
690 const casadi_int nj = IVP_NUM_JAC;
692 const Src vjp[4] = {SRC_VJP_X, SRC_VJP_Z, SRC_VJP_P, SRC_VJP_U};
694 auto call = [&](
const std::string& fname) -> TaskCall {
696 return {fname + suffix, f.n_in(), f.n_out(), {}, {}};
699 auto dyn = [&](TaskCall& c, casadi_int i,
bool fwd) ->
void {
700 if (!fwd) c.arg.push_back({i +
DYN_T, SRC_T, 0});
701 c.arg.push_back({i +
DYN_X, SRC_Y, fwd ?
nx1_ : 0});
702 c.arg.push_back({i +
DYN_Z, SRC_Y,
nx_ + (fwd ?
nz1_ : 0)});
703 c.arg.push_back({i +
DYN_P, SRC_P, fwd ?
np1_ : 0});
704 c.arg.push_back({i +
DYN_U, SRC_U, fwd ?
nu1_ : 0});
707 auto blocks = [&](std::vector<Slot>& v, casadi_int i, Src src, casadi_int ndir) ->
void {
708 for (casadi_int k = 0; k < nj; ++k) v.push_back({i + k, src, ndir *
p_.
jac_off[k]});
711 TaskCall rhs = call(
"rhsF");
713 rhs.res = {{0, SRC_RES, 0}, {1, SRC_RES, nx_}, {2, SRC_RES, nx_ + nz_}};
714 calls[IVP_EVAL_RHS].push_back(rhs);
716 TaskCall c = call(forward_name(
"rhsF", nfwd_));
718 for (casadi_int k = 0; k < 3; ++k) {
719 c.arg.push_back({
DYN_NUM_IN + k, SRC_RES, rhs.res[k].off});
722 c.res = {{0, SRC_RES, nx1_}, {1, SRC_RES, nx_ + nz1_}, {2, SRC_RES, nx_ + nz_ + nq1_}};
723 calls[IVP_EVAL_RHS].push_back(c);
725 if (needs_jacobian()) {
727 TaskCall jac = call(
"jacF");
729 blocks(jac.res, 0, SRC_JAC, 1);
730 calls[IVP_EVAL_JAC].push_back(jac);
732 TaskCall c = call(forward_name(
"jacF", nfwd_));
736 blocks(c.res, 0, SRC_JAC_FWD, nfwd_);
737 calls[IVP_EVAL_JAC].push_back(c);
742 TaskCall zero = call(
"zeroF");
744 zero.res = {{0, SRC_RES, 0}};
745 calls[IVP_EVAL_ZERO].push_back(zero);
746 TaskCall c = call(forward_name(
"zeroF", 1));
752 c.res = {{0, SRC_EDOT, 0}};
753 calls[IVP_EVAL_ZERO].push_back(c);
754 if (has_function(
"jumpF")) {
756 auto jump = [&](TaskCall& c, casadi_int i,
bool fwd) ->
void {
757 if (!fwd) c.arg.push_back({i, SRC_T, 0});
758 c.arg.push_back({i + 1, SRC_Y, fwd ? nx1_ : 0});
759 c.arg.push_back({i + 2, SRC_Y, nx_ + (fwd ? nz1_ : 0)});
760 c.arg.push_back({i + 3, SRC_Y, nx_ + nz_ + (fwd ? nq1_ : 0)});
761 c.arg.push_back({i + 4, SRC_P, fwd ? np1_ : 0});
762 c.arg.push_back({i + 5, SRC_U, fwd ? nu1_ : 0});
763 if (!fwd) c.arg.push_back({i + 6, SRC_EV_W, 0});
765 TaskCall jf = call(
"jumpF");
767 jf.res = {{0, SRC_RES, 0}, {1, SRC_RES, nx_}, {2, SRC_RES, nx_ + nz_}};
768 calls[IVP_EVAL_JUMP].push_back(jf);
770 c = call(forward_name(
"jumpF", nfwd_));
772 for (casadi_int k = 0; k < 3; ++k) c.arg.push_back({7 + k, SRC_RES, jf.res[k].off});
774 c.res = {{0, SRC_RES, nx1_}, {1, SRC_RES, nx_ + nz1_}, {2, SRC_RES, nx_ + nz_ + nq1_}};
775 calls[IVP_EVAL_JUMP].push_back(c);
779 TaskCall jb = call(
"jumpB");
781 jb.arg.push_back({7, SRC_W, 0});
782 jb.arg.push_back({8, SRC_W, nrx_});
783 jb.arg.push_back({9, SRC_W, nrx_ + nrz_});
784 for (casadi_int k = 0; k < 4; ++k) jb.res.push_back({k, vjp[k], 0});
785 calls[IVP_EVAL_JUMP_ADJ].push_back(jb);
787 c = call(forward_name(
"jumpB", nfwd_));
789 for (casadi_int k = 0; k < 4; ++k) c.arg.push_back({10 + k, vjp[k], 0});
791 c.arg.push_back({21, SRC_W, nrx1_ * nadj_});
792 c.arg.push_back({22, SRC_W, nrx_ + nrz1_ * nadj_});
793 c.arg.push_back({23, SRC_W, nrx_ + nrz_ + nrp1_ * nadj_});
794 for (casadi_int k = 0; k < 4; ++k) c.res.push_back({k, vjp[k], n_vjp[k]});
795 calls[IVP_EVAL_JUMP_ADJ].push_back(c);
802 TaskCall vjpb = call(
"vjpB");
804 vjpb.arg.push_back({BDYN_ADJ_ODE, SRC_W, 0});
805 vjpb.arg.push_back({BDYN_ADJ_ALG, SRC_W, nrx_});
806 vjpb.arg.push_back({BDYN_ADJ_QUAD, SRC_W, nrx_ + nrz_});
807 for (casadi_int k = 0; k < 4; ++k) vjpb.res.push_back({k, vjp[k], 0});
808 calls[IVP_EVAL_VJP].push_back(vjpb);
810 TaskCall c = call(forward_name(
"vjpB", nfwd_));
811 casadi_int i = BDYN_NUM_IN + 4;
813 for (casadi_int k = 0; k < 4; ++k) c.arg.push_back({BDYN_NUM_IN + k, vjp[k], 0});
815 c.arg.push_back({i + BDYN_ADJ_ODE, SRC_W, nrx1_ * nadj_});
816 c.arg.push_back({i + BDYN_ADJ_ALG, SRC_W, nrx_ + nrz1_ * nadj_});
817 c.arg.push_back({i + BDYN_ADJ_QUAD, SRC_W, nrx_ + nrz_ + nrp1_ * nadj_});
818 for (casadi_int k = 0; k < 4; ++k) c.res.push_back({k, vjp[k], n_vjp[k]});
819 calls[IVP_EVAL_VJP].push_back(c);
821 if (needs_jacobian()) {
823 TaskCall jacb = call(
"jacB");
826 blocks(jacb.arg,
DYN_NUM_IN + nj, SRC_JAC_ADJ, nadj_);
827 for (casadi_int k = 0; k < 4; ++k) jacb.res.push_back({DYN_X + k, vjp[k], 0});
828 calls[IVP_EVAL_JAC_ADJ].push_back(jacb);
830 TaskCall c = call(forward_name(
"jacB", nfwd_));
833 for (casadi_int k = 0; k < 4; ++k) c.arg.push_back({i + DYN_X + k, vjp[k], 0});
835 blocks(c.arg, i_fwd +
DYN_NUM_IN + nj, SRC_JAC_ADJ_FWD, nadj_ * nfwd_);
836 for (casadi_int k = 0; k < 4; ++k) c.res.push_back({DYN_X + k, vjp[k], n_vjp[k]});
837 calls[IVP_EVAL_JAC_ADJ].push_back(c);
844 bool RuntimeIntegrator::has_codegen()
const {
846 return nadj_ == 0 || tape_capacity() > 0;
851 for (
auto* calls : {&calls_, &calls_nom_}) {
852 for (
auto&& task : *calls) {
859 casadi_assert(has_codegen(), name_ +
": adjoint sensitivities with adaptive steps need "
860 "max_tape_steps for code generation");
862 if (step_size_ == 0) g.
add_auxiliary(CodeGenerator::AUX_IVP_ADAPT);
864 const std::string nl =
";\n", method = plugin_name();
865 g.
local(
"p",
"struct casadi_ivp_prob");
866 g.
local(
"d",
"struct casadi_ivp_data");
867 g.
local(
"pe",
"struct casadi_" + method +
"_prob");
868 g.
local(
"e",
"struct casadi_" + method +
"_data");
870 g.
local(
"pn",
"struct casadi_ivp_prob");
871 g.
local(
"dn",
"struct casadi_ivp_data");
872 g.
local(
"pen",
"struct casadi_" + method +
"_prob");
873 g.
local(
"en",
"struct casadi_" + method +
"_data");
875 g.
local(
"sarg",
"const casadi_real",
"**");
876 g.
local(
"sres",
"casadi_real",
"**");
877 g.
local(
"pv",
"casadi_real",
"*");
878 if (nu_ > 0) g.
local(
"uv",
"casadi_real",
"*");
879 g.
local(
"j",
"casadi_int");
880 g.
local(
"k",
"casadi_int");
882 g.
comment(
"Problem structure");
883 codegen_ivp_prob(g,
"p", p_.tape_size);
885 g.
comment(
"Nominal integration, whose steps are replayed");
886 root()->codegen_ivp_prob(g,
"pn", 0);
891 g <<
"pv = w; w += " << np_ << nl;
892 if (nu_ > 0) g <<
"uv = w; w += " << nu_ << nl;
893 g <<
"d.prob = &p" << nl;
894 g <<
"casadi_ivp_set_work(&d, &arg, &res, &iw, &w)" << nl;
896 g <<
"dn.prob = &pn" << nl;
897 g <<
"casadi_ivp_set_work(&dn, &arg, &res, &iw, &w)" << nl;
898 g <<
"d.nom = &dn" << nl;
903 g.
comment(
"Initial state, parameters");
906 g << g.
clear(
"d.y+" +
str(nx_ + nz_), nq_) <<
"\n";
909 g.
comment(
"Forward integration: a run of steps per control group, as RuntimeIntegrator::eval");
911 g <<
"for (j = 0; j < " << ngroup() <<
"; ++j) {\n";
914 +
str(nu_) +
" : 0", nu_,
"uv") <<
"\n";
916 g <<
"casadi_ivp_reset(&d, j ? " << tout <<
"[" << gs <<
"[j]-1] : " << t0 <<
", "
917 << (nu_ > 0 ?
"uv" :
"0") <<
", j == 0)" << nl;
918 g <<
"for (k = " << gs <<
"[j]; k < " << gs <<
"[j+1]; ++k) {\n";
919 g <<
"casadi_ivp_advance(&d, " << tout <<
"[k], " << tout <<
"[" << gs <<
"[j+1]-1], "
920 << (nrx_ > 0 ? 1 : 0) <<
")" << nl;
922 g <<
"if (d.status != IVP_SUCCESS) return 1" << nl;
932 g.
comment(
"Discrete adjoint, output interval by output interval");
933 g <<
"casadi_ivp_reset_backward(&d)" << nl;
934 g <<
"for (j = " << ngroup() - 1 <<
"; j >= 0; --j) {\n";
935 g.
comment(
"Control adjoints of this group only");
936 g << g.
clear(
"d.adj_u", nuq_) <<
"\n";
937 g <<
"for (k = " << gs <<
"[j+1]-1; k >= " << gs <<
"[j]; --k) {\n";
938 g.
comment(
"Impulse from the adjoint seeds at output time k");
940 const casadi_int adj_n[3] = {nrx_, nrz_, nrp_};
941 const casadi_int adj_off[3] = {0, nrx_, nrx_ + nrz_};
942 for (casadi_int i = 0; i < 3; ++i) {
943 if (adj_n[i] == 0)
continue;
944 g << g.
copy(
"arg[" +
str(adj_in[i]) +
"] ? arg[" +
str(adj_in[i]) +
"] + k*"
945 +
str(adj_n[i]) +
" : 0", adj_n[i],
"d.seed+" +
str(adj_off[i])) <<
"\n";
947 g <<
"casadi_ivp_impulse(&d, " << tout <<
"[k])" << nl;
949 g <<
"if (d.status != IVP_SUCCESS) return 1" << nl;
950 g <<
"casadi_ivp_retreat(&d, " << tout <<
"[k], k > 0 ? " << tout <<
"[k-1] : " << t0
953 g <<
"if (d.status != IVP_SUCCESS) return 1" << nl;
965 void RuntimeIntegrator::codegen_loop(
CodeGenerator& g)
const {
967 g <<
"while (casadi_" << plugin_name() <<
"(&d, &e)) {\n";
969 g <<
"if (d.active == &d) {\n";
970 codegen_task(g,
"d", calls_);
972 codegen_task(g,
"dn", calls_nom_);
975 codegen_task(g,
"d", calls_);
980 void RuntimeIntegrator::codegen_ivp_prob(CodeGenerator& g,
const std::string& p,
981 casadi_int tape_size)
const {
983 auto set = [&](
const std::string& field,
const std::string& value) ->
void {
984 g << p <<
"." << field <<
" = " << value <<
";\n";
986 set(
"nx1",
str(nx1_));
987 set(
"nz1",
str(nz1_));
988 set(
"nq1",
str(nq1_));
989 set(
"np1",
str(np1_));
990 set(
"nu1",
str(nu1_));
991 set(
"nfwd",
str(nfwd_));
992 set(
"nadj",
str(nadj_));
993 set(
"n_stages",
str(alpha_.size()));
994 set(
"a", g.constant(a_));
995 set(
"alpha", g.constant(alpha_));
996 set(
"m", g.constant(m_));
997 set(
"n_dense",
str(n_dense_));
998 set(
"m_dense", g.constant(m_dense_));
999 for (casadi_int k = 0; k < IVP_NUM_JAC; ++k) {
1000 set(
"sp_jac[" +
str(k) +
"]", sp_jac_.empty() ?
"0" : g.sparsity(sp_jac_[k]));
1002 set(
"tape_size",
str(tape_size));
1003 set(
"step_size", g.constant(p_.step_size));
1004 set(
"nx_err",
str(p_.nx_err));
1005 set(
"nz_err",
str(p_.nz_err));
1006 set(
"nq_err",
str(p_.nq_err));
1007 set(
"abstol", g.constant(p_.abstol));
1008 set(
"reltol", g.constant(p_.reltol));
1009 set(
"order_err",
str(p_.order_err));
1010 set(
"m_err", m_err_.empty() ?
"0" : g.constant(m_err_));
1011 set(
"safety", g.constant(p_.safety));
1012 set(
"fac_min", g.constant(p_.fac_min));
1013 set(
"fac_max", g.constant(p_.fac_max));
1014 set(
"h_min", g.constant(p_.h_min));
1015 set(
"h_max", g.constant(p_.h_max));
1016 set(
"h_init", g.constant(p_.h_init));
1017 set(
"max_num_steps",
str(p_.max_num_steps));
1018 set(
"ne",
str(p_.ne));
1020 set(
"has_transition",
str(p_.has_transition));
1021 set(
"event_tol", g.constant(p_.event_tol));
1022 set(
"event_acceptable_tol", g.constant(p_.event_acceptable_tol));
1023 set(
"max_event_iter",
str(p_.max_event_iter));
1024 set(
"max_events",
str(p_.max_events));
1026 g <<
"casadi_ivp_setup(&" << p <<
");\n";
1029 void RuntimeIntegrator::codegen_task(CodeGenerator& g,
const std::string& d,
1030 const std::vector<std::vector<TaskCall>>& calls,
bool in_event)
const {
1032 static const char* task_name[] = {
"IVP_EVAL_RHS",
"IVP_EVAL_JAC",
"IVP_EVAL_VJP",
1033 "IVP_EVAL_JAC_ADJ",
"IVP_EVAL_ZERO",
"IVP_EVAL_JUMP",
"IVP_EVAL_JUMP_ADJ"};
1034 g <<
"switch (" << d <<
".task) {\n";
1035 for (casadi_int task = 0; task < static_cast<casadi_int>(calls.size()); ++task) {
1036 if (calls[task].empty())
continue;
1038 bool ev = task >= IVP_EVAL_ZERO && task <= IVP_EVAL_JUMP_ADJ;
1039 if (in_event != ev)
continue;
1040 g <<
"case " << task_name[task] <<
":\n";
1042 std::map<casadi_int, std::string> arg_set, res_set;
1043 for (
const TaskCall& c : calls[task]) {
1044 std::vector<std::string> arg(c.n_in,
"0"), res(c.n_out,
"0");
1045 for (
const Slot& e : c.arg) arg[e.i] = slot_expr(d, e);
1046 for (
const Slot& e : c.res) res[e.i] = slot_expr(d, e);
1047 for (casadi_int i = 0; i < c.n_in; ++i) {
1048 if (arg_set.count(i) && arg_set[i] == arg[i])
continue;
1049 g <<
"sarg[" << i <<
"] = " << arg[i] <<
";\n";
1050 arg_set[i] = arg[i];
1052 for (casadi_int i = 0; i < c.n_out; ++i) {
1053 if (res_set.count(i) && res_set[i] == res[i])
continue;
1054 g <<
"sres[" << i <<
"] = " << res[i] <<
";\n";
1055 res_set[i] = res[i];
1058 std::string flag = g(get_function(c.fname),
"sarg",
"sres",
"iw",
"w");
1059 g <<
"if (" << flag <<
") {\n" << d <<
".fail = 1;\nbreak;\n}\n";
1063 if (step_size_ == 0 && !in_event) {
1064 g <<
"case IVP_STEP_CONTROL:\n";
1065 g <<
"casadi_ivp_control(&" << d <<
");\n";
1068 if (ne_ > 0 && !in_event) {
1069 for (casadi_int adj = 0; adj <= (nadj_ > 0 ? 1 : 0); ++adj) {
1070 g <<
"case IVP_EVENT" << (adj ?
"_ADJ" :
"") <<
":\n";
1071 g <<
"while (casadi_ivp_event" << (adj ?
"_adj" :
"") <<
"(&" << d <<
")) {\n";
1072 codegen_task(g, d, calls,
true);
1077 g <<
"default: " << d <<
".fail = 1;\n";
1081 Dict RuntimeIntegrator::get_stats(
void* mem)
const {
1082 Dict stats = Integrator::get_stats(mem);
1085 stats[
"n_reject"] = m->d.n_reject;
1086 stats[
"n_rhs"] = m->d.n_rhs;
1087 stats[
"n_jac"] = m->d.n_jac;
1088 stats[
"n_vjp"] = m->d.n_vjp;
1089 stats[
"n_events"] = m->d.n_events;
1095 print(
"%s: %lld steps, %lld rejected, %lld rhs, %lld jac, %lld vjp, %lld events\n",
1096 plugin_name(),
static_cast<long long>(m->d.n_steps),
static_cast<long long>(m->d.n_reject),
1097 static_cast<long long>(m->d.n_rhs),
static_cast<long long>(m->d.n_jac),
1098 static_cast<long long>(m->d.n_vjp),
static_cast<long long>(m->d.n_events));
1102 s.
version(
"RuntimeIntegrator", 1);
1121 s.
version(
"RuntimeIntegrator", 1);
Helper class for C code generation.
std::string add_dependency(const Function &f)
Add a function dependency.
std::string copy(const std::string &arg, std::size_t n, const std::string &res)
Create a copy operation.
void comment(const std::string &s)
Write a comment line (ignored if not verbose)
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
std::string clear(const std::string &res, std::size_t n)
Create a fill operation.
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
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.
void call(const std::vector< M > &arg, std::vector< M > &res, bool always_inline, bool never_inline) const
Call a function, templated.
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.
void setup(void *mem, const double **arg, double **res, casadi_int *iw, double *w) const
Set the (persistent and temporary) work vectors.
size_t sz_iw() const
Get required length of iw field.
Function derivative_of_
If the function is the derivative of another function.
casadi_int numel_in() const
Get the number of elements (rows times columns) of one input, or of all.
const MX mx_in(casadi_int ind) const
Create fresh symbols with the names and sparsity patterns of the inputs.
const Sparsity & sparsity_out(casadi_int ind) const
Get the sparsity pattern of an output, by index or name.
FunctionInternal * get() const
Function reverse(casadi_int nadj) const
Get a function computing nadj reverse (adjoint) directional derivatives.
const Sparsity & sparsity_in(casadi_int ind) const
Get the sparsity pattern of an input, by index or name.
casadi_int n_out() const
Get the number of function outputs.
casadi_int nnz_in() const
Get the number of structural nonzeros of one input, or of all inputs.
const std::vector< std::string > & name_out() const
Get the names of all outputs, in order.
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
bool is_null() const
Is a null pointer?
Internal storage for integrator related data.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
casadi_int nfwd_
Number of sensitivities.
void init(const Dict &opts) override
Initialize.
static const Options options_
Options.
void set_z(IntegratorMemory *m, const double *z) const
void get_z(IntegratorMemory *m, double *z) const
void set_x(IntegratorMemory *m, const double *x) const
Function rdae_
Backwards DAE function.
Sparsity get_sparsity_in(casadi_int i) override
Sparsities of function inputs and outputs.
casadi_int nrx_
Number of states for the backward integration.
casadi_int max_events_
Maximum total number of events during the simulation.
void get_q(IntegratorMemory *m, double *q) const
int init_mem(void *mem) const override
Initalize memory block.
casadi_int np_
Number of forward and backward parameters.
casadi_int nt() const
Number of output times.
void set_u(IntegratorMemory *m, const double *u) const
bool print_stats_
Options.
int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity forward.
void get_x(IntegratorMemory *m, double *x) const
Sparsity get_sparsity_out(casadi_int i) override
Sparsities of function inputs and outputs.
static std::vector< std::string > bdyn_in()
IO conventions for continuous time dynamics.
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 > tout_
Output time grid.
double event_tol_
Termination tolerance for the event iteration.
void set_q(IntegratorMemory *m, const double *q) const
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity backwards.
double event_acceptable_tol_
Acceptable tolerance for the event iteration.
casadi_int max_event_iter_
Maximum number of event iterations for a single event.
casadi_int nu_
Number of controls.
casadi_int nx_
Number of states for the forward integration.
casadi_int ne_
Number of of zero-crossing functions.
void set_p(IntegratorMemory *m, const double *p) const
static MX substitute(const MX &ex, const MX &v, const MX &vdef)
const Sparsity & sparsity() const
Get the sparsity pattern.
static MX jacobian(const MX &f, const MX &x, const Dict &opts=Dict())
static MX dot(const MX &x, const MX &y)
void set_function(const Function &fcn, const std::string &fname, bool jit=false)
Function oracle_
Oracle: Used to generate other functions.
Function create_function(const Function &oracle, const std::string &fname, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const Function::AuxOut &aux=Function::AuxOut(), const Dict &opts=Dict())
void join_results(OracleMemory *m) const
Combine results from different threads.
Function create_forward(const std::string &fname, casadi_int nfwd)
int calc_function(OracleMemory *m, const std::string &fcn, const double *const *arg=nullptr, int thread_id=0) const
std::vector< std::string > get_function() const override
Get list of dependency functions.
bool has_function(const std::string &fname) const override
bool verbose_
Verbose printout.
Base class of one-step integrators driven by a reverse communication kernel.
casadi_int tape_capacity() const
Capacity of the tape in the work vector (0: unbounded, in the virtual machine only)
void set_ivp_prob()
Shared problem structure, from the method coefficients.
RuntimeIntegrator(const std::string &name, const Function &dae, double t0, const std::vector< double > &tout)
Constructor.
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_int max_tape_steps_
casadi_int max_num_steps_
std::vector< casadi_int > group_start_
casadi_ivp_prob< double > p_nom_
void retreat(IntegratorMemory *mem, const double *u, double *adj_x, double *adj_p, double *adj_u) const override
Retreat solution in time.
void impulseB(IntegratorMemory *mem, const double *adj_x, const double *adj_z, const double *adj_q) const override
Introduce an impulse into the backwards integration at the current time.
const RuntimeIntegrator * root() const
Integrator at the start of the chain of derivatives.
void run(RuntimeIntegratorMemory *m) const
Carry out the tasks requested by the kernel.
void resetB(IntegratorMemory *mem) const override
Reset the backward problem.
void print_stats(IntegratorMemory *mem) const override
Print solver statistics.
Sparsity get_sparsity_out(casadi_int i) override
~RuntimeIntegrator() override
Destructor.
void reset(IntegratorMemory *mem, bool first_call) const override
Reset the forward problem at the start or after a change in the controls.
void init(const Dict &opts) override
Initialize: functions of the kernel tasks.
casadi_ivp_prob< double > p_
casadi_int ngroup() const
Number of control groups, columns of the controls.
virtual int kernel(RuntimeIntegratorMemory *m) const =0
Call the kernel of the method: returns 1 while a task is requested.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
virtual bool needs_jacobian() const =0
Does the method linearize (tasks EVAL_JAC, EVAL_JAC_ADJ)?
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically: a run of steps per control group, events in the kernel.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
int advance_noevent(IntegratorMemory *mem) const override
Advance solution in time.
int init_mem(void *mem) const override
Initalize memory block.
std::vector< double > m_err_
void alloc_ivp_work()
Work vectors of the shared kernel data.
Function jump_function() const
State after an event as a function of the state before it, see casadi_ivp_event.hpp.
Sparsity get_sparsity_in(casadi_int i) override
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.
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern.
std::vector< std::string > dyn_in()
Get the input scheme of a DAE function.
Function rootfinder(const std::string &name, const std::string &solver, const SXDict &rfp, const Dict &opts)
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
@ INTEGRATOR_ADJ_U
Adjoint sensitivities corresponding to the control vector.
@ INTEGRATOR_QF
Quadrature state at all output times.
@ INTEGRATOR_ADJ_P
Adjoint sensitivities corresponding to the parameter vector.
@ INTEGRATOR_ZF
Algebraic variable at all output times.
@ INTEGRATOR_XF
Differential state at all output times.
@ INTEGRATOR_NUM_OUT
Number of output arguments of an integrator.
@ INTEGRATOR_ADJ_X0
Adjoint sensitivities corresponding to the initial state.
@ INTEGRATOR_U
Piecewise constant control, a new control interval starts at each output time.
@ INTEGRATOR_ADJ_QF
Adjoint seeds corresponding to the quadratures at the output times.
@ INTEGRATOR_ADJ_ZF
Adjoint seeds corresponding to the algebraic variables at the output times.
@ INTEGRATOR_P
Parameters.
@ INTEGRATOR_ADJ_XF
Adjoint seeds corresponding to the states at the output times.
@ INTEGRATOR_Z0
Initial guess for the algebraic variable at the initial time.
@ INTEGRATOR_NUM_IN
Number of input arguments of an integrator.
@ INTEGRATOR_X0
Differential state at the initial time.
unsigned long long bvec_t
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
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.
void casadi_clear(T1 *x, casadi_int n)
CLEAR: x <- 0.
Options metadata for a class.
casadi_ivp_data< double > d
std::vector< double > tape
casadi_ivp_data< T1 > * active
const casadi_int * sp_jac[IVP_NUM_JAC]
casadi_int max_event_iter
casadi_int jac_off[IVP_NUM_JAC+1]