construct/memory/
vector.rs1pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
8 if a.len() != b.len() || a.is_empty() {
9 return 0.0;
10 }
11
12 let mut dot = 0.0_f64;
13 let mut norm_a = 0.0_f64;
14 let mut norm_b = 0.0_f64;
15
16 for (x, y) in a.iter().zip(b.iter()) {
17 let x = f64::from(*x);
18 let y = f64::from(*y);
19 dot += x * y;
20 norm_a += x * x;
21 norm_b += y * y;
22 }
23
24 let denom = norm_a.sqrt() * norm_b.sqrt();
25 if !denom.is_finite() || denom < f64::EPSILON {
26 return 0.0;
27 }
28
29 let raw = dot / denom;
30 if !raw.is_finite() {
31 return 0.0;
32 }
33
34 #[allow(clippy::cast_possible_truncation)]
36 let sim = raw.clamp(0.0, 1.0) as f32;
37 sim
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn cosine_identical_vectors() {
46 let v = vec![1.0, 2.0, 3.0];
47 let sim = cosine_similarity(&v, &v);
48 assert!((sim - 1.0).abs() < 0.001);
49 }
50
51 #[test]
52 fn cosine_orthogonal_vectors() {
53 let a = vec![1.0, 0.0, 0.0];
54 let b = vec![0.0, 1.0, 0.0];
55 let sim = cosine_similarity(&a, &b);
56 assert!(sim.abs() < 0.001);
57 }
58
59 #[test]
60 fn cosine_empty_returns_zero() {
61 assert_eq!(cosine_similarity(&[], &[]), 0.0);
62 }
63}