Fix IndexIVFPQ losing polysemous_ht/do_polysemous_training on I/O round-trip - #5487
Fix IndexIVFPQ losing polysemous_ht/do_polysemous_training on I/O round-trip#5487Juanpacol wants to merge 1 commit into
Conversation
…nd-trip
IndexIVFPQ never serialized its polysemous_ht and do_polysemous_training
fields. write_index() wrote only the fields common to the IVFPQ family
(by_residual, code_size, the ProductQuantizer, and the inverted lists)
under the "IwPQ"/"IwQR" fourcc, so read_index() had no data to restore
these two members and they silently reverted to their constructor
defaults (do_polysemous_training = false, polysemous_ht = 0) on every
load. A caller who trained polysemous codes and tuned a Hamming
threshold via ParameterSpace ("nprobe=10,ht=32") would get back an
index that searches as if polysemous filtering were never configured,
with no error or warning to indicate the parameters were dropped.
Reproduced the exact scenario from GH issue facebookresearch#2120: train an
"IVF100,PQ8" index, set nprobe/ht through ParameterSpace, write it to
disk, and read it back — index2.polysemous_ht came back as 0 instead
of 32.
Fix: introduce new fourcc tags "IwPh" (IndexIVFPQ) and "IwQh"
(IndexIVFPQR) that additionally write do_polysemous_training and
polysemous_ht right after the ProductQuantizer, before the inverted
lists. read_ivfpq() recognizes the new tags and reads the two extra
fields only when present, so existing "IwPQ"/"IwQR"/"IvPQ"/"IvQR"
files keep loading exactly as before (fields default to false/0, same
as pre-fix behavior) — this preserves backward and forward binary
compatibility with indexes already persisted to disk.
Added TestIORoundTrip.test_index_ivfpq_polysemous_ht in
tests/test_io.py, which builds an IndexIVFPQ, sets both fields, and
asserts they, plus search results, survive a serialize/deserialize
cycle.
Verified locally end to end:
- Built libfaiss and the SWIG Python extension from source (CMake +
SWIG, CPU-only, Accelerate BLAS/LAPACK).
- Confirmed the bug reproduces against the pre-fix code (polysemous_ht
32 -> 0 after read_index) and disappears with the fix applied
(32 -> 32).
- Full C++ suite: 284/285 tests pass (1 unrelated SIMD-hardware skip).
- Python suite (tests/test_io.py, tests/test_index.py): 79 passed,
including the legacy-format regression test TestIVFPQRead.test_reader,
confirming old on-disk indexes still deserialize correctly.
|
Hi @Juanpacol! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
Summary
IndexIVFPQ never serialized its
polysemous_htanddo_polysemous_trainingfields. Thewrite_index()function wrote only the fields common to the IVFPQ family (by_residual,code_size, the ProductQuantizer, and the inverted lists) under the "IwPQ"/"IwQR" fourcc, soread_index()had no data to restore these two members. They silently reverted to their constructor defaults (do_polysemous_training = false,polysemous_ht = 0) on every load.A caller who trained polysemous codes and tuned a Hamming threshold via ParameterSpace (
nprobe=10,ht=32) would get back an index that searches as if polysemous filtering were never configured, with no error or warning to indicate the parameters were dropped.Reproduction (Issue #2120)
Solution
Introduced new fourcc tags:
These new tags additionally write
do_polysemous_trainingandpolysemous_htright after the ProductQuantizer, before the inverted lists.The
read_ivfpq()function recognizes the new tags and reads the two extra fields only when present. Existing "IwPQ"/"IwQR"/"IvPQ"/"IvQR" files continue to load exactly as before (fields default tofalse/0, same as pre-fix behavior), preserving full backward and forward binary compatibility with indexes already persisted to disk.Testing & Verification
tests/test_io.py,tests/test_index.py): 79 passedTestIORoundTrip.test_index_ivfpq_polysemous_ht()validates that polysemous parameters survive a serialize/deserialize cycleTestIVFPQRead.test_readerconfirms old on-disk indexes deserialize correctlyCloses #2120