use crate::algebra::abstr::{AbsDiffEq, Field, RelativeEq, Scalar};
use crate::algebra::linear::matrix::Diagonal;
use crate::algebra::linear::{
matrix::{
EigenDec, EigenDecomposition, General, HessenbergDecomposition, SchurDecomposition, Solve,
UpperHessenberg,
},
vector::Vector,
};
use crate::elementary::Power;
impl<T> EigenDecomposition<T> for General<T>
where
T: Field + Scalar + Power + AbsDiffEq<Epsilon = T> + RelativeEq,
{
fn dec_eigen(&self) -> Result<EigenDec<T>, String> {
let (m, n): (usize, usize) = self.dim();
debug_assert_eq!(
m, n,
"Unable to compute the eigen value of a non-square matrix"
);
debug_assert_ne!(
m, 0,
"Unable to compute the eigen value of an empty matrix."
);
let values = self.eigenvalue_r()?;
let vectors: General<T> = self.eigenvector_r(&values);
Ok(EigenDec::new(Diagonal::new(&values), vectors))
}
}
impl<T> General<T>
where
T: Field + Scalar + Power + AbsDiffEq<Epsilon = T> + RelativeEq,
{
pub fn eigenvalue_r(&self) -> Result<Vec<T>, String> {
let (m, n): (usize, usize) = self.dim();
let h: UpperHessenberg<T> = self.dec_hessenberg().h();
let dec_schur_res = h.dec_schur();
if dec_schur_res.is_err() {
return Result::Err("".to_string());
}
let u = dec_schur_res.unwrap().u();
let mut eig = Vec::with_capacity(m);
let mut i = 0;
while i < n {
if i == (n - 1) || u[[i + 1, i]] == T::zero() {
eig.push(u[[i, i]]);
i += 1;
} else {
let a_ii = u[[i, i]];
let a_ii1 = u[[i, i + 1]];
let a_i1i = u[[i + 1, i]];
let a_i1i1 = u[[i + 1, i + 1]];
let (l1, l2) = General::eigen_2by2(a_ii, a_ii1, a_i1i, a_i1i1);
eig.push(l1);
eig.push(l2);
i += 2;
}
}
Result::Ok(eig)
}
pub fn eigenvector_r(&self, values: &[T]) -> General<T> {
let eye: General<T> = General::one(self.m);
let zero_vector: Vector<T> = Vector::zero(self.m);
let mut vectors: General<T> = General::zero(self.m, self.m);
for (c, val) in values.iter().enumerate() {
let diff: General<T> = self - &(&eye * val);
let vec: Vector<T> = diff.solve(&zero_vector).unwrap();
let v = General::norm(vec);
vectors.set_column(&v, c);
}
vectors
}
fn norm(mut v: Vector<T>) -> Vector<T> {
let norm = v.p_norm(&T::from_f32(2.0));
if norm != T::zero() {
v *= T::one() / norm;
}
v
}
fn eigen_2by2(a11: T, a12: T, a21: T, a22: T) -> (T, T) {
let m = (a11 + a22) / T::from_f32(2.0);
let p = a11 * a22 - a12 * a21;
let k = (m * m - p).sqrt();
let l1 = m + k;
let l2 = m - k;
(l1, l2)
}
}