scirs2_integrate/ode/utils/jacobian/
specialized.rs1use crate::common::IntegrateFloat;
8use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
9
10#[allow(dead_code)]
12pub fn compute_banded_jacobian<F, Func>(
13 f: &Func,
14 t: F,
15 y: &Array1<F>,
16 f_current: &Array1<F>,
17 lower: usize,
18 upper: usize,
19) -> Array2<F>
20where
21 F: IntegrateFloat,
22 Func: Fn(F, ArrayView1<F>) -> Array1<F>,
23{
24 let n = y.len();
25 let mut jac = Array2::<F>::zeros((n, n));
26 let eps = F::from_f64(1e-8).unwrap();
27
28 for j in 0..n {
30 let row_start = j.saturating_sub(lower);
32 let row_end = (j + upper + 1).min(n);
33
34 if row_start < row_end {
36 let mut y_perturbed = y.clone();
38 let perturbation = eps * (F::one() + y[j].abs()).max(F::one());
39 y_perturbed[j] += perturbation;
40
41 let f_perturbed = f(t, y_perturbed.view());
43
44 for i in row_start..row_end {
46 jac[[i, j]] = (f_perturbed[i] - f_current[i]) / perturbation;
47 }
48 }
49 }
50
51 jac
52}
53
54#[allow(dead_code)]
56pub fn compute_diagonal_jacobian<F, Func>(
57 f: &Func,
58 t: F,
59 y: &Array1<F>,
60 f_current: &Array1<F>,
61 block_size: usize,
62) -> Array2<F>
63where
64 F: IntegrateFloat,
65 Func: Fn(F, ArrayView1<F>) -> Array1<F>,
66{
67 let n = y.len();
68 let mut jac = Array2::<F>::zeros((n, n));
69 let eps = F::from_f64(1e-8).unwrap();
70
71 for j in 0..n {
73 let block_idx = j / block_size;
75 let block_start = block_idx * block_size;
76 let block_end = (block_start + block_size).min(n);
77
78 let mut y_perturbed = y.clone();
80 let perturbation = eps * (F::one() + y[j].abs()).max(F::one());
81 y_perturbed[j] += perturbation;
82
83 let f_perturbed = f(t, y_perturbed.view());
85
86 for i in block_start..block_end {
88 jac[[i, j]] = (f_perturbed[i] - f_current[i]) / perturbation;
89 }
90 }
91
92 jac
93}
94
95#[allow(dead_code)]
97pub fn compute_colored_jacobian<F, Func>(
98 f: &Func,
99 t: F,
100 y: &Array1<F>,
101 f_current: &Array1<F>,
102 coloring: &[usize], ) -> Array2<F>
104where
105 F: IntegrateFloat,
106 Func: Fn(F, ArrayView1<F>) -> Array1<F>,
107{
108 let n = y.len();
109 let mut jac = Array2::<F>::zeros((n, n));
110 let eps = F::from_f64(1e-8).unwrap();
111
112 let max_color = coloring.iter().max().map_or(0, |&x| x) + 1;
114
115 for color in 0..max_color {
117 let mut y_perturbed = y.clone();
119 let mut perturbations = vec![F::zero(); n];
120
121 for j in 0..n {
123 if coloring[j] == color {
124 let perturbation = eps * (F::one() + y[j].abs()).max(F::one());
125 y_perturbed[j] += perturbation;
126 perturbations[j] = perturbation;
127 }
128 }
129
130 let f_perturbed = f(t, y_perturbed.view());
132
133 for j in 0..n {
135 if coloring[j] == color && perturbations[j] > F::zero() {
136 for i in 0..n {
137 jac[[i, j]] = (f_perturbed[i] - f_current[i]) / perturbations[j];
138 }
139 }
140 }
141 }
142
143 jac
144}
145
146#[allow(dead_code)]
148pub fn generate_banded_coloring(n: usize, lower: usize, upper: usize) -> Vec<usize> {
149 let bandwidth = lower + upper + 1;
150 let mut coloring = vec![0; n];
151
152 for (i, color) in coloring.iter_mut().enumerate().take(n) {
153 *color = i % bandwidth;
156 }
157
158 coloring
159}
160
161#[allow(dead_code)]
164pub fn broyden_update<F>(_jac: &mut Array2<F>, delta_y: &Array1<F>, deltaf: &Array1<F>)
165where
166 F: IntegrateFloat,
167{
168 let n = delta_y.len();
169
170 let mut jac_dy = Array1::zeros(n);
172 for i in 0..n {
173 for j in 0..n {
174 jac_dy[i] += _jac[[i, j]] * delta_y[j];
175 }
176 }
177
178 let correction = deltaf - &jac_dy;
180
181 let dy_norm_squared = delta_y.iter().map(|&x| x * x).sum::<F>();
183
184 if dy_norm_squared > F::from_f64(1e-14).unwrap() {
186 for i in 0..n {
187 for j in 0..n {
188 _jac[[i, j]] += correction[i] * delta_y[j] / dy_norm_squared;
189 }
190 }
191 }
192}
193
194#[allow(dead_code)]
196pub fn block_update<F>(
197 jac: &mut Array2<F>,
198 delta_y: &Array1<F>,
199 delta_f: &Array1<F>,
200 block_size: usize,
201) where
202 F: IntegrateFloat,
203{
204 let n = delta_y.len();
205 let n_blocks = n.div_ceil(block_size);
206
207 for block in 0..n_blocks {
209 let start = block * block_size;
210 let end = (start + block_size).min(n);
211
212 let mut block_dy = Array1::zeros(end - start);
214 let mut block_df = Array1::zeros(end - start);
215
216 for i in start..end {
217 block_dy[i - start] = delta_y[i];
218 block_df[i - start] = delta_f[i];
219 }
220
221 let mut block_jac_dy = Array1::zeros(end - start);
223 for i in 0..(end - start) {
224 for j in 0..(end - start) {
225 block_jac_dy[i] += jac[[i + start, j + start]] * block_dy[j];
226 }
227 }
228
229 let correction = &block_df - &block_jac_dy;
231 let dy_norm_squared = block_dy.iter().map(|&x| x * x).sum::<F>();
232
233 if dy_norm_squared > F::from_f64(1e-14).unwrap() {
234 for i in 0..(end - start) {
235 for j in 0..(end - start) {
236 jac[[i + start, j + start]] += correction[i] * block_dy[j] / dy_norm_squared;
237 }
238 }
239 }
240 }
241}