chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
@@ -0,0 +1,4 @@
# TFLite Delegate Compatibility Checker [Experimental]
The TFLite Delegate Compatibility Checker (DCC) checks compatibility for
different delegates including GPU, NNAPI, etc.
@@ -0,0 +1,50 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "online_helper_delegate",
srcs = ["online_helper_delegate.cc"],
hdrs = ["online_helper_delegate.h"],
deps = [
"//tensorflow/lite:kernel_api",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"@com_google_absl//absl/status",
],
)
cc_library(
name = "delegate_compatibility_checker_base",
srcs = ["delegate_compatibility_checker_base.cc"],
hdrs = [
"delegate_compatibility_checker_base.h",
],
deps = [
":delegate_compatibility_checker_util",
"//tensorflow/lite:framework_stable",
"//tensorflow/lite/core:framework_stable",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/kernels:builtin_ops",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"//tensorflow/lite/tools/versioning:op_signature",
"@com_google_absl//absl/status",
],
)
cc_library(
name = "delegate_compatibility_checker_util",
hdrs = [
"delegate_compatibility_checker_util.h",
],
)
@@ -0,0 +1,62 @@
/* Copyright 2022 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/tools/delegates/compatibility/common/delegate_compatibility_checker_base.h"
#include <cstdlib>
#include "absl/status/status.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_util.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
absl::Status DelegateCompatibilityCheckerBase::checkModelCompatibilityOffline(
tflite::FlatBufferModel* model_buffer, proto::CompatibilityResult* result) {
auto model = model_buffer->GetModel();
auto subgraphs = model->subgraphs();
for (int i = 0; i < subgraphs->Length(); ++i) {
const tflite::SubGraph* subgraph = subgraphs->Get(i);
for (int j = 0; j < subgraph->operators()->Length(); ++j) {
proto::OpCompatibilityResult* op_result =
result->add_compatibility_results();
op_result->set_subgraph_index_in_model(i);
op_result->set_operator_index_in_subgraph(j);
const tflite::Operator* op = subgraph->operators()->Get(j);
const tflite::OperatorCode* op_code =
model->operator_codes()->Get(op->opcode_index());
RETURN_IF_ERROR(
checkOpCompatibilityOffline(op_code, op, subgraph, model, op_result));
}
}
return absl::OkStatus();
}
absl::Status DelegateCompatibilityCheckerBase::checkOpCompatibilityOffline(
const tflite::OperatorCode* op_code, const tflite::Operator* op,
const tflite::SubGraph* subgraph, const tflite::Model* model,
proto::OpCompatibilityResult* op_result) {
OpSignature op_sig = tflite::GetOpSignature(op_code, op, subgraph, model);
auto status = checkOpSigCompatibility(op_sig, op_result);
free(op_sig.builtin_data);
return status;
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,101 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_BASE_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_BASE_H_
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/core/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
// TODO(b/243489631): Remove online mode support.
// Base class for the delegate compatibility checker (DCC). Extracts the logic
// of iterating through the model, and lets each specific DCC to check if the
// operation of a node is compatible with that delegate. TfLiteNode and
// operator (in schema.fbs) are equivalent.
// There are two modes supported: online and offline. Online mode needs
// TfLiteContext while offline mode doesn't. Online mode is supported as an
// intermediate stage and it is recommended that delegates support offline
// mode in validation logic, if possible.
class DelegateCompatibilityCheckerBase {
public:
virtual ~DelegateCompatibilityCheckerBase() = default;
// Iterates over the subgraphs in the model, and for each operator, checks if
// it is compatible with the delegate.
// Stores the compatibility for each operator in the result structure.
absl::Status checkModelCompatibilityOffline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result);
// This function is implemented differently by each specific DCC.
// Stores the compatibility for each operator in the result structure.
virtual absl::Status checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) = 0;
// This function gets the operation signature (OpSignature) from the
// op_code, op, subgraph and model, and then call to checkOpSigCompatibility()
// with the operation signature.
// Params:
// op_code: Used to get the built in code of the operator, in
// order to know which operator is being used (e.g. MUL,
// FULLY_CONNECTED…).
// op: Used to get the input and output tensors indexes, so that obtains the
// tensors with the subgraph.
// subgraph: Used to get the tensors to finally get the OpSignature.
// model: Used to get the buffer in order to check if the tensor is
// a constant tensor.
// op_result: Stores whether the operation is compatible or not and why.
// Returns: absl::OkStatus() if the function is completed without exceptions.
absl::Status checkOpCompatibilityOffline(
const tflite::OperatorCode* op_code, const tflite::Operator* op,
const tflite::SubGraph* subgraph, const tflite::Model* model,
tflite::proto::OpCompatibilityResult* op_result);
// Returns the dictionary with the initialized keys. This is an abstract
// function because each specific DCC needs different parameters, which will
// be the keys in the returned dictionary.
virtual std::unordered_map<std::string, std::string>
getDccConfigurations() = 0;
// Sets the parameters needed in the specific DCC. Also checks if the
// value types are correct.
virtual absl::Status setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) = 0;
private:
// This function is implemented differently by each specific DCC because
// they contain the logic for checking if the operation in op_sig is
// compatible for that specific DCC. op_result stores whether the operation
// is supported or not, and why. By using offline mode, only op_signature is
// used to perform the checks.
virtual absl::Status checkOpSigCompatibility(
const tflite::OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) = 0;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_BASE_H_
@@ -0,0 +1,31 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_UTIL_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_UTIL_H_
namespace tflite {
namespace tools {
#define RETURN_IF_ERROR(s) \
{ \
auto c = (s); \
if (!c.ok()) return c; \
}
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_DELEGATE_COMPATIBILITY_CHECKER_UTIL_H_
@@ -0,0 +1,61 @@
/* Copyright 2022 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/tools/delegates/compatibility/common/online_helper_delegate.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/context_util.h"
#include "tensorflow/lite/logger.h"
#include "tensorflow/lite/minimal_logging.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
TfLiteStatus OnlineHelperDelegate::DoPrepare(TfLiteContext* context,
TfLiteDelegate* delegate) {
auto self = reinterpret_cast<OnlineHelperDelegate*>(delegate);
// Gets execution plan.
TfLiteIntArray* execution_plan;
TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
// Validates compatibility for each node.
for (auto node_index : TfLiteIntArrayView(execution_plan)) {
proto::OpCompatibilityResult* op_result =
self->result_->add_compatibility_results();
TfLiteNode* node;
TfLiteRegistration* registration;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(
context, node_index, &node, &registration));
// Always subgraph #0 because in the interpreter (and TfLiteContext), the
// methods refer to the primary_subgraph, so the only subgraph available is
// the subgraph #0.
op_result->set_subgraph_index_in_model(0);
op_result->set_operator_index_in_subgraph(node_index);
auto status = self->check_op_func_ptr_(context, node, registration,
self->dcc_configs_, op_result);
if (!status.ok()) {
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Error at node %d: %s", node_index,
status.message());
return kTfLiteError;
}
}
return kTfLiteOk;
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,97 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_COMMON_ONLINE_HELPER_DELEGATE_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_ONLINE_HELPER_DELEGATE_H_
#include <functional>
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
// This utility delegate is required because some TfLiteContext related
// functions are forbidden if not calling in delegate. This class is only used
// for online mode.
// WARNING: This is an experimental class and subject to change.
class OnlineHelperDelegate : public TfLiteDelegate {
public:
OnlineHelperDelegate(
std::unordered_map<std::string, std::string>& dcc_configs,
std::function<absl::Status(TfLiteContext*, const TfLiteNode*,
const TfLiteRegistration*,
std::unordered_map<std::string, std::string>&,
proto::OpCompatibilityResult*)>
check_op_func_ptr,
proto::CompatibilityResult* result)
: TfLiteDelegate(TfLiteDelegateCreate()),
result_(result),
dcc_configs_(dcc_configs),
check_op_func_ptr_(check_op_func_ptr) {
Prepare = DoPrepare;
CopyFromBufferHandle = DoCopyFromBufferHandle;
CopyToBufferHandle = DoCopyToBufferHandle;
FreeBufferHandle = DoFreeBufferHandle;
data_ = &delegate_data_;
}
protected:
// This function uses a pointer to a method (implemented by each specific DCC)
// which contains the logic to check whether the primary subgraph can be
// delegated to the specific delegate.
static TfLiteStatus DoPrepare(TfLiteContext* context,
TfLiteDelegate* delegate);
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyFromBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyToBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// There is no buffer handle in this delegate.
static void DoFreeBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle* handle) {}
private:
int delegate_data_;
proto::CompatibilityResult* result_;
std::unordered_map<std::string, std::string> dcc_configs_;
std::function<absl::Status(TfLiteContext*, const TfLiteNode*,
const TfLiteRegistration*,
std::unordered_map<std::string, std::string>&,
proto::OpCompatibilityResult*)>
check_op_func_ptr_;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_COMMON_ONLINE_HELPER_DELEGATE_H_
@@ -0,0 +1,48 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_test(
name = "gpu_delegate_compatibility_checker_test",
srcs = [
"gpu_delegate_compatibility_checker_test.cc",
],
data = [
"//tensorflow/lite:testdata/add.bin",
"//tensorflow/lite:testdata/conv3d_huge_im2col.bin",
],
tags = ["no_oss"], #TODO(b/276295784): Re-enable when fixed.
deps = [
":gpu_delegate_compatibility_checker",
"//tensorflow/core/platform:resource_loader",
"//tensorflow/lite:model_builder",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"@com_google_absl//absl/status",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "gpu_delegate_compatibility_checker",
srcs = ["gpu_delegate_compatibility_checker.cc"],
hdrs = [
"gpu_delegate_compatibility_checker.h",
],
deps = [
"//tensorflow/lite:model_builder",
"//tensorflow/lite/tools/delegates/compatibility/common:delegate_compatibility_checker_base",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"//tensorflow/lite/tools/versioning:gpu_compatibility",
"//tensorflow/lite/tools/versioning:op_signature",
"@com_google_absl//absl/status",
],
)
@@ -0,0 +1,95 @@
/* Copyright 2022 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/tools/delegates/compatibility/gpu/gpu_delegate_compatibility_checker.h"
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/gpu_compatibility.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
namespace {
void convertToValidationFailureType(absl::Status status,
proto::OpCompatibilityResult* op_result) {
auto compatibility_failure = op_result->add_compatibility_failures();
compatibility_failure->set_description(std::string(status.message()));
switch (status.code()) {
case absl::StatusCode::kInvalidArgument:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INVALID_ARGUMENT);
break;
case absl::StatusCode::kUnimplemented:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNIMPLEMENTED_ERROR);
break;
case absl::StatusCode::kInternal:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INTERNAL_ERROR);
break;
case absl::StatusCode::kOutOfRange:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_OUT_OF_RANGE);
break;
default:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INTERNAL_ERROR);
compatibility_failure->set_description(
"Unknown validation failure type.");
}
}
} // namespace
std::unordered_map<std::string, std::string>
tools::GpuDelegateCompatibilityChecker::getDccConfigurations() {
return {};
}
absl::Status tools::GpuDelegateCompatibilityChecker::setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) {
return absl::OkStatus();
}
absl::Status
tools::GpuDelegateCompatibilityChecker::checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) {
return absl::UnimplementedError(
"Online mode is not supported on GPU delegate compatibility checker.");
}
absl::Status tools::GpuDelegateCompatibilityChecker::checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) {
auto status = CheckGpuDelegateCompatibility(op_sig);
if (!status.ok()) {
convertToValidationFailureType(status, op_result);
op_result->set_is_supported(false);
} else {
op_result->set_is_supported(true);
}
return absl::OkStatus();
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,59 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_GPU_GPU_DELEGATE_COMPATIBILITY_CHECKER_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_GPU_GPU_DELEGATE_COMPATIBILITY_CHECKER_H_
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_base.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
// Class to check if an operation or a model is compatible with GPU delegate.
// No supported parameters.
class GpuDelegateCompatibilityChecker
: public DelegateCompatibilityCheckerBase {
public:
GpuDelegateCompatibilityChecker() {}
// Online mode is not supported in the GPU delegate compatibility checker.
absl::Status checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) override;
// No parameters are supported, no need to call to this function.
std::unordered_map<std::string, std::string> getDccConfigurations() override;
// No parameters are supported, no need to call to this function.
absl::Status setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) override;
private:
absl::Status checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) override;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_GPU_GPU_DELEGATE_COMPATIBILITY_CHECKER_H_
@@ -0,0 +1,114 @@
/* Copyright 2022 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/tools/delegates/compatibility/gpu/gpu_delegate_compatibility_checker.h"
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/status/status.h"
#include "tensorflow/core/platform/resource_loader.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
#ifndef EXPECT_OK
#define EXPECT_OK(x) EXPECT_TRUE(x.ok());
#endif
namespace {
class AddOpModel : public SingleOpModel {
public:
AddOpModel(const TensorData& input1, const TensorData& input2,
const TensorData& output, ActivationFunctionType activation_type) {
input1_ = AddInput(input1);
input2_ = AddInput(input2);
output_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_ADD, BuiltinOptions_AddOptions,
CreateAddOptions(builder_, activation_type).Union());
// Builds interpreter.
BuildInterpreter({GetShape(input1_), GetShape(input2_)});
}
protected:
int input1_;
int input2_;
int output_;
};
} // namespace
TEST(GpuDelegateCompatibilityCheckerTest, CheckOnlineMode) {
const std::string& full_path =
tensorflow::GetDataDependencyFilepath("tensorflow/lite/testdata/add.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
proto::CompatibilityResult compatibility_result;
GpuDelegateCompatibilityChecker gpu_dcc;
// Online mode is not supported by GPU DCC
EXPECT_EQ(
gpu_dcc
.checkModelCompatibilityOnline(fb_model.get(), &compatibility_result)
.code(),
absl::StatusCode::kUnimplemented);
}
TEST(GpuDelegateCompatibilityCheckerTest, CompatibleModelOfflineMode) {
const std::string& full_path =
tensorflow::GetDataDependencyFilepath("tensorflow/lite/testdata/add.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
proto::CompatibilityResult compatibility_result;
GpuDelegateCompatibilityChecker gpu_dcc;
EXPECT_OK(gpu_dcc.checkModelCompatibilityOffline(fb_model.get(),
&compatibility_result));
for (auto op_compatibility_result :
compatibility_result.compatibility_results()) {
EXPECT_TRUE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result.compatibility_results_size(), 2);
}
TEST(GpuDelegateCompatibilityCheckerTest, IncompatibleModelOfflineMode) {
const std::string& full_path = tensorflow::GetDataDependencyFilepath(
"tensorflow/lite/testdata/conv3d_huge_im2col.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
proto::CompatibilityResult compatibility_result;
GpuDelegateCompatibilityChecker gpu_dcc;
EXPECT_OK(gpu_dcc.checkModelCompatibilityOffline(fb_model.get(),
&compatibility_result));
for (auto op_compatibility_result :
compatibility_result.compatibility_results()) {
EXPECT_FALSE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result.compatibility_results_size(), 1);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,87 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
# BUILD rules for NNAPI delegate compatibility checking.
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "nnapi_compatibility_lib",
srcs = [
"nnapi_compatibility_lib.cc",
],
hdrs = [
"nnapi_compatibility_lib.h",
],
deps = [
"//tensorflow/lite:framework_stable",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
],
)
cc_library(
name = "nnapi_delegate_compatibility_checker",
srcs = ["nnapi_delegate_compatibility_checker.cc"],
hdrs = [
"nnapi_delegate_compatibility_checker.h",
],
copts = ["-DNNAPI_VERBOSE_VALIDATION"],
deps = [
"//tensorflow/lite:framework_stable",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core:cc_api_stable",
"//tensorflow/lite/core/kernels:builtin_ops",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
"//tensorflow/lite/nnapi:nnapi_lib",
"//tensorflow/lite/tools/delegates/compatibility/common:delegate_compatibility_checker_base",
"//tensorflow/lite/tools/delegates/compatibility/common:delegate_compatibility_checker_util",
"//tensorflow/lite/tools/delegates/compatibility/common:online_helper_delegate",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"//tensorflow/lite/tools/versioning:op_signature",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "nnapi_compatibility_lib_test",
srcs = [
"nnapi_compatibility_lib_test.cc",
],
deps = [
":nnapi_compatibility_lib",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate_verbose_validation",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "nnapi_delegate_compatibility_checker_test",
srcs = [
"nnapi_delegate_compatibility_checker_test.cc",
],
data = [
"//tensorflow/lite:testdata/add.bin",
],
tags = ["no_oss"], #TODO(b/276295784): Re-enable when fixed.
deps = [
":nnapi_delegate_compatibility_checker",
"//tensorflow/core/platform:resource_loader",
"//tensorflow/lite:model_builder",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/tools/delegates/compatibility/protos:compatibility_result_cc",
"@com_google_absl//absl/status",
"@com_google_googletest//:gtest_main",
],
)
@@ -0,0 +1,74 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_compatibility_lib.h"
#include <cstdint>
#include <map>
#include <utility>
#include <vector>
#include "tensorflow/lite/context_util.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/logger.h"
#include "tensorflow/lite/minimal_logging.h"
namespace tflite {
namespace tools {
using ::tflite::delegate::nnapi::NNAPIValidationFailure;
TfLiteStatus CheckCompatibility(
TfLiteContext* context, int32_t runtime_feature_level,
std::vector<int>* supported_nodes,
std::map<int, std::vector<NNAPIValidationFailure>>* failures_by_node) {
if (!context) {
TFLITE_LOG_PROD_ONCE(TFLITE_LOG_ERROR, "Context is nullptr.");
return kTfLiteError;
}
// Gets execution plan.
TfLiteIntArray* execution_plan;
TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
// Validates compatibility for each node.
for (int node_index : TfLiteIntArrayView(execution_plan)) {
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "Node index: %d", node_index);
TfLiteNode* node;
TfLiteRegistration* registration;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(
context, node_index, &node, &registration));
std::vector<delegate::nnapi::NNAPIValidationFailure> map_failures;
if (NNAPIDelegateKernel::Validate(
context, registration, runtime_feature_level, node,
/* is_accelerator_specified= */ true,
/* vendor_plugin= */ nullptr, &map_failures)) {
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "Built-in Code: %d",
registration->builtin_code);
if (supported_nodes) {
supported_nodes->push_back(node_index);
}
} else {
if (failures_by_node) {
(*failures_by_node)[node_index] = std::move(map_failures);
}
}
}
return kTfLiteOk;
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,106 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_COMPATIBILITY_LIB_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_COMPATIBILITY_LIB_H_
#include <cstdint>
#include <map>
#include <vector>
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
// WARNING: this header file is DEPRECATED.
// See https://developer.android.com/ndk/guides/neuralnetworks/migration-guide.
namespace tflite {
namespace tools {
// Check if the given TFLite flatbuffer model is compatible with NNAPI delegate.
// WARNING: This is an experimental API and subject to change.
TfLiteStatus CheckCompatibility(
TfLiteContext* context, int32_t runtime_feature_level,
std::vector<int>* supported_nodes,
std::map<int, std::vector<tflite::delegate::nnapi::NNAPIValidationFailure>>*
failures_by_node);
// This utility delegate is required because some TfLiteContext related
// functions are forbidden if not calling in delegate.
// WARNING: This is an experimental class and subject to change.
class CompatibilityCheckerDelegate : public TfLiteDelegate {
public:
explicit CompatibilityCheckerDelegate(int32_t runtime_feature_level)
: TfLiteDelegate(TfLiteDelegateCreate()),
runtime_feature_level_(runtime_feature_level),
supported_nodes_(),
failures_by_node_() {
Prepare = DoPrepare;
CopyFromBufferHandle = DoCopyFromBufferHandle;
CopyToBufferHandle = DoCopyToBufferHandle;
FreeBufferHandle = DoFreeBufferHandle;
data_ = &delegate_data_;
}
std::vector<int> GetSupportedNodes() { return supported_nodes_; }
std::map<int, std::vector<tflite::delegate::nnapi::NNAPIValidationFailure>>
GetFailuresByNode() {
return failures_by_node_;
}
protected:
static TfLiteStatus DoPrepare(TfLiteContext* context,
TfLiteDelegate* delegate) {
auto self = reinterpret_cast<CompatibilityCheckerDelegate*>(delegate);
TF_LITE_ENSURE_OK(context,
CheckCompatibility(context, self->runtime_feature_level_,
&(self->supported_nodes_),
&(self->failures_by_node_)));
return kTfLiteOk;
}
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyFromBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// This function is not expected to be called in this delegate.
static TfLiteStatus DoCopyToBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle buffer_handle,
TfLiteTensor* tensor) {
return kTfLiteError;
}
// There is no buffer handle in this delegate.
static void DoFreeBufferHandle(TfLiteContext* context,
TfLiteDelegate* delegate,
TfLiteBufferHandle* handle) {}
private:
int delegate_data_;
int runtime_feature_level_;
std::vector<int> supported_nodes_;
std::map<int, std::vector<tflite::delegate::nnapi::NNAPIValidationFailure>>
failures_by_node_;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_COMPATIBILITY_LIB_H_
@@ -0,0 +1,92 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_compatibility_lib.h"
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
namespace tools {
namespace {
void CompatibilityCheckerDelegateDelete(TfLiteDelegate* delegate) {
delete static_cast<CompatibilityCheckerDelegate*>(delegate);
}
class AddOpModel : public SingleOpModel {
public:
AddOpModel(const TensorData& input1, const TensorData& input2,
const TensorData& output, ActivationFunctionType activation_type,
std::unique_ptr<CompatibilityCheckerDelegate> checker_delegate) {
input1_ = AddInput(input1);
input2_ = AddInput(input2);
output_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_ADD, BuiltinOptions_AddOptions,
CreateAddOptions(builder_, activation_type).Union());
SetDelegate(
{checker_delegate.release(), CompatibilityCheckerDelegateDelete});
// Builds interpreter and applies delegate.
BuildInterpreter({GetShape(input1_), GetShape(input2_)});
}
protected:
int input1_;
int input2_;
int output_;
};
} // namespace
TEST(NnapiDelegateCompabilityTest, InvalidInput) {
EXPECT_EQ(CheckCompatibility(nullptr, 0, nullptr, nullptr), kTfLiteError);
}
TEST(NnapiDelegateCompabilityTest, CompatibleModel) {
auto checker_delegate = std::make_unique<CompatibilityCheckerDelegate>(
tflite::delegate::nnapi::kMinSdkVersionForNNAPI13);
auto* checker_delegate_ptr = checker_delegate.get();
AddOpModel add_op_model({TensorType_FLOAT32, {1, 2, 2, 1}},
{TensorType_FLOAT32, {1, 2, 2, 1}},
{TensorType_FLOAT32, {}}, ActivationFunctionType_NONE,
std::move(checker_delegate));
EXPECT_EQ(checker_delegate_ptr->GetSupportedNodes().size(), 1);
EXPECT_EQ(checker_delegate_ptr->GetFailuresByNode().size(), 0);
}
TEST(NnapiDelegateCompabilityTest, IncompatibleModel) {
auto checker_delegate = std::make_unique<CompatibilityCheckerDelegate>(
tflite::delegate::nnapi::kMinSdkVersionForNNAPI13);
auto* checker_delegate_ptr = checker_delegate.get();
// No activation function is supported for INT32 tensor type.
AddOpModel add_op_model(
{TensorType_INT32, {1, 2, 2, 1}}, {TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {}}, ActivationFunctionType_RELU_N1_TO_1,
std::move(checker_delegate));
EXPECT_EQ(checker_delegate_ptr->GetSupportedNodes().size(), 0);
EXPECT_EQ(checker_delegate_ptr->GetFailuresByNode().size(), 1);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,278 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_delegate_compatibility_checker.h"
#include <cctype>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "tensorflow/lite/core/interpreter_builder.h"
#include "tensorflow/lite/core/kernels/register.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/nnapi/NeuralNetworksTypes.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_util.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/online_helper_delegate.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
namespace tflite {
namespace tools {
namespace {
// Gets canonical feature level enum value from the number. In case the
// feature level is not between 1 and 8, the default value (8) would be
// assigned.
void getCanonicalFeatureLevel(int runtime_feature_level,
int& canonical_feature_level) {
switch (runtime_feature_level) {
case 1:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_1;
break;
case 2:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_2;
break;
case 3:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_3;
break;
case 4:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_4;
break;
case 5:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_5;
break;
case 6:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_6;
break;
case 7:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_7;
break;
case 8:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_8;
break;
default:
canonical_feature_level = ANEURALNETWORKS_FEATURE_LEVEL_8;
}
}
// Checks if the input string is a valid feature level. To be a valid feature
// level, the string passed as a parameter should contain only one digit
// between 1 and 8.
absl::Status IsValidFeatureLevelInt(const std::string& s) {
if (s.size() == 1 && std::isdigit(s[0]) && s[0] > '0' && s[0] < '9') {
return absl::OkStatus();
}
return absl::InvalidArgumentError("Invalid runtime feature level.");
}
// Gets the runtime feature level from the configurations and convert its
// value to an integer.
absl::Status extractRuntimeFeatureLevel(
const std::unordered_map<std::string, std::string>& dcc_configs,
int& runtime_feature_level) {
std::string str_runtime_feature_level;
if (dcc_configs.find("nnapi-runtime_feature_level") == dcc_configs.end()) {
for (const auto& dcc_config : dcc_configs) {
// If an NNAPI parameter is set, but spelled incorrectly, return an error.
if (absl::StrContains(dcc_config.first, "nnapi")) {
return absl::InvalidArgumentError(
"The correct flag name is 'nnapi-runtime_feature_level");
}
}
// Use default as no NNAPI parameter is set.
str_runtime_feature_level =
std::to_string(tools::kDefaultRuntimeFeatureLevel);
} else {
str_runtime_feature_level = dcc_configs.at("nnapi-runtime_feature_level");
RETURN_IF_ERROR(IsValidFeatureLevelInt(str_runtime_feature_level));
}
runtime_feature_level = std::stoi(str_runtime_feature_level);
return absl::OkStatus();
}
// Converts NnapiValidationFailureType to compatibilityFailureType to store
// the error in op_result.
absl::Status convertToCompatibilityFailureType(
std::vector<delegate::nnapi::NNAPIValidationFailure> map_failures,
proto::OpCompatibilityResult* op_result) {
for (const auto& status : map_failures) {
auto compatibility_failure = op_result->add_compatibility_failures();
compatibility_failure->set_description(status.message);
switch (status.type) {
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOperator:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERATOR);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedAndroidVersion:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_VERSION);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedOperatorVersion:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERATOR_VERSION);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedInputType:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_INPUT_TYPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kNotRestrictedScaleCompliant:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::
DCC_NOT_RESTRICTED_SCALE_COMPLIANT);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOutputType:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OUTPUT_TYPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOperandSize:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERAND_SIZE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedOperandValue:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERAND_VALUE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedHybridOperator:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_HYBRID_OPERATOR);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedQuantizationType:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_QUANTIZATION_TYPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::kMissingRequiredOperand:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_MISSING_REQUIRED_OPERAND);
break;
case delegate::nnapi::NNAPIValidationFailureType::kUnsupportedOperandRank:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERAND_RANK);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kInputTensorShouldHaveConstantShape:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::
DCC_INPUT_TENSOR_SHOULD_HAVE_CONSTANT_SHAPE);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedOperatorVariant:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_UNSUPPORTED_OPERATOR_VARIANT);
break;
case delegate::nnapi::NNAPIValidationFailureType::kNoActivationExpected:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_NO_ACTIVATION_EXPECTED);
break;
case delegate::nnapi::NNAPIValidationFailureType::
kUnsupportedQuantizationParameters:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::
DCC_UNSUPPORTED_QUANTIZATION_PARAMETERS);
break;
default:
compatibility_failure->set_failure_type(
proto::CompatibilityFailureType::DCC_INTERNAL_ERROR);
compatibility_failure->set_description(
"Unknown validation failure type.");
}
}
return absl::OkStatus();
}
} // namespace
absl::Status
tools::NnapiDelegateCompatibilityChecker::checkOpCompatibilityOnline(
TfLiteContext* context, const TfLiteNode* node,
const TfLiteRegistration* registration,
std::unordered_map<std::string, std::string> dcc_configs,
tflite::proto::OpCompatibilityResult* op_result) {
std::vector<delegate::nnapi::NNAPIValidationFailure> map_failures;
int runtime_feature_level;
RETURN_IF_ERROR(
extractRuntimeFeatureLevel(dcc_configs, runtime_feature_level));
getCanonicalFeatureLevel(runtime_feature_level, runtime_feature_level);
if (NNAPIDelegateKernel::Validate(
context, registration, runtime_feature_level, node,
/* is_accelerator_specified= */ true,
/* vendor_plugin= */ nullptr, &map_failures)) {
op_result->set_is_supported(true);
} else {
RETURN_IF_ERROR(convertToCompatibilityFailureType(map_failures, op_result));
op_result->set_is_supported(false);
}
return absl::OkStatus();
}
std::unordered_map<std::string, std::string>
tools::NnapiDelegateCompatibilityChecker::getDccConfigurations() {
std::unordered_map<std::string, std::string> dcc_configs;
dcc_configs["nnapi-runtime_feature_level"] =
std::to_string(runtime_feature_level_);
return dcc_configs;
}
absl::Status tools::NnapiDelegateCompatibilityChecker::setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) {
RETURN_IF_ERROR(
extractRuntimeFeatureLevel(dcc_configs, runtime_feature_level_));
return absl::OkStatus();
}
absl::Status
tools::NnapiDelegateCompatibilityChecker::checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) {
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::ops::builtin::BuiltinOpResolver resolver;
tflite::InterpreterBuilder interpreter_builder(*model_buffer, resolver);
auto dcc_configs = getDccConfigurations();
std::function<absl::Status(TfLiteContext*, const TfLiteNode*,
const TfLiteRegistration*,
std::unordered_map<std::string, std::string>,
proto::OpCompatibilityResult*)>
check_op_func_ptr = &checkOpCompatibilityOnline;
OnlineHelperDelegate delegate(dcc_configs, check_op_func_ptr, result);
interpreter_builder.AddDelegate(&delegate);
interpreter_builder(&interpreter);
return absl::OkStatus();
}
// TODO(b/243489631): Implement the function.
absl::Status tools::NnapiDelegateCompatibilityChecker::checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) {
return absl::UnimplementedError(
"Offline mode is not yet supported on NNAPI delegate compatibility "
"checker.");
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,92 @@
/* Copyright 2022 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_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_DELEGATE_COMPATIBILITY_CHECKER_H_
#define TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_DELEGATE_COMPATIBILITY_CHECKER_H_
#include <string>
#include <unordered_map>
#include "absl/status/status.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/tools/delegates/compatibility/common/delegate_compatibility_checker_base.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
#include "tensorflow/lite/tools/versioning/op_signature.h"
// WARNING: this header file is DEPRECATED.
// See https://developer.android.com/ndk/guides/neuralnetworks/migration-guide.
namespace tflite {
namespace tools {
// Default runtime feature level used when no parameters are specified.
inline constexpr int kDefaultRuntimeFeatureLevel = 8;
// Class to check if an operation or a model is compatible with NNAPI delegate.
// Supported parameters:
// - nnapi-runtime_feature_level: Between 1 and 8 (default value: 8)
class NnapiDelegateCompatibilityChecker
: public DelegateCompatibilityCheckerBase {
public:
NnapiDelegateCompatibilityChecker() {
runtime_feature_level_ = kDefaultRuntimeFeatureLevel;
}
absl::Status checkModelCompatibilityOnline(
tflite::FlatBufferModel* model_buffer,
tflite::proto::CompatibilityResult* result) override;
// Checks if the node is compatible with the NNAPI delegate using online mode.
// Params:
// context: Used to get the tensors. TfLiteTensors can be obtained via
// TfLiteContext, which are used to get tensor type and tensor data,
// the same way as with OpSignature in Offline mode.
// node: Used with context to get the desired tensor, e.g.:
// context->tensors[node->inputs->data[0]]
// registration: Used to get the builtin code and the operator version.
// op_result: Used to store if the node is compatible with the delegate or
// not and why (with a human readable message).
static absl::Status checkOpCompatibilityOnline(
TfLiteContext* context, const TfLiteNode* node,
const TfLiteRegistration* registration,
std::unordered_map<std::string, std::string> dcc_configs,
tflite::proto::OpCompatibilityResult* op_result);
// Returns a dictionary with NNAPI delegate specific params.
// Keys:
// - nnapi-runtime_feature_level
std::unordered_map<std::string, std::string> getDccConfigurations() override;
// Sets the parameters needed in the specific DCC.
// Keys:
// - nnapi-runtime_feature_level
absl::Status setDccConfigurations(
const std::unordered_map<std::string, std::string>& dcc_configs) override;
private:
absl::Status checkOpSigCompatibility(
const OpSignature& op_sig,
tflite::proto::OpCompatibilityResult* op_result) override;
// Runtime feature level
// Refer to '/tensorflow/lite/nnapi/NeuralNetworksTypes.h'
int runtime_feature_level_;
};
} // namespace tools
} // namespace tflite
#endif // TENSORFLOW_LITE_TOOLS_DELEGATES_COMPATIBILITY_NNAPI_NNAPI_DELEGATE_COMPATIBILITY_CHECKER_H_
@@ -0,0 +1,184 @@
/* Copyright 2022 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/tools/delegates/compatibility/nnapi/nnapi_delegate_compatibility_checker.h"
#include <cstdint>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/status/status.h"
#include "tensorflow/core/platform/resource_loader.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/model_builder.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/tools/delegates/compatibility/protos/compatibility_result.pb.h"
namespace tflite {
namespace tools {
#ifndef EXPECT_OK
#define EXPECT_OK(x) EXPECT_TRUE(x.ok());
#endif
namespace {
class AddOpModel : public SingleOpModel {
public:
AddOpModel(const TensorData& input1, const TensorData& input2,
const TensorData& output, ActivationFunctionType activation_type) {
input1_ = AddInput(input1);
input2_ = AddInput(input2);
output_ = AddOutput(output);
SetBuiltinOp(BuiltinOperator_ADD, BuiltinOptions_AddOptions,
CreateAddOptions(builder_, activation_type).Union());
// Builds interpreter.
BuildInterpreter({GetShape(input1_), GetShape(input2_)});
}
protected:
int input1_;
int input2_;
int output_;
};
// Class to test the NNAPI delegate compatibility checker (DCC)
class NnapiDccTest : public ::testing::Test {
protected:
void SetUp() override {}
void TearDown() override { compatibility_result_.Clear(); }
NnapiDelegateCompatibilityChecker nnapi_dcc_;
proto::CompatibilityResult compatibility_result_;
};
} // namespace
TEST_F(NnapiDccTest, ValidRuntimeFeatureLevel) {
std::unordered_map dcc_configs = nnapi_dcc_.getDccConfigurations();
EXPECT_EQ(dcc_configs["nnapi-runtime_feature_level"], "8");
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
dcc_configs["nnapi-runtime_feature_level"] = "1";
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
dcc_configs["nnapi-runtime_feature_level"] = "8";
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
dcc_configs.clear();
EXPECT_OK(nnapi_dcc_.setDccConfigurations(dcc_configs));
EXPECT_EQ(nnapi_dcc_.getDccConfigurations()["nnapi-runtime_feature_level"],
"8");
}
TEST_F(NnapiDccTest, InvalidRuntimeFeatureLevel) {
std::unordered_map dcc_configs = nnapi_dcc_.getDccConfigurations();
dcc_configs["nnapi-runtime_feature_level"] = "03";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "a";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "28123497123489123841212344516";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "30.0";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "-30";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs["nnapi-runtime_feature_level"] = "9";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
dcc_configs.clear();
dcc_configs["nnapi-runtim_feature_level"] = "8";
EXPECT_EQ(nnapi_dcc_.setDccConfigurations(dcc_configs).code(),
absl::StatusCode::kInvalidArgument);
}
TEST_F(NnapiDccTest, CompatibleModelOnlineMode) {
const std::string& full_path =
tensorflow::GetDataDependencyFilepath("tensorflow/lite/testdata/add.bin");
auto fb_model = FlatBufferModel::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
auto model = fb_model->GetModel();
EXPECT_EQ(model->subgraphs()->size(), 1);
EXPECT_EQ(model->subgraphs()->Get(0)->operators()->size(), 2);
EXPECT_OK(nnapi_dcc_.checkModelCompatibilityOnline(fb_model.get(),
&compatibility_result_));
for (auto op_compatibility_result :
compatibility_result_.compatibility_results()) {
EXPECT_TRUE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result_.compatibility_results_size(), 2);
}
TEST_F(NnapiDccTest, IncompatibleModelOperation) {
// No activation function is supported for INT32 tensor type.
AddOpModel add_op_model(
{TensorType_INT32, {1, 2, 2, 1}}, {TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {}}, ActivationFunctionType_RELU_N1_TO_1);
auto fb_model = tflite::FlatBufferModel::BuildFromModel(
tflite::GetModel(add_op_model.GetModelBuffer()));
ASSERT_TRUE(fb_model);
EXPECT_OK(nnapi_dcc_.checkModelCompatibilityOnline(fb_model.get(),
&compatibility_result_));
for (auto op_compatibility_result :
compatibility_result_.compatibility_results()) {
EXPECT_FALSE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result_.compatibility_results_size(), 1);
}
TEST_F(NnapiDccTest, IncompatibleModelFeatureLevel) {
// INT32 input tensor type is not supported if runtime feature level < 4.
AddOpModel add_op_model({TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {1, 2, 2, 1}},
{TensorType_INT32, {}}, ActivationFunctionType_NONE);
auto fb_model = tflite::FlatBufferModel::BuildFromModel(
tflite::GetModel(add_op_model.GetModelBuffer()));
ASSERT_TRUE(fb_model);
auto nnapi_configs = nnapi_dcc_.getDccConfigurations();
nnapi_configs["nnapi-runtime_feature_level"] = "2";
EXPECT_OK(nnapi_dcc_.setDccConfigurations(nnapi_configs));
EXPECT_OK(nnapi_dcc_.checkModelCompatibilityOnline(fb_model.get(),
&compatibility_result_));
for (auto op_compatibility_result :
compatibility_result_.compatibility_results()) {
EXPECT_FALSE(op_compatibility_result.is_supported());
}
EXPECT_EQ(compatibility_result_.compatibility_results_size(), 1);
}
} // namespace tools
} // namespace tflite
@@ -0,0 +1,17 @@
load(
"//tensorflow/core/platform:build_config.bzl",
"tf_proto_library",
)
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
tf_proto_library(
name = "compatibility_result",
srcs = [
"compatibility_result.proto",
],
)
@@ -0,0 +1,138 @@
// Copyright 2022 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.
syntax = "proto2";
package tflite.proto;
enum CompatibilityFailureType {
// Quantization scale and/or zero point are not in the supported
// value(s) for the accelerated operation.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_QUANTIZATION_PARAMETERS = 0;
// Indicates that the caller specified an invalid argument, such as
// incorrect stride values.
// Applied DDC(s): GPU
DCC_INVALID_ARGUMENT = 1;
// Indicates an internal error has occurred and some invariants
// expected by the underlying system have not been satisfied, such as
// expecting different number of input or ouput tensors.
// Applied DDC(s): GPU
DCC_INTERNAL_ERROR = 2;
// Indicates the operation is not implemented or supported in this
// service. In this case, the operation should not be re-attempted.
// Applied DDC(s): GPU
DCC_UNIMPLEMENTED_ERROR = 3;
// Indicates the operation was attempted past the valid range, such
// as requesting an index that goes beyond the array size.
// Applied DDC(s): GPU
DCC_OUT_OF_RANGE = 4;
// The operator is not supported by the Delegate.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERATOR = 5;
// The given operation or operands are not supported on the
// specified runtime feature level. The min supported version is specified in
// the compatibility failure message.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_VERSION = 6;
// The version of the operator (value of OpSignature.version)
// for the given op is not supported. The max supported version
// is specified in the compatibility failure message.
// For more details on each operator version see
// the GetBuiltinOperatorVersion function in
// tensorflow/lite/tools/versioning/op_version.cc.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERATOR_VERSION = 7;
// The given input operand type is not supported for the current
// combination of operator type and runtime feature level.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_INPUT_TYPE = 8;
// When using NN API version 1.0 or 1.1, the condition
// input_scale * filter_scale < output_scale
// must be true for quantized versions of the following ops:
// * CONV_2D
// * DEPTHWISE_CONV_2D
// * FULLY_CONNECTED (where filter actually stands for weights)
// The condition is relaxed and no longer required since version 1.2.
// Applied DDC(s): NNAPI
DCC_NOT_RESTRICTED_SCALE_COMPLIANT = 9;
// The given output operand type is not supported for the current
// combination of operator type and runtime feature level.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OUTPUT_TYPE = 10;
// The size of the operand tensor is too large.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERAND_SIZE = 11;
// The value of one of the operands or of a combination of operands
// is not supported. Details are provided in the compatibility failure
// message.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERAND_VALUE = 12;
// The combination of float inputs and quantized weights or filters
// is not supported.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_HYBRID_OPERATOR = 13;
// The quantization type (for example per-channel quantization) is
// not supported.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_QUANTIZATION_TYPE = 14;
// The accelerated version of operation requires a specific operand
// to be specified.
// Applied DDC(s): NNAPI
DCC_MISSING_REQUIRED_OPERAND = 15;
// The rank of the operand is not supported. Details in the
// compatibility failure message.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERAND_RANK = 16;
// The input tensor cannot be dynamically-sized.
// Applied DDC(s): NNAPI
DCC_INPUT_TENSOR_SHOULD_HAVE_CONSTANT_SHAPE = 17;
// The operator has a different number of inputs of the one or ones
// that are supported by NNAPI.
// Applied DDC(s): NNAPI
DCC_UNSUPPORTED_OPERATOR_VARIANT = 18;
// The accelerated version of the operator cannot specify an
// activation function.
// Applied DDC(s): NNAPI
DCC_NO_ACTIVATION_EXPECTED = 19;
}
// Indicates the type and a human readable text for an error in an operation.
message CompatibilityFailure {
// Type of the errors.
optional CompatibilityFailureType failure_type = 1;
// Human readable message explaining the error.
optional string description = 2;
}
// Result for one operation of the given model and stores if the operation
// is supported. If it is supported, validation_failures will not have a value.
// If it is not supported, validation_failures will contain all the errors for
// that operation. Also saves the subgraph index inside the model and the
// operator index inside the subgraph.
message OpCompatibilityResult {
// True if the operation is supported for the required DCC.
optional bool is_supported = 1;
// Index of the subgraph where this operation is contained.
optional int32 subgraph_index_in_model = 2;
// Index of the operator inside the subgraph.
optional int32 operator_index_in_subgraph = 3;
// Type of the errors.
repeated CompatibilityFailure compatibility_failures = 4;
}
message CompatibilityResult {
// One result for each operation.
repeated OpCompatibilityResult compatibility_results = 1;
}