@@ -12,10 +12,16 @@ pub trait Eig_: Scalar {
12
12
l : MatrixLayout ,
13
13
a : & mut [ Self ] ,
14
14
) -> Result < ( Vec < Self :: Complex > , Vec < Self :: Complex > ) > ;
15
+ fn eigg (
16
+ calc_v : bool ,
17
+ l : MatrixLayout ,
18
+ a : & mut [ Self ] ,
19
+ b : & mut [ Self ] ,
20
+ ) -> Result < ( Vec < Self :: Complex > , Vec < Self :: Complex > ) > ;
15
21
}
16
22
17
23
macro_rules! impl_eig_complex {
18
- ( $scalar: ty, $ev : path) => {
24
+ ( $scalar: ty, $ev1 : path , $ev2 : path) => {
19
25
impl Eig_ for $scalar {
20
26
fn eig(
21
27
calc_v: bool ,
@@ -51,7 +57,7 @@ macro_rules! impl_eig_complex {
51
57
let mut info = 0 ;
52
58
let mut work_size = [ Self :: zero( ) ] ;
53
59
unsafe {
54
- $ev (
60
+ $ev1 (
55
61
jobvl,
56
62
jobvr,
57
63
n,
@@ -74,7 +80,7 @@ macro_rules! impl_eig_complex {
74
80
let lwork = work_size[ 0 ] . to_usize( ) . unwrap( ) ;
75
81
let mut work = unsafe { vec_uninit( lwork) } ;
76
82
unsafe {
77
- $ev (
83
+ $ev1 (
78
84
jobvl,
79
85
jobvr,
80
86
n,
@@ -102,15 +108,115 @@ macro_rules! impl_eig_complex {
102
108
103
109
Ok ( ( eigs, vr. or( vl) . unwrap_or( Vec :: new( ) ) ) )
104
110
}
111
+
112
+ fn eigg(
113
+ calc_v: bool ,
114
+ l: MatrixLayout ,
115
+ mut a: & mut [ Self ] ,
116
+ mut b: & mut [ Self ] ,
117
+ ) -> Result <( Vec <Self :: Complex >, Vec <Self :: Complex >) > {
118
+ let ( n, _) = l. size( ) ;
119
+ // Because LAPACK assumes F-continious array, C-continious array should be taken Hermitian conjugate.
120
+ // However, we utilize a fact that left eigenvector of A^H corresponds to the right eigenvector of A
121
+ let ( jobvl, jobvr) = if calc_v {
122
+ match l {
123
+ MatrixLayout :: C { .. } => ( b'V' , b'N' ) ,
124
+ MatrixLayout :: F { .. } => ( b'N' , b'V' ) ,
125
+ }
126
+ } else {
127
+ ( b'N' , b'N' )
128
+ } ;
129
+ let mut alpha = unsafe { vec_uninit( n as usize ) } ;
130
+ let mut beta = unsafe { vec_uninit( n as usize ) } ;
131
+ let mut rwork = unsafe { vec_uninit( 8 * n as usize ) } ;
132
+
133
+ let mut vl = if jobvl == b'V' {
134
+ Some ( unsafe { vec_uninit( ( n * n) as usize ) } )
135
+ } else {
136
+ None
137
+ } ;
138
+ let mut vr = if jobvr == b'V' {
139
+ Some ( unsafe { vec_uninit( ( n * n) as usize ) } )
140
+ } else {
141
+ None
142
+ } ;
143
+
144
+ // calc work size
145
+ let mut info = 0 ;
146
+ let mut work_size = [ Self :: zero( ) ] ;
147
+ unsafe {
148
+ $ev2(
149
+ jobvl,
150
+ jobvr,
151
+ n,
152
+ & mut a,
153
+ n,
154
+ & mut b,
155
+ n,
156
+ & mut alpha,
157
+ & mut beta,
158
+ & mut vl. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
159
+ n,
160
+ & mut vr. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
161
+ n,
162
+ & mut work_size,
163
+ -1 ,
164
+ & mut rwork,
165
+ & mut info,
166
+ )
167
+ } ;
168
+ info. as_lapack_result( ) ?;
169
+
170
+ // actal ev
171
+ let lwork = work_size[ 0 ] . to_usize( ) . unwrap( ) ;
172
+ let mut work = unsafe { vec_uninit( lwork) } ;
173
+ unsafe {
174
+ $ev2(
175
+ jobvl,
176
+ jobvr,
177
+ n,
178
+ & mut a,
179
+ n,
180
+ & mut b,
181
+ n,
182
+ & mut alpha,
183
+ & mut beta,
184
+ & mut vl. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
185
+ n,
186
+ & mut vr. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
187
+ n,
188
+ & mut work,
189
+ lwork as i32 ,
190
+ & mut rwork,
191
+ & mut info,
192
+ )
193
+ } ;
194
+ info. as_lapack_result( ) ?;
195
+
196
+ // Hermite conjugate
197
+ if jobvl == b'V' {
198
+ for c in vl. as_mut( ) . unwrap( ) . iter_mut( ) {
199
+ c. im = -c. im
200
+ }
201
+ }
202
+ // reconstruct eigenvalues
203
+ let eigs: Vec <Self :: Complex > = alpha
204
+ . iter( )
205
+ . zip( beta. iter( ) )
206
+ . map( |( & a, & b) | a/b)
207
+ . collect( ) ;
208
+
209
+ Ok ( ( eigs, vr. or( vl) . unwrap_or( Vec :: new( ) ) ) )
210
+ }
105
211
}
106
212
} ;
107
213
}
108
214
109
- impl_eig_complex ! ( c64, lapack:: zgeev) ;
110
- impl_eig_complex ! ( c32, lapack:: cgeev) ;
215
+ impl_eig_complex ! ( c64, lapack:: zgeev, lapack :: zggev ) ;
216
+ impl_eig_complex ! ( c32, lapack:: cgeev, lapack :: cggev ) ;
111
217
112
218
macro_rules! impl_eig_real {
113
- ( $scalar: ty, $ev : path) => {
219
+ ( $scalar: ty, $ev1 : path , $ev2 : path) => {
114
220
impl Eig_ for $scalar {
115
221
fn eig(
116
222
calc_v: bool ,
@@ -146,7 +252,7 @@ macro_rules! impl_eig_real {
146
252
let mut info = 0 ;
147
253
let mut work_size = [ 0.0 ] ;
148
254
unsafe {
149
- $ev (
255
+ $ev1 (
150
256
jobvl,
151
257
jobvr,
152
258
n,
@@ -169,7 +275,7 @@ macro_rules! impl_eig_real {
169
275
let lwork = work_size[ 0 ] . to_usize( ) . unwrap( ) ;
170
276
let mut work = unsafe { vec_uninit( lwork) } ;
171
277
unsafe {
172
- $ev (
278
+ $ev1 (
173
279
jobvl,
174
280
jobvr,
175
281
n,
@@ -250,9 +356,163 @@ macro_rules! impl_eig_real {
250
356
251
357
Ok ( ( eigs, eigvecs) )
252
358
}
359
+
360
+ fn eigg(
361
+ calc_v: bool ,
362
+ l: MatrixLayout ,
363
+ mut a: & mut [ Self ] ,
364
+ mut b: & mut [ Self ] ,
365
+ ) -> Result <( Vec <Self :: Complex >, Vec <Self :: Complex >) > {
366
+ let ( n, _) = l. size( ) ;
367
+ // Because LAPACK assumes F-continious array, C-continious array should be taken Hermitian conjugate.
368
+ // However, we utilize a fact that left eigenvector of A^H corresponds to the right eigenvector of A
369
+ let ( jobvl, jobvr) = if calc_v {
370
+ match l {
371
+ MatrixLayout :: C { .. } => ( b'V' , b'N' ) ,
372
+ MatrixLayout :: F { .. } => ( b'N' , b'V' ) ,
373
+ }
374
+ } else {
375
+ ( b'N' , b'N' )
376
+ } ;
377
+ let mut alphar = unsafe { vec_uninit( n as usize ) } ;
378
+ let mut alphai = unsafe { vec_uninit( n as usize ) } ;
379
+ let mut beta = unsafe { vec_uninit( n as usize ) } ;
380
+
381
+ let mut vl = if jobvl == b'V' {
382
+ Some ( unsafe { vec_uninit( ( n * n) as usize ) } )
383
+ } else {
384
+ None
385
+ } ;
386
+ let mut vr = if jobvr == b'V' {
387
+ Some ( unsafe { vec_uninit( ( n * n) as usize ) } )
388
+ } else {
389
+ None
390
+ } ;
391
+
392
+ // calc work size
393
+ let mut info = 0 ;
394
+ let mut work_size = [ 0.0 ] ;
395
+ unsafe {
396
+ $ev2(
397
+ jobvl,
398
+ jobvr,
399
+ n,
400
+ & mut a,
401
+ n,
402
+ & mut b,
403
+ n,
404
+ & mut alphar,
405
+ & mut alphai,
406
+ & mut beta,
407
+ vl. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
408
+ n,
409
+ vr. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
410
+ n,
411
+ & mut work_size,
412
+ -1 ,
413
+ & mut info,
414
+ )
415
+ } ;
416
+ info. as_lapack_result( ) ?;
417
+
418
+ // actual ev
419
+ let lwork = work_size[ 0 ] . to_usize( ) . unwrap( ) ;
420
+ let mut work = unsafe { vec_uninit( lwork) } ;
421
+ unsafe {
422
+ $ev2(
423
+ jobvl,
424
+ jobvr,
425
+ n,
426
+ & mut a,
427
+ n,
428
+ & mut b,
429
+ n,
430
+ & mut alphar,
431
+ & mut alphai,
432
+ & mut beta,
433
+ vl. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
434
+ n,
435
+ vr. as_mut( ) . map( |v| v. as_mut_slice( ) ) . unwrap_or( & mut [ ] ) ,
436
+ n,
437
+ & mut work,
438
+ lwork as i32 ,
439
+ & mut info,
440
+ )
441
+ } ;
442
+ info. as_lapack_result( ) ?;
443
+
444
+ // reconstruct eigenvalues
445
+ let alpha: Vec <Self :: Complex > = alphar
446
+ . iter( )
447
+ . zip( alphai. iter( ) )
448
+ . map( |( & re, & im) | Self :: complex( re, im) )
449
+ . collect( ) ;
450
+
451
+ let eigs: Vec <Self :: Complex > = alpha
452
+ . iter( )
453
+ . zip( beta. iter( ) )
454
+ . map( |( & a, & b) | a/b)
455
+ . collect( ) ;
456
+
457
+ if !calc_v {
458
+ return Ok ( ( eigs, Vec :: new( ) ) ) ;
459
+ }
460
+
461
+
462
+ // Reconstruct eigenvectors into complex-array
463
+ // --------------------------------------------
464
+ //
465
+ // From LAPACK API https://software.intel.com/en-us/node/469230
466
+ //
467
+ // - If the j-th eigenvalue is real,
468
+ // - v(j) = VR(:,j), the j-th column of VR.
469
+ //
470
+ // - If the j-th and (j+1)-st eigenvalues form a complex conjugate pair,
471
+ // - v(j) = VR(:,j) + i*VR(:,j+1)
472
+ // - v(j+1) = VR(:,j) - i*VR(:,j+1).
473
+ //
474
+ // ```
475
+ // j -> <----pair----> <----pair---->
476
+ // [ ... (real), (imag), (imag), (imag), (imag), ... ] : eigs
477
+ // ^ ^ ^ ^ ^
478
+ // false false true false true : is_conjugate_pair
479
+ // ```
480
+ let n = n as usize ;
481
+ let v = vr. or( vl) . unwrap( ) ;
482
+ let mut eigvecs = unsafe { vec_uninit( n * n) } ;
483
+ let mut is_conjugate_pair = false ; // flag for check `j` is complex conjugate
484
+ for j in 0 ..n {
485
+ if alphai[ j] == 0.0 {
486
+ // j-th eigenvalue is real
487
+ for i in 0 ..n {
488
+ eigvecs[ i + j * n] = Self :: complex( v[ i + j * n] , 0.0 ) ;
489
+ }
490
+ } else {
491
+ // j-th eigenvalue is complex
492
+ // complex conjugated pair can be `j-1` or `j+1`
493
+ if is_conjugate_pair {
494
+ let j_pair = j - 1 ;
495
+ assert!( j_pair < n) ;
496
+ for i in 0 ..n {
497
+ eigvecs[ i + j * n] = Self :: complex( v[ i + j_pair * n] , v[ i + j * n] ) ;
498
+ }
499
+ } else {
500
+ let j_pair = j + 1 ;
501
+ assert!( j_pair < n) ;
502
+ for i in 0 ..n {
503
+ eigvecs[ i + j * n] =
504
+ Self :: complex( v[ i + j * n] , -v[ i + j_pair * n] ) ;
505
+ }
506
+ }
507
+ is_conjugate_pair = !is_conjugate_pair;
508
+ }
509
+ }
510
+
511
+ Ok ( ( eigs, eigvecs) )
512
+ }
253
513
}
254
514
} ;
255
515
}
256
516
257
- impl_eig_real ! ( f64 , lapack:: dgeev) ;
258
- impl_eig_real ! ( f32 , lapack:: sgeev) ;
517
+ impl_eig_real ! ( f64 , lapack:: dgeev, lapack :: dggev ) ;
518
+ impl_eig_real ! ( f32 , lapack:: sgeev, lapack :: sggev ) ;
0 commit comments