Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/docs/en/src/indexes/pyramid.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ Build-time parameters live under `index_param`.
| `neighbor_sample_rate` | float | — | ODescent neighbor sampling rate. |
| `no_build_levels` | int[] | `[]` | Tree levels that skip graph construction (0-indexed from the root). |
| `use_reorder` | bool | `false` | Keep a high-precision copy for rescoring. |
| `precise_quantization_type` | string | `"fp32"` | Quantizer for reordering. |
| `precise_quantization_type` | string | `"fp32"` | Quantizer for reordering. Use `"rabitq"` with `rabitq_bits_per_dim_precise` to enable RaBitQ x+y split reorder from base storage. |
| `rabitq_bits_per_dim_base` | int | `1` | RaBitQ stored-code bits. In x+y split mode, this is `x`, the filter bits used during graph traversal; allowed range is `[1, 8]`. |
| `rabitq_bits_per_dim_precise` | int | unset | RaBitQ split `y` bits. When set with `base_quantization_type: "rabitq"` and `precise_quantization_type: "rabitq"`, Pyramid uses split storage; `rabitq_bits_per_dim_base` remains `x`, and `x + y <= 8`. |
| `fast_encode_rabitq` | bool | `true` | Use the fast multi-bit RaBitQ encoder for RaBitQ base or precise storage; set to `false` for the exact encoder. |
| `fast_encode_rabitq_rounds` | int | `6` | Fast RaBitQ refinement rounds in `[1, 32]`. |
| `base_io_type` / `precise_io_type` | string | `"block_memory_io"` | Base and reorder storage backends; `uring_io` is available in builds with liburing. |
Expand All @@ -103,6 +105,20 @@ Build-time parameters live under `index_param`.
| `build_thread_count` | int | `1` | Threads used for parallel build. |
| `hierarchies` | array | `[]` | Named hierarchy definitions. Each element is either a string (inherits all top-level params) or an object with `name` and optional overrides (`max_degree`, `ef_construction`, `alpha`, `no_build_levels`, `index_min_size`). When present, multi-hierarchy mode is activated and each hierarchy maintains its own independent path tree. |

### RaBitQ split configuration

Set all five parameters together to enable RaBitQ x+y split storage and reordering:

```json
{
"use_reorder": true,
"base_quantization_type": "rabitq",
"precise_quantization_type": "rabitq",
"rabitq_bits_per_dim_base": 3,
"rabitq_bits_per_dim_precise": 5
}
```

## Search parameters

Search-time parameters live under the `pyramid` sub-object:
Expand All @@ -113,6 +129,7 @@ Search-time parameters live under the `pyramid` sub-object:
| `subindex_ef_search` | int | `50` | Candidate list size used when traversing intermediate sub-graphs on the path. |
Comment thread
LightWant marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] rabitq_one_bit_search search parameter is undocumented in the pyramid parameter table

PyramidSearchParameters::FromJson parses pyramid.rabitq_one_bit_search, and the pyramid.h constructor computes default_rabitq_one_bit_search_ to enable one-bit split search by default for split indexes. The rabitq_split.md docs mention this parameter, but the pyramid search parameter tables in both docs/docs/en/src/indexes/pyramid.md and docs/docs/zh/src/indexes/pyramid.md do not list it.

Consider adding a row like:
| rabitq_one_bit_search | bool | auto (true for split indexes) | Force the one-bit lower-bound search path in RaBitQ split mode. |

| `hierarchies` | string[] | `[]` | Select which hierarchy to search. Empty means use the default (unnamed) hierarchy. |
| `hierarchy_op` | string | `"single"` | How to combine results across hierarchies: `single` (search one hierarchy), `union`, or `intersection`. **Note:** `union` and `intersection` are not yet implemented — setting them will cause `KnnSearch`/`RangeSearch` to return an error. |
| `rabitq_error_rate` | float | `1.9` | Positive lower-bound error multiplier for this search. The default `1.9` is relatively large; increasing it improves accuracy but slows down search. |

```cpp
auto result = index->KnnSearch(
Expand Down
18 changes: 9 additions & 9 deletions docs/docs/en/src/quantization/rabitq.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

`rabitq` is VSAG's binary / low-bit quantizer. In its default mode each
coordinate is encoded with **1 bit**, giving the highest compression ratio
of any built-in quantizer. On HGraph, an `x+y` split mode stores low-bit base
of any built-in quantizer. On HGraph and Pyramid, an `x+y` split mode stores low-bit base
codes as `x` filter bits plus `y` supplement bits, so graph traversal can use
only the filter code and re-ranking can fetch only the supplement bits it needs.

![RaBitQ: encode each coordinate by its sign relative to a random hyperplane](../figures/quantization/rabitq-hyperplane.svg)

> Implementation: `src/quantization/rabitq_quantization/rabitq_quantizer.cpp`,
> parameter file `rabitq_quantizer_parameter.cpp`.
> For the complete HGraph split layout, lower-bound formula, and IO modes,
> For the complete split layout, lower-bound formula, and IO modes,
> see [RaBitQ x+y Split](rabitq_split.md).

## When to use it
Expand All @@ -32,7 +32,7 @@ as `"pca, rom, rabitq"`.
- `rabitq_bits_per_dim_base = 1`: `ceil(dim / 8)` bytes per vector. With
`dim = 768` that is 96 bytes (vs 3072 for fp32 → 32× smaller).
- `rabitq_bits_per_dim_base = x` plus `rabitq_bits_per_dim_precise = y`
on HGraph: split mode stores roughly `(x + y) * dim / 8` bytes per vector
on HGraph or Pyramid: split mode stores roughly `(x + y) * dim / 8` bytes per vector
for the RaBitQ code bytes. For example, `3+5` is about `dim` bytes per
vector.

Expand All @@ -42,9 +42,9 @@ as `"pca, rom, rabitq"`.
| --- | --- | --- | --- |
| `pca_dim` | int | `0` (= input dim) | Optional PCA preprocessing dimension applied inside RaBitQ. `0` means no PCA reduction (`rabitq_quantizer_parameter.cpp:30-32`). |
| `rabitq_bits_per_dim_query` | int | `32` | Bits per dimension used to encode the **query** during search. Allowed values: `4` or `32` (`rabitq_quantizer_parameter.cpp:38-43`). |
| `rabitq_bits_per_dim_base` | int | `1` | In standard RaBitQ, bits per dimension for the stored base code. In HGraph `x+y` split mode, this external key means `x`, the filter bits used during graph traversal. Allowed range `[1, 8]`. |
| `rabitq_bits_per_dim_precise` | int | unset | HGraph-only split-mode key. When present with `base_quantization_type: "rabitq"` and `precise_quantization_type: "rabitq"`, this means `y`, the supplement bits used for reorder/full-distance refinement. The sum `x + y` must be `<= 8`. |
| `rabitq_error_rate` | float | `1.9` | Default lower-bound error multiplier for HGraph split search; must be finite and positive. It can be overridden per search under the `hgraph` object. |
| `rabitq_bits_per_dim_base` | int | `1` | In standard RaBitQ, bits per dimension for the stored base code. In HGraph/Pyramid `x+y` split mode, this external key means `x`, the filter bits used during graph traversal. Allowed range `[1, 8]`. |
| `rabitq_bits_per_dim_precise` | int | unset | HGraph/Pyramid split-mode key. When present with `base_quantization_type: "rabitq"` and `precise_quantization_type: "rabitq"`, this means `y`, the supplement bits used for reorder/full-distance refinement. The sum `x + y` must be `<= 8`. |
| `rabitq_error_rate` | float | `1.9` | Default lower-bound error multiplier for HGraph/Pyramid split search; must be finite and positive. It can be overridden per search under the `hgraph` or `pyramid` object. |
| `use_fht` | bool | `false` | If `true`, applies a Fast Hadamard Transform rotation before binarization. Improves accuracy on anisotropic data with cheap O(dim log dim) cost (`rabitq_quantizer_parameter.cpp:76-78`). |
| `fast_encode_rabitq` | bool | `true` | For stored codes wider than one bit, use CAQ-based fast encoding. Set to `false` to retain the exact RaBitQ encoder. The setting is ignored for one-bit codes. |
| `fast_encode_rabitq_rounds` | int | `6` | Number of CAQ coordinate-adjustment rounds. Allowed range: `[1, 32]`. Each coordinate moves by at most one level per round. |
Expand Down Expand Up @@ -105,8 +105,8 @@ graph-distance evaluation.
```

Swap to the higher-accuracy `x+y` split mode by setting both base and precise
quantization to RaBitQ and providing `rabitq_bits_per_dim_precise`. HGraph then
automatically selects the split datacell. In the example below, traversal uses
quantization to RaBitQ and providing `rabitq_bits_per_dim_precise`. HGraph and Pyramid then
automatically select the split datacell. In the example below, traversal uses
`x = 3` filter bits and reorder reads only `y = 5` supplement bits:

```json
Expand Down Expand Up @@ -139,7 +139,7 @@ also tracks a residual norm so the inner-product estimate is unbiased.
`precise_quantization_type: "fp32"` is the safe default.
- **Rotate first.** For un-normalized data, set `rabitq_use_fht: true` or
use a `tq` chain that includes `rom` / `fht`.
- **Split mode for accuracy.** HGraph `x+y` split keeps an `x`-bit fast path
- **Split mode for accuracy.** HGraph/Pyramid `x+y` split keeps an `x`-bit fast path
for graph traversal and adds `y` supplement bits for re-ranking; expect
significantly higher recall than pure 1-bit when using more total bits.

Expand Down
14 changes: 7 additions & 7 deletions docs/docs/en/src/quantization/rabitq_split.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RaBitQ x+y Split

RaBitQ x+y split is an HGraph storage and search mode for low-bit base codes.
RaBitQ x+y split is an HGraph and Pyramid storage and search mode for low-bit base codes.
Each vector is divided into two records:

- `x` filter bits are read during graph traversal and lower-bound filtering.
Expand All @@ -13,7 +13,7 @@ memory while the colder supplement record is stored on disk.

## Enable split mode

HGraph selects split mode when both quantization types are `rabitq` and
HGraph and Pyramid select split mode when both quantization types are `rabitq` and
`rabitq_bits_per_dim_precise` is present:

```json
Expand Down Expand Up @@ -55,7 +55,7 @@ The constraints are:
x + y <= 8
```

If `rabitq_bits_per_dim_precise` is omitted, HGraph uses the standard RaBitQ
If `rabitq_bits_per_dim_precise` is omitted, HGraph and Pyramid use the standard RaBitQ
path instead of split storage.

Enable the filter/lower-bound search path with:
Expand Down Expand Up @@ -90,7 +90,7 @@ The split search path has four stages:
4. The final distance combines the filter contribution and supplement
contribution into one `x+y`-bit RaBitQ estimate.

The HGraph heap is therefore not populated with an `x+y` distance for every
The graph-search heap is therefore not populated with an `x+y` distance for every
visited vector. The inexpensive x-bit distance drives traversal; the more
accurate distance is evaluated only during candidate reorder.

Expand Down Expand Up @@ -260,7 +260,7 @@ sum_i q_i * u_i
+ sum_i q_i * s_i
```

For L2 with an x-bit lookup filter, HGraph passes the previously computed
For L2 with an x-bit lookup filter, HGraph and Pyramid pass the previously computed
filter distance to reorder as a hint. `ComputeDistWithSplitCodeAndFilterDist`
recovers the first term from that hint and computes only the second term from
the y supplement planes:
Expand Down Expand Up @@ -356,12 +356,12 @@ search-time `hgraph.rabitq_error_rate` does not.

## Operational notes

- Split storage is currently an HGraph feature and requires fp32 query codes.
- Split storage is currently available on HGraph and Pyramid and requires fp32 query codes. Pyramid enables the one-bit split search path by default for split indexes; pass `rabitq_one_bit_search: false` under `pyramid` to force the standard search path.
- `l2`, `ip`, and `cosine` are supported. The filter-hint reorder shortcut is
currently specialized for L2.
- Keep `use_reorder: true` unless x-bit traversal accuracy alone has been
validated for the dataset.
- Changing x, y, metric, or transform parameters requires rebuilding the
index. A search-time `hgraph.rabitq_error_rate` override does not.
- Use [RaBitQ](rabitq.md) for the general quantizer description and
[HGraph](../indexes/hgraph.md) for the complete index parameter table.
[HGraph](../indexes/hgraph.md) and [Pyramid](../indexes/pyramid.md) for the complete index parameter tables.
19 changes: 18 additions & 1 deletion docs/docs/zh/src/indexes/pyramid.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ auto result = index->KnnSearch(
| `neighbor_sample_rate` | float | — | ODescent 的邻居采样比率 |
| `no_build_levels` | int[] | `[]` | 跳过构图的层级(从根节点开始的 0-based 下标) |
| `use_reorder` | bool | `false` | 是否保留高精度副本用于精排 |
| `precise_quantization_type` | string | `"fp32"` | 精排使用的量化类型 |
| `precise_quantization_type` | string | `"fp32"` | 精排使用的量化类型。与 `rabitq_bits_per_dim_precise` 配合设为 `"rabitq"` 时,可启用从 base storage 重排的 RaBitQ x+y split。 |
| `rabitq_bits_per_dim_base` | int | `1` | RaBitQ 底库存储码的每维位数。在 x+y split 模式下表示 `x`,即图遍历使用的 filter bits;范围为 `[1, 8]`。 |
| `rabitq_bits_per_dim_precise` | int | 未设置 | RaBitQ split 的 `y` bits。和 `base_quantization_type: "rabitq"`、`precise_quantization_type: "rabitq"` 一起设置时,Pyramid 使用 split storage;`rabitq_bits_per_dim_base` 仍表示 `x`,且 `x + y <= 8`。 |
| `fast_encode_rabitq` | bool | `true` | 对 RaBitQ 底层或精排存储使用多 bit 快速编码器;设为 `false` 使用精确编码器 |
| `fast_encode_rabitq_rounds` | int | `6` | RaBitQ 快速编码的微调轮数,范围 `[1, 32]` |
| `base_io_type` / `precise_io_type` | string | `"block_memory_io"` | 底层与精排存储后端;以 liburing 构建时可用 `uring_io` |
Expand All @@ -98,6 +100,20 @@ auto result = index->KnnSearch(
| `build_thread_count` | int | `1` | 构建阶段并发线程数 |
| `hierarchies` | array | `[]` | 命名层级定义。每个元素可以是字符串(继承全部顶层参数)或对象(含 `name` 及可选覆盖参数:`max_degree`、`ef_construction`、`alpha`、`no_build_levels`、`index_min_size`)。设置后激活多层级模式,每个层级维护独立的路径树。 |

### RaBitQ split 配置

需要同时设置以下五个参数,才能启用 RaBitQ x+y split 存储和精排:

```json
{
"use_reorder": true,
"base_quantization_type": "rabitq",
"precise_quantization_type": "rabitq",
"rabitq_bits_per_dim_base": 3,
"rabitq_bits_per_dim_precise": 5
}
```

## 检索参数

检索参数放在 `pyramid` 子对象下:
Expand All @@ -108,6 +124,7 @@ auto result = index->KnnSearch(
| `subindex_ef_search` | int | `50` | 沿路径向下遍历中间子图时的候选集大小 |
| `hierarchies` | string[] | `[]` | 指定检索哪个层级。空数组表示使用默认(匿名)层级。 |
| `hierarchy_op` | string | `"single"` | 多层级结果合并方式:`single`(检索单个层级)、`union`、`intersection`。**注意:** `union` 和 `intersection` 尚未实现——设置后 `KnnSearch`/`RangeSearch` 会返回错误。 |
| `rabitq_error_rate` | float | `1.9` | 本次搜索使用的正数 lower-bound 误差倍率。默认值 `1.9` 较大;值越大,精度越高,但搜索速度越慢。 |

```cpp
auto result = index->KnnSearch(
Expand Down
18 changes: 9 additions & 9 deletions docs/docs/zh/src/quantization/rabitq.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# RaBitQ

`rabitq` 是 VSAG 的二值 / 低比特量化器。默认模式下每个坐标用 **1 比特**
编码,给出所有内建量化器中最高的压缩率。在 HGraph 上,`x+y` split 模式把
编码,给出所有内建量化器中最高的压缩率。在 HGraph 和 Pyramid 上,`x+y` split 模式把
底库码拆成 `x` 个过滤 bit 和 `y` 个 supplement bit:图遍历只使用 filter code,
重排 / full-distance 阶段只额外读取 supplement bits。

![RaBitQ:按坐标相对随机超平面的符号进行编码](../figures/quantization/rabitq-hyperplane.svg)

> 实现:`src/quantization/rabitq_quantization/rabitq_quantizer.cpp`,
> 参数文件 `rabitq_quantizer_parameter.cpp`。
> HGraph split 的完整存储布局、lower bound 公式和 IO 模式见
> split 的完整存储布局、lower bound 公式和 IO 模式见
> [RaBitQ x+y Split](rabitq_split.md)。

## 何时使用
Expand All @@ -27,7 +27,7 @@

- `rabitq_bits_per_dim_base = 1`:每向量 `ceil(dim / 8)` 字节。`dim = 768`
时为 96 字节(对比 fp32 的 3072 → 小 32×)。
- HGraph 上 `rabitq_bits_per_dim_base = x` 且
- HGraph 或 Pyramid 上 `rabitq_bits_per_dim_base = x` 且
`rabitq_bits_per_dim_precise = y`:split 模式约存储
`(x + y) * dim / 8` 字节的 RaBitQ code。例如 `3+5` 约为每向量 `dim`
字节。
Expand All @@ -38,9 +38,9 @@
| --- | --- | --- | --- |
| `pca_dim` | int | `0`(= 输入维度) | RaBitQ 内部可选的 PCA 预处理维度。`0` 表示不做 PCA 降维(`rabitq_quantizer_parameter.cpp:30-32`)。 |
| `rabitq_bits_per_dim_query` | int | `32` | 搜索时**查询**的每维位数。允许值:`4` 或 `32`(`rabitq_quantizer_parameter.cpp:38-43`)。 |
| `rabitq_bits_per_dim_base` | int | `1` | standard RaBitQ 下表示底库码每维位数;HGraph `x+y` split 下,这个外部 key 表示 `x`,即图遍历过滤阶段使用的 filter bits。范围 `[1, 8]`。 |
| `rabitq_bits_per_dim_precise` | int | 未设置 | HGraph-only split 模式 key。和 `base_quantization_type: "rabitq"`、`precise_quantization_type: "rabitq"` 一起出现时表示 `y`,即重排 / full-distance 阶段读取的 supplement bits。要求 `x + y <= 8`。 |
| `rabitq_error_rate` | float | `1.9` | HGraph split 搜索的默认 lower-bound 误差倍率;必须为有限正数,也可以在 `hgraph` 搜索参数中按次覆盖。 |
| `rabitq_bits_per_dim_base` | int | `1` | standard RaBitQ 下表示底库码每维位数;HGraph/Pyramid `x+y` split 下,这个外部 key 表示 `x`,即图遍历过滤阶段使用的 filter bits。范围 `[1, 8]`。 |
| `rabitq_bits_per_dim_precise` | int | 未设置 | HGraph/Pyramid split 模式 key。和 `base_quantization_type: "rabitq"`、`precise_quantization_type: "rabitq"` 一起出现时表示 `y`,即重排 / full-distance 阶段读取的 supplement bits。要求 `x + y <= 8`。 |
| `rabitq_error_rate` | float | `1.9` | HGraph/Pyramid split 搜索的默认 lower-bound 误差倍率;必须为有限正数,也可以在 `hgraph` 或 `pyramid` 搜索参数中按次覆盖。 |
| `use_fht` | bool | `false` | `true` 时在二值化前应用快速 Hadamard 变换旋转。以 O(dim log dim) 的廉价代价提升各向异性数据上的精度(`rabitq_quantizer_parameter.cpp:76-78`)。 |
| `fast_encode_rabitq` | bool | `true` | 对大于 1 bit 的底库码启用基于 CAQ 的快速编码;设为 `false` 时使用原有精确编码。1 bit 编码会忽略此参数。 |
| `fast_encode_rabitq_rounds` | int | `6` | CAQ 坐标微调轮数,范围 `[1, 32]`;每个坐标在每轮最多移动一级。 |
Expand All @@ -59,7 +59,7 @@
`rabitq_bits_per_dim_precise`、`rabitq_error_rate`、`rabitq_use_fht`;IVF
暴露 `rabitq_pca_dim`、`rabitq_bits_per_dim_query`、
`rabitq_bits_per_dim_base`、`rabitq_version`、`rabitq_error_rate`、
`rabitq_use_fht`;Pyramid 为底层量化器暴露 PCA、底库/查询位数和 FHT
`rabitq_use_fht`;Pyramid 为底层量化器暴露 PCA、底库/查询位数、split precise 位数、误差倍率和 FHT
相关 key。其中 `rabitq_use_fht` 是索引层对量化器内部 `use_fht` key
的别名,会由索引层重写。

Expand Down Expand Up @@ -97,7 +97,7 @@ PCA、ROM/FHT 或 RaBitQ 量化。scalar record 与 code-sum 数组都会在
```

切换到高精度的 `x+y` split 模式:把 base 和 precise 量化都设置为 RaBitQ,
并提供 `rabitq_bits_per_dim_precise`。HGraph 会自动选择 split datacell。
并提供 `rabitq_bits_per_dim_precise`。HGraph 和 Pyramid 会自动选择 split datacell。
下面例子中,图遍历使用 `x = 3` 个 filter bits,重排只读取 `y = 5` 个
supplement bits:

Expand Down Expand Up @@ -128,7 +128,7 @@ FHT 旋转是固定的(无需学习),因此不增加训练代价;PCA 预
`use_reorder: true` + `precise_quantization_type: "fp32"` 是稳妥默认。
- **先旋转。** 对未归一化数据,设 `rabitq_use_fht: true`,或在 `tq` 链路
中包含 `rom` / `fht`。
- **精度优先时用 split 模式。** HGraph `x+y` split 保留 `x` bit 快速
- **精度优先时用 split 模式。** HGraph/Pyramid `x+y` split 保留 `x` bit 快速
过滤路径,再添加 `y` 个 supplement bits 用于重排;相对纯 1 比特,使用
更多总 bit 时召回明显更高。

Expand Down
Loading
Loading