use super::{EigenPair, GEP};
use nalgebra::SymmetricEigen;
use std::fmt;
const MAX_DENSE_SIZE: usize = 1000;
pub fn nalgebra_solve_gep(gep: GEP, target_eigenvalue: f64) -> Result<EigenPair, NalgebraGEPError> {
if gep.a.dimension > MAX_DENSE_SIZE {
return Err(NalgebraGEPError::ProblemTooLarge);
}
let [a_mat, b_mat] = gep.to_nalgebra_dense_mats();
if let Some(cholesky_decomp) = b_mat.cholesky() {
let b_inverse = cholesky_decomp.inverse();
let ba_product = b_inverse * a_mat;
let ba_se_decomp = SymmetricEigen::new(ba_product);
if ba_se_decomp.eigenvalues.iter().all(|e| e.abs() < 1e-12) {
return Err(NalgebraGEPError::SpuriouslyConverged);
}
let mut delta_min = f64::MAX;
let mut delta_min_idx = 0;
for (eval_idx, eval) in ba_se_decomp.eigenvalues.iter().enumerate() {
let delta = (eval - target_eigenvalue).abs();
if delta < delta_min {
delta_min_idx = eval_idx;
delta_min = delta;
}
}
Ok(EigenPair {
value: *ba_se_decomp.eigenvalues.get(delta_min_idx).unwrap(),
vector: ba_se_decomp
.eigenvectors
.column(delta_min_idx)
.iter()
.cloned()
.collect(),
})
} else {
Err(NalgebraGEPError::FailedToInvertB)
}
}
#[derive(Debug, Clone)]
pub enum NalgebraGEPError {
FailedToInvertB,
SpuriouslyConverged,
ProblemTooLarge,
}
impl std::error::Error for NalgebraGEPError {}
impl std::fmt::Display for NalgebraGEPError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::FailedToInvertB => write!(
f,
"Failed to invert B-matrix (via cholesky); likely ill-conditioned!"
),
Self::SpuriouslyConverged => write!(f, "Only spurious modes were found!"),
Self::ProblemTooLarge => write!(
f,
"Matrices Exceeded Maximum Size ({}x{}); Cannot Solve!",
MAX_DENSE_SIZE, MAX_DENSE_SIZE
),
}
}
}