Files
gitlore/src/embedding/similarity.rs
teernisse c2bdc01eac feat: implement 5 CLI-IMP beads — hybrid search, robot-docs, data gaps, drift, skill rewrite
Wire hybrid search (FTS5 + vector + RRF) to CLI search command.
Enhance robot-docs with quick_start guide and example_output.
Fill data gaps in issue detail (references, note counts, closed_at).
Add lore drift command for discussion divergence detection.
Rewrite agent skills to mandate lore for reads, glab for writes.

Closes: bd-1ksf, bd-91j1, bd-2g50, bd-1cjx, bd-kvij
2026-02-12 11:51:02 -05:00

49 lines
1.4 KiB
Rust

/// Cosine similarity between two embedding vectors.
/// Returns value in [-1, 1] range; higher = more similar.
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "embedding dimensions must match");
let dot: f32 = a.iter().zip(b).map(|(x, y)| x * y).sum();
let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm_a == 0.0 || norm_b == 0.0 {
return 0.0;
}
dot / (norm_a * norm_b)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cosine_similarity_identical() {
let v = [1.0, 2.0, 3.0];
let sim = cosine_similarity(&v, &v);
assert!((sim - 1.0).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_orthogonal() {
let a = [1.0, 0.0, 0.0];
let b = [0.0, 1.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert!(sim.abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_zero_vector() {
let a = [1.0, 2.0, 3.0];
let b = [0.0, 0.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert!((sim - 0.0).abs() < 1e-6);
}
#[test]
fn test_cosine_similarity_opposite() {
let a = [1.0, 2.0, 3.0];
let b = [-1.0, -2.0, -3.0];
let sim = cosine_similarity(&a, &b);
assert!((sim - (-1.0)).abs() < 1e-6);
}
}