Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
8 changes: 5 additions & 3 deletions docs/src/format/index/vector/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ For **RabitQ (RQ)**:
| `num_bits` | u8 | Number of bits per dimension, in the range 1..=9 |
| `code_dim` | u32 | Rotated vector dimension for the 1-bit binary code |
| `packed` | bool | Whether codes are packed for optimized computation |
| `query_estimator` | string | Distance estimator layout: `residual_query` or `raw_query`. Missing values are read as `residual_query` for compatibility with released 1-bit IVF_RQ indexes. |

#### Lance File Global Buffer

Expand All @@ -279,8 +280,9 @@ to rotate vectors before binary quantization:
The rotation matrix has shape `[code_dim, code_dim]` where `code_dim` is the rotated vector
dimension. IVF_RQ always stores the 1-bit binary sign code in `_rabit_codes`; for `num_bits > 1`,
the remaining `num_bits - 1` ex-code bits are stored in `__ex_codes` instead of widening the
binary code path. `num_bits=1` indexes only store the binary-code factor columns; multi-bit indexes
also store separate ex-code additive and scale factors.
binary code path. New IVF_RQ indexes store raw-query estimator factors. `num_bits=1` indexes only
store the binary-code factor columns; multi-bit indexes also store separate ex-code additive and
scale factors.

## Appendices

Expand Down Expand Up @@ -345,7 +347,7 @@ auxiliary schema also includes `__ex_codes`, `__add_factors_ex`, and `__scale_fa
- Arrow Schema Metadata:
- `"distance_type"` → `"l2"`
- `"lance:ivf"` → tracks per-partition `offsets` and `lengths` (no centroids here)
- `"lance:rabit"` → `"{"rotate_mat_position":1,"num_bits":1,"packed":true}"`
- `"lance:rabit"` → `"{"rotate_mat_position":1,"num_bits":1,"packed":true,"query_estimator":"raw_query"}"`
Comment thread
claude[bot] marked this conversation as resolved.
- Lance File Global buffer:
- `Tensor` rotation matrix with shape `[code_dim, code_dim]` = `[128, 128]` (float32)
- Rows with Arrow schema:
Expand Down
47 changes: 32 additions & 15 deletions python/python/tests/test_vector_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -1067,13 +1067,7 @@ def test_create_ivf_rq_skip_transpose():
assert stats["indices"][0]["sub_index"]["packed"] is False


@pytest.mark.skip(
reason=(
"IVF_RQ num_bits>1 creation is gated until split-code search support "
"is implemented"
)
)
def test_create_ivf_rq_multi_bit_gates_search():
def test_create_ivf_rq_multi_bit_searches_l2_and_cosine():
ds = lance.write_dataset(create_table(), "memory://")

ds = ds.create_index(
Expand All @@ -1084,15 +1078,38 @@ def test_create_ivf_rq_multi_bit_gates_search():
)
stats = ds.stats.index_stats("vector_idx")
assert stats["indices"][0]["sub_index"]["num_bits"] == 9
assert stats["indices"][0]["sub_index"]["query_estimator"] == "raw_query"

with pytest.raises(pa.ArrowInvalid, match="num_bits>1 search is not supported"):
ds.to_table(
nearest={
"column": "vector",
"q": np.random.randn(128).astype(np.float32),
"k": 10,
}
)
result = ds.to_table(
nearest={
"column": "vector",
"q": np.random.randn(128).astype(np.float32),
"k": 10,
}
)
assert result.num_rows == 10

cosine_ds = lance.write_dataset(create_table(), "memory://")
cosine_ds = cosine_ds.create_index(
"vector",
index_type="IVF_RQ",
metric="cosine",
num_partitions=4,
num_bits=9,
)
cosine_stats = cosine_ds.stats.index_stats("vector_idx")
assert cosine_stats["indices"][0]["sub_index"]["num_bits"] == 9
assert cosine_stats["indices"][0]["sub_index"]["query_estimator"] == "raw_query"

cosine_result = cosine_ds.to_table(
nearest={
"column": "vector",
"q": np.random.randn(128).astype(np.float32),
"k": 10,
"metric": "cosine",
}
)
assert cosine_result.num_rows == 10
Comment thread
claude[bot] marked this conversation as resolved.
Outdated


def test_create_ivf_rq_requires_dim_divisible_by_8():
Expand Down
17 changes: 2 additions & 15 deletions rust/lance-index/src/vector/bq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,7 @@ pub fn validate_rq_num_bits(num_bits: u8) -> Result<()> {
}

pub fn validate_supported_rq_num_bits(num_bits: u8) -> Result<()> {
validate_rq_num_bits(num_bits)?;
if num_bits != RABIT_BINARY_NUM_BITS {
return Err(Error::not_supported(format!(
"IVF_RQ num_bits={} index creation is not supported until split-code search support is implemented",
num_bits
)));
}
Ok(())
validate_rq_num_bits(num_bits)
}

pub fn rabit_ex_bits(num_bits: u8) -> Result<u8> {
Expand Down Expand Up @@ -261,13 +254,7 @@ mod tests {
);

validate_supported_rq_num_bits(1).unwrap();
let err = validate_supported_rq_num_bits(9).unwrap_err();
assert!(
err.to_string()
.contains("num_bits=9 index creation is not supported"),
"{}",
err
);
validate_supported_rq_num_bits(9).unwrap();
}

#[test]
Expand Down
Loading
Loading