rig_core/embeddings/
distance.rs1pub trait VectorDistance {
17 fn dot_product(&self, other: &Self) -> f64;
19
20 fn cosine_similarity(&self, other: &Self, normalized: bool) -> f64;
23
24 fn angular_distance(&self, other: &Self, normalized: bool) -> f64;
26
27 fn euclidean_distance(&self, other: &Self) -> f64;
29
30 fn manhattan_distance(&self, other: &Self) -> f64;
32
33 fn chebyshev_distance(&self, other: &Self) -> f64;
35}
36
37const CHUNK: usize = 256;
40
41macro_rules! impl_vector_distance {
44 ($pair_sum:ident, $unary_sum:ident, $pair_max:ident) => {
45 fn dot_product(&self, other: &Self) -> f64 {
46 $pair_sum(&self.vec, &other.vec, |x, y| x * y)
47 }
48
49 fn cosine_similarity(&self, other: &Self, normalized: bool) -> f64 {
50 let dot_product = self.dot_product(other);
51
52 if normalized {
53 dot_product
54 } else {
55 let magnitude1: f64 = $unary_sum(&self.vec, |x| x.powi(2)).sqrt();
56 let magnitude2: f64 = $unary_sum(&other.vec, |x| x.powi(2)).sqrt();
57
58 dot_product / (magnitude1 * magnitude2)
59 }
60 }
61
62 fn angular_distance(&self, other: &Self, normalized: bool) -> f64 {
63 let cosine_sim = self.cosine_similarity(other, normalized);
64 cosine_sim.clamp(-1.0, 1.0).acos() / std::f64::consts::PI
66 }
67
68 fn euclidean_distance(&self, other: &Self) -> f64 {
69 $pair_sum(&self.vec, &other.vec, |x, y| (x - y).powi(2)).sqrt()
70 }
71
72 fn manhattan_distance(&self, other: &Self) -> f64 {
73 $pair_sum(&self.vec, &other.vec, |x, y| (x - y).abs())
74 }
75
76 fn chebyshev_distance(&self, other: &Self) -> f64 {
77 $pair_max(&self.vec, &other.vec, |x, y| (x - y).abs())
78 }
79 };
80}
81
82mod sequential {
83 use super::{CHUNK, VectorDistance};
84 use crate::embeddings::Embedding;
85
86 fn pair_sum(a: &[f64], b: &[f64], term: impl Fn(f64, f64) -> f64) -> f64 {
88 a.chunks(CHUNK)
89 .zip(b.chunks(CHUNK))
90 .map(|(a, b)| a.iter().zip(b).map(|(x, y)| term(*x, *y)).sum::<f64>())
91 .sum()
92 }
93
94 fn unary_sum(a: &[f64], term: impl Fn(f64) -> f64) -> f64 {
95 a.chunks(CHUNK)
96 .map(|a| a.iter().map(|x| term(*x)).sum::<f64>())
97 .sum()
98 }
99
100 fn pair_max(a: &[f64], b: &[f64], term: impl Fn(f64, f64) -> f64) -> f64 {
101 a.iter()
102 .zip(b)
103 .map(|(x, y)| term(*x, *y))
104 .fold(0.0, f64::max)
105 }
106
107 impl VectorDistance for Embedding {
108 impl_vector_distance!(pair_sum, unary_sum, pair_max);
109 }
110}
111
112#[cfg(test)]
113mod tests;