feat(index): stop HNSW greedy descent before level 0 - #8035
Open
u70b3 wants to merge 1 commit into
Open
Conversation
Per the HNSW paper (Algorithm 2), the greedy (ef=1) descent should cover only levels max_level-1 down to 1; level 0 must be searched solely by the ef-bounded beam search starting from the level-1 entry point. The query path was greedily descending through level 0 first, which collapsed the beam's entry point into a deep local minimum -- extra distance computations and measurably lower recall at low ef. On SIFT1M (see the issue) stopping before level 0 is a repeatable Pareto improvement: +3.7% recall@10 at ef=16, +0.5% at ef=32, statistically equal recall at higher ef, and lower paired latency throughout. The online builder's query path already used the paper-correct range. Change both HNSW::run_search and HNSW::run_search_acorn, and add a structural regression test that drives the search over a hand-built graph with a counting distance calculator: any greedy step at level 0 shows up as extra distance computations. Fixes lance-format#5208
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #5208.
Per the HNSW paper (Algorithm 2), the greedy (ef=1) descent should cover only levels
max_level-1down to 1; level 0 must be searched solely by the ef-bounded beam search starting from the level-1 entry point.HNSW::run_searchandHNSW::run_search_acornwere greedily descending through level 0 first, which collapsed the beam's entry point into a deep local minimum — extra distance computations and measurably lower recall at low ef.The SIFT1M benchmark in the issue (3 independent graph builds, paired bootstrap intervals) shows stopping before level 0 is a repeatable Pareto improvement: +3.7% recall@10 at ef=16, +0.5% at ef=32, statistically equal recall at higher ef, and lower paired latency throughout the sweep. Note the online builder's query path (
OnlineHnswBuilder::search) and both build-timeinsertpaths already used the paper-correct range — only these two query paths were off.Test
New structural regression test
test_greedy_descent_stops_before_level_0drivesrun_search/run_search_acornover a hand-built 6-node, 2-level graph with a countingDistCalculator: the entry node is alone at level 1, so any greedy step at level 0 shows up as extra distance computations (6 expected, 16 pre-fix). I used a structural assertion rather than a recall threshold because on synthetic uniform data the recall delta is not sign-stable across graph sizes — the SIFT1M numbers in the issue are the recall evidence.Verification
cargo test -p lance-index --lib: 887 passedcargo test -p lance --lib -- vector index::: 724 passedcargo clippy --all --tests --benches -- -D warnings: cleancargo fmt --all: clean