LCOV - code coverage report
Current view: top level - src - sq8.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 14 14
Test Date: 2026-07-09 07:56:57 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2026 The Authors.
       2              : //
       3              : // Licensed under the Apache License, Version 2.0 (the "License");
       4              : // you may not use this file except in compliance with the License.
       5              : // You may obtain a copy of the License at
       6              : //
       7              : //     https://www.apache.org/licenses/LICENSE-2.0
       8              : //
       9              : // Unless required by applicable law or agreed to in writing, software
      10              : // distributed under the License is distributed on an "AS IS" BASIS,
      11              : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : // See the License for the specific language governing permissions and
      13              : // limitations under the License.
      14              : 
      15              : import 'dart:math';
      16              : import 'dart:typed_data';
      17              : 
      18              : /// Quantises a float32 embedding vector to unsigned 8-bit integers (SQ8).
      19              : ///
      20              : /// Uses a **fixed symmetric range** suitable for L2-normalised vectors whose
      21              : /// components lie in `[-1.0, 1.0]`:
      22              : ///
      23              : /// ```
      24              : /// u = clamp(round((f + 1.0) / 2.0 * 255), 0, 255)
      25              : /// ```
      26              : ///
      27              : /// This maps `-1.0` → `0`, `0.0` → `127` (or `128`), `1.0` → `255`.
      28              : ///
      29              : /// ## Assumptions
      30              : ///
      31              : /// - Input must be an L2-normalised vector (all components in `[-1.0, 1.0]`).
      32              : ///   Values marginally outside the range (e.g. due to float rounding) are
      33              : ///   clamped rather than panicked.
      34              : /// - No per-vector calibration is required; the fixed range provides adequate
      35              : ///   accuracy for cosine similarity ranking.
      36              : /// - [vector] may have any positive length (384 for BGE Small En v1.5, 1024
      37              : ///   for BGE-M3, etc.). A debug-mode assertion fires for empty vectors.
      38              : ///
      39              : /// The quantisation error is bounded by `2.0 / 255 ≈ 0.00784` per component
      40              : /// (one quantisation step). In practice, round-trip error is ≤ 0.004 per
      41              : /// element.
      42            1 : Uint8List quantise(Float32List vector) {
      43              :   assert(
      44            2 :     vector.isNotEmpty,
      45            2 :     'Embedding vector must be non-empty, got length ${vector.length}',
      46              :   );
      47            2 :   final out = Uint8List(vector.length);
      48            3 :   for (var i = 0; i < vector.length; i++) {
      49            1 :     final f = vector[i];
      50              :     // Map [-1, 1] → [0, 255] and clamp to guard against float rounding.
      51            4 :     final u = ((f + 1.0) / 2.0 * 255.0).roundToDouble();
      52            4 :     out[i] = min(255, max(0, u.toInt()));
      53              :   }
      54              :   return out;
      55              : }
      56              : 
      57              : /// Dequantises an SQ8-encoded vector back to float32.
      58              : ///
      59              : /// Inverse of [quantise]:
      60              : ///
      61              : /// ```
      62              : /// f = u / 255.0 * 2.0 - 1.0
      63              : /// ```
      64              : ///
      65              : /// The reconstructed values are in `[-1.0, 1.0]` but are no longer
      66              : /// L2-normalised (the quantisation error means the norm is slightly off 1.0).
      67              : /// For cosine similarity via dot product this is acceptable — the ranking
      68              : /// order is preserved.
      69              : ///
      70              : /// [vector] may have any positive length. A debug-mode assertion fires for
      71              : /// empty vectors.
      72            1 : Float32List dequantise(Uint8List vector) {
      73              :   assert(
      74            2 :     vector.isNotEmpty,
      75            2 :     'SQ8 vector must be non-empty, got length ${vector.length}',
      76              :   );
      77            2 :   final out = Float32List(vector.length);
      78            3 :   for (var i = 0; i < vector.length; i++) {
      79            5 :     out[i] = vector[i] / 255.0 * 2.0 - 1.0;
      80              :   }
      81              :   return out;
      82              : }
        

Generated by: LCOV version 2.0-1