1use crate::error::{AgentDbError, Result};
2use rand::RngExt;
3use serde::{Deserialize, Serialize};
4use std::cmp::Reverse;
5use std::collections::{BinaryHeap, HashMap, HashSet};
6
7#[derive(Clone, Copy)]
8struct OrdF32(f32);
9impl PartialEq for OrdF32 {
10 fn eq(&self, other: &Self) -> bool {
11 self.0.total_cmp(&other.0) == std::cmp::Ordering::Equal
12 }
13}
14impl Eq for OrdF32 {}
15impl PartialOrd for OrdF32 {
16 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
17 Some(self.cmp(other))
18 }
19}
20impl Ord for OrdF32 {
21 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
22 self.0.total_cmp(&other.0)
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum DistanceMetric {
29 Cosine,
30 Euclidean,
31 DotProduct,
32}
33
34fn cosine_distance(a: &[f32], b: &[f32]) -> f32 {
35 let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
36 let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
37 let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
38 if norm_a == 0.0 || norm_b == 0.0 {
39 return 1.0;
40 }
41 1.0 - (dot / (norm_a * norm_b))
42}
43
44fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
45 a.iter()
46 .zip(b.iter())
47 .map(|(x, y)| (x - y).powi(2))
48 .sum::<f32>()
49 .sqrt()
50}
51
52fn dot_product_distance(a: &[f32], b: &[f32]) -> f32 {
53 let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
57 -dot
58}
59
60pub fn dist(a: &[f32], b: &[f32], metric: &DistanceMetric) -> f32 {
61 match metric {
62 DistanceMetric::Cosine => cosine_distance(a, b),
63 DistanceMetric::Euclidean => euclidean_distance(a, b),
64 DistanceMetric::DotProduct => dot_product_distance(a, b),
65 }
66}
67
68#[derive(Serialize, Deserialize)]
70pub struct HnswIndex {
71 m: usize,
72 ef_construction: usize,
73 vectors: Vec<Vec<f32>>,
74 id_map: HashMap<String, usize>,
75 rev_map: Vec<String>,
76 layers: Vec<HashMap<usize, Vec<usize>>>,
77 entry_point: Option<usize>,
78 metric: DistanceMetric,
79}
80
81impl HnswIndex {
82 pub fn new(m: usize, ef_construction: usize, metric: DistanceMetric) -> Self {
83 Self {
84 m,
85 ef_construction,
86 vectors: Vec::new(),
87 id_map: HashMap::new(),
88 rev_map: Vec::new(),
89 layers: Vec::new(),
90 entry_point: None,
91 metric,
92 }
93 }
94
95 fn random_level(&self) -> usize {
96 let mut rng = rand::rng();
97 let m_l = 1.0 / (self.m as f64).ln();
98 let level = (-rng.random::<f64>().ln() * m_l).floor() as usize;
99 level.min(16)
100 }
101
102 pub fn insert(&mut self, id: &str, vector: Vec<f32>) {
103 if let Some(&idx) = self.id_map.get(id) {
104 self.vectors[idx] = vector;
105 return;
106 }
107 let idx = self.vectors.len();
108 self.vectors.push(vector);
109 self.id_map.insert(id.to_string(), idx);
110 self.rev_map.push(id.to_string());
111
112 let level = self.random_level();
113 while self.layers.len() <= level {
114 self.layers.push(HashMap::new());
115 }
116 for l in 0..=level {
117 self.layers[l].insert(idx, Vec::new());
118 }
119
120 if let Some(ep) = self.entry_point {
121 let max_l = level.min(self.layers.len().saturating_sub(1));
122 for l in (0..=max_l).rev() {
123 let neighbours = self.search_layer_for(idx, ep, self.m, l);
124 if let Some(layer) = self.layers.get_mut(l) {
125 if let Some(nn) = layer.get_mut(&idx) {
126 *nn = neighbours.iter().map(|&(i, _)| i).collect();
127 }
128 for &(ni, _) in &neighbours {
129 if let Some(nlist) = layer.get_mut(&ni) {
130 nlist.push(idx);
131 if nlist.len() > self.m * 2 {
132 nlist.truncate(self.m * 2);
133 }
134 }
135 }
136 }
137 }
138 }
139
140 if self.entry_point.is_none() || level >= self.layers.len().saturating_sub(1) {
141 self.entry_point = Some(idx);
142 }
143 }
144
145 fn search_layer_for(
146 &self,
147 query_idx: usize,
148 entry: usize,
149 k: usize,
150 level: usize,
151 ) -> Vec<(usize, f32)> {
152 let query = self.vectors[query_idx].clone();
153 self.search_layer_vec(&query, entry, k, level)
154 }
155
156 fn search_layer_vec(
157 &self,
158 query: &[f32],
159 entry: usize,
160 k: usize,
161 level: usize,
162 ) -> Vec<(usize, f32)> {
163 let mut visited: HashSet<usize> = HashSet::new();
164 let mut candidates: BinaryHeap<Reverse<(OrdF32, usize)>> = BinaryHeap::new();
166 let mut result: BinaryHeap<(OrdF32, usize)> = BinaryHeap::new();
168
169 let d0 = dist(query, &self.vectors[entry], &self.metric);
170 candidates.push(Reverse((OrdF32(d0), entry)));
171 result.push((OrdF32(d0), entry));
172 visited.insert(entry);
173
174 while let Some(Reverse((OrdF32(d_curr), curr))) = candidates.pop() {
175 if let Some(&(OrdF32(worst), _)) = result.peek() {
176 if d_curr > worst && result.len() >= k {
177 break;
178 }
179 }
180 if let Some(layer) = self.layers.get(level) {
181 if let Some(neighbours) = layer.get(&curr) {
182 for &nb in neighbours {
183 if visited.insert(nb) {
184 let nd = dist(query, &self.vectors[nb], &self.metric);
185 candidates.push(Reverse((OrdF32(nd), nb)));
186 result.push((OrdF32(nd), nb));
187 while result.len() > k * 2 {
188 result.pop();
189 }
190 }
191 }
192 }
193 }
194 }
195
196 let mut out: Vec<(usize, f32)> = result.into_iter().map(|(OrdF32(d), i)| (i, d)).collect();
197 out.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
198 out.truncate(k);
199 out
200 }
201
202 pub fn search(&self, query: &[f32], k: usize) -> Vec<(String, f32)> {
203 let mut ep = match self.entry_point {
204 Some(e) => e,
205 None => return vec![],
206 };
207 let num_layers = self.layers.len();
208 if num_layers == 0 {
209 return vec![];
210 }
211
212 for l in (1..num_layers).rev() {
213 let mut improved = true;
214 while improved {
215 improved = false;
216 if let Some(layer) = self.layers.get(l) {
217 if let Some(neighbours) = layer.get(&ep) {
218 let d_ep = dist(query, &self.vectors[ep], &self.metric);
219 for &nb in neighbours {
220 let d_nb = dist(query, &self.vectors[nb], &self.metric);
221 if d_nb < d_ep {
222 ep = nb;
223 improved = true;
224 break;
225 }
226 }
227 }
228 }
229 }
230 }
231
232 let ef = k.max(self.ef_construction);
233 let raw = self.search_layer_vec(query, ep, ef, 0);
234
235 raw.into_iter()
236 .take(k)
237 .map(|(idx, d)| (self.rev_map[idx].clone(), d))
238 .collect()
239 }
240
241 pub fn serialize(&self) -> Result<Vec<u8>> {
242 bincode::serde::encode_to_vec(self, bincode::config::standard())
243 .map_err(|e| AgentDbError::Serialization(e.to_string()))
244 }
245
246 pub fn deserialize(bytes: &[u8]) -> Result<Self> {
247 bincode::serde::decode_from_slice(bytes, bincode::config::standard())
248 .map(|(val, _)| val)
249 .map_err(|e| AgentDbError::Serialization(e.to_string()))
250 }
251
252 pub fn len(&self) -> usize {
253 self.vectors.len()
254 }
255
256 pub fn is_empty(&self) -> bool {
257 self.vectors.is_empty()
258 }
259}