Files
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

266 lines
8.7 KiB
C++

/* 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 <cstdint>
#include <functional>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/algorithm/container.h"
#include "absl/types/span.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
namespace {
using testing::ElementsAre;
template <class T>
struct TensorTypeFor;
#define TENSOR_TYPE_ASSOC(CPP_TYPE, TENSORTYPE_VALUE) \
template <> \
struct TensorTypeFor<CPP_TYPE> { \
static constexpr TensorType value = TENSORTYPE_VALUE; \
};
TENSOR_TYPE_ASSOC(int8_t, TensorType_INT8);
TENSOR_TYPE_ASSOC(int16_t, TensorType_INT16);
TENSOR_TYPE_ASSOC(int32_t, TensorType_INT32);
TENSOR_TYPE_ASSOC(int64_t, TensorType_INT64);
TENSOR_TYPE_ASSOC(uint8_t, TensorType_UINT8);
TENSOR_TYPE_ASSOC(uint16_t, TensorType_UINT16);
TENSOR_TYPE_ASSOC(uint32_t, TensorType_UINT32);
TENSOR_TYPE_ASSOC(uint64_t, TensorType_UINT64);
TENSOR_TYPE_ASSOC(float, TensorType_FLOAT32);
static_assert(sizeof(float) == 4, "float type is expected to be 32 bit long");
TENSOR_TYPE_ASSOC(double, TensorType_FLOAT64);
static_assert(sizeof(double) == 8, "double type is expected to be 64 bit long");
template <class Container>
int32_t intsize(const Container& c) {
return static_cast<int32_t>(c.size());
}
template <class T>
class DilateOpModel : public SingleOpModel {
static constexpr TensorType kTensorType = TensorTypeFor<T>::value;
public:
void SetInput(absl::Span<const int32_t> shape,
absl::Span<const T> data = {}) {
input_shape_.assign(shape.begin(), shape.end());
if (data.empty()) {
input_data_.resize(absl::c_accumulate(shape, 1, std::multiplies<int>()));
absl::c_iota(input_data_, 1);
} else {
input_data_.assign(data.begin(), data.end());
}
}
void SetWindowShape(absl::Span<const int64_t> shape) {
window_shape_data_.assign(shape.begin(), shape.end());
}
// Note: the strides are counted in elements on the tensor grid not in the
// underlying buffer.
//
// For instance, {2,2} on the following matrix strting at element 1 will reach
// elements 3 (+2 horizontally), 7 (+2 vertically) and 9 (+2 vertically, +2
// horizontally):
//
// 1 2 3
// 4 5 6
// 7 8 9
void SetWindowStrides(absl::Span<const int64_t> strides) {
window_strides_data_.assign(strides.begin(), strides.end());
}
void SetWindowDilations(absl::Span<const int64_t> dilations) {
window_dilations_data_.assign(dilations.begin(), dilations.end());
}
void SetInitValue(const T& val) { init_value_data_ = val; }
void Build() {
input_ = AddInput({kTensorType, input_shape_});
init_value_ = AddConstInput(kTensorType, {init_value_data_}, {1});
window_shape_ = AddConstInput(TensorType_INT64, window_shape_data_,
{intsize(window_shape_data_)});
window_strides_ = AddConstInput(TensorType_INT64, window_strides_data_,
{intsize(window_strides_data_)});
window_dilations_ = AddConstInput(TensorType_INT64, window_dilations_data_,
{intsize(window_dilations_data_)});
output_ = AddOutput(kTensorType);
SetBuiltinOp(
BuiltinOperator_REDUCE_WINDOW, BuiltinOptions2_ReduceWindowOptions,
CreateReduceWindowOptions(builder_, ReduceWindowFunction_ADD).Union());
BuildInterpreter({input_shape_});
PopulateTensor(input_, input_data_);
}
TfLiteStatus BuildAndInvoke() {
Build();
return Invoke();
}
absl::Span<const T> GetOutputData() {
return absl::Span<const T>(interpreter_->typed_tensor<T>(output_),
GetTensorSize(output_));
}
absl::Span<const int> GetOutputShape() {
const TfLiteIntArray& shape = *(interpreter_->tensor(output_)->dims);
return absl::Span<const int>(shape.data, shape.size);
}
const std::vector<T>& GetInput() const { return input_data_; }
const std::vector<int32_t>& GetInputShape() const { return input_shape_; }
const std::vector<int64_t>& GetWindowShape() const {
return window_shape_data_;
}
const std::vector<int64_t>& GetWindowStrides() const {
return window_strides_data_;
}
const std::vector<int64_t>& GetWindowDilations() const {
return window_dilations_data_;
}
const T& GetInitValue() const { return init_value_data_; }
protected:
int input_ = -1;
int window_shape_ = -1;
int window_strides_ = -1;
int window_dilations_ = -1;
int init_value_ = -1;
int output_ = -1;
std::vector<T> input_data_;
T init_value_data_;
std::vector<int32_t> input_shape_;
std::vector<int64_t> window_shape_data_;
std::vector<int64_t> window_strides_data_;
std::vector<int64_t> window_dilations_data_;
};
template <class StorageType>
class ReduceWindowTest : public testing::Test {
protected:
DilateOpModel<StorageType> model_;
};
using TestList =
testing::Types<int8_t, int16_t, int32_t, int64_t, uint8_t, float, double>;
TYPED_TEST_SUITE(ReduceWindowTest, TestList);
TYPED_TEST(ReduceWindowTest, FullWindow) {
auto& model = this->model_;
model.SetInput(/*shape=*/{3, 3});
model.SetWindowShape({3, 3});
model.SetWindowStrides({1, 1});
model.SetWindowDilations({1, 1});
model.SetInitValue(0);
EXPECT_EQ(this->model_.BuildAndInvoke(), kTfLiteOk);
EXPECT_THAT(this->model_.GetOutputShape(), ElementsAre(1, 1));
EXPECT_THAT(this->model_.GetOutputData(), ElementsAre(45));
}
TYPED_TEST(ReduceWindowTest, NoDilation) {
auto& model = this->model_;
model.SetInput(/*shape=*/{3, 3});
model.SetWindowShape({2, 2});
model.SetWindowStrides({1, 1});
model.SetWindowDilations({1, 1});
model.SetInitValue(0);
EXPECT_EQ(this->model_.BuildAndInvoke(), kTfLiteOk);
EXPECT_THAT(this->model_.GetOutputShape(), ElementsAre(2, 2));
EXPECT_THAT(this->model_.GetOutputData(), ElementsAre(12, 16, 24, 28));
}
TYPED_TEST(ReduceWindowTest, FullWindowWithDilation) {
auto& model = this->model_;
model.SetInput(/*shape=*/{3, 3});
model.SetWindowShape({2, 2});
model.SetWindowStrides({1, 1});
model.SetWindowDilations({2, 2});
model.SetInitValue(0);
EXPECT_EQ(this->model_.BuildAndInvoke(), kTfLiteOk);
EXPECT_THAT(this->model_.GetOutputShape(), ElementsAre(1, 1));
EXPECT_THAT(this->model_.GetOutputData(), ElementsAre(20));
}
TYPED_TEST(ReduceWindowTest, WithDilation) {
auto& model = this->model_;
model.SetInput(/*shape=*/{4, 4});
model.SetWindowShape({2, 2});
model.SetWindowStrides({1, 1});
model.SetWindowDilations({2, 2});
model.SetInitValue(0);
EXPECT_EQ(this->model_.BuildAndInvoke(), kTfLiteOk);
EXPECT_THAT(this->model_.GetOutputShape(), ElementsAre(2, 2));
EXPECT_THAT(this->model_.GetOutputData(), ElementsAre(24, 28, 40, 44));
}
TYPED_TEST(ReduceWindowTest, WithStrides) {
auto& model = this->model_;
model.SetInput(/*shape=*/{4, 4});
model.SetWindowShape({2, 2});
model.SetWindowStrides({2, 2});
model.SetWindowDilations({1, 1});
model.SetInitValue(0);
EXPECT_EQ(this->model_.BuildAndInvoke(), kTfLiteOk);
EXPECT_THAT(this->model_.GetOutputShape(), ElementsAre(2, 2));
EXPECT_THAT(this->model_.GetOutputData(), ElementsAre(14, 22, 46, 54));
}
TYPED_TEST(ReduceWindowTest, WithDilationAndStrides) {
auto& model = this->model_;
model.SetInput(/*shape=*/{5, 5});
model.SetWindowShape({2, 2});
model.SetWindowStrides({2, 2});
model.SetWindowDilations({2, 2});
model.SetInitValue(2);
EXPECT_EQ(this->model_.BuildAndInvoke(), kTfLiteOk);
EXPECT_THAT(this->model_.GetOutputShape(), ElementsAre(2, 2));
EXPECT_THAT(this->model_.GetOutputData(), ElementsAre(30, 38, 70, 78));
}
TYPED_TEST(ReduceWindowTest, OutputShapeRoundingIsCorrect) {
auto& model = this->model_;
model.SetInput(/*shape=*/{1, 64, 114, 114});
model.SetWindowShape({1, 1, 3, 3});
model.SetWindowStrides({1, 1, 2, 2});
model.SetWindowDilations({1, 1, 1, 1});
model.SetInitValue(2);
EXPECT_EQ(this->model_.BuildAndInvoke(), kTfLiteOk);
EXPECT_THAT(this->model_.GetOutputShape(), ElementsAre(1, 64, 56, 56));
}
} // namespace
} // namespace tflite