aboutsummaryrefslogtreecommitdiff
path: root/src/armnnTfParser
diff options
context:
space:
mode:
authorNattapat Chaimanowong <nattapat.chaimanowong@arm.com>2018-12-04 13:47:02 +0000
committernattapat.chaimanowong <nattapat.chaimanowong@arm.com>2018-12-04 14:43:18 +0000
commit24df822711e14cd3099a926272d863ed139ed4d7 (patch)
tree28a6165db8b64247ad8ceb63a0b3aeb5ccc60779 /src/armnnTfParser
parent20e58806b94636f579c5e8b0ca91ab771b6310e6 (diff)
downloadarmnn-24df822711e14cd3099a926272d863ed139ed4d7.tar.gz
IVGCVSW-2251 Add support for Minimum operator in TfParser
Change-Id: Ib84e5bde39d706c5125e0f84577195fc61107a4a
Diffstat (limited to 'src/armnnTfParser')
-rw-r--r--src/armnnTfParser/TfParser.cpp55
-rw-r--r--src/armnnTfParser/TfParser.hpp1
-rw-r--r--src/armnnTfParser/test/Minimum.cpp165
3 files changed, 221 insertions, 0 deletions
diff --git a/src/armnnTfParser/TfParser.cpp b/src/armnnTfParser/TfParser.cpp
index abf4d3f5c9..09769ecf6e 100644
--- a/src/armnnTfParser/TfParser.cpp
+++ b/src/armnnTfParser/TfParser.cpp
@@ -369,6 +369,7 @@ const std::map<std::string, TfParser::OperationParsingFunction> TfParser::ms_Ope
{ "MaxPool", &TfParser::ParseMaxPool },
{ "AvgPool", &TfParser::ParseAvgPool },
{ "Maximum", &TfParser::ParseMaximum },
+ { "Minimum", &TfParser::ParseMinimum },
};
ITfParser* ITfParser::CreateRaw()
@@ -1424,6 +1425,60 @@ ParsedTfOperationPtr TfParser::ParseMaximum(const tensorflow::NodeDef& nodeDef,
}
}
+ParsedTfOperationPtr TfParser::ParseMinimum(const tensorflow::NodeDef& nodeDef,
+ const tensorflow::GraphDef& graphDef)
+{
+ std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
+
+ IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
+ IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
+ const unsigned int input0Dim = input0Slot->GetTensorInfo().GetNumDimensions();
+ const unsigned int input1Dim = input1Slot->GetTensorInfo().GetNumDimensions();
+
+ if (input0Dim != input1Dim)
+ {
+ // broadcasting where input0 and input1 have different number of dimensions
+ // is only supported for 1D and 4D tensors pair
+ if (input0Dim == 1 && input1Dim == 4)
+ {
+ input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, true, *m_Network, nodeDef);
+ }
+ else if (input0Dim == 4 && input1Dim == 1)
+ {
+ input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, true, *m_Network, nodeDef);
+ }
+ else
+ {
+ throw ParseException(
+ boost::str(
+ boost::format("Unsupported broadcast configuration for Minimum operation %1% %2%")
+ % nodeDef.name()
+ % CHECK_LOCATION().AsString()));
+ }
+ }
+
+ IConnectableLayer* const layer = m_Network->AddMinimumLayer(nodeDef.name().c_str());
+
+ input0Slot->Connect(layer->GetInputSlot(0));
+ input1Slot->Connect(layer->GetInputSlot(1));
+
+ TensorInfo outputInfo = input0Slot->GetTensorInfo();
+ std::vector<unsigned int> outputShape;
+
+ const TensorShape& input0Shape = input0Slot->GetTensorInfo().GetShape();
+ const TensorShape& input1Shape = input1Slot->GetTensorInfo().GetShape();
+
+ for (unsigned int i = 0; i < input0Shape.GetNumDimensions(); i++)
+ {
+ outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
+ }
+
+ outputInfo.SetShape(TensorShape(input0Shape.GetNumDimensions(), outputShape.data()));
+ layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
+
+ return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
+}
+
ParsedTfOperationPtr TfParser::ParseConcat(const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
{
diff --git a/src/armnnTfParser/TfParser.hpp b/src/armnnTfParser/TfParser.hpp
index f57ea0518e..7abf783c38 100644
--- a/src/armnnTfParser/TfParser.hpp
+++ b/src/armnnTfParser/TfParser.hpp
@@ -152,6 +152,7 @@ private:
ParsedTfOperationPtr ParsePooling2d(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef,
armnn::PoolingAlgorithm pooltype);
ParsedTfOperationPtr ParseMaximum(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
+ ParsedTfOperationPtr ParseMinimum(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
ParsedTfOperationPtr AddActivationLayer(const tensorflow::NodeDef& nodeDef, armnn::ActivationDescriptor& desc);
ParsedTfOperationPtr AddAdditionLayer(const tensorflow::NodeDef& nodeDef, bool isBiasAdd = false);
ParsedTfOperationPtr AddRealDivLayer(const tensorflow::NodeDef& nodeDef);
diff --git a/src/armnnTfParser/test/Minimum.cpp b/src/armnnTfParser/test/Minimum.cpp
new file mode 100644
index 0000000000..feb86a17d6
--- /dev/null
+++ b/src/armnnTfParser/test/Minimum.cpp
@@ -0,0 +1,165 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <boost/test/unit_test.hpp>
+#include "armnnTfParser/ITfParser.hpp"
+#include "ParserPrototxtFixture.hpp"
+
+BOOST_AUTO_TEST_SUITE(TensorflowParser)
+
+struct MinimumFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser>
+{
+ MinimumFixture()
+ {
+ m_Prototext = R"(
+ node {
+ name: "input0"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ }
+ }
+ }
+ }
+ node {
+ name: "input1"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ }
+ }
+ }
+ }
+ node {
+ name: "output"
+ op: "Minimum"
+ input: "input0"
+ input: "input1"
+ attr {
+ key: "T"
+ value {
+ type: DT_FLOAT
+ }
+ }
+ }
+ )";
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(ParseMininumUnsupportedBroadcast, MinimumFixture)
+{
+ BOOST_REQUIRE_THROW(Setup({ { "input0", {2, 3} },
+ { "input1", {1, 2, 2, 3} } },
+ { "output" }),
+ armnn::ParseException);
+}
+
+struct MinimumFixtureAutoSetup : public MinimumFixture
+{
+ MinimumFixtureAutoSetup(const armnn::TensorShape& input0Shape,
+ const armnn::TensorShape& input1Shape)
+ : MinimumFixture()
+ {
+ Setup({ { "input0", input0Shape },
+ { "input1", input1Shape } },
+ { "output" });
+ }
+};
+
+struct MinimumFixture4D : public MinimumFixtureAutoSetup
+{
+ MinimumFixture4D()
+ : MinimumFixtureAutoSetup({1, 2, 2, 3}, {1, 2, 2, 3}) {}
+};
+
+BOOST_FIXTURE_TEST_CASE(ParseMinimum4D, MinimumFixture4D)
+{
+ RunTest<4>({ { "input0", { 0.0f, 1.0f, 2.0f,
+ 3.0f, 4.0f, 5.0f,
+ 6.0f, 7.0f, 8.0f,
+ 9.0f, 10.0f, 11.0f } },
+ { "input1", { 0.0f, 0.0f, 0.0f,
+ 5.0f, 5.0f, 5.0f,
+ 7.0f, 7.0f, 7.0f,
+ 9.0f, 9.0f, 9.0f } } },
+ { { "output", { 0.0f, 0.0f, 0.0f,
+ 3.0f, 4.0f, 5.0f,
+ 6.0f, 7.0f, 7.0f,
+ 9.0f, 9.0f, 9.0f } } });
+}
+
+struct MinimumBroadcastFixture4D : public MinimumFixtureAutoSetup
+{
+ MinimumBroadcastFixture4D()
+ : MinimumFixtureAutoSetup({1, 1, 2, 1}, {1, 2, 1, 3}) {}
+};
+
+BOOST_FIXTURE_TEST_CASE(ParseMinimumBroadcast4D, MinimumBroadcastFixture4D)
+{
+ RunTest<4>({ { "input0", { 2.0f,
+ 4.0f } },
+ { "input1", { 1.0f, 2.0f, 3.0f,
+ 4.0f, 5.0f, 6.0f } } },
+ { { "output", { 1.0f, 2.0f, 2.0f,
+ 1.0f, 2.0f, 3.0f,
+ 2.0f, 2.0f, 2.0f,
+ 4.0f, 4.0f, 4.0f } } });
+}
+
+struct MinimumBroadcastFixture4D1D : public MinimumFixtureAutoSetup
+{
+ MinimumBroadcastFixture4D1D()
+ : MinimumFixtureAutoSetup({1, 2, 2, 3}, {1}) {}
+};
+
+BOOST_FIXTURE_TEST_CASE(ParseMinimumBroadcast4D1D, MinimumBroadcastFixture4D1D)
+{
+ RunTest<4>({ { "input0", { 0.0f, 1.0f, 2.0f,
+ 3.0f, 4.0f, 5.0f,
+ 6.0f, 7.0f, 8.0f,
+ 9.0f, 10.0f, 11.0f } },
+ { "input1", { 5.0f } } },
+ { { "output", { 0.0f, 1.0f, 2.0f,
+ 3.0f, 4.0f, 5.0f,
+ 5.0f, 5.0f, 5.0f,
+ 5.0f, 5.0f, 5.0f } } });
+}
+
+struct MinimumBroadcastFixture1D4D : public MinimumFixtureAutoSetup
+{
+ MinimumBroadcastFixture1D4D()
+ : MinimumFixtureAutoSetup({3}, {1, 2, 2, 3}) {}
+};
+
+BOOST_FIXTURE_TEST_CASE(ParseMinimumBroadcast1D4D, MinimumBroadcastFixture1D4D)
+{
+ RunTest<4>({ { "input0", { 5.0f, 6.0f, 7.0f } },
+ { "input1", { 0.0f, 1.0f, 2.0f,
+ 3.0f, 4.0f, 5.0f,
+ 6.0f, 7.0f, 8.0f,
+ 9.0f, 10.0f, 11.0f } } },
+ { { "output", { 0.0f, 1.0f, 2.0f,
+ 3.0f, 4.0f, 5.0f,
+ 5.0f, 6.0f, 7.0f,
+ 5.0f, 6.0f, 7.0f } } });
+}
+
+BOOST_AUTO_TEST_SUITE_END()