#[cfg(feature = "serde-serialize")] use serde::{Serialize, Deserialize}; use alga::general::Real; use allocator::Allocator; use base::{DefaultAllocator, MatrixMN, MatrixN, SquareMatrix, VectorN}; use dimension::{DimDiff, DimSub, U1}; use storage::Storage; use 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 { MatrixMN::new_uninitialized_generic(dim.sub(U1), U1) }; let mut p = unsafe { MatrixMN::new_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.gemv_symm(::convert(2.0), &m, &axis, N::zero()); let dot = axis.dot(&p); p.axpy(-dot, &axis, N::one()); m.ger_symm(-N::one(), &p, &axis, N::one()); m.ger_symm(-N::one(), &axis, &p, N::one()); } } SymmetricTridiagonal { tri: m, off_diagonal: 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, { let diag = self.diagonal(); let q = self.q(); (q, diag, self.off_diagonal) } /// Retrieve the diagonal, and off diagonal elements of this decomposition. pub fn unpack_tridiagonal(self) -> (VectorN, VectorN>) where DefaultAllocator: Allocator, { let diag = self.diagonal(); (diag, self.off_diagonal) } /// The diagonal components of this decomposition. pub fn diagonal(&self) -> VectorN where DefaultAllocator: Allocator, { self.tri.diagonal() } /// The off-diagonal components of this decomposition. pub fn off_diagonal(&self) -> &VectorN> where DefaultAllocator: Allocator, { &self.off_diagonal } /// Computes the orthogonal matrix `Q` of this decomposition. pub fn q(&self) -> MatrixN { householder::assemble_q(&self.tri) } /// 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() { self.tri[(i + 1, i)] = self.off_diagonal[i]; self.tri[(i, i + 1)] = self.off_diagonal[i]; } &q * self.tri * q.transpose() } } impl, S: Storage> SquareMatrix where DefaultAllocator: Allocator + Allocator>, { /// Computes the tridiagonalization of this symmetric matrix. /// /// Only the lower-triangular part (including the diagonal) of `m` is read. pub fn symmetric_tridiagonalize(self) -> SymmetricTridiagonal { SymmetricTridiagonal::new(self.into_owned()) } }