#![cfg(all(feature = "nalgebra", feature = "nalgebra-lapack"))]
use basin::{GramMatrix, LinearSolveError, LinearSolveSpd, SymmetricEigen};
use nalgebra::{DMatrix, DVector};
fn approx_eq(a: f64, b: f64, tol: f64) -> bool {
(a - b).abs() < tol
}
#[test]
fn solve_spd_happy_path() {
let a = DMatrix::from_row_slice(2, 2, &[4.0, 1.0, 1.0, 3.0]);
let b = DVector::from_vec(vec![1.0, 2.0]);
let x = a.solve_spd(&b).expect("SPD system must solve");
assert!(approx_eq(x[0], 1.0 / 11.0, 1e-12));
assert!(approx_eq(x[1], 7.0 / 11.0, 1e-12));
}
#[test]
fn solve_spd_indefinite_returns_error() {
let a = DMatrix::from_row_slice(2, 2, &[1.0, 2.0, 2.0, 1.0]);
let b = DVector::from_vec(vec![1.0, 1.0]);
let err = a.solve_spd(&b).expect_err("indefinite must fail");
assert_eq!(err, LinearSolveError::NotPositiveDefinite);
}
#[test]
fn solve_spd_rank_deficient_gram_is_singular() {
let a = DMatrix::from_row_slice(2, 2, &[1.0, 2.0, 2.0, 4.0]);
let g = a.gram();
let b = DVector::from_vec(vec![1.0, 1.0]);
let err = g.solve_spd(&b).expect_err("rank-deficient gram must fail");
assert_eq!(err, LinearSolveError::NotPositiveDefinite);
}
#[test]
fn symmetric_eigen_recovers_factorization() {
let c = DMatrix::<f64>::from_row_slice(2, 2, &[2.0, 1.0, 1.0, 2.0]);
let (b, lambda) = c.try_eigh().expect("eigendecomposition");
let mut lambda_diag = DMatrix::<f64>::zeros(2, 2);
for i in 0..2 {
lambda_diag[(i, i)] = lambda[i];
}
let recomposed = &b * &lambda_diag * b.transpose();
for r in 0..2 {
for col in 0..2 {
assert!(approx_eq(recomposed[(r, col)], c[(r, col)], 1e-10));
}
}
}
#[test]
fn gauss_newton_converges_through_lapack_cholesky() {
use basin::problems::RosenbrockResiduals;
use basin::{Executor, GaussNewton, NllsState, TerminationReason};
let problem = RosenbrockResiduals::<DVector<f64>>::new();
let initial = DVector::from_vec(vec![-1.2, 1.0]);
let result = Executor::new(problem, GaussNewton::new(), NllsState::new(initial))
.max_iter(20)
.run()
.unwrap();
assert_eq!(result.reason, TerminationReason::SolverConverged);
let x = result.param();
assert!(approx_eq(x[0], 1.0, 1e-6));
assert!(approx_eq(x[1], 1.0, 1e-6));
}