use mdarray::{Array, Dim, Layout, Shape, Slice};
use mdarray_linalg::{
lu::{InvError, LU},
utils::{into_i32, ipiv_to_perm_mat, transpose_in_place},
};
use num_complex::ComplexFloat;
use super::{
scalar::{LapackScalar, Workspace},
simple::{getrf, getri, potrf},
};
use crate::Lapack;
impl<T, D0: Dim, D1: Dim> LU<T, D0, D1> for Lapack
where
T: ComplexFloat + Default + LapackScalar + Workspace,
T::Real: Into<T>,
{
fn lu_write<L: Layout, Ll: Layout, Lu: Layout, Lp: Layout>(
&self,
a: &mut Slice<T, (D0, D1), L>,
l: &mut Slice<T, (D0, D0), Ll>,
u: &mut Slice<T, (D0, D1), Lu>,
p: &mut Slice<T, (D0, D0), Lp>,
) {
let ash = *a.shape();
let m = ash.dim(0);
let ipiv = getrf(a, l, u);
let p_matrix = ipiv_to_perm_mat::<T, D0, D1>(&ipiv, m);
for i in 0..m {
for j in 0..m {
p[[i, j]] = p_matrix[[i, j]];
}
}
}
fn lu<L: Layout>(
&self,
a: &mut Slice<T, (D0, D1), L>,
) -> (
Array<T, (D0, D0)>,
Array<T, (D0, D1)>,
Array<T, (D0, D0)>,
) {
let ash = *a.shape();
let (m, n) = (ash.dim(0), ash.dim(1));
let min_mn = m.min(n);
let l_shape = <(D0, D0) as Shape>::from_dims(&[m, min_mn]);
let u_shape = <(D0, D1) as Shape>::from_dims(&[min_mn, n]);
let mut l = Array::from_elem(l_shape, T::default());
let mut u = Array::from_elem(u_shape, T::default());
let ipiv = getrf::<T, D0, D1, _, _, _>(a, &mut l, &mut u);
let p_matrix = ipiv_to_perm_mat::<T, D0, D0>(&ipiv, m);
(l, u, p_matrix)
}
fn inv_write<L: Layout>(&self, a: &mut Slice<T, (D0, D1), L>) -> Result<(), InvError> {
let ash = *a.shape();
let (m, n) = (ash.dim(0), ash.dim(1));
if m != n {
return Err(InvError::NotSquare {
rows: into_i32(m),
cols: into_i32(n),
});
}
let min_mn = m.min(n);
let l_shape = <(D0, D0) as Shape>::from_dims(&[m, min_mn]);
let u_shape = <(D0, D1) as Shape>::from_dims(&[min_mn, n]);
let mut l = Array::from_elem(l_shape, T::default());
let mut u = Array::from_elem(u_shape, T::default());
let mut ipiv = getrf::<T, D0, D1, _, _, _>(a, &mut l, &mut u);
match getri::<T, D0, D1, _>(a, &mut ipiv) {
0 => Ok(()),
i if i > 0 => Err(InvError::Singular { pivot: i }),
i => Err(InvError::BackendError(i)),
}
}
fn inv<L: Layout>(
&self,
a: &mut Slice<T, (D0, D1), L>,
) -> Result<Array<T, (D0, D1)>, InvError> {
let ash = *a.shape();
let (m, n) = (ash.dim(0), ash.dim(1));
if m != n {
return Err(InvError::NotSquare {
rows: into_i32(m),
cols: into_i32(n),
});
}
let mut a_inv = Array::<T, (D0, D1)>::zeros(ash);
for i in 0..n {
for j in 0..m {
a_inv[[i, j]] = a[[i, j]];
}
}
let min_mn = m.min(n);
let l_shape = <(D0, D0) as Shape>::from_dims(&[m, min_mn]);
let u_shape = <(D0, D1) as Shape>::from_dims(&[min_mn, n]);
let mut l = Array::from_elem(l_shape, T::default());
let mut u = Array::from_elem(u_shape, T::default());
let mut ipiv = getrf::<T, D0, D1, _, _, _>(&mut a_inv, &mut l, &mut u);
match getri::<T, D0, D1, L>(a, &mut ipiv) {
0 => Ok(a.to_tensor()),
i if i > 0 => Err(InvError::Singular { pivot: i }),
i => Err(InvError::BackendError(i)),
}
}
fn det<L: Layout>(&self, a: &mut Slice<T, (D0, D1), L>) -> T {
let ash = *a.shape();
let (m, n) = (ash.dim(0), ash.dim(1));
assert_eq!(m, n, "determinant is only defined for square matrices");
let l_shape = <(D0, D0) as Shape>::from_dims(&[n, n]);
let u_shape = <(D0, D1) as Shape>::from_dims(&[n, n]);
let mut l = Array::from_elem(l_shape, T::default());
let mut u = Array::from_elem(u_shape, T::default());
let ipiv = getrf::<T, D0, D1, _, _, _>(a, &mut l, &mut u);
let mut det = T::one();
for i in 0..n {
det = det * u[[i, i]];
}
let mut sign = T::one();
for (i, &pivot) in ipiv.iter().enumerate() {
if (i as i32) != (pivot - 1) {
sign = sign * (-T::one());
}
}
det * sign
}
fn cholesky<L: Layout>(
&self,
a: &mut Slice<T, (D0, D1), L>,
) -> Result<Array<T, (D0, D1)>, InvError> {
let ash = *a.shape();
let (m, n) = (ash.dim(0), ash.dim(1));
assert_eq!(m, n, "Matrix must be square for Cholesky decomposition");
let mut l = Array::<T, (D0, D1)>::zeros(ash);
match potrf::<T, D0, D1, _>(a, 'L') {
0 => {
for i in 0..m {
for j in 0..n {
if i >= j {
l[[i, j]] = a[[j, i]];
} else {
l[[i, j]] = T::zero();
}
}
}
Ok(l)
}
i if i > 0 => Err(InvError::NotPositiveDefinite { lpm: i }),
i => Err(InvError::BackendError(i)),
}
}
fn cholesky_write<L: Layout>(&self, a: &mut Slice<T, (D0, D1), L>) -> Result<(), InvError> {
let ash = *a.shape();
let (m, n) = (ash.dim(0), ash.dim(1));
assert_eq!(m, n, "Matrix must be square for Cholesky decomposition");
match potrf::<T, D0, D1, _>(a, 'L') {
0 => {
transpose_in_place(a);
Ok(())
}
i if i > 0 => Err(InvError::NotPositiveDefinite { lpm: i }),
i => Err(InvError::BackendError(i)),
}
}
}