#![cfg(feature = "alloc")]
use super::approx_eq;
use nalgebra::{DMatrix, DVector};
use proptest::prelude::*;
use rustebra::krylov::gmres;
use rustebra::sparse::CsrMatrix;
use rustebra::storage::Basis;
const N: usize = 4;
const TOL: f64 = 1e-6;
fn csr_from_dense(a: &[f64; N * N]) -> CsrMatrix<f64> {
let mut row_ptr = vec![0_u32];
let mut col_indices = vec![];
let mut values = vec![];
for r in 0..N {
for c in 0..N {
col_indices.push(c as u32);
values.push(a[r * N + c]);
}
row_ptr.push(col_indices.len() as u32);
}
CsrMatrix::new(N, N, row_ptr, col_indices, values).unwrap()
}
prop_compose! {
fn diagonally_dominant_matrix()(
off_diagonal in prop::array::uniform16(-1.0..1.0f64),
diagonal_boost in prop::array::uniform4(5.0..10.0f64),
) -> [f64; N * N] {
let mut a = off_diagonal;
for r in 0..N {
a[r * N + r] = 0.0;
}
let mut row_sums = [0.0; N];
for r in 0..N {
row_sums[r] = (0..N).map(|c| a[r * N + c].abs()).sum();
}
for r in 0..N {
a[r * N + r] = row_sums[r] + diagonal_boost[r];
}
a
}
}
proptest! {
#[test]
fn solution_matches_nalgebra_direct_solve(
a in diagonally_dominant_matrix(),
b in prop::array::uniform4(-10.0..10.0f64),
) {
let m = csr_from_dense(&a);
let x0 = [0.0; N];
let mut out_x = [0.0; N];
let mut buffer = [0.0; N * N];
let mut basis = Basis::<f64, N>::new(&mut buffer, N).unwrap();
let mut scratch = [0.0; N];
gmres(&m, &b, &x0, 10, 1e-10, &mut out_x, &mut basis, &mut scratch).unwrap();
let a_na = DMatrix::from_row_slice(N, N, &a);
let b_na = DVector::from_row_slice(&b);
let x_na = a_na
.lu()
.solve(&b_na)
.expect("diagonally dominant matrices are always invertible");
for i in 0..N {
prop_assert!(
approx_eq(out_x[i], x_na[i], TOL),
"x[{}]: ours={} vs nalgebra={}",
i,
out_x[i],
x_na[i]
);
}
}
}