use crate::simd;
use crate::RetrieveError;
use smallvec::SmallVec;
use super::graph::NSWIndex;
pub fn construct_graph(index: &mut NSWIndex) -> Result<(), RetrieveError> {
if index.num_vectors == 0 {
return Err(RetrieveError::EmptyIndex);
}
index.neighbors = vec![SmallVec::new(); index.num_vectors];
if index.entry_point.is_none() {
index.entry_point = Some(0);
}
for current_id in 0..index.num_vectors {
let current_vector = index.get_vector(current_id);
let mut candidates = Vec::new();
for other_id in 0..index.num_vectors {
if other_id == current_id {
continue;
}
let other_vector = index.get_vector(other_id);
let dist = 1.0 - simd::dot(current_vector, other_vector);
candidates.push((other_id as u32, dist));
}
let selected = select_neighbors_rng(&candidates, index.params.m);
for &neighbor_id in &selected {
index.neighbors[current_id].push(neighbor_id);
let reverse_neighbors = &mut index.neighbors[neighbor_id as usize];
if !reverse_neighbors.contains(&(current_id as u32)) {
reverse_neighbors.push(current_id as u32);
}
}
}
Ok(())
}
fn select_neighbors_rng(candidates: &[(u32, f32)], m: usize) -> Vec<u32> {
if candidates.is_empty() {
return Vec::new();
}
let mut sorted = candidates.to_vec();
sorted.sort_unstable_by(|a, b| a.1.total_cmp(&b.1));
let mut selected = Vec::new();
let mut rng = rand::rng();
if !sorted.is_empty() {
selected.push(sorted[0].0);
}
use rand::Rng;
for (id, dist) in sorted.iter().skip(1) {
if selected.len() >= m {
break;
}
let prob = (-dist).exp().min(1.0);
if rng.random::<f32>() < prob {
selected.push(*id);
}
}
while selected.len() < m && selected.len() < sorted.len() {
for (id, _) in &sorted {
if !selected.contains(id) {
selected.push(*id);
break;
}
}
}
selected
}