Skip to content

fix(vector/search): select per-searcher top-k by distance, not underflow-prone similarity (#933) - #934

Merged
mosuka merged 1 commit into
mainfrom
fix/933-searcher-distance-sort
Aug 2, 2026
Merged

fix(vector/search): select per-searcher top-k by distance, not underflow-prone similarity (#933)#934
mosuka merged 1 commit into
mainfrom
fix/933-searcher-distance-sort

Conversation

@mosuka

@mosuka mosuka commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the in-searcher sibling of #927's defect 2: per-segment searchers sorted and truncated their results by similarity descending, but distance_to_similarity's exp(-d) (Euclidean/Manhattan) underflows to 0.0f32 at long range — collapsing every distant candidate into a tie whose unstable-sort order decided top-k membership arbitrarily (DotProduct similarly saturates via clamp(0,1)). Discovered by #931's frozen-behavior oracle: a 20-doc single-segment grid corpus returned [0, 1, 2, 3, 10] where brute force says [0, 1, 2, 3, 4]. The fan-out layer was fixed by #927/#928; the drop here happens earlier, at the per-segment truncate, where the fan-out cannot recover it.

Changes

All five per-searcher sort sites (distance is already in the same tuple/struct at each) now order ascending by distance with a doc_id tiebreak, exactly matching the fan-out's #927 convention:

  • hnsw/searcher.rs: graph-path final_results sort (the repro site) + linear-fallback candidate sort
  • flat/searcher.rs: field-filtered + unfiltered scan sorts
  • ivf/searcher.rs: probed-cluster candidate sort

min_similarity filtering and every reported similarity value are unchanged — only ordering/truncation stops flowing through the underflow-prone conversion.

Tests

  • New searcher_distance_sort_test.rs: HNSW / Flat / IVF pinned against exact brute-force top-k on the underflow-triggering grid fixture (discovery case q = 0.2 + mid/far queries; IVF probes all clusters so the expectation is exact).
  • RED-proved via reversible swap: restoring the similarity sort reproduces the original [0, 1, 2, 3, 10] corruption.

Verification

  • cargo fmt --check and cargo clippy --all-targets -- -D warnings clean on stable and 1.97.0
  • cargo test -p laurus --lib: 1258 passed; --tests: all 65 binaries ok — no order-dependent test affected (quantized and distance orders agree wherever similarity does not underflow)
  • wasm32 cargo check -p laurus-wasm --target wasm32-unknown-unknown: clean

Closes #933. Same defect class as #927 defect 2 (fan-out layer, PR #928). Found by and unblocks #931 (#650 rerank-pipeline campaign PR-1).

@mosuka
mosuka merged commit 437ee2c into main Aug 2, 2026
22 checks passed
@mosuka
mosuka deleted the fix/933-searcher-distance-sort branch August 2, 2026 02:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(vector/search): per-searcher similarity sort underflows at long range — top-k membership becomes arbitrary

1 participant