use crate::common::IntegrateFloat;
use crate::error::IntegrateResult;
use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
#[cfg(feature = "autodiff")]
#[allow(dead_code)]
pub fn autodiff_jacobian<F, Func>(
f: &Func,
t: F,
y: &Array1<F>,
_f_current: &Array1<F>, _perturbation_scale: F, ) -> IntegrateResult<Array2<F>>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>) -> Array1<F> + Clone,
{
use crate::autodiff::Dual;
let n = y.len();
let f_test = f(t, y.view());
let m = f_test.len();
let mut jacobian = Array2::zeros((m, n));
for j in 0..n {
let dual_y: Vec<Dual<F>> = y
.iter()
.enumerate()
.map(|(i, &val)| {
if i == j {
Dual::variable(val)
} else {
Dual::constant(val)
}
})
.collect();
let y_vals: Vec<F> = dual_y.iter().map(|d| d.value()).collect();
let y_arr = Array1::from_vec(y_vals);
let f_vals = f(t, y_arr.view());
let eps = F::from(1e-8).expect("Failed to convert constant to float");
let mut y_pert = y.to_owned();
y_pert[j] += eps;
let f_pert = f(t, y_pert.view());
for i in 0..m {
jacobian[[i, j]] = (f_pert[i] - f_vals[i]) / eps;
}
}
Ok(jacobian)
}
#[cfg(not(feature = "autodiff"))]
#[allow(dead_code)]
pub fn autodiff_jacobian<F, Func>(
_f: &Func,
_t: F,
_y: &Array1<F>,
_f_current: &Array1<F>,
_perturbation_scale: F,
) -> IntegrateResult<Array2<F>>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>) -> Array1<F>,
{
Err(crate::error::IntegrateError::ComputationError(
"Autodiff Jacobian computation requires the 'autodiff' feature to be enabled.".to_string(),
))
}
#[allow(dead_code)]
pub fn is_autodiff_available() -> bool {
cfg!(feature = "autodiff")
}
#[cfg(feature = "autodiff")]
#[allow(dead_code)]
pub fn adaptive_jacobian<F, Func>(
f: &Func,
t: F,
y: &Array1<F>,
f_current: &Array1<F>,
perturbation_scale: F,
) -> IntegrateResult<Array2<F>>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>) -> Array1<F> + Clone,
{
autodiff_jacobian(f, t, y, f_current, perturbation_scale)
}
#[cfg(not(feature = "autodiff"))]
#[allow(dead_code)]
pub fn adaptive_jacobian<F, Func>(
f: &Func,
t: F,
y: &Array1<F>,
f_current: &Array1<F>,
perturbation_scale: F,
) -> IntegrateResult<Array2<F>>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>) -> Array1<F> + Clone,
{
Ok(crate::ode::utils::common::finite_difference_jacobian(
f,
t,
y,
f_current,
perturbation_scale,
))
}