xref: /aosp_15_r20/external/icing/icing/index/embed/quantizer.h (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
1 // Copyright (C) 2024 Google LLC
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 //      http://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 #ifndef ICING_INDEX_EMBED_QUANTIZER_H_
16 #define ICING_INDEX_EMBED_QUANTIZER_H_
17 
18 #include <algorithm>
19 #include <cmath>
20 #include <cstdint>
21 #include <cstring>
22 #include <limits>
23 
24 #include "icing/text_classifier/lib3/utils/base/statusor.h"
25 #include "icing/absl_ports/canonical_errors.h"
26 
27 namespace icing {
28 namespace lib {
29 
30 // A class for quantizing and dequantizing floating-point values to and from
31 // 8-bit unsigned integers. The maximum quantization error is
32 // (float_max - float_min) / 255 / 2.
33 class Quantizer {
34  public:
35   // Creates a new Quantizer instance based on the specified range. Values
36   // outside this range will be quantized to the closest boundary.
37   //
38   // Returns:
39   //   - An Quantizer instance on success.
40   //   - INVALID_ARGUMENT_ERROR if float_min is greater than or equal to
41   //     float_max.
Create(float float_min,float float_max)42   static libtextclassifier3::StatusOr<Quantizer> Create(float float_min,
43                                                         float float_max) {
44     if (float_min > float_max) {
45       return absl_ports::InvalidArgumentError(
46           "float_min must be less than or equal to float_max.");
47     }
48     float scale_factor = 0.0;
49     if (float_max - float_min > kEpsilon) {  // Not equal.
50       scale_factor =
51           static_cast<float>(kMaxQuantizedValue) / (float_max - float_min);
52     }
53     return Quantizer(float_min, scale_factor);
54   }
55 
56   // Creates a new Quantizer instance from the serialized data.
Quantizer(const char * data)57   explicit Quantizer(const char* data) {
58     memcpy(this, data, sizeof(Quantizer));
59   }
60 
Quantize(float value)61   uint8_t Quantize(float value) const {
62     double normalized =
63         (static_cast<double>(value) - float_min_) * scale_factor_;
64     double quantized = std::round(normalized);
65     quantized =
66         std::clamp(quantized, 0.0, static_cast<double>(kMaxQuantizedValue));
67     return static_cast<uint8_t>(quantized);
68   }
69 
Dequantize(uint8_t quantized)70   float Dequantize(uint8_t quantized) const {
71     if (scale_factor_ == 0.0) {
72       return float_min_;
73     }
74     return (quantized / scale_factor_) + float_min_;
75   }
76 
77  private:
78   static constexpr uint8_t kMaxQuantizedValue =
79       std::numeric_limits<uint8_t>::max();
80   static constexpr float kEpsilon = 1e-6;
81 
Quantizer(float float_min,float scale_factor)82   explicit Quantizer(float float_min, float scale_factor)
83       : float_min_(float_min), scale_factor_(scale_factor) {}
84   float float_min_;
85   float scale_factor_;
86 };
87 
88 }  // namespace lib
89 }  // namespace icing
90 
91 #endif  // ICING_INDEX_EMBED_QUANTIZER_H_
92