From fac4c806ee4e043794c19dad45f181977d2bc073 Mon Sep 17 00:00:00 2001 From: John Trengrove Date: Tue, 25 Aug 2026 15:19:34 +1000 Subject: [PATCH 1/2] Add RQ4 centered quantization support --- integration/test_collection_config.py | 26 +++++++++++++++++++ test/collection/test_config.py | 17 ++++++++++++ weaviate/collections/classes/config.py | 12 ++++++++- .../collections/classes/config_methods.py | 2 ++ .../classes/config_vector_index.py | 8 ++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/integration/test_collection_config.py b/integration/test_collection_config.py index b1bd30e12..5c434231a 100644 --- a/integration/test_collection_config.py +++ b/integration/test_collection_config.py @@ -662,6 +662,32 @@ def test_hnsw_with_rq(collection_factory: CollectionFactory) -> None: assert config.vector_index_config.quantizer.rescore_limit == 20 +def test_hnsw_with_rq4c(collection_factory: CollectionFactory) -> None: + dummy = collection_factory("dummy") + if dummy._connection._weaviate_version.is_lower_than(1, 39, 2): + pytest.skip("RQ centering is not supported in Weaviate versions lower than 1.39.2") + + collection = collection_factory( + vector_index_config=Configure.VectorIndex.hnsw( + vector_cache_max_objects=5, + quantizer=Configure.VectorIndex.Quantizer.rq( + bits=4, centering=True, rescore_limit=20, training_limit=5000 + ), + ), + ) + + config = collection.config.get() + assert config.vector_index_type == VectorIndexType.HNSW + assert config.vector_index_config is not None + assert isinstance(config.vector_index_config, _VectorIndexConfigHNSW) + assert isinstance(config.vector_index_config.quantizer, _RQConfig) + assert config.vector_index_config.quantizer is not None + assert config.vector_index_config.quantizer.bits == 4 + assert config.vector_index_config.quantizer.centering is True + assert config.vector_index_config.quantizer.rescore_limit == 20 + assert config.vector_index_config.quantizer.training_limit == 5000 + + @pytest.mark.parametrize( "vector_index_config", [ diff --git a/test/collection/test_config.py b/test/collection/test_config.py index 83ea75498..08227b886 100644 --- a/test/collection/test_config.py +++ b/test/collection/test_config.py @@ -1619,6 +1619,23 @@ def test_vector_config_hnsw_rq() -> None: assert vi_dict["rq"]["rescoreLimit"] == 123 +def test_vector_config_hnsw_rq4c() -> None: + vector_index = Configure.VectorIndex.hnsw( + ef_construction=128, + quantizer=Configure.VectorIndex.Quantizer.rq( + bits=4, centering=True, rescore_limit=123, training_limit=5012 + ), + ) + + vi_dict = vector_index._to_dict() + + assert vi_dict["efConstruction"] == 128 + assert vi_dict["rq"]["bits"] == 4 + assert vi_dict["rq"]["centering"] is True + assert vi_dict["rq"]["rescoreLimit"] == 123 + assert vi_dict["rq"]["trainingLimit"] == 5012 + + def test_vector_config_flat_pq() -> None: vector_index = Configure.VectorIndex.flat( distance_metric=VectorDistances.DOT, diff --git a/weaviate/collections/classes/config.py b/weaviate/collections/classes/config.py index e9effaf15..bb099fc69 100644 --- a/weaviate/collections/classes/config.py +++ b/weaviate/collections/classes/config.py @@ -1981,6 +1981,8 @@ class _RQConfig(_ConfigBase): cache: Optional[bool] bits: Optional[int] rescore_limit: int + centering: Optional[bool] + training_limit: Optional[int] BQConfig = _BQConfig @@ -2984,6 +2986,8 @@ def rq( rescore_limit: Optional[int] = None, enabled: bool = True, bits: Optional[int] = None, + centering: Optional[bool] = None, + training_limit: Optional[int] = None, ) -> _RQConfigUpdate: """Create a `_RQConfigUpdate` object to be used when updating the Rotational quantization (RQ) configuration of Weaviate. @@ -2992,7 +2996,13 @@ def rq( Arguments: See [the docs](https://weaviate.io/developers/weaviate/concepts/vector-index#hnsw-with-compression) for a more detailed view! """ # noqa: D417 (missing argument descriptions in the docstring) - return _RQConfigUpdate(enabled=enabled, rescoreLimit=rescore_limit, bits=bits) + return _RQConfigUpdate( + enabled=enabled, + rescoreLimit=rescore_limit, + bits=bits, + centering=centering, + trainingLimit=training_limit, + ) class _VectorIndexUpdate: diff --git a/weaviate/collections/classes/config_methods.py b/weaviate/collections/classes/config_methods.py index 691cf208d..77d50314d 100644 --- a/weaviate/collections/classes/config_methods.py +++ b/weaviate/collections/classes/config_methods.py @@ -157,6 +157,8 @@ def __get_quantizer_config( cache=config["rq"].get("cache"), bits=config["rq"].get("bits"), rescore_limit=config["rq"].get("rescoreLimit"), + centering=config["rq"].get("centering"), + training_limit=config["rq"].get("trainingLimit"), ) return quantizer diff --git a/weaviate/collections/classes/config_vector_index.py b/weaviate/collections/classes/config_vector_index.py index ff6a0ba40..cc9c74994 100644 --- a/weaviate/collections/classes/config_vector_index.py +++ b/weaviate/collections/classes/config_vector_index.py @@ -299,6 +299,8 @@ class _RQConfigCreate(_QuantizerConfigCreate): cache: Optional[bool] bits: Optional[int] rescoreLimit: Optional[int] + centering: Optional[bool] + trainingLimit: Optional[int] @staticmethod def quantizer_name() -> str: @@ -337,6 +339,8 @@ class _RQConfigUpdate(_QuantizerConfigUpdate): enabled: Optional[bool] rescoreLimit: Optional[int] bits: Optional[int] + centering: Optional[bool] + trainingLimit: Optional[int] @staticmethod def quantizer_name() -> str: @@ -486,6 +490,8 @@ def rq( cache: Optional[bool] = None, bits: Optional[int] = None, rescore_limit: Optional[int] = None, + centering: Optional[bool] = None, + training_limit: Optional[int] = None, ) -> _RQConfigCreate: """Create a `_RQConfigCreate` object to be used when defining the Rotational quantization (RQ) configuration of Weaviate. @@ -498,6 +504,8 @@ def rq( cache=cache, bits=bits, rescoreLimit=rescore_limit, + centering=centering, + trainingLimit=training_limit, ) @staticmethod From e5d3ecb3137edb3a4799dbdaaaecb7699cbdff44 Mon Sep 17 00:00:00 2001 From: John Trengrove Date: Tue, 25 Aug 2026 15:40:25 +1000 Subject: [PATCH 2/2] Add update check --- integration/test_collection_config.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/integration/test_collection_config.py b/integration/test_collection_config.py index 5c434231a..68f1a0691 100644 --- a/integration/test_collection_config.py +++ b/integration/test_collection_config.py @@ -687,6 +687,20 @@ def test_hnsw_with_rq4c(collection_factory: CollectionFactory) -> None: assert config.vector_index_config.quantizer.rescore_limit == 20 assert config.vector_index_config.quantizer.training_limit == 5000 + collection.config.update( + vector_index_config=Reconfigure.VectorIndex.hnsw( + quantizer=Reconfigure.VectorIndex.Quantizer.rq(rescore_limit=50, training_limit=10000), + ), + ) + + config = collection.config.get() + assert isinstance(config.vector_index_config, _VectorIndexConfigHNSW) + assert isinstance(config.vector_index_config.quantizer, _RQConfig) + assert config.vector_index_config.quantizer.bits == 4 + assert config.vector_index_config.quantizer.centering is True + assert config.vector_index_config.quantizer.rescore_limit == 50 + assert config.vector_index_config.quantizer.training_limit == 10000 + @pytest.mark.parametrize( "vector_index_config",