use super::ComplexMatrix;
use crate::{cpx, to_i32, CcBool, Complex64, ComplexVector, StrError, C_FALSE, C_TRUE};
extern "C" {
fn c_zggev(
calc_vl: CcBool,
calc_vr: CcBool,
n: *const i32,
a: *mut Complex64,
lda: *const i32,
b: *mut Complex64,
ldb: *const i32,
alpha: *mut Complex64,
beta: *mut Complex64,
vl: *mut Complex64,
ldvl: *const i32,
vr: *mut Complex64,
ldvr: *const i32,
work: *mut Complex64,
lwork: *const i32,
rwork: *mut f64,
info: *mut i32,
);
}
pub fn complex_mat_gen_eigen(
alpha: &mut ComplexVector,
beta: &mut ComplexVector,
v: &mut ComplexMatrix,
a: &mut ComplexMatrix,
b: &mut ComplexMatrix,
) -> Result<(), StrError> {
let (m, n) = a.dims();
if m != n {
return Err("matrix must be square");
}
if alpha.dim() != m || beta.dim() != m {
return Err("vectors are incompatible");
}
if v.nrow() != m || v.ncol() != m || b.nrow() != m || b.ncol() != m {
return Err("matrices are incompatible");
}
let m_i32 = to_i32(m);
let lda = m_i32;
let ldb = m_i32;
let ldu = 1;
let ldv = m_i32;
const EXTRA: i32 = 1;
let lwork = 2 * m_i32 + EXTRA;
let mut u = vec![cpx!(0.0, 0.0); ldu as usize];
let mut work = vec![cpx!(0.0, 0.0); lwork as usize];
let mut rwork = vec![0.0; 8 * m];
let mut info = 0;
unsafe {
c_zggev(
C_FALSE,
C_TRUE,
&m_i32,
a.as_mut_data().as_mut_ptr(),
&lda,
b.as_mut_data().as_mut_ptr(),
&ldb,
alpha.as_mut_data().as_mut_ptr(),
beta.as_mut_data().as_mut_ptr(),
u.as_mut_ptr(),
&ldu,
v.as_mut_data().as_mut_ptr(),
&ldv,
work.as_mut_ptr(),
&lwork,
rwork.as_mut_ptr(),
&mut info,
);
}
if info < 0 {
println!("LAPACK ERROR (zggev): Argument #{} had an illegal value", -info);
return Err("LAPACK ERROR (zggev): An argument had an illegal value");
} else if info > 0 {
return Err("LAPACK ERROR (zggev): Iterations failed ");
}
Ok(())
}
pub fn complex_mat_gen_eigen_lr(
alpha: &mut ComplexVector,
beta: &mut ComplexVector,
u: &mut ComplexMatrix,
v: &mut ComplexMatrix,
a: &mut ComplexMatrix,
b: &mut ComplexMatrix,
) -> Result<(), StrError> {
let (m, n) = a.dims();
if m != n {
return Err("matrix must be square");
}
if alpha.dim() != m || beta.dim() != m {
return Err("vectors are incompatible");
}
if u.nrow() != m || u.ncol() != m || v.nrow() != m || v.ncol() != m || b.nrow() != m || b.ncol() != m {
return Err("matrices are incompatible");
}
let m_i32 = to_i32(m);
let lda = m_i32;
let ldb = m_i32;
let ldu = m_i32;
let ldv = m_i32;
const EXTRA: i32 = 1;
let lwork = 2 * m_i32 + EXTRA;
let mut work = vec![cpx!(0.0, 0.0); lwork as usize];
let mut rwork = vec![0.0; 8 * m];
let mut info = 0;
unsafe {
c_zggev(
C_FALSE,
C_TRUE,
&m_i32,
a.as_mut_data().as_mut_ptr(),
&lda,
b.as_mut_data().as_mut_ptr(),
&ldb,
alpha.as_mut_data().as_mut_ptr(),
beta.as_mut_data().as_mut_ptr(),
u.as_mut_data().as_mut_ptr(),
&ldu,
v.as_mut_data().as_mut_ptr(),
&ldv,
work.as_mut_ptr(),
&lwork,
rwork.as_mut_ptr(),
&mut info,
);
}
if info < 0 {
println!("LAPACK ERROR (zggev): Argument #{} had an illegal value", -info);
return Err("LAPACK ERROR (zggev): An argument had an illegal value");
} else if info > 0 {
return Err("LAPACK ERROR (zggev): Iterations failed ");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{complex_mat_gen_eigen, complex_mat_gen_eigen_lr};
use crate::matrix::testing::complex_check_gen_eigen;
use crate::{complex_vec_approx_eq, cpx, Complex64, ComplexMatrix, ComplexVector};
#[test]
fn complex_mat_gen_eigen_captures_errors() {
let mut a = ComplexMatrix::new(2, 1);
let m = a.nrow();
let mut b = ComplexMatrix::new(m, m);
let mut alpha = ComplexVector::new(m);
let mut beta = ComplexVector::new(m);
let mut v = ComplexMatrix::new(m, m);
assert_eq!(
complex_mat_gen_eigen(&mut alpha, &mut beta, &mut v, &mut a, &mut b),
Err("matrix must be square")
);
let mut a = ComplexMatrix::new(2, 2);
let mut b_wrong1 = ComplexMatrix::new(m + 1, m);
let mut b_wrong2 = ComplexMatrix::new(m, m + 1);
let mut alpha_wrong = ComplexVector::new(m + 1);
let mut beta_wrong = ComplexVector::new(m + 1);
let mut v_wrong = ComplexMatrix::new(m + 1, m);
assert_eq!(
complex_mat_gen_eigen(&mut alpha_wrong, &mut beta, &mut v, &mut a, &mut b),
Err("vectors are incompatible")
);
assert_eq!(
complex_mat_gen_eigen(&mut alpha, &mut beta_wrong, &mut v, &mut a, &mut b),
Err("vectors are incompatible")
);
assert_eq!(
complex_mat_gen_eigen(&mut alpha, &mut beta, &mut v_wrong, &mut a, &mut b),
Err("matrices are incompatible")
);
assert_eq!(
complex_mat_gen_eigen(&mut alpha, &mut beta, &mut v, &mut a, &mut b_wrong1,),
Err("matrices are incompatible")
);
assert_eq!(
complex_mat_gen_eigen(&mut alpha, &mut beta, &mut v, &mut a, &mut b_wrong2,),
Err("matrices are incompatible")
);
}
#[test]
fn complex_mat_gen_eigen_lr_captures_errors() {
let mut a = ComplexMatrix::new(2, 1);
let m = a.nrow();
let mut b = ComplexMatrix::new(m, m);
let mut alpha = ComplexVector::new(m);
let mut beta = ComplexVector::new(m);
let mut u = ComplexMatrix::new(m, m);
let mut v = ComplexMatrix::new(m, m);
assert_eq!(
complex_mat_gen_eigen_lr(&mut alpha, &mut beta, &mut u, &mut v, &mut a, &mut b),
Err("matrix must be square")
);
let mut a = ComplexMatrix::new(2, 2);
let mut b_wrong1 = ComplexMatrix::new(m + 1, m);
let mut b_wrong2 = ComplexMatrix::new(m, m + 1);
let mut alpha_wrong = ComplexVector::new(m + 1);
let mut beta_wrong = ComplexVector::new(m + 1);
let mut u_wrong = ComplexMatrix::new(m + 1, m);
let mut v_wrong = ComplexMatrix::new(m + 1, m);
assert_eq!(
complex_mat_gen_eigen_lr(&mut alpha_wrong, &mut beta, &mut u, &mut v, &mut a, &mut b),
Err("vectors are incompatible")
);
assert_eq!(
complex_mat_gen_eigen_lr(&mut alpha, &mut beta_wrong, &mut u, &mut v, &mut a, &mut b),
Err("vectors are incompatible")
);
assert_eq!(
complex_mat_gen_eigen_lr(&mut alpha, &mut beta, &mut u_wrong, &mut v, &mut a, &mut b),
Err("matrices are incompatible")
);
assert_eq!(
complex_mat_gen_eigen_lr(&mut alpha, &mut beta, &mut u, &mut v_wrong, &mut a, &mut b),
Err("matrices are incompatible")
);
assert_eq!(
complex_mat_gen_eigen_lr(&mut alpha, &mut beta, &mut u, &mut v, &mut a, &mut b_wrong1,),
Err("matrices are incompatible")
);
assert_eq!(
complex_mat_gen_eigen_lr(&mut alpha, &mut beta, &mut u, &mut v, &mut a, &mut b_wrong2,),
Err("matrices are incompatible")
);
}
#[test]
fn complex_mat_gen_eigen_works() {
#[rustfmt::skip]
let a_data = &[
[cpx!(2.0, 4.0), cpx!(1.0, 6.0), cpx!(2.0, 8.0), cpx!(4.0, 4.0)],
[cpx!(3.0, 3.0), cpx!(6.0, 1.0), cpx!(5.0, 3.0), cpx!(0.0, 0.0)],
[cpx!(5.0, 1.0), cpx!(8.0, 5.0), cpx!(3.0, 2.0), cpx!(8.0, 5.0)],
[cpx!(7.0, 6.0), cpx!(3.0, 7.0), cpx!(2.0, 1.0), cpx!(5.0, 4.0)],
];
#[rustfmt::skip]
let b_data = &[
[cpx!(0.0, 0.0), cpx!(1.0, 0.0), cpx!(0.0, 0.0), cpx!(0.0, 0.0)],
[cpx!(0.0, 0.0), cpx!(0.0, 0.0), cpx!(1.0, 0.0), cpx!(0.0, 0.0)],
[cpx!(0.0, 0.0), cpx!(0.0, 0.0), cpx!(0.0, 0.0), cpx!(1.0, 0.0)],
[cpx!(1.0, 0.0), cpx!(0.0, 0.0), cpx!(0.0, 0.0), cpx!(0.0, 0.0)],
];
let mut a = ComplexMatrix::from(a_data);
let mut b = ComplexMatrix::from(b_data);
let m = a.nrow();
let mut alpha = ComplexVector::new(m);
let mut beta = ComplexVector::new(m);
let mut v = ComplexMatrix::new(m, m);
complex_mat_gen_eigen(&mut alpha, &mut beta, &mut v, &mut a, &mut b).unwrap();
#[rustfmt::skip]
let alpha_ref = &[
cpx!(15.8864, 15.0474),
cpx!( 7.0401, 2.0585),
cpx!( 1.7083, 4.1133),
cpx!(-3.6348, -1.2193),
];
#[rustfmt::skip]
let beta_ref = &[
cpx!(1.0, 0.0),
cpx!(1.0, 0.0),
cpx!(1.0, 0.0),
cpx!(1.0, 0.0),
];
complex_vec_approx_eq(&alpha, alpha_ref, 1e-4);
complex_vec_approx_eq(&beta, beta_ref, 1e-14);
complex_check_gen_eigen(a_data, b_data, &v, &alpha, &beta, 1e-13);
}
#[test]
fn complex_mat_gen_eigen_lr_works() {
#[rustfmt::skip]
let a_data = &[
[cpx!( 1.0, 2.0), cpx!( 3.0, 4.0), cpx!(21.0,22.0)],
[cpx!(43.0,44.0), cpx!(13.0,14.0), cpx!(15.0,16.0)],
[cpx!( 5.0, 6.0), cpx!( 7.0, 8.0), cpx!(25.0,26.0)],
];
#[rustfmt::skip]
let b_data = &[
[cpx!( 2.0, 0.0), cpx!( 0.0,-1.0), cpx!( 0.0, 0.0)],
[cpx!( 0.0, 1.0), cpx!( 2.0, 0.0), cpx!( 0.0, 0.0)],
[cpx!( 0.0, 0.0), cpx!( 0.0, 0.0), cpx!( 3.0, 0.0)],
];
let mut a = ComplexMatrix::from(a_data);
let mut b = ComplexMatrix::from(b_data);
let m = a.nrow();
let mut alpha = ComplexVector::new(m);
let mut beta = ComplexVector::new(m);
let mut u = ComplexMatrix::new(m, m);
let mut v = ComplexMatrix::new(m, m);
complex_mat_gen_eigen_lr(&mut alpha, &mut beta, &mut u, &mut v, &mut a, &mut b).unwrap();
println!("α =\n{}", alpha);
println!("β =\n{}", beta);
println!("u =\n{}", u);
println!("v =\n{}", v);
#[rustfmt::skip]
let alpha_ref = &[
cpx!( 15.863783, 41.115283),
cpx!(-12.917205, 19.973815),
cpx!( 3.215518, -4.912439),
];
#[rustfmt::skip]
let beta_ref = &[
cpx!(1.668461, 0.0),
cpx!(2.024212, 0.0),
cpx!(2.664836, 0.0),
];
complex_vec_approx_eq(&alpha, alpha_ref, 1e-6);
complex_vec_approx_eq(&beta, beta_ref, 1e-6);
complex_check_gen_eigen(a_data, b_data, &v, &alpha, &beta, 1e-13);
}
}