[MOD-14952] Support normalization in QuantPreprocessor#1000
Conversation
0bf91f7 to
f438a59
Compare
f438a59 to
d37077d
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1000 +/- ##
==========================================
- Coverage 97.13% 97.12% -0.02%
==========================================
Files 141 141
Lines 8268 8304 +36
==========================================
+ Hits 8031 8065 +34
- Misses 237 239 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
* Support norm in QuantPreprocessor * Remove unnecessary allocation and duplicated code (cherry picked from commit 027d551)
d37077d to
7eaa73f
Compare
| float x_mean_ip = 0.0f; // only used for WithNorm | ||
|
|
||
| // Get transformed values (convert to float, mean-subtracted for WithNorm) | ||
| vecsim_stl::vector<float> values(this->dim, this->allocator); |
There was a problem hiding this comment.
This introduces a dim-sized heap allocation for every FP16 storage preprocessing call, including the existing WithNorm=false path. It also turns the previous two-pass flow into three memory passes: build the FP32 temporary, find min/max, then quantize.
The temporary is not required. Quantization already needs two passes because min/max must be known before calculating the scale. In the first pass, compute min/max directly from to_fp32(input[i]) - mean[i] and accumulate x·mean. In the second pass, recompute the same transformed value while quantizing and accumulating sum/ squares. A templated/inlined helper or lambda with if constexpr (WithNorm) would remain compile-time-specialized—no function pointer, vtable, or other runtime indirection.
This follows the allocation-free approach used by the previous FP16 implementation and by assign_query_metadata() below. Please preserve that behavior, particularly for WithNorm=false.
Describe the changes in the pull request
PR #999 has merged. This PR targets
mainand builds on its stored-to-query distance dispatch.Cherry-picks ARM's
027d5510b2bf7add6a45be9b0c07cda38c15b5cafrom ARM-software/VectorSimilarity-for-Arm#2.This PR adds an optional normalization-aware mode to
QuantPreprocessor. It stores the mean-vector inner product as additional SQ8 metadata for normalized storage and queries, extends SQ8 metadata sizing, and shares a float-based quantization implementation to avoid duplicated logic.Existing
QuantPreprocessorconfigurations retain their current behavior. FP32 and FP16 tests cover normalization for L2 and inner-product metrics.It also follows up on review feedback from #999 with an HNSW integration test that installs distinct stored-to-stored and stored-to-query kernels. Top-K, range, and batch-iterator searches must all return the query-kernel score, proving HNSW selects the cached
StoredToQuerydispatch.Which issues this PR fixes
Main objects this PR modified
QuantPreprocessorvecsim_types::sq8Mark if applicable
Validation
make formatmake check-formattest_componentsandtest_hnswtest_components: 32/32 passedtest_hnswexcluding the two external serialization fixtures: 169/169 passedgit diff --check main...HEADNote
Medium Risk
Introduces a new preprocessor constructor and larger SQ8 metadata when normalization is on; default
WithNorm=falselimits blast radius, but wrong mean or layout mismatches would break distance reconstruction.Overview
Adds an optional normalization-aware mode to
QuantPreprocessorvia aWithNormtemplate flag (default off, so existing indexes keep the same blob layout and behavior).When
WithNormis enabled, storage quantization uses mean-centered values (x - mean[i]) for min/max, scaling, and sums, while query vectors stay in the originalDataType. An extra FP32 metadata field records Σ(x·mean) on storage (x_mean_ip) and Σ(y·mean) on queries (y_mean_ip). A new constructor takes a precomputed FP32 mean vector; Cosine + WithNorm is rejected at compile time.vecsim_types::sq8grows metadata sizing helpers and index accessors for the extra slot.Unit coverage exercises L2 and inner product for float32 and float16 with norm. An HNSW test wires distinct stored-to-stored vs stored-to-query distance kernels and checks top-K, range, and batch search use the query path score.
Reviewed by Cursor Bugbot for commit d4e8f1c. Bugbot is set up for automated code reviews on this repo. Configure here.