Skip to main content

embedded_dsp/
distance.rs

1//! Distance metrics between vectors (Euclidean, Cosine, Chebyshev, Manhattan, Minkowski, Jaccard, Hamming, Canberra, Bray-Curtis).
2
3#[allow(unused_imports)]
4use crate::math::FloatMath;
5
6/// Euclidean distance: `sqrt(sum((a_i - b_i)^2))`
7pub fn euclidean_distance_f32(a: &[f32], b: &[f32]) -> f32 {
8    let len = a.len().min(b.len());
9    let mut sum_sq = 0.0f32;
10    for i in 0..len {
11        let diff = a[i] - b[i];
12        sum_sq += diff * diff;
13    }
14    sum_sq.sqrt()
15}
16
17/// Cosine distance: `1 - (a . b) / (||a|| * ||b||)`
18pub fn cosine_distance_f32(a: &[f32], b: &[f32]) -> f32 {
19    let len = a.len().min(b.len());
20    let mut dot = 0.0f32;
21    let mut norm_a = 0.0f32;
22    let mut norm_b = 0.0f32;
23
24    for i in 0..len {
25        dot += a[i] * b[i];
26        norm_a += a[i] * a[i];
27        norm_b += b[i] * b[i];
28    }
29    let denom = norm_a.sqrt() * norm_b.sqrt();
30    if denom != 0.0 {
31        1.0 - (dot / denom)
32    } else {
33        1.0
34    }
35}
36
37/// Chebyshev distance: `max(|a_i - b_i|)`
38pub fn chebyshev_distance_f32(a: &[f32], b: &[f32]) -> f32 {
39    let len = a.len().min(b.len());
40    let mut max_diff = 0.0f32;
41    for i in 0..len {
42        let diff = (a[i] - b[i]).abs();
43        if diff > max_diff {
44            max_diff = diff;
45        }
46    }
47    max_diff
48}
49
50/// Manhattan distance: `sum(|a_i - b_i|)`
51pub fn manhattan_distance_f32(a: &[f32], b: &[f32]) -> f32 {
52    let len = a.len().min(b.len());
53    let mut sum = 0.0f32;
54    for i in 0..len {
55        sum += (a[i] - b[i]).abs();
56    }
57    sum
58}
59
60/// Minkowski distance: `(sum(|a_i - b_i|^p))^(1/p)`
61pub fn minkowski_distance_f32(a: &[f32], b: &[f32], p: f32) -> f32 {
62    let len = a.len().min(b.len());
63    let mut sum = 0.0f32;
64    for i in 0..len {
65        let diff = (a[i] - b[i]).abs();
66        sum += diff.powf(p);
67    }
68    sum.powf(1.0 / p)
69}
70
71/// Jaccard distance for boolean/binary vectors.
72pub fn jaccard_distance_f32(a: &[f32], b: &[f32]) -> f32 {
73    let len = a.len().min(b.len());
74    let mut tf = 0.0f32;
75    let mut tt = 0.0f32;
76
77    for i in 0..len {
78        let a_bool = a[i] != 0.0;
79        let b_bool = b[i] != 0.0;
80        if a_bool && b_bool {
81            tt += 1.0;
82        } else if a_bool || b_bool {
83            tf += 1.0;
84        }
85    }
86    if tt + tf > 0.0 {
87        tf / (tt + tf)
88    } else {
89        0.0
90    }
91}
92
93/// Hamming distance between float vectors (count of mismatched elements).
94pub fn hamming_distance_f32(a: &[f32], b: &[f32]) -> f32 {
95    let len = a.len().min(b.len());
96    let mut diff_count = 0.0f32;
97    for i in 0..len {
98        if a[i] != b[i] {
99            diff_count += 1.0;
100        }
101    }
102    diff_count
103}
104
105/// Canberra distance: `sum(|a_i - b_i| / (|a_i| + |b_i|))`
106pub fn canberra_distance_f32(a: &[f32], b: &[f32]) -> f32 {
107    let len = a.len().min(b.len());
108    let mut sum = 0.0f32;
109    for i in 0..len {
110        let num = (a[i] - b[i]).abs();
111        let denom = a[i].abs() + b[i].abs();
112        if denom != 0.0 {
113            sum += num / denom;
114        }
115    }
116    sum
117}
118
119/// Bray-Curtis distance: `sum(|a_i - b_i|) / sum(|a_i + b_i|)`
120pub fn bray_curtis_distance_f32(a: &[f32], b: &[f32]) -> f32 {
121    let len = a.len().min(b.len());
122    let mut num = 0.0f32;
123    let mut denom = 0.0f32;
124    for i in 0..len {
125        num += (a[i] - b[i]).abs();
126        denom += (a[i] + b[i]).abs();
127    }
128    if denom != 0.0 {
129        num / denom
130    } else {
131        0.0
132    }
133}