Make Neighbor less incorrect#1273
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors diskann::neighbor::Neighbor into a tuple-like aggregate with private fields and removes its problematic Ord/Eq semantics, replacing them with explicit comparator functions in neighbor::ord so algorithms can choose the intended ordering (and make NaN behavior easier to document).
Changes:
- Simplifies
Neighbor(privateid/distance, accessors, tuple conversion) and moves ordering toneighbor::ord::*comparators. - Updates search, postprocess, and test code across crates to use
Neighbor::id(),Neighbor::distance(),Neighbor::as_tuple(), andneighbor::ordcomparators for sorting. - Improves test diagnostics by using
assert_eq_verbose!/VerboseEqin several updated test assertions.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| diskann/src/test/cmp.rs | Minor update in verbose_eq! macro expansion formatting. |
| diskann/src/neighbor/queue.rs | Updates fixed-capacity fast-path to compare by distance() instead of relying on Neighbor ordering. |
| diskann/src/neighbor/mod.rs | Core refactor: tuple-like Neighbor, accessors, VerboseEq (tests), new ord comparator module, and updated tests. |
| diskann/src/graph/test/provider.rs | Updates deletion filtering to use n.id() accessor. |
| diskann/src/graph/test/cases/range_search.rs | Updates range-search test helpers and baselines to use accessors/as_tuple(). |
| diskann/src/graph/test/cases/paged_search.rs | Updates paged-search invariants/baselines to use accessors/as_tuple(). |
| diskann/src/graph/test/cases/multihop.rs | Updates multihop tests to use id() accessor. |
| diskann/src/graph/search/record.rs | Updates visited-id extraction and recall accounting to use id() accessor. |
| diskann/src/graph/search/range_search.rs | Updates range search logic/tests to use distance()/id() accessors. |
| diskann/src/graph/search/paged.rs | Updates start-point filtering to use id() accessor. |
| diskann/src/graph/search/multihop_filter_search.rs | Switches sorts to neighbor::ord::fast_distance and updates ID extraction via id(). |
| diskann/src/graph/search/inline_filter_search.rs | Switches matched-results sorting to neighbor::ord::fast_distance and updates ID extraction via id(). |
| diskann/src/graph/internal/sorted_neighbors.rs | Replaces sort_unstable/select_nth_unstable ordering with neighbor::ord::fast_distance to keep behavior explicit. |
| diskann/src/graph/index.rs | Updates various ID extraction points and replaces sort_unstable() with sort_unstable_by(neighbor::ord::fast_distance). |
| diskann/src/graph/glue.rs | Uses Neighbor::as_tuple() for SearchOutputBuffer::extend and updates start-point filtering via id(). |
| diskann/src/flat/test/harness.rs | Removes bespoke neighbor sorting logic and uses neighbor::ord::fast_distance_total; updates tuple mapping/filtering via accessors. |
| diskann-tools/src/utils/ground_truth.rs | Replaces struct-literal neighbor construction and field access with Neighbor::new/accessors. |
| diskann-providers/src/test_utils/search_utils.rs | Updates sorting to neighbor::ord comparators and switches distance/id reads to accessors. |
| diskann-providers/src/model/graph/provider/async_/postprocess.rs | Updates deletion filtering and output extension to use accessors/as_tuple(). |
| diskann-providers/src/model/graph/provider/async_/inmem/full_precision.rs | Updates deletion filtering, vector reads, and extraction of ids/distances to use accessors. |
| diskann-providers/src/index/wrapped_async.rs | Updates assertions to use id()/distance() accessors. |
| diskann-providers/src/index/diskann_async.rs | Updates test sorting to neighbor::ord and replaces direct field mutation/access with accessor-based rebuilding. |
| diskann-label-filter/src/inline_beta_search/inline_beta_filter.rs | Updates attribute lookup to use id() accessor and avoids reconstructing neighbors unnecessarily. |
| diskann-garnet/src/provider.rs | Updates external ID mapping and push logic to use id()/distance() accessors. |
| diskann-disk/src/search/provider/disk_provider.rs | Updates candidate-id extraction to use id() accessor. |
| diskann-bftree/src/provider.rs | Updates vector reads and test assertions to use id() accessor. |
| diskann-benchmark/src/exhaustive/algos.rs | Updates output ID fill to use id() accessor. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // The following tests the behavior of NAN. | ||
| // | ||
| // This **must not** be taken as a guarantee of stability for this behavior. | ||
| let nan = Neighbor::new(3, 3.0); | ||
|
|
Codecov Report❌ Patch coverage is ❌ Your patch status has failed because the patch coverage (85.52%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #1273 +/- ##
==========================================
- Coverage 91.50% 91.49% -0.01%
==========================================
Files 498 498
Lines 95522 95546 +24
==========================================
+ Hits 87407 87424 +17
- Misses 8115 8122 +7
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Our current
Neighbortype has several inter-related issues:It structurally requires
Eqfor the ID type, even though this is only needed when comparing equality. This makes it impossible to construct aNeighborcontaining arbitrary external ID types and is the reasonSearchOutputBufferhas to take tuples instead ofNeighbors. This leads to duplicated sorting logic all over the code base.This structural requirement is preventing some aspects of ID handling cleanup.
Its members are public.
Its implementation of
Ordis not consistent with itsPartialEqmethod.Its implementation of
Ordis explicitly called out as invalid.This PR attempts to address many of these issues. Mainly, it turns
Neighborinto a pretty simple tuple-like type with the various ordering methods implemented as free functions inneighbors::ord. Algorithms that need to sortNeighborscan pick and choose the correctord::*methods needed, which in turn are quitegreppable. Theseord::*methods do not quite patch the issue of NaNs showing up in as distances, but at least we have a better way of documenting our requirements.