/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ #include #include #include #include #include #include "flatbuffers/flatbuffers.h" // from @flatbuffers #include "tensorflow/lite/kernels/internal/portable_tensor_utils.h" #include "tensorflow/lite/kernels/internal/tensor_utils.h" #include "tensorflow/lite/kernels/internal/types.h" #include "tensorflow/lite/kernels/kernel_util.h" #include "tensorflow/lite/kernels/test_util.h" #include "tensorflow/lite/schema/schema_generated.h" namespace tflite { namespace { using ::testing::ElementsAreArray; class QuantizeOpModel : public SingleOpModel { public: explicit QuantizeOpModel() {} QuantizeOpModel(const TensorData& input, const TensorData& output) { input_ = AddInput(input); output_ = AddOutput(output); SetBuiltinOp(BuiltinOperator_QUANTIZE, BuiltinOptions_QuantizeOptions, CreateQuantizeOptions(builder_).Union()); BuildInterpreter({GetShape(input_)}); } void SetInput(std::initializer_list data) { PopulateTensor(input_, data); } template void SetInputAndQuantize(std::initializer_list data) { QuantizeAndPopulate(input_, data); } template std::vector GetOutput() { return ExtractVector(output_); } std::vector GetOutputUnpackedInt4() { TfLiteTensor* t = interpreter_->tensor(output_); int num_elements = NumElements(t); std::vector unpacked_output(num_elements); tensor_utils::UnpackPackedIntToInt8(t->data.int8, num_elements, /*bit_width=*/4, unpacked_output.data()); return unpacked_output; } std::vector GetOutputUnpackedUInt4() { TfLiteTensor* t = interpreter_->tensor(output_); int num_elements = NumElements(t); std::vector unpacked_output(num_elements); const int8_t* src_buffer = t->data.int8; for (int i = 0; i < num_elements; i += 2) { int8_t src_val = src_buffer[i / 2]; unpacked_output[i] = src_val & 0x0F; if (i + 1 < num_elements) { unpacked_output[i + 1] = (src_val >> 4) & 0x0F; } } return unpacked_output; } protected: int input_; int output_; }; class QuantizePerChannelOpModel : public QuantizeOpModel { public: QuantizePerChannelOpModel(TensorType inputType, TensorType outputType, std::initializer_list shape, std::initializer_list scales, std::initializer_list zero_points, int channel_dim) { std::vector per_channel_scales(scales); std::vector per_channel_quantization_offsets(zero_points); const TensorData output_tensor_data = {outputType, shape, 0 /*=min*/, 0 /*=max*/, 0.0f /*=scale*/, 0 /*=zero_point*/, true /*=per_channel_quantization*/, per_channel_scales, per_channel_quantization_offsets, channel_dim}; input_ = AddInput({inputType, shape}); output_ = AddOutput(output_tensor_data); SetBuiltinOp(BuiltinOperator_QUANTIZE, BuiltinOptions_QuantizeOptions, CreateQuantizeOptions(builder_).Union()); BuildInterpreter({GetShape(input_)}); } }; // Per-node quantization tests. TEST(QuantizeOpTest, UINT8) { // [-63.5, 64] -> scale=0.5 zero_point=127 for UINT8 QuantizeOpModel m({TensorType_FLOAT32, {2, 5}}, {TensorType_UINT8, {2, 5}, 0, 0, 0.5, 127}); m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 251, 252, 253, 254, 255})); } TEST(QuantizeOpTest, INT8) { // [-63.5, 64] -> scale=0.5, zero_point=1 for INT8 QuantizeOpModel m({TensorType_FLOAT32, {2, 5}}, {TensorType_INT8, {2, 5}, 0, 0, 0.5, -1}); m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray( {-128, -127, -126, -125, -124, 123, 124, 125, 126, 127})); } TEST(QuantizeOpTest, INT16) { QuantizeOpModel m({TensorType_FLOAT32, {2, 5}}, {TensorType_INT16, {2, 5}, 0, 0, 0.005, 0}); m.SetInput({-63.5, -63, -3, -2, -1, 1, 2, 3, 63.5, 64}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({-12700, -12600, -600, -400, -200, 200, 400, 600, 12700, 12800})); } TEST(QuantizeOpTest, INT4) { // [-3.5, 4] -> scale=0.5, zero_point=0 for INT4 QuantizeOpModel m({TensorType_FLOAT32, {2, 5}}, {TensorType_INT4, {2, 5}, 0, 0, 0.5, 0}); m.SetInput({-4.5, -3, -2.5, -2, -1.5, 2, 2.5, 3, 3.5, 4}); ASSERT_EQ(m.Invoke(), kTfLiteOk); // Range of int4 is [-8, 7]. Values over the range are clamped to the range. EXPECT_THAT(m.GetOutputUnpackedInt4(), ElementsAreArray({-8, -6, -5, -4, -3, 4, 5, 6, 7, 7})); } TEST(QuantizeOpTest, UINT4) { // [-4, 3.5] -> scale=0.5, zero_point=8 for UINT4 QuantizeOpModel m({TensorType_FLOAT32, {1, 8}}, {TensorType_UINT4, {1, 8}, 0, 0, 0.5, 8}); m.SetInput({-4.5, -4, -3.5, 0, 0.5, 3, 3.5, 4}); ASSERT_EQ(m.Invoke(), kTfLiteOk); // Range of uint4 is [0, 15]. Values over the range are clamped to the range. EXPECT_THAT(m.GetOutputUnpackedUInt4(), ElementsAreArray({0, 0, 1, 8, 9, 14, 15, 15})); } // Per-channel quantization tests. TEST(QuantizePerChannelOpTest, UINT8) { // [-63.5, 64] -> scale=0.5 zero_point=127 for UINT8 QuantizePerChannelOpModel m(TensorType_FLOAT32, TensorType_UINT8, {2, 5}, {0.5, 0.5}, {127, 127}, 0); m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 251, 252, 253, 254, 255})); } TEST(QuantizePerChannelOpTest, INT8) { // [-63.5, 64] -> scale=0.5, zero_point=1 for INT8 QuantizePerChannelOpModel m(TensorType_FLOAT32, TensorType_INT8, {2, 5}, {0.5, 0.5}, {-1, -1}, 0); m.SetInput({-63.5, -63, -62.5, -62, -61.5, 62, 62.5, 63, 63.5, 64}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray( {-128, -127, -126, -125, -124, 123, 124, 125, 126, 127})); } TEST(QuantizePerChannelOpTest, INT16) { // [-63.5, 64] -> scale=0.005, zero_point=0 for INT16 QuantizePerChannelOpModel m(TensorType_FLOAT32, TensorType_INT16, {2, 5}, {0.005, 0.005}, {0, 0}, 0); m.SetInput({-63.5, -63, -3, -2, -1, 1, 2, 3, 63.5, 64}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({-12700, -12600, -600, -400, -200, 200, 400, 600, 12700, 12800})); } // Requantization tests. // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output // zeropoint 0 TEST(QuantizeOpTest, Int32Int16) { QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 1.0, 0}, {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}); m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20})); } // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output // zeropoint 0 TEST(QuantizeOpTest, Int32Int16SameScale) { QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 0.5, 0}, {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}); m.SetInputAndQuantize({0, 1, 2, 3, 4, 5, 6, 7, 8, 37767}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 2, 4, 6, 8, 10, 12, 14, 16, 32767})); } // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Int32Int8SameScale) { QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 0.5, 0}, {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1}); // Input will quantized to {1,3,5,7,9,11,13,15,17,19}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Input scale 0.500000, output scale 1.000000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Int32Int8LargerScale) { QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 0.5, 0}, {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 1.0, -1}); // Input will quantized to {1,3,5,7,9,11,13,15,17,19}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Int32Int8SmallerScale) { QuantizeOpModel m({TensorType_INT32, {1, 1, 2, 5}, 0, 0, 1.0, 0}, {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1}); // Input will quantized to {0,1,2,3,4,5,6,7,8,9}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output // zeropoint 0 TEST(QuantizeOpTest, Int16Int16) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 1.0, 0}, {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}); m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20})); } // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output // zeropoint 0 TEST(QuantizeOpTest, Int16Int16SameScale) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}, {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}); m.SetInputAndQuantize({0, 1, 2, 3, 4, 5, 6, 7, 8, 37767}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 2, 4, 6, 8, 10, 12, 14, 16, 32767})); } // Input scale 0.500000, output scale 0.500000, input zeropoint -1, output // zeropoint -1 TEST(QuantizeOpTest, Int8Int8SameScale) { QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -63.5, 64}, {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64}); // Input will quantized to {1,3,5,7,9,11,13,15,17,19}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Input scale 0.500000, output scale 1.000000, input zeropoint -1, output // zeropoint -1 TEST(QuantizeOpTest, Int8Int8LargerScale) { QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -63.5, 64}, {TensorType_INT8, {1, 1, 2, 5}, -127, 128}); // Input will quantized to {1,3,5,7,9,11,13,15,17,19}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } // Input scale 1.000000, output scale 0.500000, input zeropoint -1, output // zeropoint -1 TEST(QuantizeOpTest, Int8Int8SmallerScale) { QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -127, 128}, {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64}); // Input will quantized to {0,1,2,3,4,5,6,7,8,9}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Int8Int8SmallerScaleNeonPath) { QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -127, 128}, {TensorType_INT8, {1, 1, 4, 5}, -63.5, 64}); // Input will quantized to {0,1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,0}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1})); } // Input scale 0.500000, output scale 0.500000, input zeropoint 127, output // zeropoint 127 TEST(QuantizeOpTest, UInt8UInt8SameScale) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64}, {TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64}); // Input will quantized to {129,131,133,135,137,139,141,143,145,147}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147})); } // Input scale 0.500000, output scale 1.000000, input zeropoint 127, output // zeropoint 127 TEST(QuantizeOpTest, Uint8Uint8LargerScale) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64}, {TensorType_UINT8, {1, 1, 2, 5}, -127, 128}); // Input will quantized to {129,131,133,135,137,139,141,143,145,147}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137})); } // Input scale 1.000000, output scale 0.500000, input zeropoint 127, output // zeropoint 127 TEST(QuantizeOpTest, Uint8Uint8SmallerScale) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -127, 128}, {TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64}); // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Uint8Uint8SmallerScaleNeonPath) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, -127, 128}, {TensorType_UINT8, {1, 1, 4, 5}, -63.5, 64}); // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, // 137, 136, 135, 134, 133, 132, 131, 130, 129, 128}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 147, 145, 143, 141, 139, 137, 135, 133, 131, 129})); } // Input scale 1.000000, output scale 1.000000, input zeropoint -1, output // zeropoint 127 TEST(QuantizeOpTest, Int8Uint8SameScale) { QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -127, 128}, {TensorType_UINT8, {1, 1, 2, 5}, -127, 128}); // Input will quantized to {0,1,2,3,4,5,6,7,8,9}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Int8UInt8SameScaleNeonPath) { QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -127, 128}, {TensorType_UINT8, {1, 1, 4, 5}, -127, 128}); // Input will quantized to {0,1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,0}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128})); } // Input scale 1.000000, output scale 0.500000, input zeropoint -1, output // zeropoint 127 TEST(QuantizeOpTest, Int8Uint8SmallerScale) { QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -127, 128}, {TensorType_UINT8, {1, 1, 2, 5}, -63.5, 64}); // Input will quantized to {0,1,2,3,4,5,6,7,8,9}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Int8Uint8SmallerScaleNeonPath) { QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -127, 128}, {TensorType_UINT8, {1, 1, 4, 5}, -63.5, 64}); // Input will quantized to {0,1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,0}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 147, 145, 143, 141, 139, 137, 135, 133, 131, 129})); } // Input scale 0.500000, output scale 1.000000, input zeropoint -1, output // zeropoint 127. TEST(QuantizeOpTest, Int8Uint8LargerScale) { QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, -63.5, 63}, {TensorType_UINT8, {1, 1, 2, 5}, -127, 128}); // Input will quantized to {2,4,6,8,10,12,14,16,18,20}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Int8Uint8LargerScaleNeonPath) { QuantizeOpModel m({TensorType_INT8, {1, 1, 4, 5}, -63.5, 63}, {TensorType_UINT8, {1, 1, 4, 5}, -127, 128}); // Input will quantized to // {2,4,6,8,10,12,14,16,18,20,20,18,16,14,12,10,8,6,4,2}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), ElementsAreArray({128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128})); } // input scale 0.500000, output scale 0.500000, input zeropoint 127, output // zeropoint -1 TEST(QuantizeOpTest, UInt8Int8SameScale128Diff) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, -127, 128}, {TensorType_INT8, {1, 1, 2, 5}, -127, 128}); // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, UInt8Int8SameScale128DiffNeonPath) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, -127, 128}, {TensorType_INT8, {1, 1, 4, 5}, -127, 128}); // Input will quantized to {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, // 137, 136, 135, 134, 133, 132, 131, 130, 129, 128}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0})); } // input scale 0.500000, output scale 0.500000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Uint8Int8SameScaleArbitraryDiff) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, 0, 127.5}, {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64}); // Input will quantized to {2,4,6,8,10,12,14,16,18,20}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Uint8Int8SameScaleArbitraryDiffNeonPath) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, 0, 127.5}, {TensorType_INT8, {1, 1, 4, 5}, -63.5, 64}); // Input will quantized to // {2,4,6,8,10,12,14,16,18,20,20,18,16,14,12,10,8,6,4,2}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1})); } // input scale 0.500000, output scale 1.000000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Uint8Int8LargerScale) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, 0, 127.5}, {TensorType_INT8, {1, 1, 2, 5}, -127, 128}); // Input will quantized to {2,4,6,8,10,12,14,16,18,20}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Uint8Int8LargerScaleNeonPath) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 4, 5}, 0, 127.5}, {TensorType_INT8, {1, 1, 4, 5}, -127, 128}); // Input will quantized to // {2,4,6,8,10,12,14,16,18,20,20,18,16,14,12,10,8,6,4,2}. m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0})); } // input scale 1.000000, output scale 0.500000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Uint8Int8SmallerScale) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, 0, 255}, {TensorType_INT8, {1, 1, 2, 5}, -63.5, 64}); // Input will quantized to {1,2,3,4,5,6,7,8,9,10}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Input scale 0.500000, output scale 0.500000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Int16Int8SameScale) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}, {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1}); // Input will quantized to {2,4,6,8,10,12,14,16,18,20}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Input scale 0.500000, output scale 0.500000, input zeropoint -1, output // zeropoint -1. TEST(QuantizeOpTest, Int16ZeroPointInt8) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, -1}, {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1}); // Input will quantized to {2,4,6,8,10,12,14,16,18,20}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Input scale 0.500000, output scale 1.000000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Int16Int8LargerScale) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}, {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 1.0, -1}); m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); } // Input scale 1.000000, output scale 0.500000, input zeropoint 0, output // zeropoint -1 TEST(QuantizeOpTest, Int16Int8SmallerScale) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, 0, 0, 1.0, 0}, {TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1}); m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19})); } // Same as previous test, except more data to hit the neon path. TEST(QuantizeOpTest, Int16Int8SmallerScaleNeonPath) { QuantizeOpModel m({TensorType_INT16, {1, 1, 4, 5}, 0, 0, 1.0, 0}, {TensorType_INT8, {1, 1, 4, 5}, 0, 0, 0.5, -1}); m.SetInputAndQuantize( {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1})); } // Input scale 1.0, output scale 1.0, input zeropoint 0, output zeropoint 0 TEST(QuantizeOpTest, Int16Int32SameScale) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, std::numeric_limits::min(), std::numeric_limits::max()}, {TensorType_INT32, {1, 1, 2, 5}, std::numeric_limits::min(), static_cast(std::numeric_limits::max())}); // Input will quantized to {1,3,5,7,9,11,13,15,17,19}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); } // Input scale 0.500000, output scale 1.000000, input zeropoint -1, output // zeropoint 0 TEST(QuantizeOpTest, Int16Int32LargerScale) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, std::numeric_limits::min() / 2.0, std::numeric_limits::max() / 2.0}, {TensorType_INT32, {1, 1, 2, 5}, std::numeric_limits::min(), static_cast(std::numeric_limits::max())}); m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); } // Input scale 1.000000, output scale 0.500000, input zeropoint -1, output // zeropoint 0 TEST(QuantizeOpTest, Int16Int32SmallerScale) { QuantizeOpModel m({TensorType_INT16, {1, 1, 2, 5}, std::numeric_limits::min(), std::numeric_limits::max()}, {TensorType_INT32, {1, 1, 2, 5}, std::numeric_limits::min() / 2.0, std::numeric_limits::max() / 2.0}); m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20})); } // Input scale 0.5, output scale 0.5, input zeropoint -1, output zeropoint 0 TEST(QuantizeOpTest, Int8Int16SameScale) { QuantizeOpModel m({TensorType_INT8, {1, 1, 2, 5}, 0, 0, 0.5, -1}, {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}); // Input will quantized to {1,3,5,7,9,11,13,15,17,19}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20})); } // Input scale 0.5, output scale 0.5, input zeropoint 127, output zeropoint 0 TEST(QuantizeOpTest, UInt8Int16SameScale) { QuantizeOpModel m({TensorType_UINT8, {1, 1, 2, 5}, 0, 0, 0.5, 127}, {TensorType_INT16, {1, 1, 2, 5}, 0, 0, 0.5, 0}); // Input will quantized to {129,131,133,135,137,139,141,143,145,147}. m.SetInputAndQuantize({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({2, 4, 6, 8, 10, 12, 14, 16, 18, 20})); } } // namespace } // namespace tflite