IVGCVSW-2253 Add maximum layer and corresponding no-op factory implementation

Change-Id: I8964f5e8978c2d2a07734a381e3f7c656c22456a
diff --git a/Android.mk b/Android.mk
index 3eeddcb..9e05607 100644
--- a/Android.mk
+++ b/Android.mk
@@ -93,6 +93,7 @@
         src/armnn/layers/InputLayer.cpp \
         src/armnn/layers/L2NormalizationLayer.cpp \
         src/armnn/layers/LstmLayer.cpp \
+        src/armnn/layers/MaximumLayer.cpp \
         src/armnn/layers/MeanLayer.cpp \
         src/armnn/layers/MemCopyLayer.cpp \
         src/armnn/layers/MergerLayer.cpp \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ce04bba..611e739 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -201,6 +201,8 @@
     src/armnn/layers/L2NormalizationLayer.cpp
     src/armnn/layers/LstmLayer.cpp
     src/armnn/layers/LstmLayer.hpp
+    src/armnn/layers/MaximumLayer.cpp
+    src/armnn/layers/MaximumLayer.hpp
     src/armnn/layers/MeanLayer.hpp
     src/armnn/layers/MeanLayer.cpp
     src/armnn/layers/MemCopyLayer.hpp
diff --git a/include/armnn/ILayerSupport.hpp b/include/armnn/ILayerSupport.hpp
index 7677971..b7a7b93 100644
--- a/include/armnn/ILayerSupport.hpp
+++ b/include/armnn/ILayerSupport.hpp
@@ -126,6 +126,11 @@
                                  const TensorInfo* cellToOutputWeights,
                                  Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const;
 
+    virtual bool IsMaximumSupported(const TensorInfo& input0,
+                                    const TensorInfo& input1,
+                                    const TensorInfo& output,
+                                    Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const;
+
     virtual bool IsMeanSupported(const TensorInfo& input,
                                  const TensorInfo& output,
                                  const MeanDescriptor& descriptor,
diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp
index ab50abc..86b106f 100644
--- a/include/armnn/INetwork.hpp
+++ b/include/armnn/INetwork.hpp
@@ -296,6 +296,11 @@
     /// @return - Interface for configuring the layer.
     virtual IConnectableLayer* AddSubtractionLayer(const char* name = nullptr) = 0;
 
+    /// Add a Maximum layer to the network.
+    /// @param name - Optional name for the layer.
+    /// @ return - Interface for configuring the layer.
+    virtual IConnectableLayer* AddMaximumLayer(const char* name = nullptr) = 0;
+
     /// Add a Mean layer to the network.
     /// @param meanDescriptor - Parameters for the mean operation.
     /// @param name - Optional name for the layer.
diff --git a/include/armnn/LayerSupport.hpp b/include/armnn/LayerSupport.hpp
index 83d79ec..9d450fc 100644
--- a/include/armnn/LayerSupport.hpp
+++ b/include/armnn/LayerSupport.hpp
@@ -145,6 +145,14 @@
                      size_t reasonIfUnsupportedMaxLength = 1024);
 
 /// Deprecated in favor of IBackend and ILayerSupport interfaces
+bool IsMaximumSupported(const BackendId& backend,
+                        const TensorInfo& input0,
+                        const TensorInfo& input1,
+                        const TensorInfo& output,
+                        char* reasonIfUnSupported = nullptr,
+                        size_t reasonIfUnSupportedMaxLength = 0);
+
+/// Deprecated in favor of IBackend and ILayerSupport interfaces
 bool IsMergerSupported(const BackendId& backend,
                        const std::vector<const TensorInfo*> inputs,
                        const TensorInfo& output,
diff --git a/src/armnn/InternalTypes.cpp b/src/armnn/InternalTypes.cpp
index f37b1a0..37c63fd 100644
--- a/src/armnn/InternalTypes.cpp
+++ b/src/armnn/InternalTypes.cpp
@@ -30,6 +30,7 @@
         case LayerType::Input: return "Input";
         case LayerType::L2Normalization: return "L2Normalization";
         case LayerType::Lstm: return "Lstm";
+        case LayerType::Maximum: return "Maximum";
         case LayerType::Mean: return "Mean";
         case LayerType::MemCopy: return "MemCopy";
         case LayerType::Merger: return "Merger";
diff --git a/src/armnn/InternalTypes.hpp b/src/armnn/InternalTypes.hpp
index 3e2f298..989718b 100644
--- a/src/armnn/InternalTypes.hpp
+++ b/src/armnn/InternalTypes.hpp
@@ -30,6 +30,7 @@
     Input,
     L2Normalization,
     Lstm,
+    Maximum,
     Mean,
     MemCopy,
     Merger,
diff --git a/src/armnn/LayerSupport.cpp b/src/armnn/LayerSupport.cpp
index 6489fe4..5834b81 100644
--- a/src/armnn/LayerSupport.cpp
+++ b/src/armnn/LayerSupport.cpp
@@ -255,6 +255,17 @@
                                cellToInputWeights, inputGateBias, projectionWeights,
                                projectionBias, cellToForgetWeights, cellToOutputWeights);
 }
+
+bool IsMaximumSupported(const BackendId& backend,
+                        const TensorInfo& input0,
+                        const TensorInfo& input1,
+                        const TensorInfo& output,
+                        char* reasonIfUnsupported,
+                        size_t reasonIfUnsupportedMaxLength)
+{
+    FORWARD_LAYER_SUPPORT_FUNC(backend, IsMaximumSupported, input0, input1, output);
+}
+
 bool IsMergerSupported(const BackendId& backend,
                        std::vector<const TensorInfo*> inputs,
                        const TensorInfo& output,
diff --git a/src/armnn/LayersFwd.hpp b/src/armnn/LayersFwd.hpp
index 0e873d7..bab40da 100644
--- a/src/armnn/LayersFwd.hpp
+++ b/src/armnn/LayersFwd.hpp
@@ -22,6 +22,7 @@
 #include "layers/InputLayer.hpp"
 #include "layers/L2NormalizationLayer.hpp"
 #include "layers/LstmLayer.hpp"
+#include "layers/MaximumLayer.hpp"
 #include "layers/MeanLayer.hpp"
 #include "layers/MemCopyLayer.hpp"
 #include "layers/MergerLayer.hpp"
@@ -82,6 +83,7 @@
 DECLARE_LAYER(Input)
 DECLARE_LAYER(L2Normalization)
 DECLARE_LAYER(Lstm)
+DECLARE_LAYER(Maximum)
 DECLARE_LAYER(Mean)
 DECLARE_LAYER(MemCopy)
 DECLARE_LAYER(Merger)
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index 32464f7..57949fb 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -507,6 +507,11 @@
     return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
 }
 
+IConnectableLayer* Network::AddMaximumLayer(const char* name)
+{
+    return m_Graph->AddLayer<MaximumLayer>(name);
+}
+
 IConnectableLayer* Network::AddMergerLayer(const OriginsDescriptor& mergerDescriptor,
     const char* name)
 {
diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp
index 471ce3e..84d1f58 100644
--- a/src/armnn/Network.hpp
+++ b/src/armnn/Network.hpp
@@ -124,6 +124,8 @@
 
     IConnectableLayer* AddSubtractionLayer(const char* name = nullptr) override;
 
+    IConnectableLayer* AddMaximumLayer(const char* name = nullptr) override;
+
     IConnectableLayer* AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name = nullptr) override;
 
     IConnectableLayer* AddPadLayer(const PadDescriptor& padDescriptor, const char* name = nullptr) override;
diff --git a/src/armnn/layers/MaximumLayer.cpp b/src/armnn/layers/MaximumLayer.cpp
new file mode 100644
index 0000000..67a2342
--- /dev/null
+++ b/src/armnn/layers/MaximumLayer.cpp
@@ -0,0 +1,32 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "MaximumLayer.hpp"
+
+#include "LayerCloneBase.hpp"
+
+#include <backendsCommon/WorkloadData.hpp>
+#include <backendsCommon/WorkloadFactory.hpp>
+
+namespace armnn
+{
+
+MaximumLayer::MaximumLayer(const char* name)
+: ArithmeticBaseLayer(2, 1, LayerType::Maximum, name)
+{}
+
+std::unique_ptr<IWorkload> MaximumLayer::CreateWorkload(const Graph& graph,
+                                                        const IWorkloadFactory& factory) const
+{
+    MaximumQueueDescriptor descriptor;
+    return factory.CreateMaximum(descriptor, PrepInfoAndDesc(descriptor, graph));
+}
+
+MaximumLayer* MaximumLayer::Clone(Graph& graph) const
+{
+    return CloneBase<MaximumLayer>(graph, GetName());
+}
+
+} // namespace armnn
diff --git a/src/armnn/layers/MaximumLayer.hpp b/src/armnn/layers/MaximumLayer.hpp
new file mode 100644
index 0000000..da4c3ed
--- /dev/null
+++ b/src/armnn/layers/MaximumLayer.hpp
@@ -0,0 +1,26 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include "ArithmeticBaseLayer.hpp"
+
+namespace armnn
+{
+
+class MaximumLayer : public ArithmeticBaseLayer
+{
+public:
+    virtual std::unique_ptr<IWorkload> CreateWorkload(const Graph& graph,
+                                                      const IWorkloadFactory& factory) const override;
+
+    MaximumLayer* Clone(Graph& graph) const override;
+
+protected:
+    MaximumLayer(const char* name);
+
+    ~MaximumLayer() = default;
+};
+
+} // namespace
diff --git a/src/backends/backendsCommon/ILayerSupport.cpp b/src/backends/backendsCommon/ILayerSupport.cpp
index 55dd447..28fd0a3 100644
--- a/src/backends/backendsCommon/ILayerSupport.cpp
+++ b/src/backends/backendsCommon/ILayerSupport.cpp
@@ -183,6 +183,14 @@
     return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported);
 }
 
+bool ILayerSupport::IsMaximumSupported(const TensorInfo& input0,
+                                       const TensorInfo& input1,
+                                       const TensorInfo& output,
+                                       Optional<std::string&> reasonIfUnsupported) const
+{
+    return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported);
+}
+
 bool ILayerSupport::IsMeanSupported(const TensorInfo& input,
                                     const TensorInfo& output,
                                     const MeanDescriptor& descriptor,
diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp
index af57fee..9c51317 100644
--- a/src/backends/backendsCommon/WorkloadData.cpp
+++ b/src/backends/backendsCommon/WorkloadData.cpp
@@ -887,6 +887,19 @@
                                        "second input");
 }
 
+void MaximumQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
+{
+    ValidateTwoInputs(workloadInfo, "MaximumQueueDescriptor");
+    ValidateSingleOutput(workloadInfo, "MaximumQueueDescriptor");
+
+    ValidateBroadcastTensorShapesMatch(workloadInfo.m_InputTensorInfos[0],
+                                       workloadInfo.m_InputTensorInfos[1],
+                                       workloadInfo.m_OutputTensorInfos[0],
+                                       "MaximumQueueDescriptor",
+                                       "first input",
+                                       "second input");
+}
+
 void MeanQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
 {
     ValidateSingleInput(workloadInfo, "MeanQueueDescriptor");
diff --git a/src/backends/backendsCommon/WorkloadData.hpp b/src/backends/backendsCommon/WorkloadData.hpp
index 8cc60d0..58b1413 100644
--- a/src/backends/backendsCommon/WorkloadData.hpp
+++ b/src/backends/backendsCommon/WorkloadData.hpp
@@ -199,6 +199,12 @@
     void Validate(const WorkloadInfo& workloadInfo) const;
 };
 
+// Maximum layer workload data.
+struct MaximumQueueDescriptor : QueueDescriptor
+{
+    void Validate(const WorkloadInfo& workloadInfo) const;
+};
+
 // Mean layer workload data.
 struct MeanQueueDescriptor : QueueDescriptorWithParameters<MeanDescriptor>
 {
diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp
index 2e2824e..96b52ac 100644
--- a/src/backends/backendsCommon/WorkloadFactory.cpp
+++ b/src/backends/backendsCommon/WorkloadFactory.cpp
@@ -450,6 +450,18 @@
                                      reason);
             break;
         }
+        case LayerType::Maximum:
+        {
+            const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
+            const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
+            const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo();
+
+            result = layerSupportObject->IsMaximumSupported(OverrideDataType(input0, dataType),
+                                                            OverrideDataType(input1, dataType),
+                                                            OverrideDataType(output, dataType),
+                                                            reason);
+            break;
+        }
         case LayerType::Merger:
         {
             auto cLayer = boost::polymorphic_downcast<const MergerLayer*>(&layer);
diff --git a/src/backends/backendsCommon/WorkloadFactory.hpp b/src/backends/backendsCommon/WorkloadFactory.hpp
index a1d0400..390f70c 100644
--- a/src/backends/backendsCommon/WorkloadFactory.hpp
+++ b/src/backends/backendsCommon/WorkloadFactory.hpp
@@ -133,6 +133,9 @@
     virtual std::unique_ptr<IWorkload> CreateSubtraction(const SubtractionQueueDescriptor& descriptor,
                                                          const WorkloadInfo& info) const = 0;
 
+    virtual std::unique_ptr<IWorkload> CreateMaximum(const MaximumQueueDescriptor& descriptor,
+                                                     const WorkloadInfo& info) const = 0;
+
     virtual std::unique_ptr<IWorkload> CreateMean(const MeanQueueDescriptor& descriptor,
                                                   const WorkloadInfo& Info) const = 0;
 
diff --git a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
index 7817e42..6941fc0 100644
--- a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
+++ b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
@@ -346,6 +346,8 @@
 
 DECLARE_LAYER_POLICY_2_PARAM(Lstm)
 
+DECLARE_LAYER_POLICY_1_PARAM(Maximum)
+
 DECLARE_LAYER_POLICY_2_PARAM(Mean)
 
 DECLARE_LAYER_POLICY_2_PARAM(Merger)
diff --git a/src/backends/cl/ClWorkloadFactory.cpp b/src/backends/cl/ClWorkloadFactory.cpp
index 3c2e287..42fb833 100644
--- a/src/backends/cl/ClWorkloadFactory.cpp
+++ b/src/backends/cl/ClWorkloadFactory.cpp
@@ -296,6 +296,12 @@
     return MakeWorkload<ClConvertFp32ToFp16Workload>(descriptor, info);
 }
 
+std::unique_ptr<IWorkload> ClWorkloadFactory::CreateMaximum(const MaximumQueueDescriptor& descriptor,
+                                                            const WorkloadInfo& info) const
+{
+    return MakeWorkload<NullWorkload, NullWorkload>(descriptor, info);
+}
+
 std::unique_ptr<IWorkload> ClWorkloadFactory::CreateMean(const MeanQueueDescriptor& descriptor,
                                                          const WorkloadInfo& info) const
 {
diff --git a/src/backends/cl/ClWorkloadFactory.hpp b/src/backends/cl/ClWorkloadFactory.hpp
index a5560fd..89bded6 100644
--- a/src/backends/cl/ClWorkloadFactory.hpp
+++ b/src/backends/cl/ClWorkloadFactory.hpp
@@ -120,6 +120,9 @@
     virtual std::unique_ptr<IWorkload> CreateSubtraction(const SubtractionQueueDescriptor& descriptor,
                                                          const WorkloadInfo& info) const override;
 
+    virtual std::unique_ptr<IWorkload> CreateMaximum(const MaximumQueueDescriptor& descriptor,
+                                                     const WorkloadInfo& info) const override;
+
     virtual std::unique_ptr<IWorkload> CreateMean(const MeanQueueDescriptor& descriptor,
                                                   const WorkloadInfo& Info) const override;
 
diff --git a/src/backends/neon/NeonWorkloadFactory.cpp b/src/backends/neon/NeonWorkloadFactory.cpp
index d79373d..0a141f1 100644
--- a/src/backends/neon/NeonWorkloadFactory.cpp
+++ b/src/backends/neon/NeonWorkloadFactory.cpp
@@ -264,6 +264,12 @@
     return std::make_unique<NeonConvertFp32ToFp16Workload>(descriptor, info);
 }
 
+std::unique_ptr<IWorkload> NeonWorkloadFactory::CreateMaximum(const MaximumQueueDescriptor& descriptor,
+                                                              const WorkloadInfo& info) const
+{
+    return MakeWorkloadHelper<NullWorkload, NullWorkload>(descriptor, info);
+}
+
 std::unique_ptr<IWorkload> NeonWorkloadFactory::CreateMean(const MeanQueueDescriptor& descriptor,
                                                            const WorkloadInfo& info) const
 {
diff --git a/src/backends/neon/NeonWorkloadFactory.hpp b/src/backends/neon/NeonWorkloadFactory.hpp
index 8d7b830..f6a1b55 100644
--- a/src/backends/neon/NeonWorkloadFactory.hpp
+++ b/src/backends/neon/NeonWorkloadFactory.hpp
@@ -121,6 +121,9 @@
     virtual std::unique_ptr<IWorkload> CreateSubtraction(const SubtractionQueueDescriptor& descriptor,
                                                          const WorkloadInfo& info) const override;
 
+    virtual std::unique_ptr<IWorkload> CreateMaximum(const MaximumQueueDescriptor& descriptor,
+                                                     const WorkloadInfo& info) const override;
+
     virtual std::unique_ptr<IWorkload> CreateMean(const MeanQueueDescriptor& descriptor,
                                                   const WorkloadInfo& Info) const override;
 
diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp
index da8669c..55c9fb2 100644
--- a/src/backends/reference/RefWorkloadFactory.cpp
+++ b/src/backends/reference/RefWorkloadFactory.cpp
@@ -258,6 +258,12 @@
     return MakeWorkload<RefSubtractionFloat32Workload, RefSubtractionUint8Workload>(descriptor, info);
 }
 
+std::unique_ptr<armnn::IWorkload> RefWorkloadFactory::CreateMaximum(
+    const MaximumQueueDescriptor& descriptor, const WorkloadInfo& info) const
+{
+    return MakeWorkload<NullWorkload, NullWorkload>(descriptor, info);
+}
+
 std::unique_ptr<armnn::IWorkload> RefWorkloadFactory::CreateMean(
     const MeanQueueDescriptor& descriptor, const WorkloadInfo& info) const
 {
diff --git a/src/backends/reference/RefWorkloadFactory.hpp b/src/backends/reference/RefWorkloadFactory.hpp
index af4d98d..eb7e5cc 100644
--- a/src/backends/reference/RefWorkloadFactory.hpp
+++ b/src/backends/reference/RefWorkloadFactory.hpp
@@ -138,6 +138,9 @@
     virtual std::unique_ptr<IWorkload> CreateSubtraction(const SubtractionQueueDescriptor& descriptor,
                                                          const WorkloadInfo& info) const override;
 
+    virtual std::unique_ptr<IWorkload> CreateMaximum(const MaximumQueueDescriptor& descriptor,
+                                                     const WorkloadInfo& info) const override;
+
     virtual std::unique_ptr<IWorkload> CreateMean(const MeanQueueDescriptor& descriptor,
                                                   const WorkloadInfo& Info) const override;