@@ -12,10 +12,16 @@ pub trait Eig_: Scalar {
1212 l : MatrixLayout ,
1313 a : & mut [ Self ] ,
1414 ) -> 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 > ) > ;
1521}
1622
1723macro_rules! impl_eig_complex {
18- ( $scalar: ty, $ev : path) => {
24+ ( $scalar: ty, $ev1 : path , $ev2 : path) => {
1925 impl Eig_ for $scalar {
2026 fn eig(
2127 calc_v: bool ,
@@ -51,7 +57,7 @@ macro_rules! impl_eig_complex {
5157 let mut info = 0 ;
5258 let mut work_size = [ Self :: zero( ) ] ;
5359 unsafe {
54- $ev (
60+ $ev1 (
5561 jobvl,
5662 jobvr,
5763 n,
@@ -74,7 +80,7 @@ macro_rules! impl_eig_complex {
7480 let lwork = work_size[ 0 ] . to_usize( ) . unwrap( ) ;
7581 let mut work = unsafe { vec_uninit( lwork) } ;
7682 unsafe {
77- $ev (
83+ $ev1 (
7884 jobvl,
7985 jobvr,
8086 n,
@@ -102,15 +108,115 @@ macro_rules! impl_eig_complex {
102108
103109 Ok ( ( eigs, vr. or( vl) . unwrap_or( Vec :: new( ) ) ) )
104110 }
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+ }
105211 }
106212 } ;
107213}
108214
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 ) ;
111217
112218macro_rules! impl_eig_real {
113- ( $scalar: ty, $ev : path) => {
219+ ( $scalar: ty, $ev1 : path , $ev2 : path) => {
114220 impl Eig_ for $scalar {
115221 fn eig(
116222 calc_v: bool ,
@@ -146,7 +252,7 @@ macro_rules! impl_eig_real {
146252 let mut info = 0 ;
147253 let mut work_size = [ 0.0 ] ;
148254 unsafe {
149- $ev (
255+ $ev1 (
150256 jobvl,
151257 jobvr,
152258 n,
@@ -169,7 +275,7 @@ macro_rules! impl_eig_real {
169275 let lwork = work_size[ 0 ] . to_usize( ) . unwrap( ) ;
170276 let mut work = unsafe { vec_uninit( lwork) } ;
171277 unsafe {
172- $ev (
278+ $ev1 (
173279 jobvl,
174280 jobvr,
175281 n,
@@ -250,9 +356,162 @@ macro_rules! impl_eig_real {
250356
251357 Ok ( ( eigs, eigvecs) )
252358 }
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+ // Reconstruct eigenvectors into complex-array
462+ // --------------------------------------------
463+ //
464+ // From LAPACK API https://software.intel.com/en-us/node/469230
465+ //
466+ // - If the j-th eigenvalue is real,
467+ // - v(j) = VR(:,j), the j-th column of VR.
468+ //
469+ // - If the j-th and (j+1)-st eigenvalues form a complex conjugate pair,
470+ // - v(j) = VR(:,j) + i*VR(:,j+1)
471+ // - v(j+1) = VR(:,j) - i*VR(:,j+1).
472+ //
473+ // ```
474+ // j -> <----pair----> <----pair---->
475+ // [ ... (real), (imag), (imag), (imag), (imag), ... ] : eigs
476+ // ^ ^ ^ ^ ^
477+ // false false true false true : is_conjugate_pair
478+ // ```
479+ let n = n as usize ;
480+ let v = vr. or( vl) . unwrap( ) ;
481+ let mut eigvecs = unsafe { vec_uninit( n * n) } ;
482+ let mut is_conjugate_pair = false ; // flag for check `j` is complex conjugate
483+ for j in 0 ..n {
484+ if alphai[ j] == 0.0 {
485+ // j-th eigenvalue is real
486+ for i in 0 ..n {
487+ eigvecs[ i + j * n] = Self :: complex( v[ i + j * n] , 0.0 ) ;
488+ }
489+ } else {
490+ // j-th eigenvalue is complex
491+ // complex conjugated pair can be `j-1` or `j+1`
492+ if is_conjugate_pair {
493+ let j_pair = j - 1 ;
494+ assert!( j_pair < n) ;
495+ for i in 0 ..n {
496+ eigvecs[ i + j * n] = Self :: complex( v[ i + j_pair * n] , v[ i + j * n] ) ;
497+ }
498+ } else {
499+ let j_pair = j + 1 ;
500+ assert!( j_pair < n) ;
501+ for i in 0 ..n {
502+ eigvecs[ i + j * n] =
503+ Self :: complex( v[ i + j * n] , -v[ i + j_pair * n] ) ;
504+ }
505+ }
506+ is_conjugate_pair = !is_conjugate_pair;
507+ }
508+ }
509+
510+ Ok ( ( eigs, eigvecs) )
511+ }
253512 }
254513 } ;
255514}
256515
257- impl_eig_real ! ( f64 , lapack:: dgeev) ;
258- impl_eig_real ! ( f32 , lapack:: sgeev) ;
516+ impl_eig_real ! ( f64 , lapack:: dgeev, lapack :: dggev ) ;
517+ impl_eig_real ! ( f32 , lapack:: sgeev, lapack :: sggev ) ;
0 commit comments