feat: support MRLE with split RaBitQ - #2560
Conversation
Enable TransformQuantizer-wrapped RaBitQ split storage in HGraph and Pyramid while keeping split encoding in the terminal RaBitQ quantizer. Signed-off-by: zhuangye.yxw <2510035537@qq.com> Assisted-by: Codex:gpt-5
Merge Protections🟢 All 3 merge protections satisfied — ready to merge. Show 3 satisfied protections🟢 Require kind label
🟢 Require version label
🟢 Require linked issue for feature/bug PRs
|
|
/label status/waiting-for-review |
|
Maintainers: please add the required |
LHT129
left a comment
There was a problem hiding this comment.
Review Summary
This PR adds MRLE transform quantizer support to RaBitQ split datacells, enabling the "mrle, rabitq" TQ chain for both HGraph and Pyramid index types. The implementation is well-structured and follows existing patterns in the codebase.
What Changed
- New
BottomQuantizerAccessortemplate: A zero-overhead abstraction that peels throughTransformQuantizer<RaBitQuantizer>to access the inner quantizer, computer, and transformed input vectors - RaBitQ split datacell refactoring: Uses
BottomQuantizerAccessorthroughout instead of direct quantizer access, making it work with both directRaBitQuantizerandTransformQuantizer<RaBitQuantizer> - Factory dispatch:
MakeRaBitQSplitDataCellnow acceptsis_transform_quantizerflag; when true, validates the TQ chain is exactly"mrle, rabitq"and instantiates the correct template - Shared parameter mapping:
MapRaBitQSplitParamandValidateMRLEDimextracted from HGraph-specific code to sharedinner_index_parameter.cpp, now used by both HGraph and Pyramid - Pyramid reorder_by_base support: New
reorder_by_base_mode where reorder usesbase_codes_directly (needed when base codes are RaBitQ split codes) - TransformQuantizer additions:
GetTransformedDim()andTransformBaseVector()methods for use by the accessor - Comprehensive tests: Unit tests for the accessor pattern, integration tests for Pyramid and HGraph MRLE+RaBitQ split flows
Key Observations
- The
BottomQuantizerAccessorpattern is clean and zero-cost at compile time - The
ExecuteChainTransformmethod has a pre-existing issue wherememcpyalways copiesthis->dim_floats after each transform step, which is incorrect after MRLE dimensionality reduction — this is not introduced by this PR but is now exposed throughTransformBaseVector - Test coverage is solid across unit and integration levels
- The factory validation correctly rejects unsupported TQ chains
Verdict
The PR is well-implemented with good abstraction, proper validation, and thorough testing. No blocking issues found.
Use named validation predicates for clang-tidy compatibility and copy each transform result using its actual output dimension. Signed-off-by: zhuangye.yxw <2510035537@qq.com> Assisted-by: Codex:gpt-5
|
Automated pull request review completed. Review effort: Submitted 4 inline comments. |
LHT129
left a comment
There was a problem hiding this comment.
Code Review Summary
I have completed a thorough review of all 30 changed files in this PR. The existing inline comments from the previous reviewer have already covered the major design points well. Here is my assessment:
Architecture & Design
BottomQuantizerAccessorpattern: Clean template specialization that correctly handles both directRaBitQuantizerandTransformQuantizer<RaBitQuantizer>cases. Zero-overhead at compile time.QuantizerTtemplate parameter onRaBitQSplitDataCell: Defaults toRaBitQuantizer<metric>, preserving backward compatibility for all existing direct split usage. The TQ path instantiates withTransformQuantizer<RaBitQuantizer<metric>, metric>.- Split of responsibilities:
Train/Encode/FactoryComputer/Serializeuse the outerquantizer_(which may be TQ-wrapped), while split/merge/lower-bound/full-distance operations usebottom_quantizer()via the accessor. This is the correct separation.
Correctness
TrainImplfix: Changing fromthis->dim_toGetTransformedDim()for the output buffer size is the key correctness fix. Without it, MRLE-reduced vectors would have stride mismatches after the first vector.ExecuteChainTransformfix: Usingvector_transformer->GetOutputDim()instead ofthis->dim_for the memcpy size after each transform step. This was a pre-existing bug that is now fixed.PyramidParameters::CheckCompatibility: Correctly skipsprecise_codes_paramcomparison whenreorder_source == "base", sinceprecise_codes_paramis null in that case.TransformQuantizerParameter::CheckCompatibility: Now compares both transformer parameters AND bottom quantizer parameters, which is more thorough than the previous type-name-only comparison.Pyramid::CalcDistanceById: Usesgraph_codes()which correctly returnsbase_codes_when reordering from base. Thecalculate_precise_distanceparameter becomes a no-op in that case, which is semantically correct since there is no separate precise store.
Validation
MapRaBitQSplitParam: Validates both direct (rabitq) and MRLE (tqwith"mrle, rabitq"chain) cases. Theis_mrlebranch correctly validates the exact chain.MakeRaBitQSplitDataCellForMetric: Factory-level validation that TQ split only accepts"mrle, rabitq". This is a defense-in-depth check.ValidateMRLEDim: Properly extracted as a shared function, validates[0, dim]range.
Test Coverage
- Unit tests: MRLE+split factory creation, batch training layout, optimized build with base transform, split query vs merged full-code consistency, invalid chain rejection, parameter mapping, TQ compatibility.
- Functional tests: HGraph MRLE+RaBitQ split build+KNN, Pyramid MRLE+RaBitQ split build+KNN.
- Direct split regression tests preserved.
Documentation
- Both English and Chinese docs updated for
quantization_transform.md,hgraph.md,index_parameters.md. - Implementation plan document (
issue_2533_mrle_split_rabitq_implementation_plan.md) provides clear context.
No Blocking Issues Found
All changes are consistent with the codebase style, the validation is thorough, and the test coverage is adequate. The PR is well-structured and ready to merge.
vsag-bot
left a comment
There was a problem hiding this comment.
Automated inline review completed.
Review effort: high (1012 changed lines across 30 files).
Submitted 3 inline comments.
Reviewed commit 36331b9.
Keep an internal FP32 data cell for decode-only Pyramid paths when MRLE uses split RaBitQ base reorder. Use it for flat-node promotion and analyzer sampling while retaining split codes for graph distances. Assisted-by: Codex:gpt-5 Signed-off-by: zhuangye.yxw <2510035537@qq.com>
vsag-bot
left a comment
There was a problem hiding this comment.
Automated inline review completed.
Review effort: high (1181 changed lines across 32 files).
Submitted 4 inline comments.
Reviewed commit dc08162.
| MapRaBitQSplitParam(external_param, inner_json); | ||
| ValidateMRLEDim(external_param, common_param.dim_); | ||
| const bool requires_raw_vector = | ||
| inner_json[BASE_CODES_KEY][CODES_TYPE_KEY].GetString() == RABITQ_SPLIT_CODES and |
There was a problem hiding this comment.
[P2] Retain vectors for every accepted Pyramid TQ configuration
The new mapping accepts base_quantization_type="tq" even when split bits are absent, but this predicate retains raw vectors only for rabitq_split. For example, tq_chain="mrle,fp32" with use_reorder=false reaches NSW FLAT-node promotion, where decodable_codes() returns the TQ base cell; its Decode always returns false and Build throws Pyramid graph promotion requires decodable vectors. Either restrict Pyramid's public TQ mapping to the supported split chain or retain a decodable source for every accepted TQ configuration.
| } | ||
| CHECK_FIELD_EQ(*this, *p, use_reorder); | ||
| if (this->use_reorder) { | ||
| if (this->use_reorder and this->reorder_source != HGRAPH_REORDER_SOURCE_BASE) { |
There was a problem hiding this comment.
[P2] Compare reorder sources during Pyramid compatibility checks
This branches only on the current parameter's reorder_source and never compares it with the serialized parameter. A streaming index built with precise reorder can therefore be accepted by an otherwise identical base-reorder instance; loading skips HIGH_PRECISION_CODES, then search and reorder use base codes even though the serialized graph was built with precise codes. Compare reorder_source before selecting which code parameters to validate.
| const auto base_type = external_json[HGRAPH_BASE_QUANTIZATION_TYPE].GetString(); | ||
| const auto precise_type = external_json[HGRAPH_PRECISE_QUANTIZATION_TYPE].GetString(); | ||
| const bool is_direct = base_type == QUANTIZATION_TYPE_VALUE_RABITQ; | ||
| const bool is_mrle = base_type == QUANTIZATION_TYPE_VALUE_TQ; |
There was a problem hiding this comment.
[P2] Keep a decodable source for HGraph MRLE split statistics
Enabling this TQ split branch sets reorder to base and eliminates the precise cell, but HGraph does not automatically retain raw vectors. With the default store_raw_vector=false, HGraphAnalyzer samples through GetVectorByInnerId; TransformQuantizer::Decode returns false and HGraph ignores that result, leaving zero-filled samples and producing meaningless GetStats() ground-truth and neighbor metrics. Retain a raw/decodable source for this configuration or make analysis operate without decoding.
| those indexes will fail at index construction. Configuring TQ on non-HGraph indexes | ||
| therefore requires code-side changes to add the external mapping. | ||
| `tq` is exposed as a public, externally configurable quantization type by **HGraph** and | ||
| **Pyramid**. Both map `tq_chain` and dimension parameters into |
There was a problem hiding this comment.
[P3] Document the new parameters on the Pyramid pages
This advertises Pyramid as supporting external TQ and MRLE split configuration, but the canonical English and Chinese Pyramid pages still omit tq from base_quantization_type and do not document tq_chain, mrle_dim, reorder_source, or the split-bit parameters. Users following those pages cannot construct the newly supported configuration or understand its raw-vector storage and recall tradeoffs; update both Pyramid pages and their index-parameter reference sections.
| const bool requires_raw_vector = | ||
| inner_json[BASE_CODES_KEY][CODES_TYPE_KEY].GetString() == RABITQ_SPLIT_CODES and | ||
| inner_json[BASE_CODES_KEY][QUANTIZATION_PARAMS_KEY][TYPE_KEY].GetString() == | ||
| QUANTIZATION_TYPE_VALUE_TQ; |
There was a problem hiding this comment.
[suggestion] The requires_raw_vector predicate is too narrow — it only triggers for RABITQ_SPLIT_CODES + QUANTIZATION_TYPE_VALUE_TQ. If a future quantizer also needs raw vectors (e.g., any quantizer wrapped in TransformQuantizer that cannot decode to fp32), this check will silently skip storing them. Consider using a more general predicate like is_transform_quantizer or checking quantizer->CanDecodeToFp32() if such an API exists.
| } | ||
| CHECK_FIELD_EQ(*this, *p, use_reorder); | ||
| if (this->use_reorder) { | ||
| if (this->use_reorder and this->reorder_source != HGRAPH_REORDER_SOURCE_BASE) { |
There was a problem hiding this comment.
[suggestion] CheckCompatibility does not compare reorder_source between two parameter sets. If one index was built with reorder_source="base" and another with reorder_source="precise", they would be considered compatible despite having fundamentally different data layouts (one stores raw vectors, the other does not). This could cause silent correctness issues during merge or reload. Add a CHECK_FIELD_EQ(*this, *p, reorder_source) here.
| auto inner_json = JsonType::Parse(str); | ||
| mapping_external_param_to_inner(hgraph_json, external_mapping, inner_json); | ||
| map_rabitq_split_param(hgraph_json, inner_json); | ||
| MapRaBitQSplitParam(hgraph_json, inner_json); |
There was a problem hiding this comment.
[suggestion] HGraph MRLE split does not set store_raw_vector (unlike Pyramid at pyramid.cpp:1067). This means HGraph with MRLE split cannot decode vectors to fp32 for statistics or debugging. If this is intentional (HGraph does not need raw vectors), consider adding a comment explaining why. Otherwise, mirror the Pyramid logic to set store_raw_vector when MRLE split is detected.
LHT129
left a comment
There was a problem hiding this comment.
Code Review Summary
Reviewed PR #2560: feat: support MRLE with split RaBitQ (commit dc08162).
What this PR does
Adds MRLE (Multi-Resolution Local Encoding) dimensionality reduction support to RaBitQ split quantization in both HGraph and Pyramid indexes. Key changes:
BottomQuantizerAccessor: Template abstraction to unwrapTransformQuantizer<RaBitQuantizer>for split datacellRaBitQSplitDataCell: Now templated onQuantizerT, works with both direct RaBitQuantizer and TransformQuantizer wrapper- Pyramid: New
reorder_by_base_mode,decodable_codes()/graph_codes()helpers, raw vector storage for MRLE split - Shared utilities:
MapRaBitQSplitParamandValidateMRLEDimextracted toInnerIndexParameter
Issues identified in this review
- [suggestion]
requires_raw_vectorpredicate in pyramid.cpp:1067 is too narrow — only triggers for specific type strings - [suggestion]
CheckCompatibilityin pyramid_zparameters.cpp:295 does not comparereorder_source - [suggestion] HGraph MRLE split does not set
store_raw_vector(unlike Pyramid) - [note] Documentation could be expanded for the new
reorder_source+ MRLE split interaction
Positive observations
- Clean extraction of shared
MapRaBitQSplitParam/ValidateMRLEDimtoInnerIndexParameter BottomQuantizerAccessoris a well-designed template abstractionExecuteChainTransformfix for dimensionality reduction is correct- Test coverage added for the new parameter paths
Signed-off-by: zhuangye.yxw <2510035537@qq.com> Assisted-by: Codex:gpt-5
| void | ||
| TransformQuantizer<QuantTmpl, metric>::TransformBaseVector(const float* input, | ||
| float* output) const { | ||
| Vector<float> data_buffer(this->dim_, 0, this->allocator_); |
There was a problem hiding this comment.
[suggestion] TransformBaseVector allocates a heap buffer on every call.
The method at transform_quantizer.h:278-283 creates a Vector<float> data_buffer(this->dim_, ...) on every invocation. In the optimized-build path (rabitq_split_datacell.h:1088-1090), this is called for every inserted vector, causing repeated heap allocations.
Consider one of:
- Accept a pre-allocated scratch buffer as a parameter (caller already has
transformed_input). - Make the scratch buffer a
static thread_localto reuse across calls within the same thread.
The caller in write_encoded_vector already has a Vector<float> transformed_input that could be sized to dim_ and passed in, avoiding the internal allocation entirely.
| transformed_data.data() + i * this->dim_, base_meta_offsets_.data(), tmp_codes.data()); | ||
| const uint64_t transformed_dim = this->GetTransformedDim(); | ||
| Vector<float> transformed_data(transformed_dim * count, 0, this->allocator_); | ||
| for (uint64_t i = 0; i < count; ++i) { |
There was a problem hiding this comment.
[suggestion] TrainImpl now calls TransformBaseVector in a per-element loop, each allocating a temporary buffer.
At transform_quantizer.h:248-251, the training loop calls TransformBaseVector for each of count vectors. Each call internally allocates a Vector<float> data_buffer(this->dim_, ...). For large training sets this adds unnecessary allocation overhead.
The previous code allocated one Vector<float> transformed_data(transformed_dim * count, ...) and mutated it in-place via ExecuteChainTransform. The new approach is cleaner but regresses on allocation count.
Consider extracting the per-element transform logic into a method that accepts a pre-allocated scratch buffer, or hoisting the scratch allocation outside the loop:
Vector<float> scratch(this->dim_, 0, this->allocator_);
for (uint64_t i = 0; i < count; ++i) {
scratch.assign(data + i * this->dim_, data + (i + 1) * this->dim_);
ExecuteChainTransform(scratch.data(), nullptr, nullptr);
memcpy(transformed_data.data() + i * transformed_dim, scratch.data(), transformed_dim * sizeof(float));
}| } | ||
| CHECK_FIELD_EQ(*this, *p, use_reorder); | ||
| if (this->use_reorder) { | ||
| if (this->use_reorder and this->reorder_source != HGRAPH_REORDER_SOURCE_BASE) { |
There was a problem hiding this comment.
[suggestion] CheckCompatibility does not compare reorder_source between two parameter sets.
At pyramid_zparameters.cpp:294-297, the compatibility check branches on this->reorder_source to decide whether to compare precise_codes_param, but it never compares this->reorder_source against p->reorder_source.
This means an index built with reorder_source="precise" (has precise_codes_ with a separate fp32 cell) would be considered compatible with one built with reorder_source="base" (no precise_codes_, reorders from base_codes_), despite fundamentally different data layouts.
Add a CHECK_FIELD_EQ(*this, *p, reorder_source); before the conditional branch at line 295.
| MapRaBitQSplitParam(external_param, inner_json); | ||
| ValidateMRLEDim(external_param, common_param.dim_); | ||
| const bool requires_raw_vector = | ||
| inner_json[BASE_CODES_KEY][CODES_TYPE_KEY].GetString() == RABITQ_SPLIT_CODES and |
There was a problem hiding this comment.
[suggestion] The requires_raw_vector predicate is too narrow — it only triggers for RABITQ_SPLIT_CODES + QUANTIZATION_TYPE_VALUE_TQ.
At pyramid.cpp:1065-1068, the condition:
const bool requires_raw_vector =
inner_json[BASE_CODES_KEY][CODES_TYPE_KEY].GetString() == RABITQ_SPLIT_CODES and
inner_json[BASE_CODES_KEY][QUANTIZATION_PARAMS_KEY][TYPE_KEY].GetString() ==
QUANTIZATION_TYPE_VALUE_TQ;only retains raw vectors for the MRLE+RaBitQ split combination. If a future TQ chain (e.g., "pca, rabitq" with split storage) also produces non-decodable codes, this check will silently skip storing raw vectors, causing GetVectorByInnerId and FLAT-to-GRAPH promotion to fail at runtime.
Consider inverting the logic: check whether the base quantizer supports decoding (e.g., base_codes_->GetQuantizer()->SupportDecode() or similar), and retain raw vectors whenever it does not. This makes the predicate future-proof against any non-decodable quantizer configuration.
| @@ -628,16 +559,7 @@ HGraph::CheckAndMappingExternalParam(const JsonType& external_param, | |||
| inner_json[RAW_VECTOR_KEY][CODES_TYPE_KEY].SetString(SPARSE_CODES); | |||
There was a problem hiding this comment.
[suggestion] HGraph MRLE split does not set store_raw_vector, unlike Pyramid.
At hgraph_param_mapping.cpp:559, CheckAndMappingExternalParam calls MapRaBitQSplitParam and ValidateMRLEDim but does not set store_raw_vector when MRLE+split is detected. Pyramid does this at pyramid.cpp:1065-1068.
HGraph with MRLE split cannot decode vectors to fp32 for statistics (HGraphAnalyzer) or GetVectorByInnerId. If this is intentional (HGraph does not need raw vectors because it never promotes FLAT nodes or decodes), consider adding a comment explaining why. Otherwise, add the same requires_raw_vector logic as Pyramid.
The HGraphAnalyzer path at hgraph_analyzer.cpp calls GetVectorByInnerId which will throw INTERNAL_ERROR if the base codes cannot decode.
LHT129
left a comment
There was a problem hiding this comment.
Overall Review
This PR adds MRLE + RaBitQ x+y split support for both HGraph and Pyramid indexes. The architecture is well-designed: BottomQuantizerAccessor cleanly abstracts the TransformQuantizer wrapper, shared parameter mapping is extracted to inner_index_parameter.cpp, and Pyramid gains graph_codes()/decodable_codes() accessors that correctly handle the new reorder_by_base_ and raw_vector_ paths.
What is done well
- Clean abstraction:
BottomQuantizerAccessorwith primary template + TQ specialization keeps the split datacell code generic. - Shared parameter mapping:
MapRaBitQSplitParamandValidateMRLEDimare extracted from HGraph-specific code into shared utilities used by both HGraph and Pyramid. - Correct training layout fix:
TrainImplnow usesGetTransformedDim()for buffer sizing, fixing a bug where reduced-dimension transforms would write past valid data. ExecuteChainTransformdim fix: Usesvector_transformer->GetOutputDim()for memcpy, fixing the same class of bug in the encode path.- Good test coverage: Unit tests for MRLE split in
flatten_datacell_test.cpp,pyramid_test.cpp,pyramid_zparameters_test.cpp, and integration tests intest_pyramid.cppandtest_hgraph_rabitq_split.cpp. TransformQuantizerParameter::CheckCompatibilitynow correctly delegates to bothVectorTransformerParameterand the bottom quantizer parameter, fixing a gap where transform parameters (e.g.,mrle_dim) were not compared.
Key concerns
TransformBaseVectorper-call heap allocation (transform_quantizer.h:280): Creates aVector<float>on every call, including in the optimized-build hot path. The caller already has a scratch buffer that could be reused.TrainImplper-element allocation (transform_quantizer.h:248): The training loop callsTransformBaseVectorfor each vector, each allocating internally. A single scratch buffer outside the loop would be more efficient.CheckCompatibilitymissingreorder_sourcecomparison (pyramid_zparameters.cpp:295): Two parameter sets with differentreorder_sourcevalues are considered compatible despite fundamentally different data layouts.requires_raw_vectorpredicate too narrow (pyramid.cpp:1065): Hard-coded toRABITQ_SPLIT_CODES+QUANTIZATION_TYPE_VALUE_TQ. A future non-decodable quantizer would silently skip raw vector storage.- HGraph missing
store_raw_vector(hgraph_param_mapping.cpp:559): Unlike Pyramid, HGraph does not retain raw vectors for MRLE split, which may causeHGraphAnalyzerto fail.
Verdict
The PR is well-structured and the core logic is correct. The concerns above are primarily about future-proofing and performance optimization — none are blocking correctness issues for the current MRLE+RaBitQ split use case. Recommend addressing items 3 and 5 before merge (correctness gaps), and items 1 and 2 as follow-up optimizations.
vsag-bot
left a comment
There was a problem hiding this comment.
Automated inline review completed.
Review effort: high (1014 changed lines across 31 files).
Submitted 4 inline comments.
Reviewed commit 9aa97c8.
| const auto base_type = external_json[HGRAPH_BASE_QUANTIZATION_TYPE].GetString(); | ||
| const auto precise_type = external_json[HGRAPH_PRECISE_QUANTIZATION_TYPE].GetString(); | ||
| const bool is_direct = base_type == QUANTIZATION_TYPE_VALUE_RABITQ; | ||
| const bool is_mrle = base_type == QUANTIZATION_TYPE_VALUE_TQ; |
There was a problem hiding this comment.
[P2] Retain raw vectors for HGraph MRLE split
This newly accepts MRLE split while forcing reorder to the non-decodable base TQ cell, but HGraph still defaults store_raw_vector to false. GetVectorByInnerId ignores the failed TQ Decode, so default MRLE-split indexes feed zero vectors into GetStats() and the non-forced UpdateVector checks, producing meaningless statistics and potentially incorrect update decisions. Enable a raw/decodable source automatically for this configuration.
| mapping_external_param_to_inner(external_param, external_mapping, inner_json); | ||
| MapRaBitQSplitParam(external_param, inner_json); | ||
| ValidateMRLEDim(external_param, common_param.dim_); | ||
| const bool requires_raw_vector = |
There was a problem hiding this comment.
[P2] Retain vectors for every accepted Pyramid TQ configuration
Pyramid now maps arbitrary tq_chain values, but raw vectors are retained only for split RaBitQ. For example, base_quantization_type="tq", tq_chain="mrle,fp32", use_reorder=false, and index_min_size=3 is accepted; when the third item promotes the FLAT node, decodable_codes() returns the TQ base cell, whose Decode always fails, and Add throws. Either restrict Pyramid's new public TQ mapping to the supported split chain or retain a decodable source for every accepted TQ layout that lacks decodable precise codes.
| } | ||
| CHECK_FIELD_EQ(*this, *p, use_reorder); | ||
| if (this->use_reorder) { | ||
| if (this->use_reorder and this->reorder_source != HGRAPH_REORDER_SOURCE_BASE) { |
There was a problem hiding this comment.
[P2] Compare reorder sources before accepting serialized Pyramid parameters
Compatibility branches only on this instance's reorder_source and never compares it with p->reorder_source. Otherwise identical use_reorder=true indexes using precise and base reorder are therefore accepted despite different serialized layouts; binary loading can interpret the stored precise-code block as hierarchy data, while streaming loading skips that block and searches a graph built with a different distance source. Compare reorder_source before selecting which code parameters to validate.
| those indexes will fail at index construction. Configuring TQ on non-HGraph indexes | ||
| therefore requires code-side changes to add the external mapping. | ||
| `tq` is exposed as a public, externally configurable quantization type by **HGraph** and | ||
| **Pyramid**. Both map `tq_chain` and dimension parameters into |
There was a problem hiding this comment.
[P3] Document the new Pyramid TQ parameters on Pyramid pages
This now advertises Pyramid as publicly supporting TQ and MRLE split, but the canonical English and Chinese Pyramid pages still omit tq from base_quantization_type and do not document tq_chain, mrle_dim, reorder_source, or the split-bit parameters. Users following the Pyramid documentation cannot construct the advertised configuration or understand its raw-vector storage and recall tradeoff; update both Pyramid pages and their index-parameter reference section.
Change Type
Linked Issue
What Changed
TransformQuantizer<RaBitQuantizer>through a generic bottom-quantizer accessormrle,rabitqsplit configuration in HGraph and Pyramid while keeping split encoding in the terminal RaBitQ quantizerTest Evidence
make fmtmake lintmake testmake cov, run tests, and collect coverageThe complete unit suite passed. The complete functional suite was started and manually stopped during a long-running Daily case after the affected-path functional tests had passed; no pre-interrupt failure was observed.
Compatibility Impact
tq_chain="mrle,rabitq"can now use RaBitQ x+y split in HGraph and Pyramid; other transformed split chains remain rejectedPerformance and Concurrency Impact
Documentation Impact
README.mdDEVELOPMENT.mdCONTRIBUTING.mdRisk and Rollback
Checklist