blob: ef35d0c0676ec8ed6e0f0c91534301ef5fe54de3 [file] [log] [blame]
/* Copyright 2023 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 <type_traits>
#include "tensorflow/lite/c/builtin_op_data.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/micro/kernels/kernel_runner.h"
#include "tensorflow/lite/micro/test_helpers.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace tflite {
namespace testing {
namespace {
void ExecuteDivTest(TfLiteTensor* tensors, int tensors_count,
TfLiteFusedActivation activation) {
TfLiteDivParams builtin_data = {};
builtin_data.activation = activation;
int kInputArrayData[] = {2, 0, 1};
TfLiteIntArray* inputs_array = IntArrayFromInts(kInputArrayData);
int kOutputArrayData[] = {1, 2};
TfLiteIntArray* outputs_array = IntArrayFromInts(kOutputArrayData);
const TFLMRegistration registration = tflite::Register_DIV();
micro::KernelRunner runner(registration, tensors, tensors_count, inputs_array,
outputs_array, static_cast<void*>(&builtin_data));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.InitAndPrepare());
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.Invoke());
}
template <typename T>
void TestDiv(int* input1_dims_data, const T* input1_data, int* input2_dims_data,
const T* input2_data, int* expected_dims, const T* expected_data,
T* output_data, TfLiteFusedActivation activation) {
TfLiteIntArray* input1_dims = IntArrayFromInts(input1_dims_data);
TfLiteIntArray* input2_dims = IntArrayFromInts(input2_dims_data);
TfLiteIntArray* output_dims = IntArrayFromInts(expected_dims);
const int output_count = ElementCount(*output_dims);
constexpr int inputs_size = 2;
constexpr int outputs_size = 1;
constexpr int tensors_size = inputs_size + outputs_size;
TfLiteTensor tensors[tensors_size] = {
CreateTensor(input1_data, input1_dims),
CreateTensor(input2_data, input2_dims),
CreateTensor(output_data, output_dims),
};
constexpr int tensors_count = std::extent<decltype(tensors)>::value;
ExecuteDivTest(tensors, tensors_count, activation);
constexpr float kTolerance = 1e-5;
for (int i = 0; i < output_count; i++) {
TF_LITE_MICRO_EXPECT_NEAR(expected_data[i], output_data[i], kTolerance);
}
}
// For quantized Div, the error shouldn't exceed (2*step + step^2).
inline float GetTolerance(int min, int max) {
const float kQuantizedStep = (max - min) / 255.0f;
const float kQuantizedTolerance =
2.0f * kQuantizedStep + kQuantizedStep * kQuantizedStep;
return kQuantizedTolerance;
}
// min/max are used to compute scale, zero-point, compare tolerance
template <typename T>
struct TestQuantParams {
float data_min; // input and output data minimum value
float data_max; // input and output data maximum value
T* input1_data; // quantized input1 storage
T* input2_data; // quantized input2 storage
T* output_data; // quantized output storage
};
template <typename T>
void TestDivQuantized(int* input1_dims_data, const float* input1_data,
int* input2_dims_data, const float* input2_data,
int* expected_dims, const float* expected_data,
float* output_data, TfLiteFusedActivation activation,
const TestQuantParams<T>* params) {
TfLiteIntArray* input1_dims = IntArrayFromInts(input1_dims_data);
TfLiteIntArray* input2_dims = IntArrayFromInts(input2_dims_data);
TfLiteIntArray* output_dims = IntArrayFromInts(expected_dims);
const int output_count = ElementCount(*output_dims);
const float scale = ScaleFromMinMax<T>(params->data_min, params->data_max);
const int zero_point =
ZeroPointFromMinMax<T>(params->data_min, params->data_max);
TfLiteTensor tensors[] = {
CreateQuantizedTensor(input1_data, params->input1_data, input1_dims,
scale, zero_point),
CreateQuantizedTensor(input2_data, params->input2_data, input2_dims,
scale, zero_point),
CreateQuantizedTensor(params->output_data, output_dims, scale,
zero_point),
};
constexpr int kTensorsCount = std::extent<decltype(tensors)>::value;
ExecuteDivTest(tensors, kTensorsCount, activation);
Dequantize(params->output_data, output_count, scale, zero_point, output_data);
const float kTolerance = GetTolerance(params->data_min, params->data_max);
for (int i = 0; i < output_count; i++) {
TF_LITE_MICRO_EXPECT_NEAR(expected_data[i], output_data[i], kTolerance);
}
}
template <typename T>
void TestDivMultiShape(int** shapes, const int shapes_count,
const T* input1_data, const T* input2_data,
const T* expected_data, T* output_data,
TfLiteFusedActivation activation) {
for (int i = 0; i < shapes_count; i++) {
TestDiv(shapes[i], input1_data, shapes[i], input2_data, shapes[i],
expected_data, output_data, activation);
}
}
template <typename T>
void TestDivMultiShapeQuant(int** shapes, const int shapes_count,
const float* input1_data, const float* input2_data,
const float* expected_data, float* output_data,
TfLiteFusedActivation activation,
const TestQuantParams<T>* params) {
for (int i = 0; i < shapes_count; i++) {
TestDivQuantized(shapes[i], input1_data, shapes[i], input2_data, shapes[i],
expected_data, output_data, activation, params);
}
}
// when broadcasting input2 is a scaler
template <typename T>
void TestDivMultiBroadcast(int** shapes, const int shapes_count,
const T* input1_data, const T* input2_data,
const T* expected_data, T* output_data,
TfLiteFusedActivation activation) {
int kDimScaler[] = {1, 1};
for (int i = 0; i < shapes_count; i++) {
TestDiv(shapes[i], input1_data, kDimScaler, input2_data, shapes[i],
expected_data, output_data, activation);
}
}
// when broadcasting input2 is a scaler
template <typename T>
void TestDivMultiBroadcastQuant(int** shapes, const int shapes_count,
const float* input1_data,
const float* input2_data,
const float* expected_data, float* output_data,
TfLiteFusedActivation activation,
const TestQuantParams<T>* params) {
int kDimScaler[] = {1, 1};
for (int i = 0; i < shapes_count; i++) {
TestDivQuantized(shapes[i], input1_data, kDimScaler, input2_data, shapes[i],
expected_data, output_data, activation, params);
}
}
} // namespace
} // namespace testing
} // namespace tflite
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(FloatDivOpTestActNone) {
int kDims[] = {4, 1, 2, 2, 1};
constexpr float kInput1[] = {-0.2, 0.2, -1.2, 0.8};
constexpr float kInput2[] = {0.5, 0.2, -1.5, 0.5};
constexpr float kExpect[] = {-0.4, 1.0, 0.8, 1.6};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
tflite::testing::TestDiv(kDims, kInput1, kDims, kInput2, kDims, kExpect,
output_data, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(FloatDivOpTestActReluN1To1) {
int kDims[] = {4, 1, 2, 2, 1};
constexpr float kInput1[] = {-0.2, 0.2, -1.2, 0.8};
constexpr float kInput2[] = {0.1, 0.2, -1.5, 0.5};
constexpr float kExpect[] = {-1.0, 1.0, 0.8, 1.0};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
tflite::testing::TestDiv(kDims, kInput1, kDims, kInput2, kDims, kExpect,
output_data, kTfLiteActReluN1To1);
}
TF_LITE_MICRO_TEST(FloatDivOpTestMultiShape) {
int kShape1[] = {1, 6};
int kShape2[] = {2, 2, 3};
int kShape3[] = {3, 2, 1, 3};
int kShape4[] = {4, 1, 3, 1, 2};
int* kDims[] = {kShape1, kShape2, kShape3, kShape4};
constexpr int kDimsCount = std::extent<decltype(kDims)>::value;
constexpr float kInput1[] = {-2.0, 0.2, 0.3, 0.8, 1.1, -2.0};
constexpr float kInput2[] = {0.1, 0.2, 0.6, 0.5, -1.1, -0.1};
constexpr float kExpect[] = {-20.0, 1.0, 0.5, 1.6, -1.0, 20.0};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
tflite::testing::TestDivMultiShape(kDims, kDimsCount, kInput1, kInput2,
kExpect, output_data, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(FloatDivOpTestBroadcast) {
int kShape1[] = {1, 8};
int kShape2[] = {2, 2, 4};
int kShape3[] = {3, 2, 1, 4};
int kShape4[] = {4, 1, 2, 2, 2};
int* kDims[] = {kShape1, kShape2, kShape3, kShape4};
constexpr int kDimsCount = std::extent<decltype(kDims)>::value;
constexpr float kInput1[] = {-0.2, 0.2, 0.07, 0.08,
0.11, -0.123, -0.32, 0.54};
constexpr float kInput2[] = {0.1};
constexpr float kExpect[] = {-2.0, 2.0, 0.7, 0.8, 1.1, -1.23, -3.2, 5.4};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
tflite::testing::TestDivMultiBroadcast(kDims, kDimsCount, kInput1, kInput2,
kExpect, output_data, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(FloatDivOpTestBroadcast5D) {
int kShape1[] = {5, 1, 2, 1, 2, 2};
int* kDims[] = {kShape1};
constexpr int kDimsCount = std::extent<decltype(kDims)>::value;
constexpr float kInput1[] = {-0.2, 0.2, 0.07, 0.08,
0.11, -0.123, -0.32, 0.54};
constexpr float kInput2[] = {0.1};
constexpr float kExpect[] = {-2.0, 2.0, 0.7, 0.8, 1.1, -1.23, -3.2, 5.4};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
tflite::testing::TestDivMultiBroadcast(kDims, kDimsCount, kInput1, kInput2,
kExpect, output_data, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(QuantizedDivOpTestActNone) {
int kDims[] = {4, 1, 2, 2, 1};
constexpr float kInput1[] = {-0.8, -0.2, 0.3, 0.7};
constexpr float kInput2[] = {-0.8, 0.4, 0.8, 1.0};
constexpr float kExpect[] = {1.0, -0.5, 0.375, 0.7};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
// setup quantization storage and parameters
int8_t q_output_data[kOutputCount];
int8_t q_input1_data[kOutputCount];
int8_t q_input2_data[kOutputCount];
tflite::testing::TestQuantParams<int8_t> params = {};
params.data_min = -1.0;
params.data_max = 1.0;
params.input1_data = q_input1_data;
params.input2_data = q_input2_data;
params.output_data = q_output_data;
tflite::testing::TestDivQuantized(kDims, kInput1, kDims, kInput2, kDims,
kExpect, output_data, kTfLiteActNone,
&params);
}
TF_LITE_MICRO_TEST(QuantizedDivOpTestActReluN1To1) {
int kDims[] = {4, 1, 2, 2, 1};
constexpr float kInput1[] = {-0.8, 0.2, 0.9, 0.7};
constexpr float kInput2[] = {0.6, 0.4, 0.9, -0.8};
constexpr float kExpect1[] = {-1.0, 0.5, 1.0, -0.875};
constexpr int kOutputCount = std::extent<decltype(kExpect1)>::value;
float output_data[kOutputCount];
// setup quantization storage and parameters
int8_t q_output_data[kOutputCount];
int8_t q_input1_data[kOutputCount];
int8_t q_input2_data[kOutputCount];
tflite::testing::TestQuantParams<int8_t> params = {};
params.data_min = -1.0;
params.data_max = 1.0;
params.input1_data = q_input1_data;
params.input2_data = q_input2_data;
params.output_data = q_output_data;
tflite::testing::TestDivQuantized(kDims, kInput1, kDims, kInput2, kDims,
kExpect1, output_data, kTfLiteActReluN1To1,
&params);
constexpr float kInput3[] = {-0.5, 0.2, 0.6, 0.3};
constexpr float kInput4[] = {0.6, 0.5, -0.8, 0.5};
constexpr float kExpect2[] = {-0.833, 0.4, -0.75, 0.6};
tflite::testing::TestDivQuantized(kDims, kInput3, kDims, kInput4, kDims,
kExpect2, output_data, kTfLiteActReluN1To1,
&params);
}
TF_LITE_MICRO_TEST(QuantizedDivOpTestMultiShape) {
int kShape1[] = {1, 6};
int kShape2[] = {2, 2, 3};
int kShape3[] = {3, 2, 1, 3};
int kShape4[] = {4, 1, 3, 1, 2};
int* kDims[] = {kShape1, kShape2, kShape3, kShape4};
constexpr int kDimsCount = std::extent<decltype(kDims)>::value;
constexpr float kInput1[] = {-2.0, 0.2, 1.7, 0.9, 0.4, 2.0};
constexpr float kInput2[] = {1.3, 0.3, 1.1, 0.4, -1.1, 1.9};
constexpr float kExpect[] = {-1.538, 0.667, 1.545, 2.25, -0.364, 1.053};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
// setup quantization storage and parameters
int8_t q_output_data[kOutputCount];
int8_t q_input1_data[kOutputCount];
int8_t q_input2_data[kOutputCount];
tflite::testing::TestQuantParams<int8_t> params = {};
params.data_min = -3.0;
params.data_max = 3.0;
params.input1_data = q_input1_data;
params.input2_data = q_input2_data;
params.output_data = q_output_data;
tflite::testing::TestDivMultiShapeQuant(kDims, kDimsCount, kInput1, kInput2,
kExpect, output_data, kTfLiteActNone,
&params);
}
TF_LITE_MICRO_TEST(QuantizedDivOpTestBroadcast) {
int kShape1[] = {1, 8};
int kShape2[] = {2, 2, 4};
int kShape3[] = {3, 2, 1, 4};
int kShape4[] = {4, 1, 4, 1, 2};
int kShape5[] = {5, 1, 2, 1, 2, 2};
int* kDims[] = {kShape1, kShape2, kShape3, kShape4, kShape5};
constexpr int kDimsCount = std::extent<decltype(kDims)>::value;
constexpr float kInput1[] = {-2.0, 0.2, 0.7, 0.8, -0.5, 1.1, -1.3, 1.2};
constexpr float kInput2[] = {0.7};
constexpr float kExpect[] = {-2.857, 0.286, 1.0, 1.143,
-0.714, 1.571, -1.857, 1.714};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
float output_data[kOutputCount];
// setup quantization storage and parameters
int8_t q_output_data[kOutputCount];
int8_t q_input1_data[kOutputCount];
int8_t q_input2_data[kOutputCount];
tflite::testing::TestQuantParams<int8_t> params = {};
params.data_min = -3.0;
params.data_max = 3.0;
params.input1_data = q_input1_data;
params.input2_data = q_input2_data;
params.output_data = q_output_data;
tflite::testing::TestDivMultiBroadcastQuant(kDims, kDimsCount, kInput1,
kInput2, kExpect, output_data,
kTfLiteActNone, &params);
}
TF_LITE_MICRO_TEST(IntegerDivOpTestNoActivation) {
int kDims[] = {4, 1, 2, 2, 1};
constexpr int32_t kInput1[] = {-2, 2, -15, 8};
constexpr int32_t kInput2[] = {5, -2, -3, 5};
constexpr int32_t kExpect[] = {0, -1, 5, 1};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
int32_t output_data[kOutputCount];
tflite::testing::TestDiv(kDims, kInput1, kDims, kInput2, kDims, kExpect,
output_data, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(IntegerDivOpTestActivationRELU_N1_TO_1) {
int kDims[] = {4, 1, 2, 2, 1};
constexpr int32_t kInput1[] = {-2, 2, -12, 8};
constexpr int32_t kInput2[] = {1, 2, -15, 5};
constexpr int32_t kExpect[] = {-1, 1, 0, 1};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
int32_t output_data[kOutputCount];
tflite::testing::TestDiv(kDims, kInput1, kDims, kInput2, kDims, kExpect,
output_data, kTfLiteActReluN1To1);
}
TF_LITE_MICRO_TEST(IntegerDivOpTestVariousInputShapes) {
int kShape1[] = {1, 6};
int kShape2[] = {2, 2, 3};
int kShape3[] = {3, 2, 1, 3};
int kShape4[] = {4, 1, 3, 1, 2};
int* kDims[] = {kShape1, kShape2, kShape3, kShape4};
constexpr int kDimsCount = std::extent<decltype(kDims)>::value;
constexpr int32_t kInput1[] = {-20, 2, 3, 8, 11, -20};
constexpr int32_t kInput2[] = {1, 2, 6, 5, -11, -1};
constexpr int32_t kExpect[] = {-20, 1, 0, 1, -1, 20};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
int32_t output_data[kOutputCount];
tflite::testing::TestDivMultiShape(kDims, kDimsCount, kInput1, kInput2,
kExpect, output_data, kTfLiteActNone);
}
TF_LITE_MICRO_TEST(IntegerDivOpTestWithBroadcast) {
int kShape1[] = {1, 8};
int kShape2[] = {2, 2, 4};
int kShape3[] = {3, 2, 1, 4};
int kShape4[] = {4, 1, 4, 1, 2};
int kShape5[] = {5, 1, 2, 1, 2, 2};
int* kDims[] = {kShape1, kShape2, kShape3, kShape4, kShape5};
constexpr int kDimsCount = std::extent<decltype(kDims)>::value;
constexpr int32_t kInput1[] = {-20, 21, 7, 8, 11, -123, -42, -48};
constexpr int32_t kInput2[] = {3};
constexpr int32_t kExpect[] = {-6, 7, 2, 2, 3, -41, -14, -16};
constexpr int kOutputCount = std::extent<decltype(kExpect)>::value;
int32_t output_data[kOutputCount];
tflite::testing::TestDivMultiBroadcast(kDims, kDimsCount, kInput1, kInput2,
kExpect, output_data, kTfLiteActNone);
}
TF_LITE_MICRO_TESTS_END