use std::{
cmp::{Ordering, Reverse},
collections::BinaryHeap,
sync::Mutex,
};
use crate::{
quantizer::{Quantizer, QueryEvaluator},
Dataset,
};
pub mod config_hnsw;
pub mod hnsw_builder;
pub mod level;
#[derive(Debug, Clone, Copy, PartialOrd)]
pub struct Node(pub f32, pub usize);
impl Node {
pub fn distance(&self) -> f32 {
self.0
}
pub fn id_vec(&self) -> usize {
self.1
}
}
impl Ord for Node {
fn cmp(&self, other: &Self) -> Ordering {
self.distance()
.partial_cmp(&other.distance())
.unwrap_or(Ordering::Equal)
}
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
self.distance() == other.distance()
}
}
impl Eq for Node {}
pub fn add_neighbor_to_heaps(
min_heap: &mut BinaryHeap<Reverse<Node>>,
max_heap: &mut BinaryHeap<Node>,
node: Node,
ef_parameter: usize,
) {
let should_add_node = if max_heap.len() < ef_parameter {
true
} else if let Some(top_node) = max_heap.peek() {
top_node.distance() > node.distance()
} else {
false
};
if should_add_node {
min_heap.push(Reverse(node));
max_heap.push(node);
}
if max_heap.len() > ef_parameter {
max_heap.pop();
}
}
pub fn add_neighbors_to_heaps(
min_heap: &mut BinaryHeap<Reverse<Node>>,
max_heap: &mut BinaryHeap<Node>,
distances: &[f32],
ids: &[usize],
ef_parameter: usize,
) {
for (distance, id) in distances.iter().zip(ids.iter()) {
let should_add_node = if max_heap.len() < ef_parameter {
true
} else if let Some(top_node) = max_heap.peek() {
top_node.distance() > *distance
} else {
false
};
if should_add_node {
min_heap.push(Reverse(Node(*distance, *id)));
max_heap.push(Node(*distance, *id));
}
if max_heap.len() > ef_parameter {
max_heap.pop();
}
}
}
#[inline]
pub fn compute_closest_from_neighbors<'a, Q, D, E>(
dataset: &D,
query_evaluator: &E,
neighbors: &[usize],
nearest_vec: &mut usize,
dis_nearest_vec: &mut f32,
) where
Q: Quantizer<DatasetType = D>, D: Dataset<Q>, E: QueryEvaluator<'a, Q = Q>, {
for &neighbor in neighbors {
let distance_neighbor = query_evaluator.compute_distance(dataset, neighbor);
if distance_neighbor < *dis_nearest_vec {
*nearest_vec = neighbor;
*dis_nearest_vec = distance_neighbor;
}
}
}
#[inline]
pub fn insert_into_topk(
topk: &Mutex<Vec<(f32, usize)>>,
mut query_topk: Vec<(f32, usize)>,
index: usize,
k: usize,
) {
if query_topk.len() != k {
query_topk.resize_with(k, || (f32::MAX, usize::MAX));
}
assert_eq!(
query_topk.len(),
k,
"The length of the result vec has to be equal to k"
);
let start_index = index * k;
topk.lock()
.unwrap()
.splice(start_index..start_index + k, query_topk);
}
#[inline]
pub fn prefetch_dense_vec_with_offset<T>(vector: &[T], offset: usize, len: usize) {
let end = offset + len;
for i in (offset..end).step_by(64 / std::mem::size_of::<T>()) {
prefetch_read_T2(vector, i);
}
}
#[allow(non_snake_case)]
#[inline]
pub fn prefetch_read_T2<T>(data: &[T], offset: usize) {
let _p = data.as_ptr().wrapping_add(offset) as *const i8;
{
#[cfg(target_arch = "x86")]
use std::arch::x86::{_mm_prefetch, _MM_HINT_T2};
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::{_mm_prefetch, _MM_HINT_T2};
unsafe {
_mm_prefetch(_p, _MM_HINT_T2);
}
}
}
pub fn from_max_heap_to_min_heap(max_heap: &mut BinaryHeap<Node>) -> BinaryHeap<Reverse<Node>> {
let vec: Vec<_> = max_heap.drain().collect();
BinaryHeap::from(vec.into_iter().map(Reverse).collect::<Vec<_>>())
}
#[cfg(test)]
mod tests_from_max_heap_to_min_heap {
use super::*;
#[test]
fn test_from_max_heap_to_min_heap() {
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
max_heap.push(Node(3.2, 10));
max_heap.push(Node(2.2, 8));
max_heap.push(Node(6.2, 12));
max_heap.push(Node(7.2, 2));
max_heap.push(Node(32.2, 4));
max_heap.push(Node(4.2, 14));
max_heap.push(Node(1.2, 6));
max_heap.push(Node(7.2, 6));
let mut min_heap = from_max_heap_to_min_heap(&mut max_heap);
let mut min_heap_vec: Vec<Node> = Vec::new();
while let Some(node) = min_heap.pop() {
min_heap_vec.push(node.0);
}
let expected_vec: Vec<Node> = vec![
Node(1.2, 6),
Node(2.2, 8),
Node(3.2, 10),
Node(4.2, 14),
Node(6.2, 12),
Node(7.2, 2),
Node(7.2, 6),
Node(32.2, 4),
];
assert_eq!(expected_vec, min_heap_vec);
}
#[test]
fn test_from_max_heap_to_min_heap_with_large_data() {
let n = 100000;
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
for i in 0..n {
max_heap.push(Node(i as f32, i));
}
let mut min_heap = from_max_heap_to_min_heap(&mut max_heap);
let mut min_heap_vec: Vec<Node> = Vec::new();
while let Some(node) = min_heap.pop() {
min_heap_vec.push(node.0);
}
let mut expected_vec: Vec<Node> = Vec::new();
for i in 0..n {
expected_vec.push(Node(i as f32, i));
}
assert_eq!(expected_vec, min_heap_vec);
}
#[test]
fn test_from_max_heap_to_min_heap_empty() {
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
let min_heap = from_max_heap_to_min_heap(&mut max_heap);
assert!(min_heap.is_empty());
}
#[test]
fn test_from_max_heap_to_min_heap_single_element() {
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
max_heap.push(Node(42.0, 1));
let mut min_heap = from_max_heap_to_min_heap(&mut max_heap);
let node = min_heap.pop().unwrap().0;
assert_eq!(node, Node(42.0, 1));
assert!(min_heap.is_empty());
}
#[test]
fn test_from_max_heap_to_min_heap_all_elements_same() {
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
for _ in 0..10 {
max_heap.push(Node(5.0, 100));
}
let mut min_heap = from_max_heap_to_min_heap(&mut max_heap);
let mut min_heap_vec: Vec<Node> = Vec::new();
while let Some(Reverse(node)) = min_heap.pop() {
min_heap_vec.push(node);
}
let expected_vec: Vec<Node> = vec![Node(5.0, 100); 10];
assert_eq!(expected_vec, min_heap_vec);
}
#[test]
fn test_from_max_heap_to_min_heap_with_negative_values() {
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
max_heap.push(Node(-1.0, 1));
max_heap.push(Node(-2.0, 2));
max_heap.push(Node(-3.0, 3));
let mut min_heap = from_max_heap_to_min_heap(&mut max_heap);
let mut min_heap_vec: Vec<Node> = Vec::new();
while let Some(Reverse(node)) = min_heap.pop() {
min_heap_vec.push(node);
}
let expected_vec: Vec<Node> = vec![Node(-3.0, 3), Node(-2.0, 2), Node(-1.0, 1)];
assert_eq!(expected_vec, min_heap_vec);
}
#[test]
fn test_from_max_heap_to_min_heap_with_mixed_values() {
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
max_heap.push(Node(0.0, 0));
max_heap.push(Node(-1.0, 1));
max_heap.push(Node(2.0, 2));
max_heap.push(Node(-2.0, 3));
max_heap.push(Node(1.0, 4));
let mut min_heap = from_max_heap_to_min_heap(&mut max_heap);
let mut min_heap_vec: Vec<Node> = Vec::new();
while let Some(Reverse(node)) = min_heap.pop() {
min_heap_vec.push(node);
}
let expected_vec: Vec<Node> = vec![
Node(-2.0, 3),
Node(-1.0, 1),
Node(0.0, 0),
Node(1.0, 4),
Node(2.0, 2),
];
assert_eq!(expected_vec, min_heap_vec);
}
}
#[cfg(test)]
mod tests_insert_into_topk {
use std::{
panic,
sync::{Arc, Mutex},
};
use crate::hnsw_utils::insert_into_topk;
#[test]
fn test_exact_size_query_start_index() {
let topk = Arc::new(Mutex::new(vec![(1.0, 1), (2.0, 2), (3.0, 3)]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7)];
let index = 0;
let k = 3;
insert_into_topk(&topk, topk_query, index, k);
let topk_locked = topk.lock().unwrap();
let expected = vec![(0.5, 5), (1.5, 6), (2.5, 7)];
assert_eq!(&*topk_locked, &expected);
}
#[test]
fn test_smaller_query_start_index() {
let topk = Arc::new(Mutex::new(vec![(1.0, 1), (2.0, 2), (3.0, 3)]));
let topk_query = vec![(0.5, 5)];
let index = 0;
let k = 3;
insert_into_topk(&topk, topk_query, index, k);
let topk_locked = topk.lock().unwrap();
let expected = vec![(0.5, 5), (f32::MAX, usize::MAX), (f32::MAX, usize::MAX)];
assert_eq!(&*topk_locked, &expected);
}
#[test]
fn test_larger_query_start_index() {
let topk = Arc::new(Mutex::new(vec![
(1.0, 1),
(2.0, 2),
(3.0, 3),
(6.0, 4),
(8.0, 5),
(9.0, 6),
]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7), (3.5, 8)];
let index = 0;
let k = 3;
insert_into_topk(&topk, topk_query, index, k);
let topk_locked = topk.lock().unwrap();
let expected = vec![(0.5, 5), (1.5, 6), (2.5, 7), (6.0, 4), (8.0, 5), (9.0, 6)];
assert_eq!(&*topk_locked, &expected);
}
#[test]
fn test_empty_query_start_index() {
let topk = Arc::new(Mutex::new(vec![(1.0, 1), (2.0, 2), (3.0, 3)]));
let topk_query = vec![];
let index = 0;
let k = 3;
insert_into_topk(&topk, topk_query, index, k);
let topk_locked = topk.lock().unwrap();
let expected = vec![
(f32::MAX, usize::MAX),
(f32::MAX, usize::MAX),
(f32::MAX, usize::MAX),
];
assert_eq!(&*topk_locked, &expected);
}
#[test]
fn test_exact_size_query_middle_index() {
let topk = Arc::new(Mutex::new(vec![(f32::MAX, usize::MAX); 100]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7), (2.7, 1), (3.1, 3)];
let index = 10;
let k = 5;
insert_into_topk(&topk, topk_query.clone(), index, k);
let start_index = index * k;
for i in 0..k {
assert_eq!(topk.lock().unwrap()[i + start_index], topk_query[i]);
}
}
#[test]
fn test_smaller_query_middle_index() {
let topk = Arc::new(Mutex::new(vec![(f32::MAX, usize::MAX); 1000]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7)];
let index = 20;
let k = 5;
insert_into_topk(&topk, topk_query.clone(), index, k);
let expected = vec![
(0.5, 5),
(1.5, 6),
(2.5, 7),
(f32::MAX, usize::MAX),
(f32::MAX, usize::MAX),
];
let start_index = index * k;
for i in 0..k {
assert_eq!(topk.lock().unwrap()[i + start_index], expected[i]);
}
}
#[test]
fn test_bigger_query_middle_index() {
let topk = Arc::new(Mutex::new(vec![(f32::MAX, usize::MAX); 200]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7), (4.5, 3), (5.9, 1), (12.5, 72)];
let index = 20;
let k = 5;
insert_into_topk(&topk, topk_query.clone(), index, k);
let expected = vec![(0.5, 5), (1.5, 6), (2.5, 7), (4.5, 3), (5.9, 1)];
let start_index = index * k;
for i in 0..k {
assert_eq!(topk.lock().unwrap()[i + start_index], expected[i]);
}
}
#[test]
fn test_exact_size_query_last_index() {
let topk = Arc::new(Mutex::new(vec![(f32::MAX, usize::MAX); 200]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7), (4.5, 3), (5.9, 1)];
let index = 39;
let k = 5;
insert_into_topk(&topk, topk_query.clone(), index, k);
let start_index = index * k;
for i in 0..k {
assert_eq!(topk.lock().unwrap()[i + start_index], topk_query[i]);
}
}
#[test]
fn test_smaller_query_last_index() {
let topk = Arc::new(Mutex::new(vec![(f32::MAX, usize::MAX); 200]));
let topk_query = vec![(0.5, 5), (1.5, 6)];
let index = 39;
let k = 5;
insert_into_topk(&topk, topk_query.clone(), index, k);
let expected = vec![
(0.5, 5),
(1.5, 6),
(f32::MAX, usize::MAX),
(f32::MAX, usize::MAX),
(f32::MAX, usize::MAX),
];
let start_index = index * k;
for i in 0..k {
assert_eq!(topk.lock().unwrap()[i + start_index], expected[i]);
}
}
#[test]
fn test_bigger_query_last_index() {
let topk = Arc::new(Mutex::new(vec![(f32::MAX, usize::MAX); 200]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7), (4.5, 3), (5.9, 1), (12.5, 72)];
let index = 39;
let k = 5;
insert_into_topk(&topk, topk_query.clone(), index, k);
let start_index = index * k;
for i in 0..k {
assert_eq!(topk.lock().unwrap()[i + start_index], topk_query[i]);
}
}
#[test]
fn test_index_out_of_range() {
let topk = Arc::new(Mutex::new(vec![(f32::MAX, usize::MAX); 200]));
let topk_query = vec![(0.5, 5), (1.5, 6), (2.5, 7), (4.5, 3), (5.9, 1), (12.5, 72)];
let index = 40; let k = 5;
let topk_initial = topk.lock().unwrap().clone();
let result = panic::catch_unwind(|| {
insert_into_topk(&topk, topk_query.clone(), index, k);
});
assert!(result.is_err(), "Expected panic, but code did not panic");
let topk_after = topk.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
assert_eq!(
*topk_after, topk_initial,
"Topk should remain unchanged after panic"
);
}
}
#[cfg(test)]
mod tests_add_neighbors_to_heaps {
use super::*;
use std::collections::BinaryHeap;
#[test]
fn test_add_to_empty_heaps() {
let mut min_heap: BinaryHeap<Reverse<Node>> = BinaryHeap::new();
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
let node = Node(10.0, 1);
let ef_parameter = 3;
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, node, ef_parameter);
assert_eq!(min_heap.len(), 1);
assert_eq!(max_heap.len(), 1);
assert_eq!(min_heap.peek(), Some(&Reverse(node)));
assert_eq!(max_heap.peek(), Some(&node));
}
#[test]
fn test_add_within_ef_parameter_limit() {
let mut min_heap: BinaryHeap<Reverse<Node>> = BinaryHeap::new();
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
let ef_parameter = 3;
let nodes = vec![Node(10.0, 1), Node(5.0, 2), Node(7.0, 3)];
for node in nodes.iter().cloned() {
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, node, ef_parameter);
}
assert_eq!(min_heap.len(), 3);
assert_eq!(max_heap.len(), 3);
assert_eq!(max_heap.peek().unwrap().distance(), 10.0);
assert_eq!(min_heap.peek().unwrap().0.distance(), 5.0);
}
#[test]
fn test_add_beyond_ef_parameter_limit() {
let mut min_heap: BinaryHeap<Reverse<Node>> = BinaryHeap::new();
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
let ef_parameter = 3;
let nodes = vec![Node(10.0, 1), Node(5.0, 2), Node(7.0, 3), Node(3.0, 4)];
for node in nodes.iter().cloned() {
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, node, ef_parameter);
}
assert_eq!(min_heap.len(), 4);
assert_eq!(max_heap.len(), 3);
assert_eq!(max_heap.peek().unwrap().distance(), 7.0);
assert_eq!(min_heap.peek().unwrap().0.distance(), 3.0);
}
#[test]
fn test_add_node_with_higher_distance_than_current_max() {
let mut min_heap: BinaryHeap<Reverse<Node>> = BinaryHeap::new();
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
let ef_parameter = 2;
let nodes = vec![Node(5.0, 1), Node(3.0, 2)];
for node in nodes.iter().cloned() {
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, node, ef_parameter);
}
let new_node = Node(8.0, 3);
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, new_node, ef_parameter);
assert_eq!(min_heap.len(), 2);
assert_eq!(max_heap.len(), 2);
assert_eq!(max_heap.peek().unwrap().distance(), 5.0);
assert_eq!(min_heap.peek().unwrap().0.distance(), 3.0);
}
#[test]
fn test_ef_parameter_of_one() {
let mut min_heap: BinaryHeap<Reverse<Node>> = BinaryHeap::new();
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
let ef_parameter = 1;
let nodes = vec![Node(5.0, 1), Node(3.0, 2), Node(2.0, 3)];
for node in nodes.iter().cloned() {
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, node, ef_parameter);
}
assert_eq!(min_heap.len(), 3);
assert_eq!(max_heap.len(), 1);
assert_eq!(max_heap.peek().unwrap().distance(), 2.0);
assert_eq!(min_heap.peek().unwrap().0.distance(), 2.0);
}
#[test]
fn test_add_node_equal_to_current_max() {
let mut min_heap: BinaryHeap<Reverse<Node>> = BinaryHeap::new();
let mut max_heap: BinaryHeap<Node> = BinaryHeap::new();
let ef_parameter = 2;
let nodes = vec![Node(5.0, 1), Node(3.0, 2)];
for node in nodes.iter().cloned() {
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, node, ef_parameter);
}
let new_node = Node(5.0, 3);
add_neighbor_to_heaps(&mut min_heap, &mut max_heap, new_node, ef_parameter);
assert_eq!(min_heap.len(), 2);
assert_eq!(max_heap.len(), 2);
assert_eq!(max_heap.peek().unwrap().distance(), 5.0);
assert_eq!(min_heap.peek().unwrap().0.distance(), 3.0);
}
}
#[cfg(test)]
mod tests_compute_closest_from_neighbors_euclidean_distance {
use core::f32;
use crate::{
hnsw_utils::compute_closest_from_neighbors, plain_quantizer::PlainQuantizer, Dataset,
DenseDataset, DenseVector1D, DistanceType,
};
#[test]
fn test_compute_closest_from_neighbors_updates_nearest() {
let query_vector: &[f32] = &[1.0, 1.0];
let query = DenseVector1D::new(query_vector);
let neighbor_vectors = &[2.0, 2.0, 1.0, 1.5, 0.0, 0.0];
let quantizer = PlainQuantizer::new(2, DistanceType::Euclidean);
let dataset = DenseDataset::from_vec(neighbor_vectors.to_vec(), 2, quantizer);
let evaluator = dataset.query_evaluator(query);
let neighbors = vec![0, 1, 2];
let mut nearest_vec = 0;
let mut dis_nearest_vec = f32::MAX;
compute_closest_from_neighbors(
&dataset,
&evaluator,
&neighbors,
&mut nearest_vec,
&mut dis_nearest_vec,
);
assert_eq!(nearest_vec, 1);
}
#[test]
fn test_compute_closest_from_neighbors_single_neighbor() {
let query_vector: &[f32] = &[1.0, 1.0];
let query = DenseVector1D::new(query_vector);
let neighbor_vectors = &[2.0, 2.0];
let quantizer = PlainQuantizer::new(2, crate::DistanceType::Euclidean);
let dataset = DenseDataset::from_vec(neighbor_vectors.to_vec(), 2, quantizer);
let evaluator = dataset.query_evaluator(query);
let neighbors = vec![0];
let mut nearest_vec = 0;
let mut dis_nearest_vec = f32::MAX;
compute_closest_from_neighbors(
&dataset,
&evaluator,
&neighbors,
&mut nearest_vec,
&mut dis_nearest_vec,
);
assert_eq!(nearest_vec, 0);
}
#[test]
fn test_compute_closest_from_neighbors_equidistant_neighbors() {
let query_vector: &[f32] = &[1.0, 1.0];
let query = DenseVector1D::new(query_vector);
let neighbor_vectors = &[2.0, 5.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0];
let quantizer = PlainQuantizer::new(2, crate::DistanceType::Euclidean);
let dataset = DenseDataset::from_vec(neighbor_vectors.to_vec(), 2, quantizer);
let evaluator = dataset.query_evaluator(query);
let neighbors = vec![0, 1, 2, 3];
let mut nearest_vec = 0;
let mut dis_nearest_vec = f32::MAX;
compute_closest_from_neighbors(
&dataset,
&evaluator,
&neighbors,
&mut nearest_vec,
&mut dis_nearest_vec,
);
assert_eq!(nearest_vec, 1);
}
#[test]
fn test_compute_closest_from_neighbors_no_neighbors() {
let query_vector: &[f32] = &[1.0, 1.0];
let query = DenseVector1D::new(query_vector);
let neighbor_vectors = &[];
let quantizer = PlainQuantizer::new(2, crate::DistanceType::Euclidean);
let dataset = DenseDataset::from_vec(neighbor_vectors.to_vec(), 2, quantizer);
let evaluator = dataset.query_evaluator(query);
let neighbors: Vec<usize> = vec![];
let mut nearest_vec = 0;
let mut dis_nearest_vec = f32::MAX;
compute_closest_from_neighbors(
&dataset,
&evaluator,
&neighbors,
&mut nearest_vec,
&mut dis_nearest_vec,
);
assert_eq!(nearest_vec, 0);
assert_eq!(dis_nearest_vec, f32::MAX);
}
#[test]
fn test_compute_closest_from_neighbors_max_distance() {
let query_vector: &[f32] = &[1.0, 1.0];
let query = DenseVector1D::new(query_vector);
let neighbor_vectors = &[f32::INFINITY, f32::INFINITY, -f32::INFINITY, -f32::INFINITY];
let quantizer = PlainQuantizer::new(2, crate::DistanceType::Euclidean);
let dataset = DenseDataset::from_vec(neighbor_vectors.to_vec(), 2, quantizer);
let evaluator = dataset.query_evaluator(query);
let neighbors = vec![0, 1];
let mut nearest_vec = 0;
let mut dis_nearest_vec = f32::MAX;
compute_closest_from_neighbors(
&dataset,
&evaluator,
&neighbors,
&mut nearest_vec,
&mut dis_nearest_vec,
);
assert_eq!(nearest_vec, 0);
assert_eq!(dis_nearest_vec, f32::MAX);
}
#[test]
fn test_compute_closest_from_neighbors_exact_match() {
let query_vector: &[f32] = &[1.0, 1.0];
let query = DenseVector1D::new(query_vector);
let neighbor_vectors = &[2.0, 2.0, 1.0, 1.0];
let quantizer = PlainQuantizer::new(2, crate::DistanceType::Euclidean);
let dataset = DenseDataset::from_vec(neighbor_vectors.to_vec(), 2, quantizer);
let evaluator = dataset.query_evaluator(query);
let neighbors = vec![0, 1];
let mut nearest_vec = 0;
let mut dis_nearest_vec = f32::MAX;
compute_closest_from_neighbors(
&dataset,
&evaluator,
&neighbors,
&mut nearest_vec,
&mut dis_nearest_vec,
);
assert_eq!(nearest_vec, 1);
assert_eq!(dis_nearest_vec, 0.0);
}
}