use num::Zero;
use crate::allocator::Allocator;
use crate::{RealField, ComplexField};
use crate::storage::{Storage, StorageMut};
use crate::base::{DefaultAllocator, Matrix, Dim, MatrixMN};
use crate::constraint::{SameNumberOfRows, SameNumberOfColumns, ShapeConstraint};
pub trait Norm<N: ComplexField> {
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C>;
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>;
}
pub struct EuclideanNorm;
pub struct LpNorm(pub i32);
pub struct UniformNorm;
impl<N: ComplexField> Norm<N> for EuclideanNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.norm_squared().sqrt()
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::RealField::zero(), |acc, a, b| {
let diff = a - b;
acc + diff.modulus_squared()
}).sqrt()
}
}
impl<N: ComplexField> Norm<N> for LpNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.fold(N::RealField::zero(), |a, b| {
a + b.modulus().powi(self.0)
}).powf(crate::convert(1.0 / (self.0 as f64)))
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::RealField::zero(), |acc, a, b| {
let diff = a - b;
acc + diff.modulus().powi(self.0)
}).powf(crate::convert(1.0 / (self.0 as f64)))
}
}
impl<N: ComplexField> Norm<N> for UniformNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.fold(N::RealField::zero(), |acc, a| acc.max(a.modulus()))
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::RealField::zero(), |acc, a, b| {
let val = (a - b).modulus();
if val > acc {
val
} else {
acc
}
})
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
#[inline]
pub fn norm_squared(&self) -> N::RealField {
let mut res = N::RealField::zero();
for i in 0..self.ncols() {
let col = self.column(i);
res += col.dotc(&col).real()
}
res
}
#[inline]
pub fn norm(&self) -> N::RealField {
self.norm_squared().sqrt()
}
#[inline]
pub fn metric_distance<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>) -> N::RealField
where R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
self.apply_metric_distance(rhs, &EuclideanNorm)
}
#[inline]
pub fn apply_norm(&self, norm: &impl Norm<N>) -> N::RealField {
norm.norm(self)
}
#[inline]
pub fn apply_metric_distance<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>, norm: &impl Norm<N>) -> N::RealField
where R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
norm.metric_distance(self, rhs)
}
#[inline]
pub fn magnitude(&self) -> N::RealField {
self.norm()
}
#[inline]
pub fn magnitude_squared(&self) -> N::RealField {
self.norm_squared()
}
#[inline]
pub fn normalize(&self) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.unscale(self.norm())
}
#[inline]
pub fn try_normalize(&self, min_norm: N::RealField) -> Option<MatrixMN<N, R, C>>
where DefaultAllocator: Allocator<N, R, C> {
let n = self.norm();
if n <= min_norm {
None
} else {
Some(self.unscale(n))
}
}
#[inline]
pub fn lp_norm(&self, p: i32) -> N::RealField {
self.apply_norm(&LpNorm(p))
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
#[inline]
pub fn normalize_mut(&mut self) -> N::RealField {
let n = self.norm();
self.unscale_mut(n);
n
}
#[inline]
pub fn try_normalize_mut(&mut self, min_norm: N::RealField) -> Option<N::RealField> {
let n = self.norm();
if n <= min_norm {
None
} else {
self.unscale_mut(n);
Some(n)
}
}
}