IVGCVSW-1656 Add Mean support to Tf Parser

Change-Id: I3d31d6b72be1984acdb51fd9e7b5488a7aa5d832
diff --git a/src/armnnTfParser/TfParser.cpp b/src/armnnTfParser/TfParser.cpp
index 90bd992..0087ef8 100755
--- a/src/armnnTfParser/TfParser.cpp
+++ b/src/armnnTfParser/TfParser.cpp
@@ -2,40 +2,27 @@
 // Copyright © 2017 Arm Ltd. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
+
 #include "TfParser.hpp"
 
-#include <armnn/INetwork.hpp>
-#include <armnn/Utils.hpp>
 #include <armnn/TypesUtils.hpp>
-#include <armnn/Exceptions.hpp>
 #include <armnn/Descriptors.hpp>
 
 #include <GraphTopologicalSort.hpp>
 #include <ParserHelper.hpp>
 #include <Permute.hpp>
-#include <VerificationHelpers.hpp>
 #include <DataLayoutIndexed.hpp>
 
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/text_format.h>
 
 #include "tensorflow/core/framework/graph.pb.h"
-#include "tensorflow/core/framework/node_def.pb.h"
-#include "tensorflow/core/framework/types.pb.h"
-#include "tensorflow/core/framework/tensor.pb.h"
-#include "tensorflow/core/framework/tensor_shape.pb.h"
 
-#include <boost/assert.hpp>
 #include <boost/format.hpp>
 #include <boost/core/ignore_unused.hpp>
-#include <boost/log/trivial.hpp>
-#include <boost/numeric/conversion/cast.hpp>
 #include <boost/polymorphic_cast.hpp>
 
-#include <memory>
-#include <sstream>
 #include <numeric>
-#include <functional>
 
 using namespace armnnUtils;
 using namespace armnn;
@@ -141,6 +128,17 @@
     return attribValue;
 }
 
+bool ReadMandatoryNodeBoolAttribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
+{
+    bool attribValue = false;
+    ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kB,
+                                   [&attribValue](const tensorflow::AttrValue& attrValue)
+                                   {
+                                       attribValue = static_cast<bool>(attrValue.b());
+                                   });
+    return attribValue;
+}
+
 uint32_t ReadMandatoryNodeUint32Attribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
 {
     uint32_t attribValue = 0u;
@@ -338,6 +336,7 @@
     { "ConcatV2",              &TfParser::ParseConcat },
     { "LRN",                   &TfParser::ParseLrn },
     { "MatMul",                &TfParser::ParseMatMul },
+    { "Mean",                  &TfParser::ParseMean },
     { "Mul",                   &TfParser::ParseMul },
     { "Placeholder",           &TfParser::ParsePlaceholder },
     { "RealDiv",               &TfParser::ParseRealDiv },
@@ -2349,6 +2348,60 @@
     return std::make_unique<ParsedMatMulTfOperation>(this, nodeDef);
 }
 
+ParsedTfOperationPtr TfParser::ParseMean(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef)
+{
+    std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
+    IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
+    TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
+
+    if (inputs.size() != 2)
+    {
+        throw ParseException(
+                boost::str(boost::format("Mean expects two inputs!. Got %1% for Node %2% %3%")
+                           % inputs.size()
+                           % nodeDef.name()
+                           % CHECK_LOCATION().AsString()));
+    }
+
+    bool keepDims = ReadMandatoryNodeBoolAttribute(nodeDef, "keep_dims");
+
+    ParsedConstTfOperation<int32_t>* axisNode =
+             boost::polymorphic_downcast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
+
+    const TensorInfo& axisTensorInfo = axisNode->GetTensorInfo();
+
+    ConstTensor axisTensor(axisTensorInfo, axisNode->GetStorage());
+    const int* axisData = static_cast<const int*>(axisTensor.GetMemoryArea());
+
+    TensorInfo outputTensorInfo;
+    MeanDescriptor meanDescriptor;
+    meanDescriptor.m_KeepDims = keepDims;
+
+    // Negative axis values are supported so that the process requires
+    // to convert them into the corresponding positive ones.
+    // Duplicate values are also removed.
+    std::vector<int> rawAxisVector(axisData, axisData + axisTensorInfo.GetNumElements());
+    std::set<unsigned int> positiveAxisSet;
+    int rank = static_cast<int>(inputTensorInfo.GetNumDimensions());
+
+    std::transform(rawAxisVector.begin(), rawAxisVector.end(),
+                   std::inserter(positiveAxisSet, positiveAxisSet.begin()),
+                   [rank](int i) -> unsigned int { return static_cast<unsigned int>((i + rank) % rank); });
+
+    CalculateReducedOutputTensoInfo(inputTensorInfo, axisTensorInfo, positiveAxisSet, keepDims, outputTensorInfo);
+
+    if (inputTensorInfo.GetNumDimensions() > positiveAxisSet.size())
+    {
+        meanDescriptor.m_Axis.assign(positiveAxisSet.begin(), positiveAxisSet.end());
+    }
+
+    IConnectableLayer* layer = m_Network->AddMeanLayer(meanDescriptor, nodeDef.name().c_str());
+    layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+    inputSlot.Connect(layer->GetInputSlot(0));
+
+    return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
+}
+
 /// An ParsedTfOperation for a Mul node.
 /// Creation of the armnn Mul layer is deferred until it is actually needed, because Mul nodes
 /// are also used for the first part of a leaky relu activation function (Mul followed by Maximum)
diff --git a/src/armnnTfParser/TfParser.hpp b/src/armnnTfParser/TfParser.hpp
index 4421768..f1b7205 100644
--- a/src/armnnTfParser/TfParser.hpp
+++ b/src/armnnTfParser/TfParser.hpp
@@ -140,6 +140,7 @@
     ParsedTfOperationPtr ParseIdentity(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
     ParsedTfOperationPtr ParseLrn(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
     ParsedTfOperationPtr ParseMatMul(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
+    ParsedTfOperationPtr ParseMean(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
     ParsedTfOperationPtr ParseMul(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
     ParsedTfOperationPtr ParsePlaceholder(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
     ParsedTfOperationPtr ParseRealDiv(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef);
@@ -260,4 +261,5 @@
     /// Maps output layer names to their corresponding ids and tensor info.
     std::unordered_map<std::string, BindingPointInfo> m_NetworkOutputsBindingInfo;
 };
+
 }
diff --git a/src/armnnTfParser/test/Mean.cpp b/src/armnnTfParser/test/Mean.cpp
new file mode 100644
index 0000000..1304162
--- /dev/null
+++ b/src/armnnTfParser/test/Mean.cpp
@@ -0,0 +1,175 @@
+//
+// 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 MeanFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser>
+{
+    explicit MeanFixture(const armnn::TensorShape& inputShape, const armnn::TensorShape& outputShape,
+                         const std::vector<unsigned int>& axis, bool keepDims)
+    {
+        std::string protobufAxisString;
+        std::vector<unsigned int> protobufAxis(axis);
+
+        // If no axis range is specified, the reduction is applied to
+        // all dimensions of the input tensor
+        if (protobufAxis.size() == 0)
+        {
+            for (unsigned int i = 0; i < inputShape.GetNumDimensions(); ++i)
+            {
+                protobufAxis.push_back(i);
+            }
+        }
+
+        for (unsigned int i = 0; i < protobufAxis.size(); ++i)
+        {
+            protobufAxisString.append(ConvertInt32ToOctalString(static_cast<int>(protobufAxis[i])));
+        }
+
+        m_Prototext = R"(node {
+              name: "input"
+              op: "Placeholder"
+              attr {
+              key: "dtype"
+                value {
+                  type: DT_FLOAT
+                }
+              }
+              attr {
+                key: "shape"
+                value {
+                  shape {
+                  }
+                }
+              }
+            }
+            node {
+              name: "Const"
+              op: "Const"
+              attr {
+                key: "dtype"
+                value {
+                  type: DT_INT32
+                }
+              }
+              attr {
+                key: "value"
+                value { )";
+
+        if (axis.size() == 1)
+        {
+            m_Prototext.append(R"(      tensor {
+                    dtype: DT_INT32
+                    tensor_shape {
+                    }
+                    int_val: )").append(std::to_string(protobufAxis[0])).append(R"(
+                  } )");
+        }
+        else
+        {
+            m_Prototext.append(R"(      tensor {
+                    dtype: DT_INT32
+                    tensor_shape {
+                      dim {
+                        size: 2
+                      }
+                    }
+                    tensor_content: ")").append(protobufAxisString).append(R"("
+                  } )");
+        }
+
+        m_Prototext.append(R"(    }
+              }
+            }
+            node {
+              name: "output"
+              op: "Mean"
+              input: "input"
+              input: "Const"
+              attr {
+                key: "T"
+                value {
+                  type: DT_FLOAT
+                }
+              }
+              attr {
+                key: "Tidx"
+                  value {
+                    type: DT_INT32
+                }
+             }
+             attr {
+               key: "keep_dims"
+                 value {
+                   b: )").append(keepDims ? "true" : "false").append(R"(
+               }
+             }
+            })");
+
+        SetupSingleInputSingleOutput(inputShape, outputShape, "input", "output");
+    }
+};
+
+struct MeanNoAxisNoKeepDimsFixture: MeanFixture
+{
+    MeanNoAxisNoKeepDimsFixture() : MeanFixture({ 2, 3 }, { 1 }, {}, false) {}
+};
+
+struct MeanWithAxis0NoKeepDimsFixture: MeanFixture
+{
+    MeanWithAxis0NoKeepDimsFixture() : MeanFixture({ 2, 3 }, { 3 }, { 0 }, false) {}
+};
+
+struct MeanWithAxis1NoKeepDimsFixture: MeanFixture
+{
+    MeanWithAxis1NoKeepDimsFixture() : MeanFixture({ 2, 3 }, { 2 }, { 1 }, false) {}
+};
+
+struct MeanWithAxis0KeepDimsFixture: MeanFixture
+{
+    MeanWithAxis0KeepDimsFixture() : MeanFixture({ 2, 3 }, { 1, 3 }, { 0 }, true) {}
+};
+
+struct MeanWithAxis1KeepDimsFixture: MeanFixture
+{
+    MeanWithAxis1KeepDimsFixture() : MeanFixture({ 2, 3 }, { 2, 1 }, { 1 }, true) {}
+};
+
+
+BOOST_FIXTURE_TEST_CASE(MeanNoAxisNoKeepDims, MeanNoAxisNoKeepDimsFixture)
+{
+    RunTest<1>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } },
+               { { "output", { 1.5f } } });
+}
+
+BOOST_FIXTURE_TEST_CASE(MeanWithAxis0NoKeepDims, MeanWithAxis0NoKeepDimsFixture)
+{
+    RunTest<1>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } },
+               { { "output", { 1.5f, 1.5f, 1.5f } } });
+}
+
+BOOST_FIXTURE_TEST_CASE(MeanWithAxis1NoKeepDims, MeanWithAxis1NoKeepDimsFixture)
+{
+    RunTest<1>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } },
+               { { "output", { 1.f, 2.f } } });
+}
+
+BOOST_FIXTURE_TEST_CASE(MeanWithAxis0KeepDims, MeanWithAxis0KeepDimsFixture)
+{
+    RunTest<2>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } },
+               { { "output", { 1.5f, 1.5f, 1.5f } } });
+}
+
+BOOST_FIXTURE_TEST_CASE(MeanWithAxis1KeepDims, MeanWithAxis1KeepDimsFixture)
+{
+    RunTest<2>({ { "input", { 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f } } },
+               { { "output", { 1.f, 2.f } } });
+}
+
+BOOST_AUTO_TEST_SUITE_END()