2019-03-23 18:46:56 +08:00
|
|
|
use num::Zero;
|
2018-12-09 18:21:24 +08:00
|
|
|
|
2019-03-23 21:29:07 +08:00
|
|
|
use crate::allocator::Allocator;
|
2020-03-18 00:58:36 +08:00
|
|
|
use crate::base::{DefaultAllocator, Dim, Matrix, MatrixMN};
|
|
|
|
use crate::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint};
|
2019-03-23 21:29:07 +08:00
|
|
|
use crate::storage::{Storage, StorageMut};
|
2020-03-18 00:58:36 +08:00
|
|
|
use crate::{ComplexField, RealField, SimdComplexField, SimdRealField};
|
|
|
|
use alga::simd::SimdPartialOrd;
|
2018-12-09 18:21:24 +08:00
|
|
|
|
|
|
|
// FIXME: this should be be a trait on alga?
|
2019-02-03 15:33:07 +08:00
|
|
|
/// A trait for abstract matrix norms.
|
|
|
|
///
|
|
|
|
/// This may be moved to the alga crate in the future.
|
2020-03-18 00:58:36 +08:00
|
|
|
pub trait Norm<N: SimdComplexField> {
|
2019-02-03 15:33:07 +08:00
|
|
|
/// Apply this norm to the given matrix.
|
2020-03-18 00:58:36 +08:00
|
|
|
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
|
|
|
|
where
|
|
|
|
R: Dim,
|
|
|
|
C: Dim,
|
|
|
|
S: Storage<N, R, C>;
|
2019-02-03 15:33:07 +08:00
|
|
|
/// Use the metric induced by this norm to compute the metric distance between the two given matrices.
|
2020-03-18 00:58:36 +08:00
|
|
|
fn metric_distance<R1, C1, S1, R2, C2, S2>(
|
|
|
|
&self,
|
|
|
|
m1: &Matrix<N, R1, C1, S1>,
|
|
|
|
m2: &Matrix<N, R2, C2, S2>,
|
|
|
|
) -> N::SimdRealField
|
|
|
|
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>;
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Euclidean norm.
|
|
|
|
pub struct EuclideanNorm;
|
|
|
|
/// Lp norm.
|
|
|
|
pub struct LpNorm(pub i32);
|
|
|
|
/// L-infinite norm aka. Chebytchev norm aka. uniform norm aka. suppremum norm.
|
|
|
|
pub struct UniformNorm;
|
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
impl<N: SimdComplexField> Norm<N> for EuclideanNorm {
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
|
|
|
|
where
|
|
|
|
R: Dim,
|
|
|
|
C: Dim,
|
|
|
|
S: Storage<N, R, C>,
|
|
|
|
{
|
|
|
|
m.norm_squared().simd_sqrt()
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
fn metric_distance<R1, C1, S1, R2, C2, S2>(
|
|
|
|
&self,
|
|
|
|
m1: &Matrix<N, R1, C1, S1>,
|
|
|
|
m2: &Matrix<N, R2, C2, S2>,
|
|
|
|
) -> N::SimdRealField
|
|
|
|
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::SimdRealField::zero(), |acc, a, b| {
|
2018-12-09 18:21:24 +08:00
|
|
|
let diff = a - b;
|
2020-03-18 00:58:36 +08:00
|
|
|
acc + diff.simd_modulus_squared()
|
|
|
|
})
|
|
|
|
.simd_sqrt()
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
impl<N: SimdComplexField> Norm<N> for LpNorm {
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
|
|
|
|
where
|
|
|
|
R: Dim,
|
|
|
|
C: Dim,
|
|
|
|
S: Storage<N, R, C>,
|
|
|
|
{
|
|
|
|
m.fold(N::SimdRealField::zero(), |a, b| {
|
|
|
|
a + b.simd_modulus().simd_powi(self.0)
|
|
|
|
})
|
|
|
|
.simd_powf(crate::convert(1.0 / (self.0 as f64)))
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
fn metric_distance<R1, C1, S1, R2, C2, S2>(
|
|
|
|
&self,
|
|
|
|
m1: &Matrix<N, R1, C1, S1>,
|
|
|
|
m2: &Matrix<N, R2, C2, S2>,
|
|
|
|
) -> N::SimdRealField
|
|
|
|
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::SimdRealField::zero(), |acc, a, b| {
|
2018-12-09 18:21:24 +08:00
|
|
|
let diff = a - b;
|
2020-03-18 00:58:36 +08:00
|
|
|
acc + diff.simd_modulus().simd_powi(self.0)
|
|
|
|
})
|
|
|
|
.simd_powf(crate::convert(1.0 / (self.0 as f64)))
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
impl<N: SimdComplexField> Norm<N> for UniformNorm {
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
|
|
|
|
where
|
|
|
|
R: Dim,
|
|
|
|
C: Dim,
|
|
|
|
S: Storage<N, R, C>,
|
|
|
|
{
|
2019-03-23 21:13:00 +08:00
|
|
|
// NOTE: we don't use `m.amax()` here because for the complex
|
|
|
|
// numbers this will return the max norm1 instead of the modulus.
|
2020-03-18 00:58:36 +08:00
|
|
|
m.fold(N::SimdRealField::zero(), |acc, a| {
|
|
|
|
acc.simd_max(a.simd_modulus())
|
|
|
|
})
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
fn metric_distance<R1, C1, S1, R2, C2, S2>(
|
|
|
|
&self,
|
|
|
|
m1: &Matrix<N, R1, C1, S1>,
|
|
|
|
m2: &Matrix<N, R2, C2, S2>,
|
|
|
|
) -> N::SimdRealField
|
|
|
|
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::SimdRealField::zero(), |acc, a, b| {
|
|
|
|
let val = (a - b).simd_modulus();
|
|
|
|
acc.simd_max(val)
|
2018-12-09 18:21:24 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
impl<N: SimdComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
2018-12-09 18:21:24 +08:00
|
|
|
/// The squared L2 norm of this vector.
|
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn norm_squared(&self) -> N::SimdRealField {
|
|
|
|
let mut res = N::SimdRealField::zero();
|
2018-12-09 18:21:24 +08:00
|
|
|
|
|
|
|
for i in 0..self.ncols() {
|
|
|
|
let col = self.column(i);
|
2020-03-18 00:58:36 +08:00
|
|
|
res += col.dotc(&col).simd_real()
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The L2 norm of this matrix.
|
2019-02-03 15:33:07 +08:00
|
|
|
///
|
|
|
|
/// Use `.apply_norm` to apply a custom norm.
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn norm(&self) -> N::SimdRealField {
|
|
|
|
self.norm_squared().simd_sqrt()
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
|
2019-02-03 15:33:07 +08:00
|
|
|
/// Compute the distance between `self` and `rhs` using the metric induced by the euclidean norm.
|
|
|
|
///
|
|
|
|
/// Use `.apply_metric_distance` to apply a custom norm.
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn metric_distance<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>) -> N::SimdRealField
|
|
|
|
where
|
|
|
|
R2: Dim,
|
|
|
|
C2: Dim,
|
|
|
|
S2: Storage<N, R2, C2>,
|
|
|
|
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
|
|
|
|
{
|
2018-12-09 18:21:24 +08:00
|
|
|
self.apply_metric_distance(rhs, &EuclideanNorm)
|
|
|
|
}
|
|
|
|
|
2019-02-03 15:33:07 +08:00
|
|
|
/// Uses the given `norm` to compute the norm of `self`.
|
2019-02-03 18:29:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm};
|
|
|
|
///
|
|
|
|
/// let v = Vector3::new(1.0, 2.0, 3.0);
|
|
|
|
/// assert_eq!(v.apply_norm(&UniformNorm), 3.0);
|
|
|
|
/// assert_eq!(v.apply_norm(&LpNorm(1)), 6.0);
|
|
|
|
/// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm());
|
|
|
|
/// ```
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn apply_norm(&self, norm: &impl Norm<N>) -> N::SimdRealField {
|
2018-12-09 18:21:24 +08:00
|
|
|
norm.norm(self)
|
|
|
|
}
|
|
|
|
|
2019-02-03 15:33:07 +08:00
|
|
|
/// Uses the metric induced by the given `norm` to compute the metric distance between `self` and `rhs`.
|
2019-02-03 18:29:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm};
|
|
|
|
///
|
|
|
|
/// let v1 = Vector3::new(1.0, 2.0, 3.0);
|
|
|
|
/// let v2 = Vector3::new(10.0, 20.0, 30.0);
|
|
|
|
///
|
|
|
|
/// assert_eq!(v1.apply_metric_distance(&v2, &UniformNorm), 27.0);
|
|
|
|
/// assert_eq!(v1.apply_metric_distance(&v2, &LpNorm(1)), 27.0 + 18.0 + 9.0);
|
|
|
|
/// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm());
|
|
|
|
/// ```
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn apply_metric_distance<R2, C2, S2>(
|
|
|
|
&self,
|
|
|
|
rhs: &Matrix<N, R2, C2, S2>,
|
|
|
|
norm: &impl Norm<N>,
|
|
|
|
) -> N::SimdRealField
|
|
|
|
where
|
|
|
|
R2: Dim,
|
|
|
|
C2: Dim,
|
|
|
|
S2: Storage<N, R2, C2>,
|
|
|
|
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
|
|
|
|
{
|
2019-02-23 18:24:07 +08:00
|
|
|
norm.metric_distance(self, rhs)
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A synonym for the norm of this matrix.
|
|
|
|
///
|
|
|
|
/// Aka the length.
|
|
|
|
///
|
|
|
|
/// This function is simply implemented as a call to `norm()`
|
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn magnitude(&self) -> N::SimdRealField {
|
2018-12-09 18:21:24 +08:00
|
|
|
self.norm()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A synonym for the squared norm of this matrix.
|
|
|
|
///
|
|
|
|
/// Aka the squared length.
|
|
|
|
///
|
|
|
|
/// This function is simply implemented as a call to `norm_squared()`
|
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn magnitude_squared(&self) -> N::SimdRealField {
|
2018-12-09 18:21:24 +08:00
|
|
|
self.norm_squared()
|
|
|
|
}
|
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
/// Sets the magnitude of this vector.
|
|
|
|
#[inline]
|
|
|
|
pub fn set_magnitude(&mut self, magnitude: N::SimdRealField)
|
|
|
|
where S: StorageMut<N, R, C> {
|
|
|
|
let n = self.norm();
|
|
|
|
self.scale_mut(magnitude / n)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a normalized version of this matrix.
|
|
|
|
#[inline]
|
|
|
|
#[must_use = "Did you mean to use normalize_mut()?"]
|
|
|
|
pub fn normalize(&self) -> MatrixMN<N, R, C>
|
|
|
|
where DefaultAllocator: Allocator<N, R, C> {
|
|
|
|
self.unscale(self.norm())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The Lp norm of this matrix.
|
|
|
|
#[inline]
|
|
|
|
pub fn lp_norm(&self, p: i32) -> N::SimdRealField {
|
|
|
|
self.apply_norm(&LpNorm(p))
|
|
|
|
}
|
|
|
|
}
|
2020-01-01 22:59:46 +08:00
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
2020-01-01 22:59:46 +08:00
|
|
|
/// Sets the magnitude of this vector unless it is smaller than `min_magnitude`.
|
|
|
|
///
|
|
|
|
/// If `self.magnitude()` is smaller than `min_magnitude`, it will be left unchanged.
|
|
|
|
/// Otherwise this is equivalent to: `*self = self.normalize() * magnitude.
|
|
|
|
#[inline]
|
|
|
|
pub fn try_set_magnitude(&mut self, magnitude: N::RealField, min_magnitude: N::RealField)
|
2020-03-18 00:58:36 +08:00
|
|
|
where S: StorageMut<N, R, C> {
|
2020-01-01 22:59:46 +08:00
|
|
|
let n = self.norm();
|
|
|
|
|
|
|
|
if n >= min_magnitude {
|
|
|
|
self.scale_mut(magnitude / n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-09 18:21:24 +08:00
|
|
|
/// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`.
|
|
|
|
#[inline]
|
2019-06-06 05:04:04 +08:00
|
|
|
#[must_use = "Did you mean to use try_normalize_mut()?"]
|
2019-03-25 18:21:41 +08:00
|
|
|
pub fn try_normalize(&self, min_norm: N::RealField) -> Option<MatrixMN<N, R, C>>
|
2020-03-18 00:58:36 +08:00
|
|
|
where DefaultAllocator: Allocator<N, R, C> {
|
2018-12-09 18:21:24 +08:00
|
|
|
let n = self.norm();
|
|
|
|
|
|
|
|
if n <= min_norm {
|
|
|
|
None
|
|
|
|
} else {
|
2019-03-23 21:13:00 +08:00
|
|
|
Some(self.unscale(n))
|
2018-12-09 18:21:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
impl<N: SimdComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
|
2018-12-09 18:21:24 +08:00
|
|
|
/// Normalizes this matrix in-place and returns its norm.
|
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn normalize_mut(&mut self) -> N::SimdRealField {
|
2018-12-09 18:21:24 +08:00
|
|
|
let n = self.norm();
|
2019-03-23 21:13:00 +08:00
|
|
|
self.unscale_mut(n);
|
2018-12-09 18:21:24 +08:00
|
|
|
|
|
|
|
n
|
|
|
|
}
|
2020-03-18 00:58:36 +08:00
|
|
|
}
|
2018-12-09 18:21:24 +08:00
|
|
|
|
2020-03-18 00:58:36 +08:00
|
|
|
impl<N: ComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
|
2018-12-09 18:21:24 +08:00
|
|
|
/// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`.
|
|
|
|
///
|
2020-01-01 22:59:46 +08:00
|
|
|
/// If the normalization succeeded, returns the old norm of this matrix.
|
2018-12-09 18:21:24 +08:00
|
|
|
#[inline]
|
2020-03-18 00:58:36 +08:00
|
|
|
pub fn try_normalize_mut(&mut self, min_norm: N::RealField) -> Option<N::RealField>
|
|
|
|
where N: ComplexField {
|
2018-12-09 18:21:24 +08:00
|
|
|
let n = self.norm();
|
|
|
|
|
|
|
|
if n <= min_norm {
|
|
|
|
None
|
|
|
|
} else {
|
2019-03-23 21:13:00 +08:00
|
|
|
self.unscale_mut(n);
|
2018-12-09 18:21:24 +08:00
|
|
|
Some(n)
|
|
|
|
}
|
|
|
|
}
|
2019-02-03 17:56:30 +08:00
|
|
|
}
|