From 5160810144c6a5eb0c037bd557d460d8bcb76cc3 Mon Sep 17 00:00:00 2001 From: Ivan Despot <66276597+g-despot@users.noreply.github.com> Date: Wed, 12 Aug 2026 13:50:16 +0200 Subject: [PATCH 1/5] docs: 4-bit RQ examples for Go, Java and C# Completes the 1.39 4-bit RQ page: the Go, Java and C# tabs of the "Enable compression for new collection" and "Enable compression for existing collection" sections under 4-bit RQ now have real snippets, matching the Python and TypeScript examples already in place. - Go, C#: set bits to 4 and raise the rescore limit to 50. Both clients put the rescore limit on the wire under the name the server reads. - Java: sets bits to 4 only. The Java client serialises the rescore limit under a name the server ignores, so an example that set it would not do anything (tracked separately). - No flat-index example at 4 bits: the flat index rejects it. Also removes the Java rescore caveat include and its usage from the page. Co-Authored-By: Claude Opus 5 (1M context) --- _includes/code/csharp/ConfigureRQTest.cs | 52 ++++++++ .../howto/configure-rq/rq-compression-v3.ts | 28 +++- .../howto/configure-rq/rq-compression-v4.py | 57 ++++++++ .../go/docs/configure/compression.rq_test.go | 123 ++++++++++++++++++ .../src/test/java/ConfigureRQTest.java | 38 ++++++ .../rq-compression-parameters.mdx | 4 +- _includes/feature-notes/rq-4bit.mdx | 5 + .../starter-guides/compression-types.mdx | 2 +- docs/deploy/configuration/env-vars/index.md | 2 +- docs/weaviate/concepts/vector-quantization.md | 33 ++++- .../compression/rq-compression.md | 121 ++++++++++++++++- .../managing-resources/compression.mdx | 2 + 12 files changed, 454 insertions(+), 13 deletions(-) create mode 100644 _includes/feature-notes/rq-4bit.mdx diff --git a/_includes/code/csharp/ConfigureRQTest.cs b/_includes/code/csharp/ConfigureRQTest.cs index e54125715..a0bb9e128 100644 --- a/_includes/code/csharp/ConfigureRQTest.cs +++ b/_includes/code/csharp/ConfigureRQTest.cs @@ -55,6 +55,35 @@ await client.Collections.Create( // END EnableRQ } + [Fact] + public async Task Test4BitEnableRQ() + { + // START 4BitEnableRQ + await client.Collections.Create( + new CollectionCreateParams + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = Configure.Vector( + "default", + v => v.Text2VecTransformers(), + index: new VectorIndex.HNSW + { + // highlight-start + Quantizer = new VectorIndex.Quantizers.RQ + { + Bits = 4, + // Raise the rescore limit; the default of 20 is too low for 4-bit RQ + RescoreLimit = 50, + }, + // highlight-end + } + ), + } + ); + // END 4BitEnableRQ + } + [Fact] public async Task Test1BitEnableRQ() { @@ -157,6 +186,29 @@ await collection.Config.Update(c => // END UpdateSchema } + [Fact] + public async Task Test4BitUpdateSchema() + { + var collection = await client.Collections.Create( + new CollectionCreateParams + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = Configure.Vector("default", v => v.Text2VecTransformers()), + } + ); + + // START 4BitUpdateSchema + await collection.Config.Update(c => + { + var vectorConfig = c.VectorConfig["default"]; + vectorConfig.VectorIndexConfig.UpdateHNSW(h => + h.Quantizer = new VectorIndex.Quantizers.RQ { Bits = 4, RescoreLimit = 50 } + ); + }); + // END 4BitUpdateSchema + } + [Fact] public async Task Test1BitUpdateSchema() { diff --git a/_includes/code/howto/configure-rq/rq-compression-v3.ts b/_includes/code/howto/configure-rq/rq-compression-v3.ts index cf09d90d0..232efaf70 100644 --- a/_includes/code/howto/configure-rq/rq-compression-v3.ts +++ b/_includes/code/howto/configure-rq/rq-compression-v3.ts @@ -5,9 +5,9 @@ // ============================== import assert from 'assert'; -// START EnableRQ // START 1BitEnableRQ // START RQWithOptions // START Uncompressed +// START EnableRQ // START 4BitEnableRQ // START 1BitEnableRQ // START RQWithOptions // START Uncompressed import weaviate, { configure } from 'weaviate-client'; -// END EnableRQ // END 1BitEnableRQ // END RQWithOptions // END Uncompressed +// END EnableRQ // END 4BitEnableRQ // END 1BitEnableRQ // END RQWithOptions // END Uncompressed const client = await weaviate.connectToLocal({ @@ -39,6 +39,30 @@ await client.collections.create({ }) // END EnableRQ +// ============================== +// ===== EnableRQ 4-BIT ======== +// ============================== + +await client.collections.delete("MyCollection") + +// START 4BitEnableRQ + +await client.collections.create({ + name: "MyCollection", + vectorizers: configure.vectors.text2VecOpenAI({ + // highlight-start + quantizer: configure.vectorIndex.quantizer.rq({ + bits: 4, + rescoreLimit: 50, // Raise the rescore limit; the default of 20 is too low for 4-bit RQ + }) + // highlight-end + }), + properties: [ + { name: "title", dataType: weaviate.configure.dataType.TEXT } + ] +}) +// END 4BitEnableRQ + // ============================== // ===== EnableRQ 1-BIT ======== // ============================== diff --git a/_includes/code/howto/configure-rq/rq-compression-v4.py b/_includes/code/howto/configure-rq/rq-compression-v4.py index 6c1a388b6..cccd9e15b 100644 --- a/_includes/code/howto/configure-rq/rq-compression-v4.py +++ b/_includes/code/howto/configure-rq/rq-compression-v4.py @@ -41,6 +41,31 @@ ) # END EnableRQ +# ============================== +# ===== EnableRQ 4-BIT ======== +# ============================== + +client.collections.delete("MyCollection") + +# START 4BitEnableRQ +from weaviate.classes.config import Configure, Property, DataType + +client.collections.create( + name="MyCollection", + vector_config=Configure.Vectors.text2vec_openai( + # highlight-start + quantizer=Configure.VectorIndex.Quantizer.rq( + bits=4, + rescore_limit=50, # Raise the rescore limit; the default of 20 is too low for 4-bit RQ + ) + # highlight-end + ), + properties=[ + Property(name="title", data_type=DataType.TEXT), + ], +) +# END 4BitEnableRQ + # ============================== # ===== EnableRQ 1-BIT ======== # ============================== @@ -145,6 +170,38 @@ ) # END UpdateSchema +# ================================ +# ===== UPDATE SCHEMA 4-BIT ===== +# ================================ + +client.collections.delete("MyCollection") +client.collections.create( + name="MyCollection", + vector_config=Configure.Vectors.text2vec_openai( + quantizer=Configure.VectorIndex.Quantizer.none(), + ), + properties=[ + Property(name="title", data_type=DataType.TEXT), + ], +) + +# START 4BitUpdateSchema +from weaviate.classes.config import Reconfigure + +collection = client.collections.use("MyCollection") +collection.config.update( + vector_config=Reconfigure.Vectors.update( + name="default", + vector_index_config=Reconfigure.VectorIndex.hnsw( + quantizer=Reconfigure.VectorIndex.Quantizer.rq( + bits=4, + rescore_limit=50, + ), + ), + ) +) +# END 4BitUpdateSchema + # ================================ # ===== UPDATE SCHEMA 1-BIT ===== # ================================ diff --git a/_includes/code/howto/go/docs/configure/compression.rq_test.go b/_includes/code/howto/go/docs/configure/compression.rq_test.go index 6bcf8eece..393db5265 100644 --- a/_includes/code/howto/go/docs/configure/compression.rq_test.go +++ b/_includes/code/howto/go/docs/configure/compression.rq_test.go @@ -89,6 +89,59 @@ func TestRQConfiguration(t *testing.T) { assert.Equal(t, true, rqConfig["enabled"]) }) + t.Run("Enable 4-bit RQ", func(t *testing.T) { + className := "MyCollectionRQDefault" + // Delete the collection if it already exists to ensure a clean start + err := client.Schema().ClassDeleter().WithClassName(className).Do(context.Background()) + if err != nil { + // This is not a fatal error, the collection might not exist + log.Printf("Could not delete collection '%s', it might not exist: %v\n", className, err) + } + + // START 4BitEnableRQ + // Define the configuration for RQ. 'bits' set to 4 requires an hnsw index + // highlight-start + rq_config := map[string]interface{}{ + "enabled": true, + "bits": 4, + // Raise the rescore limit; the default of 20 is too low for 4-bit RQ + "rescoreLimit": 50, + } + // highlight-end + + // Define the class schema + class := &models.Class{ + Class: className, + Vectorizer: "text2vec-openai", + // highlight-start + // Assign the RQ configuration to the vector index config + VectorIndexConfig: map[string]interface{}{ + "rq": rq_config, + }, + // highlight-end + } + + // Create the collection in Weaviate + err = client.Schema().ClassCreator(). + WithClass(class). + Do(context.Background()) + // END 4BitEnableRQ + require.NoError(t, err) + + // Assertions to verify the configuration + classInfo, err := client.Schema().ClassGetter().WithClassName(className).Do(ctx) + require.NoError(t, err) + require.NotNil(t, classInfo) + + vic, ok := classInfo.VectorIndexConfig.(map[string]interface{}) + require.True(t, ok) + rqConfig, ok := vic["rq"].(map[string]interface{}) + require.True(t, ok) + assert.Equal(t, true, rqConfig["enabled"]) + assert.Equal(t, float64(4), rqConfig["bits"]) + assert.Equal(t, float64(50), rqConfig["rescoreLimit"]) + }) + t.Run("Enable 1-bit RQ", func(t *testing.T) { className := "MyCollectionRQDefault" // Delete the collection if it already exists to ensure a clean start @@ -257,6 +310,76 @@ func TestRQConfiguration(t *testing.T) { assert.Equal(t, float64(20), rqConfig["rescoreLimit"]) }) + t.Run("Enable 4-bit RQ on Existing Collection", func(t *testing.T) { + className := "MyExistingCollection" + + // First, create a collection without RQ + err := client.Schema().ClassDeleter().WithClassName(className).Do(context.Background()) + if err != nil { + log.Printf("Could not delete collection '%s', it might not exist: %v\n", className, err) + } + + // Create initial collection without RQ + initialClass := &models.Class{ + Class: className, + Vectorizer: "text2vec-openai", + VectorIndexConfig: map[string]interface{}{ + "distance": "cosine", + }, + } + + err = client.Schema().ClassCreator(). + WithClass(initialClass). + Do(context.Background()) + require.NoError(t, err) + + // START 4BitUpdateSchema + // Get the existing collection configuration + class, err := client.Schema().ClassGetter(). + WithClassName(className).Do(context.Background()) + + if err != nil { + log.Fatalf("get class for vec idx cfg update: %v", err) + } + + // Get the current vector index configuration + cfg := class.VectorIndexConfig.(map[string]interface{}) + + // Add RQ configuration to enable 4-bit quantization + cfg["rq"] = map[string]interface{}{ + "enabled": true, + "bits": 4, + // Raise the rescore limit; the default of 20 is too low for 4-bit RQ + "rescoreLimit": 50, + } + + // Update the class configuration + class.VectorIndexConfig = cfg + + // Apply the updated configuration to the collection + err = client.Schema().ClassUpdater(). + WithClass(class).Do(context.Background()) + + if err != nil { + log.Fatalf("update class to use rq: %v", err) + } + // END 4BitUpdateSchema + + // Verify the RQ configuration was applied + updatedClass, err := client.Schema().ClassGetter(). + WithClassName(className).Do(context.Background()) + require.NoError(t, err) + + vic, ok := updatedClass.VectorIndexConfig.(map[string]interface{}) + require.True(t, ok) + + rqConfig, ok := vic["rq"].(map[string]interface{}) + require.True(t, ok) + assert.Equal(t, true, rqConfig["enabled"]) + assert.Equal(t, float64(4), rqConfig["bits"]) + assert.Equal(t, float64(50), rqConfig["rescoreLimit"]) + }) + t.Run("Enable 1-bit RQ on Existing Collection", func(t *testing.T) { className := "MyExistingCollection" diff --git a/_includes/code/java-v6/src/test/java/ConfigureRQTest.java b/_includes/code/java-v6/src/test/java/ConfigureRQTest.java index 30083fb11..92e6abd4d 100644 --- a/_includes/code/java-v6/src/test/java/ConfigureRQTest.java +++ b/_includes/code/java-v6/src/test/java/ConfigureRQTest.java @@ -45,6 +45,23 @@ void testEnableRQ() throws IOException { // END EnableRQ } + @Test + void test4BitEnableRQ() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START 4BitEnableRQ + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecTransformers(vc -> vc + // highlight-start + .quantization(Quantization.rq(q -> q.bits(4))) + // highlight-end + )).properties(Property.text("title"))); + // END 4BitEnableRQ + } + @Test void test1BitEnableRQ() throws IOException { String collectionName = "MyCollection"; @@ -121,6 +138,27 @@ void testUpdateSchema() throws IOException { // END UpdateSchema } + @Test + void test4BitUpdateSchema() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + client.collections.create(collectionName, + col -> col + .vectorConfig(VectorConfig.text2vecTransformers( + vc -> vc.quantization(Quantization.uncompressed()))) + .properties(Property.text("title"))); + + // START 4BitUpdateSchema + CollectionHandle> collection = + client.collections.use("MyCollection"); + collection.config + .update(c -> c.vectorConfig(VectorConfig.text2vecTransformers( + vc -> vc.quantization(Quantization.rq(q -> q.bits(4)))))); + // END 4BitUpdateSchema + } + @Test void test1BitUpdateSchema() throws IOException { String collectionName = "MyCollection"; diff --git a/_includes/configuration/rq-compression-parameters.mdx b/_includes/configuration/rq-compression-parameters.mdx index 52333c815..f4247fb7f 100644 --- a/_includes/configuration/rq-compression-parameters.mdx +++ b/_includes/configuration/rq-compression-parameters.mdx @@ -1,6 +1,6 @@ | Parameter | Type | Default | Details | | :---------------------- | :------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `rq`: `bits` | integer | `8` | The number of bits used to quantize each data point. Value can be `8` or `1`.

Learn more about [8-bit](/weaviate/concepts/vector-quantization#8-bit-rq) and [1-bit](/weaviate/concepts/vector-quantization#1-bit-rq) RQ. | -| `rq`: `rescoreLimit` | integer | `20` (`hnsw`, 8-bit)
`512` (`hnsw`, 1-bit)
`-1` (`flat`) | The minimum number of candidates to fetch before rescoring.

The default depends on the vector index type, and under `hnsw` also on `bits`: `20` for 8-bit RQ and `512` for 1-bit RQ. Under the `flat` index type the default is `-1`, which lets Weaviate pick the limit.

These defaults apply to the `hnsw` and `flat` index types. For the HFresh index, see [HFresh index parameters](/weaviate/config-refs/indexing/vector-index#hfresh-index-parameters). | +| `rq`: `bits` | integer | `8` | The number of bits used to quantize each data point. Value can be `8`, `4` or `1`. Under the `flat` index type, only `8` and `1` are accepted.

This parameter is fixed once RQ is enabled and cannot be changed afterwards.

Learn more about [8-bit](/weaviate/concepts/vector-quantization#8-bit-rq), [4-bit](/weaviate/concepts/vector-quantization#4-bit-rq) and [1-bit](/weaviate/concepts/vector-quantization#1-bit-rq) RQ. | +| `rq`: `rescoreLimit` | integer | `20` (`hnsw`, 8-bit and 4-bit)
`512` (`hnsw`, 1-bit)
`-1` (`flat`) | The minimum number of candidates to fetch before rescoring. Mutable at any time.

The default depends on the vector index type, and under `hnsw` also on `bits`: `20` for 8-bit and 4-bit RQ, and `512` for 1-bit RQ. Under the `flat` index type the default is `-1`, which lets Weaviate pick the limit.

4-bit RQ inherits the 8-bit default of `20`, which is lower than the value it needs. See [4-bit RQ](/weaviate/configuration/compression/rq-compression#4-bit-rq) for guidance.

The Java client sends this parameter under a field name that Weaviate does not read, so values set from that client are ignored and the server default applies.

These defaults apply to the `hnsw` and `flat` index types. For the HFresh index, see [HFresh index parameters](/weaviate/config-refs/indexing/vector-index#hfresh-index-parameters). | | `rq` : `cache` | boolean | `false` | Whether to cache the vectors in memory.
(only when using the `flat` vector index type) | | `vectorCacheMaxObjects` | integer | `1e12` | Maximum number of objects in the memory cache. By default, this limit is set to one trillion (`1e12`) objects when a new collection is created. For sizing recommendations, see [Vector cache considerations](/weaviate/concepts/vector-index#vector-cache-considerations). | diff --git a/_includes/feature-notes/rq-4bit.mdx b/_includes/feature-notes/rq-4bit.mdx new file mode 100644 index 000000000..7f3a88af6 --- /dev/null +++ b/_includes/feature-notes/rq-4bit.mdx @@ -0,0 +1,5 @@ +:::caution Preview — added in `v1.39.0` + +**4-bit Rotational quantization (RQ)** for the **HNSW vector index** was added in **`v1.39.0`** as a preview feature. The API may change in future releases. + +::: diff --git a/_includes/starter-guides/compression-types.mdx b/_includes/starter-guides/compression-types.mdx index 6f87fc969..3b0672e26 100644 --- a/_includes/starter-guides/compression-types.mdx +++ b/_includes/starter-guides/compression-types.mdx @@ -1,5 +1,5 @@ - **[Rotational Quantization (RQ)](/weaviate/configuration/compression/rq-compression)** (_recommended_) - RQ reduces the size of each vector dimension from 32 bits to 8 bits (or 1 bit) without requiring training. RQ first applies a fast pseudorandom rotation to the vector, then quantizes each dimension. The rotation spreads information evenly across dimensions, enabling up to 98-99% recall without any configuration or training phase. + RQ reduces the size of each vector dimension from 32 bits to 8 bits (or 4 bits, or 1 bit) without requiring training. RQ first applies a fast pseudorandom rotation to the vector, then quantizes each dimension. The rotation spreads information evenly across dimensions, enabling up to 98-99% recall without any configuration or training phase. - **[Product Quantization (PQ)](/weaviate/configuration/compression/pq-compression)** PQ reduces the size of the vector embedding in two ways. PQ trains on your data to create custom segments. PQ creates segments to reduce the number of dimensions, and segments are stored as 8 bit integers instead of 32 bit floats. Compared to dimensions, there are fewer segments and each segment is much smaller than a single dimension. diff --git a/docs/deploy/configuration/env-vars/index.md b/docs/deploy/configuration/env-vars/index.md index 60026acc0..344b776cd 100644 --- a/docs/deploy/configuration/env-vars/index.md +++ b/docs/deploy/configuration/env-vars/index.md @@ -35,7 +35,7 @@ import APITable from '@site/src/components/APITable'; | `CORS_ALLOW_HEADERS` | Value of the `Access-Control-Allow-Headers` response header on the REST API, which controls the request headers a browser may send cross-origin. The default is the long list of headers Weaviate itself reads, including `Content-Type`, `Authorization` and the per-provider API-key headers. Default: the built-in header list | `string - comma separated names` | `Content-Type, Authorization` | | `CORS_ALLOW_METHODS` | Value of the `Access-Control-Allow-Methods` response header on the REST API, which controls the HTTP methods a browser may use cross-origin. Default: `*` | `string - comma separated names` | `GET, POST, OPTIONS` | | `CORS_ALLOW_ORIGIN` | Value of the `Access-Control-Allow-Origin` response header on the REST API, which controls the origins a browser may call Weaviate from. Set this to reach Weaviate directly from browser code on a specific site. Default: `*` | `string` | `https://example.com` | -| `DEFAULT_QUANTIZATION` | Default quantization technique - can be overridden by the quantization method specified in the collection definition. Available values: `rq-8`, `rq-1`, `pq`, `bq`, `sq` and `none`. Default: `none`.

Note: If the selected quantization method isn't supported for the index type of a collection (for example PQ & SQ aren't supported for the flat index), the quantization won't be applied to that collection.

Added in `v1.33` | `string` | `rq-8` | +| `DEFAULT_QUANTIZATION` | Default quantization technique - can be overridden by the quantization method specified in the collection definition. Available values: `rq-8`, `rq-4`, `rq-1`, `pq`, `bq`, `sq` and `none`. Default: `none`.

Note: If the selected quantization method isn't supported for the index type of a collection (for example PQ & SQ aren't supported for the flat index, and `rq-4` is supported for the HNSW index only), the quantization won't be applied to that collection.

Added in `v1.33`. `rq-4` added in `v1.39` as a preview. | `string` | `rq-8` | | `DEFAULT_SHARDING_COUNT` | Default `desiredCount` for new single-tenant collections, used when the collection definition does not specify one. An explicit `desiredCount` in the class creation request still takes precedence. A value of `0` (default) uses the cluster node count. Multi-tenant collections are unaffected. Must be `<= 512`. Runtime-configurable. Default: `0`
Added in `v1.37` | `string - number` | `12` | | `DEFAULT_VECTOR_INDEX` | Default vector index type for new collections (and named vectors), used when the collection definition does not specify one. An explicit `vectorIndexType` in the collection definition still takes precedence. Available values: `hnsw`, `flat`, `dynamic`, and `hfresh`. Runtime-configurable. Default: `hnsw`
Added in `v1.37.3` | `string` | `flat` | | `DEFAULT_VECTORIZER_MODULE` | Default vectorizer module - can be overridden by the vectorizer in the collection definition. | `string` | `text2vec-contextionary` | diff --git a/docs/weaviate/concepts/vector-quantization.md b/docs/weaviate/concepts/vector-quantization.md index 8ca7994fb..9bf87603a 100644 --- a/docs/weaviate/concepts/vector-quantization.md +++ b/docs/weaviate/concepts/vector-quantization.md @@ -7,6 +7,7 @@ image: og/docs/concepts.jpg --- import Rq8bit from '/_includes/feature-notes/rq-8bit.mdx'; +import Rq4bit from '/_includes/feature-notes/rq-4bit.mdx'; import Rq1bit from '/_includes/feature-notes/rq-1bit.mdx'; **Vector quantization** reduces the memory footprint of the [vector index](./indexing/vector-index.md) by compressing the vector embeddings, and thus reduces deployment costs and improves the speed of the vector similarity search process. @@ -121,7 +122,7 @@ When SQ is enabled, Weaviate boosts recall by over-fetching compressed results. ## Rotational quantization -**Rotational quantization (RQ)** provides significant compression while maintaining high recall. Unlike SQ, RQ requires no training phase and can be enabled immediately at index creation. RQ is available in: **8-bit** and **1-bit** variants. +**Rotational quantization (RQ)** provides significant compression while maintaining high recall. Unlike SQ, RQ requires no training phase and can be enabled immediately at index creation. RQ is available in: **8-bit**, **4-bit** and **1-bit** variants. ### 8-bit RQ @@ -133,6 +134,24 @@ When SQ is enabled, Weaviate boosts recall by over-fetching compressed results. 2. **Scalar quantization**: Each entry of the rotated vector is quantized to an 8-bit integer. The minimum and maximum values of each individual rotated vector define the quantization interval. +### 4-bit RQ + + + +4-bit RQ stores each dimension of the rotated vector in 4 bits, so a compressed vector is about half the size of the 8-bit equivalent and roughly 8x smaller than the uncompressed vector. It sits between 8-bit RQ and 1-bit RQ: it gives up some accuracy in the compressed distance calculation in exchange for a smaller index. + +The method works as follows: + +1. **Fast pseudorandom rotation**: The same rotation process as 8-bit RQ is applied to the input vector, and the output dimension is rounded up to the nearest multiple of 64. Because the output dimension is always a multiple of 64, two codes always pack cleanly into one byte. + +2. **Asymmetric quantization**: + - **Data vectors**: Quantized to 4 bits per dimension, over the minimum and maximum values of each individual rotated vector. Two dimensions are packed into each stored byte. + - **Query vectors**: Scalar quantized using 8 bits per dimension during search. + +Quantizing the query at a higher precision than the stored data costs no extra storage and recovers much of the accuracy that the coarser data codes give up. This is the same asymmetric idea used by 1-bit RQ. + +Because the compressed distances are coarser, 4-bit RQ depends more heavily on [rescoring](#over-fetching--re-scoring) than 8-bit RQ does. In internal testing on 1536-dimensional data, 4-bit RQ reaches around 94-95% recall from the compressed distances alone, compared with around 99% for 8-bit RQ. Rescoring the top 50 candidates against the uncompressed vectors brings both variants to 99.8% or better. The default rescore limit is not high enough to reach those figures, so set it explicitly. See [Configuration: 4-bit RQ](../configuration/compression/rq-compression.md#4-bit-rq). + ### 1-bit RQ @@ -155,12 +174,14 @@ This asymmetric approach improves recall compared to symmetric 1-bit schemes (su The rotation step provides multiple benefits. It tends to reduce the quantization interval and decrease quantization error by distributing values more uniformly. It also distributes the distance information more evenly across all dimensions, providing a better starting point for distance estimation. -Both RQ variants round up the number of dimensions to multiples of 64, which means that low-dimensional data (< 64 or 128 dimensions) might result in less than optimal compression. Additionally, several factors affect the actual compression rates: +All RQ variants round up the number of dimensions to multiples of 64, which means that low-dimensional data (< 64 or 128 dimensions) might result in less than optimal compression. Additionally, several factors affect the actual compression rates: -- **Auxiliary data storage**: 16 bytes for 8-bit RQ and 8 bytes for 1-bit RQ are stored with the compressed codes +- **Auxiliary data storage**: 16 bytes for 8-bit and 4-bit RQ and 8 bytes for 1-bit RQ are stored with the compressed codes - **Dimension rounding**: Dimensionality is rounded up to the nearest multiple of 64 and 1-bit RQ is also padded to at least 256 bits -Due to these factors, the 4x and 32x compression rates are only approached as dimensionality increases. These effects are more pronounced for low-dimensional vectors. +Due to these factors, the 4x, 8x and 32x compression rates are only approached as dimensionality increases. These effects are more pronounced for low-dimensional vectors. + +For a 1536-dimensional vector, which is already a multiple of 64, a compressed vector takes 1552 bytes with 8-bit RQ and 784 bytes with 4-bit RQ, against 6144 bytes uncompressed. Both figures include the 16 bytes of auxiliary data. While inspired by extended [RaBitQ](https://arxiv.org/abs/2405.12497), this implementation differs significantly for performance reasons. It uses fast pseudorandom rotations instead of truly random rotations. :::tip @@ -180,7 +201,7 @@ The query retrieves compressed objects until the object count reaches whichever For example, if a query is made with a limit of 10, and a rescore limit of 200, Weaviate fetches 200 objects. After rescoring, the query returns top 10 objects. This process offsets the loss in search quality (recall) that is caused by compression. :::note RQ optimization -With RQ's high native recall of 98-99%, you can often disable rescoring (set `rescoreLimit` to 0) for maximum query performance with minimal impact on search quality. +With 8-bit RQ's high native recall of 98-99%, you can often disable rescoring (set `rescoreLimit` to `0`) for maximum query performance with minimal impact on search quality. Do not do this for 4-bit RQ or 1-bit RQ, which both rely on rescoring to reach their reported recall. ::: ## Vector compression with vector indexing @@ -199,6 +220,8 @@ You might be also interested in our blog post [HNSW+PQ - Exploring ANN algorithm [RQ](#rotational-quantization) and [BQ](#binary-quantization) can be applied to a [flat index](./indexing/vector-index.md#flat-index). As a flat index search is a brute-force method, compression reduces the amount of data Weaviate has to read and increases speed. +A flat index accepts 8-bit and 1-bit RQ. [4-bit RQ](#4-bit-rq) is available for the HNSW index only. + ## Rescoring Quantization inherently involves some loss information due to the reduction in information precision. To mitigate this, Weaviate uses a technique called rescoring, using the uncompressed vectors that are also stored alongside compressed vectors. Rescoring recalculates the distance between the original vectors of the returned candidates from the initial search. This ensures that the most accurate results are returned to the user. diff --git a/docs/weaviate/configuration/compression/rq-compression.md b/docs/weaviate/configuration/compression/rq-compression.md index b8841b255..1cd3eeb54 100644 --- a/docs/weaviate/configuration/compression/rq-compression.md +++ b/docs/weaviate/configuration/compression/rq-compression.md @@ -7,6 +7,7 @@ image: og/docs/configuration.jpg import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import Rq8bit from '/_includes/feature-notes/rq-8bit.mdx'; +import Rq4bit from '/_includes/feature-notes/rq-4bit.mdx'; import Rq1bit from '/_includes/feature-notes/rq-1bit.mdx'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyCode from '!!raw-loader!/\_includes/code/howto/configure-rq/rq-compression-v4.py'; @@ -19,9 +20,10 @@ import CompressionByDefault from '/\_includes/compression-by-default.mdx'; -[**Rotational quantization (RQ)**](../../concepts/vector-quantization.md#rotational-quantization) is a fast vector compression technique that offers significant performance benefits. Two RQ variants are available in Weaviate: +[**Rotational quantization (RQ)**](../../concepts/vector-quantization.md#rotational-quantization) is a fast vector compression technique that offers significant performance benefits. Three RQ variants are available in Weaviate: - **8-bit RQ**: Up to 4x compression while retaining almost perfect recall (98-99% on most datasets). **Recommended** for most use cases. +- **4-bit RQ**: Up to 8x compression, roughly half the size of 8-bit RQ, and it depends on rescoring to reach comparable recall. Available for the `hnsw` index only. - **1-bit RQ**: Close to 32x compression as dimensionality increases with moderate recall across various datasets. ## 8-bit RQ @@ -124,6 +126,121 @@ RQ can also be enabled for an existing collection by updating the collection def +## 4-bit RQ + + + +[4-bit RQ](../../concepts/vector-quantization.md#4-bit-rq) stores each dimension in 4 bits instead of 8, so a compressed vector is about half the size of the 8-bit equivalent and roughly 8x smaller than the uncompressed vector. It sits between 8-bit RQ and 1-bit RQ: it trades some accuracy in the compressed distance calculation for a smaller index, and it makes up the difference by rescoring more candidates against the uncompressed vectors. + +:::caution Raise `rescoreLimit` when you use 4-bit RQ + +When `bits` is set to `4`, `rescoreLimit` defaults to `20`, the same default as 8-bit RQ. That window is too small for 4-bit RQ to reach the recall it is capable of, and Weaviate does not warn you about it. + +Set `rescoreLimit` explicitly. Start at `50`, and raise it to at least the largest `limit` you expect to query with. Never set it to `0` with 4-bit RQ, because that turns rescoring off entirely and leaves the coarse compressed distances as the final ranking. + +::: + +### Enable compression for new collection + +4-bit RQ can be enabled at collection creation time through the collection definition: + + + + + + + + + + + + + + + + + + + +### Enable compression for existing collection + +4-bit RQ can also be enabled for an existing collection that is not yet compressed, by updating the collection definition. Weaviate re-encodes the existing vectors in the background. + + + + + + + + + + + + + + + + + + + +### 4-bit RQ limitations + +- **`hnsw` index only.** A `flat` index rejects `bits` set to `4` with `RQ bits must be either 1 or 8`. A `dynamic` index starts on its flat portion and switches to HNSW once the collection passes the threshold, so 4-bit RQ can only be configured on the `hnsw` portion of a dynamic index. The flat portion of that collection stays uncompressed or uses another quantizer, and the collection is only compressed with 4-bit RQ after it converts to HNSW. +- **`bits` cannot be changed later.** Once RQ is enabled, the number of bits is fixed. A request that changes it fails with `rq bits is immutable`. To move between bit widths, recreate the collection and reimport. Enabling RQ on an existing uncompressed `hnsw` collection is supported, and that is the point at which `bits` is fixed. +- **`rescoreLimit` stays mutable.** You can change it at any time without reindexing. +- **Supported distance metrics.** RQ supports `cosine`, `dot` and `l2-squared`. Other distance metrics are not supported. + ## 1-bit RQ @@ -298,7 +415,7 @@ import MultiVectorCompress from '/\_includes/multi-vector-compress.mdx'; :::note Multi-vector performance -RQ supports multi-vector embeddings. Each token vector is rounded up to a multiple of 64 dimensions, which may result in less than 4x compression for very short vectors. This is a technical limitation that may be addressed in future versions. +RQ supports multi-vector embeddings. Each token vector is rounded up to a multiple of 64 dimensions, which may result in less than the nominal compression ratio for very short vectors. This is a technical limitation that may be addressed in future versions. ::: ## Further resources diff --git a/docs/weaviate/starter-guides/managing-resources/compression.mdx b/docs/weaviate/starter-guides/managing-resources/compression.mdx index 2ab2421f2..441822e4d 100644 --- a/docs/weaviate/starter-guides/managing-resources/compression.mdx +++ b/docs/weaviate/starter-guides/managing-resources/compression.mdx @@ -44,6 +44,8 @@ This table shows the compression algorithms that are available for each index ty | RQ | Yes | Yes | Yes | Yes | | BQ | Yes | Yes | Yes | No | +RQ comes in three bit widths, and they are not equally available. 8-bit and 1-bit RQ work with every index type in the table. [4-bit RQ](/weaviate/configuration/compression/rq-compression#4-bit-rq) works with the HNSW index only, which means a dynamic index can only use it after it converts from flat to HNSW. + The [dynamic index](/weaviate/config-refs/indexing/vector-index.mdx#dynamic-index) is new in v1.25. This type of index is a [flat index](/weaviate/config-refs/indexing/vector-index.mdx#flat-index) until a collection reaches a threshold size. When the collection grows larger than the threshold size, the default is 10,000 objects, the collection is automatically reindexed and converted to an HNSW index. ### Cost, recall and speed From 21ae55c5b44b5de23113a298b325f2e3ef0068ee Mon Sep 17 00:00:00 2001 From: Ivan Despot <66276597+g-despot@users.noreply.github.com> Date: Wed, 12 Aug 2026 14:23:13 +0200 Subject: [PATCH 2/5] docs: correct per-index RQ bit support and drop the empty 4-bit TS tab Verifier findings on the 4-bit RQ branch. - Starter guide claimed "8-bit and 1-bit RQ work with every index type in the table". False for the HFresh column: hfresh accepts 1-bit only (entities/vectorindex/hfresh/config.go:142-144 at v1.39.0). Restate the support per index type: hnsw 8/4/1, flat 8/1, hfresh 1 only, dynamic per side. - The shared RQ parameters include named only the flat restriction, so a reader on the vector index reference (where it renders directly below the HFresh section) was told 8, 4 and 1 are all valid for HFresh. Name all three index types. Also state the fixed 1-bit width on the HFresh rq row. - Remove the TypeScript tab from the 4-bit "existing collection" example. It rendered an empty code block because the TS client's reconfigure path takes no bits value. The swizzled Tabs component now shows the honest "not yet available" message instead. - Rename the Go marker UpdateSchemaToEnableRQ to 8BitUpdateSchema so no marker name is a prefix of another, since FilteredTextBlock matches markers by substring. Co-Authored-By: Claude Opus 5 (1M context) --- .../howto/go/docs/configure/compression.rq_test.go | 4 ++-- .../configuration/rq-compression-parameters.mdx | 2 +- .../weaviate/config-refs/indexing/vector-index.mdx | 2 +- .../configuration/compression/rq-compression.md | 14 +++----------- .../managing-resources/compression.mdx | 2 +- 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/_includes/code/howto/go/docs/configure/compression.rq_test.go b/_includes/code/howto/go/docs/configure/compression.rq_test.go index 393db5265..24b48d176 100644 --- a/_includes/code/howto/go/docs/configure/compression.rq_test.go +++ b/_includes/code/howto/go/docs/configure/compression.rq_test.go @@ -267,7 +267,7 @@ func TestRQConfiguration(t *testing.T) { Do(context.Background()) require.NoError(t, err) - // START UpdateSchemaToEnableRQ + // START 8BitUpdateSchema // Get the existing collection configuration class, err := client.Schema().ClassGetter(). WithClassName(className).Do(context.Background()) @@ -294,7 +294,7 @@ func TestRQConfiguration(t *testing.T) { if err != nil { log.Fatalf("update class to use rq: %v", err) } - // END UpdateSchemaToEnableRQ + // END 8BitUpdateSchema // Verify the RQ configuration was applied updatedClass, err := client.Schema().ClassGetter(). diff --git a/_includes/configuration/rq-compression-parameters.mdx b/_includes/configuration/rq-compression-parameters.mdx index f4247fb7f..4a2400657 100644 --- a/_includes/configuration/rq-compression-parameters.mdx +++ b/_includes/configuration/rq-compression-parameters.mdx @@ -1,6 +1,6 @@ | Parameter | Type | Default | Details | | :---------------------- | :------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `rq`: `bits` | integer | `8` | The number of bits used to quantize each data point. Value can be `8`, `4` or `1`. Under the `flat` index type, only `8` and `1` are accepted.

This parameter is fixed once RQ is enabled and cannot be changed afterwards.

Learn more about [8-bit](/weaviate/concepts/vector-quantization#8-bit-rq), [4-bit](/weaviate/concepts/vector-quantization#4-bit-rq) and [1-bit](/weaviate/concepts/vector-quantization#1-bit-rq) RQ. | +| `rq`: `bits` | integer | `8` | The number of bits used to quantize each data point. Value can be `8`, `4` or `1`, but not every index type accepts all three. The `hnsw` index type accepts `8`, `4` and `1`. The `flat` index type accepts only `8` and `1`. The `hfresh` index type accepts only `1`.

This parameter is fixed once RQ is enabled and cannot be changed afterwards.

Learn more about [8-bit](/weaviate/concepts/vector-quantization#8-bit-rq), [4-bit](/weaviate/concepts/vector-quantization#4-bit-rq) and [1-bit](/weaviate/concepts/vector-quantization#1-bit-rq) RQ. | | `rq`: `rescoreLimit` | integer | `20` (`hnsw`, 8-bit and 4-bit)
`512` (`hnsw`, 1-bit)
`-1` (`flat`) | The minimum number of candidates to fetch before rescoring. Mutable at any time.

The default depends on the vector index type, and under `hnsw` also on `bits`: `20` for 8-bit and 4-bit RQ, and `512` for 1-bit RQ. Under the `flat` index type the default is `-1`, which lets Weaviate pick the limit.

4-bit RQ inherits the 8-bit default of `20`, which is lower than the value it needs. See [4-bit RQ](/weaviate/configuration/compression/rq-compression#4-bit-rq) for guidance.

The Java client sends this parameter under a field name that Weaviate does not read, so values set from that client are ignored and the server default applies.

These defaults apply to the `hnsw` and `flat` index types. For the HFresh index, see [HFresh index parameters](/weaviate/config-refs/indexing/vector-index#hfresh-index-parameters). | | `rq` : `cache` | boolean | `false` | Whether to cache the vectors in memory.
(only when using the `flat` vector index type) | | `vectorCacheMaxObjects` | integer | `1e12` | Maximum number of objects in the memory cache. By default, this limit is set to one trillion (`1e12`) objects when a new collection is created. For sizing recommendations, see [Vector cache considerations](/weaviate/concepts/vector-index#vector-cache-considerations). | diff --git a/docs/weaviate/config-refs/indexing/vector-index.mdx b/docs/weaviate/config-refs/indexing/vector-index.mdx index 195d27716..b9f9b7cea 100644 --- a/docs/weaviate/config-refs/indexing/vector-index.mdx +++ b/docs/weaviate/config-refs/indexing/vector-index.mdx @@ -187,7 +187,7 @@ HFresh only supports `cosine` and `l2-squared` distance metrics. Dot product is | `maxPostingSizeKB` | integer | `48` | Yes | Maximum size in KB for a posting list. Weaviate uses this value along with the vector dimensions to calculate the maximum number of vectors per posting. Min: `8`, Max: `1024`. Best set when you create the collection: an update is accepted but only affects newly-indexed data. Data that is already indexed is not re-partitioned. | | `replicas` | integer | `4` | No | Number of posting lists in which a vector is added. Min: `1`, Max: `10`. | | `searchProbe` | integer | `256` | Yes | Number of posting lists to search during a query. The default is `256` in `v1.36.20`, `v1.37.10`, `v1.38.2` and later. Earlier releases on each of those lines default to `64`. | -| `rq` | object | -- | Partial | Rotational quantization (RQ) compression configuration. RQ is mandatory for HFresh and cannot be turned off. Its `rescoreLimit` (default `350`), the number of candidates rescored against uncompressed vectors, is mutable at runtime. | +| `rq` | object | -- | Partial | Rotational quantization (RQ) compression configuration. RQ is mandatory for HFresh and cannot be turned off. Its `bits` value is fixed at `1`; a request that sets a wider width is rejected. Its `rescoreLimit` (default `350`), the number of candidates rescored against uncompressed vectors, is mutable at runtime. | :::tip Tuning HFresh recall Start with the defaults. If recall is too low, increase `searchProbe` (search more posting lists per query) or the RQ `rescoreLimit` (rescore more candidates with full-precision vectors). Both are mutable at runtime and take effect **without reindexing**. diff --git a/docs/weaviate/configuration/compression/rq-compression.md b/docs/weaviate/configuration/compression/rq-compression.md index 1cd3eeb54..03a7980ed 100644 --- a/docs/weaviate/configuration/compression/rq-compression.md +++ b/docs/weaviate/configuration/compression/rq-compression.md @@ -119,8 +119,8 @@ RQ can also be enabled for an existing collection by updating the collection def @@ -200,14 +200,6 @@ Set `rescoreLimit` explicitly. Start at `50`, and raise it to at least the large language="py" /> - - - Date: Thu, 13 Aug 2026 10:07:42 +0200 Subject: [PATCH 3/5] docs: collapse the 4-bit RQ limitations into a note, drop the byte-count example Review feedback on PR #509. - Replace the "4-bit RQ limitations" subsection with a short note under "## 4-bit RQ" carrying the hnsw-only constraint. The per-index bit widths and the immutability of `bits` are already documented in the RQ parameters table and the compression starter guide. - Keep the RQ distance metric support statement by moving it into the "RQ parameters" section; it applies to all RQ variants, not just 4-bit. - Remove the 1536-dimensional byte-count example from the RQ characteristics section of the vector quantization concepts page. Co-Authored-By: Claude Opus 5 (1M context) --- docs/weaviate/concepts/vector-quantization.md | 2 -- .../configuration/compression/rq-compression.md | 15 ++++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/weaviate/concepts/vector-quantization.md b/docs/weaviate/concepts/vector-quantization.md index 9bf87603a..3e2cdc3f4 100644 --- a/docs/weaviate/concepts/vector-quantization.md +++ b/docs/weaviate/concepts/vector-quantization.md @@ -181,8 +181,6 @@ All RQ variants round up the number of dimensions to multiples of 64, which mean Due to these factors, the 4x, 8x and 32x compression rates are only approached as dimensionality increases. These effects are more pronounced for low-dimensional vectors. -For a 1536-dimensional vector, which is already a multiple of 64, a compressed vector takes 1552 bytes with 8-bit RQ and 784 bytes with 4-bit RQ, against 6144 bytes uncompressed. Both figures include the 16 bytes of auxiliary data. - While inspired by extended [RaBitQ](https://arxiv.org/abs/2405.12497), this implementation differs significantly for performance reasons. It uses fast pseudorandom rotations instead of truly random rotations. :::tip diff --git a/docs/weaviate/configuration/compression/rq-compression.md b/docs/weaviate/configuration/compression/rq-compression.md index 03a7980ed..508366c9b 100644 --- a/docs/weaviate/configuration/compression/rq-compression.md +++ b/docs/weaviate/configuration/compression/rq-compression.md @@ -132,6 +132,12 @@ RQ can also be enabled for an existing collection by updating the collection def [4-bit RQ](../../concepts/vector-quantization.md#4-bit-rq) stores each dimension in 4 bits instead of 8, so a compressed vector is about half the size of the 8-bit equivalent and roughly 8x smaller than the uncompressed vector. It sits between 8-bit RQ and 1-bit RQ: it trades some accuracy in the compressed distance calculation for a smaller index, and it makes up the difference by rescoring more candidates against the uncompressed vectors. +:::note 4-bit RQ requires the `hnsw` index + +4-bit RQ is supported on the `hnsw` index type only. The `flat` and `hfresh` index types reject `bits` set to `4`, and a `dynamic` index only uses 4-bit RQ after it converts to HNSW. For the bit widths that each index type accepts, see [RQ parameters](#rq-parameters). + +::: + :::caution Raise `rescoreLimit` when you use 4-bit RQ When `bits` is set to `4`, `rescoreLimit` defaults to `20`, the same default as 8-bit RQ. That window is too small for 4-bit RQ to reach the recall it is capable of, and Weaviate does not warn you about it. @@ -226,13 +232,6 @@ Set `rescoreLimit` explicitly. Start at `50`, and raise it to at least the large -### 4-bit RQ limitations - -- **`hnsw` index only.** A `flat` index rejects `bits` set to `4` with `RQ bits must be either 1 or 8`, and an `hfresh` index rejects it with `rq only supports 1 bit, got 4`. A `dynamic` index starts on its flat portion and switches to HNSW once the collection passes the threshold, so 4-bit RQ can only be configured on the `hnsw` portion of a dynamic index. The flat portion of that collection stays uncompressed or uses another quantizer, and the collection is only compressed with 4-bit RQ after it converts to HNSW. -- **`bits` cannot be changed later.** Once RQ is enabled, the number of bits is fixed. A request that changes it fails with `rq bits is immutable`. To move between bit widths, recreate the collection and reimport. Enabling RQ on an existing uncompressed `hnsw` collection is supported, and that is the point at which `bits` is fixed. -- **`rescoreLimit` stays mutable.** You can change it at any time without reindexing. -- **Supported distance metrics.** RQ supports `cosine`, `dot` and `l2-squared`. Other distance metrics are not supported. - ## 1-bit RQ @@ -341,6 +340,8 @@ import RQParameters from '/\_includes/configuration/rq-compression-parameters.md +RQ supports the `cosine`, `dot` and `l2-squared` distance metrics. Other distance metrics are not supported. + Date: Thu, 13 Aug 2026 10:20:35 +0200 Subject: [PATCH 4/5] docs: link 8-bit and 1-bit RQ mentions in the compression starter guide The "RQ comes in three bit widths" paragraph linked only the 4-bit mention. Link the first 8-bit and 1-bit mentions to the matching headings on the RQ compression reference page, using the same absolute path form as the existing 4-bit link. Later mentions of each width stay unlinked, following the first-mention convention. Co-Authored-By: Claude Opus 5 (1M context) --- docs/weaviate/starter-guides/managing-resources/compression.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/weaviate/starter-guides/managing-resources/compression.mdx b/docs/weaviate/starter-guides/managing-resources/compression.mdx index 7e9581dc1..7207ce72d 100644 --- a/docs/weaviate/starter-guides/managing-resources/compression.mdx +++ b/docs/weaviate/starter-guides/managing-resources/compression.mdx @@ -44,7 +44,7 @@ This table shows the compression algorithms that are available for each index ty | RQ | Yes | Yes | Yes | Yes | | BQ | Yes | Yes | Yes | No | -RQ comes in three bit widths, and they are not equally available. The HNSW index accepts all three: 8-bit, [4-bit](/weaviate/configuration/compression/rq-compression#4-bit-rq) and 1-bit. The flat index accepts 8-bit and 1-bit. The HFresh index accepts 1-bit only: RQ is mandatory on that index, so it can be neither turned off nor set to a wider bit width. A dynamic index holds a flat configuration and an HNSW configuration side by side, so each side follows its own rule: 4-bit RQ can only be set on the HNSW side, and it applies after the collection converts from flat to HNSW. +RQ comes in three bit widths, and they are not equally available. The HNSW index accepts all three: [8-bit](/weaviate/configuration/compression/rq-compression#8-bit-rq), [4-bit](/weaviate/configuration/compression/rq-compression#4-bit-rq) and [1-bit](/weaviate/configuration/compression/rq-compression#1-bit-rq). The flat index accepts 8-bit and 1-bit. The HFresh index accepts 1-bit only: RQ is mandatory on that index, so it can be neither turned off nor set to a wider bit width. A dynamic index holds a flat configuration and an HNSW configuration side by side, so each side follows its own rule: 4-bit RQ can only be set on the HNSW side, and it applies after the collection converts from flat to HNSW. The [dynamic index](/weaviate/config-refs/indexing/vector-index.mdx#dynamic-index) is new in v1.25. This type of index is a [flat index](/weaviate/config-refs/indexing/vector-index.mdx#flat-index) until a collection reaches a threshold size. When the collection grows larger than the threshold size, the default is 10,000 objects, the collection is automatically reindexed and converted to an HNSW index. From a19825083c2643b89b7ddb2ab16693d4dfe38797 Mon Sep 17 00:00:00 2001 From: Ivan Despot <66276597+g-despot@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:33:27 +0200 Subject: [PATCH 5/5] docs: scope the RQ recall claim by bit width and require rescoreLimit headroom Two review findings from PR #509. The compression starter-guide include claimed 98-99% recall "without any configuration" for RQ as a whole. That is an 8-bit figure. This branch extended the include to cover 4-bit, which inherits rescoreLimit 20 and does not reach that recall at its defaults. Scope the claim to 8-bit and point the narrower widths at the rescore guidance. The same broadening had reached three neighbouring claims on the host page (memory saving, the typical-recall list, and "RQ can run without rescoring"), so scope those too. The 4-bit rescoreLimit guidance said "at least the largest query limit", which permits rescoreLimit == limit. At v1.39.0 the HNSW rescore path truncates the candidate pool to rescoreLimit when rescoreLimit >= k, so that setting rescores exactly as many candidates as it returns: it can reorder the results but cannot recover a neighbour the compressed distances dropped. A query with limit 20 against the 4-bit default of 20 lands on that zero-headroom case. Require a value strictly greater than the largest query limit, give ef as the ceiling, and keep the warning that 0 disables rescoring. Co-Authored-By: Claude Opus 5 (1M context) --- _includes/starter-guides/compression-types.mdx | 2 +- docs/weaviate/configuration/compression/rq-compression.md | 6 +++++- .../starter-guides/managing-resources/compression.mdx | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/_includes/starter-guides/compression-types.mdx b/_includes/starter-guides/compression-types.mdx index 3b0672e26..e648ea084 100644 --- a/_includes/starter-guides/compression-types.mdx +++ b/_includes/starter-guides/compression-types.mdx @@ -1,5 +1,5 @@ - **[Rotational Quantization (RQ)](/weaviate/configuration/compression/rq-compression)** (_recommended_) - RQ reduces the size of each vector dimension from 32 bits to 8 bits (or 4 bits, or 1 bit) without requiring training. RQ first applies a fast pseudorandom rotation to the vector, then quantizes each dimension. The rotation spreads information evenly across dimensions, enabling up to 98-99% recall without any configuration or training phase. + RQ reduces the size of each vector dimension from 32 bits to 8 bits (or 4 bits, or 1 bit) without requiring training. RQ first applies a fast pseudorandom rotation to the vector, then quantizes each dimension. The rotation spreads information evenly across dimensions, so 8-bit RQ reaches up to 98-99% recall with no configuration and no training phase. The 4-bit and 1-bit widths compress further and depend on [rescoring](/weaviate/configuration/compression/rq-compression#4-bit-rq) for their recall, so 4-bit RQ needs a higher `rescoreLimit` than its default. - **[Product Quantization (PQ)](/weaviate/configuration/compression/pq-compression)** PQ reduces the size of the vector embedding in two ways. PQ trains on your data to create custom segments. PQ creates segments to reduce the number of dimensions, and segments are stored as 8 bit integers instead of 32 bit floats. Compared to dimensions, there are fewer segments and each segment is much smaller than a single dimension. diff --git a/docs/weaviate/configuration/compression/rq-compression.md b/docs/weaviate/configuration/compression/rq-compression.md index 508366c9b..2ce11e239 100644 --- a/docs/weaviate/configuration/compression/rq-compression.md +++ b/docs/weaviate/configuration/compression/rq-compression.md @@ -142,7 +142,11 @@ RQ can also be enabled for an existing collection by updating the collection def When `bits` is set to `4`, `rescoreLimit` defaults to `20`, the same default as 8-bit RQ. That window is too small for 4-bit RQ to reach the recall it is capable of, and Weaviate does not warn you about it. -Set `rescoreLimit` explicitly. Start at `50`, and raise it to at least the largest `limit` you expect to query with. Never set it to `0` with 4-bit RQ, because that turns rescoring off entirely and leaves the coarse compressed distances as the final ranking. +Rescoring re-ranks a pool of candidates against the uncompressed vectors and returns the best `limit` of them, so it can only recover a true neighbor that the compressed distances ranked too low if that pool holds more than `limit` candidates. When `rescoreLimit` is the same as the query `limit`, the pool holds exactly `limit` candidates, so the objects you get back are the ones the compressed distances chose, only reordered. A query with a `limit` of `20` against the 4-bit default of `20` is exactly that case. + +Set `rescoreLimit` higher than the largest `limit` you expect to query with, so that rescoring has spare candidates to promote, then tune upward from there until recall is high enough. `50` is a reasonable starting point for queries that return 10 to 20 objects. Each extra candidate costs one distance calculation against an uncompressed vector, and there is a ceiling: rescoring never sees more candidates than the graph search returned, which is bounded by [`ef`](/weaviate/config-refs/indexing/vector-index#hnsw-index-parameters). + +Never set `rescoreLimit` to `0` with 4-bit RQ. A value of `0` disables rescoring entirely and leaves the coarse compressed distances as the final ranking. ::: diff --git a/docs/weaviate/starter-guides/managing-resources/compression.mdx b/docs/weaviate/starter-guides/managing-resources/compression.mdx index 7207ce72d..56285ddc0 100644 --- a/docs/weaviate/starter-guides/managing-resources/compression.mdx +++ b/docs/weaviate/starter-guides/managing-resources/compression.mdx @@ -62,7 +62,7 @@ The cost savings are most visible with in-memory indexes such as HNSW. More RAM - PQ compressed vectors typically use 85% less memory than uncompressed vectors. - SQ compressed vectors use 75% less memory than uncompressed vectors. -- RQ compressed vectors typically use 75% less memory than uncompressed vectors. +- RQ compressed vectors typically use 75% less memory than uncompressed vectors (8-bit RQ; the 4-bit and 1-bit widths save more). - BQ compressed vectors use 97% less memory than uncompressed vectors. An HNSW index comprises a connection graph as well as the vectors. Quantization methods reduce the size of the vectors, but do not affect the size of the graph. As a result the overall reduction in memory usage is less than the reduction in vector size, but still significant. If you need to reduce memory further, the disk-based [HFresh index](/weaviate/concepts/vector-index#hfresh-index) avoids keeping the full graph in memory altogether. @@ -77,7 +77,7 @@ Typical recall rates: - PQ: Varies based on configuration - SQ: 95-97% recall -- RQ: 98-99% recall +- RQ: 98-99% recall for 8-bit RQ; 4-bit and 1-bit RQ start lower and rely on rescoring - BQ: Varies significantly based on data and model characteristics To improve recall with compressed vectors, Weaviate over-fetches a list of candidate vectors during a search. For each item on the candidate list, Weaviate fetches the corresponding uncompressed vector. To determine the final ranking, Weaviate calculates the distances from the uncompressed vectors to the query vector. @@ -100,7 +100,7 @@ Each compression algorithm has its own characteristics with regard to speed. - SQ significantly improves search speeds. It is faster than PQ, perhaps 3 to 4 times as fast as searching uncompressed vectors. SQ has a higher dimensional resolution than BQ that helps recall. Look for an upcoming blog post that discusses the tradeoffs with SQ compression. -- RQ provides the fastest query performance among 8-bit quantization methods. RQ uses SIMD-optimized distance computations that are typically 2-3x faster than uncompressed vectors. RQ can be faster than SQ while providing better recall. For maximum performance, RQ can run without rescoring with minimal impact on recall. +- RQ provides the fastest query performance among 8-bit quantization methods. RQ uses SIMD-optimized distance computations that are typically 2-3x faster than uncompressed vectors. RQ can be faster than SQ while providing better recall. For maximum performance, 8-bit RQ can run without rescoring with minimal impact on recall. Do not turn rescoring off for 4-bit or 1-bit RQ, which both depend on it. SQ and BQ both have optional vector caches. Use these configurable caches to load frequently used, uncompressed vectors into memory to improve overall search times. @@ -129,7 +129,7 @@ Starting in v1.22, Weaviate has an optional, [asynchronous indexing](/weaviate/c Most applications benefit from compression. The cost savings are significant. In [Weaviate Cloud](https://weaviate.io/pricing), for example, compressed collections can be more than 80% cheaper than uncompressed collections. -- For most users with HNSW indexes who want the best combination of simplicity, performance, and recall, **consider 8-bit RQ compression**. RQ provides 4x compression with 98-99% recall and requires no configuration or training. It's ideal for standard use cases with embeddings from providers like OpenAI. +- For most users with HNSW indexes who want the best combination of simplicity, performance, and recall, **consider 8-bit RQ compression**. 8-bit RQ provides 4x compression with 98-99% recall and requires no configuration or training. It's ideal for standard use cases with embeddings from providers like OpenAI. - If you have a small collection that uses a flat index, consider RQ compression. The flat index with RQ enabled is smaller and much faster than the uncompressed equivalent.