use urna_format::Int8EmbeddingsView;
use super::search::{greedy_search, layer_search};
use super::select_neighbors::select_neighbors_heuristic;
use super::visited::VisitedList;
use super::{Candidate, HnswIndex, Node, dist_rr};
use crate::materialize::PackedVectors;
impl HnswIndex {
pub fn build(
vectors: Vec<f32>,
n: usize,
dim: usize,
m: usize,
ef_construction: usize,
seed: u64,
) -> Self {
assert_eq!(vectors.len(), n * dim);
let store = PackedVectors::F32(vectors);
let m_max0 = m * 2;
let mut rng = LcgRng::new(seed);
let mut visited = VisitedList::new(n);
let mut nodes: Vec<Node> = Vec::with_capacity(n);
for _ in 0..n {
let level = sample_level(&mut rng, m);
let mut neighbors = Vec::with_capacity((level as usize) + 1);
for _ in 0..=level {
neighbors.push(Vec::new());
}
nodes.push(Node { level, neighbors });
}
let mut entry_point: u32 = 0;
let mut max_level: u32 = 0;
for i in 0..n {
let level = nodes[i].level;
if i == 0 {
entry_point = 0;
max_level = level;
continue;
}
let mut q_scratch = store.scratch(dim);
let mut sa = store.scratch(dim);
let mut sb = store.scratch(dim);
let q = store.row(i, dim, &mut q_scratch);
let mut curr = entry_point;
for layer in (level + 1..=max_level).rev() {
curr = greedy_search(curr, q, layer, &nodes, &store, dim);
}
let mut entry = curr;
let start_layer = level.min(max_level);
for layer in (0..=start_layer).rev() {
visited.clear();
let candidates = layer_search(
&[entry],
q,
layer,
ef_construction,
&nodes,
&store,
dim,
i as u32,
&mut visited,
);
let cap_layer = if layer == 0 { m_max0 } else { m };
let neighbor_ids =
select_neighbors_heuristic(&candidates, m, &store, dim, &mut sa, &mut sb, true);
nodes[i].neighbors[layer as usize] = neighbor_ids.clone();
for &nbr in &neighbor_ids {
let nbr_idx = nbr as usize;
let layer_idx = layer as usize;
if layer_idx >= nodes[nbr_idx].neighbors.len() {
continue;
}
if nodes[nbr_idx].neighbors[layer_idx].len() >= cap_layer {
let existing = nodes[nbr_idx].neighbors[layer_idx].clone();
let mut all: Vec<Candidate> = Vec::with_capacity(existing.len() + 1);
for id in existing {
let dist = dist_rr(&store, nbr_idx, id as usize, dim, &mut sa, &mut sb);
all.push(Candidate { id, dist });
}
let dist = dist_rr(&store, nbr_idx, i, dim, &mut sa, &mut sb);
all.push(Candidate { id: i as u32, dist });
nodes[nbr_idx].neighbors[layer_idx] = select_neighbors_heuristic(
&all, cap_layer, &store, dim, &mut sa, &mut sb, true,
);
} else if !nodes[nbr_idx].neighbors[layer_idx].contains(&(i as u32)) {
nodes[nbr_idx].neighbors[layer_idx].push(i as u32);
}
}
if let Some(&first) = neighbor_ids.first() {
entry = first;
} else if !candidates.is_empty() {
entry = candidates[0].id;
}
}
if level > max_level {
max_level = level;
entry_point = i as u32;
}
}
Self {
m,
m_max0,
ef_construction,
entry_point,
max_level,
nodes,
store,
dim,
n,
ef_search: ef_construction,
}
}
pub fn build_from_int8(view: &Int8EmbeddingsView<'_>, m: usize, ef: usize, seed: u64) -> Self {
let mut vectors = vec![0.0f32; view.n * view.dim];
for i in 0..view.n {
let scale = view.scale(i);
let row = view.row(i);
for j in 0..view.dim {
vectors[i * view.dim + j] = row[j] as f32 * scale;
}
}
Self::build(vectors, view.n, view.dim, m, ef, seed)
}
pub fn build_from_f16(
bytes: &[u8],
n: usize,
dim: usize,
m: usize,
ef: usize,
seed: u64,
) -> Self {
let vectors = urna_format::f16_bytes_to_f32(bytes);
Self::build(vectors, n, dim, m, ef, seed)
}
pub fn build_from_f32(
bytes: &[u8],
n: usize,
dim: usize,
m: usize,
ef: usize,
seed: u64,
) -> Self {
let mut vectors = vec![0.0f32; n * dim];
for (i, slot) in vectors.iter_mut().enumerate() {
let off = i * 4;
*slot =
f32::from_le_bytes([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]]);
}
Self::build(vectors, n, dim, m, ef, seed)
}
}
pub(super) fn sample_level(rng: &mut LcgRng, m: usize) -> u32 {
let m_l = 1.0 / (m as f64).ln();
let r = rng.next_f64();
if r <= 0.0 {
return 0;
}
let level = (-(r.ln()) * m_l).floor() as i64;
level.clamp(0, 31) as u32 }
pub(super) struct LcgRng {
state: u64,
}
impl LcgRng {
pub(super) fn new(seed: u64) -> Self {
Self {
state: seed
.wrapping_mul(2862933555777941757)
.wrapping_add(3037000493),
}
}
pub(super) fn next_u64(&mut self) -> u64 {
self.state = self
.state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.state
}
pub(super) fn next_f64(&mut self) -> f64 {
((self.next_u64() >> 11) as f64) * (1.0 / ((1u64 << 53) as f64))
}
}