nalgebra_lapack/
eigen.rs

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/// Eigendecomposition of a real square matrix with real or complex eigenvalues.
16#[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    /// The real parts of eigenvalues of the decomposed matrix.
35    pub eigenvalues_re: OVector<T, D>,
36    /// The imaginary parts of the eigenvalues of the decomposed matrix.
37    pub eigenvalues_im: OVector<T, D>,
38    /// The (right) eigenvectors of the decomposed matrix.
39    pub eigenvectors: Option<OMatrix<T, D, D>>,
40    /// The left eigenvectors of the decomposed matrix.
41    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    /// Computes the eigenvalues and eigenvectors of the square matrix `m`.
57    ///
58    /// If `eigenvectors` is `false` then, the eigenvectors are not computed explicitly.
59    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        // TODO: avoid the initialization?
78        let mut wr = Matrix::zeros_generic(nrows, Const::<1>);
79        // TODO: Tap into the workspace.
80        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    /// Returns `true` if all the eigenvalues are real.
151    pub fn eigenvalues_are_real(&self) -> bool {
152        self.eigenvalues_im.iter().all(|e| e.is_zero())
153    }
154
155    /// The determinant of the decomposed matrix.
156    #[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    /// Returns a tuple of vectors. The elements of the tuple are the real parts of the eigenvalues, left eigenvectors and right eigenvectors respectively.
168    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                //skip next entry
216                c += 1;
217            }
218            c += 1;
219        }
220        (eigenvalues, left_eigenvectors, eigenvectors)
221    }
222
223    /// Returns a tuple of vectors. The elements of the tuple are the complex eigenvalues, complex left eigenvectors and complex right eigenvectors respectively.
224    /// The elements appear as conjugate pairs within each vector, with the positive of the pair always being first.
225    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                        //Complex conjugate pairs of eigenvalues appear consecutively with the eigenvalue having the positive imaginary part first.
262                        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                        //skip next entry
321                        c += 1;
322                    }
323                    c += 1;
324                }
325                (Some(eigenvalues), left_eigenvectors, eigenvectors)
326            }
327        }
328    }
329}
330
331/*
332 *
333 * Lapack functions dispatch.
334 *
335 */
336/// Trait implemented by scalar type for which Lapack function exist to compute the
337/// eigendecomposition.
338pub 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//// TODO: decomposition of complex matrix and matrices with complex eigenvalues.
403// eigensystem_complex_impl!(f32, lapack::cgeev);
404// eigensystem_complex_impl!(f64, lapack::zgeev);