use num_complex::Complex;
use faer::sparse::{Argsort, Pair, SparseColMat, SymbolicSparseColMat, Triplet};
use faer::sparse::linalg::solvers::{Lu, SymbolicLu};
use faer::Col;
use faer::prelude::{Reborrow, Solve};
pub struct SparseMatrix {
n: usize,
mat: SparseColMat<usize, Complex<f64>>,
}
impl SparseMatrix {
pub fn build(n: usize, entries: &[(usize, usize, Complex<f64>)]) -> Option<Self> {
let triplets: Vec<Triplet<usize, usize, Complex<f64>>> =
entries.iter().map(|&(r, c, v)| Triplet::new(r, c, v)).collect();
let mat = SparseColMat::<usize, Complex<f64>>::try_new_from_triplets(n, n, &triplets).ok()?;
Some(Self { n, mat })
}
pub fn mul_vec(&self, v: &[Complex<f64>]) -> Vec<Complex<f64>> {
let vcol = Col::<Complex<f64>>::from_fn(self.n, |i| v[i]);
let result = &self.mat * &vcol;
(0..self.n).map(|i| result[i]).collect()
}
}
pub fn solve_complex(
n: usize,
entries: &[(usize, usize, Complex<f64>)],
rhs: &[Complex<f64>],
) -> Option<Vec<Complex<f64>>> {
let triplets: Vec<Triplet<usize, usize, Complex<f64>>> =
entries.iter().map(|&(r, c, v)| Triplet::new(r, c, v)).collect();
let mat = SparseColMat::<usize, Complex<f64>>::try_new_from_triplets(n, n, &triplets).ok()?;
let lu = mat.sp_lu().ok()?;
let b = Col::<Complex<f64>>::from_fn(n, |i| rhs[i]);
let x = lu.solve(&b);
if (0..n).any(|i| !x[i].re.is_finite() || !x[i].im.is_finite()) {
return None;
}
Some((0..n).map(|i| x[i]).collect())
}
pub struct RealSparseSystem {
n: usize,
symbolic_mat: SymbolicSparseColMat<usize>,
argsort: Argsort<usize>,
symbolic_lu: SymbolicLu<usize>,
}
impl RealSparseSystem {
pub fn new(n: usize, entries: &[(usize, usize, f64)]) -> Option<Self> {
let pairs: Vec<Pair<usize, usize>> =
entries.iter().map(|&(r, c, _)| Pair { row: r, col: c }).collect();
let (symbolic_mat, argsort) = SymbolicSparseColMat::try_new_from_indices(n, n, &pairs).ok()?;
let symbolic_lu = SymbolicLu::try_new(symbolic_mat.rb()).ok()?;
Some(Self { n, symbolic_mat, argsort, symbolic_lu })
}
pub fn factor_and_solve(&self, entries: &[(usize, usize, f64)], rhs: &[f64]) -> Option<Vec<f64>> {
let vals: Vec<f64> = entries.iter().map(|&(_, _, v)| v).collect();
self.solve_values(&vals, rhs)
}
pub fn solve_values(&self, vals: &[f64], rhs: &[f64]) -> Option<Vec<f64>> {
let mat = SparseColMat::new_from_argsort(self.symbolic_mat.clone(), &self.argsort, vals).ok()?;
let lu = Lu::try_new_with_symbolic(self.symbolic_lu.clone(), mat.as_ref()).ok()?;
let b = Col::<f64>::from_fn(self.n, |i| rhs[i]);
let x = lu.solve(&b);
if (0..self.n).any(|i| !x[i].is_finite()) {
return None;
}
Some((0..self.n).map(|i| x[i]).collect())
}
}
impl crate::solver::LinearSolver for RealSparseSystem {
fn new(n: usize, entries: &[(usize, usize, f64)]) -> Option<Self> {
RealSparseSystem::new(n, entries)
}
fn factor_and_solve(&mut self, entries: &[(usize, usize, f64)], rhs: &[f64]) -> Option<Vec<f64>> {
RealSparseSystem::factor_and_solve(self, entries, rhs)
}
fn factor_and_solve_values(&mut self, values: &[f64], rhs: &[f64]) -> Option<Vec<f64>> {
self.solve_values(values, rhs)
}
}
pub struct ComplexSparseSystem {
n: usize,
lu: Lu<usize, Complex<f64>>,
}
impl ComplexSparseSystem {
pub fn new(n: usize, entries: &[(usize, usize, Complex<f64>)]) -> Option<Self> {
let triplets: Vec<Triplet<usize, usize, Complex<f64>>> =
entries.iter().map(|&(r, c, v)| Triplet::new(r, c, v)).collect();
let mat = SparseColMat::<usize, Complex<f64>>::try_new_from_triplets(n, n, &triplets).ok()?;
Some(Self { n, lu: mat.sp_lu().ok()? })
}
pub fn solve(&self, rhs: &[Complex<f64>]) -> Option<Vec<Complex<f64>>> {
let b = Col::<Complex<f64>>::from_fn(self.n, |i| rhs[i]);
let x = self.lu.solve(&b);
if (0..self.n).any(|i| !x[i].re.is_finite() || !x[i].im.is_finite()) {
return None;
}
Some((0..self.n).map(|i| x[i]).collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn solve_complex_simple_system() {
let entries = vec![
(0, 0, Complex::new(2.0, 0.0)),
(0, 1, Complex::new(1.0, 0.0)),
(1, 0, Complex::new(1.0, 0.0)),
(1, 1, Complex::new(3.0, 0.0)),
];
let rhs = vec![Complex::new(5.0, 0.0), Complex::new(10.0, 0.0)];
let x = solve_complex(2, &entries, &rhs).unwrap();
assert!((x[0] - Complex::new(1.0, 0.0)).norm() < 1e-10);
assert!((x[1] - Complex::new(3.0, 0.0)).norm() < 1e-10);
}
#[test]
fn solve_complex_duplicate_entries_sum() {
let entries = vec![
(0, 0, Complex::new(1.0, 0.0)),
(0, 0, Complex::new(1.0, 0.0)),
(0, 1, Complex::new(1.0, 0.0)),
(1, 0, Complex::new(1.0, 0.0)),
(1, 1, Complex::new(3.0, 0.0)),
];
let rhs = vec![Complex::new(5.0, 0.0), Complex::new(10.0, 0.0)];
let x = solve_complex(2, &entries, &rhs).unwrap();
assert!((x[0] - Complex::new(1.0, 0.0)).norm() < 1e-10);
assert!((x[1] - Complex::new(3.0, 0.0)).norm() < 1e-10);
}
#[test]
fn real_sparse_system_refactor_reuses_symbolic() {
let entries_a = vec![(0, 0, 2.0), (0, 1, 1.0), (1, 0, 1.0), (1, 1, 3.0)];
let sys = RealSparseSystem::new(2, &entries_a).unwrap();
let x = sys.factor_and_solve(&entries_a, &[5.0, 10.0]).unwrap();
assert!((x[0] - 1.0).abs() < 1e-10);
assert!((x[1] - 3.0).abs() < 1e-10);
let entries_b = vec![(0, 0, 4.0), (0, 1, 1.0), (1, 0, 1.0), (1, 1, 2.0)];
let x2 = sys.factor_and_solve(&entries_b, &[6.0, 5.0]).unwrap();
assert!((x2[0] - 1.0).abs() < 1e-10);
assert!((x2[1] - 2.0).abs() < 1e-10);
}
#[test]
fn singular_matrix_returns_none() {
let entries = vec![(0, 0, 1.0), (0, 1, 1.0), (1, 0, 2.0), (1, 1, 2.0)];
let sys = RealSparseSystem::new(2, &entries);
if let Some(sys) = sys {
assert!(sys.factor_and_solve(&entries, &[1.0, 2.0]).is_none());
}
}
}