use super::hermitian::hermitian;
use super::schur::{eigenvectors, schur};
use super::{Matrix, Scalar};
use crate::math::complex::{Complex, Float};
#[derive(Clone, Debug)]
pub struct Eigen<R> {
values: Vec<Complex<R>>,
vectors: Matrix<Complex<R>>,
}
impl<R: Float> Eigen<R> {
pub fn values(&self) -> &[Complex<R>] {
&self.values
}
pub fn vectors(&self) -> &Matrix<Complex<R>> {
&self.vectors
}
}
#[derive(Clone, Debug)]
pub struct HermitianEigen<T: Scalar> {
values: Vec<T::Real>,
vectors: Matrix<T>,
}
impl<T: Scalar> HermitianEigen<T> {
pub fn values(&self) -> &[T::Real] {
&self.values
}
pub fn vectors(&self) -> &Matrix<T> {
&self.vectors
}
}
impl<T: Scalar> Matrix<T> {
#[track_caller]
pub fn eigen(&self) -> Eigen<T::Real> {
assert!(self.is_square(), "the matrix is not square");
let n = self.rows();
let (t, q, values) = schur(self, true, true);
let vectors = eigenvectors(&t.data, &q.unwrap_or_default().data, n);
Eigen {
values,
vectors: Matrix::from_column_major(n, n, vectors),
}
}
#[track_caller]
pub fn hermitian_eigen(&self) -> HermitianEigen<T> {
assert!(self.is_square(), "the matrix is not square");
let (values, vectors) = hermitian(self, true);
HermitianEigen {
values,
vectors: vectors.unwrap_or_default(),
}
}
#[track_caller]
pub fn hermitian_eigenvalues(&self) -> Vec<T::Real> {
assert!(self.is_square(), "the matrix is not square");
hermitian(self, false).0
}
}
pub(crate) fn real_to_complex_schur<C: Scalar>(t: &mut [C], q: &mut [C], n: usize) {
let at = |i: usize, j: usize| i + j * n;
let zero = C::Real::ZERO;
for m in (1..n).rev() {
let sub = t[at(m, m - 1)];
if sub == C::zero() {
continue;
}
let (a, b, c, d) = (t[at(m - 1, m - 1)], t[at(m - 1, m)], sub, t[at(m, m)]);
let part = |x: C| x.re().abs().max(x.im().abs());
let s = part(a).max(part(b)).max(part(c)).max(part(d));
let down = |x: C| C::from_parts(x.re() / s, x.im() / s);
let (a, b, c, d) = (down(a), down(b), down(c), down(d));
let half = (a - d).scale(C::Real::HALF);
let disc = (half * half + b * c).sqrt();
let mu = half + disc;
let r = mu.abs().hypot(c.abs());
if r == zero {
continue;
}
let cs = mu.scale(C::Real::ONE / r);
let sn = c.scale(C::Real::ONE / r);
for j in m - 1..n {
let (x, y) = (t[at(m - 1, j)], t[at(m, j)]);
t[at(m - 1, j)] = cs.conj() * x + sn * y;
t[at(m, j)] = -sn.conj() * x + cs * y;
}
for i in 0..=m {
let (x, y) = (t[at(i, m - 1)], t[at(i, m)]);
t[at(i, m - 1)] = x * cs + y * sn.conj();
t[at(i, m)] = -(x * sn) + y * cs.conj();
}
for i in 0..n {
let (x, y) = (q[at(i, m - 1)], q[at(i, m)]);
q[at(i, m - 1)] = x * cs + y * sn.conj();
q[at(i, m)] = -(x * sn) + y * cs.conj();
}
t[at(m, m - 1)] = C::zero();
}
}