Skip to content

Quant preprocessor fp16 [MOD-15269] #944

Open
dor-forer wants to merge 9 commits intomainfrom
feat/MOD-15269-quant-preprocessor-fp16
Open

Quant preprocessor fp16 [MOD-15269] #944
dor-forer wants to merge 9 commits intomainfrom
feat/MOD-15269-quant-preprocessor-fp16

Conversation

@dor-forer
Copy link
Copy Markdown
Collaborator

@dor-forer dor-forer commented Apr 30, 2026

Describe the changes in the pull request

Adds float16 (FP16) input support to QuantPreprocessor, completing the producer side of the asymmetric SQ8 / FP16 hybrid layout consumed by the kernels added in #942.

  • Storage layout (unchanged): [uint8 × dim] [float × N].
  • Query layout for FP16 input: [float16 × dim] [float × M]. FP32 path is byte-for-byte unchanged.
  • All SQ8 metadata (min, delta, sum, sum_squares) is pinned to FP32 regardless of input type.
  • FP16 inputs are widened to FP32 before accumulation to limit precision loss.
  • Metadata I/O uses memcpy to handle the 2-byte-aligned offset that occurs when dim is odd and DataType is FP16.
  • QuantPreprocessor's DataType is now constrained by a C++20 QuantInput concept (float or vecsim_types::float16).

Which issues this PR fixes

  1. MOD-15269

Main objects this PR modified

  1. QuantPreprocessor and the to_fp32 helper in src/VecSim/spaces/computer/preprocessors.h.
  2. tests/unit/test_components.cpp — new QuantPreprocessorFP16MetricTest (L2 / IP / Cosine) and QuantPreprocessorFP16Test.QuantizeReconstructRoundTripL2 (odd-dim, in-place + out-of-place).
  3. tests/unit/unit_test_utils.hComputeSQ8Quantization baseline updated to use memcpy for metadata writes.

Mark if applicable

  • This PR introduces API changes
  • This PR introduces serialization changes

Note

Medium Risk
Changes the in-memory blob layout/size for QuantPreprocessor when instantiated with float16 and adjusts metadata reads/writes to handle potential misalignment, which could affect distance computations or interop with existing kernels if assumptions differ.

Overview
Adds vecsim_types::float16 support to QuantPreprocessor by constraining DataType via a C++20 QuantInput concept and widening FP16 values to FP32 during min/max and accumulation.

Pins all SQ8 metadata to FP32 (for both storage and query blobs) and switches metadata writes to memcpy to handle unaligned offsets (notably odd dim with FP16 query bodies). Updates float16 to compare using FP32 semantics, and adds unit tests covering FP16 storage/query blob sizes, metadata correctness vs an FP32 baseline, and FP16 quantize→reconstruct round-trips (including in-place quantization).

Reviewed by Cursor Bugbot for commit c95341f. Bugbot is set up for automated code reviews on this repo. Configure here.

Refactor QuantPreprocessor so DataType may be either float or
vecsim_types::float16. The vector body is stored/queried in DataType
width while all SQ8 metadata (min, delta, sum, sum_squares) remains
FP32 to match the asymmetric distance kernels added in MOD-15141.

- Replace std::is_floating_point_v<DataType> constraint with an
  is_quant_input trait that opts in float and float16 explicitly.
- Pin metadata to a single MetadataType alias (float) and use it for
  every value that is written into the metadata region (min/max/diff/
  delta, final sum/sum_squares, store_*_metadata signatures, and the
  return type of find_min_max). Per-element SIMD-lane accumulators
  remain plain float as a precision choice for FP16 inputs.
- Compute storage_bytes_count and query_bytes_count using
  sizeof(MetadataType) for the metadata region and sizeof(DataType)
  for the vector body.
- Write metadata via memcpy because the metadata offset is not
  guaranteed to be sizeof(MetadataType)-aligned when DataType is
  float16 (e.g. odd dim).
- Update the class-level documentation to reflect the layout and
  precision contract.

Tests:
- Add QuantPreprocessorFP16MetricTest (L2/IP/Cosine) covering blob
  size, layout, FP16 body fidelity, and FP32 metadata against an FP32
  baseline computed from the widened input.
- Add QuantPreprocessorFP16Test.QuantizeReconstructRoundTripL2 with
  an odd dimension (17) verifying |min + delta * q - x| <= delta.
- Switch the FP16 fixture's baseline metadata reads to the existing
  load_meta() memcpy helper, and rewrite ComputeSQ8Quantization to
  store metadata via memcpy as well.
The test comment claimed it 'covers the in-place quantization path'
but only called preprocessForStorage. Add an actual
preprocessStorageInPlace call: seed a buffer sized to
max(input_size, storage_size) with the FP16 input and verify the
resulting SQ8 blob byte-for-byte matches the one produced by
preprocessForStorage.
@jit-ci
Copy link
Copy Markdown

jit-ci Bot commented Apr 30, 2026

🛡️ Jit Security Scan Results

CRITICAL HIGH MEDIUM

✅ No security findings were detected in this PR


Security scan by Jit

Use a C++20 named concept on the QuantPreprocessor template head and
on the to_fp32 helper instead of an is_quant_input trait combined
with a static_assert. The constraint is now checked at the template's
instantiation point with a named diagnostic, and the helper trait
machinery is dropped.
@dor-forer dor-forer marked this pull request as ready for review April 30, 2026 12:19
@dor-forer dor-forer requested a review from Copilot April 30, 2026 12:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds FP16 (float16) input support to QuantPreprocessor so SQ8 storage blobs can be produced from FP16 inputs while keeping all SQ8 metadata in FP32 and safely handling potentially unaligned metadata regions.

Changes:

  • Constrain QuantPreprocessor input types to float / vecsim_types::float16, widen FP16 to FP32 for accumulation, and store all metadata as FP32.
  • Use memcpy for storage/query metadata writes to avoid UB with unaligned metadata offsets (notably for odd dims and FP16 query bodies).
  • Add unit tests validating FP16 storage/query layout correctness, metadata equivalence vs FP32-widened baseline, and round-trip reconstruction (including odd-dim + in-place path).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/VecSim/spaces/computer/preprocessors.h Implements FP16 input support, FP32 metadata, FP16→FP32 widening for accumulation, and memcpy-based metadata I/O for alignment safety.
tests/unit/test_components.cpp Adds FP16-focused QuantPreprocessor layout/metadata tests and an odd-dimension in-place reconstruction round-trip test.
tests/unit/unit_test_utils.h Updates SQ8 quantization test baseline to write metadata via memcpy to support unaligned metadata regions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.92%. Comparing base (5bcc53e) to head (c95341f).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #944      +/-   ##
==========================================
+ Coverage   96.71%   96.92%   +0.21%     
==========================================
  Files         129      130       +1     
  Lines        8057     7749     -308     
==========================================
- Hits         7792     7511     -281     
+ Misses        265      238      -27     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dor-forer dor-forer requested a review from BenGoldberger May 3, 2026 07:24
Comment thread src/VecSim/spaces/computer/preprocessors.h Outdated
Comment thread src/VecSim/spaces/computer/preprocessors.h Outdated
Comment thread src/VecSim/spaces/computer/preprocessors.h Outdated
dor-forer added 3 commits May 4, 2026 12:08
The implicit conversion to uint16_t made any < / > comparison between
two float16 values silently use raw bit-pattern ordering, which is
incorrect for IEEE 754: the sign bit is the MSB, so negatives compared
as larger than positives, and same-signed values compared correctly
only when non-negative.

Add operator<=> (returning std::partial_ordering for NaN handling) and
operator==, both delegating to FP32 semantics. The exact-match
overloads take precedence over the implicit uint16_t conversion path,
so all six comparisons now do the right thing.

Add unit tests covering the full operator set across negatives, +/-0,
and positives, plus a regression test for std::minmax_element on a
mix where the min and max are negative.
Two cleanups in QuantPreprocessor, both enabled by recent groundwork:

1. find_min_max no longer needs the float16-specific manual loop; with
   the new operator<=> on vecsim_types::float16, std::minmax_element
   works correctly for both DataTypes. The function collapses to a
   single line shared by both branches.

2. Inline store_storage_metadata and store_query_metadata into their
   only call sites. The original helpers always built both the L2 and
   IP/Cosine metadata buffers and then memcpy'd one; the inlined form
   constructs only the buffer that's actually written.
@dor-forer dor-forer requested a review from BenGoldberger May 4, 2026 09:18
@dor-forer dor-forer added this pull request to the merge queue May 4, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 4, 2026
@dor-forer dor-forer added this pull request to the merge queue May 4, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants