COMPMID-3381: Implement graph example for YoLo v3 output detector

Add sub/exp/splitv support in graph api

Signed-off-by: Sheri Zhang <sheri.zhang@arm.com>
Change-Id: I4e08cc19a46655717068b12c93d67e619a595d9a
Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/3309
Reviewed-by: Georgios Pinitas <georgios.pinitas@arm.com>
Tested-by: Arm Jenkins <bsgcomp@arm.com>
Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
diff --git a/arm_compute/graph/LayerDescriptors.h b/arm_compute/graph/LayerDescriptors.h
index d8e6a6a..ba53622 100644
--- a/arm_compute/graph/LayerDescriptors.h
+++ b/arm_compute/graph/LayerDescriptors.h
@@ -89,6 +89,31 @@
     ActivationLayerInfo fused_activation; /**< Fused activation info */
 };
 
+/** Unary Elementwise layer descriptor */
+struct UnaryEltwiseLayerDescriptor
+{
+    /** Constructor
+     *
+     * @param[in] op               Unary element-wise operation to perform
+     * @param[in] out_quant_info   (Optional) Output quantization information. Defaults to empty @ref QuantizationInfo
+     * @param[in] c_policy         (Optional) Convert policy used for the operation. Defaults to @ref ConvertPolicy::SATURATE
+     * @param[in] r_policy         (Optional) Rounding policy used for the operation. Defaults to @ref RoundingPolicy::TO_ZERO
+     * @param[in] fused_activation (Optional) Fused activation information. Defaults to empty (identity) @ref ActivationLayerInfo
+     */
+    UnaryEltwiseLayerDescriptor(UnaryEltwiseOperation op, QuantizationInfo out_quant_info = QuantizationInfo(), ConvertPolicy c_policy = ConvertPolicy::SATURATE,
+                                RoundingPolicy      r_policy         = RoundingPolicy::TO_ZERO,
+                                ActivationLayerInfo fused_activation = ActivationLayerInfo())
+        : op(op), out_quant_info(out_quant_info), c_policy(c_policy), r_policy(r_policy), fused_activation(fused_activation)
+    {
+    }
+
+    UnaryEltwiseOperation op;               /**< Unary element-wise operation to perform */
+    QuantizationInfo      out_quant_info;   /**< Output quantization information */
+    ConvertPolicy         c_policy;         /**< Convert policy */
+    RoundingPolicy        r_policy;         /**< Rounding policy */
+    ActivationLayerInfo   fused_activation; /**< Fused activation info */
+};
+
 /** Deconvolution layer descriptor */
 struct DeconvolutionLayerDescriptor
 {
diff --git a/arm_compute/graph/TypePrinter.h b/arm_compute/graph/TypePrinter.h
index d56407b..dea055a 100644
--- a/arm_compute/graph/TypePrinter.h
+++ b/arm_compute/graph/TypePrinter.h
@@ -98,6 +98,9 @@
         case NodeType::EltwiseLayer:
             os << "EltwiseLayer";
             break;
+        case NodeType::UnaryEltwiseLayer:
+            os << "UnaryEltwiseLayer";
+            break;
         case NodeType::FlattenLayer:
             os << "FlattenLayer";
             break;
diff --git a/arm_compute/graph/Types.h b/arm_compute/graph/Types.h
index 296f757..4220692 100644
--- a/arm_compute/graph/Types.h
+++ b/arm_compute/graph/Types.h
@@ -103,7 +103,13 @@
 {
     Add, /**< Arithmetic addition */
     Sub, /**< Arithmetic subtraction */
-    Mul  /**< Arithmetic multiplication */
+    Mul, /**< Arithmetic multiplication */
+};
+
+/** Supported Unary Element-wise operations */
+enum class UnaryEltwiseOperation
+{
+    Exp /**< Exp */
 };
 
 /** Supported Convolution layer methods */
@@ -168,6 +174,7 @@
     SplitLayer,
     StackLayer,
     UpsampleLayer,
+    UnaryEltwiseLayer,
     YOLOLayer,
 
     Input,
diff --git a/arm_compute/graph/backends/FunctionHelpers.h b/arm_compute/graph/backends/FunctionHelpers.h
index 975e5fe..e21b8ed 100644
--- a/arm_compute/graph/backends/FunctionHelpers.h
+++ b/arm_compute/graph/backends/FunctionHelpers.h
@@ -816,6 +816,54 @@
     return RETURN_UNIQUE_PTR(func);
 }
 
+/** Create a backend unary element-wise operation layer function
+ *
+ * @tparam UnaryEltwiseFunctions Backend unary element-wise function
+ * @tparam TargetInfo       Target-specific information
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend unary element-wise operation layer function
+ */
+template <typename UnaryEltwiseFunctions, typename TargetInfo>
+std::unique_ptr<IFunction> create_unary_eltwise_layer(UnaryEltwiseLayerNode &node)
+{
+    validate_node<TargetInfo>(node, 1 /* expected inputs */, 1 /* expected outputs */);
+
+    // Extract IO and info
+    typename TargetInfo::TensorType *input      = get_backing_tensor<TargetInfo>(node.input(0));
+    typename TargetInfo::TensorType *output     = get_backing_tensor<TargetInfo>(node.output(0));
+    const UnaryEltwiseOperation      eltwise_op = node.eltwise_descriptor().op;
+
+    ARM_COMPUTE_ERROR_ON(input == nullptr);
+    ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+    std::unique_ptr<IFunction> func = nullptr;
+    std::string                func_name;
+    if(eltwise_op == UnaryEltwiseOperation::Exp)
+    {
+        std::tie(func, func_name) = create_named_function<typename UnaryEltwiseFunctions::Exp>(
+                                        std::string("Exp"),
+                                        input, output);
+    }
+    else
+    {
+        ARM_COMPUTE_ERROR("Unsupported unary element-wise operation!");
+    }
+
+    // Log info
+    ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated "
+                               << node.name()
+                               << " Type: " << node.type()
+                               << " Target: " << TargetInfo::TargetType
+                               << " Operation: " << func_name
+                               << " Data Type: " << input->info()->data_type()
+                               << " Shape: " << input->info()->tensor_shape()
+                               << std::endl);
+
+    return RETURN_UNIQUE_PTR(func);
+}
+
 /** Create a backend flatten layer function
  *
  * @tparam FlattenLayerFunction Backend flatten function
diff --git a/arm_compute/graph/backends/ValidateHelpers.h b/arm_compute/graph/backends/ValidateHelpers.h
index 673caf9..d689c1f 100644
--- a/arm_compute/graph/backends/ValidateHelpers.h
+++ b/arm_compute/graph/backends/ValidateHelpers.h
@@ -579,6 +579,79 @@
     // Validate function
     return YOLOLayer::validate(input, output, node.activation_info(), node.num_classes());
 }
+/** Validates a element-wise layer node
+ *
+ * @param[in] node Node to validate
+ *
+ * @return Status
+ */
+template <typename EltwiseLayerFunctions>
+Status validate_eltwise_Layer(EltwiseLayerNode &node)
+{
+    ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+    ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 2);
+    ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1);
+
+    // Extract input and output
+    const arm_compute::ITensorInfo *input1         = detail::get_backing_tensor_info(node.input(0));
+    const arm_compute::ITensorInfo *input2         = detail::get_backing_tensor_info(node.input(1));
+    const arm_compute::ITensorInfo *output         = get_backing_tensor_info(node.output(0));
+    const EltwiseOperation          eltwise_op     = node.eltwise_operation();
+    const ConvertPolicy             convert_policy = node.convert_policy();
+    const RoundingPolicy            round_policy   = node.rounding_policy();
+    const ActivationLayerInfo       act_info       = node.fused_activation();
+    const QuantizationInfo          quant_info     = node.output_quant_info();
+    const float                     scale          = (quant_info.scale().empty()) ? 1.0f : quant_info.scale()[0];
+
+    // Validate function
+    if(eltwise_op == EltwiseOperation::Add)
+    {
+        return EltwiseLayerFunctions::ArithmeticAddition::validate(input1, input2, output, convert_policy, act_info);
+    }
+    else if(eltwise_op == EltwiseOperation::Sub)
+    {
+        return EltwiseLayerFunctions::ArithmeticSubtraction::validate(input1, input2, output, convert_policy, act_info);
+    }
+    else if(eltwise_op == EltwiseOperation::Mul)
+    {
+        return EltwiseLayerFunctions::PixelWiseMultiplication::validate(input1, input2, output, scale, convert_policy, round_policy, act_info);
+    }
+    else
+    {
+        ARM_COMPUTE_ERROR("Unsupported element-wise operation!");
+    }
+    return Status{};
+}
+/** Validates a unary element-wise layer node
+ *
+ * @param[in] node Node to validate
+ *
+ * @return Status
+ */
+template <typename UnaryEltwiseLayerFunctions>
+Status validate_unary_eltwise_layer(UnaryEltwiseLayerNode &node)
+{
+    ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+    ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 1);
+    ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1);
+
+    // Extract input and output
+    arm_compute::ITensorInfo   *input      = detail::get_backing_tensor_info(node.input(0));
+    arm_compute::ITensorInfo   *output     = get_backing_tensor_info(node.output(0));
+    const UnaryEltwiseOperation eltwise_op = node.eltwise_descriptor().op;
+
+    // Validate function
+    if(eltwise_op == UnaryEltwiseOperation::Exp)
+    {
+        return UnaryEltwiseLayerFunctions::ExpLayer::validate(input, output);
+    }
+    else
+    {
+        ARM_COMPUTE_ERROR("Unsupported unary element-wise operation!");
+    }
+
+    return Status{};
+}
 } // namespace detail
 } // namespace backends
 } // namespace graph
diff --git a/arm_compute/graph/nodes/EltwiseLayerNode.h b/arm_compute/graph/nodes/EltwiseLayerNode.h
index d619ad2..9ea5d69 100644
--- a/arm_compute/graph/nodes/EltwiseLayerNode.h
+++ b/arm_compute/graph/nodes/EltwiseLayerNode.h
@@ -63,6 +63,12 @@
      */
     ActivationLayerInfo fused_activation() const;
 
+    /** Returns output quantization info
+     *
+     * @return Output quantization info
+     */
+    QuantizationInfo output_quant_info() const;
+
     /** Sets fused activation
      *
      * @param[in] fused_activation Fused activation to set
@@ -80,6 +86,40 @@
 private:
     descriptors::EltwiseLayerDescriptor descriptor;
 };
+
+/** Unary Eltwise Layer node */
+class UnaryEltwiseLayerNode final : public INode
+{
+public:
+    /** Constructor
+     *
+     * @param[in] descriptor Containing information for the node described in @ref descriptors::EltwiseLayerDescriptor
+     */
+    UnaryEltwiseLayerNode(const descriptors::UnaryEltwiseLayerDescriptor &descriptor);
+    /** Unary eltwise layer descriptor
+     *
+     * @return Unary eltwise layer descriptor which containing information
+     */
+    descriptors::UnaryEltwiseLayerDescriptor eltwise_descriptor() const;
+
+    /** Sets fused activation
+     *
+     * @param[in] fused_activation Fused activation to set
+     */
+    void set_fused_activation(ActivationLayerInfo fused_activation);
+
+    // Inherited overridden methods:
+    NodeType         type() const override;
+    bool             forward_descriptors() override;
+    TensorDescriptor configure_output(size_t idx) const override;
+    void accept(INodeVisitor &v) override;
+
+    static constexpr NodeType node_type = NodeType::UnaryEltwiseLayer;
+
+private:
+    descriptors::UnaryEltwiseLayerDescriptor descriptor;
+};
+
 } // namespace graph
 } // namespace arm_compute
 #endif /* ARM_COMPUTE_GRAPH_ELTWISE_LAYER_NODE_H */
diff --git a/arm_compute/graph/nodes/SplitLayerNode.h b/arm_compute/graph/nodes/SplitLayerNode.h
index 345260a..b5dea7f 100644
--- a/arm_compute/graph/nodes/SplitLayerNode.h
+++ b/arm_compute/graph/nodes/SplitLayerNode.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2019 ARM Limited.
+ * Copyright (c) 2018-2020 ARM Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -38,10 +38,13 @@
 public:
     /** Default Constructor
      *
-     * @param[in] num_splits Number of splits
-     * @param[in] axis       (Optional) Axis to split on. Supported axis >= 2. Defaults to 0
+     * @param[in] num_splits  Number of splits
+     * @param[in] axis        (Optional) Axis to split on. Defaults to 0
+     * @param[in] size_splits (Optional) The sizes of each output tensor along the split dimension.
+     *                        Must sum to the dimension of value along split_dim.
+     *                        Can contain one -1 indicating that dimension is to be inferred.
      */
-    SplitLayerNode(unsigned int num_splits, unsigned int axis = 0);
+    SplitLayerNode(unsigned int num_splits, int axis = 0, std::vector<int> size_splits = std::vector<int>());
     /** Computes split layer output descriptor
      *
      * @param[in] input_descriptor Descriptor of the input tensor
@@ -51,8 +54,8 @@
      *
      * @return  A pair with the descriptor of the split and the starting coordinates
      */
-    static std::pair<TensorDescriptor, Coordinates> compute_output_descriptor(const TensorDescriptor &input_descriptor,
-                                                                              unsigned int num_splits, unsigned int axis, unsigned int idx);
+    std::pair<TensorDescriptor, Coordinates> compute_output_descriptor(const TensorDescriptor &input_descriptor,
+                                                                       unsigned int num_splits, int axis, unsigned int idx);
     /** Number of splits accessor
      *
      * @return Number of splits
@@ -72,8 +75,9 @@
     void accept(INodeVisitor &v) override;
 
 private:
-    unsigned int _num_splits;
-    unsigned int _axis;
+    unsigned int     _num_splits;
+    int              _axis;
+    std::vector<int> _size_splits;
 };
 } // namespace graph
 } // namespace arm_compute