use num::Zero;
use itertools::Itertools;
use crate::{
base_types::{RlstResult, UpLo},
dense::{
array::DynArray,
linalg::lapack::{
eigenvalue_decomposition::EigMode,
generalized_schur::QzDecomposition,
ldlt::LdlDecomposition,
lq::LqDecomposition,
lu::LuDecomposition,
pseudo_inverse::PInv,
qr::{EnablePivoting, QrDecomposition},
singular_value_decomposition::SvdMode,
sylvester::SylvesterSolution,
symmeig::SymmEigMode,
},
},
traits::{base_operations::EvaluateArray, rlst_num::RlstScalar},
};
use super::{base::Gemm, lapack::Lapack};
use crate::dense::linalg::lapack::interface::trsyl::TrsylSign;
pub trait Inverse {
type Output;
fn inverse(&self) -> RlstResult<Self::Output>;
}
pub trait Lu {
type Item: Lapack;
fn lu(&self) -> RlstResult<LuDecomposition<Self::Item>>;
}
pub trait Ldl {
type Item: Lapack;
fn ldl(&self, uplo: UpLo) -> RlstResult<LdlDecomposition<Self::Item>>;
}
pub trait Qr {
type Item: Lapack;
fn qr(&self, pivoting: EnablePivoting) -> RlstResult<QrDecomposition<Self::Item>>;
}
pub trait Lq {
type Item: Lapack;
fn lq(&self) -> RlstResult<LqDecomposition<Self::Item>>;
}
pub trait SymmEig {
type Item: Lapack;
fn eigenvaluesh(&self) -> RlstResult<DynArray<<Self::Item as RlstScalar>::Real, 1>> {
Ok(self.eigh(UpLo::Upper, SymmEigMode::EigenvaluesOnly)?.0)
}
#[allow(clippy::type_complexity)]
fn eigh(
&self,
uplo: UpLo,
mode: SymmEigMode,
) -> RlstResult<(
DynArray<<Self::Item as RlstScalar>::Real, 1>,
Option<DynArray<Self::Item, 2>>,
)>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GeneralizedSymmEigType {
AxEqualLambdaBx = 1,
ABxEqualLambdaX = 2,
BAxEqualLambdaX = 3,
}
pub trait GeneralizedSymmEig<B> {
type Item: Lapack;
fn eigenvaluesh_generalized(
&self,
b: &B,
uplo: UpLo,
itype: GeneralizedSymmEigType,
) -> RlstResult<DynArray<<Self::Item as RlstScalar>::Real, 1>> {
Ok(self
.eigh_generalized(b, uplo, SymmEigMode::EigenvaluesOnly, itype)?
.0)
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
fn eigh_generalized(
&self,
b: &B,
uplo: UpLo,
mode: SymmEigMode,
itype: GeneralizedSymmEigType,
) -> RlstResult<(
DynArray<<Self::Item as RlstScalar>::Real, 1>,
Option<DynArray<Self::Item, 2>>,
)>;
}
pub trait EigenvalueDecomposition {
type Item: Lapack;
fn eigenvalues(&self) -> RlstResult<DynArray<<Self::Item as RlstScalar>::Complex, 1>>;
#[allow(clippy::type_complexity)]
fn schur(&self) -> RlstResult<(DynArray<Self::Item, 2>, DynArray<Self::Item, 2>)>;
#[allow(clippy::type_complexity)]
fn eig(
&self,
mode: EigMode,
) -> RlstResult<(
DynArray<<Self::Item as RlstScalar>::Complex, 1>,
Option<DynArray<<Self::Item as RlstScalar>::Complex, 2>>,
Option<DynArray<<Self::Item as RlstScalar>::Complex, 2>>,
)>;
}
pub trait GeneralizedEigenvalueDecomposition<B> {
type Item: Lapack;
fn eigenvalues_generalized(
&self,
b: &B,
) -> RlstResult<DynArray<<Self::Item as RlstScalar>::Complex, 1>> {
let (lam, _, _) = self.eig_generalized(b, EigMode::EigenvaluesOnly)?;
Ok(lam)
}
#[allow(clippy::type_complexity)]
fn eig_generalized(
&self,
b: &B,
mode: EigMode,
) -> RlstResult<(
DynArray<<Self::Item as RlstScalar>::Complex, 1>,
Option<DynArray<<Self::Item as RlstScalar>::Complex, 2>>,
Option<DynArray<<Self::Item as RlstScalar>::Complex, 2>>,
)>;
}
pub trait GeneralizedSchur<B> {
type Item: Lapack;
fn qz(&self, b: &B) -> RlstResult<QzDecomposition<Self::Item>>;
}
pub trait SylvesterSolve<B, C> {
type Item: Lapack + Gemm;
fn solve_sylvester(
&self,
b: &B,
c: &C,
sign: TrsylSign,
) -> RlstResult<SylvesterSolution<Self::Item>>;
}
pub trait SingularValueDecomposition {
type Item: Lapack + Gemm;
fn singular_values(&self) -> RlstResult<DynArray<<Self::Item as RlstScalar>::Real, 1>>;
#[allow(clippy::type_complexity)]
fn svd(
&self,
mode: SvdMode,
) -> RlstResult<(
DynArray<<Self::Item as RlstScalar>::Real, 1>,
DynArray<Self::Item, 2>,
DynArray<Self::Item, 2>,
)>;
#[allow(clippy::type_complexity)]
fn svd_truncated(
&self,
max_singular_values: Option<usize>,
tol: Option<<Self::Item as RlstScalar>::Real>,
) -> RlstResult<(
DynArray<<Self::Item as RlstScalar>::Real, 1>,
DynArray<Self::Item, 2>,
DynArray<Self::Item, 2>,
)> {
let (s, u, vh) = self.svd(SvdMode::Compact)?;
let nvalues = std::cmp::min(
match max_singular_values {
Some(n) => n,
None => s.len(),
},
s.len(),
);
let tol = match tol {
Some(t) => t,
None => <<Self::Item as RlstScalar>::Real as Zero>::zero(),
};
let count = match s
.iter_value()
.take(nvalues)
.find_position(|&elem| elem <= tol * s[[0]])
{
Some((index, _)) => index,
None => nvalues,
};
let s = s.into_subview([0], [count]).eval();
let u = u.r().into_subview([0, 0], [u.shape()[0], count]).eval();
let vh = vh.r().into_subview([0, 0], [count, vh.shape()[1]]).eval();
Ok((s, u, vh))
}
fn pseudo_inverse(
&self,
max_singular_values: Option<usize>,
tol: Option<<Self::Item as RlstScalar>::Real>,
) -> RlstResult<PInv<Self::Item>>
where
<Self::Item as RlstScalar>::Real: Into<Self::Item>,
{
let (s, u, vh) = self.svd_truncated(max_singular_values, tol)?;
Ok(PInv::new(
s.into_type().eval(),
u.conj().transpose().eval(),
vh.conj().transpose().eval(),
))
}
}
pub trait Solve<Rhs> {
type Output;
fn solve(&self, rhs: &Rhs) -> RlstResult<Self::Output>;
}
pub trait Cholesky {
type Item;
fn cholesky(&self, uplo: UpLo) -> RlstResult<DynArray<Self::Item, 2>>;
}
pub trait CholeskySolve<Rhs> {
type Output;
fn cholesky_solve(&self, uplo: UpLo, rhs: &Rhs) -> RlstResult<Self::Output>;
}
pub trait SolveTriangular<Rhs> {
type Output;
fn solve_triangular(&self, uplo: UpLo, rhs: &Rhs) -> RlstResult<Self::Output>;
}