pub fn gaussian_eliminate(a: &[Vec<f64>], b: &[f64]) -> Option<Vec<f64>> {
let n = a.len();
if n == 0 || b.len() != n {
return None;
}
for row in a {
if row.len() != n {
return None;
}
}
let mut aug: Vec<Vec<f64>> = Vec::with_capacity(n);
for i in 0..n {
let mut row = a[i].clone();
row.push(b[i]);
aug.push(row);
}
for col in 0..n {
let mut max_row = col;
let mut max_val = aug[col][col].abs();
for r in (col + 1)..n {
if aug[r][col].abs() > max_val {
max_val = aug[r][col].abs();
max_row = r;
}
}
if max_val < 1e-12 {
return None; }
aug.swap(col, max_row);
let pivot = aug[col][col];
for r in (col + 1)..n {
let factor = aug[r][col] / pivot;
for c in col..=n {
aug[r][c] -= factor * aug[col][c];
}
}
}
let mut x = vec![0.0; n];
for i in (0..n).rev() {
if aug[i][i].abs() < 1e-12 {
return None;
}
let mut sum = aug[i][n];
for j in (i + 1)..n {
sum -= aug[i][j] * x[j];
}
x[i] = sum / aug[i][i];
}
Some(x)
}
pub fn jacobi_eigenvalues(mat: &[Vec<f64>], max_iters: usize) -> Vec<f64> {
let n = mat.len();
if n == 0 {
return vec![];
}
let mut a: Vec<Vec<f64>> = mat.to_vec();
for _ in 0..max_iters {
let mut max_val = 0.0_f64;
let mut p = 0usize;
let mut q = 1usize;
for i in 0..n {
for j in (i + 1)..n {
if a[i][j].abs() > max_val {
max_val = a[i][j].abs();
p = i;
q = j;
}
}
}
if max_val < 1e-10 {
break; }
let app = a[p][p];
let aqq = a[q][q];
let apq = a[p][q];
let theta = if (app - aqq).abs() < 1e-15 {
std::f64::consts::FRAC_PI_4
} else {
0.5 * (2.0 * apq / (app - aqq)).atan()
};
let c = theta.cos();
let s = theta.sin();
let mut new_a = a.clone();
for i in 0..n {
if i != p && i != q {
let aip = a[i][p];
let aiq = a[i][q];
new_a[i][p] = c * aip + s * aiq;
new_a[p][i] = new_a[i][p];
new_a[i][q] = -s * aip + c * aiq;
new_a[q][i] = new_a[i][q];
}
}
new_a[p][p] = c * c * app + 2.0 * s * c * apq + s * s * aqq;
new_a[q][q] = s * s * app - 2.0 * s * c * apq + c * c * aqq;
new_a[p][q] = 0.0;
new_a[q][p] = 0.0;
a = new_a;
}
let mut eigenvalues: Vec<f64> = (0..n).map(|i| a[i][i]).collect();
eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
eigenvalues
}
pub fn mat_mul(a: &[Vec<f64>], b: &[Vec<f64>]) -> Vec<Vec<f64>> {
let n = a.len();
let mut c = vec![vec![0.0; n]; n];
for i in 0..n {
for j in 0..n {
let mut sum = 0.0;
for k in 0..n {
sum += a[i][k] * b[k][j];
}
c[i][j] = sum;
}
}
c
}
pub fn transpose(a: &[Vec<f64>]) -> Vec<Vec<f64>> {
let n = a.len();
let mut t = vec![vec![0.0; n]; n];
for i in 0..n {
for j in 0..n {
t[i][j] = a[j][i];
}
}
t
}
pub fn trace(a: &[Vec<f64>]) -> f64 {
(0..a.len()).map(|i| a[i][i]).sum()
}
pub fn frobenius_norm(a: &[Vec<f64>]) -> f64 {
a.iter().flat_map(|row| row.iter()).map(|x| x * x).sum::<f64>().sqrt()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gaussian_identity() {
let a = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
let b = vec![3.0, 5.0];
let x = gaussian_eliminate(&a, &b).unwrap();
assert!((x[0] - 3.0).abs() < 1e-9);
assert!((x[1] - 5.0).abs() < 1e-9);
}
#[test]
fn test_gaussian_2x2() {
let a = vec![vec![2.0, 1.0], vec![5.0, 3.0]];
let b = vec![11.0, 27.0]; let x = gaussian_eliminate(&a, &b).unwrap();
assert!((x[0] - 6.0).abs() < 1e-9);
assert!((x[1] - (-1.0)).abs() < 1e-9);
}
#[test]
fn test_gaussian_singular() {
let a = vec![vec![1.0, 2.0], vec![2.0, 4.0]];
let b = vec![3.0, 6.0];
assert!(gaussian_eliminate(&a, &b).is_none());
}
#[test]
fn test_jacobi_identity() {
let a = vec![vec![3.0, 0.0], vec![0.0, 7.0]];
let eigs = jacobi_eigenvalues(&a, 100);
assert!((eigs[0] - 3.0).abs() < 1e-6);
assert!((eigs[1] - 7.0).abs() < 1e-6);
}
#[test]
fn test_jacobi_2x2() {
let a = vec![vec![4.0, 1.0], vec![1.0, 3.0]];
let eigs = jacobi_eigenvalues(&a, 100);
let l1 = (7.0 - 5.0_f64.sqrt()) / 2.0;
let l2 = (7.0 + 5.0_f64.sqrt()) / 2.0;
assert!((eigs[0] - l1).abs() < 1e-6);
assert!((eigs[1] - l2).abs() < 1e-6);
}
#[test]
fn test_jacobi_3x3() {
let a = vec![vec![2.0, -1.0, 0.0], vec![-1.0, 2.0, -1.0], vec![0.0, -1.0, 2.0]];
let eigs = jacobi_eigenvalues(&a, 200);
let sqrt2 = 2.0_f64.sqrt();
assert!((eigs[0] - (2.0 - sqrt2)).abs() < 1e-4);
assert!((eigs[1] - 2.0).abs() < 1e-4);
assert!((eigs[2] - (2.0 + sqrt2)).abs() < 1e-4);
}
#[test]
fn test_trace() {
let a = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
assert!((trace(&a) - 5.0).abs() < 1e-9);
}
}