Switch SearchOutputBuffer to accepting Neighbors instead of tuples - #1284
Switch SearchOutputBuffer to accepting Neighbors instead of tuples#1284hildebrandmw wants to merge 6 commits into
SearchOutputBuffer to accepting Neighbors instead of tuples#1284Conversation
| let mut process = |n: u32| { | ||
| if let Some(entry) = accessor.scratch.distance_cache.get(&n) { | ||
| Some(Ok::<((u32, _), f32), ANNError>(((n, entry.1), entry.0))) | ||
| Some(Neighbor::new((n, entry.1), entry.0)) |
There was a problem hiding this comment.
Note: The original code was unnecessarily wrapping the intermediate value in a Result but didn't call any fallible methods.
There was a problem hiding this comment.
Pull request overview
This PR follows up on #1273 by completing the migration from “(id, distance) tuples” to the unified Neighbor type for search result emission. It also makes Neighbor’s distance type generic (defaulting to f32) so reranking/postprocessing code can sort with neighbor::ord::fast_distance directly rather than re-implementing distance ordering.
Changes:
- Generalize
NeighbortoNeighbor<I, D = f32>and update downstream uses to accommodatedistance()returning a reference. - Update
SearchOutputBufferto acceptNeighbor<I, D>inpush/extend, and adjust implementations and call sites across the workspace. - Switch multiple reranking implementations to sort with
neighbor::ord::fast_distanceinstead of hand-rolledpartial_cmplogic.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| diskann/src/neighbor/mod.rs | Makes Neighbor distance generic and updates BackInserter/buffer impls to accept Neighbor directly. |
| diskann/src/graph/search_output_buffer.rs | Changes SearchOutputBuffer trait to accept Neighbor in push/extend and updates internal buffer implementations/tests. |
| diskann/src/graph/search/range_search.rs | Updates range search logic and DistanceFiltered buffer wrapper to work with Neighbor and ref-returning distance(). |
| diskann/src/graph/test/cases/range_search.rs | Adjusts range-search test invariants for ref-returning Neighbor::distance(). |
| diskann/src/graph/index.rs | Updates distance extraction to dereference Neighbor::distance(). |
| diskann/src/graph/glue.rs | Removes tuple mapping and passes Neighbor iterators directly into output buffers. |
| diskann/src/flat/test/harness.rs | Adjusts test harness to collect copied distances from Neighbor::distance(). |
| diskann-tools/src/utils/ground_truth.rs | Updates ground-truth writing to dereference Neighbor::distance(). |
| diskann-providers/src/test_utils/search_utils.rs | Updates distance comparisons for ref-returning distance() (but one arithmetic use still needs updating). |
| diskann-providers/src/model/graph/provider/async_/postprocess.rs | Removes tuple conversion in postprocess output and forwards Neighbor directly. |
| diskann-providers/src/model/graph/provider/async_/inmem/full_precision.rs | Builds reranked results as Neighbor and sorts via neighbor::ord::fast_distance. |
| diskann-providers/src/index/wrapped_async.rs | Updates assertions to compare dereferenced distances. |
| diskann-providers/src/index/diskann_async.rs | Updates distance comparisons/mutations to dereference Neighbor::distance(). |
| diskann-garnet/src/provider.rs | Updates reranking paths to use Neighbor and sort via fast_distance. |
| diskann-garnet/src/lib.rs | Updates SearchOutputBuffer implementation and tests to accept/push Neighbor. |
| diskann-disk/src/search/provider/disk_provider.rs | Migrates reranking to store Neighbor (including cached results) and sort via fast_distance. |
| diskann-bftree/src/provider.rs | Updates reranking to produce/sort Neighbor instead of (id, f32) tuples. |
| diskann-benchmark-core/src/search/api.rs | Updates benchmark test plumbing to emit Neighbor into output buffers. |
| diskann-benchmark-core/src/internal/buffer.rs | Updates internal benchmark output buffer implementation to accept Neighbor items. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Returns a [`BufferState`] to indicate whether future insertions will succeed. | ||
| /// | ||
| /// Unlike the iterator interface, implementations should return [`BufferState::Full`] | ||
| /// if **future** insertions will fail to prevent unnecessary work. | ||
| fn push(&mut self, id: I, distance: D) -> BufferState; | ||
| fn push(&mut self, neighbor: Neighbor<I, D>) -> BufferState; |
| /// A [`SearchOutputBuffer`] wrapper around `&mut [Neighbor<I>]`. This can be used to | ||
| /// populate such a mutable slice as the result of [`crate::graph::DiskANNIndex::search`]. | ||
| #[derive(Debug)] | ||
| pub struct BackInserter<'a, I> { | ||
| buffer: &'a mut [Neighbor<I>], | ||
| pub struct BackInserter<'a, I, D = f32> { | ||
| buffer: &'a mut [Neighbor<I, D>], |
| /// Return the distance. | ||
| #[inline] | ||
| pub fn distance(&self) -> f32 { | ||
| self.distance | ||
| pub fn distance(&self) -> &D { | ||
| &self.distance | ||
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
diskann/src/neighbor/mod.rs:150
Neighbornow has a generic distance parameter (Neighbor<I, D>), butord::fast_distanceis still hard-coded toNeighbor<I>(i.e.,D = f32). That prevents usingfast_distanceas a sorting callback forNeighbor<I, D>whenDis notf32(even thoughpartial_cmpwould work for anyD: PartialOrd). Consider making this function generic overD(and updating the doc comment that currently mentionsf32::NAN).
pub fn fast_distance<I>(x: &Neighbor<I>, y: &Neighbor<I>) -> std::cmp::Ordering {
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1284 +/- ##
=======================================
Coverage 90.62% 90.62%
=======================================
Files 512 512
Lines 98807 98817 +10
=======================================
+ Hits 89541 89553 +12
+ Misses 9266 9264 -2
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
diskann/src/neighbor/mod.rs:154
Neighbornow has a generic distance type parameter (D), butord::fast_distanceis still specialized toNeighbor<I>(i.e.,D = f32). This prevents reusing the sorting callback forNeighbor<I, D>with other distance types (e.g.,f64orNotNan<f32>), which is one of the main benefits of makingdistancegeneric.
pub fn fast_distance<I>(x: &Neighbor<I>, y: &Neighbor<I>) -> std::cmp::Ordering {
x.distance()
.partial_cmp(y.distance())
.unwrap_or(std::cmp::Ordering::Equal)
}
With #1273, the structural
Eqbound onNeighborhas been removed. This PR is a follow-up that changes overSearchOutputBufferto acceptNeighborinstead of raw tuples. Outside of being symmetric, this has the nice property of allowing full-precision reranking methods to usediskann::neighbor::ord::fast_distanceas their sorting callback instead of hand-rolling the implementation every time.This PR does make the distance member of
Neighborgeneric, but defaults it tof32to keep syntactic compatibility.Key files to review:
diskann/src/neighbors.rs: Additional (defaulted) generic distance parameter toNeighbor.diskann/src/graph/search_output_buffer.rs: Updated trait definition.