1#[cfg(feature = "serde-serialize")]
2use serde::{Deserialize, Serialize};
3
4use num::Zero;
5use std::ops::MulAssign;
6
7use simba::scalar::RealField;
8
9use crate::ComplexHelper;
10use na::allocator::Allocator;
11use na::dimension::{Const, Dim};
12use na::{DefaultAllocator, Matrix, OMatrix, OVector, Scalar};
13
14use lapack;
15
16#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
18#[cfg_attr(
19 feature = "serde-serialize",
20 serde(bound(serialize = "DefaultAllocator: Allocator<D, D> +
21 Allocator<D>,
22 OVector<T, D>: Serialize,
23 OMatrix<T, D, D>: Serialize"))
24)]
25#[cfg_attr(
26 feature = "serde-serialize",
27 serde(bound(deserialize = "DefaultAllocator: Allocator<D, D> +
28 Allocator<D>,
29 OVector<T, D>: Deserialize<'de>,
30 OMatrix<T, D, D>: Deserialize<'de>"))
31)]
32#[derive(Clone, Debug)]
33pub struct SymmetricEigen<T: Scalar, D: Dim>
34where
35 DefaultAllocator: Allocator<D> + Allocator<D, D>,
36{
37 pub eigenvectors: OMatrix<T, D, D>,
39
40 pub eigenvalues: OVector<T, D>,
42}
43
44impl<T: Scalar + Copy, D: Dim> Copy for SymmetricEigen<T, D>
45where
46 DefaultAllocator: Allocator<D, D> + Allocator<D>,
47 OMatrix<T, D, D>: Copy,
48 OVector<T, D>: Copy,
49{
50}
51
52impl<T: SymmetricEigenScalar + RealField, D: Dim> SymmetricEigen<T, D>
53where
54 DefaultAllocator: Allocator<D, D> + Allocator<D>,
55{
56 pub fn new(m: OMatrix<T, D, D>) -> Self {
61 let (vals, vecs) =
62 Self::do_decompose(m, true).expect("SymmetricEigen: convergence failure.");
63 Self {
64 eigenvalues: vals,
65 eigenvectors: vecs.unwrap(),
66 }
67 }
68
69 pub fn try_new(m: OMatrix<T, D, D>) -> Option<Self> {
74 Self::do_decompose(m, true).map(|(vals, vecs)| SymmetricEigen {
75 eigenvalues: vals,
76 eigenvectors: vecs.unwrap(),
77 })
78 }
79
80 fn do_decompose(
81 mut m: OMatrix<T, D, D>,
82 eigenvectors: bool,
83 ) -> Option<(OVector<T, D>, Option<OMatrix<T, D, D>>)> {
84 assert!(
85 m.is_square(),
86 "Unable to compute the eigenvalue decomposition of a non-square matrix."
87 );
88
89 let jobz = if eigenvectors { b'V' } else { b'T' };
90
91 let nrows = m.shape_generic().0;
92 let n = nrows.value();
93
94 let lda = n as i32;
95
96 let mut values = Matrix::zeros_generic(nrows, Const::<1>);
97 let mut info = 0;
98
99 let lwork = T::xsyev_work_size(jobz, b'L', n as i32, m.as_mut_slice(), lda, &mut info);
100 lapack_check!(info);
101
102 let mut work = vec![T::zero(); lwork as usize];
103
104 T::xsyev(
105 jobz,
106 b'L',
107 n as i32,
108 m.as_mut_slice(),
109 lda,
110 values.as_mut_slice(),
111 &mut work,
112 lwork,
113 &mut info,
114 );
115 lapack_check!(info);
116
117 let vectors = if eigenvectors { Some(m) } else { None };
118 Some((values, vectors))
119 }
120
121 pub fn eigenvalues(m: OMatrix<T, D, D>) -> OVector<T, D> {
125 Self::do_decompose(m, false)
126 .expect("SymmetricEigen eigenvalues: convergence failure.")
127 .0
128 }
129
130 pub fn try_eigenvalues(m: OMatrix<T, D, D>) -> Option<OVector<T, D>> {
134 Self::do_decompose(m, false).map(|res| res.0)
135 }
136
137 #[inline]
139 #[must_use]
140 pub fn determinant(&self) -> T {
141 let mut det = T::one();
142 for e in self.eigenvalues.iter() {
143 det *= e.clone();
144 }
145
146 det
147 }
148
149 #[must_use]
153 pub fn recompose(&self) -> OMatrix<T, D, D> {
154 let mut u_t = self.eigenvectors.clone();
155 for i in 0..self.eigenvalues.len() {
156 let val = self.eigenvalues[i].clone();
157 u_t.column_mut(i).mul_assign(val);
158 }
159 u_t.transpose_mut();
160 &self.eigenvectors * u_t
161 }
162}
163
164pub trait SymmetricEigenScalar: Scalar {
172 #[allow(missing_docs)]
173 fn xsyev(
174 jobz: u8,
175 uplo: u8,
176 n: i32,
177 a: &mut [Self],
178 lda: i32,
179 w: &mut [Self],
180 work: &mut [Self],
181 lwork: i32,
182 info: &mut i32,
183 );
184 #[allow(missing_docs)]
185 fn xsyev_work_size(jobz: u8, uplo: u8, n: i32, a: &mut [Self], lda: i32, info: &mut i32)
186 -> i32;
187}
188
189macro_rules! real_eigensystem_scalar_impl (
190 ($N: ty, $xsyev: path) => (
191 impl SymmetricEigenScalar for $N {
192 #[inline]
193 fn xsyev(jobz: u8, uplo: u8, n: i32, a: &mut [Self], lda: i32, w: &mut [Self], work: &mut [Self],
194 lwork: i32, info: &mut i32) {
195 unsafe { $xsyev(jobz, uplo, n, a, lda, w, work, lwork, info) }
196 }
197
198
199 #[inline]
200 fn xsyev_work_size(jobz: u8, uplo: u8, n: i32, a: &mut [Self], lda: i32, info: &mut i32) -> i32 {
201 let mut work = [ Zero::zero() ];
202 let mut w = [ Zero::zero() ];
203 let lwork = -1 as i32;
204
205 unsafe { $xsyev(jobz, uplo, n, a, lda, &mut w, &mut work, lwork, info); }
206 ComplexHelper::real_part(work[0]) as i32
207 }
208 }
209 )
210);
211
212real_eigensystem_scalar_impl!(f32, lapack::ssyev);
213real_eigensystem_scalar_impl!(f64, lapack::dsyev);