RaBitQ Library 0.8.0¶
This release gives the Python indexes a Faiss-style train → add → search
workflow, adds optional k-means sampling, tightens input and state validation,
and renames three C++ query-factor accessors.
Python changes¶
IvfIndex,HnswIndexandSymqgIndexfollowtrain→add→search. IVF-RaBitQ and HNSW-RaBitQ learn their own centroids intrain, configured byindex.cp, or accept external centroids throughset_centroids. Storage grows onadd, so constructors no longer takemax_elements. SymphonyQG builds its graph inadd, takesnbits(4, 8, or 32 for raw vectors) instead ofquantization_bits, and itssearchreplacessearch_batch.- 0.7 constructors,
build,is_built, positionaladdarguments and the SymphonyQGbuild,search_batchandquantization_bitsstill work, but raise aDeprecationWarningand will be removed in 0.9. The migration guide maps each old call to its 0.8 form. - Behavior changes that apply without a warning, detailed in the migration
guide:
trainandadddefault to every available CPU, including 0.7-styleadd(x)calls;searchand the deprecatedbuildkeep one thread.- Multi-threaded HNSW-RaBitQ
addis not reproducible; passnum_threads=1for the same graph on every run. - HNSW-RaBitQ capacity grows on
add, andsearchlimitskto the stored point count. - SymphonyQG
searchaccepts an omitted, zero or smallef, as HNSW-RaBitQ does, and one seed now drives both rotation and graph construction.
RaBitQKMeansandQGKMeansacceptmax_points_per_centroidto train on a seeded sample, and gainassign(x)for new vectors. Sampling is off by default for these classes; indextrainsamples at most 256 vectors per centroid. See training-set sampling.- Indexes reject NaN, infinite and oversized coordinates in data, centroids and
queries with
ValueError. See input requirements. HnswIndex.searchclampsefto the stored point count instead of failing on a very large value, with unchanged results.
C++ changes¶
-
Breaking: query-factor accessors take a
g_prefix, without aliases:Old New Classes k1xsumq()g_k1xsumq()SplitSingleQuery,BatchQuery,SplitBatchQuery,symqg::QuantizedQuerykbxsumq()g_kbxsumq()SplitSingleQuery,SplitBatchQuerykbase_sumq()g_kbase_sumq()SplitSingleQueryThe optional
k1xsumqpointer parameter ofBatchQueryand the SymphonyQG search entry points is now namedg_k1xsumq; its position and type are unchanged. - Breaking: saving an index that stores no points throwsstd::logic_error. Earlier releases wrote such an HNSW index as an empty file; loading old files is unchanged. - Breaking: the installed headerrabitqlib/utils/array.hppand itsrabitqlib::Arrayclass are removed; use standard containers instead. - IVF indexes can be created without a point count, trained or given centroids, then filled withadd. Pass the rotation seed to this constructor: four arguments such asIVF(dim, num_clusters, 4, METRIC_IP)select the point-count constructor instead. HNSW indexes keep their capacity argument and can be trained or given centroids before the firstadd. Existing constructors andconstructare unchanged. See IVF-RaBitQ and HNSW-RaBitQ. - On MSVC, libc++ and libstdc++ before GCC 11, a seed now gives a different FHT/Kac rotation, and therefore differentRaBitQKMeansresults, because the signs no longer depend on the standard library.faster_configreads a frozen scale table, so itst_constcan shift by a few percent on those toolchains. GCC 11+ Linux results are unchanged. A seededMatrixRotatoris now reproducible on the same toolchain. Saved indexes store their rotation and are unaffected. - Index entry points reject non-finite and oversized coordinates withstd::invalid_argument. HNSWcluster_id_ofvalidates its argument, IVF loading rejects trailing bytes, SymphonyQG rejectsef_constructionof zero, and the advanced encoders check their preconditions.
Fixes¶
- Code compiled for AVX2 or AVX-512 no longer reaches generic callers through shared inline functions and templates, which could fail on CPUs without those instructions. CI now checks ISA-specific builds for such functions.
- All three indexes save a regular file by writing a temporary file beside it and renaming it into place, so a failed save leaves the existing file unchanged. The replaced file keeps its permission bits, but not its owner or other hard links. Symbolic links and other non-regular destinations are still written in place.
- HNSW-RaBitQ rejects a zero capacity with
ValueError(C++:std::invalid_argument) and reports allocation failure asMemoryError(C++:std::bad_alloc) in construction,add,resizeandloadalike. - IVF initializers clamp
nprobeto the cluster count and size their output, instead of reading and writing out of range when called directly. - AVX2 and AVX-512 FHT rotation accepts overlapping input, as the other backends do.
- A failed copy assignment leaves the target object in its previous state, and HNSW search no longer prefetches addresses outside its allocation.
Internal changes¶
Index, SIMD and binding internals share more helpers, and identifier naming is
enforced by clang-tidy. Several pull requests optimize query preparation, LUT
quantization and graph search; their measurements are recorded in those pull
requests and do not establish a general result. No performance measurement was
made for k-means sampling or for the 2,000-cluster threshold at which index
train switches to QGKMeans. The published benchmarks
retain their recorded 0.7.1 provenance.
The package declares its license as an SPDX expression with expanded PyPI metadata, and the repository adds a security policy and a software citation.
Upgrading¶
Python code written for 0.7 runs unchanged apart from the behavior changes above; follow the deprecation warnings and the migration guide before 0.9. Index files from 0.7 load as trained indexes, and no conversion is required.
C++ consumers must rename the three query-factor accessors, replace
rabitqlib::Array, and must not save empty indexes. For example, zvec, which
uses the core and FastScan components, built and passed its RaBitQ tests after
renaming one kbxsumq() call in a test. The stable core API v1 and component
targets are unchanged.
Rebuild C++ consumers with matching headers and libraries; no cross-version
C++ binary ABI is promised.
Use the installation guide for supported platforms and the quick start for a complete Python example.