#[cfg(feature = "serde-serialize-no-std")] use serde::{Deserialize, Serialize}; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, OMatrix, OVector}; use crate::dimension::{Const, DimDiff, DimSub, U1}; use simba::scalar::ComplexField; use crate::linalg::householder; use crate::Matrix; use std::mem::MaybeUninit; /// Tridiagonalization of a symmetric matrix. #[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Serialize, OVector>: Serialize")) )] #[cfg_attr( feature = "serde-serialize-no-std", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, OMatrix: Deserialize<'de>, OVector>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct SymmetricTridiagonal> where DefaultAllocator: Allocator + Allocator>, { tri: OMatrix, off_diagonal: OVector>, } impl> Copy for SymmetricTridiagonal where DefaultAllocator: Allocator + Allocator>, OMatrix: Copy, OVector>: Copy, { } impl> SymmetricTridiagonal where DefaultAllocator: Allocator + Allocator>, { /// Computes the tridiagonalization of the symmetric matrix `m`. /// /// Only the lower-triangular part (including the diagonal) of `m` is read. pub fn new(mut m: OMatrix) -> Self { let dim = m.shape_generic().0; assert!( m.is_square(), "Unable to compute the symmetric tridiagonal decomposition of a non-square matrix." ); assert!( dim.value() != 0, "Unable to compute the symmetric tridiagonal decomposition of an empty matrix." ); let mut off_diagonal = Matrix::uninit(dim.sub(Const::<1>), Const::<1>); let mut p = Matrix::zeros_generic(dim.sub(Const::<1>), Const::<1>); for i in 0..dim.value() - 1 { let mut m = m.rows_range_mut(i + 1..); let (mut axis, mut m) = m.columns_range_pair_mut(i, i + 1..); let (norm, not_zero) = householder::reflection_axis_mut(&mut axis); off_diagonal[i] = MaybeUninit::new(norm); if not_zero { let mut p = p.rows_range_mut(i..); p.hegemv(crate::convert(2.0), &m, &axis, T::zero()); let dot = axis.dotc(&p); m.hegerc(-T::one(), &p, &axis, T::one()); m.hegerc(-T::one(), &axis, &p, T::one()); m.hegerc(dot * crate::convert(2.0), &axis, &axis, T::one()); } } // Safety: off_diagonal has been fully initialized. let off_diagonal = unsafe { off_diagonal.assume_init() }; Self { tri: m, off_diagonal, } } #[doc(hidden)] // For debugging. pub fn internal_tri(&self) -> &OMatrix { &self.tri } /// Retrieve the orthogonal transformation, diagonal, and off diagonal elements of this /// decomposition. pub fn unpack( self, ) -> ( OMatrix, OVector, OVector>, ) where DefaultAllocator: Allocator + Allocator>, { let diag = self.diagonal(); let q = self.q(); (q, diag, self.off_diagonal.map(T::modulus)) } /// Retrieve the diagonal, and off diagonal elements of this decomposition. pub fn unpack_tridiagonal( self, ) -> ( OVector, OVector>, ) where DefaultAllocator: Allocator + Allocator>, { (self.diagonal(), self.off_diagonal.map(T::modulus)) } /// The diagonal components of this decomposition. #[must_use] pub fn diagonal(&self) -> OVector where DefaultAllocator: Allocator, { self.tri.map_diagonal(|e| e.real()) } /// The off-diagonal components of this decomposition. #[must_use] pub fn off_diagonal(&self) -> OVector> where DefaultAllocator: Allocator>, { self.off_diagonal.map(T::modulus) } /// Computes the orthogonal matrix `Q` of this decomposition. #[must_use] pub fn q(&self) -> OMatrix { householder::assemble_q(&self.tri, self.off_diagonal.as_slice()) } /// Recomputes the original symmetric matrix. pub fn recompose(mut self) -> OMatrix { let q = self.q(); self.tri.fill_lower_triangle(T::zero(), 2); self.tri.fill_upper_triangle(T::zero(), 2); for i in 0..self.off_diagonal.len() { let val = T::from_real(self.off_diagonal[i].clone().modulus()); self.tri[(i + 1, i)] = val.clone(); self.tri[(i, i + 1)] = val; } &q * self.tri * q.adjoint() } }