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

247 lines
8.4 KiB
C++

/* Copyright 2020 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.
==============================================================================*/
#ifndef TENSORFLOW_LITE_DELEGATES_DELEGATE_TEST_UTIL_H_
#define TENSORFLOW_LITE_DELEGATES_DELEGATE_TEST_UTIL_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include "Eigen/Core" // from @eigen_archive
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/core/kernels/register.h"
#include "tensorflow/lite/core/subgraph.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
namespace tflite {
namespace delegates {
namespace test_utils {
// Build a kernel registration for a custom addition op that adds its two
// tensor inputs to produce a tensor output.
TfLiteRegistration AddOpRegistration();
class SimpleDelegate {
public:
enum Options {
kNone = 0,
kFailOnInit = 1 << 0, // To simulate failure of Delegate node's Init().
kFailOnPrepare =
1 << 1, // To simulate failure of Delegate node's Prepare().
kFailOnInvoke = 1 << 2, // To simulate failure of Delegate node's Invoke().
kAutomaticShapePropagation =
1 << 3, // This assumes that the runtime will propagate
// shapes using the original execution plan.
kNoCustomOp =
1 << 4, // If not set, the graph nodes specified in the 'nodes'
// parameter should be custom ops with name "my_add";
// if set, they should be the builtin ADD operator.
kSetOutputTensorDynamic = 1
<< 5, // If set, this delegate sets output tensor
// to as dynamic during kernel Prepare.
};
// Create a simple implementation of a TfLiteDelegate. We use the C++ class
// SimpleDelegate and it can produce a handle TfLiteDelegate that is
// value-copyable and compatible with TfLite.
//
// Parameters:
// nodes: Indices of the graph nodes that the delegate will handle.
// min_ops_per_subset: If >0, partitioning preview is used to choose only
// those subsets with min_ops_per_subset number of nodes.
// options: Options to control the behavior of the delegate.
explicit SimpleDelegate(const std::vector<int>& nodes,
int64_t delegate_flags = kTfLiteDelegateFlagsNone,
Options options = Options::kNone,
int min_ops_per_subset = 0);
static std::unique_ptr<SimpleDelegate> DelegateWithRuntimeShapePropagation(
const std::vector<int>& nodes, int64_t delegate_flags,
int min_ops_per_subset);
static std::unique_ptr<SimpleDelegate> DelegateWithDynamicOutput(
const std::vector<int>& nodes);
TfLiteRegistration FakeFusedRegistration();
TfLiteDelegate* get_tf_lite_delegate() { return &delegate_; }
int min_ops_per_subset() { return min_ops_per_subset_; }
private:
std::vector<int> nodes_;
TfLiteDelegate delegate_;
bool fail_delegate_node_init_ = false;
bool fail_delegate_node_prepare_ = false;
bool fail_delegate_node_invoke_ = false;
int min_ops_per_subset_ = 0;
bool automatic_shape_propagation_ = false;
bool custom_op_ = true;
bool set_output_tensor_dynamic_ = false;
};
// Overload bitwise AND operator for SimpleDelegate::Options
inline SimpleDelegate::Options operator&(SimpleDelegate::Options a,
SimpleDelegate::Options b) {
return static_cast<SimpleDelegate::Options>(static_cast<unsigned int>(a) &
static_cast<unsigned int>(b));
}
// Overload bitwise OR operator for SimpleDelegate::Options
inline SimpleDelegate::Options operator|(SimpleDelegate::Options a,
SimpleDelegate::Options b) {
return static_cast<SimpleDelegate::Options>(static_cast<unsigned int>(a) |
static_cast<unsigned int>(b));
}
// Base class for single/multiple delegate tests.
// Friend of Interpreter to access private methods.
class TestDelegation {
public:
virtual ~TestDelegation() = default;
// Returns an empty interpreter that uses the same default delegates that are
// normally enabled by default.
static std::unique_ptr<impl::Interpreter>
NewInterpreterWithDefaultDelegates() {
auto interpreter = std::make_unique<impl::Interpreter>();
interpreter->lazy_delegate_providers_ =
tflite::ops::builtin::BuiltinOpResolver().GetDelegateCreators();
return interpreter;
}
protected:
TfLiteStatus RemoveAllDelegates() {
return interpreter_->RemoveAllDelegates();
}
void SetMetadata(const std::map<std::string, std::string>& metadata) {
interpreter_->SetMetadata(metadata);
}
virtual void SetUpSubgraph(Subgraph* subgraph);
void AddSubgraphs(int subgraphs_to_add,
int* first_new_subgraph_index = nullptr);
std::unique_ptr<impl::Interpreter> interpreter_;
};
// Tests scenarios involving a single delegate.
class TestDelegate : public TestDelegation, public ::testing::Test {
protected:
void SetUp() override;
void TearDown() override;
TfLiteBufferHandle last_allocated_handle_ = kTfLiteNullBufferHandle;
TfLiteBufferHandle AllocateBufferHandle() { return ++last_allocated_handle_; }
std::unique_ptr<SimpleDelegate> delegate_, delegate2_;
};
// Tests scenarios involving a single delegate and control edges.
// Subgraph 0 has the form
//
// /---OP2---\
// / \
// >---OP0 OP3--->
// \ /
// \---OP1---/
//
// Delegating OP0, OP2 will generate an execution graph with a "super-node"
// {OP0->OP2}, which can be disabled by adding (in metadata) a control edge
// between OP1 and OP2:
//
// /->-OP2---\
// / ^ \
// >---OP0 ^ OP3--->
// \ ^ /
// \---OP1---/
//
class TestDelegateWithControlEdges : public TestDelegate {
protected:
void SetUpSubgraph(Subgraph* subgraph) override;
};
// Tests scenarios involving two delegates, parametrized by the first & second
// delegate's flags.
class TestTwoDelegates
: public TestDelegation,
public ::testing::TestWithParam<
std::pair<TfLiteDelegateFlags, TfLiteDelegateFlags>> {
protected:
void SetUp() override;
void TearDown() override;
std::unique_ptr<SimpleDelegate> delegate_, delegate2_;
};
// Tests delegate functionality related to FP16 graphs.
// Model architecture:
// 1->DEQ->2 4->DEQ->5 7->DEQ->8 10->DEQ->11
// | | | |
// 0----->ADD->3----->ADD->6----->MUL->9------>ADD-->12
// Input: 0, Output:12.
// All constants are 2, so the function is: (x + 2 + 2) * 2 + 2 = 2x + 10
//
// Delegate only supports ADD, so can have up to two delegated partitions.
// TODO(b/156707497): Add more cases here once we have landed CPU kernels
// supporting FP16.
class TestFP16Delegation : public ::testing::TestWithParam<int> {
protected:
void SetUp() override;
void VerifyInvoke();
void TearDown() override { interpreter_.reset(); }
protected:
class FP16Delegate {
public:
// Uses FP16GraphPartitionHelper to accept ADD nodes with fp16 input.
explicit FP16Delegate(int num_delegated_subsets,
bool fail_node_prepare = false,
bool fail_node_invoke = false);
TfLiteRegistration FakeFusedRegistration();
TfLiteDelegate* get_tf_lite_delegate() { return &delegate_; }
int num_delegated_subsets() { return num_delegated_subsets_; }
private:
TfLiteDelegate delegate_;
int num_delegated_subsets_;
bool fail_delegate_node_prepare_ = false;
bool fail_delegate_node_invoke_ = false;
};
std::unique_ptr<impl::Interpreter> interpreter_;
std::unique_ptr<FP16Delegate> delegate_;
Eigen::half float16_const_;
};
} // namespace test_utils
} // namespace delegates
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_DELEGATE_TEST_UTIL_H_