ndarray_linalg/eig.rs
1//! Eigenvalue decomposition for non-symmetric square matrices
2
3use crate::error::*;
4use crate::layout::*;
5use crate::types::*;
6pub use lax::GeneralizedEigenvalue;
7use ndarray::*;
8
9#[cfg_attr(doc, katexit::katexit)]
10/// Eigenvalue decomposition of general matrix reference
11pub trait Eig {
12 /// EigVec is the right eivenvector
13 type EigVal;
14 type EigVec;
15 /// Calculate eigenvalues with the right eigenvector
16 ///
17 /// $$ A u_i = \lambda_i u_i $$
18 ///
19 /// ```
20 /// use ndarray::*;
21 /// use ndarray_linalg::*;
22 ///
23 /// let a: Array2<f64> = array![
24 /// [-1.01, 0.86, -4.60, 3.31, -4.81],
25 /// [ 3.98, 0.53, -7.04, 5.29, 3.55],
26 /// [ 3.30, 8.26, -3.89, 8.20, -1.51],
27 /// [ 4.43, 4.96, -7.66, -7.33, 6.18],
28 /// [ 7.31, -6.43, -6.16, 2.47, 5.58],
29 /// ];
30 /// let (eigs, vecs) = a.eig().unwrap();
31 ///
32 /// let a = a.map(|v| v.as_c());
33 /// for (&e, vec) in eigs.iter().zip(vecs.axis_iter(Axis(1))) {
34 /// let ev = vec.map(|v| v * e);
35 /// let av = a.dot(&vec);
36 /// assert_close_l2!(&av, &ev, 1e-5);
37 /// }
38 /// ```
39 fn eig(&self) -> Result<(Self::EigVal, Self::EigVec)>;
40}
41
42impl<A, S> Eig for ArrayBase<S, Ix2>
43where
44 A: Scalar + Lapack,
45 S: Data<Elem = A>,
46{
47 type EigVal = Array1<A::Complex>;
48 type EigVec = Array2<A::Complex>;
49
50 fn eig(&self) -> Result<(Self::EigVal, Self::EigVec)> {
51 let mut a = self.to_owned();
52 let layout = a.square_layout()?;
53 let (s, t) = A::eig(true, layout, a.as_allocated_mut()?)?;
54 let n = layout.len() as usize;
55 Ok((
56 ArrayBase::from(s),
57 Array2::from_shape_vec((n, n).f(), t).unwrap(),
58 ))
59 }
60}
61
62/// Calculate eigenvalues without eigenvectors
63pub trait EigVals {
64 type EigVal;
65 fn eigvals(&self) -> Result<Self::EigVal>;
66}
67
68impl<A, S> EigVals for ArrayBase<S, Ix2>
69where
70 A: Scalar + Lapack,
71 S: Data<Elem = A>,
72{
73 type EigVal = Array1<A::Complex>;
74
75 fn eigvals(&self) -> Result<Self::EigVal> {
76 let mut a = self.to_owned();
77 let (s, _) = A::eig(false, a.square_layout()?, a.as_allocated_mut()?)?;
78 Ok(ArrayBase::from(s))
79 }
80}
81
82#[cfg_attr(doc, katexit::katexit)]
83/// Eigenvalue decomposition of general matrix reference
84pub trait EigGeneralized {
85 /// EigVec is the right eivenvector
86 type EigVal;
87 type EigVec;
88 type Real;
89 /// Calculate eigenvalues with the right eigenvector
90 ///
91 /// $$ A u_i = \lambda_i B u_i $$
92 ///
93 /// ```
94 /// use ndarray::*;
95 /// use ndarray_linalg::*;
96 ///
97 /// let a: Array2<f64> = array![
98 /// [-1.01, 0.86, -4.60, 3.31, -4.81],
99 /// [ 3.98, 0.53, -7.04, 5.29, 3.55],
100 /// [ 3.30, 8.26, -3.89, 8.20, -1.51],
101 /// [ 4.43, 4.96, -7.66, -7.33, 6.18],
102 /// [ 7.31, -6.43, -6.16, 2.47, 5.58],
103 /// ];
104 /// let b: Array2<f64> = array![
105 /// [ 1.23, -4.56, 7.89, 0.12, -3.45],
106 /// [ 6.78, -9.01, 2.34, -5.67, 8.90],
107 /// [-1.11, 3.33, -6.66, 9.99, -2.22],
108 /// [ 4.44, -7.77, 0.00, 1.11, 5.55],
109 /// [-8.88, 6.66, -3.33, 2.22, -9.99],
110 /// ];
111 /// let (geneigs, vecs) = (a.clone(), b.clone()).eig_generalized(None).unwrap();
112 ///
113 /// let a = a.map(|v| v.as_c());
114 /// let b = b.map(|v| v.as_c());
115 /// for (ge, vec) in geneigs.iter().zip(vecs.axis_iter(Axis(1))) {
116 /// if let GeneralizedEigenvalue::Finite(e, _) = ge {
117 /// let ebv = b.dot(&vec).map(|v| v * e);
118 /// let av = a.dot(&vec);
119 /// assert_close_l2!(&av, &ebv, 1e-5);
120 /// }
121 /// }
122 /// ```
123 ///
124 /// # Arguments
125 ///
126 /// * `thresh_opt` - An optional threshold for determining approximate zero |β| values when
127 /// computing the eigenvalues as α/β. If `None`, no approximate comparisons to zero will be
128 /// made.
129 fn eig_generalized(
130 &self,
131 thresh_opt: Option<Self::Real>,
132 ) -> Result<(Self::EigVal, Self::EigVec)>;
133}
134
135impl<A, S> EigGeneralized for (ArrayBase<S, Ix2>, ArrayBase<S, Ix2>)
136where
137 A: Scalar + Lapack,
138 S: Data<Elem = A>,
139{
140 type EigVal = Array1<GeneralizedEigenvalue<A::Complex>>;
141 type EigVec = Array2<A::Complex>;
142 type Real = A::Real;
143
144 fn eig_generalized(
145 &self,
146 thresh_opt: Option<Self::Real>,
147 ) -> Result<(Self::EigVal, Self::EigVec)> {
148 let (mut a, mut b) = (self.0.to_owned(), self.1.to_owned());
149 let layout = a.square_layout()?;
150 let (s, t) = A::eig_generalized(
151 true,
152 layout,
153 a.as_allocated_mut()?,
154 b.as_allocated_mut()?,
155 thresh_opt,
156 )?;
157 let n = layout.len() as usize;
158 Ok((
159 ArrayBase::from(s),
160 Array2::from_shape_vec((n, n).f(), t).unwrap(),
161 ))
162 }
163}