Repository navigation
test: make graph tests independent of graph connectivity - #228
Merged
Merged
Conversation
HNSW-RaBitQ, SymphonyQG and hnswlib-based IVF routing do not guarantee that every point is reachable. Tests that required every point after a multi-threaded or unseeded build failed now and then in CI. Tests that check exact results now build with a fixed seed and one thread: the large-dimension round trip, the SymphonyQG fixture, rebuild and portable cases, and the HNSW add, resize and remove cases whose searches compare graphs. Tests about parallel construction keep their threads and check the self-recall of every point instead: the example scripts, and IVF HNSW routing over 1,024 centroids. Tests whose graphs keep every reverse link stay exact. With M equal to the point count, the base layer holds 2M >= N - 1 links and never prunes one, so every point stays reachable under any thread interleaving. Their comments now say so.
This was referenced Nov 12, 2026
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.
Closes #216
HNSW-RaBitQ, SymphonyQG and the hnswlib graph behind IVF-RaBitQ's HNSW routing do not guarantee that every point is reachable. A multi-threaded or unseeded build can leave a point with no incoming edge, so tests that require every point to be found fail now and then in CI. #227 fixed two Python cases. This PR finishes the audit of the graph tests in
tests/unit/rabitqlib/index/andtests/python/.Changes
Two approaches, depending on what a test checks.
test_large_dimensions.py: SymphonyQG is seeded and builds with one thread. HNSW already used one thread and the default seed.test_symqg.py: the sharedbuilt_symqgfixture uses the 0.8 form withseed=42and one thread. The rebuild case passesseed=42to the 0.7 keyword form.test_portable_indexes.py: the SymphonyQG case uses the 0.8 form withseed=42and one thread.test_hnsw.py: three cases passnum_threads=1toadd, where the Python default is every CPU:add;test_examples.py: the example scripts build graphs with two threads, and SymphonyQG also has no fixed seed. HNSW and SymphonyQG now require a self-recall of at least 95% over all 128 points. IVF keeps its exact check.test_ivf_initializers.py: the parallel HNSW-routing build over 1,024 centroids requires a self-recall of at least 99% over every point.Tests that stay exact
Two tests listed in #216 keep their exact checks, although they build with several threads:
HnswAddTest.ParallelAddKeepsLabelsAndTheGraph;test_parallel_build_preserves_self_retrieval.Both set M to the point count. The base layer therefore holds 2M ≥ N − 1 links, so
mutually_connectnever prunes a reverse link, and every point stays reachable under any thread interleaving. Only their comments change, to say this. The same holds for the dense-graphaddtest, which keeps the default thread count, and for hnswlib routing over 32 or 33 centroids (kM = 16, somaxM0_ = 32).An aggregate check would have weakened these tests. An experimental build that drops 5 of 512 reverse links passed a 99% self-recall check, and failed the exact one.
Relation to #214
test_hnsw.py,test_symqg.py,test_portable_indexes.pyandtest_ivf_initializers.pyare still on the #214 list. This PR changes only the cases above. Intest_symqg.pyandtest_portable_indexes.py, the SymphonyQG fixture and the portable case already use the 0.8 form, so that they can be seeded.Checks
All on Linux x86-64, Xeon Gold 6418H (96 CPUs), GCC 14.3, Release,
RABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF; the Python package was built from this branch.Full suites: C++ 499/499; Python 1146 passed, 2 skipped.
check-format.shandcheck-python.shpass.Loops of the changed and restored tests, all with 0 failures:
HnswAddTest.ParallelAddKeepsLabelsAndTheGraphtest_hnsw.pycases,test_symqg.py,test_portable_indexes.py, the parallel routing case,test_large_dimensions.pytest_large_dimensions.pyStill fails when broken: a search returning wrong IDs fails every changed check. The exact checks also catch a lost reverse link, as described above. Apart from that experiment, the Python changes were not mutation-tested.
Review: an independent review found that a first version had weakened the two tests above, and had changed routing tests that cannot fail. Those changes were reverted.
Not covered