use uqa_core::memory::BudgetedVec;
use super::{invalid, VamanaGraph, VamanaPoint};
use crate::diskann_index::{metric, random::SplitMix64, NavigationVector};
use crate::{read_control::StorageReadControl, StorageBackendResult};
const ORDER_STREAM: u64 = 0x9e37_79b9_7f4a_7c15;
const ENTRY_STREAM: u64 = 0x94d0_49bb_1331_11eb;
const ENTRY_SAMPLE: usize = 256;
type EntryVisitor<'a> = dyn FnMut(&NavigationVector) -> StorageBackendResult<()> + 'a;
type EntrySource<'a> = dyn FnMut(u64, &mut EntryVisitor<'_>) -> StorageBackendResult<()> + 'a;
fn sample(
population: u64,
count: usize,
random: &mut SplitMix64,
control: &StorageReadControl,
) -> StorageBackendResult<BudgetedVec<u64>> {
if count as u64 > population {
return Err(invalid("sample exceeds population"));
}
let mut chosen = BudgetedVec::new(control.memory());
chosen.reserve(count)?;
for upper in population - count as u64..population {
control.check()?;
let candidate = random.below(upper + 1, control)?;
chosen.push(if chosen.contains(&candidate) {
upper
} else {
candidate
})?;
}
chosen.sort_unstable();
Ok(chosen)
}
pub(super) fn edges(
graph: &mut VamanaGraph,
seed: u64,
control: &StorageReadControl,
) -> StorageBackendResult<()> {
if graph.len() < 2 {
return Ok(());
}
let mut random = SplitMix64(seed);
for node in 0..graph.len() {
control.check()?;
let mut neighbors = sample((graph.len() - 1) as u64, graph.degree, &mut random, control)?;
for neighbor in &mut *neighbors {
if *neighbor >= node as u64 {
*neighbor += 1;
}
}
graph.replace(node as u64, &neighbors, control)?;
}
Ok(())
}
pub(super) fn permutation(
count: usize,
seed: u64,
control: &StorageReadControl,
) -> StorageBackendResult<BudgetedVec<u64>> {
let mut order = BudgetedVec::new(control.memory());
order.reserve(count)?;
for index in 0..count {
metric::checkpoint(index, control)?;
order.push(index as u64)?;
}
let mut random = SplitMix64(seed ^ ORDER_STREAM);
for index in 0..count {
let selected = index + random.below((count - index) as u64, control)? as usize;
order.swap(index, selected);
}
Ok(order)
}
pub(super) fn entry(
points: &[VamanaPoint<'_>],
seed: u64,
control: &StorageReadControl,
) -> StorageBackendResult<u64> {
let dimensions = points
.first()
.ok_or_else(|| invalid("entry requires a point"))?
.vector
.coordinates()
.len();
select_entry(
points.len() as u64,
dimensions,
seed,
control,
&mut |node, visitor| visitor(points[node as usize].vector),
)
}
pub(in crate::diskann_index) fn select_entry(
count: u64,
dimensions: usize,
seed: u64,
control: &StorageReadControl,
read: &mut EntrySource<'_>,
) -> StorageBackendResult<u64> {
control.check()?;
if count == 0 || dimensions == 0 {
return Err(invalid("entry requires navigation points"));
}
let sample = sample(
count,
count.min(ENTRY_SAMPLE as u64) as usize,
&mut SplitMix64(seed ^ ENTRY_STREAM),
control,
)?;
let mut centroid = BudgetedVec::new(control.memory());
centroid.reserve(dimensions)?;
for coordinate in 0..dimensions {
metric::checkpoint(coordinate, control)?;
centroid.push(0.0)?;
}
for &node in &*sample {
visit(read, node, dimensions, &mut |vector| {
for (coordinate, value) in vector.coordinates().iter().enumerate() {
metric::checkpoint(coordinate, control)?;
centroid[coordinate] += value;
}
Ok(())
})?;
}
for (coordinate, value) in centroid.iter_mut().enumerate() {
metric::checkpoint(coordinate, control)?;
*value /= sample.len() as f64;
}
let mut best = (0, f64::INFINITY);
for node in 0..count {
control.check()?;
visit(read, node, dimensions, &mut |vector| {
let distance = metric::squared_distance(vector.coordinates(), ¢roid, control)?;
if distance < best.1 {
best = (node, distance);
}
Ok(())
})?;
}
control.check()?;
Ok(best.0)
}
#[cfg(test)]
pub(super) fn entry_sample(
count: usize,
seed: u64,
control: &StorageReadControl,
) -> StorageBackendResult<BudgetedVec<u64>> {
sample(
count as u64,
count.min(ENTRY_SAMPLE),
&mut SplitMix64(seed ^ ENTRY_STREAM),
control,
)
}
fn visit(
read: &mut EntrySource<'_>,
node: u64,
dimensions: usize,
consumer: &mut EntryVisitor<'_>,
) -> StorageBackendResult<()> {
let mut seen = false;
let mut failure = None;
let result = read(node, &mut |vector| {
if failure.is_some() {
return Err(invalid("entry source already failed"));
}
let outcome = if seen || vector.coordinates().len() != dimensions {
Err(invalid("entry source identity or dimensions differ"))
} else {
seen = true;
consumer(vector)
};
if let Err(error) = outcome {
failure = Some(error);
return Err(invalid("entry source rejected"));
}
Ok(())
});
if let Some(error) = failure {
return Err(error);
}
result?;
if !seen {
return Err(invalid("entry source omitted its vector"));
}
Ok(())
}