aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--src/armnn/Network.cpp5
-rw-r--r--src/armnn/layers/ElementwiseBaseLayer.cpp31
-rw-r--r--src/armnn/optimizations/AddBroadcastReshapeLayer.hpp85
-rw-r--r--src/armnn/optimizations/All.hpp1
-rw-r--r--src/armnn/test/optimizations/AddBroadcastReshapeLayerTests.cpp288
-rw-r--r--src/armnnTfLiteParser/TfLiteParser.cpp124
-rw-r--r--src/armnnTfLiteParser/test/LoadScopeDynamicTensor.cpp205
8 files changed, 617 insertions, 124 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b013828d93..48405327c2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -451,6 +451,7 @@ list(APPEND armnn_sources
src/armnn/Utils.cpp
src/armnn/WallClockTimer.cpp
src/armnn/WallClockTimer.hpp
+ src/armnn/optimizations/AddBroadcastReshapeLayer.hpp
src/armnn/optimizations/AddDebug.hpp
src/armnn/optimizations/All.hpp
src/armnn/optimizations/ConvertConstants.hpp
@@ -641,6 +642,7 @@ if(BUILD_UNIT_TESTS)
src/armnn/test/NetworkTests.cpp
src/armnn/test/ObservableTest.cpp
src/armnn/test/OptimizerTests.cpp
+ src/armnn/test/optimizations/AddBroadcastReshapeLayerTests.cpp
src/armnn/test/optimizations/ConvertConstantsBFloatTests.cpp
src/armnn/test/optimizations/ConvertConstantsFloatToHalfTests.cpp
src/armnn/test/optimizations/ConvertConstantsHalfToFloatTests.cpp
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index 17813a8983..cd5f369271 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -1038,11 +1038,14 @@ IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
// Get the optimized graph
Graph& optGraph = optNetObjPtr->GetGraph();
+ // Perform AddBroadcastReshapeLayer optimisation
+ using namespace optimizations;
+ Optimizer::Pass(optGraph, MakeOptimizations(AddBroadcastReshapeLayer()));
+
// Infer the tensor infos for all output slots. Throws an exception on failure
optGraph.InferTensorInfos();
// Perform optimisation passes
- using namespace optimizations;
Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
SquashEqualTransposeSiblings(),
SquashEqualReshapeSiblings(),
diff --git a/src/armnn/layers/ElementwiseBaseLayer.cpp b/src/armnn/layers/ElementwiseBaseLayer.cpp
index b4a3cea9e1..631e08c2ac 100644
--- a/src/armnn/layers/ElementwiseBaseLayer.cpp
+++ b/src/armnn/layers/ElementwiseBaseLayer.cpp
@@ -22,18 +22,29 @@ ElementwiseBaseLayer::ElementwiseBaseLayer(unsigned int numInputSlots, unsigned
std::vector<TensorShape> ElementwiseBaseLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
{
ARMNN_ASSERT(inputShapes.size() == 2);
- auto& input0 = inputShapes[0];
- auto& input1 = inputShapes[1];
+ TensorShape input0 = inputShapes[0];
+ TensorShape input1 = inputShapes[1];
+
+ if (m_ShapeInferenceMethod == ShapeInferenceMethod::ValidateOnly)
+ {
+ ARMNN_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions());
+ }
+ else if (m_ShapeInferenceMethod == ShapeInferenceMethod::InferAndValidate &&
+ inputShapes[0].GetNumDimensions() < inputShapes[1].GetNumDimensions())
+ {
+ input1 = inputShapes[0];
+ input0 = inputShapes[1];
+ }
- // Get the max of the inputs.
- ARMNN_ASSERT(input0.GetNumDimensions() == input1.GetNumDimensions());
unsigned int numDims = input0.GetNumDimensions();
- std::vector<unsigned int> dims(numDims);
+ unsigned int shiftedDims = input0.GetNumDimensions() - input1.GetNumDimensions();
- for (unsigned int i = 0; i < numDims; i++)
+ // Get the max of the inputs.
+ std::vector<unsigned int> dims(numDims);
+ for (unsigned int i = shiftedDims; i < numDims; i++)
{
unsigned int dim0 = input0[i];
- unsigned int dim1 = input1[i];
+ unsigned int dim1 = input1[i - shiftedDims];
#if !NDEBUG
// Validate inputs are broadcast compatible.
@@ -44,6 +55,12 @@ std::vector<TensorShape> ElementwiseBaseLayer::InferOutputShapes(const std::vect
dims[i] = std::max(dim0, dim1);
}
+ // Fill in the rest of the shifted dimensions.
+ for (unsigned int i = 0; i < shiftedDims; i++)
+ {
+ dims[i] = input0[i];
+ }
+
return std::vector<TensorShape>({ TensorShape(numDims, dims.data()) });
}
diff --git a/src/armnn/optimizations/AddBroadcastReshapeLayer.hpp b/src/armnn/optimizations/AddBroadcastReshapeLayer.hpp
new file mode 100644
index 0000000000..6bb53d0f12
--- /dev/null
+++ b/src/armnn/optimizations/AddBroadcastReshapeLayer.hpp
@@ -0,0 +1,85 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include "Optimization.hpp"
+
+#include <armnn/utility/IgnoreUnused.hpp>
+#include <armnn/utility/PolymorphicDowncast.hpp>
+
+namespace armnn
+{
+namespace optimizations
+{
+
+static const std::set<armnn::LayerType> broadcastOps {
+ LayerType::Addition,
+ LayerType::Division,
+ LayerType::Maximum,
+ LayerType::Minimum,
+ LayerType::Multiplication,
+ LayerType::Subtraction
+};
+
+class AddBroadcastReshapeLayerImpl
+{
+public:
+ /// Run for every ElementwiseBaseLayer. Add Broadcast reshape layer if the inputs shape are different.
+ void Run(Graph& graph, Layer& layer) const
+ {
+ if (std::find(broadcastOps.begin(), broadcastOps.end(), layer.GetType()) != broadcastOps.end())
+ {
+ layer.GetInputSlot(0).GetConnectedOutputSlot()->IsTensorInfoSet();
+ layer.GetInputSlot(1).GetConnectedOutputSlot()->IsTensorInfoSet();
+
+ const TensorInfo &inputInfo0 = layer.GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo();
+ const TensorInfo &inputInfo1 = layer.GetInputSlot(1).GetConnectedOutputSlot()->GetTensorInfo();
+
+ if (inputInfo0.GetNumDimensions() == inputInfo1.GetNumDimensions())
+ {
+ return;
+ }
+
+ unsigned int reshapeSlot = 1;
+ TensorInfo reshapeInfo = inputInfo1;
+ TensorInfo inputInfo = inputInfo0;
+
+ if (inputInfo0.GetNumDimensions() < inputInfo1.GetNumDimensions())
+ {
+ reshapeSlot = 0;
+ reshapeInfo = inputInfo0;
+ inputInfo = inputInfo1;
+ }
+
+ uint32_t numDimensions = inputInfo.GetNumDimensions();
+
+ std::vector<unsigned> reshapedDim;
+ for (unsigned int i = 0; i < reshapeInfo.GetNumDimensions(); ++i)
+ {
+ reshapedDim.push_back(reshapeInfo.GetShape()[i]);
+ }
+
+ std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
+ std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
+
+ reshapeInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
+ const std::string layerName = "Reshape_for:" + layer.GetNameStr() + "-" + std::to_string(reshapeSlot);
+ const ReshapeDescriptor descriptor{reshapeInfo.GetShape()};
+ ReshapeLayer *reshapeLayer = graph.InsertNewLayer<ReshapeLayer>(layer.GetInputSlot(reshapeSlot),
+ descriptor,
+ layerName.c_str());
+ reshapeLayer->GetOutputSlot().SetTensorInfo(reshapeInfo);
+ }
+ }
+
+protected:
+ AddBroadcastReshapeLayerImpl() = default;
+ ~AddBroadcastReshapeLayerImpl() = default;
+};
+
+using AddBroadcastReshapeLayer = OptimizeForType<Layer, AddBroadcastReshapeLayerImpl>;
+
+} // namespace optimizations
+} // namespace armnn
diff --git a/src/armnn/optimizations/All.hpp b/src/armnn/optimizations/All.hpp
index cb484d5a59..e89c36b834 100644
--- a/src/armnn/optimizations/All.hpp
+++ b/src/armnn/optimizations/All.hpp
@@ -4,6 +4,7 @@
//
#pragma once
+#include "AddBroadcastReshapeLayer.hpp"
#include "AddDebug.hpp"
#include "ConvertConstants.hpp"
#include "ConvertFp32NetworkToBf16.hpp"
diff --git a/src/armnn/test/optimizations/AddBroadcastReshapeLayerTests.cpp b/src/armnn/test/optimizations/AddBroadcastReshapeLayerTests.cpp
new file mode 100644
index 0000000000..fe3cc31838
--- /dev/null
+++ b/src/armnn/test/optimizations/AddBroadcastReshapeLayerTests.cpp
@@ -0,0 +1,288 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "../GraphUtils.hpp"
+#include "../TestUtils.hpp"
+
+#include <Optimizer.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+using namespace armnn;
+
+BOOST_AUTO_TEST_SUITE(Optimizer)
+using namespace optimizations;
+
+void AddBroadcastReshapeLayerOptimizerTest(const TensorInfo& info0,
+ const TensorInfo& info1,
+ const TensorInfo& outputInfo,
+ const std::string& reshapeLayerName,
+ const TensorShape& expectedReshapeShape,
+ const DataType expectedDataType)
+{
+ Graph graph;
+
+ auto input0 = graph.AddLayer<InputLayer>(0, "input0");
+ auto input1 = graph.AddLayer<InputLayer>(1, "input1");
+ auto add = graph.AddLayer<AdditionLayer>("add");
+ auto output = graph.AddLayer<OutputLayer>(0, "output");
+ input0->GetOutputSlot().SetTensorInfo(info0);
+ input1->GetOutputSlot().SetTensorInfo(info1);
+ add->GetOutputSlot().SetTensorInfo(outputInfo);
+
+ input0->GetOutputSlot().Connect(add->GetInputSlot(0));
+ input1->GetOutputSlot().Connect(add->GetInputSlot(1));
+ add->GetOutputSlot().Connect(output->GetInputSlot(0));
+
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<AdditionLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ // Run optimizer
+ armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
+
+ // Broadcast reshape layer has been added to the graph correctly
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<ReshapeLayer>,
+ &IsLayerOfType<AdditionLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ Layer* const reshapeLayer = GetFirstLayerWithName(graph, reshapeLayerName);
+ BOOST_TEST(reshapeLayer);
+ auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
+
+ // Tensorshape and the data type are correct
+ BOOST_TEST((addedReshapeTensorInfo.GetShape() == expectedReshapeShape));
+ BOOST_TEST((addedReshapeTensorInfo.GetDataType() == expectedDataType));
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayerSimpleTest)
+{
+ const TensorInfo info0({ 1, 2, 3, 5 }, DataType::Float32);
+ const TensorInfo info1({ 1 }, DataType::Float32);
+ AddBroadcastReshapeLayerOptimizerTest(info0, info1, info0, "Reshape_for:add-1",
+ TensorShape({ 1, 1, 1, 1 }),
+ DataType::Float32);
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayer1DTest)
+{
+ const TensorInfo info0({ 1, 2, 3, 5 }, DataType::Float32);
+ const TensorInfo info1({ 5 }, DataType::Float32);
+ const TensorInfo outputInfo({ 1, 1, 1, 5 }, DataType::Float32);
+ AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
+ TensorShape({ 1, 1, 1, 5 }),
+ DataType::Float32);
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayer2DTest)
+{
+ const TensorInfo info0({ 1, 2, 3, 5 }, DataType::Float32);
+ const TensorInfo info1({ 3, 5 }, DataType::Float32);
+ const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::Float32);
+ AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
+ TensorShape({ 1, 1, 3, 5 }),
+ DataType::Float32);
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayer3DTest)
+{
+ const TensorInfo info0({ 2, 1, 1, 1 }, DataType::Float32);
+ const TensorInfo info1({ 3, 4, 5 }, DataType::Float32);
+ const TensorInfo outputInfo({ 2, 3, 4, 5 }, DataType::Float32);
+ AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
+ TensorShape({ 1, 3, 4, 5 }),
+ DataType::Float32);
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayer3DMergedTest)
+{
+ const TensorInfo info0({ 2, 3, 1, 1 }, DataType::Float32);
+ const TensorInfo info1({ 3, 4, 5 }, DataType::Float32);
+ const TensorInfo outputInfo({ 2, 3, 4, 5 }, DataType::Float32);
+ AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
+ TensorShape({ 1, 3, 4, 5 }),
+ DataType::Float32);
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayerSubtractionTest)
+{
+ Graph graph;
+ const TensorInfo info0({ 5 }, DataType::Float32);
+ const TensorInfo info1({ 1, 2, 3, 5 }, DataType::Float32);
+ const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::Float32);
+
+ auto input0 = graph.AddLayer<InputLayer>(0, "input0");
+ auto input1 = graph.AddLayer<InputLayer>(1, "input1");
+ auto sub = graph.AddLayer<SubtractionLayer>("sub");
+ auto output = graph.AddLayer<OutputLayer>(0, "output");
+ input0->GetOutputSlot().SetTensorInfo(info0);
+ input1->GetOutputSlot().SetTensorInfo(info1);
+ sub->GetOutputSlot().SetTensorInfo(outputInfo);
+
+ input0->GetOutputSlot().Connect(sub->GetInputSlot(0));
+ input1->GetOutputSlot().Connect(sub->GetInputSlot(1));
+ sub->GetOutputSlot().Connect(output->GetInputSlot(0));
+
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<SubtractionLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ // Run optimizer
+ armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
+
+ // Broadcast reshape layer has been added to the graph correctly
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<ReshapeLayer>,
+ &IsLayerOfType<SubtractionLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:sub-0");
+ BOOST_TEST(reshapeLayer);
+ auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
+
+ // Tensorshape and the data type are correct
+ BOOST_TEST((addedReshapeTensorInfo.GetShape() == TensorShape({ 1, 1, 1, 5 })));
+ BOOST_TEST((addedReshapeTensorInfo.GetDataType() == DataType::Float32));
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayerDivisionTest)
+{
+ Graph graph;
+ const TensorInfo info0({ 1, 4, 5 }, DataType::QAsymmS8);
+ const TensorInfo info1({ 1, 2, 4, 5 }, DataType::QAsymmS8);
+ const TensorInfo outputInfo({ 1, 2, 4, 5 }, DataType::QAsymmS8);
+
+ auto input0 = graph.AddLayer<InputLayer>(0, "input0");
+ auto input1 = graph.AddLayer<InputLayer>(1, "input1");
+ auto div = graph.AddLayer<DivisionLayer>("div");
+ auto output = graph.AddLayer<OutputLayer>(0, "output");
+ input0->GetOutputSlot().SetTensorInfo(info0);
+ input1->GetOutputSlot().SetTensorInfo(info1);
+ div->GetOutputSlot().SetTensorInfo(outputInfo);
+
+ input0->GetOutputSlot().Connect(div->GetInputSlot(0));
+ input1->GetOutputSlot().Connect(div->GetInputSlot(1));
+ div->GetOutputSlot().Connect(output->GetInputSlot(0));
+
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<DivisionLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ // Run optimizer
+ armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
+
+ // Broadcast reshape layer has been added to the graph correctly
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<ReshapeLayer>,
+ &IsLayerOfType<DivisionLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:div-0");
+ BOOST_TEST(reshapeLayer);
+ auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
+
+ // Tensorshape and the data type are correct
+ BOOST_TEST((addedReshapeTensorInfo.GetShape() == TensorShape({ 1, 1, 4, 5 })));
+ BOOST_TEST((addedReshapeTensorInfo.GetDataType() == DataType::QAsymmS8));
+}
+
+BOOST_AUTO_TEST_CASE(AddBroadcastReshapeLayerMultiplicationTest)
+{
+ Graph graph;
+ const TensorInfo info0({ 3, 5 }, DataType::QAsymmU8);
+ const TensorInfo info1({ 1, 2, 3, 5 }, DataType::QAsymmU8);
+ const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::QAsymmU8);
+
+ auto input0 = graph.AddLayer<InputLayer>(0, "input0");
+ auto input1 = graph.AddLayer<InputLayer>(1, "input1");
+ auto mul = graph.AddLayer<MultiplicationLayer>("mul");
+ auto output = graph.AddLayer<OutputLayer>(0, "output");
+ input0->GetOutputSlot().SetTensorInfo(info0);
+ input1->GetOutputSlot().SetTensorInfo(info1);
+ mul->GetOutputSlot().SetTensorInfo(outputInfo);
+
+ input0->GetOutputSlot().Connect(mul->GetInputSlot(0));
+ input1->GetOutputSlot().Connect(mul->GetInputSlot(1));
+ mul->GetOutputSlot().Connect(output->GetInputSlot(0));
+
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<MultiplicationLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ // Run optimizer
+ armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
+
+ // Broadcast reshape layer has been added to the graph correctly
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<ReshapeLayer>,
+ &IsLayerOfType<MultiplicationLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:mul-0");
+ BOOST_TEST(reshapeLayer);
+ auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
+
+ // Tensorshape and the data type are correct
+ BOOST_TEST((addedReshapeTensorInfo.GetShape() == TensorShape({ 1, 1, 3, 5 })));
+ BOOST_TEST((addedReshapeTensorInfo.GetDataType() == DataType::QAsymmU8));
+}
+
+BOOST_AUTO_TEST_CASE(AddNoBroadcastReshapeLayerTest)
+{
+ Graph graph;
+ const TensorInfo info0({ 1, 1, 1, 1 }, DataType::QAsymmU8);
+ const TensorInfo info1({ 1, 2, 3, 5 }, DataType::QAsymmU8);
+ const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::QAsymmU8);
+
+ auto input0 = graph.AddLayer<InputLayer>(0, "input0");
+ auto input1 = graph.AddLayer<InputLayer>(1, "input1");
+ auto mul = graph.AddLayer<MultiplicationLayer>("mul");
+ auto output = graph.AddLayer<OutputLayer>(0, "output");
+ input0->GetOutputSlot().SetTensorInfo(info0);
+ input1->GetOutputSlot().SetTensorInfo(info1);
+ mul->GetOutputSlot().SetTensorInfo(outputInfo);
+
+ input0->GetOutputSlot().Connect(mul->GetInputSlot(0));
+ input1->GetOutputSlot().Connect(mul->GetInputSlot(1));
+ mul->GetOutputSlot().Connect(output->GetInputSlot(0));
+
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<MultiplicationLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ // Run optimizer
+ armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
+
+ // Broadcast reshape layer has not been added to the graph
+ BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<InputLayer>,
+ &IsLayerOfType<MultiplicationLayer>,
+ &IsLayerOfType<OutputLayer>));
+
+ Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:mul-0");
+ BOOST_TEST(!reshapeLayer);
+}
+
+BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file
diff --git a/src/armnnTfLiteParser/TfLiteParser.cpp b/src/armnnTfLiteParser/TfLiteParser.cpp
index 109c2c2be1..6143f4af6a 100644
--- a/src/armnnTfLiteParser/TfLiteParser.cpp
+++ b/src/armnnTfLiteParser/TfLiteParser.cpp
@@ -443,7 +443,7 @@ armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
}
}
-armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
+armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
{
auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
@@ -609,69 +609,6 @@ void TfLiteParser::ResetParser()
m_SubgraphConnections.clear();
}
-void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
- size_t operatorIndex,
- IConnectableLayer *layer)
-{
- CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
- ARMNN_ASSERT(layer != nullptr);
-
- const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
- const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
-
- ARMNN_ASSERT(operatorPtr->inputs.size() > 1);
-
- uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
- TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
- uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
- TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
-
- armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
- armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
-
- uint32_t inputSlotId = 1;
- uint32_t reshapeSlotId = 0;
-
- if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
- {
- uint32_t id = reshapedInputId;
- reshapedInputId = inputId;
- inputId = id;
-
- reshapedTensorInfo = ToTensorInfo(tensorPtr1);
- inputTensorInfo = ToTensorInfo(tensorPtr);
-
- inputSlotId = 0;
- reshapeSlotId = 1;
- }
-
- uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
-
- std::vector<unsigned> reshapedDim;
- for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
- {
- reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
- }
-
- std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
- std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
-
- reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
-
- std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
- armnn::ReshapeDescriptor desc;
- desc.m_TargetShape = reshapedTensorInfo.GetShape();
- armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
-
- reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
- reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(reshapeSlotId));
-
- RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
-
- armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(inputSlotId));
- RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
-}
-
INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
{
ResetParser();
@@ -995,7 +932,7 @@ void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorInd
// Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
-
+
armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1], permutationVector);
@@ -1353,14 +1290,7 @@ void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
- if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
- {
- AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
- }
- else
- {
- RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
- }
+ RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
@@ -1390,14 +1320,7 @@ void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
- if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
- {
- AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
- }
- else
- {
- RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
- }
+ RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
@@ -1768,14 +1691,7 @@ void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
- if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
- {
- AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
- }
- else
- {
- RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
- }
+ RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
@@ -1807,15 +1723,7 @@ void TfLiteParser::ParseDiv(size_t subgraphIndex, size_t operatorIndex)
layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
- if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
- {
- AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
- }
- else
- {
- RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
- }
-
+ RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
@@ -1846,15 +1754,7 @@ void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
- if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
- {
- AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
- }
- else
- {
- RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
- }
-
+ RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
@@ -1885,15 +1785,7 @@ void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
- if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
- {
- AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
- }
- else
- {
- RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
- }
-
+ RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
diff --git a/src/armnnTfLiteParser/test/LoadScopeDynamicTensor.cpp b/src/armnnTfLiteParser/test/LoadScopeDynamicTensor.cpp
index c4f0db7f49..89a6640e41 100644
--- a/src/armnnTfLiteParser/test/LoadScopeDynamicTensor.cpp
+++ b/src/armnnTfLiteParser/test/LoadScopeDynamicTensor.cpp
@@ -171,4 +171,209 @@ BOOST_FIXTURE_TEST_CASE(LoadScopeDynamicTensor2, LoadScopeDynamicTensor2Fixture)
true);
}
+struct LoadScopeDynamicTensorBroadcastingFixture : public ParserFlatbuffersFixture
+{
+ explicit LoadScopeDynamicTensorBroadcastingFixture(const std::string& inputShape0,
+ const std::string& inputShape1,
+ const std::string& inputShape2,
+ const std::string& addShape,
+ const std::string& outputShape)
+ {
+ m_JsonString = R"(
+ {
+ "version": 3,
+ "operator_codes": [
+ {
+ "builtin_code": "ADD",
+ "version": 1
+ },
+ {
+ "builtin_code": "SUB",
+ "version": 1
+ }
+ ],
+ "subgraphs": [
+ {
+ "tensors": [
+ {
+ "shape": )" + inputShape0 + R"(,
+ "type": "FLOAT32",
+ "buffer": 1,
+ "name": "input0",
+ "quantization": {
+ "details_type": 0,
+ "quantized_dimension": 0
+ },
+ "is_variable": false
+ },
+ {
+ "shape": )" + inputShape1 + R"(,
+ "type": "FLOAT32",
+ "buffer": 2,
+ "name": "input1",
+ "quantization": {
+ "details_type": 0,
+ "quantized_dimension": 0
+ },
+ "is_variable": false
+ },
+ {
+ "shape": )" + outputShape + R"(,
+ "type": "FLOAT32",
+ "buffer": 5,
+ "name": "output",
+ "quantization": {
+ "details_type": 0,
+ "quantized_dimension": 0
+ },
+ "is_variable": false
+ },
+
+ {
+ "shape": )" + addShape + R"(,
+ "type": "FLOAT32",
+ "buffer": 4,
+ "name": "model/add/add",
+ "quantization": {
+ "details_type": 0,
+ "quantized_dimension": 0
+ },
+ "is_variable": false
+ },
+ {
+ "shape": )" + inputShape2 + R"(,
+ "type": "FLOAT32",
+ "buffer": 3,
+ "name": "input2",
+ "quantization": {
+ "details_type": 0,
+ "quantized_dimension": 0
+ },
+ "is_variable": false
+ },
+ ],
+ "inputs": [
+ 0,
+ 1,
+ 4
+ ],
+ "outputs": [
+ 2
+ ],
+ "operators": [
+ {
+ "opcode_index": 0,
+ "inputs": [
+ 0,
+ 1
+ ],
+ "outputs": [
+ 3
+ ],
+ "builtin_options_type": "AddOptions",
+ "builtin_options": {
+ "fused_activation_function": "NONE"
+ },
+ "custom_options_format": "FLEXBUFFERS"
+ },
+ {
+ "opcode_index": 1,
+ "inputs": [
+ 3,
+ 4
+ ],
+ "outputs": [
+ 2
+ ],
+ "builtin_options_type": "SubOptions",
+ "builtin_options": {
+ "fused_activation_function": "NONE"
+ },
+ "custom_options_format": "FLEXBUFFERS"
+ }
+ ],
+ "name": "main"
+ }
+ ],
+ "buffers": [
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ },
+ {
+ }
+ ]
+ }
+ )";
+ Setup();
+ }
+};
+
+struct LoadScopeDynamicTensorBroadcasting3DFixture : LoadScopeDynamicTensorBroadcastingFixture
+{
+ LoadScopeDynamicTensorBroadcasting3DFixture() : LoadScopeDynamicTensorBroadcastingFixture("[ 1, 2, 3, 2 ]",
+ "[ 2, 3, 2 ]",
+ "[ 2, 3, 2 ]",
+ "[ 1, 2, 3, 2 ]", "[]") {}
+};
+
+struct LoadScopeDynamicTensorBroadcasting2DFixture : LoadScopeDynamicTensorBroadcastingFixture
+{
+ LoadScopeDynamicTensorBroadcasting2DFixture() : LoadScopeDynamicTensorBroadcastingFixture("[ 1, 2, 3, 2 ]",
+ "[ 3, 2 ]",
+ "[ 3, 2 ]",
+ "[]", "[]") {}
+};
+
+struct LoadScopeDynamicTensorBroadcasting1DFixture : LoadScopeDynamicTensorBroadcastingFixture
+{
+ LoadScopeDynamicTensorBroadcasting1DFixture() : LoadScopeDynamicTensorBroadcastingFixture("[ 1, 2, 3, 2 ]",
+ "[ 1 ]",
+ "[ 1 ]",
+ "[]",
+ "[ 1, 2, 3, 2 ]") {}
+};
+
+BOOST_FIXTURE_TEST_CASE(LoadScopeDynamicTensorBroadcasting3D, LoadScopeDynamicTensorBroadcasting3DFixture)
+{
+ RunTest<4, armnn::DataType::Float32, armnn::DataType::Float32>(
+ 0,
+ { {"input0", { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f }},
+ {"input1", { 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f }},
+ {"input2", { 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f }}
+ },
+ { {"output", { 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f }} },
+ true);
+}
+
+BOOST_FIXTURE_TEST_CASE(LoadScopeDynamicTensorBroadcasting2D, LoadScopeDynamicTensorBroadcasting2DFixture)
+{
+ RunTest<4, armnn::DataType::Float32, armnn::DataType::Float32>(
+ 0,
+ { {"input0", { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f }},
+ {"input1", { 3.f, 4.f, 5.f, 6.f, 7.f, 8.f }},
+ {"input2", { -1.f, -2.f, 3.f, 4.f, 5.f, 6.f }}
+ },
+ { {"output", { 4.f, 7.f, 4.f, 5.f, 6.f, 7.f, 10.f, 13.f, 10.f, 11.f, 12.f, 13.f }} },
+ true);
+}
+
+BOOST_FIXTURE_TEST_CASE(LoadScopeDynamicTensorBroadcasting1D, LoadScopeDynamicTensorBroadcasting1DFixture)
+{
+ RunTest<4, armnn::DataType::Float32, armnn::DataType::Float32>(
+ 0,
+ { {"input0", { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f }},
+ {"input1", { 5.f }},
+ {"input2", { 1.f }}
+ },
+ { {"output", { 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f }} },
+ true);
+}
+
BOOST_AUTO_TEST_SUITE_END()