scirs2_integrate/analysis/
utils.rs1use crate::error::{IntegrateError, IntegrateResult};
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::numeric::Complex64;
9
10pub fn compute_determinant(matrix: &Array2<f64>) -> f64 {
12 let n = matrix.nrows();
13 if n != matrix.ncols() {
14 return 0.0; }
16
17 let mut lu = matrix.clone();
18 let mut determinant = 1.0;
19
20 for k in 0..n {
22 let mut max_val = lu[[k, k]].abs();
24 let mut max_idx = k;
25
26 for i in (k + 1)..n {
27 if lu[[i, k]].abs() > max_val {
28 max_val = lu[[i, k]].abs();
29 max_idx = i;
30 }
31 }
32
33 if max_idx != k {
35 for j in 0..n {
36 let temp = lu[[k, j]];
37 lu[[k, j]] = lu[[max_idx, j]];
38 lu[[max_idx, j]] = temp;
39 }
40 determinant *= -1.0; }
42
43 if lu[[k, k]].abs() < 1e-14 {
45 return 0.0;
46 }
47
48 determinant *= lu[[k, k]];
49
50 for i in (k + 1)..n {
52 let factor = lu[[i, k]] / lu[[k, k]];
53 for j in (k + 1)..n {
54 lu[[i, j]] -= factor * lu[[k, j]];
55 }
56 }
57 }
58
59 determinant
60}
61
62pub fn compute_trace(matrix: &Array2<f64>) -> f64 {
64 let n = std::cmp::min(matrix.nrows(), matrix.ncols());
65 (0..n).map(|i| matrix[[i, i]]).sum()
66}
67
68pub fn estimate_matrix_rank(matrix: &Array2<f64>, tolerance: f64) -> usize {
70 let (m, n) = matrix.dim();
72 let mut a = matrix.clone();
73 let mut rank = 0;
74
75 for k in 0..std::cmp::min(m, n) {
76 let mut max_norm = 0.0;
78 let mut max_col = k;
79
80 for j in k..n {
81 let col_norm: f64 = (k..m).map(|i| a[[i, j]].powi(2)).sum::<f64>().sqrt();
82 if col_norm > max_norm {
83 max_norm = col_norm;
84 max_col = j;
85 }
86 }
87
88 if max_norm < tolerance {
90 break;
91 }
92
93 if max_col != k {
95 for i in 0..m {
96 let temp = a[[i, k]];
97 a[[i, k]] = a[[i, max_col]];
98 a[[i, max_col]] = temp;
99 }
100 }
101
102 rank += 1;
103
104 for i in k..m {
106 a[[i, k]] /= max_norm;
107 }
108
109 for j in (k + 1)..n {
110 let dot_product: f64 = (k..m).map(|i| a[[i, k]] * a[[i, j]]).sum();
111 for i in k..m {
112 a[[i, j]] -= dot_product * a[[i, k]];
113 }
114 }
115 }
116
117 rank
118}
119
120pub fn solve_linear_system(a: &Array2<f64>, b: &Array1<f64>) -> IntegrateResult<Array1<f64>> {
122 let n = a.nrows();
123 let mut lu = a.clone();
124 let mut x = b.clone();
125
126 let mut pivot = Array1::<usize>::zeros(n);
128 for i in 0..n {
129 pivot[i] = i;
130 }
131
132 for k in 0..n - 1 {
133 let mut max_val = lu[[k, k]].abs();
135 let mut max_idx = k;
136
137 for i in k + 1..n {
138 if lu[[i, k]].abs() > max_val {
139 max_val = lu[[i, k]].abs();
140 max_idx = i;
141 }
142 }
143
144 if max_idx != k {
146 for j in 0..n {
147 let temp = lu[[k, j]];
148 lu[[k, j]] = lu[[max_idx, j]];
149 lu[[max_idx, j]] = temp;
150 }
151 pivot.swap(k, max_idx);
152 }
153
154 for i in k + 1..n {
156 if lu[[k, k]].abs() < 1e-14 {
157 return Err(IntegrateError::ComputationError(
158 "Matrix is singular".to_string(),
159 ));
160 }
161
162 let factor = lu[[i, k]] / lu[[k, k]];
163 lu[[i, k]] = factor;
164
165 for j in k + 1..n {
166 lu[[i, j]] -= factor * lu[[k, j]];
167 }
168 }
169 }
170
171 for k in 0..n - 1 {
173 x.swap(k, pivot[k]);
174 }
175
176 for i in 1..n {
178 for j in 0..i {
179 x[i] -= lu[[i, j]] * x[j];
180 }
181 }
182
183 for i in (0..n).rev() {
185 for j in i + 1..n {
186 x[i] -= lu[[i, j]] * x[j];
187 }
188 if lu[[i, i]].abs() < 1e-14 {
190 return Err(IntegrateError::ComputationError(
191 "Zero diagonal element in back substitution".to_string(),
192 ));
193 }
194 x[i] /= lu[[i, i]];
195 }
196
197 Ok(x)
198}
199
200pub fn is_bifurcation_point(eigenvalues: &[Complex64]) -> bool {
202 eigenvalues.iter().any(|&eig| eig.re.abs() < 1e-8)
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use approx::assert_abs_diff_eq;
210 use scirs2_core::ndarray::array;
211
212 #[test]
213 fn test_compute_determinant() {
214 let matrix = array![[1.0, 2.0], [3.0, 4.0]];
216 let det = compute_determinant(&matrix);
217 assert_abs_diff_eq!(det, -2.0, epsilon = 1e-10);
218
219 let identity = array![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
221 let det = compute_determinant(&identity);
222 assert_abs_diff_eq!(det, 1.0, epsilon = 1e-10);
223
224 let singular = array![[1.0, 2.0], [2.0, 4.0]];
226 let det = compute_determinant(&singular);
227 assert_abs_diff_eq!(det, 0.0, epsilon = 1e-10);
228 }
229
230 #[test]
231 fn test_compute_trace() {
232 let matrix = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
233 let trace = compute_trace(&matrix);
234 assert_abs_diff_eq!(trace, 15.0, epsilon = 1e-10);
235 }
236
237 #[test]
238 fn test_estimate_matrix_rank() {
239 let matrix = array![[1.0, 0.0], [0.0, 1.0]];
241 let rank = estimate_matrix_rank(&matrix, 1e-10);
242 assert_eq!(rank, 2);
243
244 let matrix = array![[1.0, 2.0], [2.0, 4.0]];
246 let rank = estimate_matrix_rank(&matrix, 1e-10);
247 assert_eq!(rank, 1);
248 }
249
250 #[test]
251 fn test_solve_linear_system() {
252 let a = array![[2.0, 1.0], [1.0, 1.0]];
254 let b = array![3.0, 2.0];
255 let result = solve_linear_system(&a, &b).unwrap();
256 let expected = array![1.0, 1.0];
257
258 for i in 0..result.len() {
259 assert_abs_diff_eq!(result[i], expected[i], epsilon = 1e-10);
260 }
261 }
262
263 #[test]
264 fn test_is_bifurcation_point() {
265 use scirs2_core::numeric::Complex64;
266
267 let eigenvalues = vec![Complex64::new(0.0, 1.0), Complex64::new(-1.0, 0.0)];
269 assert!(is_bifurcation_point(&eigenvalues));
270
271 let eigenvalues = vec![Complex64::new(-1.0, 0.0), Complex64::new(-2.0, 0.0)];
273 assert!(!is_bifurcation_point(&eigenvalues));
274 }
275}