RaBitQ Library

repository·main·Indexed 42 days ago

https://github.com/vectordb-ntu/rabitq-library

A C++17 library with Python bindings (rabitqlib v0.3.6) for high-performance Approximate Nearest Neighbor (ANN) search. It utilizes 1-bit and multi-bit vector quantization and provides optimized implementations of IVF, HNSW, and SymphonyQG. The library supports Euclidean (l2) and inner product metrics, leveraging AVX2, FMA, and AVX-512 SIMD kernels for accelerated search.

Tokens
15.9K
Snippets
39
Records
61
Agent score
54%

What's inside RaBitQ

  1. Key advantages of using RaBitQ

    main

    RaBitQ is designed as a drop-in replacement for binary quantization and (uniform) scalar quantization, offering several benefits:

    • High Accuracy with Tiny Space: Provides state-of-the-art similarity estimates across various space budgets. The smallest representation uses a one-bit code per padded dimension plus per-vector factors.
    • Fast Distance Estimation: Utilizes bitwise operations for speed. For batched scans in IVF and SymphonyQG, it leverages FastScan for optimized performance.
    • Theoretical Error Bound: Provides an asymptotically optimal error bound for distance and inner product estimation, which can be used for reliable ordering and reranking.
  2. Available distance metrics in RaBitQ

    main

    RaBitQ provides two primary public metric choices for distance estimation:

    1. METRIC_L2: Used for squared Euclidean distance estimation.
    2. METRIC_IP: Used for Inner Product estimation.

    Note on Cosine Similarity: There is no dedicated METRIC_COSINE. To perform a cosine search, you must normalize both your data vectors and your query vectors before performing an Inner Product (METRIC_IP) search.

  3. Understand the HNSW + RaBitQ data layout

    main

    Each element in the HNSW + RaBitQ index is stored using a specific layout to balance graph connectivity and quantized vector storage. The layout is as follows:

    [number of edges]
    [edges]
    [cluster ID]
    [external label]
    [BinData (1-bit * dim + factors)]
    [ExData (ex-bits * dim + factors)]
  4. How SymphonyQG (QG + RaBitQ) works

    main

    SymphonyQG combines graph search (from QG/NGT) with batched RaBitQ distance estimation. It uses FastScan to estimate neighbor distances. Visited vertices are scored using either stored raw vectors or quantized codes.

    Quantization Settings:

    • quantization_bits=0 (default): Uses raw vectors.
    • quantization_bits=4 or 8: Uses QG-quantization where quantized vectors share a rotated global centroid.

    Refinement Process: During refinement, the system temporarily reconstructs source vectors to estimate distances to stored target codes. Because these estimates are directional, reverse edges are also rescored. When using raw vectors, refinement uses the owned raw vectors; no full reconstructed-vector cache is maintained.

  5. Inner Product for Binary Codes: Single Code vs Batch Code

    main

    RaBitQ provides two distinct methods for computing the inner product between binary codes and a floating-point vector $\mathbf{q}_r'$:

    Single Code Approach

    For a single binary vector $\mathbf{x}_0$, the library quantizes $\mathbf{q}_r'$ into a vector of 4-bit unsigned integers $\mathbf{q}_u$. The inner product is approximated using bitwise-AND (&) and popcnt operations:

    $$\Delta_q \sum_{i=0}^{B_q-1} 2^i \cdot \mathrm{popcnt}(\mathbf{x}_0 & \mathbf{q}_u^{(i)}) + v_q \cdot \mathrm{popcnt}(\mathbf{x}_0)$$

    Batch Code Approach (FastScan)

    For batches of binary vectors, the library uses a FastScan approach. A $D$-bit binary vector is split into $M = D/4$ segments. For each segment, a Look-Up Table (LUT) is prepared:

    • LUT[m][mask]: Stores the inner product for the $m$-th segment (dimensions $4m$ to $4m+3$) when the code segment matches mask.

    The total inner product is the sum of the LUT values for all segments: $$\sum_{m=0}^{M-1} \mathrm{LUT}[m][\mathbf{x}_{0}[4m:4m+3]]$$

  6. How Inner Product kernels work in RaBitQ

    main

    RaBitQ uses inner-product kernels to compute the similarity between quantization codes ($\mathbf{x}$) and rotated query vectors ($\mathbf{q}_r'$). The library selects the optimal implementation at runtime based on available hardware instructions, specifically supporting AVX2/FMA and AVX-512. When available, AVX-512 VPOPCNTDQ is utilized for optimized popcount-specific execution paths.

    The kernels are categorized into two main types:

    1. Binary Code Kernels: Used for computing inner products between binary vectors and floating-point query vectors.
    2. Multi-bit Code Kernels: Used for computing inner products with multi-bit quantization codes.
  7. Understand IVF + RaBitQ data layout and modes

    main

    The IVF implementation uses a specific data layout to optimize searching:

    1. [batch data]: 1-bit code and factors.
    2. [ex_data]: Either the code for remaining bits (quantized mode) or the original float32 vectors (raw mode).
    3. [ids]: PIDs of vectors, organized by clusters.
    4. [cluster_lst]: Metadata for the clusters.

    Quantized vs. Raw Mode:

    • Quantized Mode: Uses total_bits (1–9) to store compressed codes. nbits() returns the bit width.
    • Raw Mode: Set by using total_bits=32. In this mode, ex_data stores the original float32 vectors. The index owns a copy of the input, so the original data can be released after construction. nbits() returns 32 in this mode. Raw mode files include a magic/version header and rotation state, allowing them to be loaded without an external dataset.
  8. Choose between FFHT + Kac’s Walk and Matrix Rotator

    main

    RaBitQLib offers two rotation methods depending on your requirements for speed and memory:

    1. FFHT + Kac’s Walk (RotatorType::FhtKacRotator)

    This is the default method. It combines Fast Hadamard Transform (FFHT) with Kac’s Walk principles. It is highly efficient for high-dimensional vectors.

    • Time Complexity: $O(D \log D)$
    • Space Consumption: $4D$ bits

    2. Random Orthogonal Transformation (RotatorType::MatrixRotator)

    This is the classical Johnson-Lindenstrauss Transformation using a random Gaussian matrix orthogonalized via QR decomposition.

    • Time Complexity: $O(D^2)$
    • Space Consumption: $D^2$ floating-point numbers
  9. The RaBitQ reranking algorithm logic

    main

    To minimize raw vector retrieval from disks, RaBitQ follows this algorithmic strategy when $K$ nearest neighbors are targeted:

    1. Sort all candidates by their estimated distances.
    2. Initialize a max-heap named KNNs (sorted by the upper bounds of distances) containing the top-$K$ candidates with the smallest estimated distances.
    3. Iterate through the remaining candidates (Candidate A) using these conditions:
      • Skip Candidate: If A's lower bound > max upper bound in KNNs, drop the candidate.
      • Rerank Candidate: If A's lower bound <= max upper bound in KNNs AND the top candidate in KNNs has already been reranked, rerank Candidate A and update KNNs.
      • Rerank Top Candidate: If A's lower bound <= max upper bound in KNNs AND the top candidate in KNNs has NOT been reranked, rerank the top candidate in KNNs, update KNNs, and then re-evaluate Candidate A.
  10. Inner Product for Multi-bit Codes

    main

    For multi-bit codes, the kernel unpacks packed unsigned values and accumulates them using selected AVX2/FMA or AVX-512 kernels.

    The dispatch table supports between zero and eight extended bits. Consequently, full RaBitQ codes can contain anywhere from one to nine bits per dimension.

  11. How RaBitQ optimizes the reranking process

    main

    RaBitQ improves vector search recall through an optimized reranking strategy that minimizes expensive disk I/O (retrieving raw vectors). Unlike standard ANN algorithms that typically rerank a fixed number of candidates, RaBitQ uses its theoretical error bounds to skip candidates that cannot possibly be part of the $K$ nearest neighbors.

    By comparing a candidate's lower bound of estimated distance against the upper bound of the distances of current nearest neighbors, RaBitQ can achieve nearly perfect recall while reranking significantly fewer than $K$ vectors. It specifically targets only those vectors located on the boundaries of the $K$-nearest neighbor sets.

  12. Compare RaBitQ index types

    main

    RaBitQ provides different index structures to balance memory, latency, and recall. Choose the one that fits your workload:

    IndexBest fitTypical relative memoryMain search control
    IVF + RaBitQLarge datasets and predictable memory useLowestNumber of probed clusters (nprobe)
    HNSW + RaBitQGeneral-purpose graph searchModerateSearch candidate list size
    SymphonyQGLatency-focused graph searchHighestSearch window size

    Implementation Details:

    • IVF: Stores quantized vectors by default. To retain original float32 vectors for reranking, set nbits=32.
    • HNSW: Searches quantized vectors instead of accessing raw vectors during search.
    • SymphonyQG: Uses additional memory and multiple codes per vector to optimize access patterns.