Files
tensorflow--tensorflow/tensorflow/lite/kernels/test_util_test.cc
T
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

242 lines
10 KiB
C++

/* Copyright 2018 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 "tensorflow/lite/kernels/test_util.h"
#include <stdint.h>
#include <cfloat>
#include <cmath>
#include <initializer_list>
#include <tuple>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/array.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/kernels/test_delegate_providers.h"
#include "tensorflow/lite/types/half.h"
#include "tensorflow/lite/util.h"
namespace tflite {
namespace {
using ::testing::ElementsAreArray;
TEST(TestUtilTest, ArrayFloatNearFp32) {
std::vector<float> expected = {0.1, 100.0, 0.0, -1};
// 9 * 10^-6 abs error should be tolerated by 1e-5 abs error.
std::vector<float> near = {0.100009, 99.999991, 0.000009, -1.000009};
// 2 * 10^-5 abs error should not be tolerated by 1e-5 abs error.
std::vector<float> not_near = {0.10002, 99.99998, 0.00002, -1.00002};
// Manually set the absoulte error and relative error to 1% and 1e-4.
std::vector<float> manual_error = {0.1009, 99.1, 0.00009, -1.009};
EXPECT_THAT(near, ElementsAreArray(ArrayFloatNear(expected)));
auto not_near_matchers = ArrayFloatNear(expected);
for (auto& matcher : not_near_matchers) {
matcher = Not(matcher);
}
EXPECT_THAT(not_near, ElementsAreArray(not_near_matchers));
EXPECT_THAT(manual_error, ElementsAreArray(ArrayFloatNear(
expected, /*max_abs_err=*/1e-4, kFpErrorAuto,
/*max_rel_err=*/0.01)));
}
TEST(TestUtilTest, ArrayFloatNearFp16) {
std::vector<float> expected = {0.1, 100.0, 0.0, -1};
// 0.003 abs error or <1% rel error should be tolerated by ArrayFloatNear.
std::vector<float> near = {0.103, 99.1, 0.003, -1.009};
// 0.004 abs error or >1% rel error should not be tolerated by ArrayFloatNear.
std::vector<float> not_near = {0.104, 98.9, 0.004, -1.011};
// Manually set the FP16 absoulte error and FP16 relative error to 10% and
// 1.
std::vector<float> manual_error = {1, 91, 0.9, -1.9};
// Setup FP16 mode.
tflite::KernelTestDelegateProviders::Get()->MutableParams()->Set<bool>(
tflite::KernelTestDelegateProviders::kAllowFp16PrecisionForFp32, true);
EXPECT_THAT(near, ElementsAreArray(ArrayFloatNear(expected)));
auto not_near_matchers = ArrayFloatNear(expected);
for (auto& matcher : not_near_matchers) {
matcher = Not(matcher);
}
EXPECT_THAT(not_near, ElementsAreArray(not_near_matchers));
EXPECT_THAT(manual_error,
ElementsAreArray(ArrayFloatNear(
expected, /*max_abs_err=*/0, /*fp16_max_abs_err=*/1,
/*max_rel_err=*/0, /*fp16_max_rel_err=*/0.1)));
// Revoke FP16 mode.
tflite::KernelTestDelegateProviders::Get()->MutableParams()->Set<bool>(
tflite::KernelTestDelegateProviders::kAllowFp16PrecisionForFp32, false);
}
TEST(TestUtilTest, FloatingPointEqFp32) {
// Minimum number that FP32 could represent. When the expected is a subnormal
// FP32 number, i.e. its exponent is the minimum, -126, FLT_TRUE_MIN is the
// ULP used.
constexpr float fp32_true_min = FLT_TRUE_MIN;
EXPECT_THAT(std::tuple(0.1, 0.1), FloatingPointEq());
EXPECT_THAT(std::tuple(100, 100), FloatingPointEq());
EXPECT_THAT(std::tuple(-1, -1), FloatingPointEq());
EXPECT_THAT(std::tuple(0, 0), FloatingPointEq());
EXPECT_THAT(std::tuple(0.1, 0.10000002), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(100, 100.00002), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(-1, -1.0000002), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(0, 4 * fp32_true_min), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(0, -4 * fp32_true_min), Not(FloatingPointEq()));
// FP32 has 23 bits for the fraction part, so the ULP error is between
// 2^-23 / 2 and 2^-23 relative error. With rounding to nearest, up to 4.5
// ULPs error should be considered as 4 ULPs. So the tolerated relative error
// of 4 ULPs is between 4.5 * 2^-23 / 2 and 4.5 * 2^-23 ~= 2.68 * 10^-7 and
// 5.36 * 10^-7.
// 2.5 * 10^-7 relative error should be tolerated by 4 ULPs.
EXPECT_THAT(std::tuple(0.1, 0.100000025), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(100, 100.000025), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(-1, -1.00000025), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(0, 4 * fp32_true_min), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(0, -4 * fp32_true_min), FloatingPointAlmostEq());
// 5.5 * 10^-7 relative error should not be tolerated by 4 ULPs.
EXPECT_THAT(std::tuple(0.1, 0.100000055), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(100, 100.000055), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(-1, -1.00000055), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(0, 5 * fp32_true_min), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(0, -5 * fp32_true_min), Not(FloatingPointAlmostEq()));
}
TEST(TestUtilTest, FloatingPointEqFp16) {
// Minimum number that FP16 could represent. When the expected is a subnormal
// FP16 number, i.e. its exponent is the minimum, -14, this is the ULP used.
// Given minimum exponent is -14 and fraction has 10 bits, the true minimum
// of FP16 is 2^(-14-10) = 2^(-24).
constexpr float fp16_true_min = 0x1p-24;
// Setup FP16 mode.
tflite::KernelTestDelegateProviders::Get()->MutableParams()->Set<bool>(
tflite::KernelTestDelegateProviders::kAllowFp16PrecisionForFp32, true);
// FP16 has 10 bits for tha fraction part, so the ULP error is between
// 2^-10 / 2 and 2^-10 relative error. Since we emulate a FP16 ULP by 2^13
// FP32 ULPs, rounding error is negligible. So the tolerated relative error
// of 4 ULPs is roughly between 4 * 2^-10 / 2 and 4 * 2^-10 ~= 0.195% and
// 0.39%.
// 0.15% relative error should be tolerated by 4 ULPs in FP16.
EXPECT_THAT(std::tuple(0.1, 0.10015), FloatingPointEq());
EXPECT_THAT(std::tuple(100, 100.15), FloatingPointEq());
EXPECT_THAT(std::tuple(-1, -1.0015), FloatingPointEq());
EXPECT_THAT(std::tuple(0, 4 * fp16_true_min), FloatingPointEq());
EXPECT_THAT(std::tuple(0, -4 * fp16_true_min), FloatingPointEq());
// NaN equals to NaN in FP16 mode.
EXPECT_THAT(std::tuple(std::nanf(""), std::nanf("")), FloatingPointEq());
// FloatingPointEq() should behave exactly like FloatingPointAlmostEq() in
// FP16 mode.
EXPECT_THAT(std::tuple(0.1, 0.10015), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(100, 100.15), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(-1, -1.0015), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(0, 4 * fp16_true_min), FloatingPointAlmostEq());
EXPECT_THAT(std::tuple(0, -4 * fp16_true_min), FloatingPointAlmostEq());
// 0.4% relative error should not be tolerated by 4 ULPs in FP16.
EXPECT_THAT(std::tuple(0.1, 0.1004), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(100, 100.4), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(-1, -1.004), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(0, 5 * fp16_true_min), Not(FloatingPointEq()));
EXPECT_THAT(std::tuple(0, -5 * fp16_true_min), Not(FloatingPointEq()));
// NaN equals to NaN in FP16 mode.
EXPECT_THAT(std::tuple(std::nanf(""), std::nanf("")),
FloatingPointAlmostEq());
// FloatingPointEq() should behave exactly like FloatingPointAlmostEq() in
// FP16 mode.
EXPECT_THAT(std::tuple(0.1, 0.1004), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(100, 100.4), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(-1, -1.004), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(0, 5 * fp16_true_min), Not(FloatingPointAlmostEq()));
EXPECT_THAT(std::tuple(0, -5 * fp16_true_min), Not(FloatingPointAlmostEq()));
// Revoke FP16 mode.
tflite::KernelTestDelegateProviders::Get()->MutableParams()->Set<bool>(
tflite::KernelTestDelegateProviders::kAllowFp16PrecisionForFp32, false);
}
TEST(TestUtilTest, QuantizeVector) {
std::vector<float> data = {-1.0, -0.5, 0.0, 0.5, 1.0, 1000.0};
auto q_data = Quantize<uint8_t>(data, /*scale=*/1.0, /*zero_point=*/0);
std::vector<uint8_t> expected = {0, 0, 0, 1, 1, 255};
EXPECT_THAT(q_data, ElementsAreArray(expected));
}
TEST(TestUtilTest, QuantizeVectorScalingDown) {
std::vector<float> data = {-1.0, -0.5, 0.0, 0.5, 1.0, 1000.0};
auto q_data = Quantize<uint8_t>(data, /*scale=*/10.0, /*zero_point=*/0);
std::vector<uint8_t> expected = {0, 0, 0, 0, 0, 100};
EXPECT_THAT(q_data, ElementsAreArray(expected));
}
TEST(TestUtilTest, QuantizeVectorScalingUp) {
std::vector<float> data = {-1.0, -0.5, 0.0, 0.5, 1.0, 1000.0};
auto q_data = Quantize<uint8_t>(data, /*scale=*/0.1, /*zero_point=*/0);
std::vector<uint8_t> expected = {0, 0, 0, 5, 10, 255};
EXPECT_THAT(q_data, ElementsAreArray(expected));
}
TEST(TestUtilTest, DequantizeVectorFp16) {
std::vector<half> data = {half(-1.0f), half(-0.5f), half(0.0f), half(0.5f),
half(1.0f)};
auto f_data = Dequantize<half>(data, /*scale=*/0.1f, /*zero_point=*/0);
std::vector<float> expected = {-0.1f, -0.05f, 0.0f, 0.05f, 0.1f};
EXPECT_THAT(f_data, ElementsAreArray(tflite::ArrayFloatNear(expected, 1e-7)));
}
TEST(DimsAreMatcherTestTensor, ValidOneD) {
TensorUniquePtr t = BuildTfLiteTensor(kTfLiteInt32, {2}, kTfLiteDynamic);
EXPECT_THAT(t.get(), DimsAre({2}));
}
TEST(DimsAreMatcherTestTensor, ValidTwoD) {
TensorUniquePtr t = BuildTfLiteTensor(kTfLiteInt32, {2, 3}, kTfLiteDynamic);
EXPECT_THAT(t.get(), DimsAre({2, 3}));
}
TEST(DimsAreMatcherTestTensor, ValidScalar) {
TensorUniquePtr t =
BuildTfLiteTensor(kTfLiteInt32, std::vector<int>{}, kTfLiteDynamic);
EXPECT_THAT(t.get(), DimsAre({}));
}
TEST(DimsAreMatcherTestArray, ValidOneD) {
IntArrayUniquePtr arr = BuildTfLiteArray({2});
EXPECT_THAT(arr.get(), DimsAre({2}));
}
TEST(DimsAreMatcherTestArray, ValidTwoD) {
IntArrayUniquePtr arr = BuildTfLiteArray({2, 3});
EXPECT_THAT(arr.get(), DimsAre({2, 3}));
}
TEST(DimsAreMatcherTestArray, ValidScalar) {
IntArrayUniquePtr arr = BuildTfLiteArray({});
EXPECT_THAT(arr.get(), DimsAre({}));
}
} // namespace
} // namespace tflite