1#[cfg(feature = "serde-serialize")]
2use serde::{Deserialize, Serialize};
3
4use num::Zero;
5use num_complex::Complex;
6
7use simba::scalar::RealField;
8
9use crate::ComplexHelper;
10use na::dimension::{Const, Dim};
11use na::{DefaultAllocator, Matrix, OMatrix, OVector, Scalar, allocator::Allocator};
12
13use lapack;
14
15#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
17#[cfg_attr(
18 feature = "serde-serialize",
19 serde(bound(serialize = "DefaultAllocator: Allocator<D, D> + Allocator<D>,
20 OVector<T, D>: Serialize,
21 OMatrix<T, D, D>: Serialize"))
22)]
23#[cfg_attr(
24 feature = "serde-serialize",
25 serde(bound(deserialize = "DefaultAllocator: Allocator<D, D> + Allocator<D>,
26 OVector<T, D>: Deserialize<'de>,
27 OMatrix<T, D, D>: Deserialize<'de>"))
28)]
29#[derive(Clone, Debug)]
30pub struct Eigen<T: Scalar, D: Dim>
31where
32 DefaultAllocator: Allocator<D> + Allocator<D, D>,
33{
34 pub eigenvalues_re: OVector<T, D>,
36 pub eigenvalues_im: OVector<T, D>,
38 pub eigenvectors: Option<OMatrix<T, D, D>>,
40 pub left_eigenvectors: Option<OMatrix<T, D, D>>,
42}
43
44impl<T: Scalar + Copy, D: Dim> Copy for Eigen<T, D>
45where
46 DefaultAllocator: Allocator<D> + Allocator<D, D>,
47 OVector<T, D>: Copy,
48 OMatrix<T, D, D>: Copy,
49{
50}
51
52impl<T: EigenScalar + RealField, D: Dim> Eigen<T, D>
53where
54 DefaultAllocator: Allocator<D, D> + Allocator<D>,
55{
56 pub fn new(
60 mut m: OMatrix<T, D, D>,
61 left_eigenvectors: bool,
62 eigenvectors: bool,
63 ) -> Option<Eigen<T, D>> {
64 assert!(
65 m.is_square(),
66 "Unable to compute the eigenvalue decomposition of a non-square matrix."
67 );
68
69 let ljob = if left_eigenvectors { b'V' } else { b'N' };
70 let rjob = if eigenvectors { b'V' } else { b'N' };
71
72 let (nrows, ncols) = m.shape_generic();
73 let n = nrows.value();
74
75 let lda = n as i32;
76
77 let mut wr = Matrix::zeros_generic(nrows, Const::<1>);
79 let mut wi = Matrix::zeros_generic(nrows, Const::<1>);
81
82 let mut info = 0;
83 let mut placeholder1 = [T::zero()];
84 let mut placeholder2 = [T::zero()];
85
86 let lwork = T::xgeev_work_size(
87 ljob,
88 rjob,
89 n as i32,
90 m.as_mut_slice(),
91 lda,
92 wr.as_mut_slice(),
93 wi.as_mut_slice(),
94 &mut placeholder1,
95 n as i32,
96 &mut placeholder2,
97 n as i32,
98 &mut info,
99 );
100
101 lapack_check!(info);
102
103 let mut work = vec![T::zero(); lwork as usize];
104 let mut vl = if left_eigenvectors {
105 Some(Matrix::zeros_generic(nrows, ncols))
106 } else {
107 None
108 };
109 let mut vr = if eigenvectors {
110 Some(Matrix::zeros_generic(nrows, ncols))
111 } else {
112 None
113 };
114
115 let vl_ref = vl
116 .as_mut()
117 .map(|m| m.as_mut_slice())
118 .unwrap_or(&mut placeholder1);
119 let vr_ref = vr
120 .as_mut()
121 .map(|m| m.as_mut_slice())
122 .unwrap_or(&mut placeholder2);
123
124 T::xgeev(
125 ljob,
126 rjob,
127 n as i32,
128 m.as_mut_slice(),
129 lda,
130 wr.as_mut_slice(),
131 wi.as_mut_slice(),
132 vl_ref,
133 if left_eigenvectors { n as i32 } else { 1 },
134 vr_ref,
135 if eigenvectors { n as i32 } else { 1 },
136 &mut work,
137 lwork,
138 &mut info,
139 );
140 lapack_check!(info);
141
142 Some(Self {
143 eigenvalues_re: wr,
144 eigenvalues_im: wi,
145 left_eigenvectors: vl,
146 eigenvectors: vr,
147 })
148 }
149
150 pub fn eigenvalues_are_real(&self) -> bool {
152 self.eigenvalues_im.iter().all(|e| e.is_zero())
153 }
154
155 #[inline]
157 #[must_use]
158 pub fn determinant(&self) -> Complex<T> {
159 let mut det: Complex<T> = na::one();
160 for (re, im) in self.eigenvalues_re.iter().zip(self.eigenvalues_im.iter()) {
161 det *= Complex::new(re.clone(), im.clone());
162 }
163
164 det
165 }
166
167 pub fn get_real_elements(
169 &self,
170 ) -> (
171 Vec<T>,
172 Option<Vec<OVector<T, D>>>,
173 Option<Vec<OVector<T, D>>>,
174 )
175 where
176 DefaultAllocator: Allocator<D>,
177 {
178 let (number_of_elements, _) = self.eigenvalues_re.shape_generic();
179 let number_of_elements_value = number_of_elements.value();
180 let mut eigenvalues = Vec::<T>::with_capacity(number_of_elements_value);
181 let mut eigenvectors = match self.eigenvectors.is_some() {
182 true => Some(Vec::<OVector<T, D>>::with_capacity(
183 number_of_elements_value,
184 )),
185 false => None,
186 };
187 let mut left_eigenvectors = match self.left_eigenvectors.is_some() {
188 true => Some(Vec::<OVector<T, D>>::with_capacity(
189 number_of_elements_value,
190 )),
191 false => None,
192 };
193
194 let mut c = 0;
195 while c < number_of_elements_value {
196 eigenvalues.push(self.eigenvalues_re[c].clone());
197
198 if eigenvectors.is_some() {
199 eigenvectors
200 .as_mut()
201 .unwrap()
202 .push(self.eigenvectors.as_ref().unwrap().column(c).into_owned());
203 }
204
205 if left_eigenvectors.is_some() {
206 left_eigenvectors.as_mut().unwrap().push(
207 self.left_eigenvectors
208 .as_ref()
209 .unwrap()
210 .column(c)
211 .into_owned(),
212 );
213 }
214 if self.eigenvalues_im[c] != T::zero() {
215 c += 1;
217 }
218 c += 1;
219 }
220 (eigenvalues, left_eigenvectors, eigenvectors)
221 }
222
223 pub fn get_complex_elements(
226 &self,
227 ) -> (
228 Option<Vec<Complex<T>>>,
229 Option<Vec<OVector<Complex<T>, D>>>,
230 Option<Vec<OVector<Complex<T>, D>>>,
231 )
232 where
233 DefaultAllocator: Allocator<D>,
234 {
235 match self.eigenvalues_are_real() {
236 true => (None, None, None),
237 false => {
238 let (number_of_elements, _) = self.eigenvalues_re.shape_generic();
239 let number_of_elements_value = number_of_elements.value();
240 let number_of_complex_entries = self
241 .eigenvalues_im
242 .iter()
243 .fold(0, |acc, e| if !e.is_zero() { acc + 1 } else { acc });
244 let mut eigenvalues = Vec::<Complex<T>>::with_capacity(number_of_complex_entries);
245 let mut eigenvectors = match self.eigenvectors.is_some() {
246 true => Some(Vec::<OVector<Complex<T>, D>>::with_capacity(
247 number_of_complex_entries,
248 )),
249 false => None,
250 };
251 let mut left_eigenvectors = match self.left_eigenvectors.is_some() {
252 true => Some(Vec::<OVector<Complex<T>, D>>::with_capacity(
253 number_of_complex_entries,
254 )),
255 false => None,
256 };
257
258 let mut c = 0;
259 while c < number_of_elements_value {
260 if self.eigenvalues_im[c] != T::zero() {
261 eigenvalues.push(Complex::<T>::new(
263 self.eigenvalues_re[c].clone(),
264 self.eigenvalues_im[c].clone(),
265 ));
266 eigenvalues.push(Complex::<T>::new(
267 self.eigenvalues_re[c + 1].clone(),
268 self.eigenvalues_im[c + 1].clone(),
269 ));
270
271 if eigenvectors.is_some() {
272 let mut vec = OVector::<Complex<T>, D>::zeros_generic(
273 number_of_elements,
274 Const::<1>,
275 );
276 let mut vec_conj = OVector::<Complex<T>, D>::zeros_generic(
277 number_of_elements,
278 Const::<1>,
279 );
280
281 for r in 0..number_of_elements_value {
282 vec[r] = Complex::<T>::new(
283 self.eigenvectors.as_ref().unwrap()[(r, c)].clone(),
284 self.eigenvectors.as_ref().unwrap()[(r, c + 1)].clone(),
285 );
286 vec_conj[r] = Complex::<T>::new(
287 self.eigenvectors.as_ref().unwrap()[(r, c)].clone(),
288 -self.eigenvectors.as_ref().unwrap()[(r, c + 1)].clone(),
289 );
290 }
291
292 eigenvectors.as_mut().unwrap().push(vec);
293 eigenvectors.as_mut().unwrap().push(vec_conj);
294 }
295
296 if left_eigenvectors.is_some() {
297 let mut vec = OVector::<Complex<T>, D>::zeros_generic(
298 number_of_elements,
299 Const::<1>,
300 );
301 let mut vec_conj = OVector::<Complex<T>, D>::zeros_generic(
302 number_of_elements,
303 Const::<1>,
304 );
305
306 for r in 0..number_of_elements_value {
307 vec[r] = Complex::<T>::new(
308 self.left_eigenvectors.as_ref().unwrap()[(r, c)].clone(),
309 self.left_eigenvectors.as_ref().unwrap()[(r, c + 1)].clone(),
310 );
311 vec_conj[r] = Complex::<T>::new(
312 self.left_eigenvectors.as_ref().unwrap()[(r, c)].clone(),
313 -self.left_eigenvectors.as_ref().unwrap()[(r, c + 1)].clone(),
314 );
315 }
316
317 left_eigenvectors.as_mut().unwrap().push(vec);
318 left_eigenvectors.as_mut().unwrap().push(vec_conj);
319 }
320 c += 1;
322 }
323 c += 1;
324 }
325 (Some(eigenvalues), left_eigenvectors, eigenvectors)
326 }
327 }
328 }
329}
330
331pub trait EigenScalar: Scalar {
339 #[allow(missing_docs)]
340 fn xgeev(
341 jobvl: u8,
342 jobvr: u8,
343 n: i32,
344 a: &mut [Self],
345 lda: i32,
346 wr: &mut [Self],
347 wi: &mut [Self],
348 vl: &mut [Self],
349 ldvl: i32,
350 vr: &mut [Self],
351 ldvr: i32,
352 work: &mut [Self],
353 lwork: i32,
354 info: &mut i32,
355 );
356 #[allow(missing_docs)]
357 fn xgeev_work_size(
358 jobvl: u8,
359 jobvr: u8,
360 n: i32,
361 a: &mut [Self],
362 lda: i32,
363 wr: &mut [Self],
364 wi: &mut [Self],
365 vl: &mut [Self],
366 ldvl: i32,
367 vr: &mut [Self],
368 ldvr: i32,
369 info: &mut i32,
370 ) -> i32;
371}
372
373macro_rules! real_eigensystem_scalar_impl (
374 ($N: ty, $xgeev: path) => (
375 impl EigenScalar for $N {
376 #[inline]
377 fn xgeev(jobvl: u8, jobvr: u8, n: i32, a: &mut [Self], lda: i32,
378 wr: &mut [Self], wi: &mut [Self],
379 vl: &mut [Self], ldvl: i32, vr: &mut [Self], ldvr: i32,
380 work: &mut [Self], lwork: i32, info: &mut i32) {
381 unsafe { $xgeev(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork, info) }
382 }
383
384
385 #[inline]
386 fn xgeev_work_size(jobvl: u8, jobvr: u8, n: i32, a: &mut [Self], lda: i32,
387 wr: &mut [Self], wi: &mut [Self], vl: &mut [Self], ldvl: i32,
388 vr: &mut [Self], ldvr: i32, info: &mut i32) -> i32 {
389 let mut work = [ Zero::zero() ];
390 let lwork = -1 as i32;
391
392 unsafe { $xgeev(jobvl, jobvr, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, &mut work, lwork, info) };
393 ComplexHelper::real_part(work[0]) as i32
394 }
395 }
396 )
397);
398
399real_eigensystem_scalar_impl!(f32, lapack::sgeev);
400real_eigensystem_scalar_impl!(f64, lapack::dgeev);
401
402