Files
tensorflow--tensorflow/tensorflow/lite/kernels/subgraph_test_util.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

2094 lines
75 KiB
C++

/* Copyright 2019 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/subgraph_test_util.h"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <numeric>
#include <random>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/core/c/builtin_op_data.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/kernels/builtin_op_kernels.h"
#include "tensorflow/lite/core/subgraph.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/string_util.h"
namespace tflite {
// Forward declaration for op kernels.
namespace ops {
namespace custom {
namespace random_int {
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
TF_LITE_ENSURE_EQ(context, NumInputs(node), 0);
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
TfLiteTensor* output = GetOutput(context, node, 0);
TfLiteIntArray* outputSize = TfLiteIntArrayCreate(1);
outputSize->data[0] = 1;
return context->ResizeTensor(context, output, outputSize);
}
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
TfLiteTensor& output = context->tensors[node->outputs->data[0]];
std::random_device rd;
std::uniform_int_distribution<int> dist(1, 32768);
output.data.i32[0] = dist(rd);
return kTfLiteOk;
}
} // namespace random_int
TfLiteRegistration* Register_RANDOM_INT() {
static TfLiteRegistration r = {nullptr, nullptr, random_int::Prepare,
random_int::Eval};
return &r;
}
} // namespace custom
} // namespace ops
namespace subgraph_test_util {
namespace {
void AddTileNode(Subgraph* subgraph, int input0, int input1, int output) {
int node_index;
auto* tile_reg = ops::builtin::Register_TILE();
tile_reg->builtin_code = kTfLiteBuiltinTile;
subgraph->AddNodeWithParameters({input0, input1}, {output}, {}, nullptr, 0,
nullptr, tile_reg, &node_index);
}
void AddFlexNode(Subgraph* subgraph, int input_tensor, int output_tensor) {
auto prepare = [](TfLiteContext* context, TfLiteNode* node) {
TfLiteTensor& input = context->tensors[node->inputs->data[0]];
TfLiteTensor& output = context->tensors[node->outputs->data[0]];
TfLiteArrayUniquePtr<int> shape =
BuildTfLiteArray(input.dims->size, input.dims->data);
return context->ResizeTensor(context, &output, shape.release());
};
auto eval = [](TfLiteContext* context, TfLiteNode* node) {
TfLiteTensor& input = context->tensors[node->inputs->data[0]];
TfLiteTensor& output = context->tensors[node->outputs->data[0]];
memcpy(output.data.data, input.data.data, input.bytes);
return kTfLiteOk;
};
TfLiteRegistration reg = {nullptr, nullptr, prepare, eval};
reg.builtin_code = BuiltinOperator_CUSTOM;
reg.custom_name = "Flex";
int node_index;
ASSERT_EQ(
subgraph->AddNodeWithParameters({input_tensor}, {output_tensor}, {},
nullptr, 0, nullptr, &reg, &node_index),
kTfLiteOk);
}
void AddReshapeNode(Subgraph* subgraph, int input0, int input1, int output) {
int node_index;
TfLiteReshapeParams* reshape_params = reinterpret_cast<TfLiteReshapeParams*>(
calloc(1, sizeof(TfLiteReshapeParams)));
auto* reshape_reg = ops::builtin::Register_RESHAPE();
reshape_reg->builtin_code = kTfLiteBuiltinReshape;
ASSERT_EQ(subgraph->AddNodeWithParameters({input0, input1}, {output}, {},
nullptr, 0, reshape_params,
reshape_reg, &node_index),
kTfLiteOk);
}
void AddOffsetAddNode(Subgraph* subgraph, int input0, int input1, int output) {
auto prepare = [](TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor& input0 = context->tensors[node->inputs->data[0]];
TfLiteTensor& output = context->tensors[node->outputs->data[0]];
TfLiteIntArray* shape = TfLiteIntArrayCopy(input0.dims);
return context->ResizeTensor(context, &output, shape);
};
auto invoke = [](TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor& input0 = context->tensors[node->inputs->data[0]];
const TfLiteTensor& input1 = context->tensors[node->inputs->data[1]];
TfLiteTensor& output = context->tensors[node->outputs->data[0]];
int num_elements = input0.dims->data[0];
const int kOffset = 1;
const int* i0 = static_cast<int*>(input0.data.data);
const int* i1 = static_cast<int*>(input1.data.data);
int* o = static_cast<int*>(output.data.data);
for (int i = 0; i < num_elements; ++i) {
int input0_pos = (i + kOffset) % num_elements;
o[i] = i0[input0_pos] + i1[i];
}
return kTfLiteOk;
};
int node_index;
TfLiteRegistration offset_add_reg = {/*Init=*/nullptr, /*Free=*/nullptr,
prepare, invoke};
offset_add_reg.builtin_code = BuiltinOperator_CUSTOM;
offset_add_reg.custom_name = "OffsetAdd";
offset_add_reg.inplace_operator = kTfLiteInplaceOpInput1Shared;
subgraph->AddNodeWithParameters({input0, input1}, {output}, {}, nullptr, 0,
nullptr, &offset_add_reg, &node_index);
}
void AddAddNode(Subgraph* subgraph, int input0, int input1, int output) {
int node_index;
TfLiteAddParams* add_params =
reinterpret_cast<TfLiteAddParams*>(calloc(1, sizeof(TfLiteAddParams)));
auto* add_reg = ops::builtin::Register_ADD();
add_reg->builtin_code = kTfLiteBuiltinAdd;
subgraph->AddNodeWithParameters({input0, input1}, {output}, {}, nullptr, 0,
add_params, add_reg, &node_index);
}
// Add a DYNAMIC_UPDATE_SLICE node to the subgraph.
void AddDynamicUpdateSliceNode(Subgraph* subgraph, int input0, int input1,
int input2, int output) {
int node_index;
auto* reg = ops::builtin::Register_DYNAMIC_UPDATE_SLICE();
reg->builtin_code = kTfLiteBuiltinDynamicUpdateSlice;
subgraph->AddNodeWithParameters({input0, input1, input2}, {output}, {},
nullptr, 0, nullptr, reg, &node_index);
}
} // namespace
void Setup1DTensor(Subgraph* subgraph, int tensor_index, TfLiteType type) {
int dim = 1;
ASSERT_EQ(subgraph->SetTensorParametersReadWrite(tensor_index, type, "", 1,
&dim, {}, false),
kTfLiteOk);
}
void SetupTensor(Subgraph* subgraph, int tensor_index, TfLiteType type) {
ASSERT_EQ(subgraph->SetTensorParametersReadWrite(tensor_index, type, "", 0,
nullptr, {}, false),
kTfLiteOk);
}
SubgraphBuilder::~SubgraphBuilder() {
for (auto buffer : buffers_) {
free(buffer);
}
}
void SubgraphBuilder::BuildInplaceDynamicUpdateSliceSubgraph(
Subgraph& subgraph, bool multiple_consumers) {
enum {
kInput0,
kInput1,
kInput2,
kConstRhs,
kOutput,
kIntermediate0,
kIntermediate1,
kTensorCount
};
int first_new_tensor_index;
ASSERT_EQ(subgraph.AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph.SetInputs({kInput0, kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph.SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(&subgraph, i, kTfLiteInt32);
}
// kInput0 --> +---+
// |ADD| --> kIntermediate0 --> +---+
// kConstRhs --> +---+ | kInput1 --> |DUS| --> kIntermediate1
// | | kInput2 --> +---+ |
// | | +----> +---+
// |if one consumer |if multiple consumers |ADD|
// +---------------------+-------------------------------------> +---+
CreateConstantTensor(&subgraph, kConstRhs, {1}, {1});
AddAddNode(&subgraph, kInput0, kConstRhs, kIntermediate0);
AddDynamicUpdateSliceNode(&subgraph, kIntermediate0, kInput1, kInput2,
kIntermediate1);
AddAddNode(&subgraph, kIntermediate1,
multiple_consumers ? kIntermediate0 : kConstRhs, kOutput);
}
void SubgraphBuilder::BuildInputDynamicUpdateSliceSubgraph(Subgraph& subgraph) {
enum {
kInput0,
kInput1,
kInput2,
kConstRhs,
kOutput,
kIntermediate0,
kTensorCount
};
int first_new_tensor_index;
ASSERT_EQ(subgraph.AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph.SetInputs({kInput0, kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph.SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(&subgraph, i, kTfLiteInt32);
}
// kInput0 --> +---+
// kInput1 --> |DUS| --> kIntermediate0 --> +---+
// kInput2 --> +---+ |ADD| --> kOutput
// kConstRhs --> +---+
CreateConstantTensor(&subgraph, kConstRhs, {1}, {1});
AddDynamicUpdateSliceNode(&subgraph, kInput0, kInput1, kInput2,
kIntermediate0);
AddAddNode(&subgraph, kIntermediate0, kConstRhs, kOutput);
}
void SubgraphBuilder::BuildOutputNotConsumedSubgraph(Subgraph& subgraph) {
enum {
kInput0,
kInput1,
kInput2,
kOutput0,
kOutput1,
kConstRhs,
kTensorCount
};
int first_new_tensor_index;
ASSERT_EQ(subgraph.AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph.SetInputs({kInput0, kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph.SetOutputs({kOutput0, kOutput1, kConstRhs}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
Setup1DTensor(&subgraph, i, kTfLiteInt32);
}
// kInput0 --> +---+
// kInput1 --> |DUS| --> kIntermediate0 --> +---+
// kInput2 --> +---+ |ADD| --> kOutput
// kConstRhs --> +---+
CreateConstantTensor(&subgraph, kConstRhs, {1}, {1});
AddAddNode(&subgraph, kInput0, kInput1, kOutput0);
AddTileNode(&subgraph, kInput0, kInput2, kOutput1);
}
void SubgraphBuilder::BuildXNNPACKSubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue,
kOutputCounter,
kOutputValue,
kIntermediateTensor0,
kIntermediateTensor1,
kTensorCount
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteFloat32);
}
AddAddNode(subgraph, kInputCounter, kInputValue, kIntermediateTensor0);
AddAddNode(subgraph, kInputCounter, kInputValue, kIntermediateTensor1);
AddAddNode(subgraph, kIntermediateTensor0, kIntermediateTensor1,
kOutputCounter);
AddAddNode(subgraph, kIntermediateTensor0, kIntermediateTensor1,
kOutputValue);
}
void SubgraphBuilder::BuildInputIsOutputSubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue0,
kInputOutput,
kOutputCounter,
kOutputValue0,
kConstRhs,
kTensorCount
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue0, kInputOutput}),
kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue0, kInputOutput}),
kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstRhs, {1}, {1});
AddAddNode(subgraph, kInputCounter, kConstRhs, kOutputCounter);
AddAddNode(subgraph, kInputValue0, kInputOutput, kOutputValue0);
}
void SubgraphBuilder::BuildInputIsDifferentOutputSubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue,
kOutputCounter,
kOutputValue,
kTensorCount
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kInputValue, kOutputValue}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
AddAddNode(subgraph, kInputCounter, kInputValue, kOutputValue);
}
void SubgraphBuilder::BuildFlexOutputSubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue,
kOutputCounter,
kOutputValue,
kConstRhs,
kIntermediateTensor,
kTensorCount
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstRhs, {1}, {1});
AddAddNode(subgraph, kInputCounter, kConstRhs, kOutputCounter);
AddAddNode(subgraph, kConstRhs, kInputValue, kIntermediateTensor);
AddFlexNode(subgraph, kIntermediateTensor, kOutputValue);
}
void SubgraphBuilder::BuildCounterOnlySubgraph(Subgraph* subgraph) {
enum { kInputCounter, kOutputCounter, kConstRhs, kTensorCount };
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstRhs, {1}, {1});
AddAddNode(subgraph, kInputCounter, kConstRhs, kOutputCounter);
}
void SubgraphBuilder::BuildAddSubgraph(Subgraph* subgraph,
const TfLiteType operand_type) {
TfLiteAddParams* params =
reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_ADD, kTfLiteBuiltinAdd,
params, operand_type, operand_type, operand_type);
}
void SubgraphBuilder::BuildStablehloAddSubgraph(Subgraph* subgraph,
const TfLiteType operand_type) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_STABLEHLO_ADD,
kTfLiteBuiltinStablehloAdd, nullptr, operand_type,
operand_type, operand_type);
}
// This body subgraph has arena and dynamic output tensors which are not in
// place to verify that body subgraph outputs are written directly to node
// outputs. It also has inplace dynamic and arena outputs.
void SubgraphBuilder::BuildAllInplaceScenariosSubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue0,
kInputValue1,
kInputValue2,
kOutputCounter,
kOutputValue0,
kOutputValue1,
kOutputValue2,
kIntermediateTensor0,
kIntermediateTensor1,
kInputOutputTensor,
kTensorCount
};
// kInputCounter --> +---+
// |Add| --> kOutputCounter
// kConstRhs ------> +---+
//
// kInputValue1 --> +------+
// | TILE | -> kOutputValue1 ------|
// kInputCounter -> +------+ v
// +-----+
// kInputValue0 --> +-----+ | Add |->kOutputValue0
// | Add | -> kIntermediateTensor->+-----+
// kConstRhs -----> +-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue0, kInputValue1,
kInputValue2, kInputOutputTensor}),
kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue0, kOutputValue1,
kOutputValue2, kInputOutputTensor}),
kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kInputOutputTensor, {1}, {1});
// Arena, not in place.
AddAddNode(subgraph, kInputCounter, kInputOutputTensor, kOutputCounter);
// Arena, in place.
AddAddNode(subgraph, kInputValue0, kInputOutputTensor, kIntermediateTensor0);
AddAddNode(subgraph, kIntermediateTensor0, kInputOutputTensor, kOutputValue0);
// Dynamic, not in place.
AddTileNode(subgraph, kInputValue1, kInputCounter, kOutputValue1);
// Dynamic, in place.
AddTileNode(subgraph, kInputValue2, kInputCounter, kIntermediateTensor1);
AddAddNode(subgraph, kIntermediateTensor1, kInputOutputTensor, kOutputValue2);
}
void SubgraphBuilder::BuildDynamicOpTriggersAllocationOfUnsedInputSubgraph(
Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue0,
kInputValue1,
kOutputCounter,
kOutputValue0,
kOutputValue1,
kIntermediateTensor0,
kIntermediateTensor1,
kConstRhs,
kTensorCount
};
// kInputCounter --> +---+
// |Add| --> kOutputCounter
// kConstRhs ------> +---+
//
// kInputValue1 --> +------+
// | TILE | -> kOutputValue1 ------|
// kInputCounter -> +------+ v
// +-----+
// kInputValue0 --> +-----+ | Add |->kOutputValue0
// | Add | -> kIntermediateTensor->+-----+
// kConstRhs -----> +-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue0, kInputValue1}),
kTfLiteOk);
ASSERT_EQ(
subgraph->SetOutputs({kOutputCounter, kOutputValue0, kOutputValue1}),
kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstRhs, {1}, {1});
AddAddNode(subgraph, kInputCounter, kConstRhs, kOutputCounter);
AddTileNode(subgraph, kInputValue1, kInputCounter, kOutputValue1);
AddAddNode(subgraph, kInputValue0, kConstRhs, kIntermediateTensor0);
AddAddNode(subgraph, kIntermediateTensor0, kOutputValue1, kOutputValue0);
}
void SubgraphBuilder::BuildBinaryOpSubgraph(
Subgraph* subgraph, TfLiteRegistration* (*Register_OP)(),
const TfLiteBuiltinOperator builtin_code, void* const params,
const TfLiteType input1_type, const TfLiteType input2_type,
const TfLiteType output_type) {
enum { kInput1, kInput2, kOutput, kTensorCount };
// kInput1(0) --> +---+
// | OP| --> kOutput(2)
// kInput2(1) --> +---+
ASSERT_NE(Register_OP, nullptr);
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
SetupTensor(subgraph, kInput1, input1_type);
SetupTensor(subgraph, kInput2, input2_type);
SetupTensor(subgraph, kOutput, output_type);
TfLiteRegistration* reg = Register_OP();
reg->builtin_code = builtin_code;
int node_index;
subgraph->AddNodeWithParameters({kInput1, kInput2}, {kOutput}, {}, nullptr, 0,
params, reg, &node_index);
}
void SubgraphBuilder::BuildMaximumSubgraph(Subgraph* subgraph,
const TfLiteType operand_type) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_MAXIMUM,
kTfLiteBuiltinMaximum, /*params=*/nullptr,
/*input1_type=*/operand_type,
/*input2_type=*/operand_type,
/*output_type=*/operand_type);
}
void SubgraphBuilder::BuildStablehloMaximumSubgraph(
Subgraph* subgraph, const TfLiteType operand_type) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_STABLEHLO_MAXIMUM,
kTfLiteBuiltinStablehloMaximum, nullptr, operand_type,
operand_type, operand_type);
}
void SubgraphBuilder::BuildMinimumSubgraph(Subgraph* subgraph,
const TfLiteType operand_type) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_MINIMUM,
kTfLiteBuiltinMinimum, /*params=*/nullptr,
/*input1_type=*/operand_type,
/*input2_type=*/operand_type,
/*output_type=*/operand_type);
}
void SubgraphBuilder::BuildStablehloMinimumSubgraph(
Subgraph* subgraph, const TfLiteType operand_type) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_STABLEHLO_MINIMUM,
kTfLiteBuiltinStablehloMinimum, nullptr, operand_type,
operand_type, operand_type);
}
void SubgraphBuilder::BuildLogicalOrSubgraph(Subgraph* subgraph) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_LOGICAL_OR,
kTfLiteBuiltinLogicalOr, /*params=*/nullptr,
/*input1_type=*/kTfLiteBool,
/*input2_type=*/kTfLiteBool,
/*output_type=*/kTfLiteBool);
}
void SubgraphBuilder::BuildLogicalAndSubgraph(Subgraph* subgraph) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_LOGICAL_AND,
kTfLiteBuiltinLogicalAnd, /*params=*/nullptr,
/*input1_type=*/kTfLiteBool,
/*input2_type=*/kTfLiteBool,
/*output_type=*/kTfLiteBool);
}
void SubgraphBuilder::BuildOutputIsSecondInputSubgraph(Subgraph* subgraph) {
const int kInput1 = 0;
const int kInput2 = 1;
const int kTensorCount = 2;
// kInput1(0) --> x
// | --> kOutput(2)
// kInput2(1) --> ----^
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kInput2}), kTfLiteOk);
SetupTensor(subgraph, kInput1, kTfLiteInt32);
SetupTensor(subgraph, kInput2, kTfLiteInt32);
}
// Build a subgraph with an mul op. Helper function for testing.
void SubgraphBuilder::BuildMulSubgraph(Subgraph* subgraph,
TfLiteType operand_type) {
TfLiteMulParams* params =
reinterpret_cast<TfLiteMulParams*>(malloc(sizeof(TfLiteMulParams)));
params->activation = kTfLiteActNone;
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_MUL, kTfLiteBuiltinMul,
params, /*input1_type=*/operand_type,
/*input2_type=*/operand_type,
/*output_type=*/operand_type);
}
void SubgraphBuilder::BuildStablehloMulSubgraph(Subgraph* subgraph,
const TfLiteType operand_type) {
BuildBinaryOpSubgraph(subgraph, ops::builtin::Register_STABLEHLO_MULTIPLY,
kTfLiteBuiltinStablehloMultiply, nullptr, operand_type,
operand_type, operand_type);
}
// Build a subgraph with a pad op. Helper function for testing.
void SubgraphBuilder::BuildPadSubgraph(Subgraph* subgraph) {
const int kInput1 = 0;
const int kInput2 = 1;
const int kOutput = 2;
const int kTensorCount = 3;
// kInput1(0) --> +---+
// |PAD| --> kOutput(2)
// kInput2(1) --> +---+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
SetupTensor(subgraph, kInput1, kTfLiteInt32);
SetupTensor(subgraph, kInput2, kTfLiteInt32);
SetupTensor(subgraph, kOutput, kTfLiteInt32);
TfLitePadParams* params =
reinterpret_cast<TfLitePadParams*>(malloc(sizeof(TfLitePadParams)));
auto* pad_reg = ops::builtin::Register_PAD();
pad_reg->builtin_code = kTfLiteBuiltinPad;
int node_index;
subgraph->AddNodeWithParameters({kInput1, kInput2}, {kOutput}, {}, nullptr, 0,
params, pad_reg, &node_index);
}
void SubgraphBuilder::BuildIfSubgraph(Subgraph* subgraph) {
const int kCondInput = 0;
const int kInput1 = 1;
const int kInput2 = 2;
const int kOutput = 3;
const int kTensorCount = 4;
// kCondInput(0) --> +----+
// kInput1(1) ----> | IF | --> kOutput(3)
// kInput2(2) ----> +----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kCondInput, kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
SetupTensor(subgraph, kCondInput, kTfLiteBool);
SetupTensor(subgraph, kInput1, kTfLiteInt32);
SetupTensor(subgraph, kInput2, kTfLiteInt32);
SetupTensor(subgraph, kOutput, kTfLiteInt32);
TfLiteIfParams* params =
reinterpret_cast<TfLiteIfParams*>(malloc(sizeof(TfLiteIfParams)));
params->then_subgraph_index = 1;
params->else_subgraph_index = 2;
auto* if_reg = ops::builtin::Register_IF();
if_reg->builtin_code = kTfLiteBuiltinIf;
int node_index;
subgraph->AddNodeWithParameters({kCondInput, kInput1, kInput2}, {kOutput}, {},
nullptr, 0, params, if_reg, &node_index);
}
void SubgraphBuilder::BuildCompositeSubgraph(Subgraph* subgraph,
const Subgraph* decomposition) {
// +-----------+
// kInputs ----> | COMPOSITE | --> kOutput
// +-----------+
// | ^
// v |
// DECOMPOSITION
const int decomposition_subgraph_index = decomposition->GetSubgraphIndex();
const auto& inputs = decomposition->inputs();
const auto& outputs = decomposition->outputs();
const int decomposition_tensor_count = inputs.size() + outputs.size();
std::vector<int> subgraph_inputs(inputs.size());
std::iota(begin(subgraph_inputs), end(subgraph_inputs), 0);
std::vector<int> subgraph_outputs(outputs.size());
std::iota(begin(subgraph_outputs), end(subgraph_outputs), inputs.size());
int first_new_tensor_index;
ASSERT_EQ(
subgraph->AddTensors(decomposition_tensor_count, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs(subgraph_inputs), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(subgraph_outputs), kTfLiteOk);
for (size_t i = 0; i < inputs.size(); ++i) {
const TfLiteTensor* src = decomposition->tensor(inputs[i]);
SetupTensor(subgraph, i, src->type);
}
for (size_t i = 0; i < outputs.size(); ++i) {
const TfLiteTensor* src = decomposition->tensor(outputs[i]);
SetupTensor(subgraph, inputs.size() + i, src->type);
}
TfLiteStablehloCompositeParams* params =
reinterpret_cast<TfLiteStablehloCompositeParams*>(
malloc(sizeof(TfLiteStablehloCompositeParams)));
params->name = "test_composite";
params->subgraph_index = decomposition_subgraph_index;
params->attributes = nullptr;
params->attributes_size = 0;
params->version = 1;
auto* composite_reg = ops::builtin::Register_STABLEHLO_COMPOSITE();
composite_reg->builtin_code = kTfLiteBuiltinStablehloComposite;
int node_index;
subgraph->AddNodeWithParameters(subgraph_inputs, subgraph_outputs, {},
nullptr, 0, params, composite_reg,
&node_index);
}
void SubgraphBuilder::BuildLargeLessEqualCondSubgraph(Subgraph* subgraph,
int rhs, int num_inputs) {
const int kOutput = 0;
const int kConstRhs = 1;
const int kInput0 = 2;
// kInput1(0) ----> +------------+
// | LESS_EQUAL | --> kOutput(2)
// kConstRhs(3) --> +------------+
//
// kInput2(1) --> (unused)
int tensor_count = 3 + num_inputs;
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(tensor_count, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = kInput0 + i;
}
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < num_inputs; ++i) {
SetupTensor(subgraph, kInput0 + i, kTfLiteInt32);
}
SetupTensor(subgraph, kOutput, kTfLiteBool);
auto* le_reg = ops::builtin::Register_LESS_EQUAL();
le_reg->builtin_code = kTfLiteBuiltinLessEqual;
CreateConstantTensor(subgraph, kConstRhs, {1}, {rhs});
int node_index;
subgraph->AddNodeWithParameters({kInput0, kConstRhs}, {kOutput}, {}, nullptr,
0, nullptr, le_reg, &node_index);
}
void SubgraphBuilder::BuildOffsetAddSharing(Subgraph* subgraph) {
enum {
kInput0,
kInput1,
kIntermediateTensor0,
kIntermediateTensor1,
kIntermediateTensor2,
kOutput,
kConstRhs,
kTensorCount,
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput0, kInput1}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor<int>(subgraph, kConstRhs, {1}, {1});
// Consume input so that following ops can share tensor memory.
AddAddNode(subgraph, kInput0, kInput1, kIntermediateTensor0);
AddAddNode(subgraph, kInput0, kInput1, kIntermediateTensor1);
// Input1 may be shared but not input0.
AddOffsetAddNode(subgraph, kIntermediateTensor0, kIntermediateTensor1,
kIntermediateTensor2);
AddAddNode(subgraph, kIntermediateTensor2, kConstRhs, kOutput);
}
void SubgraphBuilder::BuildBroadcastingSubgraph(Subgraph* subgraph) {
enum {
kInput0,
kInput1,
kIntermediateTensor0,
kIntermediateTensor1,
kIntermediateTensor2,
kIntermediateTensor3,
kOutput,
kConstRhs,
kTensorCount,
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput0, kInput1}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor<int>(subgraph, kConstRhs, {1}, {1});
// Consume input so that following ops can share tensor memory.
AddAddNode(subgraph, kInput0, kInput1, kIntermediateTensor0);
// Sharing is not possible as there is more than one consumer of
// kIntermediateTensor0.
AddAddNode(subgraph, kIntermediateTensor0, kIntermediateTensor0,
kIntermediateTensor1);
// Broadcasting ADD with sharing input1. kIntermediateTensor2 will share the
// same memory as kIntermediateTensor0 and kIntermediateTensor1.
AddAddNode(subgraph, kConstRhs, kIntermediateTensor1, kIntermediateTensor2);
AddAddNode(subgraph, kIntermediateTensor2, kConstRhs, kIntermediateTensor3);
// Consume this output to allow sharing.
AddAddNode(subgraph, kIntermediateTensor3, kConstRhs, kOutput);
}
void SubgraphBuilder::BuildInplaceOpSubgraph(Subgraph* subgraph) {
enum {
kInput0,
kInput1,
kIntermediateTensor0,
kIntermediateTensor1,
kOutput,
kConstRhs,
kTensorCount,
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput0, kInput1}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor<int>(subgraph, kConstRhs, {1}, {2});
// Consume input as Reshape cannot share subgraph input.
AddAddNode(subgraph, kInput0, kInput1, kIntermediateTensor0);
// Shape tensor is constant so that output is arena allocated.
AddReshapeNode(subgraph, kIntermediateTensor0, kConstRhs,
kIntermediateTensor1);
// Consume output of Reshape as subgraph outputs cannot be shared.
AddAddNode(subgraph, kIntermediateTensor1, kInput1, kOutput);
}
void SubgraphBuilder::BuildFloatLessCondSubgraph(Subgraph* subgraph,
float rhs) {
enum {
kInput1,
kInput2,
kOutput,
kConstRhs,
kTensorCount,
};
// kInput1(0) ----> +------------+
// | LESS_EQUAL | --> kOutput(2)
// kConstRhs(3) --> +------------+
//
// kInput2(1) --> (unused)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
auto* le_reg = ops::builtin::Register_LESS();
le_reg->builtin_code = kTfLiteBuiltinLess;
CreateConstantTensor<float>(subgraph, kConstRhs, {1}, {rhs});
int node_index;
subgraph->AddNodeWithParameters({kInput1, kConstRhs}, {kOutput}, {}, nullptr,
0, nullptr, le_reg, &node_index);
}
void SubgraphBuilder::BuildLessEqualCondSubgraph(Subgraph* subgraph, int rhs) {
enum {
kInput1,
kInput2,
kOutput,
kConstRhs,
kTensorCount,
};
// kInput1(0) ----> +------------+
// | LESS_EQUAL | --> kOutput(2)
// kConstRhs(3) --> +------------+
//
// kInput2(1) --> (unused)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
for (int i = 0; i < kTensorCount - 1; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
SetupTensor(subgraph, kOutput, kTfLiteBool);
auto* le_reg = ops::builtin::Register_LESS_EQUAL();
le_reg->builtin_code = kTfLiteBuiltinLessEqual;
CreateConstantTensor(subgraph, kConstRhs, {1}, {rhs});
int node_index;
subgraph->AddNodeWithParameters({kInput1, kConstRhs}, {kOutput}, {}, nullptr,
0, nullptr, le_reg, &node_index);
}
void SubgraphBuilder::BuildLargeBodySubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue,
kOutputCounter,
kOutputValue,
kConstStep,
kConstSum,
kIntermediateTensor0,
kTensorCount,
};
// kInputCounter(0) --> +-----+
// | ADD | -> kIntermediateTensor0(6)
// kInputValue(1) ----> +-----+ |
// v
// +-----+
// | SUM | --> kOutputCounter(2)
// kConstSum(4) -----------------------> +-----+
// |
// v
// +-----+
// | ADD | --> kOutputValue(3)
// kConstStep(4) ---------------------------+-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstSum, {1}, {-1});
CreateConstantTensor(subgraph, kConstStep, {1}, {4});
int node_index;
TfLiteAddParams* params =
reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
params->pot_scale_int16 = false;
auto* add_reg0 = ops::builtin::Register_ADD();
add_reg0->builtin_code = kTfLiteBuiltinAdd;
auto* add_reg1 = ops::builtin::Register_ADD();
add_reg1->builtin_code = kTfLiteBuiltinAdd;
subgraph->AddNodeWithParameters({kInputCounter, kInputValue},
{kIntermediateTensor0}, {}, nullptr, 0,
params, add_reg0, &node_index);
auto* sum_reg = ops::builtin::Register_SUM();
sum_reg->builtin_code = kTfLiteBuiltinSum;
TfLiteReducerParams* sum_params = reinterpret_cast<TfLiteReducerParams*>(
calloc(1, sizeof(TfLiteReducerParams)));
subgraph->AddNodeWithParameters({kInputValue, kConstSum}, {kOutputCounter},
{}, nullptr, 0, sum_params, sum_reg,
&node_index);
params = reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
params->pot_scale_int16 = false;
subgraph->AddNodeWithParameters({kIntermediateTensor0, kConstStep},
{kOutputValue}, {}, nullptr, 0, params,
add_reg1, &node_index);
}
void SubgraphBuilder::BuildDynamicBodySubgraphWithAliases(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue0,
kInputValue1,
kInputValue2,
kInputValue3,
kOutputCounter,
kOutputValue0,
kOutputValue1,
kOutputValue2,
kOutputValue3,
kConstSum0,
kConstSum1,
kConstSum2,
kIntermediateTensor0,
kIntermediateTensor1,
kIntermediateTensor2,
kTensorCount,
};
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue0, kInputValue1,
kInputValue2, kInputValue3}),
kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue0, kOutputValue1,
kOutputValue2, kOutputValue3}),
kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstSum0, {1}, {1});
CreateConstantTensor(subgraph, kConstSum1, {1}, {2});
CreateConstantTensor(subgraph, kConstSum2, {1}, {3});
AddAddNode(subgraph, kInputCounter, kConstSum0, kOutputCounter);
AddAddNode(subgraph, kInputValue0, kInputValue1, kIntermediateTensor0);
AddAddNode(subgraph, kInputValue2, kInputValue3, kIntermediateTensor1);
AddAddNode(subgraph, kIntermediateTensor0, kIntermediateTensor1,
kIntermediateTensor2);
AddAddNode(subgraph, kIntermediateTensor2, kConstSum0, kOutputValue0);
AddAddNode(subgraph, kIntermediateTensor2, kConstSum1, kOutputValue1);
AddAddNode(subgraph, kIntermediateTensor2, kConstSum2, kOutputValue2);
AddAddNode(subgraph, kIntermediateTensor2, kConstSum2, kOutputValue3);
}
void SubgraphBuilder::BuildDynamicIncreasingSizeSubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue0,
kInputValue1,
kInputValue2,
kOutputCounter,
kOutputValue0,
kOutputValue1,
kOutputValue2,
kConstSum,
kIntermediateTensor0,
kIntermediateTensor1,
kIntermediateTensor2,
kTensorCount,
};
// kInputCounter -----> +-----+
// | ADD | -> kIntermediateTensor0 -> Add ->
// kOutputCounter
// kConstsum ---------> +-----+
//
// kInputValue0 ------> +-----+
// | ADD | -> kIntermediateTensor0(6)
// kInputValue1 ----> +-----+ |
// v
// +-----+
// | ADD | --> kOutputValue0
// kConstSum(4) -----------------------> +-----+
// |
// v
// +-----+
// | PAD | --> kOutputValue1
// kConstStep(4) ---------------------------+-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs(
{kInputCounter, kInputValue0, kInputValue1, kInputValue2}),
kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(
{kOutputCounter, kOutputValue0, kOutputValue1, kOutputValue2}),
kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstSum, {1}, {1});
AddAddNode(subgraph, kInputCounter, kConstSum, kOutputCounter);
AddTileNode(subgraph, kInputValue0, kInputCounter, kIntermediateTensor0);
AddAddNode(subgraph, kInputValue1, kConstSum, kIntermediateTensor1);
AddAddNode(subgraph, kInputValue2, kConstSum, kIntermediateTensor2);
AddAddNode(subgraph, kIntermediateTensor0, kConstSum, kOutputValue0);
AddAddNode(subgraph, kIntermediateTensor1, kConstSum, kOutputValue1);
AddAddNode(subgraph, kIntermediateTensor2, kConstSum, kOutputValue2);
}
void SubgraphBuilder::BuildLargePadSubgraph(Subgraph* subgraph,
const std::vector<int> padding) {
enum {
kInputCounter,
kInputValue0,
kInputValue1,
kOutputCounter,
kOutputValue0,
kOutputValue1,
kConstPadding,
kConstSum,
kIntermediateTensor0,
kIntermediateTensor1,
kTensorCount,
};
// kInputCounter -> +-----+
// | ADD | -> kIntermediateTensor0 -> Add -> kOutputCounter
// kConstsum -----> +-----+
//
// kInputValue0 ------> +-----+
// | ADD | -> kIntermediateTensor0(6)
// kInputValue1 ----> +-----+ |
// v
// +-----+
// | ADD | --> kOutputValue0
// kConstSum(4) -----------------------> +-----+
// |
// v
// +-----+
// | PAD | --> kOutputValue1
// kConstStep(4) ---------------------------+-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue0, kInputValue1}),
kTfLiteOk);
ASSERT_EQ(
subgraph->SetOutputs({kOutputCounter, kOutputValue0, kOutputValue1}),
kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstSum, {1}, {1});
ASSERT_EQ(padding.size() % 2, 0);
int padding_dims = padding.size();
CreateConstantTensor(subgraph, kConstPadding, {1, padding_dims}, padding);
AddAddNode(subgraph, kInputCounter, kConstSum, kIntermediateTensor0);
AddAddNode(subgraph, kInputCounter, kInputValue1, kIntermediateTensor1);
AddAddNode(subgraph, kIntermediateTensor0, kConstSum, kOutputCounter);
AddAddNode(subgraph, kIntermediateTensor1, kConstSum, kOutputValue0);
int node_index;
auto* pad_reg = ops::builtin::Register_PAD();
pad_reg->builtin_code = kTfLiteBuiltinPad;
TfLitePadParams* pad_params =
reinterpret_cast<TfLitePadParams*>(calloc(1, sizeof(TfLitePadParams)));
subgraph->AddNodeWithParameters({kOutputValue0, kConstPadding},
{kOutputValue1}, {}, nullptr, 0, pad_params,
pad_reg, &node_index);
}
void SubgraphBuilder::BuildDeepBodySubgraph(Subgraph* subgraph) {
enum {
kInputCounter,
kInputValue,
kOutputCounter,
kOutputValue,
kConstStep,
kIntermediateTensor,
kTensorCount,
};
// kInputCounter ---> +-----+
// | ADD | -> kOutputCounter
// kConstStep ----> +-----+ |
// v
// +-----+
// | ADD |
// kInputValue ------------------> +-----+
// | kIntermediateTensor
// v
// +-----+
// | ADD | --> kOutputValue
// kConstStep ----------------------+-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
CreateConstantTensor(subgraph, kConstStep, {1}, {1});
int node_index;
TfLiteAddParams* params =
reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
params->pot_scale_int16 = false;
auto* add_reg0 = ops::builtin::Register_ADD();
add_reg0->builtin_code = kTfLiteBuiltinAdd;
auto* add_reg1 = ops::builtin::Register_ADD();
add_reg1->builtin_code = kTfLiteBuiltinAdd;
auto* add_reg2 = ops::builtin::Register_ADD();
add_reg2->builtin_code = kTfLiteBuiltinAdd;
subgraph->AddNodeWithParameters({kInputCounter, kConstStep}, {kOutputCounter},
{}, nullptr, 0, params, add_reg0,
&node_index);
params = reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
params->pot_scale_int16 = false;
subgraph->AddNodeWithParameters({kOutputCounter, kInputValue},
{kIntermediateTensor}, {}, nullptr, 0, params,
add_reg1, &node_index);
params = reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
params->pot_scale_int16 = false;
subgraph->AddNodeWithParameters({kIntermediateTensor, kConstStep},
{kOutputValue}, {}, nullptr, 0, params,
add_reg2, &node_index);
}
void SubgraphBuilder::BuildAccumulateLoopBodySubgraph(Subgraph* subgraph) {
const int kInputCounter = 0;
const int kInputValue = 1;
const int kOutputCounter = 2;
const int kOutputValue = 3;
const int kConstStep = 4;
const int kTensorCount = 5;
// kInputCounter(0) --> +-----+
// | ADD | --> kOutputCounter(2)
// kConstStep(4) -----> +-----+ |
// |
// v
// +-----+
// | ADD | --> kOutputValue(3)
// kInputValue(1) ----------------------+-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue}), kTfLiteOk);
SetupTensor(subgraph, kInputCounter, kTfLiteInt32);
SetupTensor(subgraph, kInputValue, kTfLiteInt32);
SetupTensor(subgraph, kOutputCounter, kTfLiteInt32);
SetupTensor(subgraph, kOutputValue, kTfLiteInt32);
CreateConstantTensor(subgraph, kConstStep, {1}, {1});
int node_index;
TfLiteAddParams* params =
reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
params->pot_scale_int16 = false;
auto* add_reg = ops::builtin::Register_ADD();
add_reg->builtin_code = kTfLiteBuiltinAdd;
subgraph->AddNodeWithParameters({0, 4}, {2}, {}, nullptr, 0, params, add_reg,
&node_index);
params = reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
params->activation = kTfLiteActNone;
params->pot_scale_int16 = false;
subgraph->AddNodeWithParameters({2, 1}, {3}, {}, nullptr, 0, params, add_reg,
&node_index);
}
void SubgraphBuilder::BuildPadLoopBodySubgraph(
Subgraph* subgraph, const std::vector<int>& padding) {
const int kInputCounter = 0;
const int kInputValue = 1;
const int kOutputCounter = 2;
const int kOutputValue = 3;
const int kConstStep = 4;
const int kConstPadding = 5;
const int kTensorCount = 6;
// kInputCounter(0) --> +-----+
// | ADD | --> kOutputCounter(2)
// kConstStep(4) -----> +-----+
//
// kInputValue(1) ----> +-----+
// | PAD | --> kOutputValue(3)
// kConstPadding(5) --> +-----+
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInputCounter, kInputValue}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutputCounter, kOutputValue}), kTfLiteOk);
SetupTensor(subgraph, kInputCounter, kTfLiteInt32);
SetupTensor(subgraph, kInputValue, kTfLiteInt32);
SetupTensor(subgraph, kOutputCounter, kTfLiteInt32);
SetupTensor(subgraph, kOutputValue, kTfLiteInt32);
CreateConstantTensor(subgraph, kConstStep, {1}, {1});
ASSERT_EQ(padding.size() % 2, 0);
int padding_dims = padding.size();
CreateConstantTensor(subgraph, kConstPadding, {1, padding_dims}, padding);
int node_index;
TfLiteAddParams* add_params =
reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
add_params->activation = kTfLiteActNone;
auto* add_reg = ops::builtin::Register_ADD();
add_reg->builtin_code = kTfLiteBuiltinAdd;
subgraph->AddNodeWithParameters({kInputCounter, kConstStep}, {kOutputCounter},
{}, nullptr, 0, add_params, add_reg,
&node_index);
TfLitePadParams* pad_params =
reinterpret_cast<TfLitePadParams*>(malloc(sizeof(TfLiteAddParams)));
auto* pad_reg = ops::builtin::Register_PAD();
pad_reg->builtin_code = kTfLiteBuiltinPad;
subgraph->AddNodeWithParameters({kInputValue, kConstPadding}, {kOutputValue},
{}, nullptr, 0, pad_params, pad_reg,
&node_index);
}
void SubgraphBuilder::BuildOutputNotConsumedIfSubgraph(Subgraph* subgraph) {
enum {
kInput0,
kInput1,
kInput2,
kInput3,
kOutput0,
kOutput1,
kOutput2,
kTensorCount
};
int num_inputs = 4;
int num_outputs = 3;
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_outputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
}
for (int i = 0; i < num_outputs; ++i) {
output_tensors[i] = i + num_inputs;
}
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
SetupTensor(subgraph, input_tensors[0], kTfLiteBool);
for (int i = 1; i < num_inputs; ++i) {
SetupTensor(subgraph, input_tensors[i], kTfLiteInt32);
}
for (int i = 0; i < num_outputs; ++i) {
SetupTensor(subgraph, output_tensors[i], kTfLiteInt32);
}
TfLiteIfParams* params =
reinterpret_cast<TfLiteIfParams*>(malloc(sizeof(TfLiteIfParams)));
params->then_subgraph_index = 1;
params->else_subgraph_index = 2;
auto* if_reg = ops::builtin::Register_IF();
if_reg->builtin_code = kTfLiteBuiltinIf;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, if_reg, &node_index);
}
void SubgraphBuilder::BuildOutputNotConsumedWhileSubgraph(Subgraph* subgraph) {
enum {
kInput0,
kInput1,
kInput2,
kOutput0,
kOutput1,
kOutput2,
kTensorCount
};
// kInput1(0) --> +-------+ --> kOutput1(2)
// | WHILE |
// kInput2(1) --> +-------+ --> kOutput2(3)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput0, kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput0}), kTfLiteOk);
for (int i = 0; i < kTensorCount; ++i) {
SetupTensor(subgraph, i, kTfLiteInt32);
}
TfLiteWhileParams* params =
reinterpret_cast<TfLiteWhileParams*>(malloc(sizeof(TfLiteWhileParams)));
params->cond_subgraph_index = 1;
params->body_subgraph_index = 2;
auto* while_reg = ops::builtin::Register_WHILE();
while_reg->builtin_code = kTfLiteBuiltinWhile;
int node_index;
subgraph->AddNodeWithParameters({0, 1, 2}, {3, 4, 5}, {}, nullptr, 0, params,
while_reg, &node_index);
}
void SubgraphBuilder::BuildFloatIfSubgraph(Subgraph* subgraph, int num_inputs) {
int num_outputs = num_inputs - 1;
int first_new_tensor_index;
ASSERT_EQ(
subgraph->AddTensors(num_inputs + num_outputs, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_outputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
}
for (int i = 0; i < num_outputs; ++i) {
output_tensors[i] = i + num_inputs;
}
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
SetupTensor(subgraph, input_tensors[0], kTfLiteBool);
for (int i = 1; i < num_inputs; ++i) {
SetupTensor(subgraph, input_tensors[i], kTfLiteFloat32);
}
for (int i = 0; i < num_outputs; ++i) {
SetupTensor(subgraph, output_tensors[i], kTfLiteFloat32);
}
TfLiteIfParams* params =
reinterpret_cast<TfLiteIfParams*>(malloc(sizeof(TfLiteWhileParams)));
params->then_subgraph_index = 1;
params->else_subgraph_index = 2;
auto* if_reg = ops::builtin::Register_IF();
if_reg->builtin_code = kTfLiteBuiltinIf;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, if_reg, &node_index);
}
void SubgraphBuilder::BuildFloatWhileSubgraph(Subgraph* subgraph,
int num_inputs) {
// kInput1(0) --> +-------+ --> kOutput1(2)
// | WHILE |
// kInput2(1) --> +-------+ --> kOutput2(3)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(num_inputs * 2, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_inputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
output_tensors[i] = i + num_inputs;
}
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
for (int i = 0; i < num_inputs; ++i) {
SetupTensor(subgraph, input_tensors[i], kTfLiteFloat32);
SetupTensor(subgraph, output_tensors[i], kTfLiteFloat32);
}
TfLiteWhileParams* params =
reinterpret_cast<TfLiteWhileParams*>(malloc(sizeof(TfLiteWhileParams)));
params->cond_subgraph_index = 1;
params->body_subgraph_index = 2;
auto* while_reg = ops::builtin::Register_WHILE();
while_reg->builtin_code = kTfLiteBuiltinWhile;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, while_reg, &node_index);
}
void SubgraphBuilder::BuildMultiInputIfSubgraphWithUnconsumedOutput(
Subgraph* subgraph, int num_inputs) {
int num_outputs = num_inputs - 1;
int first_new_tensor_index;
ASSERT_EQ(
subgraph->AddTensors(num_inputs + num_outputs, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_outputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
}
for (int i = 0; i < num_outputs; ++i) {
output_tensors[i] = i + num_inputs;
}
SetupTensor(subgraph, input_tensors[0], kTfLiteBool);
for (int i = 1; i < num_inputs; ++i) {
SetupTensor(subgraph, input_tensors[i], kTfLiteInt32);
}
for (int i = 0; i < num_outputs; ++i) {
SetupTensor(subgraph, output_tensors[i], kTfLiteInt32);
}
TfLiteIfParams* params =
reinterpret_cast<TfLiteIfParams*>(malloc(sizeof(TfLiteIfParams)));
params->then_subgraph_index = 1;
params->else_subgraph_index = 2;
auto* if_reg = ops::builtin::Register_IF();
if_reg->builtin_code = kTfLiteBuiltinIf;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, if_reg, &node_index);
output_tensors.pop_back();
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
}
void SubgraphBuilder::BuildMultiInputWhileSubgraphWithUnconsumedOutput(
Subgraph* subgraph, int num_inputs) {
// kInput1(0) --> +-------+ --> kOutput1(2)
// | WHILE |
// kInput2(1) --> +-------+ --> kOutput2(3)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(num_inputs * 2, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_inputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
output_tensors[i] = i + num_inputs;
}
for (int i = 0; i < num_inputs; ++i) {
SetupTensor(subgraph, input_tensors[i], kTfLiteInt32);
SetupTensor(subgraph, output_tensors[i], kTfLiteInt32);
}
TfLiteWhileParams* params =
reinterpret_cast<TfLiteWhileParams*>(malloc(sizeof(TfLiteWhileParams)));
params->cond_subgraph_index = 1;
params->body_subgraph_index = 2;
auto* while_reg = ops::builtin::Register_WHILE();
while_reg->builtin_code = kTfLiteBuiltinWhile;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, while_reg, &node_index);
output_tensors.pop_back();
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
}
void SubgraphBuilder::BuildMultiInputIfSubgraph(Subgraph* subgraph,
int num_inputs) {
int num_outputs = num_inputs - 1;
int first_new_tensor_index;
ASSERT_EQ(
subgraph->AddTensors(num_inputs + num_outputs, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_outputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
}
for (int i = 0; i < num_outputs; ++i) {
output_tensors[i] = i + num_inputs;
}
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
SetupTensor(subgraph, input_tensors[0], kTfLiteBool);
for (int i = 1; i < num_inputs; ++i) {
SetupTensor(subgraph, input_tensors[i], kTfLiteInt32);
}
for (int i = 0; i < num_outputs; ++i) {
SetupTensor(subgraph, output_tensors[i], kTfLiteInt32);
}
TfLiteIfParams* params =
reinterpret_cast<TfLiteIfParams*>(malloc(sizeof(TfLiteIfParams)));
params->then_subgraph_index = 1;
params->else_subgraph_index = 2;
auto* if_reg = ops::builtin::Register_IF();
if_reg->builtin_code = kTfLiteBuiltinIf;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, if_reg, &node_index);
}
void SubgraphBuilder::BuildMultiInputWhileSubgraph(Subgraph* subgraph,
int num_inputs) {
// kInput1(0) --> +-------+ --> kOutput1(2)
// | WHILE |
// kInput2(1) --> +-------+ --> kOutput2(3)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(num_inputs * 2, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_inputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
output_tensors[i] = i + num_inputs;
}
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
for (int i = 0; i < num_inputs; ++i) {
SetupTensor(subgraph, input_tensors[i], kTfLiteInt32);
SetupTensor(subgraph, output_tensors[i], kTfLiteInt32);
}
TfLiteWhileParams* params =
reinterpret_cast<TfLiteWhileParams*>(malloc(sizeof(TfLiteWhileParams)));
params->cond_subgraph_index = 1;
params->body_subgraph_index = 2;
auto* while_reg = ops::builtin::Register_WHILE();
while_reg->builtin_code = kTfLiteBuiltinWhile;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, while_reg, &node_index);
}
void SubgraphBuilder::BuildWhileSubgraph(Subgraph* subgraph) {
const int kInput1 = 0;
const int kInput2 = 1;
const int kOutput1 = 2;
const int kOutput2 = 3;
const int kTensorCount = 4;
// kInput1(0) --> +-------+ --> kOutput1(2)
// | WHILE |
// kInput2(1) --> +-------+ --> kOutput2(3)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kInput1, kInput2}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput1, kOutput2}), kTfLiteOk);
SetupTensor(subgraph, kInput1, kTfLiteInt32);
SetupTensor(subgraph, kInput2, kTfLiteInt32);
SetupTensor(subgraph, kOutput1, kTfLiteInt32);
SetupTensor(subgraph, kOutput2, kTfLiteInt32);
TfLiteWhileParams* params =
reinterpret_cast<TfLiteWhileParams*>(malloc(sizeof(TfLiteWhileParams)));
params->cond_subgraph_index = 1;
params->body_subgraph_index = 2;
auto* while_reg = ops::builtin::Register_WHILE();
while_reg->builtin_code = kTfLiteBuiltinWhile;
int node_index;
subgraph->AddNodeWithParameters({0, 1}, {2, 3}, {}, nullptr, 0, params,
while_reg, &node_index);
}
void SubgraphBuilder::BuildAssignRandomValueToVariableSubgraph(
Subgraph* subgraph) {
const int kConstResourceId = 0;
const int kRandomValue = 1;
const int kTensorCount = 3;
// Construct a graph like ths:
// %1 = random_int()
// variable_assign(%0, %1)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(subgraph->SetInputs({}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({}), kTfLiteOk);
SetupTensor(subgraph, kRandomValue, kTfLiteInt32);
CreateConstantTensor(subgraph, kConstResourceId, {1}, {1024});
int node_index;
subgraph->AddNodeWithParameters({}, {kRandomValue}, {}, nullptr, 0, nullptr,
::tflite::ops::custom::Register_RANDOM_INT(),
&node_index);
subgraph->AddNodeWithParameters(
{kConstResourceId, kRandomValue}, {}, {}, nullptr, 0, nullptr,
::tflite::ops::builtin::Register_ASSIGN_VARIABLE(), &node_index);
}
void SubgraphBuilder::BuildCallOnceAndReadVariableSubgraph(Subgraph* subgraph) {
const int kConstResourceId = 0;
const int kOutput = 1;
const int kTensorCount = 2;
// Construct a graph like ths:
// Output: %1
// %1 = read_variable(%0)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(subgraph->SetInputs({}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
SetupTensor(subgraph, kOutput, kTfLiteInt32);
CreateConstantTensor(subgraph, kConstResourceId, {1}, {1024});
TfLiteCallOnceParams* params = reinterpret_cast<TfLiteCallOnceParams*>(
malloc(sizeof(TfLiteCallOnceParams)));
params->init_subgraph_index = 1;
int node_index;
subgraph->AddNodeWithParameters({}, {}, {}, nullptr, 0, params,
::tflite::ops::builtin::Register_CALL_ONCE(),
&node_index);
subgraph->AddNodeWithParameters(
{kConstResourceId}, {kOutput}, {}, nullptr, 0, nullptr,
::tflite::ops::builtin::Register_READ_VARIABLE(), &node_index);
}
void SubgraphBuilder::BuildCallOnceAndReadVariablePlusOneSubgraph(
Subgraph* subgraph) {
const int kConstResourceId = 0;
const int kConstOne = 1;
const int kReadVariableResult = 2;
const int kOutput = 3;
const int kTensorCount = 4;
// Construct a graph like ths:
// Output: %3
// %2 = read_variable(%0)
// %3 = add(%2, %1)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(subgraph->SetInputs({}), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
SetupTensor(subgraph, kReadVariableResult, kTfLiteInt32);
SetupTensor(subgraph, kOutput, kTfLiteInt32);
CreateConstantTensor(subgraph, kConstResourceId, {1}, {1024});
CreateConstantTensor(subgraph, kConstOne, {1}, {1});
TfLiteCallOnceParams* params = reinterpret_cast<TfLiteCallOnceParams*>(
malloc(sizeof(TfLiteCallOnceParams)));
params->init_subgraph_index = 1;
int node_index;
subgraph->AddNodeWithParameters({}, {}, {}, nullptr, 0, params,
::tflite::ops::builtin::Register_CALL_ONCE(),
&node_index);
subgraph->AddNodeWithParameters(
{kConstResourceId}, {kReadVariableResult}, {}, nullptr, 0, nullptr,
::tflite::ops::builtin::Register_READ_VARIABLE(), &node_index);
TfLiteAddParams* add_params =
reinterpret_cast<TfLiteAddParams*>(malloc(sizeof(TfLiteAddParams)));
add_params->activation = kTfLiteActNone;
subgraph->AddNodeWithParameters(
{kReadVariableResult, kConstOne}, {kOutput}, {}, nullptr, 0, add_params,
::tflite::ops::builtin::Register_ADD(), &node_index);
}
void SubgraphBuilder::BuildLessEqualCondSubgraphWithDynamicTensor(
Subgraph* subgraph, int rhs) {
const int kStringInput1 = 0;
const int kStringInput2 = 1;
const int kIntegerInput = 2;
const int kOutput = 3;
const int kConstRhs = 4;
const int kTensorCount = 5;
// kIntegerInput(2) --> +------------+
// | LESS_EQUAL | --> kOutput(3)
// kConstRhs(4) --> +------------+
//
// kStringInput1(0) --> (unused)
// kStringInput2(1) --> (unused)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kStringInput1, kStringInput2, kIntegerInput}),
kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs({kOutput}), kTfLiteOk);
SetupTensor(subgraph, kStringInput1, kTfLiteString);
SetupTensor(subgraph, kStringInput2, kTfLiteString);
SetupTensor(subgraph, kIntegerInput, kTfLiteInt32);
SetupTensor(subgraph, kOutput, kTfLiteBool);
auto* le_reg = ops::builtin::Register_LESS_EQUAL();
le_reg->builtin_code = kTfLiteBuiltinLessEqual;
CreateConstantTensor(subgraph, kConstRhs, {1}, {rhs});
int node_index;
subgraph->AddNodeWithParameters({kIntegerInput, kConstRhs}, {kOutput}, {},
nullptr, 0, nullptr, le_reg, &node_index);
}
void SubgraphBuilder::BuildBodySubgraphWithDynamicTensor(Subgraph* subgraph) {
const int kStringInput1 = 0;
const int kStringInput2 = 1;
const int kIntegerInput = 2;
const int kStringOutput1 = 0; // Forwarded of the `kStringInput1` tensor.
const int kStringOutput2 = 4;
const int kIntegerOutput = 5;
const int kConst = 6;
const int kTensorCount = 7;
// Construct a graph like this:
// %5 = tf.Add(%2, 1)
// %4 = tf.Fill(%0, %5)
// yield(%0, %4, %5)
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kStringInput1, kStringInput2, kIntegerInput}),
kTfLiteOk);
ASSERT_EQ(
subgraph->SetOutputs({kStringOutput1, kStringOutput2, kIntegerOutput}),
kTfLiteOk);
SetupTensor(subgraph, kStringInput1, kTfLiteString);
SetupTensor(subgraph, kStringInput2, kTfLiteString);
SetupTensor(subgraph, kIntegerInput, kTfLiteInt32);
SetupTensor(subgraph, kStringOutput1, kTfLiteString);
SetupTensor(subgraph, kStringOutput2, kTfLiteString);
SetupTensor(subgraph, kIntegerOutput, kTfLiteInt32);
SetupTensor(subgraph, kConst, kTfLiteInt32);
CreateConstantTensor(subgraph, kConst, {1}, {1});
AddAddNode(subgraph, kIntegerInput, kConst, kIntegerOutput);
int node_index;
auto* fill_reg = ops::builtin::Register_FILL();
fill_reg->builtin_code = kTfLiteBuiltinFill;
subgraph->AddNodeWithParameters({kIntegerOutput, kStringInput1},
{kStringOutput2}, {}, nullptr, 0, nullptr,
fill_reg, &node_index);
}
void SubgraphBuilder::BuildIfSubgraphWithDynamicTensor(Subgraph* subgraph) {
enum {
kBoolInput0,
kStringInput1,
kStringInput2,
kIntegerInput,
kStringOutput1,
kStringOutput2,
kIntegerOutput,
kTensorCount
};
int num_inputs = 4;
int num_outputs = num_inputs - 1;
// Create a if op with 2 string tensor and 1 integer tensor.
int first_new_tensor_index;
std::vector<int> input_tensors(num_inputs);
std::vector<int> output_tensors(num_outputs);
for (int i = 0; i < num_inputs; ++i) {
input_tensors[i] = i;
}
for (int i = 0; i < num_outputs; ++i) {
output_tensors[i] = i + num_inputs;
}
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs(input_tensors), kTfLiteOk);
ASSERT_EQ(subgraph->SetOutputs(output_tensors), kTfLiteOk);
SetupTensor(subgraph, kBoolInput0, kTfLiteBool);
SetupTensor(subgraph, kStringInput1, kTfLiteString);
SetupTensor(subgraph, kStringInput2, kTfLiteString);
SetupTensor(subgraph, kIntegerInput, kTfLiteInt32);
SetupTensor(subgraph, kStringOutput1, kTfLiteString);
SetupTensor(subgraph, kStringOutput2, kTfLiteString);
SetupTensor(subgraph, kIntegerOutput, kTfLiteInt32);
TfLiteIfParams* params =
reinterpret_cast<TfLiteIfParams*>(malloc(sizeof(TfLiteWhileParams)));
params->then_subgraph_index = 1;
params->else_subgraph_index = 2;
auto* if_reg = ops::builtin::Register_IF();
if_reg->builtin_code = kTfLiteBuiltinIf;
int node_index;
subgraph->AddNodeWithParameters(input_tensors, output_tensors, {}, nullptr, 0,
params, if_reg, &node_index);
}
void SubgraphBuilder::BuildWhileSubgraphWithDynamicTensor(Subgraph* subgraph) {
const int kStringInput1 = 0;
const int kStringInput2 = 1;
const int kIntegerInput = 2;
const int kStringOutput1 = 3;
const int kStringOutput2 = 4;
const int kIntegerOutput = 5;
const int kTensorCount = 6;
// Create a while op with 2 string tensor and 1 integer tensor.
int first_new_tensor_index;
ASSERT_EQ(subgraph->AddTensors(kTensorCount, &first_new_tensor_index),
kTfLiteOk);
ASSERT_EQ(first_new_tensor_index, 0);
ASSERT_EQ(subgraph->SetInputs({kStringInput1, kStringInput2, kIntegerInput}),
kTfLiteOk);
ASSERT_EQ(
subgraph->SetOutputs({kStringOutput1, kStringOutput2, kIntegerOutput}),
kTfLiteOk);
SetupTensor(subgraph, kStringInput1, kTfLiteString);
SetupTensor(subgraph, kStringInput2, kTfLiteString);
SetupTensor(subgraph, kIntegerInput, kTfLiteInt32);
SetupTensor(subgraph, kStringOutput1, kTfLiteString);
SetupTensor(subgraph, kStringOutput2, kTfLiteString);
SetupTensor(subgraph, kIntegerOutput, kTfLiteInt32);
TfLiteWhileParams* params =
reinterpret_cast<TfLiteWhileParams*>(malloc(sizeof(TfLiteWhileParams)));
params->cond_subgraph_index = 1;
params->body_subgraph_index = 2;
auto* while_reg = ops::builtin::Register_WHILE();
while_reg->builtin_code = kTfLiteBuiltinWhile;
int node_index;
subgraph->AddNodeWithParameters(
{kStringInput1, kStringInput2, kIntegerInput},
{kStringOutput1, kStringOutput2, kIntegerOutput}, {}, nullptr, 0, params,
while_reg, &node_index);
}
void FillIntTensor(TfLiteTensor* tensor, const std::vector<int32_t>& data) {
int count = NumElements(tensor);
ASSERT_EQ(count, data.size());
for (int i = 0; i < count; ++i) {
tensor->data.i32[i] = data[i];
}
}
void FillScalarStringTensor(TfLiteTensor* tensor, const std::string& data) {
StringRef str_ref;
str_ref.str = data.c_str();
str_ref.len = data.size();
DynamicBuffer buf;
buf.AddString(str_ref);
buf.WriteToTensor(tensor, /*new_shape=*/TfLiteIntArrayCreate(0));
}
void CheckScalarStringTensor(const TfLiteTensor* tensor,
const std::string& data) {
ASSERT_EQ(tensor->dims->size, 0);
ASSERT_EQ(tensor->type, kTfLiteString);
StringRef str_ref = GetString(tensor, 0);
EXPECT_EQ(std::string(str_ref.str, str_ref.len), data);
}
void CheckStringTensor(const TfLiteTensor* tensor,
const std::vector<int>& shape,
const std::vector<std::string>& data) {
ASSERT_EQ(tensor->dims->size, shape.size());
for (int i = 0; i < tensor->dims->size; ++i) {
ASSERT_EQ(tensor->dims->data[i], shape[i]);
}
ASSERT_EQ(tensor->type, kTfLiteString);
int count = GetStringCount(tensor);
ASSERT_EQ(count, data.size());
for (int i = 0; i < count; ++i) {
StringRef str_ref = GetString(tensor, i);
EXPECT_EQ(std::string(str_ref.str, str_ref.len), data[i]);
}
}
void CheckIntTensor(const TfLiteTensor* tensor, const std::vector<int>& shape,
const std::vector<int32_t>& data) {
ASSERT_EQ(tensor->dims->size, shape.size());
for (int i = 0; i < tensor->dims->size; ++i) {
ASSERT_EQ(tensor->dims->data[i], shape[i]);
}
ASSERT_EQ(tensor->type, kTfLiteInt32);
int count = NumElements(tensor);
ASSERT_EQ(count, data.size());
for (int i = 0; i < count; ++i) {
EXPECT_EQ(tensor->data.i32[i], data[i]);
}
}
void CheckBoolTensor(const TfLiteTensor* tensor, const std::vector<int>& shape,
const std::vector<bool>& data) {
ASSERT_EQ(tensor->dims->size, shape.size());
for (int i = 0; i < tensor->dims->size; ++i) {
ASSERT_EQ(tensor->dims->data[i], shape[i]);
}
ASSERT_EQ(tensor->type, kTfLiteBool);
int count = NumElements(tensor);
ASSERT_EQ(count, data.size());
for (int i = 0; i < count; ++i) {
EXPECT_EQ(tensor->data.b[i], data[i]);
}
}
} // namespace subgraph_test_util
} // namespace tflite