use crate::error::Error;
use ndarray::Array2;
use num_complex::Complex;
pub trait EigenvalueBackend<T> {
fn eigenvalues(&self, matrix: Array2<T>) -> Result<Vec<Complex<T>>>;
}
type Result<T> = std::result::Result<T, EigenvaluesError>;
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct EigenvaluesError(pub String);
impl From<EigenvaluesError> for Error {
fn from(value: EigenvaluesError) -> Error {
Error::EigenvaluesError(value.0)
}
}
#[cfg(any(feature = "lapack-backend", feature = "nalgebra-backend"))]
macro_rules! default_eigenvalue_doc {
() => {
r#" Default eigenvalue backend.
This defines the default eigenvalue backend, which depends on what feature
flags are enabled. The selected default backend is the first available from this priority list:
- `lapack-backend`
- `nalgebra-backend`
"#
};
}
#[doc = default_eigenvalue_doc!()]
#[cfg(feature = "lapack-backend")]
pub type DefaultEigenvalueBackend = LapackBackend;
#[doc = default_eigenvalue_doc!()]
#[cfg(all(not(any(feature = "lapack-backend")), feature = "nalgebra-backend"))]
pub type DefaultEigenvalueBackend = NalgebraBackend;
#[cfg(feature = "lapack-backend")]
pub use lapack::LapackBackend;
#[cfg(feature = "lapack-backend")]
mod lapack {
use super::*;
use crate::lapack::ToLapack;
use ndarray_linalg::{EigVals, Scalar, error::LinalgError};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct LapackBackend {}
impl<T: ToLapack> EigenvalueBackend<T> for LapackBackend {
fn eigenvalues(&self, matrix: Array2<T>) -> Result<Vec<Complex<T>>> {
let matrix = T::array_to_lapack(matrix);
let eig = matrix.eigvals()?;
Ok(eig
.into_iter()
.map(|z| {
Complex::new(
T::from_lapack(&<T::Lapack>::from_real(z.re())),
T::from_lapack(&<T::Lapack>::from_real(z.im())),
)
})
.collect())
}
}
impl From<LinalgError> for EigenvaluesError {
fn from(value: LinalgError) -> EigenvaluesError {
EigenvaluesError(value.to_string())
}
}
}
#[cfg(feature = "nalgebra-backend")]
pub use nalgebra::NalgebraBackend;
#[cfg(feature = "nalgebra-backend")]
mod nalgebra {
use super::*;
use ::nalgebra::{DMatrix, RealField};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct NalgebraBackend {}
pub trait IsRealField: RealField {}
impl IsRealField for f64 {}
impl IsRealField for f32 {}
impl<T: IsRealField> EigenvalueBackend<T> for NalgebraBackend {
fn eigenvalues(&self, matrix: Array2<T>) -> Result<Vec<Complex<T>>> {
let matrix = DMatrix::from_row_iterator(matrix.nrows(), matrix.ncols(), matrix);
let eig = matrix.complex_eigenvalues();
Ok(eig.into_iter().cloned().collect())
}
}
#[cfg(feature = "num-bigfloat")]
impl EigenvalueBackend<num_bigfloat::BigFloat> for NalgebraBackend {
fn eigenvalues(
&self,
matrix: Array2<num_bigfloat::BigFloat>,
) -> Result<Vec<Complex<num_bigfloat::BigFloat>>> {
let matrix = DMatrix::from_row_iterator(
matrix.nrows(),
matrix.ncols(),
matrix.into_iter().map(|x| x.to_f64()),
);
let eig = matrix.complex_eigenvalues();
Ok(eig
.into_iter()
.map(|z| {
Complex::new(
num_bigfloat::BigFloat::from_f64(z.re),
num_bigfloat::BigFloat::from_f64(z.im),
)
})
.collect())
}
}
}