Skip to content

Latest commit

 

History

History
358 lines (264 loc) · 14.6 KB

File metadata and controls

358 lines (264 loc) · 14.6 KB

QuIVer — Artifact Documentation

Paper: QuIVer: Rethinking ANN Graph Topology via Training-Free Binary Quantization

Venue: PVLDB Vol. 20, 2027

This document describes how to reproduce every experiment reported in the paper.


Table of Contents


Repository Layout

TriviumDB/
├── src/index/
│   ├── bq.rs              # BQ binary quantization signatures (QuIVer foundation)
│   └── quiver.rs          # 🚀 QuIVer ANN index core implementation
├── benches/
│   ├── bench_cohere1m.rs          # Single-dataset Recall-QPS benchmark
│   ├── bench_sensitivity.rs       # Parameter sensitivity grid scan (§5.4)
│   ├── bench_encoding_ablation.rs # Encoding ablation: 1-bit/2-bit SM/SQ (§5.5)
│   ├── bench_ssd_cold_hot.rs      # SSD cold/hot separation experiment (§5.3)
│   ├── bench_variance.rs          # Build variance + tail latency (Appendix)
│   ├── bench_rbq2_precision.rs    # BQ2 vs RaBitQ top-K precision (Appendix)
│   ├── bench_recall_at_k.rs       # Recall@K for K=1,10,100,500
│   ├── bench_random1m.rs          # Synthetic-LR dataset generator
│   ├── bench_random_sphere.rs     # Random-Sphere dataset generator
│   ├── bench_baselines.py         # All baselines: hnswlib/FAISS/USearch/VSAG
│   ├── bench_rabitq_refine.py     # FAISS IVF+RaBitQ+Refine benchmark
│   ├── run_all_benchmarks.py      # Batch runner for all datasets
│   └── run_vibe_recall.py         # VIBE dataset batch runner
├── scripts/
│   ├── prepare_all.py             # Dataset download & preparation (one command)
│   ├── convert_hdf5_to_f32.py     # VIBE HDF5 → f32 format converter
│   └── convert_to_fbin.py         # f32 → DiskANN fbin format converter
└── Cargo.toml                     # Build configuration

Prerequisites

Hardware (paper configuration):

  • Main experiments (§5.1–5.6): AMD Ryzen 7 7840HS laptop (Zen 4, 8 cores / 16 threads, AVX-512 with VPOPCNTDQ), 32 GB DDR5-5600 RAM, Windows 11
  • Scalability (§5.7, 5M vectors): AMD EPYC 9T24 (Zen 4, 16 threads, AVX-512), 64 GB DDR5, Debian Linux

Software:

# Rust toolchain (stable)
rustup update stable

# Python dependencies (for baselines & data prep)
pip install numpy tqdm h5py datasets
pip install hnswlib faiss-cpu usearch    # baseline systems

Compiler flags (as used in the paper):

  • All Rust code compiled with target-cpu=native in release mode with LTO
  • Set in .cargo/config.toml or via RUSTFLAGS="-C target-cpu=native"

Step 1: Prepare Datasets

1a. Automated datasets (11 of 13)

prepare_all.py handles download, sampling, L2-normalization, and ground truth computation for 11 datasets:

python scripts/prepare_all.py                   # prepare all 11
python scripts/prepare_all.py cohere minilm      # prepare specific ones
python scripts/prepare_all.py --list             # show status
Script name Paper name Dim Source Notes
cohere Cohere-1M 768 HF: YoKONCy/Cohere-1M-wikipedia-768d Streaming, 1M sample
minilm MiniLM-1M 384 HF: maloyan/wikipedia-22-12-en-embeddings-all-MiniLM-L6-v2 Streaming, 1M sample
bge_m3 BGE-M3-1M 1024 HF: Qdrant/BGE-m3-1-million-ads Full download
dbpedia1536 DBpedia-1M (1536) 1536 HF: Qdrant/dbpedia-entities-openai3-text-embedding-3-large-1536-1M Full download
dbpedia3072 DBpedia-3072 3072 HF: Qdrant/dbpedia-entities-openai3-text-embedding-3-large-3072-1M Full download
wolt_clip Wolt-CLIP-1M 512 HF: Qdrant/wolt-food-clip-ViT-B-32-embeddings Streaming, 1M sample
sift128 SIFT-128 128 ann-benchmarks.com Download HDF5 to project root
gist960 GIST-960 960 ann-benchmarks.com Download HDF5 to project root
glove100 GloVe-100 100 ann-benchmarks.com Download HDF5 to project root
random Random-Sphere 768 Synthetic (uniform on S^{D-1}, seed 42) Generated by script
sphere Synthetic-LR 768 Synthetic (64-d subspace + Zipf clusters) Generated by script

ann-benchmarks HDF5: Download these files manually and place them in the project root before running prepare_all.py:

1b. Manual datasets (2 of 13)

These require manual preparation:

RedCaps-1M (512-d, CLIP ViT-B/32):

  1. Download the pre-computed CLIP-embedded RedCaps dataset from Zenodo: 13137120
  2. Convert to f32 format and save as redcaps_train.f32 (1M×512), redcaps_test.f32 (10K×512), redcaps_groundtruth.i32 (10K×10)

MSMARCO-5M (1024-d, Cohere Embed v3, §5.7 scalability only):

  1. Download from HF: CohereLabs/msmarco-v2.1-embed-english-v3 (passages split)
  2. Extract the first 1M / 5M passage embeddings and 10K held-out queries
  3. Save as msmarco1m_train.f32, msmarco5m_train.f32, etc.

1c. Output format

Each dataset produces 3 files in the project root:

File Format Description
{prefix}_train.f32 N×D float32 raw binary Base vectors
{prefix}_test.f32 Q×D float32 raw binary Query vectors
{prefix}_groundtruth.i32 Q×K int32 raw binary Ground truth top-K IDs

Step 2: Reproduce Main Results

Paper section: §5.2, Table 4 (QuIVer on embedding datasets, 384–3072 dimensions)

# Default: Cohere-1M (768-d), m=32, ef_c=128, α=1.2
cargo bench --bench bench_cohere1m

# Other datasets: set TRIVIUM_ANN_NAME
$env:TRIVIUM_ANN_NAME="minilm-384"
cargo bench --bench bench_cohere1m

$env:TRIVIUM_ANN_NAME="dbpedia-1536"
cargo bench --bench bench_cohere1m

Available dataset names for bench_cohere1m:

Name Dim Description
cohere-1m (default) 768 Cohere Wikipedia embed-english-v2.0
minilm-384 384 all-MiniLM-L6-v2 sentence embeddings
dbpedia-1536 1536 DBpedia OpenAI text-embedding-3-large
redcaps-512 512 RedCaps CLIP ViT-B/32
random-1m 768 Synthetic-LR (low-rank + Zipf clusters)
msmarco-1m 1024 MSMARCO Cohere-v3 (1M subset)
msmarco-5m 1024 MSMARCO Cohere-v3 (5M, scalability)

Additional datasets for bench_sensitivity: wolt-clip-512, sift-128, gist-960, glove-100, bge-m3-1024, dbpedia-3072, sphere-1m, plus all VIBE datasets (see Step 6)


Step 3: Reproduce Baseline Comparisons

Paper section: §5.3, Table 5 (matched-recall throughput on Cohere-1M) & Figure 3 (Pareto curves)

All baselines are evaluated on Cohere-1M (768-d) with M=32, ef_c=128 (or DiskANN R=64, L_b=128, α=1.2).

# Install baseline systems
pip install hnswlib faiss-cpu usearch pyvsag

# Run all installed baselines on Cohere-1M
python benches/bench_baselines.py

# Run specific baselines only
$env:BASELINES="hnswlib,faiss_hnsw"
python benches/bench_baselines.py

# RaBitQ-specific benchmark (IVF+RaBitQfs+Refine)
python benches/bench_rabitq_refine.py

For DiskANN Rust: Follow the official DiskANN repository instructions. Convert data files first:

python scripts/convert_to_fbin.py   # converts cohere → DiskANN fbin format

Step 4: Reproduce Sensitivity Analysis

Paper section: §5.4, Tables 6–8 (m, ef_c, α sensitivity on Cohere-1M)

# Full sensitivity analysis on Cohere-1M (all sub-experiments)
$env:TRIVIUM_ANN_NAME="cohere-1m"
$env:TRIVIUM_SENSITIVITY_MODE="all"
cargo bench --bench bench_sensitivity

# Quick mode: only ef_search curve + Pareto (skips 1a/1b/1c parameter scans)
$env:TRIVIUM_SENSITIVITY_START="1d"
cargo bench --bench bench_sensitivity

Sub-experiments:

Tag Content Paper Reference
1a m sweep (4–64, ef_c=128, α=1.2) Table 6
1b ef_c sweep (16–512, m=32, α=1.2) Table 7
1c α sweep (1.0–1.25, m=32, ef_c=128) Table 8
1d ef_search fine Recall-QPS curve Figure 3 data points
1e Pareto curves across m values Supplemental
1f m × α cross-experiment Supplemental

Step 5: Reproduce Encoding Ablation

Paper section: §5.5, Table 9 (1-bit sign vs 2-bit SM vs 2-bit SQ on Cohere-100K)

cargo bench --bench bench_encoding_ablation --features ablation

This runs two phases:

  1. Phase 1: Top-10 overlap (BQ-ranked vs float32 ground truth) + distance computation latency (ns/call)
  2. Phase 2: Full graph search Recall@10 / QPS comparison across all three encoding schemes

Step 6: Reproduce Cross-Dataset Boundary

Paper section: §5.6, Table 10 & Figure 4 (applicability boundary across 12 datasets)

The main 12-dataset comparison uses bench_sensitivity with sub-experiment 1d (ef_search Recall-QPS curve at default m=32, ef_c=128, α=1.2).

# Batch runner for VIBE datasets (writes logs to research/bench_logs/)
python benches/run_vibe_recall.py

# Or run individually
$env:TRIVIUM_ANN_NAME="arxiv-nomic"
$env:TRIVIUM_SENSITIVITY_MODE="params"
$env:TRIVIUM_SENSITIVITY_START="1d"
$env:TRIVIUM_SENSITIVITY_END="1d"
cargo bench --bench bench_sensitivity

VIBE dataset names: arxiv-nomic, ccnews-nomic, coco-nomic, codesearch-jina, gooaq-roberta, landmark-nomic, landmark-dino

Note: VIBE HDF5 source files must first be converted via python scripts/convert_hdf5_to_f32.py


Step 7: Reproduce Scalability

Paper section: §5.7, Table 11 (MSMARCO Cohere-v3 at 1M and 5M vectors)

Note: The 5M scalability experiment in the paper was conducted on a separate cloud server (AMD EPYC 9T24, 64 GB DDR5, Debian Linux).

# 1M subset
$env:TRIVIUM_ANN_NAME="msmarco-1m"
cargo bench --bench bench_cohere1m

# 5M full scale
$env:TRIVIUM_ANN_NAME="msmarco-5m"
cargo bench --bench bench_cohere1m

Step 8: Reproduce SSD Cold/Hot Experiment

Paper section: §4, Tables 2–3 (hot/cold memory breakdown, cold-path storage tolerance)

# Requires administrator privileges (for page cache clearing)
# Also requires 'ablation' feature flag
cargo bench --bench bench_ssd_cold_hot --features ablation

⚠️ Windows: Run as Administrator for NtSetSystemInformation page cache clearing. Linux: Run as root for /proc/sys/vm/drop_caches.


Step 9: Reproduce BQ2 vs RaBitQ Precision

Paper section: Supplemental material (top-K overlap: BQ2 vs RaBitQ-sym vs RaBitQ-asym)

cargo bench --bench bench_rbq2_precision

Compares three quantization approaches on 100K Cohere vectors:

  • BQ2 (2-bit SM): Our approach, no rotation, weighted Hamming
  • RaBitQ-sym: 4-round FHT-Kac rotation + 1-bit sign + Hamming
  • RaBitQ-asym: 4-round rotation + 1-bit + f32 asymmetric + correction factors

Step 10: Reproduce Build Variance & Tail Latency

Paper section: Supplemental material (construction variance RSD, P50/P95/P99 latency)

cargo bench --bench bench_variance --features ablation

Runs three phases:

  1. Build variance: 5 independent builds with shuffled insertion order → recall RSD
  2. Tail latency: Per-query P50/P95/P99/P99.9/Max distribution
  3. Concurrent throughput: 1/2/4/8/16 parallel clients → QPS-latency curve

Environment Variables Reference

Variable Default Description
TRIVIUM_ANN_NAME cohere-1m Dataset preset name
TRIVIUM_ANN_DIM (auto) Vector dimension override
TRIVIUM_ANN_TRAIN (auto) Training set file path
TRIVIUM_ANN_TEST (auto) Query set file path
TRIVIUM_ANN_GT (auto) Ground truth file path
TRIVIUM_ANN_M 32 Graph max degree
TRIVIUM_ANN_EF_CONSTRUCTION 128 Build-time beam width
TRIVIUM_ANN_ALPHA 1.2 Pruning factor
TRIVIUM_ANN_EF 64,128,256,512,1024 Search ef values (comma-separated)
TRIVIUM_ANN_TRAIN_LIMIT full Limit training set size
TRIVIUM_SENSITIVITY_MODE all all, params, or threads
TRIVIUM_SENSITIVITY_START 1a Start from sub-experiment
TRIVIUM_SENSITIVITY_END 1e End at sub-experiment

Data Format

All data files use raw binary format (no headers):

  • .f32: Contiguous float32 values, row-major. File size = N × D × 4 bytes.
  • .i32: Contiguous int32 values, row-major. File size = Q × K × 4 bytes.

To inspect file dimensions:

import numpy as np
data = np.fromfile("cohere_train.f32", dtype=np.float32)
print(f"Vectors: {len(data) // 768} × 768")  # → 1000000 × 768