• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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