use ndarray::{Array1, Array2, ArrayBase, ArrayView1, ArrayView2, Axis, Data, Ix2};
use ndarray_linalg::error::LinalgError;
use ndarray_linalg::Norm;
use ndarray_linalg::OperationNorm;
use thiserror::Error;
pub use ndarray_linalg::{c32, c64, Scalar};
#[derive(Error, Debug)]
pub enum RustyCompressionError {
#[error("Lapack Error")]
LinalgError(LinalgError),
#[error("Could not compress to desired tolerance")]
CompressionError,
#[error("Incompatible memory layout")]
LayoutError,
#[error("Pivoted QR failed")]
PivotedQRError,
}
pub type Result<T> = std::result::Result<T, RustyCompressionError>;
pub trait Apply<A, Lhs> {
type Output;
fn dot(&self, lhs: &Lhs) -> Self::Output;
}
pub trait RApply<A, Lhs> {
type Output;
fn dot(&self, lhs: &Lhs) -> Self::Output;
}
pub trait MatVec {
type A: Scalar;
fn nrows(&self) -> usize;
fn ncols(&self) -> usize;
fn matvec(&self, mat: ArrayView1<Self::A>) -> Array1<Self::A>;
}
pub trait MatMat: MatVec {
fn matmat(&self, mat: ArrayView2<Self::A>) -> Array2<Self::A> {
let mut output = Array2::<Self::A>::zeros((self.nrows(), mat.ncols()));
for (index, col) in mat.axis_iter(Axis(1)).enumerate() {
output
.index_axis_mut(Axis(1), index)
.assign(&self.matvec(col));
}
output
}
}
pub trait ConjMatVec: MatVec {
fn conj_matvec(&self, vec: ArrayView1<Self::A>) -> Array1<Self::A>;
}
pub trait ConjMatMat: MatMat + ConjMatVec {
fn conj_matmat(&self, mat: ArrayView2<Self::A>) -> Array2<Self::A> {
let mut output = Array2::<Self::A>::zeros((self.ncols(), mat.ncols()));
for (index, col) in mat.axis_iter(Axis(1)).enumerate() {
output
.index_axis_mut(Axis(1), index)
.assign(&self.conj_matvec(col));
}
output
}
}
impl<A, S> MatVec for ArrayBase<S, Ix2>
where
A: Scalar,
S: Data<Elem = A>,
{
type A = A;
fn nrows(&self) -> usize {
self.nrows()
}
fn ncols(&self) -> usize {
self.ncols()
}
fn matvec(&self, vec: ArrayView1<Self::A>) -> Array1<Self::A> {
self.dot(&vec)
}
}
impl<A, S> ConjMatVec for ArrayBase<S, Ix2>
where
A: Scalar,
S: Data<Elem = A>,
{
fn conj_matvec(&self, vec: ArrayView1<Self::A>) -> Array1<Self::A> {
vec.map(|item| item.conj())
.dot(self)
.map(|item| item.conj())
}
}
impl<A: Scalar, T: MatVec<A=A>> MatMat for T {}
impl<A: Scalar, T: ConjMatVec<A=A>> ConjMatMat for T {}
pub trait RelDiff {
type A: Scalar;
fn rel_diff_fro(
first: ArrayView2<Self::A>,
second: ArrayView2<Self::A>,
) -> <<Self as RelDiff>::A as Scalar>::Real;
fn rel_diff_l2(
first: ArrayView1<Self::A>,
second: ArrayView1<Self::A>,
) -> <<Self as RelDiff>::A as Scalar>::Real;
}
macro_rules! rel_diff_impl {
($scalar:ty) => {
impl RelDiff for $scalar {
type A = $scalar;
fn rel_diff_fro(
first: ArrayView2<Self::A>,
second: ArrayView2<Self::A>,
) -> <<Self as RelDiff>::A as Scalar>::Real {
let diff = first.to_owned() - &second;
diff.opnorm_fro().unwrap() / second.opnorm_fro().unwrap()
}
fn rel_diff_l2(
first: ArrayView1<Self::A>,
second: ArrayView1<Self::A>,
) -> <<Self as RelDiff>::A as Scalar>::Real {
let diff = first.to_owned() - &second;
diff.norm_l2() / second.norm_l2()
}
}
};
}
rel_diff_impl!(f32);
rel_diff_impl!(f64);
rel_diff_impl!(c32);
rel_diff_impl!(c64);