use matrix::BaseMatrix;
use vector::Vector;
use utils;
use std::ops::Sub;
use libnum::Float;
pub trait VectorNorm<T> {
fn norm(&self, v: &Vector<T>) -> T;
}
pub trait VectorMetric<T> {
fn metric(&self, v1: &Vector<T>, v2: &Vector<T>) -> T;
}
pub trait MatrixNorm<T, M: BaseMatrix<T>> {
fn norm(&self, m: &M) -> T;
}
pub trait MatrixMetric<'a, 'b, T, M1: 'a + BaseMatrix<T>, M2: 'b + BaseMatrix<T>> {
fn metric(&self, m1: &'a M1, m2: &'b M2) -> T;
}
impl<U, T> VectorMetric<T> for U
where U: VectorNorm<T>, T: Copy + Sub<T, Output=T> {
fn metric(&self, v1: &Vector<T>, v2: &Vector<T>) -> T {
self.norm(&(v1 - v2))
}
}
impl<'a, 'b, U, T, M1, M2> MatrixMetric<'a, 'b, T, M1, M2> for U
where U: MatrixNorm<T, M1>,
M1: 'a + BaseMatrix<T>,
M2: 'b + BaseMatrix<T>,
&'a M1: Sub<&'b M2, Output=M1> {
fn metric(&self, m1: &'a M1, m2: &'b M2) -> T {
self.norm(&(m1 - m2))
}
}
#[derive(Debug)]
pub struct Euclidean;
impl<T: Float> VectorNorm<T> for Euclidean {
fn norm(&self, v: &Vector<T>) -> T {
utils::dot(v.data(), v.data()).sqrt()
}
}
impl<T: Float, M: BaseMatrix<T>> MatrixNorm<T, M> for Euclidean {
fn norm(&self, m: &M) -> T {
let mut s = T::zero();
for row in m.row_iter() {
s = s + utils::dot(row.raw_slice(), row.raw_slice());
}
s.sqrt()
}
}
#[derive(Debug)]
pub enum Lp<T: Float> {
Infinity,
Integer(i32),
Float(T)
}
impl<T: Float> VectorNorm<T> for Lp<T> {
fn norm(&self, v: &Vector<T>) -> T {
match *self {
Lp::Infinity => {
let mut abs_sup = T::zero();
for d in v.iter().map(|d| d.abs()) {
if d > abs_sup {
abs_sup = d;
}
}
abs_sup
},
Lp::Integer(p) => {
assert!(p >= 1, "p value in Lp norm must be >= 1");
let mut s = T::zero();
for x in v {
s = s + x.abs().powi(p);
}
s.powf(T::from(p).expect("Could not cast i32 to float").recip())
},
Lp::Float(p) => {
assert!(p >= T::one(), "p value in Lp norm must be >= 1");
let mut s = T::zero();
for x in v {
s = s + x.abs().powf(p);
}
s.powf(p.recip())
}
}
}
}
impl<T: Float, M: BaseMatrix<T>> MatrixNorm<T, M> for Lp<T> {
fn norm(&self, m: &M) -> T {
match *self {
Lp::Infinity => {
let mut abs_sup = T::zero();
for d in m.iter().map(|d| d.abs()) {
if d > abs_sup {
abs_sup = d;
}
}
abs_sup
},
Lp::Integer(p) => {
assert!(p >= 1, "p value in Lp norm must be >= 1");
let mut s = T::zero();
for x in m.iter() {
s = s + x.abs().powi(p);
}
s.powf(T::from(p).expect("Could not cast i32 to float").recip())
},
Lp::Float(p) => {
assert!(p >= T::one(), "p value in Lp norm must be >= 1");
let mut s = T::zero();
for x in m.iter() {
s = s + x.abs().powf(p);
}
s.powf(p.recip())
}
}
}
}
#[cfg(test)]
mod tests {
use libnum::Float;
use std::f64;
use super::*;
use vector::Vector;
use matrix::{Matrix, MatrixSlice};
#[test]
fn test_euclidean_vector_norm() {
let v = vector![3.0, 4.0];
assert!((VectorNorm::norm(&Euclidean, &v) - 5.0) < 1e-14);
}
#[test]
fn test_euclidean_matrix_norm() {
let m = matrix![3.0, 4.0;
1.0, 3.0];
assert!((MatrixNorm::norm(&Euclidean, &m) - 35.0.sqrt()) < 1e-14);
let slice = MatrixSlice::from_matrix(&m, [0,0], 1, 2);
assert!((MatrixNorm::norm(&Euclidean, &slice) - 5.0) < 1e-14);
}
#[test]
fn test_euclidean_vector_metric() {
let v = vector![3.0, 4.0];
assert!((VectorMetric::metric(&Euclidean, &v, &v)) < 1e-14);
let v1 = vector![0.0, 0.0];
assert!((VectorMetric::metric(&Euclidean, &v, &v1) - 5.0) < 1e-14);
let v2 = vector![4.0, 3.0];
assert!((VectorMetric::metric(&Euclidean, &v, &v2) - 2.0.sqrt()) < 1e-14);
}
#[test]
#[should_panic]
fn test_euclidean_vector_metric_bad_dim() {
let v = vector![3.0, 4.0];
let v2 = vector![1.0, 2.0, 3.0];
VectorMetric::metric(&Euclidean, &v, &v2);
}
#[test]
fn test_euclidean_matrix_metric() {
let m = matrix![3.0, 4.0;
1.0, 3.0];
assert!((MatrixMetric::metric(&Euclidean, &m, &m)) < 1e-14);
let m1 = Matrix::zeros(2, 2);
assert!((MatrixMetric::metric(&Euclidean, &m, &m1) - 35.0.sqrt()) < 1e-14);
let m2 = matrix![2.0, 3.0;
2.0, 4.0];
assert!((MatrixMetric::metric(&Euclidean, &m, &m2) - 2.0) < 1e-14);
}
#[test]
#[should_panic]
fn test_euclidean_matrix_metric_bad_dim() {
let m = matrix![3.0, 4.0];
let m2 = matrix![1.0, 2.0, 3.0];
MatrixMetric::metric(&Euclidean, &m, &m2);
}
#[test]
fn test_lp_vector_supremum() {
let v = vector![-5.0, 3.0];
let sup = VectorNorm::norm(&Lp::Infinity, &v);
assert_eq!(sup, 5.0);
}
#[test]
fn test_lp_matrix_supremum() {
let m = matrix![0.0, -2.0;
3.5, 1.0];
let sup = MatrixNorm::norm(&Lp::Infinity, &m);
assert_eq!(sup, 3.5);
}
#[test]
fn test_lp_vector_one() {
let v = vector![1.0, 2.0, -2.0];
assert_eq!(VectorNorm::norm(&Lp::Integer(1), &v), 5.0);
}
#[test]
fn test_lp_matrix_one() {
let m = matrix![1.0, -2.0;
0.5, 1.0];
assert_eq!(MatrixNorm::norm(&Lp::Integer(1), &m), 4.5);
}
#[test]
fn test_lp_vector_float() {
let v = vector![1.0, 2.0, -2.0];
assert_eq!(VectorNorm::norm(&Lp::Float(1.0), &v), 5.0);
}
#[test]
fn test_lp_matrix_float() {
let m = matrix![1.0, -2.0;
0.5, 1.0];
assert_eq!(MatrixNorm::norm(&Lp::Float(1.0), &m), 4.5);
}
#[test]
#[should_panic]
fn test_lp_vector_bad_p() {
let v = Vector::new(vec![]);
VectorNorm::norm(&Lp::Float(0.5), &v);
}
#[test]
#[should_panic]
fn test_lp_matrix_bad_p() {
let m = matrix![];
MatrixNorm::norm(&Lp::Float(0.5), &m);
}
#[test]
#[should_panic]
fn test_lp_vector_bad_int_p() {
let v: Vector<f64> = Vector::new(vec![]);
VectorNorm::norm(&Lp::Integer(0), &v);
}
#[test]
#[should_panic]
fn test_lp_matrix_bad_int_p() {
let m: Matrix<f64> = matrix![];
MatrixNorm::norm(&Lp::Integer(0), &m);
}
}