use crate::{
linalg::Matrix,
traits::{DefaultState, Real, State},
};
pub trait ODE<T = f64, Y = DefaultState<T>>
where
T: Real,
Y: State<T>,
{
fn diff(&self, t: T, y: &Y, dydt: &mut Y);
fn jacobian(&self, t: T, y: &Y, j: &mut Matrix<T>) {
let dim = y.len();
let mut y_perturbed = y.clone();
let mut f_perturbed = y.zeros_like();
let mut f_origin = y.zeros_like();
self.diff(t, y, &mut f_origin);
let eps = T::default_epsilon().sqrt();
for j_col in 0..dim {
let y_original_j = y.get_component(j_col);
let perturbation = eps * y_original_j.abs().max(T::one());
y_perturbed.copy_from_state(y);
y_perturbed.set_component(j_col, y_original_j + perturbation);
self.diff(t, &y_perturbed, &mut f_perturbed);
for i_row in 0..dim {
j[(i_row, j_col)] = (f_perturbed.get_component(i_row)
- f_origin.get_component(i_row))
/ perturbation;
}
}
}
}
impl<EqType, T: Real, Y: State<T>> ODE<T, Y> for &EqType
where
EqType: ODE<T, Y> + ?Sized,
{
fn diff(&self, t: T, y: &Y, dydt: &mut Y) {
(*self).diff(t, y, dydt);
}
fn jacobian(&self, t: T, y: &Y, j: &mut Matrix<T>) {
(*self).jacobian(t, y, j);
}
}