diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7aec5f72..abf8f7d4 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,5 +4,5 @@ contact_links: url: https://vectordb-ntu.github.io/RaBitQ-Library/ about: Find installation instructions, examples, and index guides. - name: Maintenance and feedback - url: https://github.com/VectorDB-NTU/RaBitQ-Library/blob/main/ROADMAP.md + url: https://github.com/VectorDB-NTU/RaBitQ-Library/blob/main/CONTRIBUTING.md#feedback-and-further-reading about: Find the maintainer, contribution guide, and published releases. diff --git a/.github/scripts/test_ci_changes.py b/.github/scripts/test_ci_changes.py index d8dedcf0..6afb658d 100644 --- a/.github/scripts/test_ci_changes.py +++ b/.github/scripts/test_ci_changes.py @@ -141,7 +141,7 @@ def test_ci_routing_changes_run_full_pr_ci_but_not_repeat_on_tagged_main(self): ".github/scripts/test_ci_changes.py", ".github/workflows/changes.yml", ".github/workflows/test.yaml", - "MAINTAINING.md", + "docs/maintaining.md", ] changed = "\0".join(paths) + "\0" with patch("ci_changes.git", side_effect=["merge-base", changed]): diff --git a/AGENTS.md b/AGENTS.md index 8f10047d..9d2ed6e3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,8 +34,8 @@ factors estimate L2 distance or inner product. | `tests/unit/`, `tests/python/` | C++ reference/unit tests and Python boundary/integration tests | | `docs/docs/` | MkDocs site sources | -See [README.md](README.md) for usage, [DEVELOPMENT.md](DEVELOPMENT.md) for tool setup and -[implementation recipes](DEVELOPMENT.md#implementation-recipes), and +See [README.md](README.md) for usage, [docs/development.md](docs/development.md) for tool setup and +[implementation recipes](docs/development.md#implementation-recipes), and [tests/README.md](tests/README.md) for build and test commands. CMake exposes `rabitqlib::core`, `rabitqlib::fastscan` (depending on core), and @@ -100,7 +100,7 @@ workspace APIs must never silently fall back to heap allocation or spawn worker Run the smallest relevant check first, then broaden for shared contracts and regression risk. Apply all matching rows; these are conditional requirements, not a checklist for every edit. Run tests in the foreground. Use the existing project interpreter wherever commands say `python`. -Tool versions and installation instructions are maintained in [DEVELOPMENT.md](DEVELOPMENT.md). +Tool versions and installation instructions are maintained in [docs/development.md](docs/development.md). | Change | Required verification | | --- | --- | @@ -121,7 +121,7 @@ If a required tool, dependency, or supported CPU is unavailable, report the bloc still needed; do not silently skip it or replace the user's environment. When a check fails, determine whether the change caused it. Fix introduced failures; report unrelated baseline failures without expanding scope. Preserve user edits when comparing baselines. -For focused static analysis, use the recipe in [DEVELOPMENT.md](DEVELOPMENT.md#focused-static-analysis). +For focused static analysis, use the recipe in [docs/development.md](docs/development.md#focused-static-analysis). Behavior changes require focused regression coverage. Python-visible core changes normally need both an underlying C++ test and a Python boundary test; Python-only changes need Python coverage. @@ -166,7 +166,7 @@ Recommended: `src/fastscan/fastscan_dispatch.cpp`; HNSW dispatch lives in `src/index/hnsw_dispatch.cpp` so core never references index search symbols. Keep calculations in backend source files; see the dispatch coverage table in - [DEVELOPMENT.md](DEVELOPMENT.md#dispatch-conventions-and-coverage). + [docs/development.md](docs/development.md#dispatch-conventions-and-coverage). - Dispatch caches function pointers in thread-safe function-local statics so calls before `main()` are safe regardless of translation-unit initialization order. Detection must use safe generic code; never execute a high-ISA kernel to find out whether the CPU supports it. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 23b21fb7..e9a883ae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -122,17 +122,17 @@ test during development, then apply every matching row below before submitting. | --- | --- | | Repository Markdown | Check accuracy and local links; `git diff --check` | | Documentation site | Above, plus the strict MkDocs build shown above | -| Python sources | [Python lint and formatting](DEVELOPMENT.md#python-formatting-and-linting), plus affected tests with `python -m pytest` | -| First-party C++ | [C++ formatting](DEVELOPMENT.md#c-formatting), affected build/tests, and [static analysis](DEVELOPMENT.md#static-analysis); focused analysis is sufficient during iteration | +| Python sources | [Python lint and formatting](docs/development.md#python-formatting-and-linting), plus affected tests with `python -m pytest` | +| First-party C++ | [C++ formatting](docs/development.md#c-formatting), affected build/tests, and [static analysis](docs/development.md#static-analysis); focused analysis is sufficient during iteration | | Bindings or Python-visible C++ behavior | Both Python and C++ checks; reinstall before testing and verify package/extension paths as described below | | Examples | Run the example and its language checks | -| Shell scripts | [ShellCheck](DEVELOPMENT.md#shell-scripts) on affected scripts | +| Shell scripts | [ShellCheck](docs/development.md#shell-scripts) on affected scripts | For shared code or changes affecting multiple indexes, run the full relevant C++ or Python suite. Changes to SIMD, metrics, packing, persistence, hot paths, or build configuration also need the applicable [specialized checks](AGENTS.md#verification-by-change-type). Consult the -[implementation recipes](DEVELOPMENT.md#implementation-recipes) before changing +[implementation recipes](docs/development.md#implementation-recipes) before changing these contracts; preserve public APIs and stored index formats unless a breaking change has been agreed on. @@ -144,11 +144,11 @@ In your PR, describe: **CI and merge requirements:** CI selects jobs from the changed files and may run broader checks than your focused local tests. Core library and binding changes -require the full [static analysis check](DEVELOPMENT.md#static-analysis) before merging. +require the full [static analysis check](docs/development.md#static-analysis) before merging. Tests and examples are intentionally excluded from CI clang-tidy; use focused local analysis for substantial logic changes. If a tool or supported CPU is unavailable, explain that in the PR so the -maintainer can help arrange validation. See [CI routing](MAINTAINING.md#checks-selected-by-changed-files) +maintainer can help arrange validation. See [CI routing](docs/maintaining.md#checks-selected-by-changed-files) for details. ## If setup fails @@ -158,7 +158,7 @@ for details. | CMake cannot find OpenMP or a compiler | Check the [platform-specific prerequisites](tests/README.md#prerequisites). On Apple Silicon, install `libomp` and pass its prefix as `OpenMP_ROOT`. Include the compiler version and CMake error when asking for help. | | Unsupported CPU or architecture | Use Linux or Windows x86-64 with AVX2/FMA, or Linux or macOS ARM64 with NEON. Disabling native tuning does not add an unsupported architecture. Report your CPU model in the issue or PR. | | Tests do not reflect your Python or C++ edits | Re-run the platform-specific install command above in the active environment, then check the import paths below. Re-run the regression test that exercises your change. | -| Formatting or analysis reports the wrong tool version | Use the pinned versions and executable overrides in the [development reference](DEVELOPMENT.md). | +| Formatting or analysis reports the wrong tool version | Use the pinned versions and executable overrides in the [development reference](docs/development.md). | Check which installed package and extension Python imports: @@ -186,7 +186,12 @@ another. Each task can be a separate PR. reviews. Review timing depends on availability; keep questions in the relevant issue or PR so others can learn from the answers. -- [Development reference](DEVELOPMENT.md): formatters, analysis, +- [Development reference](docs/development.md): formatters, analysis, performance requirements, and implementation recipes. -- [Maintainer reference](MAINTAINING.md): CI routing, releases, and PR labels. -- [Roadmap and project contacts](ROADMAP.md). +- [Maintainer reference](docs/maintaining.md): CI routing, releases, and PR labels. + +Use [GitHub Issues](https://github.com/VectorDB-NTU/RaBitQ-Library/issues/new/choose) +for bug reports, feature requests, and usage or contribution questions. Check +existing issues first. See [your first contribution](#your-first-contribution) +to get started and [GitHub Releases](https://github.com/VectorDB-NTU/RaBitQ-Library/releases) +for published changes. diff --git a/README.md b/README.md index cca6d7b5..b3fc20d5 100644 --- a/README.md +++ b/README.md @@ -2,247 +2,228 @@

RaBitQ Library

-

Compact vectors. Accurate distances. Fast search.

+

Compact vector quantization and search with RaBitQ.

- A research-backed C++17 library with Python bindings for RaBitQ
- vector quantization, clustering, and indexes including IVF, HNSW and SymphonyQG. + Build and query vectors with IVF, HNSW, and SymphonyQG,
+ or integrate RaBitQ quantization into your own search engine.
+ C++17 components and Python bindings.

PyPI Python versions Documentation - Paper DOI License

+ Vector indexes · + Quick start · + Quantization components · Documentation · - Python package · - Paper · - Releases · - Maintenance + Adoption and resources

+RaBitQ-Library is a research-backed library for vector quantization and +approximate nearest-neighbor search. See the [research papers](docs/docs/papers.md). + ## News - **v0.7.0:** Modular C++ components, stable core API v1, optional OpenMP for numerical components, and unified index counts. This documentation targets 0.7.0; see the [upgrade notes](docs/docs/releases/0.7.0.md). -- **v0.6.0:** Concurrent Python index searches, configurable IVF centroid routing, - 32-coordinate padding, and Eigen 5.0.1. See the - [upgrade notes](docs/docs/releases/0.6.0.md). -- **October 2026 — v0.5.2:** [HNSW add, resize, and remove](docs/docs/index/hnsw.md#updating-an-index), - batch search for IVF and SymphonyQG, and clustering and allocation optimizations. -- **September 2026 — Cross-platform support:** C++ builds and CPython 3.11–3.14 - wheels for Linux x86-64/ARM64, Windows x86-64, and macOS 14+ ARM64. -## Install +## Vector indexes + +Use the included indexes when you want construction, search, and save/load in one +library. All three support L2 and inner product; normalize vectors for cosine +search. Their structures, storage costs, and search parameters offer different +QPS–recall tradeoffs. + +| Index | Search approach | Storage and updates | +| --- | --- | --- | +| [IVF](docs/docs/index/ivf.md) | Partitioned search with batch scoring. | 1–9-bit codes or raw-vector reranking; add/remove. | +| [HNSW](docs/docs/index/hnsw.md) | Graph traversal over quantized vectors. | 1–9-bit codes plus graph edges; add/remove and resize. | +| [SymphonyQG](docs/docs/index/qg.md) | Graph search with batched distance estimation. | Raw or 4/8-bit storage; updates require rebuilding. | + +[Native clustering methods](docs/docs/clustering.md) prepare centroids and +assignments for IVF and HNSW. + +Search throughput and index size for three quantized configurations. +QPS uses the fastest measured setting reaching **Recall@10 ≥ 90%**; +actual recall and storage budgets differ. + + + +**GIST1M** + +1,000,000 vectors · 960 dimensions · L2 + +![GIST1M: QPS at Recall@10 ≥ 90%, serialized index size](docs/docs/benchmarks/figures/homepage-gist1m.svg) + + + +[Detailed results, settings, and measurement conditions](docs/docs/benchmarks/bench.md#homepage-selection). + +## Quick start + +### Python ```bash python -m pip install --upgrade "rabitqlib>=0.7.0" ``` -Wheels cover the platforms above. x86-64 requires AVX2/FMA and optionally uses +Wheels support CPython 3.11–3.14 on Linux x86-64/ARM64, Windows x86-64, +and macOS 14+ ARM64. x86-64 requires AVX2/FMA and optionally uses AVX-512; ARM64 uses NEON. See [platform requirements and source installation](docs/docs/installation.md#requirements). -## Python quick start +Build and search a small IVF index with synthetic data; no dataset download is +required. This example demonstrates the API, not performance. + +```python +import numpy as np +from rabitqlib import FinalAssignmentMode, IvfIndex, RaBitQKMeans + +rng = np.random.default_rng(42) +data = rng.standard_normal((500, 64)).astype(np.float32) +queries = rng.standard_normal((5, 64)).astype(np.float32) + +clustering = RaBitQKMeans( + 64, 5, num_threads=2, final_assignment=FinalAssignmentMode.Exact +) +clustering.train(data) +index = IvfIndex(dim=64, max_elements=len(data), num_clusters=5, nbits=4, metric="l2") +index.build(data, clustering.centroids, clustering.assignments) +ids, distances = index.search(queries, k=10, nprobe=5) +print(ids.shape, distances.shape) # (5, 10) (5, 10) +``` + +IDs identify input rows; distances here estimate squared L2 distance, with smaller +values first. Quantization remains approximate even when all clusters are probed. +The [complete quick start](docs/docs/quick_start.md) explains result handling, +measures recall while varying `nprobe`, and shows save/load. + +For other indexes, see the [HNSW](docs/docs/index/hnsw.md#minimal-workflow) and +[SymphonyQG](docs/docs/index/qg.md#minimal-workflow) examples. + +### C++ + +Use the [C++ integration guide](docs/docs/cpp_integration.md) for source builds, +component linking, and installation. Start with the +[C++ index examples](docs/docs/cpp_integration.md#c-examples), or use the +quantization components below in your own engine. + +## Quantization components + +Integrate encoding and distance estimation into your own storage and search +engine, independently of the included indexes. -Follow the [complete Python quick start](docs/docs/quick_start.md) to build and -search an IVF index, interpret its results, and save/load it using synthetic data. -Native [clustering](docs/docs/clustering.md) needs no external k-means package. +| Component | What it provides | Start here | +| --- | --- | --- | +| `rabitqlib::core` | Rotation, per-vector encoding, query preparation, and distance estimation. | [Core example](sample/cpp/core/) · [Stable v1 API](docs/docs/rabitq/core_api.md) | +| `rabitqlib::fastscan` | Batch encoding and lookup-table scoring over 32-vector layouts, built on core. | [FastScan example](sample/cpp/fastscan/) | -Then see the [IVF](docs/docs/index/ivf.md), [HNSW](docs/docs/index/hnsw.md), and -[SymphonyQG](docs/docs/index/qg.md) guides, or the -[dataset-based Python examples](sample/python/README.md). -See [threading and save/load paths](docs/docs/index/usage.md#threading-and-file-paths) -for runtime conventions. +See [component integration](docs/docs/cpp_integration.md#core-only-c-integration) +for build options and compatibility, the [quantizer guide](docs/docs/rabitq/quantizer.md) +for encoding choices, and the [core benchmark](benchmarks/core/README.md) for +component throughput and estimation error. -## Choose the right building block +## Adoption and resources -| Component | Use case | +### Built with RaBitQ-Library + +Two examples of direct library integration: + +| Project | Integration | | --- | --- | -| [Quantizer](docs/docs/rabitq/quantizer.md) | Integrate 1-bit or multi-bit encoding and distance estimation into your system. | -| [IVF](docs/docs/index/ivf.md) | Memory-efficient partitioned search, with optional raw-vector reranking. | -| [HNSW](docs/docs/index/hnsw.md) | Graph search directly over compact quantized vectors. | -| [SymphonyQG](docs/docs/index/qg.md) | Fast graph search with raw or packed 4-bit/8-bit vector storage. | - -IVF and HNSW support adding and removing vectors; HNSW also supports explicit -capacity resizing. See their guides for update costs and file compatibility. -The library supports L2 and inner product; normalize vectors for cosine search. - -C++ systems with their own indexes can link `rabitqlib::core` for encoding, -single-vector estimation and rotation, or `rabitqlib::fastscan` for batch scoring. -Configure `RABITQ_BUILD_INDEX=OFF` to omit ANN implementations; also disable -`RABITQ_BUILD_FASTSCAN` for pure core. Set `RABITQ_USE_OPENMP=OFF` for core/FastScan -without an OpenMP dependency; ANN builds retain OpenMP. `rabitqlib::index` -provides indexes and clustering. The 0.7.0 refactor removes the former -`rabitqlib::rabitqlib` target and forwarding headers; see the -[migration guide](docs/docs/modular_migration.md). Optional Python bindings retain -`import rabitqlib`. -For a source-stable, storage-neutral interface with caller-backed workspace, use -[`rabitqlib/core/api.hpp`](docs/docs/rabitq/core_api.md). Its v1 entry points make no heap -allocations on valid calls and keep code and factors separate. -See [modular integration](docs/docs/cpp_integration.md#core-only-c-integration), the -[core example](sample/cpp/core/) and [FastScan example](sample/cpp/fastscan/). -The [core benchmark](benchmarks/core/README.md) runs independently of ANN and Python. - -## Why RaBitQ? - -- **Compact codes:** Choose 1-bit or multi-bit quantization for your memory and accuracy needs. -- **Accurate estimates:** An asymptotically optimal error bound supports distance estimation. -- **Native CPU acceleration:** Runtime AVX2/AVX-512 dispatch on x86-64 and NEON on ARM64. - -Developed by the [VectorDB group](https://vectordb-ntu.github.io/) at Nanyang -Technological University. For GPU support, see [cuvs_rabitq](https://github.com/Stardust-SJF/cuvs_rabitq/tree/cuvs_ivf_rabitq). - -## RaBitQ across the vector-search ecosystem - -The projects below illustrate adoption of RaBitQ techniques across vector -search; this is not a list of direct dependencies on RaBitQ-Library. - -[txtai](https://github.com/neuml/txtai) uses the library as an ANN backend -([configuration](https://github.com/neuml/txtai/blob/master/docs/embeddings/configuration/ann.md#rabitq)). -Read [how zvec integrates RaBitQ-Library](docs/docs/integrations/zvec.md) for -another integration example. +| [txtai](docs/docs/integrations/txtai.md) | Uses the Python IVF and HNSW indexes as an embedding-search backend. | +| [zvec](docs/docs/integrations/zvec.md) | Uses core quantization in its own IVF-RaBitQ and HNSW-RaBitQ indexes, with FastScan batch scoring in IVF. | + +The linked walkthroughs describe integration boundaries and provide versioned +source references. + +### RaBitQ across the vector-search ecosystem + +The projects below illustrate adoption of RaBitQ techniques across vector search. +They do not necessarily depend on RaBitQ-Library; direct library integrations are +listed above. - - - - - + - + - - - - - - - - - - - - -
- Milvus logo
Milvus
+
+ Milvus logo
Milvus
- Faiss logo
Faiss
+
+ Faiss logo
Faiss
- NVIDIA cuVS logo
NVIDIA cuVS
+
+ NVIDIA cuVS logo
NVIDIA cuVS
- Microsoft DiskANN logo
Microsoft DiskANN
+
+ Microsoft DiskANN logo
Microsoft DiskANN
- VSAG logo
VSAG
+
+ VSAG logo
VSAG
+
+ VectorChord logo
VectorChord
- VectorChord logo
VectorChord
+
+ Volcengine OpenSearch logo
Volcengine OpenSearch
+
+ CockroachDB logo
CockroachDB
- Volcengine OpenSearch logo
Volcengine OpenSearch
+
+ Elasticsearch logo
Elasticsearch
- CockroachDB logo
CockroachDB
+
+ Apache Lucene logo
Apache Lucene
- Elasticsearch logo
Elasticsearch
+
+ turbopuffer logo
turbopuffer
- Apache Lucene logo
Apache Lucene
+
+ Zvec logo
Zvec
- turbopuffer logo
turbopuffer
-
- Zvec logo
Zvec
+
+ LanceDB logo
LanceDB
- LanceDB logo
LanceDB
+
+ Databricks logo
Databricks
- Databricks logo
Databricks
+
+ ClickHouse logo
ClickHouse
- ClickHouse logo
ClickHouse
-
- Qdrant logo
Qdrant
+
+ Qdrant logo
Qdrant
- Weaviate logo
Weaviate
+
+ Weaviate logo
Weaviate
-## C++ quick start - -Requires CMake 3.20+, a C++17 compiler with OpenMP, and an x86-64 CPU with -AVX2/FMA or an ARM64 CPU. First [get the 0.7.0 source](docs/docs/installation.md#get-the-source), -then build the library and examples from the repository root: - -```bash -cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -cmake --build build --parallel -``` - -Set `-DRABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF` for binaries that will run on -other CPUs. See [platform-specific build instructions](tests/README.md#prerequisites). - -### Use RaBitQ-Library in another C++ project - -The component targets below are introduced in 0.7.0. Use a matching checkout, -as described in the [migration guide](docs/docs/modular_migration.md). Add that -checkout as a submodule at `third_party/rabitqlib`, pin its revision, and link the -component you need: - -```cmake -set(RABITQ_BUILD_SAMPLES OFF CACHE BOOL "" FORCE) -add_subdirectory(third_party/rabitqlib) -target_link_libraries(my_program PRIVATE rabitqlib::index) -``` +### Further reading -The target propagates C++17, OpenMP, and required public-header definitions. -Optimization and native-tuning options apply only to RaBitQ's own targets; -configure your application's optimization flags separately. Explicit sanitizer -builds propagate instrumentation and runtime linkage for header compatibility. - -The [core v1 API](docs/docs/rabitq/core_api.md) has a source-compatibility contract; -other C++ interfaces may evolve. No cross-version C++ binary ABI is promised, so -rebuild consumers when updating the library. For an installed ANN package, use -`find_package(rabitqlib CONFIG REQUIRED COMPONENTS index)` and the same target. -ANN requires OpenMP; core/FastScan can be built with `RABITQ_USE_OPENMP=OFF`. - -See [submodule setup and version pinning](docs/docs/cpp_integration.md#use-in-another-c-project), -[C++ installation](docs/docs/cpp_integration.md#install-the-c-library), and the -[build and test guide](tests/README.md) for complete workflows. -Examples: [C++ indexes and quantization](docs/docs/cpp_integration.md#c-examples). -Benchmarks: [FAISS clustering comparison](docs/docs/clustering.md#compare-with-faiss) -and [GIST workflow](example.sh). - -## Citation - -If RaBitQ helps your research or system, please cite: - -> Jianyang Gao, Yutong Gou, Yuexuan Xu, Yongyi Yang, Cheng Long, and Raymond -> Chi-Wing Wong. “Practical and Asymptotically Optimal Quantization of -> High-Dimensional Vectors in Euclidean Space for Approximate Nearest Neighbor -> Search.” *Proceedings of the ACM on Management of Data* 3, 3, Article 202 -> (June 2025), 26 pages. [https://doi.org/10.1145/3725413](https://doi.org/10.1145/3725413). - -> Yutong Gou, Jianyang Gao, Yuexuan Xu, and Cheng Long. “SymphonyQG: Towards -> Symphonious Integration of Quantization and Graph for Approximate Nearest -> Neighbor Search.” *Proceedings of the ACM on Management of Data* 3, 1, -> Article 80 (February 2025), 26 pages. -> [https://doi.org/10.1145/3709730](https://doi.org/10.1145/3709730). - -> Jianyang Gao and Cheng Long. “RaBitQ: Quantizing High-Dimensional Vectors -> with a Theoretical Error Bound for Approximate Nearest Neighbor Search.” -> *Proceedings of the ACM on Management of Data* 2, 3, Article 167 (May 2024), -> 27 pages. [https://doi.org/10.1145/3654970](https://doi.org/10.1145/3654970). +- [Documentation](https://vectordb-ntu.github.io/RaBitQ-Library/) and [algorithm overview](docs/docs/rabitq/rabitq.md). +- [Papers and citations](docs/docs/papers.md) for the research behind the library. +- [Release notes](https://github.com/VectorDB-NTU/RaBitQ-Library/releases) and [0.7 migration guide](docs/docs/modular_migration.md). +- [GPU implementation](https://github.com/Stardust-SJF/cuvs_rabitq/tree/cuvs_ivf_rabitq) in cuVS. ## Contributing Start with the [contribution guide](CONTRIBUTING.md#your-first-contribution) or [starter tasks](CONTRIBUTING.md#starter-tasks). Report bugs and request features through [GitHub Issues](https://github.com/VectorDB-NTU/RaBitQ-Library/issues/new/choose). -See [maintenance and feedback](ROADMAP.md) for maintainer information. +See [maintenance and feedback](CONTRIBUTING.md#feedback-and-further-reading) for maintainer information. ## Acknowledgements diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index aef863db..00000000 --- a/ROADMAP.md +++ /dev/null @@ -1,12 +0,0 @@ -# Maintenance and feedback - -[Yutong Gou (@gouyt13)](https://github.com/gouyt13) handles issue -triage and code review. Review timing depends on availability. - -Use [GitHub Issues](https://github.com/VectorDB-NTU/RaBitQ-Library/issues/new/choose) -for bug reports, feature requests, and usage or contribution questions. -Check existing issues before opening a new one. - -See the [contributing guide](CONTRIBUTING.md#your-first-contribution) to get -started and [GitHub Releases](https://github.com/VectorDB-NTU/RaBitQ-Library/releases) -for published changes. diff --git a/benchmarks/README.md b/benchmarks/README.md index 828b5067..314f1dcb 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -32,3 +32,12 @@ python benchmarks/reproduce.py --benchmarks ivf --datasets gist1m python benchmarks/reproduce.py --datasets dbpedia --methods qgkmeans python benchmarks/reproduce.py --dry-run ``` + +## Homepage figures + +Run `python benchmarks/plot_homepage.py` to regenerate the GIST1M homepage figure in +both README.md and the documentation homepage from the committed query and indexing CSVs. This performs +no new measurements. It uses three fixed configurations on GIST1M, +selects the fastest measured setting reaching Recall@10 >= 90% without +interpolation, and preserves the historical measurement scope. The benchmark overview records +the actual recall and selected parameter for each bar. diff --git a/benchmarks/hotpaths/README.md b/benchmarks/hotpaths/README.md index f8d30c90..84a4f0fe 100644 --- a/benchmarks/hotpaths/README.md +++ b/benchmarks/hotpaths/README.md @@ -31,7 +31,7 @@ ctest --test-dir build-hotpaths -R 'Hotpath' --output-on-failure Set both `RABITQ_BUILD_INDEX=OFF` and `RABITQ_BUILD_FASTSCAN=OFF` for pure core. For the Python helper, additionally enable bindings and select your existing interpreter with `Python_EXECUTABLE` and its `pybind11_DIR`, as described in -[development setup](../../DEVELOPMENT.md). Multi-configuration generators need +[development setup](../../docs/development.md). Multi-configuration generators need `--config Release`, `ctest -C Release`, and the `Release/` executable subdirectory. Examples (from the repository root): diff --git a/benchmarks/plot_homepage.py b/benchmarks/plot_homepage.py new file mode 100644 index 00000000..2f7015cb --- /dev/null +++ b/benchmarks/plot_homepage.py @@ -0,0 +1,165 @@ +"""Regenerate homepage bars and tables from the committed benchmark CSVs.""" + +import csv +import json +from pathlib import Path + +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as plt + +ROOT = Path(__file__).resolve().parents[1] +FIGURES = ROOT / "docs/docs/benchmarks/figures" +# These are not matched-memory configurations. +METHODS = ( + ("rabitq-ivf-1p4", "IVF · 5-bit", "#635bff"), + ("rabitq-hnsw-1p4", "HNSW · 5-bit", "#149eaa"), + ("rabitq-symqg-4", "SymphonyQG · 4-bit", "#e29838"), +) + + +def main(): + with (FIGURES / "qps-recall-points.csv").open() as stream: + queries = list(csv.DictReader(stream)) + with (FIGURES / "indexing.csv").open() as stream: + builds = list(csv.DictReader(stream)) + plt.rcParams.update( + {"font.size": 11, "svg.fonttype": "none", "svg.hashsalt": "homepage"} + ) + sections = [] + detail_sections = [] + readme_sections = [] + for dataset, title, description in ( + ("gist1m", "GIST1M", "1,000,000 vectors · 960 dimensions · L2"), + ): + fig, axes = plt.subplots(1, 2, figsize=(10, 4.4)) + fig.subplots_adjust(left=0.065, right=0.98, top=0.72, bottom=0.22, wspace=0.36) + fig.suptitle( + title, + x=0.065, + y=0.98, + ha="left", + fontsize=20, + fontweight="bold", + color="#0f172a", + ) + fig.text( + 0.065, + 0.88, + "Fastest measured QPS at Recall@10 ≥ 90% · RaBitQ-Library 0.7.0", + color="#475569", + fontsize=12, + ) + values = [[], []] + table = [ + "| Configuration | Measured Recall@10 | Selected search setting |", + "| --- | ---: | --- |", + ] + for method, label, _ in METHODS: + rows = [ + r for r in queries if r["dataset"] == dataset and r["method"] == method + ] + assert rows, (dataset, method) + eligible = [r for r in rows if float(r["recall_at_10"]) >= 0.90] + assert eligible, (dataset, method, "90% recall not reached") + selected = max(eligible, key=lambda r: float(r["qps"])) + values[0].append(float(selected["qps"])) + matches = [ + r for r in builds if r["dataset"] == dataset and r["method"] == method + ] + assert len(matches) == 1, (dataset, method) + build = matches[0] + gib = int(build["serialized_index_bytes"]) / 2**30 + values[1].append(gib) + setting = json.loads(selected["setting"]) + parameter = ( + f"nprobe={setting['nprobe']}" + if "nprobe" in setting + else f"ef={setting['ef_search']}" + ) + table.append( + f"| {label} | {float(selected['recall_at_10']):.2%} | `{parameter}` |" + ) + labels = ["IVF\n5-bit", "HNSW\n5-bit", "SymphonyQG\n4-bit"] + colors = [m[2] for m in METHODS] + for ax, numbers, heading, direction, precision in zip( + axes, + values, + ("Queries / second", "Index size · GiB"), + ("Higher is better", "Lower is better"), + (0, 2), + ): + bars = ax.bar(range(3), numbers, width=0.55, color=colors, zorder=3) + ax.bar_label( + bars, + labels=[f"{v:,.{precision}f}" for v in numbers], + padding=7, + fontsize=12, + fontweight="bold", + color="#0f172a", + ) + ax.set_ylim(0, max(numbers) * 1.25) + ax.set_xticks(range(3), labels, fontsize=11) + ax.set_title( + heading + "\n" + direction, + loc="left", + fontsize=12, + pad=16, + color="#334155", + ) + ax.grid(axis="y", color="#e5e7eb", zorder=0) + ax.spines[["top", "right", "left"]].set_visible(False) + ax.spines["bottom"].set_color("#cbd5e1") + ax.tick_params(axis="both", length=0, colors="#475569") + fig.text( + 0.065, + 0.035, + "1 search thread · 1 query/call · Index size is serialized storage, not RAM", + fontsize=10, + color="#64748b", + ) + filename = f"homepage-{dataset}.svg" + path = FIGURES / filename + fig.savefig(path, metadata={"Date": None}) + path.write_text( + "\n".join(line.rstrip() for line in path.read_text().splitlines()) + "\n" + ) + plt.close(fig) + body = [ + description, + "", + f"![{title}: QPS at Recall@10 ≥ 90%, serialized index size](benchmarks/figures/{filename})", + ] + detail_sections.append(f"### {title}\n\n" + "\n".join(table)) + sections.append(f"**{title}**\n\n" + "\n".join(body)) + readme_body = sections[-1].replace( + "](benchmarks/figures/", "](docs/docs/benchmarks/figures/" + ) + readme_sections.append(readme_body) + details = ROOT / "docs/docs/benchmarks/bench.md" + start, end = "", "" + before, rest = details.read_text().split(start) + _, after = rest.split(end) + details.write_text( + before + start + "\n\n" + "\n\n".join(detail_sections) + "\n\n" + end + after + ) + readme = ROOT / "README.md" + start, end = "", "" + before, rest = readme.read_text().split(start) + _, after = rest.split(end) + readme.write_text( + before + start + "\n\n" + "\n\n".join(readme_sections) + "\n\n" + end + after + ) + homepage = ROOT / "docs/docs/index.md" + text = homepage.read_text() + start, end = "", "" + before, rest = text.split(start) + _, after = rest.split(end) + homepage.write_text( + before + start + "\n\n" + "\n\n".join(sections) + "\n" + end + after + ) + + +if __name__ == "__main__": + main() diff --git a/DEVELOPMENT.md b/docs/development.md similarity index 97% rename from DEVELOPMENT.md rename to docs/development.md index a1e85f8f..cb5ccb63 100644 --- a/DEVELOPMENT.md +++ b/docs/development.md @@ -1,10 +1,10 @@ # Development reference -Start with [Contributing to RaBitQ](CONTRIBUTING.md) for setup and your first PR. +Start with [Contributing to RaBitQ](../CONTRIBUTING.md) for setup and your first PR. This reference covers tool configuration and changes to library internals. Run commands from the repository root in your activated -[project environment](CONTRIBUTING.md#python-environment), after installing -the [source-build prerequisites](CONTRIBUTING.md#setup-for-code-changes). +[project environment](../CONTRIBUTING.md#python-environment), after installing +the [source-build prerequisites](../CONTRIBUTING.md#setup-for-code-changes). Refresh APT package lists with `sudo apt-get update` before installing tools. ## Compiler and tool versions @@ -24,7 +24,7 @@ The LLVM APT release branch supplies updated builds within that major version; CI prints the exact tool versions, rather than promising a fixed patch build. Upgrading them requires reviewing format differences and diagnostics together with the scripts and CI configuration. The Ubuntu CI setup lives in -[setup-clang](.github/actions/setup-clang/action.yml). +[setup-clang](../.github/actions/setup-clang/action.yml). ## C++ formatting @@ -156,7 +156,7 @@ tests and benchmarks. Disable optional targets above for a smaller check; skippe files are listed explicitly. Installed-package consumers and the historical fixture generator have separate build configurations and must be checked in those contexts. The historical generator requires the pinned release headers described in -[its README](tests/fixtures/persistence/README.md). +[its README](../tests/fixtures/persistence/README.md). Headers are checked as main files with `-x c++-header`, preserving `#pragma once` for mutually including headers. Include Cleaner does not report findings in included @@ -354,7 +354,7 @@ must happen in generic code first. their function name alone does not specify the LUT format. Estimator templates require explicit accumulator arguments so new call sites must make this choice deliberately. Never bypass public dimension checks, unsupported-CPU errors or initialization safety. -- Keep performance regression workloads in [component hot-path benchmarks](benchmarks/hotpaths/README.md). +- Keep performance regression workloads in [component hot-path benchmarks](../benchmarks/hotpaths/README.md). Validate numerical results first, then inspect generated code and alternate comparable baseline/candidate runs. Keep failed experiments and quality results. Fusion, inlining, scratch reuse and dispatch changes need measurements across relevant vector sizes; @@ -405,7 +405,7 @@ Core must not depend on FastScan scoring or index headers, objects or symbols. F must not depend on indexes. The canonical headers are under `core/`, `fastscan/` and `index/{ivf,hnsw,symqg}/`. Old `quantization/`, `utils/rotator.hpp` and generic `index/{query,lut,estimator}.hpp` forwarders have been removed. Migrate consumers to -their owning module; see the [migration guide](docs/docs/modular_migration.md). +their owning module; see the [migration guide](../docs/docs/modular_migration.md). Core-only installs exclude FastScan/index/clustering headers and vendored hnswlib. Installed detail headers support templates and are not public APIs. Internal-only non-template dispatch declarations stay in `src/`. @@ -440,12 +440,12 @@ ANN still requires it. Guard optional numerical OpenMP calls/pragmas with the ex functions. Test both configurations, including installed consumers. Python is a MODULE extension with an optional build-tree alias; it is not a C++ link library or a C++ package component. Validate these boundaries with the -[external-consumer checks](tests/README.md#core-only-cmake-package-tests). +[external-consumer checks](../tests/README.md#core-only-cmake-package-tests). ### Stable core API changes `core/api.hpp` is the source-stable float32 v1 boundary; the implementation is -`src/core/api.cpp`. See [its contract](docs/docs/rabitq/core_api.md). Keep the frozen +`src/core/api.cpp`. See [its contract](../docs/docs/rabitq/core_api.md). Keep the frozen consumer, API unit tests and both `core_api_no_alloc_*` processes in the validation matrix. The scalar allocation process forces cached dispatch onto the exact event fallback. Do not replace that fallback with an approximate winner to avoid allocations. diff --git a/docs/docs/benchmarks/bench.md b/docs/docs/benchmarks/bench.md index ef522f32..6baedb65 100644 --- a/docs/docs/benchmarks/bench.md +++ b/docs/docs/benchmarks/bench.md @@ -179,3 +179,30 @@ reuses saved indexes; export runs no experiments. `--dry-run` previews commands; [Query CSV](figures/qps-recall-points.csv) · [Indexing CSV](figures/indexing.csv) · [K-means CSV](figures/kmeans.csv) + +## Homepage selection + +The homepage shows IVF/HNSW at 5 total bits (one sign bit plus four extra bits) +and SymphonyQG at 4 bits. For each GIST1M configuration, the QPS bar selects +the fastest measured setting with Recall@10 >= 90%, without interpolation. +Actual recall and total storage differ; this is neither an exact-recall nor an +equal-memory comparison. The tables below identify the selected CSV rows. + + + +### GIST1M + +| Configuration | Measured Recall@10 | Selected search setting | +| --- | ---: | --- | +| IVF · 5-bit | 92.85% | `nprobe=128` | +| HNSW · 5-bit | 92.56% | `ef=512` | +| SymphonyQG · 4-bit | 90.69% | `ef=512` | + + + +The figures use the 0.7.0 measurements described above, with one search thread +and one query per call. Each setting has one timing pass. QPS includes Python +call overhead after warm-up and excludes loading. Index size is serialized +storage including sidecars, not resident or peak RAM. +See [IVF results](ivf.md) and [graph results](graphs.md) for full curves, +construction costs, and additional precision/storage configurations. diff --git a/docs/docs/benchmarks/figures/homepage-gist1m.svg b/docs/docs/benchmarks/figures/homepage-gist1m.svg new file mode 100644 index 00000000..9a906520 --- /dev/null +++ b/docs/docs/benchmarks/figures/homepage-gist1m.svg @@ -0,0 +1,345 @@ + + + + + + + + image/svg+xml + + + Matplotlib v3.11.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + IVF + 5-bit + + + + + + HNSW + 5-bit + + + + + + SymphonyQG + 4-bit + + + + + + + + + + + 0 + + + + + + + + + 500 + + + + + + + + + 1000 + + + + + + + + + 1500 + + + + + + + + + 2000 + + + + + + + + + 2500 + + + + + + + + + 3000 + + + + + + + + + + + + + + + + + 948 + + + 1,466 + + + 2,642 + + + Queries / second + Higher is better + + + + + + + + + + + IVF + 5-bit + + + + + + HNSW + 5-bit + + + + + + SymphonyQG + 4-bit + + + + + + + + + + + 0 + + + + + + + + + 1 + + + + + + + + + 2 + + + + + + + + + 3 + + + + + + + + + 4 + + + + + + + + + 5 + + + + + + + + + + + + + + + + + 0.60 + + + 0.72 + + + 4.39 + + + Index size · GiB + Lower is better + + + + GIST1M + + + Fastest measured QPS at Recall@10 ≥ 90% · RaBitQ-Library 0.7.0 + + + 1 search thread · 1 query/call · Index size is serialized storage, not RAM + + + + + + + + + + + diff --git a/docs/docs/clustering.md b/docs/docs/clustering.md index 11ceea03..7cb16db7 100644 --- a/docs/docs/clustering.md +++ b/docs/docs/clustering.md @@ -69,7 +69,7 @@ clusters for the C++ indexing examples: File mode uses exact final assignment and the default training parameters. Append `ip` for spherical clustering of normalized input; the default is `l2`. -`example.sh` runs the C++ clustering and indexing workflow without Python. +`sample/run_cpp.sh` runs the C++ clustering and indexing workflow without Python. ## Parameters and input diff --git a/docs/docs/cpp_integration.md b/docs/docs/cpp_integration.md index 645c78ea..6145ef82 100644 --- a/docs/docs/cpp_integration.md +++ b/docs/docs/cpp_integration.md @@ -52,7 +52,7 @@ indexing and querying workflows: - [Core quantization and distance estimation](https://github.com/VectorDB-NTU/RaBitQ-Library/blob/main/sample/cpp/core/main.cpp) The core example is built as the `core_example` CMake target. For the GIST benchmark workflow, see -[`example.sh`](https://github.com/VectorDB-NTU/RaBitQ-Library/blob/main/example.sh). +[`sample/run_cpp.sh`](https://github.com/VectorDB-NTU/RaBitQ-Library/blob/main/sample/run_cpp.sh). ## Use in another C++ project diff --git a/docs/docs/index.md b/docs/docs/index.md index db450723..034a4294 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -39,25 +39,35 @@ from compact quantization to complete indexes.

-## Choose an index +## Choose your index -| Index | Best fit | Typical relative memory | -| --- | --- | --- | -| [IVF RaBitQ](index/ivf.md) | Large datasets and predictable storage | Lowest | -| [HNSW RaBitQ](index/hnsw.md) | General-purpose graph search | Moderate | -| [SymphonyQG](index/qg.md) | Latency-focused graph search | Highest | +Explore search throughput, recall, and storage for three quantized configurations. -Memory, latency, and recall depend on the dataset and search settings. See the -[index usage guide](index/usage.md#estimate-storage) for storage estimates and the -[benchmarks](benchmarks/bench.md) for measured workloads. + + +**GIST1M** + +1,000,000 vectors · 960 dimensions · L2 + +![GIST1M: QPS at Recall@10 ≥ 90%, serialized index size](benchmarks/figures/homepage-gist1m.svg) + + +QPS uses the fastest measured setting reaching **Recall@10 ≥ 90%**; +actual recall and storage budgets differ. +[Detailed results, settings, and measurement conditions](benchmarks/bench.md#homepage-selection). + +## Built with RaBitQ-Library + +- [txtai](integrations/txtai.md) uses the Python IVF and HNSW indexes as an + embedding-search backend. See how to select the backend and its search settings. +- [zvec](integrations/zvec.md) uses core quantization in its IVF-RaBitQ and + HNSW-RaBitQ implementations, with FastScan batch scoring in IVF. See the + integration walkthrough for storage, scoring, and platform boundaries. + +These are direct library integrations, with versioned downstream source links. ## Explore further - [Algorithm overview](rabitq/rabitq.md) for the rotation, encoding, and scoring workflow. - [RaBitQ Library 0.7.0 release notes](releases/0.7.0.md) for new APIs and upgrade guidance. - -## Research papers - -- [1-bit RaBitQ](https://doi.org/10.1145/3654970) · 2024 · Quantization with a theoretical error bound. -- [SymphonyQG](https://doi.org/10.1145/3709730) · 2025 · Quantization and graph search. -- [Multi-bit RaBitQ](https://doi.org/10.1145/3725413) · 2025 · Higher-bit quantization. +- [Papers and citations](papers.md) for the original RaBitQ method, its multi-bit extension, and SymphonyQG. diff --git a/docs/docs/integrations/txtai.md b/docs/docs/integrations/txtai.md new file mode 100644 index 00000000..417dbf85 --- /dev/null +++ b/docs/docs/integrations/txtai.md @@ -0,0 +1,80 @@ +# txtai: using the complete indexes + +[txtai](https://github.com/neuml/txtai) exposes RaBitQ-Library through its +`rabitq` ANN backend. Applications can select an IVF or HNSW index within txtai's +embedding-search workflow, while txtai handles the surrounding application API +and result conversion. + +This walkthrough was checked on October 21, 2026 against txtai commit +[`0ea7cb0d8234`][snapshot]. It describes the downstream adapter at that revision; +it is a source snapshot, not a verification of every published txtai package. +Consult the [current configuration reference][current-config] for later changes. + +## Select the backend + +In an existing txtai embeddings configuration, select `backend: rabitq` and add +the corresponding settings. This fragment uses four-bit IVF: + +```yaml +backend: rabitq +rabitq: + mode: ivf + nbits: 4 +``` + +Keep your existing embedding model and other application settings. Install +optional dependencies according to [txtai's installation guide][install]; its +adapter imports both `rabitqlib` and scikit-learn. The inspected +[package configuration][dependencies] declares `rabitqlib>=0.7.0` in its `ann` extra. The fragment above selects +the backend, rather than providing a complete model-and-data example. + +`clusters` controls the KMeans centroid count in both modes: IVF uses these +centroids for partitioning, while HNSW uses them for residual quantization. +`nprobe` controls how many IVF clusters are searched. For HNSW, select `mode: hnsw` and use `m`, `efconstruction`, and +`efsearch`. Both modes accept 1–9 quantization bits; IVF also accepts `nbits: 32` +for original-vector reranking. Measure recall and latency on representative +queries when choosing precision and search parameters. + +## How the integration works + +The [adapter source][adapter] shows the boundary between the projects: + +| Stage | txtai's role | RaBitQ-Library's role | +| --- | --- | --- | +| Build | Converts embeddings to contiguous float32 and uses scikit-learn KMeans to produce centroids and assignments. | Builds `IvfIndex` or `HnswIndex` with the inner-product metric. | +| Search | Selects `nprobe` or `ef`, drops missing-result IDs, and converts returned distances to scores with `1 - distance`. | Searches the native index and returns IDs and distances; quantized modes return estimates, while IVF raw mode uses original-vector reranking. | +| Persistence | Calls the backend's save/load methods. | Stores and restores the native index state. | +| Updates | Routes IVF append/delete requests to the backend. | Adds vectors or removes them from search. | + +This is an example of integrating complete indexes: txtai calls the Python index +APIs without implementing RaBitQ encoding or graph traversal itself. + +## Boundaries to keep in mind + +At the inspected revision, txtai enables append/delete only for IVF. Its HNSW +adapter raises `NotImplementedError` for append/delete through the +[ANN base class][base], even though RaBitQ-Library itself supports HNSW updates. +IVF additions reuse the original clusters, and deletions retain their storage; +txtai recommends rebuilding when new data differs enough to affect recall. +See its [versioned configuration documentation][config] for these limits. + +The adapter requests inner-product search and converts its `1 - dot` distance +convention back to scores. For normalized database and query embeddings, these are cosine-similarity +estimates in quantized modes; normalization does not remove quantization error. + +The source inspection establishes the integration path. It does not establish +production deployment scale or a performance gain, and this configuration has +not been exercised end to end in this documentation update. + +To use the same indexes directly, follow the library's [Python quick start](../quick_start.md) +and [HNSW guide](../index/hnsw.md). To integrate only quantization into your own +index, see the [zvec walkthrough](zvec.md). + +[snapshot]: https://github.com/neuml/txtai/tree/0ea7cb0d8234497e8ea9ed1a0f2f58492e00460b +[adapter]: https://github.com/neuml/txtai/blob/0ea7cb0d8234497e8ea9ed1a0f2f58492e00460b/src/python/txtai/ann/dense/rabitq.py +[config]: https://github.com/neuml/txtai/blob/0ea7cb0d8234497e8ea9ed1a0f2f58492e00460b/docs/embeddings/configuration/ann.md#rabitq +[current-config]: https://github.com/neuml/txtai/blob/master/docs/embeddings/configuration/ann.md#rabitq +[install]: https://neuml.github.io/txtai/install/#ann + +[dependencies]: https://github.com/neuml/txtai/blob/0ea7cb0d8234497e8ea9ed1a0f2f58492e00460b/setup.py +[base]: https://github.com/neuml/txtai/blob/0ea7cb0d8234497e8ea9ed1a0f2f58492e00460b/src/python/txtai/ann/base.py diff --git a/docs/docs/integrations/zvec.md b/docs/docs/integrations/zvec.md index 802207a5..e7199528 100644 --- a/docs/docs/integrations/zvec.md +++ b/docs/docs/integrations/zvec.md @@ -1,31 +1,71 @@ # How zvec integrates RaBitQ-Library Zvec uses RaBitQ-Library for rotation, quantization, and distance estimation -inside its own IVF and HNSW indexes. Zvec manages storage, search traversal, -filtering, and result collection. +inside its IVF-RaBitQ and HNSW-RaBitQ implementations. Zvec manages storage, search traversal, +filtering, and result collection. This is a component integration: zvec links +core/FastScan functionality while keeping its own index implementations. + +Checked on October 21, 2026 against the merged RaBitQ 0.7 migration at commit +[`c5496863ee98`](https://github.com/alibaba/zvec/tree/c5496863ee98bca155abca1e9e4aadf46e9ebed1). +This identifies the inspected source, not a released zvec package version. + +## Where the components fit | Index | How it uses RaBitQ-Library | | --- | --- | | **IVF** | The [reformer][ivf-reformer] quantizes vectors in batches and prepares query lookup tables. The [search code][ivf-entity] uses FastScan estimates and extra-bit refinement for selected candidates. | -| **HNSW** | The [reformer][hnsw-reformer] quantizes individual vectors. The [query algorithm][hnsw-query] estimates distances from those codes during graph traversal. | +| **HNSW** | The [reformer][hnsw-reformer] quantizes individual vectors. The [query algorithm][hnsw-query] uses core per-vector estimators during graph traversal; it does not use the IVF FastScan batch-scoring path. | + +The inspected reformers serialize centroids, padded dimensions, and rotator +state, then restore them when loading. Their query preprocessing uses the stored +padded dimension and rotation. Preserving this state is a key integration +contract; this source walkthrough is not a complete persistence audit. + +## A maintained integration + +[PR #814](https://github.com/alibaba/zvec/pull/814), merged on October 19, 2026, +upgraded zvec's bundled RaBitQ-Library from 0.3.8 to 0.7.0. It replaced custom +SIMD source lists and a local lookup-table reuse patch with upstream component +targets and `SplitBatchQuery::reset`. -Both integrations preserve centroids and rotator state with the index and use -padded dimensions consistently for rotated vectors and queries. These are -key contracts when embedding the quantizer in another system. +When `RABITQ_SUPPORTED` is enabled, the [dependency configuration][dependency] +links `rabitqlib::fastscan`, which also links core, +and disables RaBitQ's standalone indexes, Python bindings, OpenMP, and native +tuning. The [IVF query preparation][ivf-reformer] passes lookup-table buffers owned by +its query state to `SplitBatchQuery::reset`. This permits reuse when that state +is reused; it does not imply that every search is allocation-free. -This overview follows zvec commit -[`44d8bc6f7c3d`](https://github.com/alibaba/zvec/tree/44d8bc6f7c3d656efde2641c9410432ea14c1657). -Its [bundled dependency][dependency] includes local patches for lookup-table -reuse and SIMD error handling; check the pinned version before adapting its code. +This change illustrates a practical integration benefit of the component split: +zvec can consume upstream quantization and scoring implementations while retaining +control of its indexes and threading. The PR description reports compatibility checks and +Linux x86-64 validation, along with untested platforms and incomplete full-database +sanitizer checks. Those checks were not rerun for this walkthrough; it +is not evidence of production deployment scale or a measured speedup. + +## Platform and API boundaries + +At this snapshot, zvec's [build gate][platform] enables RaBitQ only for Linux +x86-64 or Windows x86-64 with MSVC, and only if its compiler checks pass. +Other platforms use stub implementations. RaBitQ-Library's own ARM64 support +does not make these zvec integrations available on ARM64 or macOS. + +The code uses the advanced encoder, query, estimator, and record-layout headers. +It does not use the stable `rabitqlib::quant::v1` API. The recommendation below is +an entry point for new integrations, not a description of zvec's API usage. + +## Use this pattern in your own engine For a new per-vector integration, start with the [stable core API v1](../rabitq/core_api.md#minimal-example). For FastScan or existing record layouts like those used here, consult the advanced [quantizer](../rabitq/quantizer.md) and [estimator](../rabitq/estimator.md) references. -For a complete standalone index, see the [quick start](../quick_start.md). +For a complete standalone index, see the [quick start](../quick_start.md) or +the [txtai integration](txtai.md). + +[ivf-reformer]: https://github.com/alibaba/zvec/blob/c5496863ee98bca155abca1e9e4aadf46e9ebed1/src/core/algorithm/ivf_rabitq/ivf_rabitq_reformer.cc +[ivf-entity]: https://github.com/alibaba/zvec/blob/c5496863ee98bca155abca1e9e4aadf46e9ebed1/src/core/algorithm/ivf_rabitq/ivf_rabitq_entity.cc +[hnsw-reformer]: https://github.com/alibaba/zvec/blob/c5496863ee98bca155abca1e9e4aadf46e9ebed1/src/core/algorithm/hnsw_rabitq/rabitq_reformer.cc +[hnsw-query]: https://github.com/alibaba/zvec/blob/c5496863ee98bca155abca1e9e4aadf46e9ebed1/src/core/algorithm/hnsw_rabitq/hnsw_rabitq_query_algorithm.cc +[dependency]: https://github.com/alibaba/zvec/blob/c5496863ee98bca155abca1e9e4aadf46e9ebed1/thirdparty/RaBitQ-Library/CMakeLists.txt -[ivf-reformer]: https://github.com/alibaba/zvec/blob/44d8bc6f7c3d656efde2641c9410432ea14c1657/src/core/algorithm/ivf_rabitq/ivf_rabitq_reformer.cc -[ivf-entity]: https://github.com/alibaba/zvec/blob/44d8bc6f7c3d656efde2641c9410432ea14c1657/src/core/algorithm/ivf_rabitq/ivf_rabitq_entity.cc -[hnsw-reformer]: https://github.com/alibaba/zvec/blob/44d8bc6f7c3d656efde2641c9410432ea14c1657/src/core/algorithm/hnsw_rabitq/rabitq_reformer.cc -[hnsw-query]: https://github.com/alibaba/zvec/blob/44d8bc6f7c3d656efde2641c9410432ea14c1657/src/core/algorithm/hnsw_rabitq/hnsw_rabitq_query_algorithm.cc -[dependency]: https://github.com/alibaba/zvec/blob/44d8bc6f7c3d656efde2641c9410432ea14c1657/thirdparty/RaBitQ-Library/CMakeLists.txt +[platform]: https://github.com/alibaba/zvec/blob/c5496863ee98bca155abca1e9e4aadf46e9ebed1/CMakeLists.txt diff --git a/docs/docs/papers.md b/docs/docs/papers.md new file mode 100644 index 00000000..87f83f3c --- /dev/null +++ b/docs/docs/papers.md @@ -0,0 +1,35 @@ +# Papers and citations + +Research behind RaBitQ-Library's quantization and graph-search implementations. +If you use this work, please cite the papers relevant to the method you use. + +## Original RaBitQ + +The one-bit method and its theoretical error bound. + +> Jianyang Gao and Cheng Long. “RaBitQ: Quantizing High-Dimensional Vectors +> with a Theoretical Error Bound for Approximate Nearest Neighbor Search.” +> *Proceedings of the ACM on Management of Data* 2, 3, Article 167 (May 2024), +> 27 pages. [https://doi.org/10.1145/3654970](https://doi.org/10.1145/3654970). + +## Multi-bit RaBitQ + +Practical multi-bit quantization and asymptotically optimal error guarantees. + +> Jianyang Gao, Yutong Gou, Yuexuan Xu, Yongyi Yang, Cheng Long, and Raymond +> Chi-Wing Wong. “Practical and Asymptotically Optimal Quantization of +> High-Dimensional Vectors in Euclidean Space for Approximate Nearest Neighbor +> Search.” *Proceedings of the ACM on Management of Data* 3, 3, Article 202 +> (June 2025), 26 pages. [https://doi.org/10.1145/3725413](https://doi.org/10.1145/3725413). + +## SymphonyQG + +Integration of quantization and graph search. The paper uses raw vectors for +candidate scoring; the library's optional 4/8-bit storage and PiPNN initialization +are implementation extensions. See the [SymphonyQG guide](index/qg.md). + +> Yutong Gou, Jianyang Gao, Yuexuan Xu, and Cheng Long. “SymphonyQG: Towards +> Symphonious Integration of Quantization and Graph for Approximate Nearest +> Neighbor Search.” *Proceedings of the ACM on Management of Data* 3, 1, +> Article 80 (February 2025), 26 pages. +> [https://doi.org/10.1145/3709730](https://doi.org/10.1145/3709730). diff --git a/MAINTAINING.md b/docs/maintaining.md similarity index 96% rename from MAINTAINING.md rename to docs/maintaining.md index 849c4543..051cc968 100644 --- a/MAINTAINING.md +++ b/docs/maintaining.md @@ -1,6 +1,6 @@ # Maintainer reference -For your first contribution, see [Contributing to RaBitQ](CONTRIBUTING.md). +For your first contribution, see [Contributing to RaBitQ](../CONTRIBUTING.md). This reference describes CI routing and release maintenance. ## Checks selected by changed files @@ -23,7 +23,7 @@ only the affected job groups: | `pyproject.toml` | Python lint, builds, and wheels; no C++ checks | | Shell scripts | ShellCheck and any check driven by the changed script | -See [CI coverage](tests/README.md#ci-coverage) for full-suite versus smoke-test +See [CI coverage](../tests/README.md#ci-coverage) for full-suite versus smoke-test ownership, dedicated FAISS comparisons, and emulated SIMD checks. The optional include-cleaner report is available through diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 8821dc41..e222dbf1 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -31,8 +31,10 @@ nav: - HNSW RaBitQ: index/hnsw.md - SymphonyQG: index/qg.md - Integrations: + - txtai: integrations/txtai.md - Zvec: integrations/zvec.md - Clustering: clustering.md + - Papers and citations: papers.md - Benchmarks: - Overview: benchmarks/bench.md - IVF indexes: benchmarks/ivf.md diff --git a/example.sh b/sample/run_cpp.sh similarity index 88% rename from example.sh rename to sample/run_cpp.sh index 1582232c..c1619b80 100755 --- a/example.sh +++ b/sample/run_cpp.sh @@ -1,3 +1,9 @@ +#!/usr/bin/env bash + +# Resolve data, build, and example paths from the repository root. +repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +cd -- "$repo_root" || exit 1 + # compiling cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel diff --git a/example_py.sh b/sample/run_python.sh similarity index 88% rename from example_py.sh rename to sample/run_python.sh index d47b17c3..4eb78fa5 100755 --- a/example_py.sh +++ b/sample/run_python.sh @@ -1,3 +1,9 @@ +#!/usr/bin/env bash + +# Resolve data, build, and example paths from the repository root. +repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +cd -- "$repo_root" || exit 1 + # compiling python -m pip install . @@ -9,7 +15,7 @@ fi tar -xzvf ./data/gist/gist.tar.gz -C ./data # indexing and querying for symqg -python ./sample/python/symqg_indexing.py --max-degree 32 --ef-construction 400 ./data/gist/gist_base.fvecs ./data/gist/symqg_32.index +python ./sample/python/symqg_indexing.py --max-degree 32 --ef-construction 400 ./data/gist/gist_base.fvecs ./data/gist/symqg_32.index python ./sample/python/symqg_querying.py ./data/gist/symqg_32.index ./data/gist/gist_query.fvecs ./data/gist/gist_groundtruth.ivecs diff --git a/tests/README.md b/tests/README.md index 8af6b18c..969e4d30 100644 --- a/tests/README.md +++ b/tests/README.md @@ -20,7 +20,7 @@ and rejection of truncated or unsupported formats. Linux CI additionally selects Clang 23 explicitly for portable builds, full C++ tests, installed consumers, and core/FastScan integrations with OpenMP ON/OFF. This is a tested development toolchain, not a new minimum compiler requirement; -see [compiler and tool versions](../DEVELOPMENT.md#compiler-and-tool-versions). +see [compiler and tool versions](../docs/development.md#compiler-and-tool-versions). OpenMP is optional for core/FastScan tests: configure `RABITQ_BUILD_INDEX=OFF` and `RABITQ_USE_OPENMP=OFF`. ANN/Python testing retains the full prerequisites above.