1use std::cmp::Reverse;
22use std::collections::{BinaryHeap, HashMap, HashSet};
23use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
24
25use super::distance::{
26 cmp_f32, distance_simd, DistanceMetric, DistanceResult, ReverseDistanceResult,
27};
28
29pub type NodeId = u64;
31
32#[derive(Debug, Clone)]
34pub struct HnswConfig {
35 pub m: usize,
37 pub m_max0: usize,
39 pub ef_construction: usize,
41 pub ef_search: usize,
43 pub ml: f64,
45 pub metric: DistanceMetric,
47}
48
49impl Default for HnswConfig {
50 fn default() -> Self {
51 let m = 16;
52 Self {
53 m,
54 m_max0: m * 2,
55 ef_construction: 100,
56 ef_search: 50,
57 ml: 1.0 / (m as f64).ln(),
58 metric: DistanceMetric::L2,
59 }
60 }
61}
62
63impl HnswConfig {
64 pub fn with_m(m: usize) -> Self {
66 Self {
67 m,
68 m_max0: m * 2,
69 ml: 1.0 / (m as f64).ln(),
70 ..Default::default()
71 }
72 }
73
74 pub fn with_metric(mut self, metric: DistanceMetric) -> Self {
76 self.metric = metric;
77 self
78 }
79
80 pub fn with_ef_construction(mut self, ef: usize) -> Self {
82 self.ef_construction = ef;
83 self
84 }
85
86 pub fn with_ef_search(mut self, ef: usize) -> Self {
88 self.ef_search = ef;
89 self
90 }
91}
92
93#[derive(Debug, Clone)]
95struct HnswNode {
96 id: NodeId,
98 vector: Vec<f32>,
100 max_layer: usize,
102 connections: Vec<Vec<NodeId>>,
104}
105
106impl HnswNode {
107 fn new(id: NodeId, vector: Vec<f32>, max_layer: usize) -> Self {
108 let mut connections = Vec::with_capacity(max_layer + 1);
109 for _ in 0..=max_layer {
110 connections.push(Vec::new());
111 }
112 Self {
113 id,
114 vector,
115 max_layer,
116 connections,
117 }
118 }
119}
120
121pub struct HnswIndex {
123 config: HnswConfig,
125 nodes: HashMap<NodeId, HnswNode>,
127 entry_point: Option<NodeId>,
129 max_layer: usize,
131 dimension: usize,
133 next_id: AtomicU64,
135 rng_state: u64,
137}
138
139impl HnswIndex {
140 pub fn new(dimension: usize, config: HnswConfig) -> Self {
142 Self {
143 config,
144 nodes: HashMap::new(),
145 entry_point: None,
146 max_layer: 0,
147 dimension,
148 next_id: AtomicU64::new(0),
149 rng_state: 0x853c49e6748fea9b, }
151 }
152
153 pub fn with_dimension(dimension: usize) -> Self {
155 Self::new(dimension, HnswConfig::default())
156 }
157
158 pub fn len(&self) -> usize {
160 self.nodes.len()
161 }
162
163 pub fn is_empty(&self) -> bool {
165 self.nodes.is_empty()
166 }
167
168 pub fn get_vector(&self, id: NodeId) -> Option<&[f32]> {
170 self.nodes.get(&id).map(|n| n.vector.as_slice())
171 }
172
173 pub fn insert(&mut self, vector: Vec<f32>) -> NodeId {
175 let id = self.next_id.fetch_add(1, AtomicOrdering::SeqCst);
176 self.insert_with_id(id, vector);
177 id
178 }
179
180 pub fn insert_with_id(&mut self, id: NodeId, vector: Vec<f32>) {
182 assert_eq!(
183 vector.len(),
184 self.dimension,
185 "Vector dimension mismatch: expected {}, got {}",
186 self.dimension,
187 vector.len()
188 );
189
190 let node_layer = self.random_layer();
192
193 let node = HnswNode::new(id, vector, node_layer);
195
196 if self.entry_point.is_none() {
197 self.nodes.insert(id, node);
199 self.entry_point = Some(id);
200 self.max_layer = node_layer;
201 return;
202 }
203
204 let entry_point = self.entry_point.unwrap();
205
206 let vector = node.vector.clone();
209 self.nodes.insert(id, node);
210
211 let mut current = entry_point;
213
214 for layer in (node_layer + 1..=self.max_layer).rev() {
217 current = self.search_layer_single(&vector, current, layer);
218 }
219
220 for layer in (0..=node_layer.min(self.max_layer)).rev() {
222 let neighbors = self.search_layer(&vector, current, self.config.ef_construction, layer);
224
225 let m = if layer == 0 {
227 self.config.m_max0
228 } else {
229 self.config.m
230 };
231 let selected: Vec<NodeId> = neighbors.into_iter().take(m).map(|r| r.id).collect();
232
233 if let Some(node) = self.nodes.get_mut(&id) {
235 node.connections[layer] = selected.clone();
236 }
237
238 for &neighbor_id in &selected {
240 self.add_connection(neighbor_id, id, layer);
241 }
242
243 if let Some(&first) = selected.first() {
245 current = first;
246 }
247 }
248
249 if node_layer > self.max_layer {
251 self.entry_point = Some(id);
252 self.max_layer = node_layer;
253 }
254 }
255
256 pub fn search(&self, query: &[f32], k: usize) -> Vec<DistanceResult> {
258 self.search_with_ef(query, k, self.config.ef_search)
259 }
260
261 pub fn search_with_ef(&self, query: &[f32], k: usize, ef: usize) -> Vec<DistanceResult> {
263 if self.entry_point.is_none() {
264 return Vec::new();
265 }
266
267 let entry_point = self.entry_point.unwrap();
268 let mut current = entry_point;
269
270 for layer in (1..=self.max_layer).rev() {
272 current = self.search_layer_single(query, current, layer);
273 }
274
275 let candidates = self.search_layer(query, current, ef.max(k), 0);
277
278 candidates.into_iter().take(k).collect()
280 }
281
282 pub fn search_filtered(
284 &self,
285 query: &[f32],
286 k: usize,
287 filter: &HashSet<NodeId>,
288 ) -> Vec<DistanceResult> {
289 self.search_filtered_with_ef(query, k, filter, self.config.ef_search)
290 }
291
292 pub fn search_filtered_with_ef(
294 &self,
295 query: &[f32],
296 k: usize,
297 filter: &HashSet<NodeId>,
298 ef: usize,
299 ) -> Vec<DistanceResult> {
300 if self.entry_point.is_none() || filter.is_empty() {
301 return Vec::new();
302 }
303
304 let entry_point = self.entry_point.unwrap();
305 let mut current = entry_point;
306
307 for layer in (1..=self.max_layer).rev() {
309 current = self.search_layer_single(query, current, layer);
310 }
311
312 let expanded_ef = (ef * 2).max(k * 4);
315 let candidates = self.search_layer(query, current, expanded_ef, 0);
316
317 candidates
319 .into_iter()
320 .filter(|r| filter.contains(&r.id))
321 .take(k)
322 .collect()
323 }
324
325 fn random_layer(&mut self) -> usize {
331 self.rng_state ^= self.rng_state << 13;
333 self.rng_state ^= self.rng_state >> 7;
334 self.rng_state ^= self.rng_state << 17;
335
336 let uniform = (self.rng_state as f64) / (u64::MAX as f64);
338
339 (-uniform.ln() * self.config.ml).floor() as usize
342 }
343
344 fn search_layer_single(&self, query: &[f32], entry: NodeId, layer: usize) -> NodeId {
346 let mut current = entry;
347 let mut current_dist = self.compute_distance(query, current);
348
349 loop {
350 let mut changed = false;
351
352 if let Some(node) = self.nodes.get(¤t) {
353 if layer < node.connections.len() {
354 for &neighbor in &node.connections[layer] {
355 let dist = self.compute_distance(query, neighbor);
356 if dist < current_dist {
357 current_dist = dist;
358 current = neighbor;
359 changed = true;
360 }
361 }
362 }
363 }
364
365 if !changed {
366 break;
367 }
368 }
369
370 current
371 }
372
373 fn search_layer(
375 &self,
376 query: &[f32],
377 entry: NodeId,
378 ef: usize,
379 layer: usize,
380 ) -> Vec<DistanceResult> {
381 let entry_dist = self.compute_distance(query, entry);
382
383 let mut candidates: BinaryHeap<Reverse<DistanceResult>> = BinaryHeap::new();
385 candidates.push(Reverse(DistanceResult::new(entry, entry_dist)));
386
387 let mut results: BinaryHeap<ReverseDistanceResult> = BinaryHeap::new();
389 results.push(ReverseDistanceResult(DistanceResult::new(
390 entry, entry_dist,
391 )));
392
393 let mut visited: HashSet<NodeId> = HashSet::new();
395 visited.insert(entry);
396
397 while let Some(Reverse(current)) = candidates.pop() {
398 let furthest_dist = results.peek().map(|r| r.0.distance).unwrap_or(f32::MAX);
400
401 if current.distance > furthest_dist {
403 break;
404 }
405
406 if let Some(node) = self.nodes.get(¤t.id) {
408 if layer < node.connections.len() {
409 for &neighbor_id in &node.connections[layer] {
410 if visited.contains(&neighbor_id) {
411 continue;
412 }
413 visited.insert(neighbor_id);
414
415 let dist = self.compute_distance(query, neighbor_id);
416 let furthest_dist =
417 results.peek().map(|r| r.0.distance).unwrap_or(f32::MAX);
418
419 if dist < furthest_dist || results.len() < ef {
421 candidates.push(Reverse(DistanceResult::new(neighbor_id, dist)));
422 results.push(ReverseDistanceResult(DistanceResult::new(
423 neighbor_id,
424 dist,
425 )));
426
427 while results.len() > ef {
429 results.pop();
430 }
431 }
432 }
433 }
434 }
435 }
436
437 let mut result_vec: Vec<DistanceResult> = results.into_iter().map(|r| r.0).collect();
439 result_vec.sort_by(|a, b| cmp_f32(a.distance, b.distance).then_with(|| a.id.cmp(&b.id)));
440 result_vec
441 }
442
443 fn add_connection(&mut self, from: NodeId, to: NodeId, layer: usize) {
445 let max_connections = if layer == 0 {
446 self.config.m_max0
447 } else {
448 self.config.m
449 };
450
451 if let Some(node) = self.nodes.get_mut(&from) {
452 while node.connections.len() <= layer {
454 node.connections.push(Vec::new());
455 }
456
457 if !node.connections[layer].contains(&to) {
459 node.connections[layer].push(to);
460
461 if node.connections[layer].len() > max_connections {
463 self.prune_connections(from, layer, max_connections);
464 }
465 }
466 }
467 }
468
469 fn prune_connections(&mut self, node_id: NodeId, layer: usize, max_connections: usize) {
471 let node_vector = self.nodes.get(&node_id).map(|n| n.vector.clone());
472 let node_vector = match node_vector {
473 Some(v) => v,
474 None => return,
475 };
476
477 let neighbors: Vec<NodeId> = self
479 .nodes
480 .get(&node_id)
481 .map(|n| n.connections[layer].clone())
482 .unwrap_or_default();
483
484 let mut scored: Vec<DistanceResult> = neighbors
485 .iter()
486 .map(|&neighbor_id| {
487 let dist = self.compute_distance(&node_vector, neighbor_id);
488 DistanceResult::new(neighbor_id, dist)
489 })
490 .collect();
491
492 scored.sort_by(|a, b| cmp_f32(a.distance, b.distance).then_with(|| a.id.cmp(&b.id)));
494
495 let kept: Vec<NodeId> = scored
497 .into_iter()
498 .take(max_connections)
499 .map(|r| r.id)
500 .collect();
501
502 if let Some(node) = self.nodes.get_mut(&node_id) {
503 node.connections[layer] = kept;
504 }
505 }
506
507 fn compute_distance(&self, query: &[f32], node_id: NodeId) -> f32 {
509 match self.nodes.get(&node_id) {
510 Some(node) => distance_simd(query, &node.vector, self.config.metric),
511 None => f32::MAX,
512 }
513 }
514
515 pub fn insert_batch(&mut self, vectors: Vec<Vec<f32>>) -> Vec<NodeId> {
523 vectors.into_iter().map(|v| self.insert(v)).collect()
524 }
525
526 pub fn insert_batch_with_ids(&mut self, items: Vec<(NodeId, Vec<f32>)>) {
528 for (id, vector) in items {
529 self.insert_with_id(id, vector);
530 }
531 }
532
533 pub fn delete(&mut self, id: NodeId) -> bool {
543 if self.nodes.remove(&id).is_none() {
544 return false;
545 }
546
547 if self.entry_point == Some(id) {
549 self.entry_point = self.nodes.keys().next().copied();
550
551 if let Some(ep) = self.entry_point {
553 self.max_layer = self.nodes.get(&ep).map(|n| n.max_layer).unwrap_or(0);
554 } else {
555 self.max_layer = 0;
556 }
557 }
558
559 for node in self.nodes.values_mut() {
561 for layer_connections in node.connections.iter_mut() {
562 layer_connections.retain(|&neighbor| neighbor != id);
563 }
564 }
565
566 true
567 }
568
569 pub fn contains(&self, id: NodeId) -> bool {
571 self.nodes.contains_key(&id)
572 }
573
574 pub fn search_adaptive(&self, query: &[f32], k: usize) -> Vec<DistanceResult> {
583 let n = self.nodes.len();
585 let adaptive_ef = if n < 100 {
586 k.max(10)
587 } else if n < 10000 {
588 k.max(50)
589 } else if n < 100000 {
590 k.max(100)
591 } else {
592 k.max(200)
593 };
594
595 self.search_with_ef(query, k, adaptive_ef)
596 }
597
598 pub fn stats(&self) -> HnswStats {
604 let mut layer_counts = vec![0usize; self.max_layer + 1];
605 let mut total_connections = 0usize;
606 let mut max_connections = 0usize;
607 let mut min_connections = usize::MAX;
608
609 for node in self.nodes.values() {
610 for (layer, layer_count) in layer_counts.iter_mut().enumerate().take(node.max_layer + 1)
611 {
612 *layer_count += 1;
613 let conns = node.connections.get(layer).map(|c| c.len()).unwrap_or(0);
614 total_connections += conns;
615 max_connections = max_connections.max(conns);
616 if conns > 0 {
617 min_connections = min_connections.min(conns);
618 }
619 }
620 }
621
622 if self.nodes.is_empty() {
623 min_connections = 0;
624 }
625
626 HnswStats {
627 node_count: self.nodes.len(),
628 dimension: self.dimension,
629 max_layer: self.max_layer,
630 layer_counts,
631 total_connections,
632 avg_connections: if self.nodes.is_empty() {
633 0.0
634 } else {
635 total_connections as f64 / self.nodes.len() as f64
636 },
637 max_connections,
638 min_connections,
639 entry_point: self.entry_point,
640 }
641 }
642
643 pub fn to_bytes(&self) -> Vec<u8> {
653 let metric = match self.config.metric {
654 DistanceMetric::L2 => 0,
655 DistanceMetric::Cosine => 1,
656 DistanceMetric::InnerProduct => 2,
657 };
658 let nodes = self
659 .nodes
660 .values()
661 .map(|node| reddb_file::HnswNodeFrame {
662 id: node.id,
663 max_layer: node.max_layer as u32,
664 vector: node.vector.clone(),
665 connections: node.connections.clone(),
666 })
667 .collect();
668 let frame = reddb_file::HnswIndexFrame {
669 dimension: self.dimension as u32,
670 m: self.config.m as u32,
671 m_max0: self.config.m_max0 as u32,
672 ef_construction: self.config.ef_construction as u32,
673 ef_search: self.config.ef_search as u32,
674 ml: self.config.ml,
675 metric,
676 max_layer: self.max_layer as u32,
677 entry_point: self.entry_point,
678 nodes,
679 };
680 reddb_file::encode_hnsw_index_frame(&frame)
681 }
682
683 pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
685 let frame = reddb_file::decode_hnsw_index_frame(bytes).map_err(|e| e.to_string())?;
686
687 let metric = match frame.metric {
688 0 => DistanceMetric::L2,
689 1 => DistanceMetric::Cosine,
690 2 => DistanceMetric::InnerProduct,
691 _ => return Err("Invalid distance metric".to_string()),
692 };
693
694 let config = HnswConfig {
695 m: frame.m as usize,
696 m_max0: frame.m_max0 as usize,
697 ef_construction: frame.ef_construction as usize,
698 ef_search: frame.ef_search as usize,
699 ml: frame.ml,
700 metric,
701 };
702
703 let mut nodes = HashMap::new();
704 let mut max_id = 0u64;
705 for node in frame.nodes {
706 max_id = max_id.max(node.id);
707 nodes.insert(
708 node.id,
709 HnswNode {
710 id: node.id,
711 max_layer: node.max_layer as usize,
712 vector: node.vector,
713 connections: node.connections,
714 },
715 );
716 }
717
718 Ok(Self {
719 config,
720 nodes,
721 entry_point: frame.entry_point,
722 max_layer: frame.max_layer as usize,
723 dimension: frame.dimension as usize,
724 next_id: AtomicU64::new(max_id + 1),
725 rng_state: 12345, })
727 }
728}
729
730#[derive(Debug, Clone)]
732pub struct HnswStats {
733 pub node_count: usize,
735 pub dimension: usize,
737 pub max_layer: usize,
739 pub layer_counts: Vec<usize>,
741 pub total_connections: usize,
743 pub avg_connections: f64,
745 pub max_connections: usize,
747 pub min_connections: usize,
749 pub entry_point: Option<NodeId>,
751}
752
753#[derive(Debug, Clone)]
755pub struct Bitset {
756 bits: Vec<u64>,
757 len: usize,
758}
759
760impl Bitset {
761 pub fn with_capacity(n: usize) -> Self {
763 let num_words = n.div_ceil(64);
764 Self {
765 bits: vec![0; num_words],
766 len: n,
767 }
768 }
769
770 pub fn all(n: usize) -> Self {
772 let num_words = n.div_ceil(64);
773 let mut bits = vec![u64::MAX; num_words];
774
775 if !n.is_multiple_of(64) {
777 let last_idx = num_words - 1;
778 let valid_bits = n % 64;
779 bits[last_idx] = (1u64 << valid_bits) - 1;
780 }
781
782 Self { bits, len: n }
783 }
784
785 pub fn set(&mut self, idx: usize) {
787 if idx < self.len {
788 let word = idx / 64;
789 let bit = idx % 64;
790 self.bits[word] |= 1u64 << bit;
791 }
792 }
793
794 pub fn clear(&mut self, idx: usize) {
796 if idx < self.len {
797 let word = idx / 64;
798 let bit = idx % 64;
799 self.bits[word] &= !(1u64 << bit);
800 }
801 }
802
803 pub fn is_set(&self, idx: usize) -> bool {
805 if idx >= self.len {
806 return false;
807 }
808 let word = idx / 64;
809 let bit = idx % 64;
810 (self.bits[word] & (1u64 << bit)) != 0
811 }
812
813 pub fn to_hashset(&self) -> HashSet<NodeId> {
815 let mut set = HashSet::new();
816 for i in 0..self.len {
817 if self.is_set(i) {
818 set.insert(i as NodeId);
819 }
820 }
821 set
822 }
823}
824
825#[cfg(test)]
826mod tests {
827 use super::*;
828
829 fn random_vector(dim: usize, seed: u64) -> Vec<f32> {
830 let mut state = seed;
831 (0..dim)
832 .map(|_| {
833 state ^= state << 13;
834 state ^= state >> 7;
835 state ^= state << 17;
836 (state as f32) / (u64::MAX as f32)
837 })
838 .collect()
839 }
840
841 #[test]
842 fn test_empty_index() {
843 let index = HnswIndex::with_dimension(128);
844 assert!(index.is_empty());
845 assert_eq!(index.len(), 0);
846
847 let results = index.search(&vec![0.0; 128], 10);
848 assert!(results.is_empty());
849 }
850
851 #[test]
852 fn test_single_insert() {
853 let mut index = HnswIndex::with_dimension(3);
854 let id = index.insert(vec![1.0, 2.0, 3.0]);
855
856 assert_eq!(index.len(), 1);
857 assert!(!index.is_empty());
858 assert!(index.get_vector(id).is_some());
859 }
860
861 #[test]
862 fn test_exact_match() {
863 let mut index = HnswIndex::with_dimension(3);
864 index.insert(vec![1.0, 0.0, 0.0]);
865 index.insert(vec![0.0, 1.0, 0.0]);
866 index.insert(vec![0.0, 0.0, 1.0]);
867
868 let results = index.search(&[1.0, 0.0, 0.0], 1);
870 assert_eq!(results.len(), 1);
871 assert_eq!(results[0].distance, 0.0);
872 }
873
874 #[test]
875 fn test_nearest_neighbor() {
876 let mut index = HnswIndex::with_dimension(2);
877 index.insert_with_id(0, vec![0.0, 0.0]);
878 index.insert_with_id(1, vec![1.0, 0.0]);
879 index.insert_with_id(2, vec![2.0, 0.0]);
880 index.insert_with_id(3, vec![3.0, 0.0]);
881
882 let results = index.search(&[0.9, 0.0], 1);
884 assert_eq!(results.len(), 1);
885 assert_eq!(results[0].id, 1); }
887
888 #[test]
889 fn test_k_nearest() {
890 let mut index = HnswIndex::with_dimension(2);
891 for i in 0..10 {
892 index.insert_with_id(i, vec![i as f32, 0.0]);
893 }
894
895 let results = index.search(&[4.5, 0.0], 3);
897 assert_eq!(results.len(), 3);
898
899 let ids: HashSet<_> = results.iter().map(|r| r.id).collect();
901 assert!(ids.contains(&4));
902 assert!(ids.contains(&5));
903 }
904
905 #[test]
906 fn test_filtered_search() {
907 let mut index = HnswIndex::with_dimension(2);
908 for i in 0..10 {
909 index.insert_with_id(i, vec![i as f32, 0.0]);
910 }
911
912 let filter: HashSet<NodeId> = [0, 2, 4, 6, 8].iter().copied().collect();
914
915 let results = index.search_filtered(&[5.0, 0.0], 2, &filter);
917
918 assert_eq!(results.len(), 2);
920 let ids: HashSet<_> = results.iter().map(|r| r.id).collect();
921 assert!(ids.contains(&4));
922 assert!(ids.contains(&6));
923 }
924
925 #[test]
926 fn test_cosine_distance() {
927 let config = HnswConfig::default().with_metric(DistanceMetric::Cosine);
928 let mut index = HnswIndex::new(3, config);
929
930 index.insert_with_id(0, vec![1.0, 0.0, 0.0]);
932 index.insert_with_id(1, vec![0.0, 1.0, 0.0]);
933 index.insert_with_id(2, vec![0.707, 0.707, 0.0]); let results = index.search(&[0.707, 0.707, 0.0], 1);
937 assert_eq!(results[0].id, 2);
938 }
939
940 #[test]
941 fn test_many_vectors() {
942 let dim = 64;
943 let n: usize = 1000;
944
945 let mut index = HnswIndex::with_dimension(dim);
946
947 for i in 0..n {
949 let vector = random_vector(dim, i as u64);
950 index.insert_with_id(i as u64, vector);
951 }
952
953 assert_eq!(index.len(), n);
954
955 let query = random_vector(dim, 12345);
957 let results = index.search(&query, 10);
958 assert_eq!(results.len(), 10);
959
960 for i in 1..results.len() {
962 assert!(results[i].distance >= results[i - 1].distance);
963 }
964 }
965
966 #[test]
967 fn test_bitset() {
968 let mut bs = Bitset::with_capacity(100);
969
970 bs.set(0);
971 bs.set(50);
972 bs.set(99);
973
974 assert!(bs.is_set(0));
975 assert!(bs.is_set(50));
976 assert!(bs.is_set(99));
977 assert!(!bs.is_set(1));
978 assert!(!bs.is_set(64));
979
980 bs.clear(50);
981 assert!(!bs.is_set(50));
982 }
983
984 #[test]
985 fn test_bitset_all() {
986 let bs = Bitset::all(100);
987
988 for i in 0..100 {
989 assert!(bs.is_set(i));
990 }
991 assert!(!bs.is_set(100)); }
993}