use proptest::prelude::*;
use rustebra::krylov::arnoldi;
use rustebra::storage::{Basis, StaticStorage};
const N: usize = 4;
const ALGORITHM_TOL: f64 = 1e-10;
const TOL: f64 = 1e-6;
fn dense_hessenberg(h: &rustebra::krylov::HessenbergMatrix<f64, N>) -> nalgebra::Matrix4<f64> {
let mut dense = nalgebra::Matrix4::zeros();
for r in 0..N {
for c in 0..N {
dense[(r, c)] = h.entry(r, c).unwrap();
}
}
dense
}
fn sorted_eigenvalues(m: &nalgebra::Matrix4<f64>) -> [nalgebra::Complex<f64>; N] {
let mut eigenvalues = [nalgebra::Complex::new(0.0, 0.0); N];
eigenvalues.copy_from_slice(m.complex_eigenvalues().as_slice());
eigenvalues.sort_by(|x, y| (x.re, x.im).partial_cmp(&(y.re, y.im)).unwrap());
eigenvalues
}
proptest! {
#[test]
fn full_basis_hessenberg_has_the_spectrum_of_a(
entries in prop::collection::vec(-10.0..10.0f64, N * N),
v0 in prop::array::uniform4(-1.0..1.0f64),
) {
let a = nalgebra::Matrix4::from_row_slice(&entries);
let mut a_row_major = [0.0; N * N];
for r in 0..N {
for c in 0..N {
a_row_major[r * N + c] = a[(r, c)];
}
}
let norm_v0: f64 = v0.iter().map(|x| x * x).sum::<f64>().sqrt();
prop_assume!(norm_v0 > 0.1);
let mut buffer = [0.0; N * N];
let mut basis = Basis::<f64, N>::new(&mut buffer, N).unwrap();
let mut scratch = [0.0; N];
let (h, reached) = arnoldi(
&StaticStorage::new(a_row_major),
N,
&StaticStorage::new(v0),
ALGORITHM_TOL,
&mut basis,
&mut scratch,
)
.unwrap();
prop_assume!(reached == N);
let expected = sorted_eigenvalues(&a);
let actual = sorted_eigenvalues(&dense_hessenberg(&h));
for (index, (got, want)) in actual.iter().zip(expected.iter()).enumerate() {
prop_assert!(
(got - want).norm() < TOL,
"eigenvalue {index}: hessenberg gives {got}, input matrix gives {want}",
);
}
}
}