norman 0.0.4

Implementations of different norms for elements of vector spaces
Documentation
/******************************************************************************
 * Copyright 2019 Manuel Simon
 * This file is part of the norman library.
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 *****************************************************************************/

#![doc(html_root_url = "https://docs.rs/norman/0.0.4")]

//! The norman library provides everything you need for calculationg norms of
//! or general distances between elements of vector spaces.
//!
//! Based on two traits—[`Norm`](crate::Norm) and [`Distance`](crate::Distance)
//!—this crate implements different kinds of norm and distance
//! functions for a wide variety of types—including complex numbers and arrays.
//!
//! # Usage
//!
//! If you only want to compute the _standard norm_ of an element,
//! like the absolute value of a floating point number or the euclidean
//! norm of a vector, then you can use the traits [`NormEucl`](special::NormEucl)
//! or [`DistanceEucl`](special::DistanceEucl) without specifying a certain
//! type of norm:
//!
//! ```
//! use ndarray::Array1;
//!
//! use norman::special::NormEucl;
//!
//! let a = Array1::from(vec![2.0f32, -4.0, -2.0]);
//!
//! assert_eq!(a.norm_eucl(), (2.0f32*2.0 + 4.0*4.0 + 2.0*2.0).sqrt());
//! ```
//!
//! For a detailed description on how these traits are implemented,
//! see the module documentation of [`special::implementation`].
//!
//! However, there are many ways to define a norm on a vector.
//! If you want more control over the exact kind of norm or distance function used,
//! the full [`Norm`] and [`Distance`] traits are the right ones for you.
//!
//! These traits have a generic parameter `D`, the
//! _descriptor_ of the norm or distance function. This makes it possible
//! to implement not only a single type of norm function for a type, but
//! multiple ones.
//!
//! E.g. [`ndarray::ArrayBase`] implements [`Norm<Sup>`](crate::desc::Sup),
//! which yields the spuremum norm, and [`Norm<PNorm>`](crate::desc::PNorm)
//! which yields the _p_-norm.
//!
//! ```
//! use ndarray::Array1;
//!
//! use norman::Norm;
//! use norman::desc::{Sup, PNorm};
//!
//! let a = Array1::from(vec![2.0f32, -4.0, -2.0]);
//!
//! assert_eq!(a.norm(Sup::new()), 4.0);
//! assert_eq!(a.norm(PNorm::new(2)), (2.0f32*2.0 + 4.0*4.0 + 2.0*2.0).sqrt());
//! assert_eq!(a.norm(PNorm::new(1)), 2.0f32 + 4.0 + 2.0);
//! ```
//!
//! You see: The norm-function recieves one additional parameter which further
//! describes the norm. There is only one supremum norm, so this one needs no
//! further description—we will always call it with
//! [`Sup::new()`](crate::desc::Sup::new).
//!
//! But the _p_-norms do need additional specification: We need to specify
//! whether we want a 1-norm or a 2-norm. So we pass `PNorm::new(2)`
//! or `PNorm::new(1)` as the additional parameter.
//!
//! These norms are implemented on [`ndarray::ArrayBase`](ndarray::ArrayBase)
//! as long as the elments of the array implement [`Norm<Abs>`](crate::desc::Abs):
//!
//! ```
//! use num_complex::Complex;
//! use ndarray::Array1;
//!
//! use norman::Norm;
//! use norman::desc::{Sup, PNorm};
//!
//! let a = Array1::from(vec![
//!     Complex::new(- 2.0,  0.0),
//!     Complex::new(  3.0,  4.0),
//!     Complex::new(-15.0,  8.0),
//! ]);
//!
//! assert_eq!(a.norm(Sup::new()), 17.0);
//! assert_eq!(
//!     a.norm(PNorm::new(2)),
//!     (2.0f32*2.0 + 0.0*0.0 + 3.0*3.0 + 4.0*4.0 + 15.0*15.0 + 8.0*8.0).sqrt()
//! );
//! assert_eq!(a.norm(PNorm::new(1)), (2.0f32 + 5.0 + 17.0));
//! ```
//!
//! # The [`Distance`](crate::Distance) trait
//!
//! In many cases, you do not want to calculate the norm of a single value,
//! but you have to retrieve the distance between two values. This
//! would often be possible by calling `(a-b).norm(norm_descriptor)`,
//! but e.g. for ndarrays this would imply calculating the difference
//! and once storing it in a new ndarray, which will cause one unnecessary
//! memory allocation. Instead you can use the `Distance`
//! trait, which calculates the same result without storing the intermediate
//! difference of the arrays:
//!
//! ```
//! use ndarray::Array1;
//!
//! use norman::{Norm, Distance};
//! use norman::desc::{Sup, PNorm};
//!
//! let a = Array1::from(vec![ 2.0f32, -4.0, -2.0]);
//! let b = Array1::from(vec![ 6.0f32, -1.0,  4.0]);
//!
//! assert_eq!(a.distance(&b, Sup::new()), (a.clone()-&b).norm(Sup::new()));
//! assert_eq!(a.distance(&b, PNorm::new(2)), (a.clone()-b).norm(PNorm::new(2)));
//! ```
//!
//! For a detailed description of how norms and distances are implemented
//! on various types, see the module documentation of [`implementation`].
//!
//! # Accessing the implementations
//!
//! The implementations on types of external crates are behind feature gates,
//! which have the same name as the crate. E.g. in order to use the `Norm`
//! trait on an [`ndarray::Array1`], `norman` must be included with the feature
//! "ndarray", i.e. the corresponding part of your Cargo.toml would look like:
//!
//! ```toml
//! norman = { version = "0.0.4", features = ["ndarray"] }
//! ```
//!
//! However, [`ndarray`] and [`num_complex`] are declared as default features,
//! so they do not need to be named explicitly.
//!
//! ## All crate features
//!
//! * `num-complex`: Unlocks the implementations on [`num_complex::Complex`].
//! * `ndarray`: Unlocks the implementations on [`ndarray::ArrayBase`].
//! * `array`: Unlocks the implementations on the array types [T; N] for
//! N=0 to N=32.
//! If [const generics](https://github.com/rust-lang/rust/issues/44580)
//! land some day, this feature gate will probably be removed.

pub mod desc;
pub mod implementation;
pub mod special;
mod utility;

use std::ops::{Div, DivAssign};

use num_traits::Num;

/// The `Norm` trait is the core of the `norman` crate.
///
/// It provides a [`norm`](Norm::norm) function which calculates a specific
/// norm of the vector.
///
/// The type `D` is the norm descriptor, which specifies the exact kind of norm; e.g.
/// a supremum norm or a euclidean norm. See the [`desc`](crate::desc) module
/// for several different norm descriptors.
pub trait Norm<D> {
    /// The resulting type of the norm function.
    ///
    /// Mathematically, a norm is a mapping from a vector space _V_ into the non-negative
    /// real numbers, so `Output` will usually be a floating point type
    /// or in some cases an unsigned integer type.
    type Output: Num;

    /// Calculates the norm of `self`, specified by the descriptor `desc`.
    ///
    /// # Panics
    ///
    /// An implementation of `norm` should never panic.
    ///
    /// An exception may be made for types like the [`noisy_float`]
    /// floating point types that already have a special panicking behaviour
    /// to ensure that no invalid values occur.
    ///
    /// # Example
    ///
    /// ```
    /// use num_complex::Complex;
    ///
    /// use norman::Norm;
    /// use norman::desc::Abs;
    ///
    /// assert_eq!(Norm::norm(&Complex::new(3.0, 4.0), Abs::new()), 5.0);
    /// ```
    fn norm(&self, desc: D) -> Self::Output;
}

/// The abstract notion of the distance between two values.
///
/// This can be used to calculate the distance between two arbitrary
/// values without storing their difference as an intermediate result.
pub trait Distance<D> {
    /// The resulting type of the distance function.
    ///
    /// Mathematically, a distance metric is a mapping
    /// from 2-tuples of vectors of a vector space _V_
    /// into the non-negative real numbers, so `Output` will usually be a floating point type
    /// or in some cases an unsigned integer type.
    type Output: Num;

    /// Calculates the distance between `self` and `other`.
    ///
    /// # Panics
    ///
    /// An implementation of `distance` may panic if the operands
    /// do not fit together, e.g. have different sizes etc.
    ///
    /// # Example
    ///
    /// ```
    /// use num_complex::Complex;
    ///
    /// use norman::Distance;
    /// use norman::desc::Abs;
    ///
    /// assert_eq!(Complex::new(2.0, 5.0).distance(&Complex::new(-1.0, 1.0), Abs::new()), 5.0);
    /// ```
    fn distance(&self, other: &Self, desc: D) -> Self::Output;
}


/// Normalizes the vector `v` according to the norm `desc`,
/// i.e. divides it by its norm.
///
/// As long as the implementations of `Div` and `DivAssign` on `T` match,
/// `v` will be equal to `normalized(v)` after calling this function.
///
/// # Attention
///
/// Due to numerical errors, `v` is **not** guaranteed to have exactly norm `1`
/// after calling this function.
///
/// On integer types this function will do complete nonsense since
/// `DivAssign` is implemented as an integer division for integers.
///
/// # Example
///
/// ```
/// use norman::normalize;
/// use norman::desc::Abs;
///
/// let mut a = 0.25f32;
/// normalize(&mut a, Abs::new());
/// assert_eq!(a, 1.0);
/// let mut a = -3.0f32;
/// normalize(&mut a, Abs::new());
/// assert_eq!(a, -1.0);
/// ```
pub fn normalize<T: Norm<D, Output=R> + DivAssign<R>, R: Num, D>(v: &mut T, desc: D) {
    *v /= v.norm(desc);
}

/// Returns the normalization of `v` according to the norm `desc`,
/// i.e. `v` divided by its norm.
///
/// # Attention
///
/// Due to numerical errors, the result is **not** guaranteed to have exactly norm `1`
/// after calling this function.
///
/// On integer types this function will do complete nonsense since
/// `Div` is implemented as an integer division for integers.
///
/// # Example
///
/// ```
/// use norman::normalized;
/// use norman::desc::Abs;
///
/// assert_eq!(normalized( 0.25f32, Abs::new()),  1.0);
/// assert_eq!(normalized(-3.00f32, Abs::new()), -1.0);
/// ```
pub fn normalized<T: Norm<D, Output=R> + Div<R, Output=T>, D, R: Num>(v: T, desc: D) -> T {
    let norm = v.norm(desc);
    v / norm
}


/*impl<T: Copy + Norm<D> + Sub<Self, Output=Self>, D> Distance<D> for T {
    type Output = <Self as Norm<D>>::Output;
    fn distance(&self, other: &Self, desc: D) -> <Self as Distance<D>>::Output {
        (*self - *other).norm(desc)
    }
}*/