use crate::error::LinalgError;
use crate::linear_algebra::Matrix;
use crate::scalar::Numeric;
impl<const N: usize, T: Numeric> Matrix<N, N, T> {
pub fn expm(self) -> Result<Matrix<N, N, T>, LinalgError> {
let mut nrm = T::ZERO;
for j in 0..N {
let mut col = T::ZERO;
for i in 0..N {
col += self[(i, j)].abs();
}
if col > nrm {
nrm = col;
}
}
let mut s: i32 = 0;
let mut scaled = nrm;
while scaled > T::HALF {
scaled *= T::HALF;
s += 1;
}
let a = self.scale(T::HALF.powi(s));
let mut c = [T::ZERO; 7];
c[0] = T::ONE;
for k in 1..7 {
let kf = k as f64;
c[k] = c[k - 1] * T::from_f64((6.0 - kf + 1.0) / (kf * (12.0 - kf + 1.0)));
}
let mut apow = Matrix::identity();
let mut num = Matrix::zeros();
let mut den = Matrix::zeros();
#[allow(clippy::needless_range_loop)]
for k in 0..7 {
num += apow.scale(c[k]);
let signed = if k % 2 == 0 { c[k] } else { -c[k] };
den += apow.scale(signed);
if k < 6 {
apow = apow * a;
}
}
let mut result = den.lu()?.solve_matrix(num);
for _ in 0..s {
result = result * result;
}
if !result.is_finite() {
return Err(LinalgError::NonFinite);
}
Ok(result)
}
}