hipparchus_metrics/
vector.rs

1use hipparchus_mean::Fp;
2use crate::metrics::Metrics;
3
4/// Trait for simularity metrics between two vectors
5#[repr(i32)]
6#[derive(Clone,PartialEq,Debug)]
7pub enum VectorMetrics
8{
9    /// Dot product
10    DotProduct = 1,
11
12    /// Cosine similarity
13    Cosine = 2,
14}
15
16impl<T:Fp> Metrics<&[T], T> for VectorMetrics
17{
18    fn measure(self, x:&[T], y:&[T]) -> T
19    {
20        let it = x.iter().zip(y.iter());
21        match self
22        {
23            VectorMetrics::DotProduct => it.fold( T::zero(), | agg, (&a, &b) | agg + a * b),
24            VectorMetrics::Cosine => 
25            {
26                let mut aa = T::zero();
27                let mut bb = T::zero();
28                let ab = it.fold( T::zero(), | agg, (&a, &b) |
29                {
30                    aa = aa + a * a;
31                    bb = bb + b * b;
32                    agg + a * b
33                });
34                ab / (aa.sqrt() * bb.sqrt())
35            },
36        }
37    }
38}
39
40#[cfg(test)]
41mod tests 
42{
43    use super::*;
44    use float_cmp::assert_approx_eq;
45    use hipparchus_mean::LpNorm;
46
47    #[test]
48    fn test_dotproduct_overlap()
49    {
50        let v = 1.0;
51        let x = [v, v];
52        let y = [2.0 * v, 2.0 * v];
53        let l2x = x.iter().l2norm().unwrap();
54        let l2y = y.iter().l2norm().unwrap();
55        let expected =  l2x * l2y;
56        assert_approx_eq!(f32, expected, VectorMetrics::DotProduct.measure(&x, &y));
57        assert_approx_eq!(f32, expected, VectorMetrics::DotProduct.measure(&y, &x));
58    }
59
60    #[test]
61    fn test_dotproduct_orthogonal()
62    {
63        let v = 1.0;
64        let x = [v, v];
65        let y = [v, -v];
66        let expected = 0.0;
67        assert_approx_eq!(f32, expected, VectorMetrics::DotProduct.measure(&x, &y));
68        assert_approx_eq!(f32, expected, VectorMetrics::DotProduct.measure(&y, &x));
69    }
70
71    #[test]
72    fn test_cosine_opposite()
73    {
74        let v = 1.0;
75        let x = [v, v];
76        let y = [-v, -v];
77        let expected = f32::cos(180.0f32.to_radians());
78        assert_approx_eq!(f32, expected, VectorMetrics::Cosine.measure(&x, &y));
79        assert_approx_eq!(f32, expected, VectorMetrics::Cosine.measure(&x, &y));
80    }
81
82    #[test]
83    fn test_cosine_overlap()
84    {
85        let v = 1.0;
86        let x = [v, v];
87        let y = [2.0 * v, 2.0 * v];
88        let expected = f32::cos(0.0f32.to_radians());
89        assert_approx_eq!(f32, expected, VectorMetrics::Cosine.measure(&x, &y));
90        assert_approx_eq!(f32, expected, VectorMetrics::Cosine.measure(&y, &x));
91    }
92
93    #[test]
94    fn test_cosine_eq()
95    {
96        let x = [1.0, 2.0];
97        let expected = f32::cos(0.0f32.to_radians());
98        assert_approx_eq!(f32, expected, VectorMetrics::Cosine.measure(&x, &x));
99    }
100}