COMPMID-996: Add support for grouped convolution.

Change-Id: I279e29ce20b3dde57445264dc11491f127b44d70
Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/124429
Reviewed-by: Anthony Barbier <anthony.barbier@arm.com>
Tested-by: Jenkins <bsgcomp@arm.com>
diff --git a/arm_compute/graph2/GraphBuilder.h b/arm_compute/graph2/GraphBuilder.h
index f92746a..f9fb251 100644
--- a/arm_compute/graph2/GraphBuilder.h
+++ b/arm_compute/graph2/GraphBuilder.h
@@ -101,10 +101,11 @@
      *
      * @param[in] g                     Graph to add the node to
      * @param[in] params                Common node parameters
-     * @param[in] input                 Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input                 Input to the convolution layer node as a NodeID-Index pair
      * @param[in] kernel_spatial_extend Spatial extend of convolution kernels
      * @param[in] depth                 Number of convolution kernels
      * @param[in] conv_info             Convolution layer information
+     * @param[in] num_groups            (Optional) Number of groups for a grouped convolution. Defaults to 1
      * @param[in] method                (Optional) Convolution method to use
      * @param[in] weights_accessor      (Optional) Accessor of the weights node data
      * @param[in] bias_accessor         (Optional) Accessor of the bias node data
@@ -113,13 +114,13 @@
      */
     static NodeID add_convolution_node(Graph &g, NodeParams params, NodeIdxPair input,
                                        Size2D kernel_spatial_extend, unsigned int depth, PadStrideInfo conv_info,
-                                       ConvolutionMethod   method           = ConvolutionMethod::DEFAULT,
+                                       unsigned int num_groups = 1, ConvolutionMethod method = ConvolutionMethod::DEFAULT,
                                        ITensorAccessorUPtr weights_accessor = nullptr, ITensorAccessorUPtr bias_accessor = nullptr);
     /** Adds a depth concatenate node to the graph
      *
      * @param[in] g      Graph to add the node to
      * @param[in] params Common node parameters
-     * @param[in] inputs Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] inputs Inputs to the depth concatenate layer node as a NodeID-Index pair
      *
      * @return Node ID of the created node, EmptyNodeID in case of error
      */
@@ -128,7 +129,7 @@
      *
      * @param[in] g                     Graph to add the node to
      * @param[in] params                Common node parameters
-     * @param[in] input                 Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input                 Input to the depthwise convolution layer node as a NodeID-Index pair
      * @param[in] kernel_spatial_extend Spatial extend of convolution kernels
      * @param[in] conv_info             Convolution layer information
      * @param[in] method                (Optional) Convolution method to use
@@ -156,7 +157,7 @@
      *
      * @param[in] g      Graph to add the node to
      * @param[in] params Common node parameters
-     * @param[in] input  Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input  Input to the flatten layer node as a NodeID-Index pair
      *
      * @return Node ID of the created node, EmptyNodeID in case of error
      */
@@ -165,7 +166,7 @@
      *
      * @param[in] g                Graph to add the layer to
      * @param[in] params           Common node parameters
-     * @param[in] input            Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input            Input to the fully connected layer node as a NodeID-Index pair
      * @param[in] num_outputs      Number of output neurons
      * @param[in] weights_accessor (Optional) Accessor of the weights node data
      * @param[in] bias_accessor    (Optional) Accessor of the bias node data
@@ -178,7 +179,7 @@
      *
      * @param[in] g         Graph to add the node to
      * @param[in] params    Common node parameters
-     * @param[in] input     Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input     Input to the normalization layer node as a NodeID-Index pair
      * @param[in] norm_info Normalization layer information
      *
      * @return Node ID of the created node, EmptyNodeID in case of error
@@ -188,7 +189,7 @@
      *
      * @param[in] g         Graph to add the node to
      * @param[in] params    Common node parameters
-     * @param[in] input     Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input     Input to the pooling layer node as a NodeID-Index pair
      * @param[in] pool_info Pooling layer information
      *
      * @return Node ID of the created node, EmptyNodeID in case of error
@@ -198,7 +199,7 @@
      *
      * @param[in] g      Graph to add the node to
      * @param[in] params Common node parameters
-     * @param[in] input  Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input  Input to the reshape layer node as a NodeID-Index pair
      * @param[in] shape  Output reshaped shape
      *
      * @return Node ID of the created node, EmptyNodeID in case of error
@@ -208,12 +209,23 @@
      *
      * @param[in] g      Graph to add the node to
      * @param[in] params Common node parameters
-     * @param[in] input  Input to the batch normalization layer node as a NodeID-Index pair
+     * @param[in] input  Input to the softmax layer node as a NodeID-Index pair
      * @param[in] beta   Beta parameter
      *
      * @return Node ID of the created node, EmptyNodeID in case of error
      */
     static NodeID add_softmax_node(Graph &g, NodeParams params, NodeIdxPair input, float beta = 1.f);
+    /** Adds a split node to the graph
+     *
+     * @param[in] g          Graph to add the node to
+     * @param[in] params     Common node parameters
+     * @param[in] input      Input to the split layer node as a NodeID-Index pair
+     * @param[in] num_splits Number of different splits
+     * @param[in] axis       (Optional) Split axis. Defaults to 0
+     *
+     * @return Node ID of the created node, EmptyNodeID in case of error
+     */
+    static NodeID add_split_node(Graph &g, NodeParams params, NodeIdxPair input, unsigned int num_splits, unsigned int axis = 0);
 };
 } // namespace graph2
 } // namespace arm_compute
diff --git a/arm_compute/graph2/IDeviceBackend.h b/arm_compute/graph2/IDeviceBackend.h
index 2e8f3cb..f0d6297 100644
--- a/arm_compute/graph2/IDeviceBackend.h
+++ b/arm_compute/graph2/IDeviceBackend.h
@@ -65,13 +65,14 @@
     virtual std::unique_ptr<ITensorHandle> create_tensor(const Tensor &tensor) = 0;
     /** Create a backend Sub-Tensor
      *
-     * @param[in] parent Parent sub-tensor handle
-     * @param[in] shape  Shape of the sub-tensor
-     * @param[in] coords Starting coordinates of the sub-tensor
+     * @param[in] parent        Parent sub-tensor handle
+     * @param[in] shape         Shape of the sub-tensor
+     * @param[in] coords        Starting coordinates of the sub-tensor
+     * @param[in] extend_parent Extends parent shape if true
      *
      * @return Backend sub-tensor handle
      */
-    virtual std::unique_ptr<ITensorHandle> create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords) = 0;
+    virtual std::unique_ptr<ITensorHandle> create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent) = 0;
     /** Configure a backend Node
      *
      * @note This creates an appropriate configured backend function for the given node
diff --git a/arm_compute/graph2/INodeVisitor.h b/arm_compute/graph2/INodeVisitor.h
index a7b8aeb..024d83c 100644
--- a/arm_compute/graph2/INodeVisitor.h
+++ b/arm_compute/graph2/INodeVisitor.h
@@ -116,6 +116,11 @@
      * @param[in] n Node to visit.
      */
     virtual void visit(SoftmaxLayerNode &n) = 0;
+    /** Visit SplitLayerNode.
+     *
+     * @param[in] n Node to visit.
+     */
+    virtual void visit(SplitLayerNode &n) = 0;
 };
 
 /** Default visitor implementation
@@ -195,6 +200,10 @@
     {
         default_visit();
     }
+    virtual void visit(SplitLayerNode &n) override
+    {
+        default_visit();
+    }
 #endif /* DOXYGEN_SKIP_THIS */
 
     /** Function to be overloaded by the client and implement default behavior for the
diff --git a/arm_compute/graph2/Types.h b/arm_compute/graph2/Types.h
index 2e9fe38..4cbfc72 100644
--- a/arm_compute/graph2/Types.h
+++ b/arm_compute/graph2/Types.h
@@ -38,6 +38,7 @@
 {
 using arm_compute::Status;
 
+using arm_compute::Coordinates;
 using arm_compute::DataType;
 using arm_compute::TensorShape;
 using arm_compute::Size2D;
@@ -125,6 +126,7 @@
     PoolingLayer,
     ReshapeLayer,
     SoftmaxLayer,
+    SplitLayer,
 
     Input,
     Output,
diff --git a/arm_compute/graph2/backends/CL/CLDeviceBackend.h b/arm_compute/graph2/backends/CL/CLDeviceBackend.h
index 77a8faf..3a70f0b 100644
--- a/arm_compute/graph2/backends/CL/CLDeviceBackend.h
+++ b/arm_compute/graph2/backends/CL/CLDeviceBackend.h
@@ -55,7 +55,7 @@
     void initialize_backend() override;
     void setup_backend_context(GraphContext &ctx) override;
     std::unique_ptr<ITensorHandle> create_tensor(const Tensor &tensor) override;
-    std::unique_ptr<ITensorHandle> create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords) override;
+    std::unique_ptr<ITensorHandle> create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent) override;
     std::unique_ptr<arm_compute::IFunction> configure_node(INode &node, GraphContext &ctx) override;
     Status validate_node(INode &node) override;
     std::shared_ptr<arm_compute::IMemoryManager> create_memory_manager(MemoryManagerAffinity affinity) override;
diff --git a/arm_compute/graph2/backends/CL/CLSubTensorHandle.h b/arm_compute/graph2/backends/CL/CLSubTensorHandle.h
index 5584a8b..9910980 100644
--- a/arm_compute/graph2/backends/CL/CLSubTensorHandle.h
+++ b/arm_compute/graph2/backends/CL/CLSubTensorHandle.h
@@ -43,8 +43,9 @@
      * @param[in] parent_handle Parent tensor handle
      * @param[in] shape         Sub-Tensor shape
      * @param[in] coords        Starting coordinates
+     * @param[in] extend_parent Extends parent shape if true
      */
-    CLSubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords);
+    CLSubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords, bool extend_parent = false);
     /** Destructor: free the tensor's memory */
     ~CLSubTensorHandle() = default;
     /** Allow instances of this class to be move constructed */
diff --git a/arm_compute/graph2/backends/NEON/NEDeviceBackend.h b/arm_compute/graph2/backends/NEON/NEDeviceBackend.h
index 5d1394b..e81e9d9 100644
--- a/arm_compute/graph2/backends/NEON/NEDeviceBackend.h
+++ b/arm_compute/graph2/backends/NEON/NEDeviceBackend.h
@@ -44,7 +44,7 @@
     void initialize_backend() override;
     void setup_backend_context(GraphContext &ctx) override;
     std::unique_ptr<ITensorHandle> create_tensor(const Tensor &tensor) override;
-    std::unique_ptr<ITensorHandle> create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords) override;
+    std::unique_ptr<ITensorHandle> create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent) override;
     std::unique_ptr<arm_compute::IFunction> configure_node(INode &node, GraphContext &ctx) override;
     Status validate_node(INode &node) override;
     std::shared_ptr<arm_compute::IMemoryManager> create_memory_manager(MemoryManagerAffinity affinity) override;
diff --git a/arm_compute/graph2/backends/NEON/NESubTensorHandle.h b/arm_compute/graph2/backends/NEON/NESubTensorHandle.h
index e027b0c..eacdfe0 100644
--- a/arm_compute/graph2/backends/NEON/NESubTensorHandle.h
+++ b/arm_compute/graph2/backends/NEON/NESubTensorHandle.h
@@ -43,8 +43,9 @@
      * @param[in] parent_handle Parent tensor handle
      * @param[in] shape         Sub-Tensor shape
      * @param[in] coords        Starting coordinates
+     * @param[in] extend_parent Extends parent shape if true
      */
-    NESubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords);
+    NESubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords, bool extend_parent = false);
     /** Destructor: free the tensor's memory */
     ~NESubTensorHandle() = default;
     /** Allow instances of this class to be move constructed */
diff --git a/arm_compute/graph2/frontend/Layers.h b/arm_compute/graph2/frontend/Layers.h
index 7ea23e0..779b471 100644
--- a/arm_compute/graph2/frontend/Layers.h
+++ b/arm_compute/graph2/frontend/Layers.h
@@ -187,11 +187,10 @@
 
     NodeID create_layer(IStream &s) override
     {
-        ARM_COMPUTE_UNUSED(_num_groups);
         NodeIdxPair input         = { s.tail_node(), 0 };
         NodeParams  common_params = { "", s.hints().target_hint };
         return GraphBuilder::add_convolution_node(s.graph(), common_params, input,
-                                                  Size2D(_conv_width, _conv_height), _ofm, _conv_info,
+                                                  Size2D(_conv_width, _conv_height), _ofm, _conv_info, _num_groups,
                                                   s.hints().convolution_method_hint,
                                                   std::move(_weights), std::move(_bias));
     }
diff --git a/arm_compute/graph2/mutators/GraphMutators.h b/arm_compute/graph2/mutators/GraphMutators.h
index b432e32..3275e32 100644
--- a/arm_compute/graph2/mutators/GraphMutators.h
+++ b/arm_compute/graph2/mutators/GraphMutators.h
@@ -27,5 +27,6 @@
 #include "arm_compute/graph2/mutators/DepthConcatSubTensorMutator.h"
 #include "arm_compute/graph2/mutators/InPlaceOperationMutator.h"
 #include "arm_compute/graph2/mutators/NodeFusionMutator.h"
+#include "arm_compute/graph2/mutators/SplitLayerSubTensorMutator.h"
 
 #endif /* __ARM_COMPUTE_GRAPH2_GRAPH_MUTATORS_H__ */
diff --git a/arm_compute/graph2/mutators/SplitLayerSubTensorMutator.h b/arm_compute/graph2/mutators/SplitLayerSubTensorMutator.h
new file mode 100644
index 0000000..82ee509
--- /dev/null
+++ b/arm_compute/graph2/mutators/SplitLayerSubTensorMutator.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_GRAPH2_SPLIT_LAYER_SUBTENSOR_MUTATOR_H__
+#define __ARM_COMPUTE_GRAPH2_SPLIT_LAYER_SUBTENSOR_MUTATOR_H__
+
+#include "arm_compute/graph2/IGraphMutator.h"
+
+namespace arm_compute
+{
+namespace graph2
+{
+/** Mutation pass to optimize split operations by using sub-tensors
+ *
+ * @warning This is compulsory to run in case Split layers are present in the model
+ **/
+class SplitLayerSubTensorMutator final : public IGraphMutator
+{
+public:
+    // Inherited methods overridden
+    virtual void mutate(Graph &g) override;
+    const char *name() override;
+};
+} // namespace graph2
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_GRAPH2_SPLIT_LAYER_SUBTENSOR_MUTATOR_H__ */
diff --git a/arm_compute/graph2/nodes/Nodes.h b/arm_compute/graph2/nodes/Nodes.h
index 8201361..3786978 100644
--- a/arm_compute/graph2/nodes/Nodes.h
+++ b/arm_compute/graph2/nodes/Nodes.h
@@ -39,5 +39,6 @@
 #include "arm_compute/graph2/nodes/PoolingLayerNode.h"
 #include "arm_compute/graph2/nodes/ReshapeLayerNode.h"
 #include "arm_compute/graph2/nodes/SoftmaxLayerNode.h"
+#include "arm_compute/graph2/nodes/SplitLayerNode.h"
 
 #endif /* __ARM_COMPUTE_GRAPH2_NODES_H__ */
diff --git a/arm_compute/graph2/nodes/NodesFwd.h b/arm_compute/graph2/nodes/NodesFwd.h
index 03ca65e..08f2454 100644
--- a/arm_compute/graph2/nodes/NodesFwd.h
+++ b/arm_compute/graph2/nodes/NodesFwd.h
@@ -45,6 +45,7 @@
 class PoolingLayerNode;
 class ReshapeLayerNode;
 class SoftmaxLayerNode;
+class SplitLayerNode;
 } // namespace graph2
 } // namespace arm_compute
 #endif /* __ARM_COMPUTE_GRAPH2_NODES_FWD_H__ */
diff --git a/arm_compute/graph2/nodes/SplitLayerNode.h b/arm_compute/graph2/nodes/SplitLayerNode.h
new file mode 100644
index 0000000..90e6134
--- /dev/null
+++ b/arm_compute/graph2/nodes/SplitLayerNode.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_GRAPH2_SPLIT_LAYER_NODE_H__
+#define __ARM_COMPUTE_GRAPH2_SPLIT_LAYER_NODE_H__
+
+#include "arm_compute/graph2/INode.h"
+
+#include <tuple>
+
+namespace arm_compute
+{
+namespace graph2
+{
+/** Split Layer node */
+class SplitLayerNode final : public INode
+{
+public:
+    /** Default Constructor
+     *
+     * @param[in] num_splits Number of splits
+     * @param[in] axis       (Optional) Axis to split on. Supported axis >= 2. Defaults to 0
+     */
+    SplitLayerNode(unsigned int num_splits, unsigned int axis = 0);
+    /** Computes split layer output shape
+     *
+     * @param[in] input_shape Shape of the input
+     * @param[in] num_splits  Number of splits
+     * @param[in] axis        Axis to perform the split on
+     * @param[in] idx         Index of the split
+     *
+     * @return  A pair with the shape of the split and the starting coordinates
+     */
+    static std::pair<TensorShape, Coordinates> compute_output_shape(TensorShape input_shape, unsigned int num_splits, unsigned int axis, unsigned int idx);
+    /** Number of splits accessor
+     *
+     * @return Number of splits
+     */
+    unsigned int num_splits() const;
+    /** Split axis accessor
+     *
+     * @return Split axis
+     */
+    unsigned int axis() const;
+
+    // Inherited overridden methods:
+    Status           validate() override;
+    NodeType         type() const override;
+    bool             forward_descriptors() override;
+    TensorDescriptor configure_output(size_t idx) const override;
+    void accept(INodeVisitor &v) override;
+
+private:
+    unsigned int _num_splits;
+    unsigned int _axis;
+};
+} // namespace graph2
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_GRAPH2_SPLIT_LAYER_NODE_H__ */