1+ //! Eigenvalue problem for general matricies
2+
13use crate :: { error:: * , layout:: MatrixLayout , * } ;
24use cauchy:: * ;
35use num_traits:: { ToPrimitive , Zero } ;
46
57#[ cfg_attr( doc, katexit:: katexit) ]
68/// Eigenvalue problem for general matrix
79///
8- /// LAPACK assumes a column-major input. A row-major input can
9- /// be interpreted as the transpose of a column-major input. So,
10- /// for row-major inputs, we we want to solve the following,
11- /// given the column-major input `A`:
10+ /// To manage memory more strictly, use [EigWork].
11+ ///
12+ /// Right and Left eigenvalue problem
13+ /// ----------------------------------
14+ /// LAPACK can solve both right eigenvalue problem
15+ /// $$
16+ /// AV_R = V_R \Lambda
17+ /// $$
18+ /// where $V_R = \left( v_R^1, \cdots, v_R^n \right)$ are right eigenvectors
19+ /// and left eigenvalue problem
20+ /// $$
21+ /// V_L^\dagger A = V_L^\dagger \Lambda
22+ /// $$
23+ /// where $V_L = \left( v_L^1, \cdots, v_L^n \right)$ are left eigenvectors
24+ /// and eigenvalues
25+ /// $$
26+ /// \Lambda = \begin{pmatrix}
27+ /// \lambda_1 & & 0 \\\\
28+ /// & \ddots & \\\\
29+ /// 0 & & \lambda_n
30+ /// \end{pmatrix}
31+ /// $$
32+ /// which satisfies $A v_R^i = \lambda_i v_R^i$ and
33+ /// $\left(v_L^i\right)^\dagger A = \lambda_i \left(v_L^i\right)^\dagger$
34+ /// for column-major matrices, although row-major matrices are not supported.
35+ /// Since a row-major matrix can be interpreted
36+ /// as a transpose of a column-major matrix,
37+ /// this transforms right eigenvalue problem to left one:
1238///
13- /// A^T V = V Λ ⟺ V^T A = Λ V^T ⟺ conj(V)^H A = Λ conj(V)^H
39+ /// $$
40+ /// A^\dagger V = V Λ ⟺ V^\dagger A = Λ V^\dagger
41+ /// $$
1442///
15- /// So, in this case, the right eigenvectors are the conjugates
16- /// of the left eigenvectors computed with `A`, and the
17- /// eigenvalues are the eigenvalues computed with `A`.
1843pub trait Eig_ : Scalar {
1944 /// Compute right eigenvalue and eigenvectors $Ax = \lambda x$
2045 ///
@@ -41,7 +66,7 @@ macro_rules! impl_eig {
4166 a: & mut [ Self ] ,
4267 ) -> Result <( Vec <Self :: Complex >, Vec <Self :: Complex >) > {
4368 let work = EigWork :: <$s>:: new( calc_v, l) ?;
44- let Eig { eigs, vr, vl } = work. eval( a) ?;
69+ let EigOwned { eigs, vr, vl } = work. eval( a) ?;
4570 Ok ( ( eigs, vr. or( vl) . unwrap_or_default( ) ) )
4671 }
4772 }
@@ -53,13 +78,16 @@ impl_eig!(f64);
5378impl_eig ! ( f32 ) ;
5479
5580/// Working memory for [Eig_]
56- #[ derive ( Debug , Clone ) ]
81+ #[ non_exhaustive ]
5782pub struct EigWork < T : Scalar > {
83+ /// Problem size
5884 pub n : i32 ,
85+ /// Compute right eigenvectors or not
5986 pub jobvr : JobEv ,
87+ /// Compute left eigenvectors or not
6088 pub jobvl : JobEv ,
6189
62- /// Eigenvalues used in complex routines
90+ /// Eigenvalues
6391 pub eigs : Vec < MaybeUninit < T :: Complex > > ,
6492 /// Real part of eigenvalues used in real routines
6593 pub eigs_re : Option < Vec < MaybeUninit < T :: Real > > > ,
@@ -68,9 +96,11 @@ pub struct EigWork<T: Scalar> {
6896
6997 /// Left eigenvectors
7098 pub vc_l : Option < Vec < MaybeUninit < T :: Complex > > > ,
99+ /// Left eigenvectors used in real routines
71100 pub vr_l : Option < Vec < MaybeUninit < T :: Real > > > ,
72101 /// Right eigenvectors
73102 pub vc_r : Option < Vec < MaybeUninit < T :: Complex > > > ,
103+ /// Right eigenvectors used in real routines
74104 pub vr_r : Option < Vec < MaybeUninit < T :: Real > > > ,
75105
76106 /// Working memory
@@ -79,28 +109,55 @@ pub struct EigWork<T: Scalar> {
79109 pub rwork : Option < Vec < MaybeUninit < T :: Real > > > ,
80110}
81111
112+ impl < T > EigWork < T >
113+ where
114+ T : Scalar ,
115+ EigWork < T > : EigWorkImpl < Elem = T > ,
116+ {
117+ /// Create new working memory for eigenvalues compution.
118+ pub fn new ( calc_v : bool , l : MatrixLayout ) -> Result < Self > {
119+ EigWorkImpl :: new ( calc_v, l)
120+ }
121+
122+ /// Compute eigenvalues and vectors on this working memory.
123+ pub fn calc ( & mut self , a : & mut [ T ] ) -> Result < EigRef < T > > {
124+ EigWorkImpl :: calc ( self , a)
125+ }
126+
127+ /// Compute eigenvalues and vectors by consuming this working memory.
128+ pub fn eval ( self , a : & mut [ T ] ) -> Result < EigOwned < T > > {
129+ EigWorkImpl :: eval ( self , a)
130+ }
131+ }
132+
133+ /// Owned result of eigenvalue problem by [EigWork::eval]
82134#[ derive( Debug , Clone , PartialEq ) ]
83- pub struct Eig < T : Scalar > {
135+ pub struct EigOwned < T : Scalar > {
136+ /// Eigenvalues
84137 pub eigs : Vec < T :: Complex > ,
138+ /// Right eigenvectors
85139 pub vr : Option < Vec < T :: Complex > > ,
140+ /// Left eigenvectors
86141 pub vl : Option < Vec < T :: Complex > > ,
87142}
88143
144+ /// Reference result of eigenvalue problem by [EigWork::calc]
89145#[ derive( Debug , Clone , PartialEq ) ]
90146pub struct EigRef < ' work , T : Scalar > {
147+ /// Eigenvalues
91148 pub eigs : & ' work [ T :: Complex ] ,
149+ /// Right eigenvectors
92150 pub vr : Option < & ' work [ T :: Complex ] > ,
151+ /// Left eigenvectors
93152 pub vl : Option < & ' work [ T :: Complex ] > ,
94153}
95154
155+ /// Helper trait for implementing [EigWork] methods
96156pub trait EigWorkImpl : Sized {
97157 type Elem : Scalar ;
98- /// Create new working memory for eigenvalues compution.
99158 fn new ( calc_v : bool , l : MatrixLayout ) -> Result < Self > ;
100- /// Compute eigenvalues and vectors on this working memory.
101159 fn calc < ' work > ( & ' work mut self , a : & mut [ Self :: Elem ] ) -> Result < EigRef < ' work , Self :: Elem > > ;
102- /// Compute eigenvalues and vectors by consuming this working memory.
103- fn eval ( self , a : & mut [ Self :: Elem ] ) -> Result < Eig < Self :: Elem > > ;
160+ fn eval ( self , a : & mut [ Self :: Elem ] ) -> Result < EigOwned < Self :: Elem > > ;
104161}
105162
106163macro_rules! impl_eig_work_c {
@@ -210,7 +267,7 @@ macro_rules! impl_eig_work_c {
210267 } )
211268 }
212269
213- fn eval( mut self , a: & mut [ Self :: Elem ] ) -> Result <Eig <Self :: Elem >> {
270+ fn eval( mut self , a: & mut [ Self :: Elem ] ) -> Result <EigOwned <Self :: Elem >> {
214271 let lwork = self . work. len( ) . to_i32( ) . unwrap( ) ;
215272 let mut info = 0 ;
216273 unsafe {
@@ -239,7 +296,7 @@ macro_rules! impl_eig_work_c {
239296 value. im = -value. im;
240297 }
241298 }
242- Ok ( Eig {
299+ Ok ( EigOwned {
243300 eigs: unsafe { self . eigs. assume_init( ) } ,
244301 vl: self . vc_l. map( |v| unsafe { v. assume_init( ) } ) ,
245302 vr: self . vc_r. map( |v| unsafe { v. assume_init( ) } ) ,
@@ -377,7 +434,7 @@ macro_rules! impl_eig_work_r {
377434 } )
378435 }
379436
380- fn eval( mut self , a: & mut [ Self :: Elem ] ) -> Result <Eig <Self :: Elem >> {
437+ fn eval( mut self , a: & mut [ Self :: Elem ] ) -> Result <EigOwned <Self :: Elem >> {
381438 let lwork = self . work. len( ) . to_i32( ) . unwrap( ) ;
382439 let mut info = 0 ;
383440 unsafe {
@@ -421,7 +478,7 @@ macro_rules! impl_eig_work_r {
421478 reconstruct_eigenvectors( false , eigs_im, v, self . vc_r. as_mut( ) . unwrap( ) ) ;
422479 }
423480
424- Ok ( Eig {
481+ Ok ( EigOwned {
425482 eigs: unsafe { self . eigs. assume_init( ) } ,
426483 vl: self . vc_l. map( |v| unsafe { v. assume_init( ) } ) ,
427484 vr: self . vc_r. map( |v| unsafe { v. assume_init( ) } ) ,
0 commit comments