use crate::{
ode_solver::problem::OdeSolverSolution, Context, Matrix, OdeBuilder,
OdeEquationsImplicitAdjoint, OdeSolverProblem, Op, Vector,
};
use num_traits::{FromPrimitive, One, Zero};
fn logistic_state<T: crate::Scalar>(r: T, k: T, y0: T, t: T) -> T {
let exp_rt = (r * t).exp();
let numerator = y0 * exp_rt;
let denominator = T::one() - y0 / k + (y0 / k) * exp_rt;
numerator / denominator
}
#[allow(clippy::type_complexity)]
fn logistic_adjoint_problem<M: Matrix + 'static>(
ctx: M::C,
) -> OdeSolverProblem<impl OdeEquationsImplicitAdjoint<M = M, V = M::V, T = M::T, C = M::C>> {
let nbatch = ctx.nbatch();
let (r, k, y0) = (1.0, 1.0, 0.1);
OdeBuilder::<M>::new()
.context(ctx)
.p([r, k, y0].repeat(nbatch))
.sens_rtol(1e-6)
.sens_atol([1e-6])
.param_rtol(1e-6)
.param_atol([1e-6])
.rhs_adjoint_implicit(
|x: &[M::T], p: &[M::T], _t: M::T, y: &mut [M::T]| {
let (r, k, u) = (p[0], p[1], x[0]);
y[0] = r * u * (M::T::one() - u / k);
},
|x: &[M::T], p: &[M::T], _t: M::T, v: &[M::T], y: &mut [M::T]| {
let (r, k, u) = (p[0], p[1], x[0]);
y[0] = r * (M::T::one() - M::T::from_f64(2.0).unwrap() * u / k) * v[0];
},
|x: &[M::T], p: &[M::T], _t: M::T, v: &[M::T], y: &mut [M::T]| {
let (r, k, u) = (p[0], p[1], x[0]);
y[0] = -r * (M::T::one() - M::T::from_f64(2.0).unwrap() * u / k) * v[0];
},
|x: &[M::T], p: &[M::T], _t: M::T, v: &[M::T], y: &mut [M::T]| {
let (r, k, u) = (p[0], p[1], x[0]);
y[0] = -u * (M::T::one() - u / k) * v[0];
y[1] = -(r * u * u / (k * k) * v[0]);
y[2] = M::T::zero();
},
)
.init_adjoint(
|p: &[M::T], _t: M::T, y: &mut [M::T]| y[0] = p[2],
|_p: &[M::T], _t: M::T, v: &[M::T], y: &mut [M::T]| {
y[0] = M::T::zero();
y[1] = M::T::zero();
y[2] = -v[0];
},
1,
)
.build()
.unwrap()
}
#[allow(clippy::type_complexity)]
pub fn logistic_problem_adjoint_no_out<M: Matrix + 'static>() -> (
OdeSolverProblem<impl OdeEquationsImplicitAdjoint<M = M, V = M::V, T = M::T, C = M::C>>,
OdeSolverSolution<M::V>,
) {
let (r, k, y0) = (1.0, 1.0, 0.1);
let problem = logistic_adjoint_problem::<M>(M::C::default());
let r = M::T::from_f64(r).unwrap();
let k = M::T::from_f64(k).unwrap();
let y0 = M::T::from_f64(y0).unwrap();
let mut soln = OdeSolverSolution {
atol: problem.atol.clone(),
rtol: problem.rtol,
..Default::default()
};
for i in 0..10 {
let t = M::T::from_f64(i as f64).unwrap();
let y = M::V::from_vec(
vec![logistic_state(r, k, y0, t)],
problem.eqn.context().clone(),
);
soln.push(y, t);
}
(problem, soln)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
NalgebraContext, NalgebraMat, NalgebraVec, NonLinearOp, NonLinearOpAdjoint,
NonLinearOpJacobian, NonLinearOpSensAdjoint, OdeEquations,
};
type M = NalgebraMat<f64>;
#[test]
fn operators_run_batched() {
let nbatch = 2;
let ctx = NalgebraContext::default()
.clone_with_nbatch(nbatch)
.unwrap();
let problem = logistic_adjoint_problem::<M>(ctx);
let eqn = problem.eqn.rhs();
let x = NalgebraVec::<f64>::from_element(1, 0.5, ctx);
let v = NalgebraVec::<f64>::from_element(1, 1.0, ctx);
let mut y = NalgebraVec::<f64>::zeros(1, ctx);
let mut yp = NalgebraVec::<f64>::zeros(3, ctx);
let t = 0.0;
eqn.call_inplace(&x, t, &mut y);
assert_eq!(y.clone_as_vec(), vec![0.25; nbatch]);
eqn.jac_mul_inplace(&x, t, &v, &mut y);
assert_eq!(y.clone_as_vec(), vec![0.0; nbatch]);
eqn.jac_transpose_mul_inplace(&x, t, &v, &mut y);
assert_eq!(y.clone_as_vec(), vec![0.0; nbatch]);
eqn.sens_transpose_mul_inplace(&x, t, &v, &mut yp);
assert_eq!(
yp.clone_as_vec(),
vec![-0.25, -0.25, 0.0, -0.25, -0.25, 0.0]
);
}
}