1
+ //! Eigenvalue problem for general matricies
2
+
1
3
use crate :: { error:: * , layout:: MatrixLayout , * } ;
2
4
use cauchy:: * ;
3
5
use num_traits:: { ToPrimitive , Zero } ;
4
6
5
7
#[ cfg_attr( doc, katexit:: katexit) ]
6
8
/// Eigenvalue problem for general matrix
7
9
///
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:
12
38
///
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
+ /// $$
14
42
///
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`.
18
43
pub trait Eig_ : Scalar {
19
44
/// Compute right eigenvalue and eigenvectors $Ax = \lambda x$
20
45
///
@@ -41,7 +66,7 @@ macro_rules! impl_eig {
41
66
a: & mut [ Self ] ,
42
67
) -> Result <( Vec <Self :: Complex >, Vec <Self :: Complex >) > {
43
68
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) ?;
45
70
Ok ( ( eigs, vr. or( vl) . unwrap_or_default( ) ) )
46
71
}
47
72
}
@@ -53,13 +78,16 @@ impl_eig!(f64);
53
78
impl_eig ! ( f32 ) ;
54
79
55
80
/// Working memory for [Eig_]
56
- #[ derive ( Debug , Clone ) ]
81
+ #[ non_exhaustive ]
57
82
pub struct EigWork < T : Scalar > {
83
+ /// Problem size
58
84
pub n : i32 ,
85
+ /// Compute right eigenvectors or not
59
86
pub jobvr : JobEv ,
87
+ /// Compute left eigenvectors or not
60
88
pub jobvl : JobEv ,
61
89
62
- /// Eigenvalues used in complex routines
90
+ /// Eigenvalues
63
91
pub eigs : Vec < MaybeUninit < T :: Complex > > ,
64
92
/// Real part of eigenvalues used in real routines
65
93
pub eigs_re : Option < Vec < MaybeUninit < T :: Real > > > ,
@@ -68,9 +96,11 @@ pub struct EigWork<T: Scalar> {
68
96
69
97
/// Left eigenvectors
70
98
pub vc_l : Option < Vec < MaybeUninit < T :: Complex > > > ,
99
+ /// Left eigenvectors used in real routines
71
100
pub vr_l : Option < Vec < MaybeUninit < T :: Real > > > ,
72
101
/// Right eigenvectors
73
102
pub vc_r : Option < Vec < MaybeUninit < T :: Complex > > > ,
103
+ /// Right eigenvectors used in real routines
74
104
pub vr_r : Option < Vec < MaybeUninit < T :: Real > > > ,
75
105
76
106
/// Working memory
@@ -79,28 +109,55 @@ pub struct EigWork<T: Scalar> {
79
109
pub rwork : Option < Vec < MaybeUninit < T :: Real > > > ,
80
110
}
81
111
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]
82
134
#[ derive( Debug , Clone , PartialEq ) ]
83
- pub struct Eig < T : Scalar > {
135
+ pub struct EigOwned < T : Scalar > {
136
+ /// Eigenvalues
84
137
pub eigs : Vec < T :: Complex > ,
138
+ /// Right eigenvectors
85
139
pub vr : Option < Vec < T :: Complex > > ,
140
+ /// Left eigenvectors
86
141
pub vl : Option < Vec < T :: Complex > > ,
87
142
}
88
143
144
+ /// Reference result of eigenvalue problem by [EigWork::calc]
89
145
#[ derive( Debug , Clone , PartialEq ) ]
90
146
pub struct EigRef < ' work , T : Scalar > {
147
+ /// Eigenvalues
91
148
pub eigs : & ' work [ T :: Complex ] ,
149
+ /// Right eigenvectors
92
150
pub vr : Option < & ' work [ T :: Complex ] > ,
151
+ /// Left eigenvectors
93
152
pub vl : Option < & ' work [ T :: Complex ] > ,
94
153
}
95
154
155
+ /// Helper trait for implementing [EigWork] methods
96
156
pub trait EigWorkImpl : Sized {
97
157
type Elem : Scalar ;
98
- /// Create new working memory for eigenvalues compution.
99
158
fn new ( calc_v : bool , l : MatrixLayout ) -> Result < Self > ;
100
- /// Compute eigenvalues and vectors on this working memory.
101
159
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 > > ;
104
161
}
105
162
106
163
macro_rules! impl_eig_work_c {
@@ -210,7 +267,7 @@ macro_rules! impl_eig_work_c {
210
267
} )
211
268
}
212
269
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 >> {
214
271
let lwork = self . work. len( ) . to_i32( ) . unwrap( ) ;
215
272
let mut info = 0 ;
216
273
unsafe {
@@ -239,7 +296,7 @@ macro_rules! impl_eig_work_c {
239
296
value. im = -value. im;
240
297
}
241
298
}
242
- Ok ( Eig {
299
+ Ok ( EigOwned {
243
300
eigs: unsafe { self . eigs. assume_init( ) } ,
244
301
vl: self . vc_l. map( |v| unsafe { v. assume_init( ) } ) ,
245
302
vr: self . vc_r. map( |v| unsafe { v. assume_init( ) } ) ,
@@ -377,7 +434,7 @@ macro_rules! impl_eig_work_r {
377
434
} )
378
435
}
379
436
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 >> {
381
438
let lwork = self . work. len( ) . to_i32( ) . unwrap( ) ;
382
439
let mut info = 0 ;
383
440
unsafe {
@@ -421,7 +478,7 @@ macro_rules! impl_eig_work_r {
421
478
reconstruct_eigenvectors( false , eigs_im, v, self . vc_r. as_mut( ) . unwrap( ) ) ;
422
479
}
423
480
424
- Ok ( Eig {
481
+ Ok ( EigOwned {
425
482
eigs: unsafe { self . eigs. assume_init( ) } ,
426
483
vl: self . vc_l. map( |v| unsafe { v. assume_init( ) } ) ,
427
484
vr: self . vc_r. map( |v| unsafe { v. assume_init( ) } ) ,
0 commit comments