#[cfg(feature = "serde-serialize")] use serde::{Deserialize, Serialize}; use crate::allocator::Allocator; use crate::base::{DefaultAllocator, MatrixN, VectorN}; use crate::dimension::{DimDiff, DimSub, U1}; use crate::storage::Storage; use simba::scalar::ComplexField; use crate::linalg::householder; /// Tridiagonalization of a symmetric matrix. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", serde(bound(serialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Serialize, VectorN>: Serialize")) )] #[cfg_attr( feature = "serde-serialize", serde(bound(deserialize = "DefaultAllocator: Allocator + Allocator>, MatrixN: Deserialize<'de>, VectorN>: Deserialize<'de>")) )] #[derive(Clone, Debug)] pub struct SymmetricTridiagonal> where DefaultAllocator: Allocator + Allocator>, { tri: MatrixN, off_diagonal: VectorN>, } impl> Copy for SymmetricTridiagonal where DefaultAllocator: Allocator + Allocator>, MatrixN: Copy, VectorN>: 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: MatrixN) -> Self { let dim = m.data.shape().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 = unsafe { crate::unimplemented_or_uninitialized_generic!(dim.sub(U1), U1) }; let mut p = unsafe { crate::unimplemented_or_uninitialized_generic!(dim.sub(U1), U1) }; 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] = norm; if not_zero { let mut p = p.rows_range_mut(i..); p.hegemv(crate::convert(2.0), &m, &axis, N::zero()); let dot = axis.dotc(&p); m.hegerc(-N::one(), &p, &axis, N::one()); m.hegerc(-N::one(), &axis, &p, N::one()); m.hegerc(dot * crate::convert(2.0), &axis, &axis, N::one()); } } Self { tri: m, off_diagonal, } } #[doc(hidden)] // For debugging. pub fn internal_tri(&self) -> &MatrixN { &self.tri } /// Retrieve the orthogonal transformation, diagonal, and off diagonal elements of this /// decomposition. pub fn unpack( self, ) -> ( MatrixN, VectorN, VectorN>, ) where DefaultAllocator: Allocator + Allocator>, { let diag = self.diagonal(); let q = self.q(); (q, diag, self.off_diagonal.map(N::modulus)) } /// Retrieve the diagonal, and off diagonal elements of this decomposition. pub fn unpack_tridiagonal( self, ) -> ( VectorN, VectorN>, ) where DefaultAllocator: Allocator + Allocator>, { (self.diagonal(), self.off_diagonal.map(N::modulus)) } /// The diagonal components of this decomposition. pub fn diagonal(&self) -> VectorN where DefaultAllocator: Allocator, { self.tri.map_diagonal(|e| e.real()) } /// The off-diagonal components of this decomposition. pub fn off_diagonal(&self) -> VectorN> where DefaultAllocator: Allocator>, { self.off_diagonal.map(N::modulus) } /// Computes the orthogonal matrix `Q` of this decomposition. pub fn q(&self) -> MatrixN { householder::assemble_q(&self.tri, self.off_diagonal.as_slice()) } /// Recomputes the original symmetric matrix. pub fn recompose(mut self) -> MatrixN { let q = self.q(); self.tri.fill_lower_triangle(N::zero(), 2); self.tri.fill_upper_triangle(N::zero(), 2); for i in 0..self.off_diagonal.len() { let val = N::from_real(self.off_diagonal[i].modulus()); self.tri[(i + 1, i)] = val; self.tri[(i, i + 1)] = val; } &q * self.tri * q.adjoint() } }