scirs2_integrate/ode/utils/jacobian/
autodiff.rs1use crate::common::IntegrateFloat;
9use crate::error::IntegrateResult;
10use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
11
12#[cfg(feature = "autodiff")]
30#[allow(dead_code)]
31pub fn autodiff_jacobian<F, Func>(
32 f: &Func,
33 t: F,
34 y: &Array1<F>,
35 _f_current: &Array1<F>, _perturbation_scale: F, ) -> IntegrateResult<Array2<F>>
38where
39 F: IntegrateFloat,
40 Func: Fn(F, ArrayView1<F>) -> Array1<F> + Clone,
41{
42 use crate::autodiff::Dual;
44
45 let n = y.len();
46 let f_test = f(t, y.view());
47 let m = f_test.len();
48
49 let mut jacobian = Array2::zeros((m, n));
50
51 for j in 0..n {
53 let dual_y: Vec<Dual<F>> = y
55 .iter()
56 .enumerate()
57 .map(|(i, &val)| {
58 if i == j {
59 Dual::variable(val)
60 } else {
61 Dual::constant(val)
62 }
63 })
64 .collect();
65
66 let y_vals: Vec<F> = dual_y.iter().map(|d| d.value()).collect();
68 let y_arr = Array1::from_vec(y_vals);
69 let f_vals = f(t, y_arr.view());
70
71 let eps = F::from(1e-8).unwrap();
76 let mut y_pert = y.to_owned();
77 y_pert[j] += eps;
78 let f_pert = f(t, y_pert.view());
79
80 for i in 0..m {
81 jacobian[[i, j]] = (f_pert[i] - f_vals[i]) / eps;
82 }
83 }
84
85 Ok(jacobian)
86}
87
88#[cfg(not(feature = "autodiff"))]
90#[allow(dead_code)]
91pub fn autodiff_jacobian<F, Func>(
92 _f: &Func,
93 _t: F,
94 _y: &Array1<F>,
95 _f_current: &Array1<F>,
96 _perturbation_scale: F,
97) -> IntegrateResult<Array2<F>>
98where
99 F: IntegrateFloat,
100 Func: Fn(F, ArrayView1<F>) -> Array1<F>,
101{
102 Err(crate::error::IntegrateError::ComputationError(
103 "Autodiff Jacobian computation requires the 'autodiff' feature to be enabled.".to_string(),
104 ))
105}
106
107#[allow(dead_code)]
109pub fn is_autodiff_available() -> bool {
110 cfg!(feature = "autodiff")
111}
112
113#[cfg(feature = "autodiff")]
116#[allow(dead_code)]
117pub fn adaptive_jacobian<F, Func>(
118 f: &Func,
119 t: F,
120 y: &Array1<F>,
121 f_current: &Array1<F>,
122 perturbation_scale: F,
123) -> IntegrateResult<Array2<F>>
124where
125 F: IntegrateFloat,
126 Func: Fn(F, ArrayView1<F>) -> Array1<F> + Clone,
127{
128 autodiff_jacobian(f, t, y, f_current, perturbation_scale)
130}
131
132#[cfg(not(feature = "autodiff"))]
135#[allow(dead_code)]
136pub fn adaptive_jacobian<F, Func>(
137 f: &Func,
138 t: F,
139 y: &Array1<F>,
140 f_current: &Array1<F>,
141 perturbation_scale: F,
142) -> IntegrateResult<Array2<F>>
143where
144 F: IntegrateFloat,
145 Func: Fn(F, ArrayView1<F>) -> Array1<F> + Clone,
146{
147 Ok(crate::ode::utils::common::finite_difference_jacobian(
149 f,
150 t,
151 y,
152 f_current,
153 perturbation_scale,
154 ))
155}