1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 #include <cstdint>
16 #include <initializer_list>
17 #include <limits>
18 #include <vector>
19
20 #include <gtest/gtest.h>
21 #include "flatbuffers/flatbuffers.h" // from @flatbuffers
22 #include "tensorflow/lite/kernels/internal/types.h"
23 #include "tensorflow/lite/kernels/test_util.h"
24 #include "tensorflow/lite/schema/schema_generated.h"
25
26 namespace tflite {
27 namespace {
28
29 using ::testing::ElementsAreArray;
30
31 class QuantizeOpModel : public SingleOpModel {
32 public:
QuantizeOpModel()33 explicit QuantizeOpModel() {}
34
QuantizeOpModel(const TensorData & input,const TensorData & output)35 QuantizeOpModel(const TensorData& input, const TensorData& output) {
36 input_ = AddInput(input);
37 output_ = AddOutput(output);
38 SetBuiltinOp(BuiltinOperator_QUANTIZE, BuiltinOptions_QuantizeOptions,
39 CreateQuantizeOptions(builder_).Union());
40
41 BuildInterpreter({GetShape(input_)});
42 }
43
SetInput(std::initializer_list<float> data)44 void SetInput(std::initializer_list<float> data) {
45 PopulateTensor(input_, data);
46 }
47
48 template <typename T>
SetInputAndQuantize(std::initializer_list<float> data)49 void SetInputAndQuantize(std::initializer_list<float> data) {
50 QuantizeAndPopulate<T>(input_, data);
51 }
52
53 template <typename T>
GetOutput()54 std::vector<T> GetOutput() {
55 return ExtractVector<T>(output_);
56 }
57
58 protected:
59 int input_;
60 int output_;
61 };
62
63 class QuantizePerChannelOpModel : public QuantizeOpModel {
64 public:
QuantizePerChannelOpModel(TensorType inputType,TensorType outputType,std::initializer_list<int> shape,std::initializer_list<float> scales,std::initializer_list<int64_t> zero_points,int channel_dim)65 QuantizePerChannelOpModel(TensorType inputType, TensorType outputType,
66 std::initializer_list<int> shape,
67 std::initializer_list<float> scales,
68 std::initializer_list<int64_t> zero_points,
69 int channel_dim) {
70 std::vector<float> per_channel_scales(scales);
71 std::vector<int64_t> per_channel_quantization_offsets(zero_points);
72 const TensorData output_tensor_data = {outputType,
73 shape,
74 0 /*=min*/,
75 0 /*=max*/,
76 0.0f /*=scale*/,
77 0 /*=zero_point*/,
78 true /*=per_channel_quantization*/,
79 per_channel_scales,
80 per_channel_quantization_offsets,
81 channel_dim};
82 input_ = AddInput({inputType, shape});
83 output_ = AddOutput(output_tensor_data);
84 SetBuiltinOp(BuiltinOperator_QUANTIZE, BuiltinOptions_QuantizeOptions,
85 CreateQuantizeOptions(builder_).Union());
86
87 BuildInterpreter({GetShape(input_)});
88 }
89 };
90
91 // Per-node quantization tests.
92
TEST(QuantizeOpTest,UINT8)93 TEST(QuantizeOpTest, UINT8) {
94 // [-63.5, 64] -> scale=0.5 zero_point=127 for UINT8
95 QuantizeOpModel m({TensorType_FLOAT32, {2, 5}},
96 {TensorType_UINT8, {2, 5}, 0, 0, 0.5, 127});
97
98 m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64});
99 ASSERT_EQ(m.Invoke(), kTfLiteOk);
100 EXPECT_THAT(m.GetOutput<uint8_t>(),
101 ElementsAreArray({0, 1, 2, 3, 4, 251, 252, 253, 254, 255}));
102 }
103
TEST(QuantizeOpTest,INT8)104 TEST(QuantizeOpTest, INT8) {
105 // [-63.5, 64] -> scale=0.5, zero_point=1 for INT8
106 QuantizeOpModel m({TensorType_FLOAT32, {2, 5}},
107 {TensorType_INT8, {2, 5}, 0, 0, 0.5, -1});
108
109 m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64});
110 ASSERT_EQ(m.Invoke(), kTfLiteOk);
111 EXPECT_THAT(m.GetOutput<int8_t>(),
112 ElementsAreArray(
113 {-128, -127, -126, -125, -124, 123, 124, 125, 126, 127}));
114 }
115
TEST(QuantizeOpTest,INT16)116 TEST(QuantizeOpTest, INT16) {
117 QuantizeOpModel m({TensorType_FLOAT32, {2, 5}},
118 {TensorType_INT16, {2, 5}, 0, 0, 0.005, 0});
119
120 m.SetInput({-63.5, -63, -3, -2, -1, 1, 2, 3, 63.5, 64});
121 ASSERT_EQ(m.Invoke(), kTfLiteOk);
122 EXPECT_THAT(m.GetOutput<int16_t>(),
123 ElementsAreArray({-12700, -12600, -600, -400, -200, 200, 400, 600,
124 12700, 12800}));
125 }
126
127 // Per-channel quantization tests.
128
TEST(QuantizePerChannelOpTest,UINT8)129 TEST(QuantizePerChannelOpTest, UINT8) {
130 // [-63.5, 64] -> scale=0.5 zero_point=127 for UINT8
131 QuantizePerChannelOpModel m(TensorType_FLOAT32, TensorType_UINT8, {2, 5},
132 {0.5, 0.5}, {127, 127}, 0);
133
134 m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64});
135 ASSERT_EQ(m.Invoke(), kTfLiteOk);
136 EXPECT_THAT(m.GetOutput<uint8_t>(),
137 ElementsAreArray({0, 1, 2, 3, 4, 251, 252, 253, 254, 255}));
138 }
139
TEST(QuantizePerChannelOpTest,INT8)140 TEST(QuantizePerChannelOpTest, INT8) {
141 // [-63.5, 64] -> scale=0.5, zero_point=1 for INT8
142 QuantizePerChannelOpModel m(TensorType_FLOAT32, TensorType_INT8, {2, 5},
143 {0.5, 0.5}, {-1, -1}, 0);
144
145 m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64});
146 ASSERT_EQ(m.Invoke(), kTfLiteOk);
147 EXPECT_THAT(m.GetOutput<int8_t>(),
148 ElementsAreArray(
149 {-128, -127, -126, -125, -124, 123, 124, 125, 126, 127}));
150 }
151
TEST(QuantizePerChannelOpTest,INT16)152 TEST(QuantizePerChannelOpTest, INT16) {
153 // [-63.5, 64] -> scale=0.005, zero_point=0 for INT16
154 QuantizePerChannelOpModel m(TensorType_FLOAT32, TensorType_INT16, {2, 5},
155 {0.005, 0.005}, {0, 0}, 0);
156
157 m.SetInput({-63.5, -63, -3, -2, -1, 1, 2, 3, 63.5, 64});
158 ASSERT_EQ(m.Invoke(), kTfLiteOk);
159 EXPECT_THAT(m.GetOutput<int16_t>(),
160 ElementsAreArray({-12700, -12600, -600, -400, -200, 200, 400, 600,
161 12700, 12800}));
162 }
163
164 // Requantization tests.
165 // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output
166 // zeropoint 0
TEST(QuantizeOpTest,Int32Int16)167 TEST(QuantizeOpTest, Int32Int16) {
168 QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 1.0, 0},
169 {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0});
170
171 m.SetInputAndQuantize<int32_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
172 ASSERT_EQ(m.Invoke(), kTfLiteOk);
173 EXPECT_THAT(m.GetOutput<int16_t>(),
174 ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20}));
175 }
176
177 // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output
178 // zeropoint 0
TEST(QuantizeOpTest,Int32Int16SameScale)179 TEST(QuantizeOpTest, Int32Int16SameScale) {
180 QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 0.5, 0},
181 {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0});
182 m.SetInputAndQuantize<int32_t>({0, 1, 2, 3, 4, 5, 6, 7, 8, 37767});
183 ASSERT_EQ(m.Invoke(), kTfLiteOk);
184 EXPECT_THAT(m.GetOutput<int16_t>(),
185 ElementsAreArray({0, 2, 4, 6, 8, 10, 12, 14, 16, 32767}));
186 }
187
188 // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output
189 // zeropoint -1
TEST(QuantizeOpTest,Int32Int8SameScale)190 TEST(QuantizeOpTest, Int32Int8SameScale) {
191 QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 0.5, 0},
192 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1});
193
194 // Input will quantized to {1,3,5,7,9,11,13,15,17,19}.
195 m.SetInputAndQuantize<int32_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
196 ASSERT_EQ(m.Invoke(), kTfLiteOk);
197 EXPECT_THAT(m.GetOutput<int8_t>(),
198 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
199 }
200
201 // Input scale 0.500000, output scale 1.000000, input zeropoint 0, output
202 // zeropoint -1
TEST(QuantizeOpTest,Int32Int8LargerScale)203 TEST(QuantizeOpTest, Int32Int8LargerScale) {
204 QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 0.5, 0},
205 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 1.0, -1});
206
207 // Input will quantized to {1,3,5,7,9,11,13,15,17,19}.
208 m.SetInputAndQuantize<int32_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
209 ASSERT_EQ(m.Invoke(), kTfLiteOk);
210 EXPECT_THAT(m.GetOutput<int8_t>(),
211 ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
212 }
213
214 // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output
215 // zeropoint -1
TEST(QuantizeOpTest,Int32Int8SmallerScale)216 TEST(QuantizeOpTest, Int32Int8SmallerScale) {
217 QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 1.0, 0},
218 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1});
219
220 // Input will quantized to {0,1,2,3,4,5,6,7,8,9}.
221 m.SetInputAndQuantize<int32_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
222 ASSERT_EQ(m.Invoke(), kTfLiteOk);
223 EXPECT_THAT(m.GetOutput<int8_t>(),
224 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
225 }
226
227 // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output
228 // zeropoint 0
TEST(QuantizeOpTest,Int16Int16)229 TEST(QuantizeOpTest, Int16Int16) {
230 QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 1.0, 0},
231 {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0});
232
233 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
234 ASSERT_EQ(m.Invoke(), kTfLiteOk);
235 EXPECT_THAT(m.GetOutput<int16_t>(),
236 ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20}));
237 }
238
239 // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output
240 // zeropoint 0
TEST(QuantizeOpTest,Int16Int16SameScale)241 TEST(QuantizeOpTest, Int16Int16SameScale) {
242 QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0},
243 {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0});
244 m.SetInputAndQuantize<int16_t>({0, 1, 2, 3, 4, 5, 6, 7, 8, 37767});
245 ASSERT_EQ(m.Invoke(), kTfLiteOk);
246 EXPECT_THAT(m.GetOutput<int16_t>(),
247 ElementsAreArray({0, 2, 4, 6, 8, 10, 12, 14, 16, 32767}));
248 }
249
250 // Input scale 0.500000, output scale 0.500000, input zeropoint -1, output
251 // zeropoint -1
TEST(QuantizeOpTest,Int8Int8SameScale)252 TEST(QuantizeOpTest, Int8Int8SameScale) {
253 QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -63.5, 64},
254 {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64});
255
256 // Input will quantized to {1,3,5,7,9,11,13,15,17,19}.
257 m.SetInputAndQuantize<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
258 ASSERT_EQ(m.Invoke(), kTfLiteOk);
259 EXPECT_THAT(m.GetOutput<int8_t>(),
260 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
261 }
262
263 // Input scale 0.500000, output scale 1.000000, input zeropoint -1, output
264 // zeropoint -1
TEST(QuantizeOpTest,Int8Int8LargerScale)265 TEST(QuantizeOpTest, Int8Int8LargerScale) {
266 QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -63.5, 64},
267 {TensorType_INT8, {1, 1, 2, 5}, -127, 128});
268
269 // Input will quantized to {1,3,5,7,9,11,13,15,17,19}.
270 m.SetInputAndQuantize<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
271 ASSERT_EQ(m.Invoke(), kTfLiteOk);
272 EXPECT_THAT(m.GetOutput<int8_t>(),
273 ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
274 }
275
276 // Input scale 1.000000, output scale 0.500000, input zeropoint -1, output
277 // zeropoint -1
TEST(QuantizeOpTest,Int8Int8SmallerScale)278 TEST(QuantizeOpTest, Int8Int8SmallerScale) {
279 QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -127, 128},
280 {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64});
281
282 // Input will quantized to {0,1,2,3,4,5,6,7,8,9}.
283 m.SetInputAndQuantize<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
284 ASSERT_EQ(m.Invoke(), kTfLiteOk);
285 EXPECT_THAT(m.GetOutput<int8_t>(),
286 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
287 }
288
289 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Int8Int8SmallerScaleNeonPath)290 TEST(QuantizeOpTest, Int8Int8SmallerScaleNeonPath) {
291 QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -127, 128},
292 {TensorType_INT8, {1, 1, 4, 5}, -63.5, 64});
293
294 // Input will quantized to {0,1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,0}.
295 m.SetInputAndQuantize<int8_t>(
296 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
297 ASSERT_EQ(m.Invoke(), kTfLiteOk);
298 EXPECT_THAT(m.GetOutput<int8_t>(),
299 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
300 19, 17, 15, 13, 11, 9, 7, 5, 3, 1}));
301 }
302
303 // Input scale 0.500000, output scale 0.500000, input zeropoint 127, output
304 // zeropoint 127
TEST(QuantizeOpTest,UInt8UInt8SameScale)305 TEST(QuantizeOpTest, UInt8UInt8SameScale) {
306 QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64},
307 {TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64});
308
309 // Input will quantized to {129,131,133,135,137,139,141,143,145,147}.
310 m.SetInputAndQuantize<uint8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
311 ASSERT_EQ(m.Invoke(), kTfLiteOk);
312 EXPECT_THAT(
313 m.GetOutput<uint8_t>(),
314 ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147}));
315 }
316
317 // Input scale 0.500000, output scale 1.000000, input zeropoint 127, output
318 // zeropoint 127
TEST(QuantizeOpTest,Uint8Uint8LargerScale)319 TEST(QuantizeOpTest, Uint8Uint8LargerScale) {
320 QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64},
321 {TensorType_UINT8, {1, 1, 2, 5}, -127, 128});
322
323 // Input will quantized to {129,131,133,135,137,139,141,143,145,147}.
324 m.SetInputAndQuantize<uint8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
325 ASSERT_EQ(m.Invoke(), kTfLiteOk);
326 EXPECT_THAT(
327 m.GetOutput<uint8_t>(),
328 ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137}));
329 }
330
331 // Input scale 1.000000, output scale 0.500000, input zeropoint 127, output
332 // zeropoint 127
TEST(QuantizeOpTest,Uint8Uint8SmallerScale)333 TEST(QuantizeOpTest, Uint8Uint8SmallerScale) {
334 QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -127, 128},
335 {TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64});
336
337 // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137}.
338 m.SetInputAndQuantize<uint8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
339 ASSERT_EQ(m.Invoke(), kTfLiteOk);
340 EXPECT_THAT(
341 m.GetOutput<uint8_t>(),
342 ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147}));
343 }
344
345 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Uint8Uint8SmallerScaleNeonPath)346 TEST(QuantizeOpTest, Uint8Uint8SmallerScaleNeonPath) {
347 QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, -127, 128},
348 {TensorType_UINT8, {1, 1, 4, 5}, -63.5, 64});
349
350 // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
351 // 137, 136, 135, 134, 133, 132, 131, 130, 129, 128}.
352 m.SetInputAndQuantize<uint8_t>(
353 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
354 ASSERT_EQ(m.Invoke(), kTfLiteOk);
355 EXPECT_THAT(
356 m.GetOutput<uint8_t>(),
357 ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147,
358 147, 145, 143, 141, 139, 137, 135, 133, 131, 129}));
359 }
360
361 // Input scale 1.000000, output scale 1.000000, input zeropoint -1, output
362 // zeropoint 127
TEST(QuantizeOpTest,Int8Uint8SameScale)363 TEST(QuantizeOpTest, Int8Uint8SameScale) {
364 QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -127, 128},
365 {TensorType_UINT8, {1, 1, 2, 5}, -127, 128});
366
367 // Input will quantized to {0,1,2,3,4,5,6,7,8,9}.
368 m.SetInputAndQuantize<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
369 ASSERT_EQ(m.Invoke(), kTfLiteOk);
370 EXPECT_THAT(
371 m.GetOutput<uint8_t>(),
372 ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137}));
373 }
374
375 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Int8UInt8SameScaleNeonPath)376 TEST(QuantizeOpTest, Int8UInt8SameScaleNeonPath) {
377 QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -127, 128},
378 {TensorType_UINT8, {1, 1, 4, 5}, -127, 128});
379
380 // Input will quantized to {0,1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,0}.
381 m.SetInputAndQuantize<int8_t>(
382 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
383 ASSERT_EQ(m.Invoke(), kTfLiteOk);
384 EXPECT_THAT(
385 m.GetOutput<uint8_t>(),
386 ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
387 137, 136, 135, 134, 133, 132, 131, 130, 129, 128}));
388 }
389
390 // Input scale 1.000000, output scale 0.500000, input zeropoint -1, output
391 // zeropoint 127
TEST(QuantizeOpTest,Int8Uint8SmallerScale)392 TEST(QuantizeOpTest, Int8Uint8SmallerScale) {
393 QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -127, 128},
394 {TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64});
395
396 // Input will quantized to {0,1,2,3,4,5,6,7,8,9}.
397 m.SetInputAndQuantize<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
398 ASSERT_EQ(m.Invoke(), kTfLiteOk);
399 EXPECT_THAT(
400 m.GetOutput<uint8_t>(),
401 ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147}));
402 }
403
404 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Int8Uint8SmallerScaleNeonPath)405 TEST(QuantizeOpTest, Int8Uint8SmallerScaleNeonPath) {
406 QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -127, 128},
407 {TensorType_UINT8, {1, 1, 4, 5}, -63.5, 64});
408
409 // Input will quantized to {0,1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,0}.
410 m.SetInputAndQuantize<int8_t>(
411 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
412 ASSERT_EQ(m.Invoke(), kTfLiteOk);
413 EXPECT_THAT(
414 m.GetOutput<uint8_t>(),
415 ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147,
416 147, 145, 143, 141, 139, 137, 135, 133, 131, 129}));
417 }
418
419 // Input scale 1.000000, output scale 2.000000, input zeropoint -1, output
420 // zeropoint 127
TEST(QuantizeOpTest,Int8Uint8LargerScale)421 TEST(QuantizeOpTest, Int8Uint8LargerScale) {
422 QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -127, 128},
423 {TensorType_UINT8, {1, 1, 2, 5}, -254, 256});
424
425 // Input will quantized to {0,1,2,3,4,5,6,7,8,9}.
426 m.SetInputAndQuantize<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
427 ASSERT_EQ(m.Invoke(), kTfLiteOk);
428 EXPECT_THAT(
429 m.GetOutput<uint8_t>(),
430 ElementsAreArray({128, 128, 129, 129, 130, 130, 131, 131, 132, 132}));
431 }
432
433 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Int8Uint8LargerScaleNeonPath)434 TEST(QuantizeOpTest, Int8Uint8LargerScaleNeonPath) {
435 QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -127, 128},
436 {TensorType_UINT8, {1, 1, 4, 5}, -254, 256});
437
438 // Input will quantized to {0,1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,0}.
439 m.SetInputAndQuantize<int8_t>(
440 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
441 ASSERT_EQ(m.Invoke(), kTfLiteOk);
442 EXPECT_THAT(
443 m.GetOutput<uint8_t>(),
444 ElementsAreArray({128, 128, 129, 129, 130, 130, 131, 131, 132, 132,
445 132, 132, 131, 131, 130, 130, 129, 129, 128, 128}));
446 }
447
448 // input scale 0.500000, output scale 0.500000, input zeropoint 127, output
449 // zeropoint -1
TEST(QuantizeOpTest,UInt8Int8SameScale128Diff)450 TEST(QuantizeOpTest, UInt8Int8SameScale128Diff) {
451 QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -127, 128},
452 {TensorType_INT8, {1, 1, 2, 5}, -127, 128});
453
454 // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137}.
455 m.SetInputAndQuantize<uint8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
456 ASSERT_EQ(m.Invoke(), kTfLiteOk);
457 EXPECT_THAT(m.GetOutput<int8_t>(),
458 ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
459 }
460
461 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,UInt8Int8SameScale128DiffNeonPath)462 TEST(QuantizeOpTest, UInt8Int8SameScale128DiffNeonPath) {
463 QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, -127, 128},
464 {TensorType_INT8, {1, 1, 4, 5}, -127, 128});
465
466 // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
467 // 137, 136, 135, 134, 133, 132, 131, 130, 129, 128}.
468 m.SetInputAndQuantize<uint8_t>(
469 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
470 ASSERT_EQ(m.Invoke(), kTfLiteOk);
471 EXPECT_THAT(m.GetOutput<int8_t>(),
472 ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
473 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}));
474 }
475
476 // input scale 0.500000, output scale 0.500000, input zeropoint 0, output
477 // zeropoint -1
TEST(QuantizeOpTest,Uint8Int8SameScaleArbitraryDiff)478 TEST(QuantizeOpTest, Uint8Int8SameScaleArbitraryDiff) {
479 QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, 0, 127.5},
480 {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64});
481
482 // Input will quantized to {2,4,6,8,10,12,14,16,18,20}.
483 m.SetInputAndQuantize<uint8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
484 ASSERT_EQ(m.Invoke(), kTfLiteOk);
485 EXPECT_THAT(m.GetOutput<int8_t>(),
486 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
487 }
488
489 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Uint8Int8SameScaleArbitraryDiffNeonPath)490 TEST(QuantizeOpTest, Uint8Int8SameScaleArbitraryDiffNeonPath) {
491 QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, 0, 127.5},
492 {TensorType_INT8, {1, 1, 4, 5}, -63.5, 64});
493
494 // Input will quantized to
495 // {2,4,6,8,10,12,14,16,18,20,20,18,16,14,12,10,8,6,4,2}.
496 m.SetInputAndQuantize<uint8_t>(
497 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
498 ASSERT_EQ(m.Invoke(), kTfLiteOk);
499 EXPECT_THAT(m.GetOutput<int8_t>(),
500 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
501 19, 17, 15, 13, 11, 9, 7, 5, 3, 1}));
502 }
503
504 // input scale 0.500000, output scale 1.000000, input zeropoint 0, output
505 // zeropoint -1
TEST(QuantizeOpTest,Uint8Int8LargerScale)506 TEST(QuantizeOpTest, Uint8Int8LargerScale) {
507 QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, 0, 127.5},
508 {TensorType_INT8, {1, 1, 2, 5}, -127, 128});
509
510 // Input will quantized to {2,4,6,8,10,12,14,16,18,20}.
511 m.SetInputAndQuantize<uint8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
512 ASSERT_EQ(m.Invoke(), kTfLiteOk);
513 EXPECT_THAT(m.GetOutput<int8_t>(),
514 ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
515 }
516
517 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Uint8Int8LargerScaleNeonPath)518 TEST(QuantizeOpTest, Uint8Int8LargerScaleNeonPath) {
519 QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, 0, 127.5},
520 {TensorType_INT8, {1, 1, 4, 5}, -127, 128});
521
522 // Input will quantized to
523 // {2,4,6,8,10,12,14,16,18,20,20,18,16,14,12,10,8,6,4,2}.
524 m.SetInputAndQuantize<uint8_t>(
525 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
526 ASSERT_EQ(m.Invoke(), kTfLiteOk);
527 EXPECT_THAT(m.GetOutput<int8_t>(),
528 ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
529 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}));
530 }
531
532 // input scale 1.000000, output scale 0.500000, input zeropoint 0, output
533 // zeropoint -1
TEST(QuantizeOpTest,Uint8Int8SmallerScale)534 TEST(QuantizeOpTest, Uint8Int8SmallerScale) {
535 QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, 0, 255},
536 {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64});
537
538 // Input will quantized to {1,2,3,4,5,6,7,8,9,10}.
539 m.SetInputAndQuantize<uint8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
540 ASSERT_EQ(m.Invoke(), kTfLiteOk);
541 EXPECT_THAT(m.GetOutput<int8_t>(),
542 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
543 }
544
545 // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output
546 // zeropoint -1
TEST(QuantizeOpTest,Int16Int8SameScale)547 TEST(QuantizeOpTest, Int16Int8SameScale) {
548 QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0},
549 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1});
550
551 // Input will quantized to {2,4,6,8,10,12,14,16,18,20}.
552 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
553 ASSERT_EQ(m.Invoke(), kTfLiteOk);
554 EXPECT_THAT(m.GetOutput<int8_t>(),
555 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
556 }
557
558 // Input scale 0.500000, output scale 0.500000, input zeropoint -1, output
559 // zeropoint -1.
TEST(QuantizeOpTest,Int16ZeroPointInt8)560 TEST(QuantizeOpTest, Int16ZeroPointInt8) {
561 QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, -1},
562 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1});
563
564 // Input will quantized to {2,4,6,8,10,12,14,16,18,20}.
565 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
566 ASSERT_EQ(m.Invoke(), kTfLiteOk);
567 EXPECT_THAT(m.GetOutput<int8_t>(),
568 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
569 }
570
571 // Input scale 0.500000, output scale 0.500000, input zeropoint -1, output
572 // zeropoint -1.
TEST(QuantizeOpTest,Int16ZeroPointInt8)573 TEST(QuantizeOpTest, Int16ZeroPointInt8) {
574 QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, -1},
575 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1});
576
577 // Input will quantized to {2,4,6,8,10,12,14,16,18,20}.
578 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
579 m.Invoke();
580 EXPECT_THAT(m.GetOutput<int8_t>(),
581 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
582 }
583
584 // Input scale 0.500000, output scale 1.000000, input zeropoint 0, output
585 // zeropoint -1
TEST(QuantizeOpTest,Int16Int8LargerScale)586 TEST(QuantizeOpTest, Int16Int8LargerScale) {
587 QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0},
588 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 1.0, -1});
589
590 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
591 ASSERT_EQ(m.Invoke(), kTfLiteOk);
592 EXPECT_THAT(m.GetOutput<int8_t>(),
593 ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
594 }
595
596 // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output
597 // zeropoint -1
TEST(QuantizeOpTest,Int16Int8SmallerScale)598 TEST(QuantizeOpTest, Int16Int8SmallerScale) {
599 QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 1.0, 0},
600 {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1});
601
602 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
603 ASSERT_EQ(m.Invoke(), kTfLiteOk);
604 EXPECT_THAT(m.GetOutput<int8_t>(),
605 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19}));
606 }
607
608 // Same as previous test, except more data to hit the neon path.
TEST(QuantizeOpTest,Int16Int8SmallerScaleNeonPath)609 TEST(QuantizeOpTest, Int16Int8SmallerScaleNeonPath) {
610 QuantizeOpModel m({TensorType_INT16, {1, 1, 4, 5}, 0, 0, 1.0, 0},
611 {TensorType_INT8, {1, 1, 4, 5}, 0, 0, 0.5, -1});
612
613 m.SetInputAndQuantize<int16_t>(
614 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1});
615 ASSERT_EQ(m.Invoke(), kTfLiteOk);
616 EXPECT_THAT(m.GetOutput<int8_t>(),
617 ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
618 19, 17, 15, 13, 11, 9, 7, 5, 3, 1}));
619 }
620
621 // Input scale 1.0, output scale 1.0, input zeropoint 0, output zeropoint 0
TEST(QuantizeOpTest,Int16Int32SameScale)622 TEST(QuantizeOpTest, Int16Int32SameScale) {
623 QuantizeOpModel m({TensorType_INT16,
624 {1, 1, 2, 5},
625 std::numeric_limits<int16_t>::min(),
626 std::numeric_limits<int16_t>::max()},
627 {TensorType_INT32,
628 {1, 1, 2, 5},
629 std::numeric_limits<int32_t>::min(),
630 static_cast<float>(std::numeric_limits<int32_t>::max())});
631
632 // Input will quantized to {1,3,5,7,9,11,13,15,17,19}.
633 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
634 ASSERT_EQ(m.Invoke(), kTfLiteOk);
635 EXPECT_THAT(m.GetOutput<int32_t>(),
636 ElementsAreArray({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
637 }
638
639 // Input scale 0.500000, output scale 1.000000, input zeropoint -1, output
640 // zeropoint 0
TEST(QuantizeOpTest,Int16Int32LargerScale)641 TEST(QuantizeOpTest, Int16Int32LargerScale) {
642 QuantizeOpModel m({TensorType_INT16,
643 {1, 1, 2, 5},
644 std::numeric_limits<int16_t>::min() / 2.0,
645 std::numeric_limits<int16_t>::max() / 2.0},
646 {TensorType_INT32,
647 {1, 1, 2, 5},
648 std::numeric_limits<int32_t>::min(),
649 static_cast<float>(std::numeric_limits<int32_t>::max())});
650
651 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
652 ASSERT_EQ(m.Invoke(), kTfLiteOk);
653 EXPECT_THAT(m.GetOutput<int32_t>(),
654 ElementsAreArray({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));
655 }
656
657 // Input scale 1.000000, output scale 0.500000, input zeropoint -1, output
658 // zeropoint 0
TEST(QuantizeOpTest,Int16Int32SmallerScale)659 TEST(QuantizeOpTest, Int16Int32SmallerScale) {
660 QuantizeOpModel m({TensorType_INT16,
661 {1, 1, 2, 5},
662 std::numeric_limits<int16_t>::min(),
663 std::numeric_limits<int16_t>::max()},
664 {TensorType_INT32,
665 {1, 1, 2, 5},
666 std::numeric_limits<int32_t>::min() / 2.0,
667 std::numeric_limits<int32_t>::max() / 2.0});
668
669 m.SetInputAndQuantize<int16_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
670 ASSERT_EQ(m.Invoke(), kTfLiteOk);
671 EXPECT_THAT(m.GetOutput<int32_t>(),
672 ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20}));
673 }
674
675 } // namespace
676 } // namespace tflite
677