ruvector_memopt/neural/
hnsw_patterns.rs1use std::cmp::Ordering;
4
5pub struct PatternIndex {
7 dim: usize,
8 vectors: Vec<Vec<f32>>,
9 m: usize,
10 ef_construction: usize,
11}
12
13#[derive(Clone, Copy)]
14struct Candidate {
15 id: usize,
16 distance: f32,
17}
18
19impl PartialEq for Candidate {
20 fn eq(&self, other: &Self) -> bool {
21 self.distance == other.distance
22 }
23}
24
25impl Eq for Candidate {}
26
27impl PartialOrd for Candidate {
28 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
29 other.distance.partial_cmp(&self.distance)
30 }
31}
32
33impl Ord for Candidate {
34 fn cmp(&self, other: &Self) -> Ordering {
35 self.partial_cmp(other).unwrap_or(Ordering::Equal)
36 }
37}
38
39impl PatternIndex {
40 pub fn new(dim: usize) -> Result<Self, String> {
41 if dim == 0 {
42 return Err("Dimension must be > 0".into());
43 }
44
45 Ok(Self {
46 dim,
47 vectors: Vec::new(),
48 m: 16,
49 ef_construction: 200,
50 })
51 }
52
53 pub fn add(&mut self, vector: &[f32]) -> Result<usize, String> {
54 if vector.len() != self.dim {
55 return Err(format!("Expected dim {}, got {}", self.dim, vector.len()));
56 }
57
58 let id = self.vectors.len();
59 self.vectors.push(vector.to_vec());
60 Ok(id)
61 }
62
63 pub fn search(&self, query: &[f32], k: usize) -> Result<Vec<(usize, f32)>, String> {
65 if query.len() != self.dim {
66 return Err(format!("Expected dim {}, got {}", self.dim, query.len()));
67 }
68
69 let mut results: Vec<(usize, f32)> = self.vectors
70 .iter()
71 .enumerate()
72 .map(|(i, v)| (i, self.distance(query, v)))
73 .collect();
74
75 results.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal));
76 Ok(results.into_iter().take(k).collect())
77 }
78
79 fn distance(&self, a: &[f32], b: &[f32]) -> f32 {
80 a.iter()
81 .zip(b.iter())
82 .map(|(x, y)| (x - y).powi(2))
83 .sum::<f32>()
84 .sqrt()
85 }
86
87 pub fn len(&self) -> usize {
88 self.vectors.len()
89 }
90
91 pub fn is_empty(&self) -> bool {
92 self.vectors.is_empty()
93 }
94}