/* 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 #include #include #include #include #include #include "Eigen/Core" // from @eigen_archive #include "tensorflow/lite/kernels/test_util.h" #include "tensorflow/lite/schema/schema_generated.h" #include "tensorflow/lite/string_type.h" #include "tensorflow/lite/types/half.h" namespace tflite { namespace { using ::testing::ElementsAreArray; using ::testing::IsEmpty; enum class TestType { kConst = 0, kDynamic = 1, }; template class FillOpModel : public SingleOpModel { public: explicit FillOpModel(TensorType dims_tensor_type, std::initializer_list dims_shape, std::initializer_list dims_data, value_type value, TestType input_tensor_types) { if (input_tensor_types == TestType::kDynamic) { dims_ = AddInput(dims_tensor_type); } else { dims_ = AddConstInput(dims_tensor_type, dims_data, dims_shape); } value_ = AddInput(GetTensorType()); output_ = AddOutput(GetTensorType()); SetBuiltinOp(BuiltinOperator_FILL, BuiltinOptions_FillOptions, CreateFillOptions(builder_).Union()); BuildInterpreter({dims_shape, {}}); if (input_tensor_types == TestType::kDynamic) { if (dims_data.size() > 0) { PopulateTensor(dims_, dims_data); } } PopulateTensor(value_, {value}); } std::vector GetOutput() { return ExtractVector(output_); } std::vector GetOutputShape() { return GetTensorShape(output_); } protected: int dims_; int value_; int output_; }; template class QuantizedFillOpModel : public SingleOpModel { public: explicit QuantizedFillOpModel(TensorType dims_tensor_type, std::initializer_list dims_shape, std::initializer_list dims_data, const TensorData& tensor_data, float value) { dims_ = AddInput(dims_tensor_type); value_ = AddInput(tensor_data); output_ = AddOutput(tensor_data); SetBuiltinOp(BuiltinOperator_FILL, BuiltinOptions_FillOptions, CreateFillOptions(builder_).Union()); BuildInterpreter({dims_shape, {}}); if (dims_data.size() > 0) { PopulateTensor(dims_, dims_data); } QuantizeAndPopulate(value_, {value}); } std::vector GetOutput() { return ExtractVector(output_); } std::vector GetDequantizedOutput() { TfLiteTensor* t = interpreter_->tensor(output_); return Dequantize(GetOutput(), t->params.scale, t->params.zero_point); } std::vector GetOutputShape() { return GetTensorShape(output_); } protected: int dims_; int value_; int output_; }; class FillOpTest : public ::testing::TestWithParam {}; TEST_P(FillOpTest, FillInt32) { FillOpModel m(TensorType_INT32, {2}, {2, 3}, -11, GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({-11, -11, -11, -11, -11, -11})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 3})); } TEST_P(FillOpTest, FillInt64) { FillOpModel m(TensorType_INT64, {2}, {2, 4}, 1LL << 45, GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({1LL << 45, 1LL << 45, 1LL << 45, 1LL << 45, 1LL << 45, 1LL << 45, 1LL << 45, 1LL << 45})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 4})); } TEST_P(FillOpTest, FillFloat) { FillOpModel m(TensorType_INT64, {3}, {2, 2, 2}, 4.0, GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), Pointwise(FloatingPointEq(), {4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 2})); } TEST_P(FillOpTest, FillFloat16) { FillOpModel m(TensorType_INT64, {3}, {2, 2, 2}, half(4.0f), GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), Pointwise(FloatingPointEq(), {4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 2})); } TEST_P(FillOpTest, FillFloatInt32Dims) { FillOpModel m(TensorType_INT32, {3}, {2, 2, 2}, 4.0, GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT( m.GetOutput(), Pointwise(FloatingPointEq(), {4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 2})); } TEST_P(FillOpTest, FillOutputScalar) { FillOpModel m(TensorType_INT64, {0}, {}, 4.0, GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), Pointwise(FloatingPointEq(), {4.0})); EXPECT_THAT(m.GetOutputShape(), IsEmpty()); } TEST_P(FillOpTest, FillBool) { FillOpModel m(TensorType_INT64, {3}, {2, 2, 2}, true, GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({true, true, true, true, true, true, true, true})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 2})); } TEST(FillOpTest, FillString) { FillOpModel m(TensorType_INT64, {3}, {2, 2, 2}, "AB", TestType::kDynamic); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({"AB", "AB", "AB", "AB", "AB", "AB", "AB", "AB"})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 2})); } TEST_P(FillOpTest, FillInt8) { FillOpModel m(TensorType_INT64, {3}, {2, 2, 2}, 5, GetParam()); ASSERT_EQ(m.Invoke(), kTfLiteOk); EXPECT_THAT(m.GetOutput(), ElementsAreArray({5, 5, 5, 5, 5, 5, 5, 5})); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 2, 2})); } template void QuantizedFill(float value) { // Prepare TensorData for quantization of value const float kMin = -1; // Workaround to get a zero-point of 0 const float kMax = std::numeric_limits::max() / static_cast(std::numeric_limits::max() + 1); const TensorData tensor_data(GetTensorType(), {}, std::abs(value) * kMin, std::abs(value) * kMax); QuantizedFillOpModel m(TensorType_INT32, {2}, {2, 3}, tensor_data, value); ASSERT_EQ(m.Invoke(), kTfLiteOk); constexpr float epsilon = 0.01f; const float min_value = tensor_data.min - epsilon; const float max_value = tensor_data.max + epsilon; const float kQuantizedTolerance = (max_value - min_value) / (std::numeric_limits::max() - std::numeric_limits::min()); EXPECT_THAT( m.GetDequantizedOutput(), ElementsAreArray(ArrayFloatNear( {value, value, value, value, value, value}, kQuantizedTolerance))); EXPECT_THAT(m.GetOutputShape(), ElementsAreArray({2, 3})); } TEST(FillOpTest, QuantizedFillInt8) { QuantizedFill(3.14f); } TEST(FillOpTest, QuantizedFillInt16) { QuantizedFill(3.14f); } INSTANTIATE_TEST_SUITE_P(FillOpTest, FillOpTest, ::testing::Values(TestType::kConst, TestType::kDynamic)); } // namespace } // namespace tflite