2017-08-14 01:53:04 +08:00
#[ cfg(feature = " serde-serialize " ) ]
2018-10-22 13:00:10 +08:00
use serde ::{ Deserialize , Serialize } ;
2017-08-14 01:53:04 +08:00
2017-08-03 01:38:28 +08:00
use num ::Zero ;
2017-08-07 01:41:33 +08:00
use num_complex ::Complex ;
2017-08-03 01:38:28 +08:00
2020-03-21 19:16:46 +08:00
use simba ::scalar ::RealField ;
2017-08-03 01:38:28 +08:00
2020-03-21 19:16:46 +08:00
use crate ::ComplexHelper ;
2022-10-15 22:49:13 +08:00
use na ::dimension ::{ Const , Dim , DimName } ;
use na ::{ DefaultAllocator , Matrix , OMatrix , OVector , Scalar , allocator ::Allocator } ;
2017-08-03 01:38:28 +08:00
2018-05-25 05:51:57 +08:00
use lapack ;
2017-08-03 01:38:28 +08:00
2022-10-09 22:26:26 +08:00
/// Eigendecomposition of a real square matrix with real or complex eigenvalues.
2017-08-14 01:53:04 +08:00
#[ cfg_attr(feature = " serde-serialize " , derive(Serialize, Deserialize)) ]
2018-05-25 05:51:57 +08:00
#[ cfg_attr(
feature = " serde-serialize " ,
2020-03-21 19:16:46 +08:00
serde (
2021-04-11 17:00:38 +08:00
bound ( serialize = " DefaultAllocator: Allocator<T, D, D> + Allocator<T, D>,
OVector < T , D > : Serialize ,
OMatrix < T , D , D > : Serialize " )
2020-03-21 19:16:46 +08:00
)
2018-05-25 05:51:57 +08:00
) ]
#[ cfg_attr(
feature = " serde-serialize " ,
2020-03-21 19:16:46 +08:00
serde (
2021-04-11 17:00:38 +08:00
bound ( deserialize = " DefaultAllocator: Allocator<T, D, D> + Allocator<T, D>,
OVector < T , D > : Serialize ,
OMatrix < T , D , D > : Deserialize < ' de > " )
2020-03-21 19:16:46 +08:00
)
2018-05-25 05:51:57 +08:00
) ]
2021-08-03 00:41:46 +08:00
#[ derive(Clone, Debug) ]
pub struct Eigen < T : Scalar , D : Dim >
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator : Allocator < T , D > + Allocator < T , D , D > ,
2018-02-02 19:26:35 +08:00
{
2022-10-09 22:26:26 +08:00
/// The real parts of eigenvalues of the decomposed matrix.
pub eigenvalues_re : OVector < T , D > ,
/// The imaginary parts of the eigenvalues of the decomposed matrix.
pub eigenvalues_im : OVector < T , D > ,
2017-08-14 01:52:58 +08:00
/// The (right) eigenvectors of the decomposed matrix.
2021-04-11 17:00:38 +08:00
pub eigenvectors : Option < OMatrix < T , D , D > > ,
2017-08-14 01:52:58 +08:00
/// The left eigenvectors of the decomposed matrix.
2021-04-11 17:00:38 +08:00
pub left_eigenvectors : Option < OMatrix < T , D , D > > ,
2017-08-03 01:38:28 +08:00
}
2021-08-03 00:41:46 +08:00
impl < T : Scalar + Copy , D : Dim > Copy for Eigen < T , D >
2018-02-02 19:26:35 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator : Allocator < T , D > + Allocator < T , D , D > ,
OVector < T , D > : Copy ,
OMatrix < T , D , D > : Copy ,
2020-03-21 19:16:46 +08:00
{
}
2017-08-03 01:38:28 +08:00
2021-04-11 17:00:38 +08:00
impl < T : EigenScalar + RealField , D : Dim > Eigen < T , D >
2020-04-06 00:49:48 +08:00
where
2021-04-11 17:00:38 +08:00
DefaultAllocator : Allocator < T , D , D > + Allocator < T , D > ,
2018-02-02 19:26:35 +08:00
{
2017-08-03 01:38:28 +08:00
/// Computes the eigenvalues and eigenvectors of the square matrix `m`.
///
/// If `eigenvectors` is `false` then, the eigenvectors are not computed explicitly.
2018-02-02 19:26:35 +08:00
pub fn new (
2021-04-11 17:00:38 +08:00
mut m : OMatrix < T , D , D > ,
2018-02-02 19:26:35 +08:00
left_eigenvectors : bool ,
eigenvectors : bool ,
2021-04-11 17:00:38 +08:00
) -> Option < Eigen < T , D > > {
2018-02-02 19:26:35 +08:00
assert! (
m . is_square ( ) ,
" Unable to compute the eigenvalue decomposition of a non-square matrix. "
) ;
2022-05-03 03:55:51 +08:00
let ljob = if left_eigenvectors { b 'V' } else { b 'N' } ;
let rjob = if eigenvectors { b 'V' } else { b 'N' } ;
2017-08-03 01:38:28 +08:00
2021-08-03 00:41:46 +08:00
let ( nrows , ncols ) = m . shape_generic ( ) ;
2017-08-03 01:38:28 +08:00
let n = nrows . value ( ) ;
let lda = n as i32 ;
2021-08-03 23:39:45 +08:00
// TODO: avoid the initialization?
let mut wr = Matrix ::zeros_generic ( nrows , Const ::< 1 > ) ;
2020-11-15 23:57:49 +08:00
// TODO: Tap into the workspace.
2021-08-03 23:39:45 +08:00
let mut wi = Matrix ::zeros_generic ( nrows , Const ::< 1 > ) ;
2017-08-03 01:38:28 +08:00
let mut info = 0 ;
2021-04-11 17:00:38 +08:00
let mut placeholder1 = [ T ::zero ( ) ] ;
let mut placeholder2 = [ T ::zero ( ) ] ;
2018-02-02 19:26:35 +08:00
2021-04-11 17:00:38 +08:00
let lwork = T ::xgeev_work_size (
2018-02-02 19:26:35 +08:00
ljob ,
rjob ,
n as i32 ,
m . as_mut_slice ( ) ,
lda ,
wr . as_mut_slice ( ) ,
wi . as_mut_slice ( ) ,
& mut placeholder1 ,
n as i32 ,
& mut placeholder2 ,
n as i32 ,
& mut info ,
) ;
2017-08-03 01:38:28 +08:00
lapack_check! ( info ) ;
2021-08-03 23:39:45 +08:00
let mut work = vec! [ T ::zero ( ) ; lwork as usize ] ;
2022-10-09 22:26:26 +08:00
let mut vl = if left_eigenvectors {
Some ( Matrix ::zeros_generic ( nrows , ncols ) )
} else {
None
} ;
let mut vr = if eigenvectors {
Some ( Matrix ::zeros_generic ( nrows , ncols ) )
} else {
None
} ;
let vl_ref = vl
. as_mut ( )
. map ( | m | m . as_mut_slice ( ) )
. unwrap_or ( & mut placeholder1 ) ;
let vr_ref = vr
. as_mut ( )
. map ( | m | m . as_mut_slice ( ) )
. unwrap_or ( & mut placeholder2 ) ;
T ::xgeev (
2022-05-03 03:55:51 +08:00
ljob ,
rjob ,
2018-02-02 19:26:35 +08:00
n as i32 ,
m . as_mut_slice ( ) ,
lda ,
wr . as_mut_slice ( ) ,
wi . as_mut_slice ( ) ,
2022-10-09 22:26:26 +08:00
vl_ref ,
if left_eigenvectors { n as i32 } else { 1 } ,
vr_ref ,
if eigenvectors { n as i32 } else { 1 } ,
& mut work ,
lwork ,
2018-02-02 19:26:35 +08:00
& mut info ,
) ;
2022-10-09 22:26:26 +08:00
lapack_check! ( info ) ;
2017-08-07 01:41:33 +08:00
2022-10-09 22:26:26 +08:00
Some ( Self {
eigenvalues_re : wr ,
eigenvalues_im : wi ,
left_eigenvectors : vl ,
2022-10-15 22:49:13 +08:00
eigenvectors : vr
2022-10-09 22:26:26 +08:00
} )
}
2022-05-03 03:55:51 +08:00
2022-10-09 22:26:26 +08:00
/// Returns `true` if all the eigenvalues are real.
pub fn eigenvalues_are_real ( & self ) -> bool {
self . eigenvalues_im . iter ( ) . all ( | e | e . is_zero ( ) )
2017-08-07 01:41:33 +08:00
}
2017-08-03 01:38:28 +08:00
/// The determinant of the decomposed matrix.
#[ inline ]
2021-06-07 22:34:03 +08:00
#[ must_use ]
2022-10-09 22:26:26 +08:00
pub fn determinant ( & self ) -> Complex < T > {
let mut det : Complex < T > = na ::one ( ) ;
for ( re , im ) in self . eigenvalues_re . iter ( ) . zip ( self . eigenvalues_im . iter ( ) ) {
det * = Complex ::new ( re . clone ( ) , im . clone ( ) ) ;
2017-08-03 01:38:28 +08:00
}
det
}
2022-10-15 22:49:13 +08:00
/// Returns a tuple of vectors. The elements of the tuple are the complex eigenvalues, complex left eigenvectors and complex right eigenvectors respectively.
/// The elements appear as conjugate pairs within each vector, with the positive of the pair always being first.
pub fn get_complex_elements ( & self ) -> ( Option < Vec < Complex < T > > > , Option < Vec < OVector < Complex < T > , D > > > , Option < Vec < OVector < Complex < T > , D > > > ) where DefaultAllocator : Allocator < Complex < T > , D > {
match ! self . eigenvalues_are_real ( ) {
true = > ( None , None , None ) ,
false = > {
let number_of_elements = self . eigenvalues_re . nrows ( ) ;
let number_of_complex_entries = self . eigenvalues_im . iter ( ) . fold ( 0 , | acc , e | if ! e . is_zero ( ) { acc + 1 } else { acc } ) ;
let mut eigenvalues = Vec ::< Complex < T > > ::with_capacity ( 2 * number_of_complex_entries ) ;
let mut eigenvectors = match self . eigenvectors . is_some ( ) {
true = > Some ( Vec ::< OVector < Complex < T > , D > > ::with_capacity ( 2 * number_of_complex_entries ) ) ,
false = > None
} ;
let mut left_eigenvectors = match self . left_eigenvectors . is_some ( ) {
true = > Some ( Vec ::< OVector < Complex < T > , D > > ::with_capacity ( 2 * number_of_complex_entries ) ) ,
false = > None
} ;
let eigenvectors_raw = self . eigenvectors ;
let left_eigenvectors_raw = self . left_eigenvectors ;
for mut i in 0 .. number_of_elements {
if self . eigenvalues_im [ i ] ! = T ::zero ( ) {
//Complex conjugate pairs of eigenvalues appear consecutively with the eigenvalue having the positive imaginary part first.
eigenvalues . push ( Complex ::< T > ::new ( self . eigenvalues_re [ i ] . clone ( ) , self . eigenvalues_im [ i ] . clone ( ) ) ) ;
eigenvalues . push ( Complex ::< T > ::new ( self . eigenvalues_re [ i ] . clone ( ) , - self . eigenvalues_im [ i ] . clone ( ) ) ) ;
if eigenvectors . is_some ( ) {
let mut r1_vec = OVector ::< Complex < T > , D > ::zeros ( number_of_elements ) ;
let mut r1_vec_conj = OVector ::< Complex < T > , D > ::zeros ( number_of_elements ) ;
for j in 0 .. number_of_elements {
r1_vec [ j ] = Complex ::< T > ::new ( self . eigenvectors . unwrap ( ) [ ( i , j ) ] . clone ( ) , self . eigenvectors . unwrap ( ) [ ( i , j + 1 ) ] . clone ( ) ) ;
r1_vec_conj [ j ] = Complex ::< T > ::new ( self . eigenvectors . unwrap ( ) [ ( i , j ) ] . clone ( ) , - self . eigenvectors . unwrap ( ) [ ( i , j + 1 ) ] . clone ( ) ) ;
}
eigenvectors . unwrap ( ) . push ( r1_vec ) ;
eigenvectors . unwrap ( ) . push ( r1_vec_conj ) ;
}
if left_eigenvectors . is_some ( ) {
//TODO: Do the same for left
}
i + = 1 ;
}
}
( Some ( eigenvalues ) , left_eigenvectors , eigenvectors )
}
}
}
2017-08-03 01:38:28 +08:00
}
/*
*
* Lapack functions dispatch .
*
* /
2018-09-24 12:48:42 +08:00
/// Trait implemented by scalar type for which Lapack function exist to compute the
2017-08-14 01:52:58 +08:00
/// eigendecomposition.
2019-12-17 07:09:14 +08:00
pub trait EigenScalar : Scalar {
2017-08-14 01:52:58 +08:00
#[ allow(missing_docs) ]
2018-02-02 19:26:35 +08:00
fn xgeev (
jobvl : u8 ,
jobvr : u8 ,
n : i32 ,
a : & mut [ Self ] ,
lda : i32 ,
wr : & mut [ Self ] ,
wi : & mut [ Self ] ,
vl : & mut [ Self ] ,
ldvl : i32 ,
vr : & mut [ Self ] ,
ldvr : i32 ,
work : & mut [ Self ] ,
lwork : i32 ,
info : & mut i32 ,
) ;
2017-08-14 01:52:58 +08:00
#[ allow(missing_docs) ]
2018-02-02 19:26:35 +08:00
fn xgeev_work_size (
jobvl : u8 ,
jobvr : u8 ,
n : i32 ,
a : & mut [ Self ] ,
lda : i32 ,
wr : & mut [ Self ] ,
wi : & mut [ Self ] ,
vl : & mut [ Self ] ,
ldvl : i32 ,
vr : & mut [ Self ] ,
ldvr : i32 ,
info : & mut i32 ,
) -> i32 ;
2017-08-03 01:38:28 +08:00
}
macro_rules ! real_eigensystem_scalar_impl (
( $N : ty , $xgeev : path ) = > (
2017-08-07 01:41:33 +08:00
impl EigenScalar for $N {
2017-08-03 01:38:28 +08:00
#[ inline ]
fn xgeev ( jobvl : u8 , jobvr : u8 , n : i32 , a : & mut [ Self ] , lda : i32 ,
wr : & mut [ Self ] , wi : & mut [ Self ] ,
vl : & mut [ Self ] , ldvl : i32 , vr : & mut [ Self ] , ldvr : i32 ,
work : & mut [ Self ] , lwork : i32 , info : & mut i32 ) {
2018-05-25 05:51:57 +08:00
unsafe { $xgeev ( jobvl , jobvr , n , a , lda , wr , wi , vl , ldvl , vr , ldvr , work , lwork , info ) }
2017-08-03 01:38:28 +08:00
}
#[ inline ]
fn xgeev_work_size ( jobvl : u8 , jobvr : u8 , n : i32 , a : & mut [ Self ] , lda : i32 ,
wr : & mut [ Self ] , wi : & mut [ Self ] , vl : & mut [ Self ] , ldvl : i32 ,
vr : & mut [ Self ] , ldvr : i32 , info : & mut i32 ) -> i32 {
let mut work = [ Zero ::zero ( ) ] ;
let lwork = - 1 as i32 ;
2018-05-25 05:51:57 +08:00
unsafe { $xgeev ( jobvl , jobvr , n , a , lda , wr , wi , vl , ldvl , vr , ldvr , & mut work , lwork , info ) } ;
2017-08-03 01:38:28 +08:00
ComplexHelper ::real_part ( work [ 0 ] ) as i32
}
}
)
) ;
2018-05-25 05:51:57 +08:00
real_eigensystem_scalar_impl! ( f32 , lapack ::sgeev ) ;
real_eigensystem_scalar_impl! ( f64 , lapack ::dgeev ) ;
2017-08-03 01:38:28 +08:00
2020-11-15 23:57:49 +08:00
//// TODO: decomposition of complex matrix and matrices with complex eigenvalues.
2018-05-25 05:51:57 +08:00
// eigensystem_complex_impl!(f32, lapack::cgeev);
// eigensystem_complex_impl!(f64, lapack::zgeev);