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

708 lines
32 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 <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
#include "tensorflow/lite/kernels/subgraph_test_util.h"
#include "tensorflow/lite/profiling/memory_info.h"
namespace tflite {
using subgraph_test_util::CheckIntTensor;
using subgraph_test_util::CheckScalarStringTensor;
using subgraph_test_util::CheckStringTensor;
using subgraph_test_util::ControlFlowOpTest;
using subgraph_test_util::FillIntTensor;
using subgraph_test_util::FillScalarStringTensor;
namespace {
class WhileTest : public ControlFlowOpTest {};
TEST_F(WhileTest, TestWithXNNPACK) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildFloatLessCondSubgraph(interpreter_->subgraph(1), 100);
builder_->BuildXNNPACKSubgraph(interpreter_->subgraph(2));
builder_->BuildFloatWhileSubgraph(&interpreter_->primary_subgraph(), 2);
const auto opt = TfLiteXNNPackDelegateOptionsDefault();
std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)> xnnpack_delegate(
TfLiteXNNPackDelegateCreate(&opt), TfLiteXNNPackDelegateDelete);
interpreter_->primary_subgraph().MarkAsDelegationSkippable();
interpreter_->subgraph(1)->MarkAsDelegationSkippable();
ASSERT_EQ(interpreter_->ModifyGraphWithDelegate(std::move(xnnpack_delegate)),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
float* input0 =
GetTensorData<float>(interpreter_->tensor(interpreter_->inputs()[0]));
input0[0] = 1;
float* input1 =
GetTensorData<float>(interpreter_->tensor(interpreter_->inputs()[1]));
input1[0] = 1;
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
float* output0_data = GetTensorData<float>(output0);
ASSERT_EQ(output0_data[0], 256);
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
float* output1_data = GetTensorData<float>(output1);
ASSERT_EQ(output1_data[0], 256);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestInputIsOutput) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 3);
builder_->BuildInputIsOutputSubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 3);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {1});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {4});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {1}, {4});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestInputIsOutputButDifferent) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 2);
builder_->BuildInputIsDifferentOutputSubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 2);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {2});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {5});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {1}, {8});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestFlexOutput) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 2);
builder_->BuildFlexOutputSubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 2);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {2}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {2, 3});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {4});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {2}, {5, 6});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestCounterOnly) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 1);
builder_->BuildCounterOnlySubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 1);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {4});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestAllCases) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 5);
builder_->BuildAllInplaceScenariosSubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 5);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[3], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[4], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[3]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[4]), {1});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {4});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {1}, {5});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[2]);
CheckIntTensor(output2, {6}, {2, 2, 2, 2, 2, 2});
TfLiteTensor* output3 = interpreter_->tensor(interpreter_->outputs()[3]);
CheckIntTensor(output3, {6}, {4, 4, 4, 4, 4, 4});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestStaticUnconsumedOutputs) {
for (bool dynamic_tensors : {true, false}) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 3);
builder_->BuildInputIsOutputSubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraphWithUnconsumedOutput(
&interpreter_->primary_subgraph(), 3);
InterpreterOptions options;
if (dynamic_tensors) {
options.OptimizeMemoryForLargeTensors(1);
interpreter_->ApplyOptions(&options);
}
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {2});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {4});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {1}, {8});
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {2}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {2, 2});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
CheckIntTensor(output1, {2}, {8, 8});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
}
// Test a body subgraph which triggers the reallocation of an inplace output
// tensor whose corresponding input has not been consumed yet. This tests that
// the input pointer has be updated.
TEST_F(WhileTest, TestDynamicOpTriggersAllocationOfUnsedInput) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 2, 3);
builder_->BuildDynamicOpTriggersAllocationOfUnsedInputSubgraph(
interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 3);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {2});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {3});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {2}, {4, 4});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[2]);
CheckIntTensor(output2, {2}, {2, 2});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestStaticInPlace) {
const std::vector<int> expected = {6, 10, 15, 21, 28};
for (int i = 0; i < expected.size(); ++i) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLessEqualCondSubgraph(interpreter_->subgraph(1), i + 1);
builder_->BuildDeepBodySubgraph(interpreter_->subgraph(2));
builder_->BuildWhileSubgraph(&interpreter_->primary_subgraph());
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {0});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output1, {1}, {i + 2});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output2, {1}, {expected[i]});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
}
TEST_F(WhileTest, TestStaticInPlaceLarge) {
int size = 10000;
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLessEqualCondSubgraph(interpreter_->subgraph(1), 60000);
builder_->BuildLargeBodySubgraph(interpreter_->subgraph(2));
builder_->BuildWhileSubgraph(&interpreter_->primary_subgraph());
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {size}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]),
std::vector<int>(size, 1));
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output1, {}, {10010 * size});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output2, {size}, std::vector<int>(size, 70014));
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
// The test builds a model that produces the i-th number of
// triangular number sequence.
TEST_F(WhileTest, TestTriangularNumberSequence) {
const std::vector<int> expected = {1, 3, 6, 10, 15, 21, 28};
for (int i = 0; i < expected.size(); ++i) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLessEqualCondSubgraph(interpreter_->subgraph(1), i);
builder_->BuildAccumulateLoopBodySubgraph(interpreter_->subgraph(2));
builder_->BuildWhileSubgraph(&interpreter_->primary_subgraph());
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1}),
kTfLiteOk);
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1});
// Check While BODY inputs are static tensors.
auto body_subgraph = interpreter_->subgraph(2);
TfLiteTensor* subgraph_input2 =
body_subgraph->tensor(body_subgraph->inputs()[1]);
EXPECT_EQ(subgraph_input2->allocation_type, kTfLiteCustom);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output1, {1}, {i + 1});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output2, {1}, {expected[i]});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
}
TEST_F(WhileTest, TestTriangularNumberSequenceWithShallowCopy) {
const std::vector<int> expected = {1, 3, 6, 10, 15, 21, 28};
for (int i = 0; i < expected.size(); ++i) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLessEqualCondSubgraph(interpreter_->subgraph(1), i);
builder_->BuildAccumulateLoopBodySubgraph(interpreter_->subgraph(2));
builder_->BuildWhileSubgraph(&interpreter_->primary_subgraph());
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
// Use 4MB inputs to test shallow copy.
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1000000});
// Apply DynamicAllocationForLargeTensors option to enable shallow copy.
InterpreterOptions options;
options.OptimizeMemoryForLargeTensors(1000000);
ASSERT_EQ(interpreter_->ApplyOptions(&options), kTfLiteOk);
const size_t initial_mem_usage =
profiling::memory::GetMemoryUsage().mem_footprint_kb;
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
// Memory usage shouldn't exceed 9MB (2 x inputs + margin).
ASSERT_LE(profiling::memory::GetMemoryUsage().mem_footprint_kb -
initial_mem_usage,
9000);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
const std::vector<int> input_vector(1000000, 1);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]),
input_vector);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
auto body_subgraph = interpreter_->subgraph(2);
// While BODY inputs are dynamic tensors with shallow copy.
TfLiteTensor* subgraph_input2 =
body_subgraph->tensor(body_subgraph->inputs()[1]);
ASSERT_EQ(subgraph_input2->allocation_type, kTfLiteCustom);
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output1, {1}, {i + 1});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[1]);
const std::vector<int> expected2(1000000, expected[i]);
CheckIntTensor(output2, {1000000}, expected2);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
}
TEST_F(WhileTest, TestPadLoop) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLessEqualCondSubgraph(interpreter_->subgraph(1), 4);
builder_->BuildPadLoopBodySubgraph(interpreter_->subgraph(2), {1, 2});
builder_->BuildWhileSubgraph(&interpreter_->primary_subgraph());
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {2});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {5, 7});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output1, {1}, {5});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output2, {14}, {0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 0, 0, 0});
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestDynamicBodyWithSharingEarlyExit) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 0, 4);
builder_->BuildDynamicIncreasingSizeSubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 4);
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {3});
interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {10000});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1, 2, 3});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {1});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {3}, {1, 2, 3});
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestDynamicBodyWithSharing) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 4);
builder_->BuildDynamicIncreasingSizeSubgraph(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 4);
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {3});
interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1000000});
interpreter_->ResizeInputTensor(interpreter_->inputs()[3], {1000000});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1, 2, 3});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {4});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {18},
{4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[2]);
EXPECT_EQ(output2->dims->data[0], 1000000);
TfLiteTensor* output3 = interpreter_->tensor(interpreter_->outputs()[3]);
EXPECT_EQ(output3->dims->data[0], 1000000);
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestDynamicBodyWithSharingAndAliases) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 0, 5);
builder_->BuildDynamicBodySubgraphWithAliases(interpreter_->subgraph(2));
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 5);
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[3], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[4], {1});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {0});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[3]), {3});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[4]), {4});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {1});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {1}, {11});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[2]);
CheckIntTensor(output2, {1}, {12});
TfLiteTensor* output3 = interpreter_->tensor(interpreter_->outputs()[4]);
CheckIntTensor(output3, {1}, {13});
TfLiteTensor* output4 = interpreter_->tensor(interpreter_->outputs()[4]);
CheckIntTensor(output4, {1}, {13});
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestOutputNotConsumed) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 11, 3);
builder_->BuildOutputNotConsumedSubgraph(*interpreter_->subgraph(2));
builder_->BuildOutputNotConsumedWhileSubgraph(
&interpreter_->primary_subgraph());
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {3});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {3}, {18, 18, 18});
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestPadLoopWithSharing) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLargeLessEqualCondSubgraph(interpreter_->subgraph(1), 3, 3);
builder_->BuildLargePadSubgraph(interpreter_->subgraph(2), {1, 2});
builder_->BuildMultiInputWhileSubgraph(&interpreter_->primary_subgraph(), 3);
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1});
interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {2});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), {2});
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {3, 4});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output0 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output0, {1}, {5});
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[1]);
CheckIntTensor(output1, {5}, {4, 9, 10, 4, 4});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[2]);
CheckIntTensor(output2, {8}, {0, 4, 9, 10, 4, 4, 0, 0});
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestPadLoopWithShallowCopy) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLessEqualCondSubgraph(interpreter_->subgraph(1), 3);
builder_->BuildPadLoopBodySubgraph(interpreter_->subgraph(2), {1, 2});
builder_->BuildWhileSubgraph(&interpreter_->primary_subgraph());
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {1});
// Use 4MB inputs to test shallow copy.
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {1000000});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[0]), {1});
std::vector<int> input_vector(1000000, 0);
input_vector[0] = 5;
input_vector[1] = 7;
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[1]), input_vector);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* output1 = interpreter_->tensor(interpreter_->outputs()[0]);
CheckIntTensor(output1, {1}, {4});
TfLiteTensor* output2 = interpreter_->tensor(interpreter_->outputs()[1]);
std::vector<int> output_vector(1000009, 0);
output_vector[3] = 5;
output_vector[4] = 7;
CheckIntTensor(output2, {1000009}, output_vector);
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
TEST_F(WhileTest, TestWhileLoopWithDynamicTensor) {
interpreter_ = std::make_unique<Interpreter>();
AddSubgraphs(2);
builder_->BuildLessEqualCondSubgraphWithDynamicTensor(
interpreter_->subgraph(1), 3);
builder_->BuildBodySubgraphWithDynamicTensor(interpreter_->subgraph(2));
builder_->BuildWhileSubgraphWithDynamicTensor(
&interpreter_->primary_subgraph());
interpreter_->ResizeInputTensor(interpreter_->inputs()[0], {});
interpreter_->ResizeInputTensor(interpreter_->inputs()[1], {});
interpreter_->ResizeInputTensor(interpreter_->inputs()[2], {1});
ASSERT_EQ(interpreter_->AllocateTensors(), kTfLiteOk);
FillScalarStringTensor(interpreter_->tensor(interpreter_->inputs()[0]), "A");
FillScalarStringTensor(interpreter_->tensor(interpreter_->inputs()[1]), "A");
FillIntTensor(interpreter_->tensor(interpreter_->inputs()[2]), {1});
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
TfLiteTensor* string_output1 =
interpreter_->tensor(interpreter_->outputs()[0]);
CheckScalarStringTensor(string_output1, "A");
TfLiteTensor* string_output2 =
interpreter_->tensor(interpreter_->outputs()[1]);
CheckStringTensor(string_output2, {4}, {"A", "A", "A", "A"});
TfLiteTensor* integer_output =
interpreter_->tensor(interpreter_->outputs()[2]);
CheckIntTensor(integer_output, {1}, {4});
// The extra invocation serves as a regression test: There was a bug that
// invoking a while loop with dynamic shaped body makes the interpreter
// state uninvokable.
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
ASSERT_EQ(interpreter_->Invoke(), kTfLiteOk);
}
} // namespace
} // namespace tflite