COMPMID-630: Rework nodes

Reworked node:
-BatchNormalization
-Floor
-FullyConncted
-L2Normalize
-Normalization
-Pooling
-Softmax

Change-Id: I4c71cfffb1f59aac3326ba8b1f831339c5244394
Reviewed-on: http://mpd-gerrit.cambridge.arm.com/93134
Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com>
Reviewed-by: Anthony Barbier <anthony.barbier@arm.com>
diff --git a/src/graph/nodes/ActivationLayer.cpp b/src/graph/nodes/ActivationLayer.cpp
index ea87fd9..d335214 100644
--- a/src/graph/nodes/ActivationLayer.cpp
+++ b/src/graph/nodes/ActivationLayer.cpp
@@ -25,6 +25,7 @@
 
 #include "arm_compute/graph/NodeContext.h"
 #include "arm_compute/graph/OperationRegistry.h"
+#include "support/ToolchainSupport.h"
 
 using namespace arm_compute::graph;
 
@@ -38,20 +39,17 @@
     ARM_COMPUTE_ERROR_ON(input == nullptr || input->tensor() == nullptr);
     ARM_COMPUTE_ERROR_ON(output == nullptr || output->tensor() == nullptr);
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
-
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
     // Create node context
-    NodeContext node_ctx("ActivationLayer");
+    NodeContext node_ctx(OperationType::ActivationLayer);
+    node_ctx.set_target(_target_hint);
     node_ctx.add_input(in);
     node_ctx.add_output(out);
     node_ctx.add_parameter<ActivationLayerInfo>("ActivationLayerInfo", _activation_info);
 
     // Get function
-    func = OperationRegistry::get().find_operation("ActivationLayer", _target_hint)->configure(node_ctx);
-
-    return func;
+    return OperationRegistry::get().find_operation(OperationType::ActivationLayer, _target_hint)->configure(node_ctx);
 }
diff --git a/src/graph/nodes/BatchNormalizationLayer.cpp b/src/graph/nodes/BatchNormalizationLayer.cpp
index db809f4..bce1901 100644
--- a/src/graph/nodes/BatchNormalizationLayer.cpp
+++ b/src/graph/nodes/BatchNormalizationLayer.cpp
@@ -23,60 +23,20 @@
  */
 #include "arm_compute/graph/nodes/BatchNormalizationLayer.h"
 
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/functions/CLBatchNormalizationLayer.h"
-#include "arm_compute/runtime/NEON/functions/NEBatchNormalizationLayer.h"
-#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/OperationRegistry.h"
 #include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
 
 using namespace arm_compute::graph;
 
-namespace
-{
-template <typename BatchBatchNormalizationLayer, typename TensorType, TargetHint target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *output, Tensor &mean, Tensor &var, Tensor &beta, Tensor &gamma, float epsilon)
-{
-    auto norm = arm_compute::support::cpp14::make_unique<BatchBatchNormalizationLayer>();
-    norm->configure(
-        dynamic_cast<TensorType *>(input),
-        dynamic_cast<TensorType *>(output),
-        dynamic_cast<TensorType *>(mean.set_target(target_hint)),
-        dynamic_cast<TensorType *>(var.set_target(target_hint)),
-        dynamic_cast<TensorType *>(beta.set_target(target_hint)),
-        dynamic_cast<TensorType *>(gamma.set_target(target_hint)),
-        epsilon);
-
-    return std::move(norm);
-}
-
-template <TargetHint                    target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, arm_compute::ITensor *output, Tensor &mean, Tensor &var, Tensor &beta, Tensor &gamma, float epsilon);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, arm_compute::ITensor *output, Tensor &mean, Tensor &var, Tensor &beta, Tensor &gamma,
-                                                                        float epsilon)
-{
-    return instantiate_function<arm_compute::CLBatchNormalizationLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, output, mean, var, beta, gamma, epsilon);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, arm_compute::ITensor *output, Tensor &mean, Tensor &var, Tensor &beta, Tensor &gamma, float epsilon)
-{
-    return instantiate_function<arm_compute::NEBatchNormalizationLayer, arm_compute::ITensor, TargetHint::NEON>(input, output, mean, var, beta, gamma, epsilon);
-}
-} // namespace
-
 std::unique_ptr<arm_compute::IFunction> BatchNormalizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
 {
     ARM_COMPUTE_ERROR_ON(input == nullptr || input->tensor() == nullptr);
     ARM_COMPUTE_ERROR_ON(output == nullptr || output->tensor() == nullptr);
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
-
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
     unsigned int batch_norm_size = in->info()->dimension(2);
     if(_mean.tensor() == nullptr)
@@ -96,21 +56,17 @@
         _gamma.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()));
     }
 
-    if(_target_hint == TargetHint::OPENCL)
-    {
-        func = instantiate<TargetHint::OPENCL>(in, out, _mean, _var, _beta, _gamma, _epsilon);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLBatchNormalizationLayer");
-    }
-    else
-    {
-        func = instantiate<TargetHint::NEON>(in, out, _mean, _var, _beta, _gamma, _epsilon);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEBatchNormalizationLayer");
-    }
+    // Create node context
+    NodeContext node_ctx(OperationType::BatchNormalizationLayer);
+    node_ctx.set_target(_target_hint);
+    node_ctx.add_input(in);
+    node_ctx.add_input(_mean.tensor());
+    node_ctx.add_input(_var.tensor());
+    node_ctx.add_input(_beta.tensor());
+    node_ctx.add_input(_gamma.tensor());
+    node_ctx.add_output(out);
+    node_ctx.add_parameter<float>("epsilon", _epsilon);
 
-    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
-                               << " Input shape: " << in->info()->tensor_shape()
-                               << " Output shape: " << out->info()->tensor_shape()
-                               << std::endl);
-
-    return func;
+    // Get function
+    return OperationRegistry::get().find_operation(OperationType::BatchNormalizationLayer, _target_hint)->configure(node_ctx);
 }
\ No newline at end of file
diff --git a/src/graph/nodes/DepthConcatenateLayer.cpp b/src/graph/nodes/DepthConcatenateLayer.cpp
deleted file mode 100644
index 2171db3..0000000
--- a/src/graph/nodes/DepthConcatenateLayer.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2017 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.
- */
-#include <algorithm>
-#include <vector>
-
-#include "arm_compute/graph/nodes/DepthConcatenateLayer.h"
-
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/functions/CLDepthConcatenate.h"
-#include "arm_compute/runtime/NEON/functions/NEDepthConcatenate.h"
-#include "arm_compute/runtime/Tensor.h"
-#include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
-
-using namespace arm_compute::graph;
-
-namespace
-{
-template <typename DepthConcatenationType, typename TensorType, TargetHint hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(std::vector<arm_compute::ITensor *> inputs, arm_compute::ITensor *output)
-{
-    auto                      depth_concat = arm_compute::support::cpp14::make_unique<DepthConcatenationType>();
-    std::vector<TensorType *> casted_inputs;
-    std::transform(inputs.begin(), inputs.end(), std::back_inserter(casted_inputs), [](arm_compute::ITensor * input)
-    {
-        return dynamic_cast<TensorType *>(input);
-    });
-    depth_concat->configure(
-        casted_inputs,
-        dynamic_cast<TensorType *>(output));
-
-    return std::move(depth_concat);
-}
-
-template <TargetHint                    hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(std::vector<arm_compute::ITensor *> inputs, arm_compute::ITensor *output);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(std::vector<arm_compute::ITensor *> inputs, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::CLDepthConcatenate, arm_compute::ICLTensor, TargetHint::OPENCL>(std::move(inputs), output);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(std::vector<arm_compute::ITensor *> inputs, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::NEDepthConcatenate, arm_compute::ITensor, TargetHint::NEON>(std::move(inputs), output);
-}
-} // namespace
-
-std::unique_ptr<arm_compute::IFunction> DepthConcatenateLayer::instantiate_node(GraphContext &ctx, std::vector<arm_compute::ITensor *> inputs, arm_compute::ITensor *output)
-{
-    std::unique_ptr<arm_compute::IFunction> func;
-    _hint   = ctx.hints().target_hint();
-    _inputs = std::move(inputs);
-    _output = output;
-
-    if(_hint == TargetHint::OPENCL)
-    {
-        func = instantiate<TargetHint::OPENCL>(_inputs, _output);
-    }
-    else
-    {
-        func = instantiate<TargetHint::NEON>(_inputs, _output);
-    }
-    return func;
-}
-
-void DepthConcatenateLayer::print_info()
-{
-    if(_hint == TargetHint::OPENCL)
-    {
-        std::cout << "Instantiating NEDepthConcatenate";
-    }
-    else
-    {
-        std::cout << "Instantiating CLDepthConcatenate";
-    }
-
-    for(const auto &i : _inputs)
-    {
-        std::cout << " Input: " << i->info()->tensor_shape();
-    }
-    std::cout << " Output: " << _output->info()->tensor_shape();
-}
diff --git a/src/graph/nodes/FloorLayer.cpp b/src/graph/nodes/FloorLayer.cpp
index 45e2c3e..21c82b8 100644
--- a/src/graph/nodes/FloorLayer.cpp
+++ b/src/graph/nodes/FloorLayer.cpp
@@ -23,70 +23,27 @@
  */
 #include "arm_compute/graph/nodes/FloorLayer.h"
 
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/functions/CLFloor.h"
-#include "arm_compute/runtime/NEON/functions/NEFloor.h"
-#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/OperationRegistry.h"
 #include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
 
 using namespace arm_compute::graph;
 
-namespace
-{
-template <typename FloorType, typename TensorType, TargetHint hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *output)
-{
-    auto floorlayer = arm_compute::support::cpp14::make_unique<FloorType>();
-    floorlayer->configure(
-        dynamic_cast<TensorType *>(input),
-        dynamic_cast<TensorType *>(output));
-
-    return std::move(floorlayer);
-}
-
-template <TargetHint                    target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, arm_compute::ITensor *output);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::CLFloor, arm_compute::ICLTensor, TargetHint::OPENCL>(input, output);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::NEFloor, arm_compute::ITensor, TargetHint::NEON>(input, output);
-}
-} // namespace
-
 std::unique_ptr<arm_compute::IFunction> FloorLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
 {
     ARM_COMPUTE_ERROR_ON(input == nullptr || input->tensor() == nullptr);
     ARM_COMPUTE_ERROR_ON(output == nullptr || output->tensor() == nullptr);
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
-
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
-    if(_target_hint == TargetHint::OPENCL)
-    {
-        func = instantiate<TargetHint::OPENCL>(in, out);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFloorLayer");
-    }
-    else
-    {
-        func = instantiate<TargetHint::NEON>(in, out);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFloorLayer");
-    }
+    // Create node context
+    NodeContext node_ctx(OperationType::FloorLayer);
+    node_ctx.set_target(_target_hint);
+    node_ctx.add_input(in);
+    node_ctx.add_output(out);
 
-    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
-                               << " Input shape: " << in->info()->tensor_shape()
-                               << " Output shape: " << out->info()->tensor_shape()
-                               << std::endl);
-
-    return func;
+    // Get function
+    return OperationRegistry::get().find_operation(OperationType::FloorLayer, _target_hint)->configure(node_ctx);
 }
diff --git a/src/graph/nodes/FullyConnectedLayer.cpp b/src/graph/nodes/FullyConnectedLayer.cpp
index 5f4807a..39ed827 100644
--- a/src/graph/nodes/FullyConnectedLayer.cpp
+++ b/src/graph/nodes/FullyConnectedLayer.cpp
@@ -23,11 +23,9 @@
  */
 #include "arm_compute/graph/nodes/FullyConnectedLayer.h"
 
-#include "arm_compute/core/Helpers.h"
-#include "arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h"
-#include "arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h"
+#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/OperationRegistry.h"
 #include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
 
 using namespace arm_compute::graph;
 
@@ -43,44 +41,6 @@
     }
     return TensorShape(output_neurons, batches);
 }
-template <typename FullyConnectedType, typename TensorType, TargetHint target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, Tensor &weights, Tensor &biases, arm_compute::ITensor *output)
-{
-    bool weights_are_loaded = weights.tensor() != nullptr;
-    bool biases_are_loaded  = biases.tensor() != nullptr;
-
-    auto conv = arm_compute::support::cpp14::make_unique<FullyConnectedType>();
-    conv->configure(
-        dynamic_cast<TensorType *>(input),
-        dynamic_cast<TensorType *>(weights.set_target(target_hint)),
-        dynamic_cast<TensorType *>(biases.set_target(target_hint)),
-        dynamic_cast<TensorType *>(output));
-    if(!weights_are_loaded)
-    {
-        weights.allocate_and_fill_if_needed();
-    }
-    if(!biases_are_loaded)
-    {
-        biases.allocate_and_fill_if_needed();
-    }
-
-    return std::move(conv);
-}
-
-template <TargetHint                    target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, Tensor &weights, Tensor &biases, arm_compute::ITensor *output);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, Tensor &weights, Tensor &biases, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::CLFullyConnectedLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, weights, biases, output);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, Tensor &weights, Tensor &biases, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::NEFullyConnectedLayer, arm_compute::ITensor, TargetHint::NEON>(input, weights, biases, output);
-}
 } // namespace
 
 std::unique_ptr<arm_compute::IFunction> FullyConnectedLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
@@ -90,6 +50,7 @@
 
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
     if(_weights.tensor() == nullptr)
     {
@@ -116,26 +77,27 @@
                                     calculate_fullyconnected_layer_output_shape(in->info()->tensor_shape(), _num_neurons),
                                     in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position());
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
+    bool weights_are_loaded = _weights.tensor() != nullptr;
+    bool biases_are_loaded  = _biases.tensor() != nullptr;
 
-    if(_target_hint == TargetHint::OPENCL)
+    // Create node context
+    NodeContext node_ctx(OperationType::FullyConnectedLayer);
+    node_ctx.set_target(_target_hint);
+    node_ctx.add_input(in);
+    node_ctx.add_input(_weights.set_target(_target_hint));
+    node_ctx.add_input(_biases.set_target(_target_hint));
+    node_ctx.add_output(out);
+
+    // Fill biases
+    if(!weights_are_loaded)
     {
-        func = instantiate<TargetHint::OPENCL>(in, _weights, _biases, out);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFullyConnectedLayer");
+        _weights.allocate_and_fill_if_needed();
     }
-    else
+    if(!biases_are_loaded)
     {
-        func = instantiate<TargetHint::NEON>(in, _weights, _biases, out);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFullyConnectedLayer");
+        _biases.allocate_and_fill_if_needed();
     }
 
-    ARM_COMPUTE_LOG_GRAPH_INFO(" Type: " << in->info()->data_type()
-                               << " Input Shape: " << in->info()->tensor_shape()
-                               << " Weights shape: " << _weights.info().tensor_shape()
-                               << " Biases Shape: " << _biases.info().tensor_shape()
-                               << " Output Shape: " << out->info()->tensor_shape()
-                               << std::endl);
-
-    return func;
+    // Get function
+    return OperationRegistry::get().find_operation(OperationType::FullyConnectedLayer, _target_hint)->configure(node_ctx);
 }
diff --git a/src/graph/nodes/L2NormalizeLayer.cpp b/src/graph/nodes/L2NormalizeLayer.cpp
index c5689e1..bcc3b94 100644
--- a/src/graph/nodes/L2NormalizeLayer.cpp
+++ b/src/graph/nodes/L2NormalizeLayer.cpp
@@ -23,72 +23,34 @@
  */
 #include "arm_compute/graph/nodes/L2NormalizeLayer.h"
 
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/functions/CLL2Normalize.h"
-#include "arm_compute/runtime/NEON/functions/NEL2Normalize.h"
-#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/OperationRegistry.h"
 #include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
 
 using namespace arm_compute::graph;
 
-namespace
+L2NormalizeLayer::L2NormalizeLayer(unsigned int axis, float epsilon)
+    : _axis(axis), _epsilon(epsilon)
 {
-template <typename L2NormalizeType, typename TensorType, TargetHint hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *output, unsigned int axis, float epsilon)
-{
-    auto l2norm = arm_compute::support::cpp14::make_unique<L2NormalizeType>();
-    l2norm->configure(
-        dynamic_cast<TensorType *>(input),
-        dynamic_cast<TensorType *>(output),
-        axis,
-        epsilon);
-
-    return std::move(l2norm);
 }
 
-template <TargetHint                    target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, arm_compute::ITensor *output, unsigned int axis, float epsilon);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, arm_compute::ITensor *output, unsigned int axis, float epsilon)
-{
-    return instantiate_function<arm_compute::CLL2Normalize, arm_compute::ICLTensor, TargetHint::OPENCL>(input, output, axis, epsilon);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, arm_compute::ITensor *output, unsigned int axis, float epsilon)
-{
-    return instantiate_function<arm_compute::NEL2Normalize, arm_compute::ITensor, TargetHint::NEON>(input, output, axis, epsilon);
-}
-} // namespace
-
 std::unique_ptr<arm_compute::IFunction> L2NormalizeLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
 {
     ARM_COMPUTE_ERROR_ON(input == nullptr || input->tensor() == nullptr);
     ARM_COMPUTE_ERROR_ON(output == nullptr || output->tensor() == nullptr);
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
-
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
-    if(_target_hint == TargetHint::OPENCL)
-    {
-        func = instantiate<TargetHint::OPENCL>(in, out, _axis, _epsilon);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLL2NormalizeLayer");
-    }
-    else
-    {
-        func = instantiate<TargetHint::NEON>(in, out, _axis, _epsilon);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEL2NormalizeLayer");
-    }
+    // Create node context
+    NodeContext node_ctx(OperationType::L2NormalizeLayer);
+    node_ctx.set_target(_target_hint);
+    node_ctx.add_input(in);
+    node_ctx.add_output(out);
+    node_ctx.add_parameter<unsigned int>("axis", _axis);
+    node_ctx.add_parameter<float>("epsilon", _epsilon);
 
-    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
-                               << " Input shape: " << in->info()->tensor_shape()
-                               << " Output shape: " << out->info()->tensor_shape()
-                               << std::endl);
-
-    return func;
+    // Get function
+    return OperationRegistry::get().find_operation(OperationType::L2NormalizeLayer, _target_hint)->configure(node_ctx);
 }
diff --git a/src/graph/nodes/NormalizationLayer.cpp b/src/graph/nodes/NormalizationLayer.cpp
index 680925a..5036231 100644
--- a/src/graph/nodes/NormalizationLayer.cpp
+++ b/src/graph/nodes/NormalizationLayer.cpp
@@ -23,45 +23,12 @@
  */
 #include "arm_compute/graph/nodes/NormalizationLayer.h"
 
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/functions/CLNormalizationLayer.h"
-#include "arm_compute/runtime/NEON/functions/NENormalizationLayer.h"
-#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/OperationRegistry.h"
 #include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
 
 using namespace arm_compute::graph;
 
-namespace
-{
-template <typename NormalizationType, typename TensorType, TargetHint target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *output, const NormalizationLayerInfo &norm_info)
-{
-    auto norm = arm_compute::support::cpp14::make_unique<NormalizationType>();
-    norm->configure(
-        dynamic_cast<TensorType *>(input),
-        dynamic_cast<TensorType *>(output),
-        norm_info);
-
-    return std::move(norm);
-}
-
-template <TargetHint                    target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, arm_compute::ITensor *output, const NormalizationLayerInfo &norm_info);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, arm_compute::ITensor *output, const NormalizationLayerInfo &norm_info)
-{
-    return instantiate_function<arm_compute::CLNormalizationLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, output, norm_info);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, arm_compute::ITensor *output, const NormalizationLayerInfo &norm_info)
-{
-    return instantiate_function<arm_compute::NENormalizationLayer, arm_compute::ITensor, TargetHint::NEON>(input, output, norm_info);
-}
-} // namespace
-
 NormalizationLayer::NormalizationLayer(const NormalizationLayerInfo norm_info)
     : _norm_info(norm_info)
 {
@@ -72,28 +39,17 @@
     ARM_COMPUTE_ERROR_ON(input == nullptr || input->tensor() == nullptr);
     ARM_COMPUTE_ERROR_ON(output == nullptr || output->tensor() == nullptr);
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
-
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
-    if(_target_hint == TargetHint::OPENCL)
-    {
-        func = instantiate<TargetHint::OPENCL>(in, out, _norm_info);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLNormalizationLayer");
-    }
-    else
-    {
-        func = instantiate<TargetHint::NEON>(in, out, _norm_info);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NENormalizationLayer");
-    }
+    // Create node context
+    NodeContext node_ctx(OperationType::NormalizationLayer);
+    node_ctx.set_target(_target_hint);
+    node_ctx.add_input(in);
+    node_ctx.add_output(out);
+    node_ctx.add_parameter<NormalizationLayerInfo>("NormalizationLayerInfo", _norm_info);
 
-    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
-                               << " Input shape: " << in->info()->tensor_shape()
-                               << " Output shape: " << out->info()->tensor_shape()
-                               << " Normalization info: " << _norm_info
-                               << std::endl);
-
-    return func;
+    // Get function
+    return OperationRegistry::get().find_operation(OperationType::NormalizationLayer, _target_hint)->configure(node_ctx);
 }
diff --git a/src/graph/nodes/PoolingLayer.cpp b/src/graph/nodes/PoolingLayer.cpp
index 6357915..26df585 100644
--- a/src/graph/nodes/PoolingLayer.cpp
+++ b/src/graph/nodes/PoolingLayer.cpp
@@ -23,45 +23,12 @@
  */
 #include "arm_compute/graph/nodes/PoolingLayer.h"
 
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/functions/CLPoolingLayer.h"
-#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h"
-#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/OperationRegistry.h"
 #include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
 
 using namespace arm_compute::graph;
 
-namespace
-{
-template <typename PoolingType, typename TensorType, TargetHint target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *output, const PoolingLayerInfo &pool_info)
-{
-    auto pool = arm_compute::support::cpp14::make_unique<PoolingType>();
-    pool->configure(
-        dynamic_cast<TensorType *>(input),
-        dynamic_cast<TensorType *>(output),
-        pool_info);
-
-    return std::move(pool);
-}
-
-template <TargetHint                    target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, arm_compute::ITensor *output, const PoolingLayerInfo &pool_info);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, arm_compute::ITensor *output, const PoolingLayerInfo &pool_info)
-{
-    return instantiate_function<arm_compute::CLPoolingLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, output, pool_info);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, arm_compute::ITensor *output, const PoolingLayerInfo &pool_info)
-{
-    return instantiate_function<arm_compute::NEPoolingLayer, arm_compute::ITensor, TargetHint::NEON>(input, output, pool_info);
-}
-} // namespace
-
 PoolingLayer::PoolingLayer(const PoolingLayerInfo pool_info)
     : _pool_info(pool_info)
 {
@@ -72,27 +39,17 @@
     ARM_COMPUTE_ERROR_ON(input == nullptr || input->tensor() == nullptr);
     ARM_COMPUTE_ERROR_ON(output == nullptr || output->tensor() == nullptr);
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
-
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
-    if(_target_hint == TargetHint::OPENCL)
-    {
-        func = instantiate<TargetHint::OPENCL>(in, out, _pool_info);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLPoolingLayer");
-    }
-    else
-    {
-        func = instantiate<TargetHint::NEON>(in, out, _pool_info);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEPoolingLayer");
-    }
+    // Create node context
+    NodeContext node_ctx(OperationType::PoolingLayer);
+    node_ctx.set_target(_target_hint);
+    node_ctx.add_input(in);
+    node_ctx.add_output(out);
+    node_ctx.add_parameter<PoolingLayerInfo>("PoolingLayerInfo", _pool_info);
 
-    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
-                               << " Input shape: " << in->info()->tensor_shape()
-                               << " Output shape: " << out->info()->tensor_shape()
-                               << " Pooling info: " << _pool_info << std::endl);
-
-    return func;
+    // Get function
+    return OperationRegistry::get().find_operation(OperationType::PoolingLayer, _target_hint)->configure(node_ctx);
 }
diff --git a/src/graph/nodes/SoftmaxLayer.cpp b/src/graph/nodes/SoftmaxLayer.cpp
index 3cdbc9c..62057c7 100644
--- a/src/graph/nodes/SoftmaxLayer.cpp
+++ b/src/graph/nodes/SoftmaxLayer.cpp
@@ -23,70 +23,27 @@
  */
 #include "arm_compute/graph/nodes/SoftmaxLayer.h"
 
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/functions/CLSoftmaxLayer.h"
-#include "arm_compute/runtime/NEON/functions/NESoftmaxLayer.h"
-#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/OperationRegistry.h"
 #include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
 
 using namespace arm_compute::graph;
 
-namespace
-{
-template <typename SoftmaxType, typename TensorType, TargetHint hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *output)
-{
-    auto softmax = arm_compute::support::cpp14::make_unique<SoftmaxType>();
-    softmax->configure(
-        dynamic_cast<TensorType *>(input),
-        dynamic_cast<TensorType *>(output));
-
-    return std::move(softmax);
-}
-
-template <TargetHint                    target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, arm_compute::ITensor *output);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::CLSoftmaxLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, output);
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, arm_compute::ITensor *output)
-{
-    return instantiate_function<arm_compute::NESoftmaxLayer, arm_compute::ITensor, TargetHint::NEON>(input, output);
-}
-} // namespace
-
 std::unique_ptr<arm_compute::IFunction> SoftmaxLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
 {
     ARM_COMPUTE_ERROR_ON(input == nullptr || input->tensor() == nullptr);
     ARM_COMPUTE_ERROR_ON(output == nullptr || output->tensor() == nullptr);
 
-    std::unique_ptr<arm_compute::IFunction> func;
-    _target_hint = ctx.hints().target_hint();
-
     arm_compute::ITensor *in  = input->tensor();
     arm_compute::ITensor *out = output->tensor();
+    _target_hint              = ctx.hints().target_hint();
 
-    if(_target_hint == TargetHint::OPENCL)
-    {
-        func = instantiate<TargetHint::OPENCL>(in, out);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLSoftmaxLayer");
-    }
-    else
-    {
-        func = instantiate<TargetHint::NEON>(in, out);
-        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NESoftmaxLayer");
-    }
+    // Create node context
+    NodeContext node_ctx(OperationType::SoftmaxLayer);
+    node_ctx.set_target(_target_hint);
+    node_ctx.add_input(in);
+    node_ctx.add_output(out);
 
-    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
-                               << " Input shape: " << in->info()->tensor_shape()
-                               << " Output shape: " << out->info()->tensor_shape()
-                               << std::endl);
-
-    return func;
+    // Get function
+    return OperationRegistry::get().find_operation(OperationType::SoftmaxLayer, _target_hint)->configure(node_ctx);
 }