aboutsummaryrefslogtreecommitdiff
path: root/src/graph
diff options
context:
space:
mode:
authorGeorgios Pinitas <georgios.pinitas@arm.com>2018-04-03 13:44:29 +0100
committerAnthony Barbier <anthony.barbier@arm.com>2018-11-02 16:49:16 +0000
commitd9eb27597eabe5b7c17520f4f9b3f8a282d72573 (patch)
tree9b2b7d74b0ef83623b18d6d4279a564e5b63d641 /src/graph
parenta8ca2b0cfe052c9a28b691317a674f28f495c139 (diff)
downloadComputeLibrary-d9eb27597eabe5b7c17520f4f9b3f8a282d72573.tar.gz
COMPMID-797: Switch to new graph.
- Cleaned up build system Change-Id: If2faa27ee5b31fa8b972836960ab3ef671059c8d Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/126435 Tested-by: Jenkins <bsgcomp@arm.com> Reviewed-by: Pablo Tello <pablo.tello@arm.com>
Diffstat (limited to 'src/graph')
-rw-r--r--src/graph/Graph.cpp344
-rw-r--r--src/graph/GraphBuilder.cpp394
-rw-r--r--src/graph/GraphContext.cpp56
-rw-r--r--src/graph/GraphManager.cpp117
-rw-r--r--src/graph/INode.cpp176
-rw-r--r--src/graph/OperationRegistry.cpp61
-rw-r--r--src/graph/PassManager.cpp88
-rw-r--r--src/graph/SubGraph.cpp110
-rw-r--r--src/graph/SubTensor.cpp119
-rw-r--r--src/graph/Tensor.cpp141
-rw-r--r--src/graph/Utils.cpp104
-rw-r--r--src/graph/Workload.cpp (renamed from src/graph/CL/CLUnmap.cpp)30
-rw-r--r--src/graph/backends/BackendRegistry.cpp (renamed from src/graph/nodes/QuantizationLayer.cpp)51
-rw-r--r--src/graph/backends/CL/CLDeviceBackend.cpp180
-rw-r--r--src/graph/backends/CL/CLFunctionsFactory.cpp590
-rw-r--r--src/graph/backends/CL/CLNodeValidator.cpp64
-rw-r--r--src/graph/backends/CL/CLSubTensorHandle.cpp78
-rw-r--r--src/graph/backends/CL/CLTensorHandle.cpp78
-rw-r--r--src/graph/backends/GLES/GCDeviceBackend.cpp138
-rw-r--r--src/graph/backends/GLES/GCFunctionsFactory.cpp507
-rw-r--r--src/graph/backends/GLES/GCNodeValidator.cpp122
-rw-r--r--src/graph/backends/GLES/GCTensorHandle.cpp (renamed from src/graph/NodeContext.cpp)61
-rw-r--r--src/graph/backends/NEON/NEDeviceBackend.cpp146
-rw-r--r--src/graph/backends/NEON/NEFunctionFactory.cpp563
-rw-r--r--src/graph/backends/NEON/NENodeValidator.cpp65
-rw-r--r--src/graph/backends/NEON/NESubTensorHandle.cpp75
-rw-r--r--src/graph/backends/NEON/NETensorHandle.cpp (renamed from src/graph/nodes/FloorLayer.cpp)66
-rw-r--r--src/graph/detail/ExecutionHelpers.cpp199
-rw-r--r--src/graph/frontend/Stream.cpp69
-rw-r--r--src/graph/frontend/SubStream.cpp (renamed from src/graph/CL/CLMap.cpp)42
-rw-r--r--src/graph/mutators/DepthConcatSubTensorMutator.cpp86
-rw-r--r--src/graph/mutators/InPlaceOperationMutator.cpp63
-rw-r--r--src/graph/mutators/NodeFusionMutator.cpp96
-rw-r--r--src/graph/mutators/SplitLayerSubTensorMutator.cpp89
-rw-r--r--src/graph/nodes/ActivationLayer.cpp56
-rw-r--r--src/graph/nodes/ActivationLayerNode.cpp83
-rw-r--r--src/graph/nodes/BatchNormalizationLayer.cpp105
-rw-r--r--src/graph/nodes/BatchNormalizationLayerNode.cpp94
-rw-r--r--src/graph/nodes/BranchLayer.cpp130
-rw-r--r--src/graph/nodes/ConstNode.cpp72
-rw-r--r--src/graph/nodes/ConvolutionLayer.cpp363
-rw-r--r--src/graph/nodes/ConvolutionLayerNode.cpp111
-rw-r--r--src/graph/nodes/DeQuantizationLayer.cpp68
-rw-r--r--src/graph/nodes/DepthConcatenateLayerNode.cpp133
-rw-r--r--src/graph/nodes/DepthConvertLayer.cpp58
-rw-r--r--src/graph/nodes/DepthwiseConvolutionLayer.cpp91
-rw-r--r--src/graph/nodes/DepthwiseConvolutionLayerNode.cpp110
-rw-r--r--src/graph/nodes/EltwiseLayerNode.cpp83
-rw-r--r--src/graph/nodes/FlattenLayer.cpp54
-rw-r--r--src/graph/nodes/FlattenLayerNode.cpp80
-rw-r--r--src/graph/nodes/FullyConnectedLayer.cpp125
-rw-r--r--src/graph/nodes/InputNode.cpp72
-rw-r--r--src/graph/nodes/L2NormalizeLayer.cpp56
-rw-r--r--src/graph/nodes/NormalizationLayer.cpp55
-rw-r--r--src/graph/nodes/NormalizationLayerNode.cpp84
-rw-r--r--src/graph/nodes/OutputNode.cpp66
-rw-r--r--src/graph/nodes/PoolingLayer.cpp55
-rw-r--r--src/graph/nodes/PoolingLayerNode.cpp103
-rw-r--r--src/graph/nodes/ReshapeLayer.cpp70
-rw-r--r--src/graph/nodes/ResidualLayer.cpp199
-rw-r--r--src/graph/nodes/SoftmaxLayer.cpp49
-rw-r--r--src/graph/nodes/SoftmaxLayerNode.cpp84
-rw-r--r--src/graph/nodes/SplitLayerNode.cpp117
-rw-r--r--src/graph/operations/CLSimpleOperations.cpp495
-rw-r--r--src/graph/operations/NESimpleOperations.cpp495
-rw-r--r--src/graph/printers/DotGraphPrinter.cpp173
66 files changed, 6004 insertions, 3153 deletions
diff --git a/src/graph/Graph.cpp b/src/graph/Graph.cpp
index 47bd672114..e1ffeed668 100644
--- a/src/graph/Graph.cpp
+++ b/src/graph/Graph.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -23,293 +23,205 @@
*/
#include "arm_compute/graph/Graph.h"
-#include "arm_compute/graph/CL/CLMap.h"
-#include "arm_compute/graph/CL/CLUnmap.h"
-#include "arm_compute/graph/INode.h"
-#include "arm_compute/graph/ITensorObject.h"
-#include "arm_compute/graph/Tensor.h"
-#include "arm_compute/runtime/CL/CLScheduler.h"
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/CL/CLTuner.h"
-#include "arm_compute/runtime/Tensor.h"
-#include "support/ToolchainSupport.h"
-
-#include <sys/stat.h>
-
-using namespace arm_compute::graph;
-
-namespace
+namespace arm_compute
{
-bool file_exists(const std::string &filename)
+namespace graph
{
- std::ifstream file(filename);
- return file.good();
-}
-
-} // namespace
-struct Stage
+Graph::Graph(GraphID id, std::string name)
+ : _id(id), _name(std::move(name)), _nodes(), _edges(), _tensors(), _tagged_nodes(), _mtx()
{
- ITensorObject *_input;
- ITensorObject *_output;
- std::unique_ptr<arm_compute::IFunction> _function;
-};
+}
-struct Graph::Private
-{
-public:
- /** Finalizes the current node's configuration
- *
- * @param _next_hint Device execution hint
- */
- void configure(GraphHints _next_hints);
-
- GraphContext _ctx{};
- std::vector<Stage> _pipeline{};
- std::vector<std::unique_ptr<ITensorObject>> _tensors{};
- std::vector<std::unique_ptr<INode>> _nodes{};
- GraphHints _current_hints{};
- GraphHints _next_hints{};
- std::unique_ptr<ITensorObject> _graph_input{ nullptr };
- std::unique_ptr<ITensorObject> _graph_output{ nullptr };
- std::unique_ptr<INode> _current_node{ nullptr };
- ITensorObject *_current_output{ nullptr };
- bool _info_enabled{ false };
- CLTuner _tuner{};
-
-private:
- ITensorObject *_current_input{ nullptr };
- GraphHints _previous_hints{};
-};
-
-static const std::string tuner_data_filename = "acl_tuner.csv";
-Graph::~Graph() //NOLINT
+bool Graph::remove_node(NodeID nid)
{
- if(_pimpl->_tuner.tune_new_kernels() && !_pimpl->_tuner.lws_table().empty())
+ if(nid >= _nodes.size())
{
- _pimpl->_tuner.save_to_file(tuner_data_filename);
+ return false;
}
-}
-Graph::Graph()
- : _pimpl{ new Private() }
-{
- graph_init();
-}
+ std::unique_ptr<INode> &node = _nodes[nid];
-void Graph::graph_init(const bool use_cl_tuner)
-{
- // Check if OpenCL is available and initialize the scheduler
- if(opencl_is_available())
+ // Remove node connections
+ if(node)
{
- if(_pimpl->_tuner.lws_table().empty() && file_exists(tuner_data_filename))
+ for(auto &input_eid : node->_input_edges)
{
- _pimpl->_tuner.load_from_file(tuner_data_filename);
+ remove_connection(input_eid);
}
- _pimpl->_tuner.set_tune_new_kernels(use_cl_tuner);
- arm_compute::CLScheduler::get().default_init(&_pimpl->_tuner);
- }
-}
-void Graph::run()
-{
- while(true)
- {
- if(_pimpl->_graph_input->has_accessor() && !_pimpl->_graph_input->call_accessor())
+ for(auto &outpud_eid : node->_output_edges)
{
- return;
+ remove_connection(outpud_eid);
}
+ }
- for(auto &stage : _pimpl->_pipeline)
- {
- stage._function->run();
- }
+ node = nullptr;
- if((_pimpl->_graph_output->has_accessor() && !_pimpl->_graph_output->call_accessor())
- || (!_pimpl->_graph_output->has_accessor()))
- {
- return;
- }
- }
+ return true;
}
-//Finalize current node's configuration
-void Graph::Private::configure(GraphHints _next_hints)
+EdgeID Graph::add_connection(NodeID source, size_t source_idx, NodeID sink, size_t sink_idx)
{
- ARM_COMPUTE_ERROR_ON(_current_node == nullptr);
- ARM_COMPUTE_ERROR_ON(_graph_input == nullptr);
+ std::lock_guard<arm_compute::Mutex> lock(_mtx);
- // Is it the first node of the graph ?
- if(_current_input == nullptr)
- {
- _graph_input->set_target(_current_hints.target_hint());
- _current_input = _graph_input.get();
- _previous_hints = _current_hints; // For the first node just assume the previous node was of the same type as this one
- }
+ // Check if node index is valid, if node exists and finally if the connection index is valid
+ ARM_COMPUTE_ERROR_ON((source >= _nodes.size()) || (_nodes[source] == nullptr) || (source_idx >= _nodes[source]->num_outputs()));
+ ARM_COMPUTE_ERROR_ON((sink >= _nodes.size()) || (_nodes[sink] == nullptr) || (sink_idx >= _nodes[sink]->num_inputs()));
- if(_current_node->supports_in_place())
- {
- _current_output = _current_input;
- }
+ // Get nodes
+ std::unique_ptr<INode> &source_node = _nodes[source];
+ std::unique_ptr<INode> &sink_node = _nodes[sink];
- //Automatic output configuration ?
- if(_current_output == nullptr)
+ // Check for duplicate connections (Check only sink node)
+ Edge *sink_node_edge = sink_node->input_edge(sink_idx);
+ if((sink_node_edge != nullptr) && (sink_node_edge->producer_id() == source) && (sink_node_edge->producer_idx() == source_idx)
+ && (sink_node_edge->consumer_id() == sink) && (sink_node_edge->consumer_idx() == sink_idx))
{
- _tensors.push_back(arm_compute::support::cpp14::make_unique<Tensor>(TensorInfo()));
- _current_output = _tensors.back().get();
+ return sink_node_edge->id();
}
- // If either the writer or reader node needs OpenCL then use OpenCL memory:
- if((_next_hints.target_hint() == TargetHint::OPENCL || _current_hints.target_hint() == TargetHint::OPENCL))
+ // Check if there is already a tensor associated with output if not create one
+ TensorID tid = source_node->output_id(source_idx);
+ if(tid == NullTensorID)
{
- _current_output->set_target(TargetHint::OPENCL);
- }
- else
- {
- _current_output->set_target(TargetHint::NEON);
+ tid = create_tensor();
}
+ std::unique_ptr<Tensor> &tensor = _tensors[tid];
+
+ // Create connections
+ EdgeID eid = _edges.size();
+ auto connection = arm_compute::support::cpp14::make_unique<Edge>(eid, source_node.get(), source_idx, sink_node.get(), sink_idx, tensor.get());
+ _edges.push_back(std::move(connection));
+
+ // Add connections to source and sink nodes
+ source_node->_output_edges.insert(eid);
+ sink_node->_input_edges[sink_idx] = eid;
- // Instantiate Node
- _ctx.hints() = _current_hints;
- std::unique_ptr<arm_compute::IFunction> func = _current_node->instantiate_node(_ctx, _current_input, _current_output);
+ // Set tensor output node
+ source_node->_outputs[source_idx] = tid;
+
+ // Bind tensor to the edge
+ tensor->bind_edge(eid);
+
+ // Try and propagate shapes in sink node
+ sink_node->forward_descriptors();
+
+ return eid;
+}
- // If the operation is done in-place, do not allocate or it will prevent following layers from performing the configuration
- if(!_current_node->supports_in_place())
+bool Graph::remove_connection(EdgeID eid)
+{
+ if(eid >= _edges.size())
{
- // Allocate current input
- _current_input->allocate();
+ return false;
}
- // Map input if needed
- if(_current_input->target() == TargetHint::OPENCL)
+ std::unique_ptr<Edge> &edge = _edges[eid];
+
+ // Remove node connections
+ if(edge != nullptr)
{
- if(_previous_hints.target_hint() == TargetHint::NEON)
+ // Get tensor bound to the edge
+ if(edge->tensor() != nullptr)
+ {
+ edge->tensor()->unbind_edge(eid);
+ }
+
+ // Remove edges from source node
+ if(edge->producer() != nullptr)
{
- ARM_COMPUTE_ERROR_ON(_current_hints.target_hint() == TargetHint::NEON);
- _pipeline.push_back({ _current_input, _current_input, arm_compute::support::cpp14::make_unique<CLUnmap>(_current_input) });
+ edge->producer()->_output_edges.erase(eid);
}
- if(_current_hints.target_hint() == TargetHint::NEON)
+
+ // Remove edges from sink node
+ if((edge->consumer() != nullptr) && (edge->consumer_idx() < edge->consumer()->_input_edges.size()))
{
- ARM_COMPUTE_ERROR_ON(_previous_hints.target_hint() == TargetHint::NEON);
- _pipeline.push_back({ _current_input, _current_input, arm_compute::support::cpp14::make_unique<CLMap>(_current_input, true) });
+ edge->consumer()->_input_edges[edge->consumer_idx()] = EmptyEdgeID;
}
}
- _pipeline.push_back({ _current_input, _current_output, std::move(func) });
+ // Clear edge
+ edge = nullptr;
- _current_input = _current_output;
- _current_output = nullptr;
- std::swap(_previous_hints, _current_hints);
- std::swap(_current_hints, _next_hints);
+ return true;
}
-void Graph::add_node(std::unique_ptr<INode> node)
+TensorID Graph::create_tensor(TensorDescriptor desc)
{
- ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_graph_input == nullptr, "The graph's input must be set before the first node is added");
- ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_graph_output != nullptr, "Nothing can be added after the output tensor");
- //Trigger the creation of the current Node:
-
- GraphHints _next_hints = _pimpl->_next_hints;
- _next_hints.set_target_hint(node->override_target_hint(_pimpl->_next_hints.target_hint()));
- ARM_COMPUTE_ERROR_ON(_next_hints.target_hint() == TargetHint::DONT_CARE);
- if(_pimpl->_current_node)
- {
- //Finalize the previous Node:
- _pimpl->configure(_pimpl->_next_hints);
- }
- else
- {
- // If that's the first node then use the same TargetHint before and after the node.
- _pimpl->_current_hints = _next_hints;
- }
- if(_pimpl->_current_node)
- {
- _pimpl->_nodes.push_back(std::move(_pimpl->_current_node));
- }
- _pimpl->_current_node = std::move(node);
+ TensorID tid = _tensors.size();
+ auto tensor = support::cpp14::make_unique<Tensor>(tid, desc);
+ _tensors.push_back(std::move(tensor));
+
+ return tid;
}
-//Add a tensor with an Accessor (i.e either the input or output of the graph)
-void Graph::add_tensor_object(std::unique_ptr<ITensorObject> tensor)
+std::string Graph::name() const
{
- // If it's the first Tensor added then it will be the input of the Graph.
- if(_pimpl->_graph_input == nullptr)
- {
- ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr);
- ARM_COMPUTE_ERROR_ON(_pimpl->_current_node != nullptr);
- _pimpl->_graph_input = std::move(tensor);
- }
- else
- {
- // Else it will be the output of the Graph
- ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr);
- ARM_COMPUTE_ERROR_ON(_pimpl->_current_node == nullptr);
- _pimpl->_graph_output = std::move(tensor);
- _pimpl->_current_output = _pimpl->_graph_output.get();
-
- // Finalize the graph by configuring the last Node of the graph:
- _pimpl->configure(_pimpl->_current_hints); // Ignore _next_hint as this is the last node, and just use the same hint as before this node.
- _pimpl->_graph_output->allocate();
- }
+ return _name;
}
-bool Graph::opencl_is_available()
+GraphID Graph::id() const
{
- return arm_compute::opencl_is_available();
+ return _id;
}
-arm_compute::GPUTarget Graph::gpu_target()
+const std::vector<NodeID> &Graph::inputs()
{
- // Check if OpenCL is available before returning the GPU target
- if(opencl_is_available())
- {
- return arm_compute::CLScheduler::get().target();
- }
- else
- {
- return GPUTarget::MIDGARD;
- }
+ return _tagged_nodes[NodeType::Input];
+}
+
+std::vector<std::unique_ptr<INode>> &Graph::nodes()
+{
+ return _nodes;
}
-void Graph::set_temp(TensorInfo &&tmp)
+const std::vector<std::unique_ptr<INode>> &Graph::nodes() const
{
- ARM_COMPUTE_ERROR_ON(_pimpl->_graph_input == nullptr);
- ARM_COMPUTE_ERROR_ON(_pimpl->_graph_output != nullptr);
- ARM_COMPUTE_ERROR_ON_MSG(_pimpl->_current_output != nullptr, "TensorInfo for temporary tensor already set");
+ return _nodes;
+}
+
+const std::vector<std::unique_ptr<Edge>> &Graph::edges() const
+{
+ return _edges;
+}
- _pimpl->_tensors.push_back(arm_compute::support::cpp14::make_unique<Tensor>(std::move(tmp)));
- _pimpl->_current_output = _pimpl->_tensors.back().get();
+std::vector<std::unique_ptr<Tensor>> &Graph::tensors()
+{
+ return _tensors;
+}
+
+const std::vector<std::unique_ptr<Tensor>> &Graph::tensors() const
+{
+ return _tensors;
}
-GraphHints &Graph::hints()
+const INode *Graph::node(NodeID id) const
{
- return _pimpl->_next_hints;
+ return (id >= _nodes.size()) ? nullptr : _nodes[id].get();
}
-Graph &arm_compute::graph::operator<<(Graph &graph, TensorInfo &&info)
+INode *Graph::node(NodeID id)
{
- graph.set_temp(std::move(info));
- return graph;
+ return (id >= _nodes.size()) ? nullptr : _nodes[id].get();
}
-Graph &arm_compute::graph::operator<<(Graph &graph, Tensor &&tensor)
+const Edge *Graph::edge(EdgeID id) const
{
- graph.add_tensor_object(arm_compute::support::cpp14::make_unique<Tensor>(std::move(tensor)));
- return graph;
+ return (id >= _edges.size()) ? nullptr : _edges[id].get();
}
-Graph &arm_compute::graph::operator<<(Graph &graph, SubTensor &&sub_tensor)
+Edge *Graph::edge(EdgeID id)
{
- graph.add_tensor_object(arm_compute::support::cpp14::make_unique<SubTensor>(std::move(sub_tensor)));
- return graph;
+ return (id >= _edges.size()) ? nullptr : _edges[id].get();
}
-Graph &arm_compute::graph::operator<<(Graph &graph, TargetHint target_hint)
+const Tensor *Graph::tensor(TensorID id) const
{
- graph.hints().set_target_hint(target_hint);
- return graph;
+ return (id >= _tensors.size()) ? nullptr : _tensors[id].get();
}
-Graph &arm_compute::graph::operator<<(Graph &graph, ConvolutionMethodHint conv_method_hint)
+Tensor *Graph::tensor(TensorID id)
{
- graph.hints().set_convolution_method_hint(conv_method_hint);
- return graph;
+ return (id >= _tensors.size()) ? nullptr : _tensors[id].get();
}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/GraphBuilder.cpp b/src/graph/GraphBuilder.cpp
new file mode 100644
index 0000000000..0d1bdc3596
--- /dev/null
+++ b/src/graph/GraphBuilder.cpp
@@ -0,0 +1,394 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/GraphBuilder.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/Utils.h"
+#include "arm_compute/graph/algorithms/BFS.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+
+#define CHECK_NODEIDX_PAIR(pair, g) \
+ ARM_COMPUTE_ERROR_ON(((pair).node_id >= (g).nodes().size()) || ((g).node((pair).node_id) == nullptr) || ((pair).index >= (g).node((pair).node_id)->num_outputs()));
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace
+{
+Status set_node_params(Graph &g, NodeID nid, NodeParams &params)
+{
+ INode *node = g.node(nid);
+ ARM_COMPUTE_RETURN_ERROR_ON(!node);
+
+ node->set_common_node_parameters(params);
+
+ return Status{};
+}
+
+Status set_accessor_on_node(Graph &g, NodeID nid, bool is_output, size_t idx, ITensorAccessorUPtr accessor)
+{
+ INode *node = g.node(nid);
+ ARM_COMPUTE_RETURN_ERROR_ON(!node);
+
+ Tensor *tensor = is_output ? node->output(idx) : node->input(idx);
+ ARM_COMPUTE_RETURN_ERROR_ON(!tensor);
+
+ tensor->set_accessor(std::move(accessor));
+
+ return Status{};
+}
+
+NodeID add_const_node_with_name(Graph &g, NodeParams params, const std::string &name, TensorDescriptor desc, ITensorAccessorUPtr accessor)
+{
+ params.name = params.name.empty() ? "" : params.name + name;
+ auto nid = GraphBuilder::add_const_node(g, params, desc, std::move(accessor));
+ set_node_params(g, nid, params);
+ return nid;
+}
+
+template <typename NT, typename... Args>
+NodeID create_simple_single_input_output_node(Graph &g, NodeParams &params, NodeIdxPair input, Args &&... args)
+{
+ CHECK_NODEIDX_PAIR(input, g);
+
+ NodeID nid = g.add_node<NT>(std::forward<Args>(args)...);
+ g.add_connection(input.node_id, input.index, nid, 0);
+ set_node_params(g, nid, params);
+
+ return nid;
+}
+
+NodeID create_grouped_convolution(Graph &g, NodeParams &params, NodeIdxPair input, NodeID weights, NodeID bias,
+ PadStrideInfo conv_info, ConvolutionMethod method, unsigned int num_groups)
+{
+ bool has_bias = (bias != EmptyNodeID);
+
+ // Split input
+ NodeID input_split = GraphBuilder::add_split_node(g, params, input, num_groups, 2);
+
+ // Split weights
+ NodeID weights_split = GraphBuilder::add_split_node(g, params, { weights, 0 }, num_groups, 3);
+
+ // Split bias
+ NodeID bias_split = EmptyNodeID;
+ if(has_bias)
+ {
+ // Split bias
+ bias_split = GraphBuilder::add_split_node(g, params, { bias, 0 }, num_groups, 0);
+ }
+
+ std::vector<NodeIdxPair> convolution_outputs;
+ for(unsigned int i = 0; i < num_groups; ++i)
+ {
+ NodeID conv_nid = g.add_node<ConvolutionLayerNode>(conv_info, method);
+ g.add_connection(input_split, i, conv_nid, 0);
+ g.add_connection(weights_split, i, conv_nid, 1);
+ if(has_bias)
+ {
+ g.add_connection(bias_split, i, conv_nid, 2);
+ }
+ set_node_params(g, conv_nid, params);
+ convolution_outputs.push_back({ conv_nid, 0 });
+ }
+
+ // Depth concatenate output
+ return GraphBuilder::add_depth_concatenate_node(g, params, convolution_outputs);
+}
+} // namespace
+
+NodeID GraphBuilder::add_const_node(Graph &g, NodeParams params, TensorDescriptor desc, ITensorAccessorUPtr accessor)
+{
+ auto nid = g.add_node<ConstNode>(desc);
+ set_node_params(g, nid, params);
+ set_accessor_on_node(g, nid, true, 0, std::move(accessor));
+ return nid;
+}
+
+NodeID GraphBuilder::add_input_node(Graph &g, NodeParams params, TensorDescriptor desc, ITensorAccessorUPtr accessor)
+{
+ auto nid = g.add_node<InputNode>(desc);
+ set_node_params(g, nid, params);
+ set_accessor_on_node(g, nid, true, 0, std::move(accessor));
+ return nid;
+}
+
+NodeID GraphBuilder::add_output_node(Graph &g, NodeParams params, NodeIdxPair input, ITensorAccessorUPtr accessor)
+{
+ CHECK_NODEIDX_PAIR(input, g);
+
+ NodeID nid = g.add_node<OutputNode>();
+ g.add_connection(input.node_id, input.index, nid, 0);
+ set_node_params(g, nid, params);
+ set_accessor_on_node(g, nid, false, 0, std::move(accessor));
+
+ return nid;
+}
+
+NodeID GraphBuilder::add_activation_node(Graph &g, NodeParams params, NodeIdxPair input, ActivationLayerInfo act_info)
+{
+ return create_simple_single_input_output_node<ActivationLayerNode>(g, params, input, act_info);
+}
+
+NodeID GraphBuilder::add_batch_normalization_node(Graph &g, NodeParams params, NodeIdxPair input, float epsilon,
+ ITensorAccessorUPtr mean_accessor, ITensorAccessorUPtr var_accessor,
+ ITensorAccessorUPtr beta_accessor, ITensorAccessorUPtr gamma_accessor)
+{
+ CHECK_NODEIDX_PAIR(input, g);
+
+ bool has_beta = (beta_accessor != nullptr);
+ bool has_gamma = (gamma_accessor != nullptr);
+
+ // Get input tensor descriptor
+ const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]);
+
+ // Calculate Common Descriptor
+ TensorDescriptor common_desc = input_tensor_desc;
+ common_desc.shape = TensorShape(common_desc.shape.z());
+
+ // Create mean and nodes
+ auto mean_nid = add_const_node_with_name(g, params, "Mean", common_desc, std::move(mean_accessor));
+ auto var_nid = add_const_node_with_name(g, params, "Variance", common_desc, std::move(var_accessor));
+
+ // Create beta node
+ NodeID beta_nid = EmptyNodeID;
+ if(has_beta)
+ {
+ beta_nid = add_const_node_with_name(g, params, "Beta", common_desc, std::move(beta_accessor));
+ }
+
+ // Create gamma node
+ NodeID gamma_nid = EmptyNodeID;
+ if(has_gamma)
+ {
+ gamma_nid = add_const_node_with_name(g, params, "Gamma", common_desc, std::move(gamma_accessor));
+ }
+
+ // Create batch normalization node and add connections
+ NodeID batch_norm_nid = g.add_node<BatchNormalizationLayerNode>(epsilon);
+ g.add_connection(input.node_id, input.index, batch_norm_nid, 0);
+ g.add_connection(mean_nid, 0, batch_norm_nid, 1);
+ g.add_connection(var_nid, 0, batch_norm_nid, 2);
+ if(has_beta)
+ {
+ g.add_connection(beta_nid, 0, batch_norm_nid, 3);
+ }
+ if(has_gamma)
+ {
+ g.add_connection(gamma_nid, 0, batch_norm_nid, 4);
+ }
+ set_node_params(g, batch_norm_nid, params);
+
+ return batch_norm_nid;
+}
+
+NodeID GraphBuilder::add_convolution_node(Graph &g, NodeParams params, NodeIdxPair input,
+ Size2D kernel_spatial_extend, unsigned int depth, PadStrideInfo conv_info,
+ unsigned int num_groups, ConvolutionMethod method,
+ ITensorAccessorUPtr weights_accessor, ITensorAccessorUPtr bias_accessor)
+{
+ CHECK_NODEIDX_PAIR(input, g);
+ ARM_COMPUTE_ERROR_ON(depth == 0);
+ ARM_COMPUTE_ERROR_ON((kernel_spatial_extend.width == 0) || (kernel_spatial_extend.height == 0));
+
+ bool has_bias = (bias_accessor != nullptr);
+
+ // Get input tensor descriptor
+ const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]);
+
+ // Create weights node
+ TensorDescriptor w_desc = input_tensor_desc;
+ w_desc.shape = TensorShape(kernel_spatial_extend.width, kernel_spatial_extend.height, w_desc.shape.z() / num_groups, depth);
+ NodeID w_nid = add_const_node_with_name(g, params, "Weights", w_desc, std::move(weights_accessor));
+
+ // Create bias nodes
+ NodeID b_nid = EmptyNodeID;
+ if(has_bias)
+ {
+ TensorDescriptor b_desc = input_tensor_desc;
+ b_desc.shape = TensorShape(depth);
+ b_nid = add_const_node_with_name(g, params, "Bias", b_desc, std::move(bias_accessor));
+ }
+
+ if(num_groups == 1)
+ {
+ // Create convolution node and connect
+ NodeID conv_nid = g.add_node<ConvolutionLayerNode>(conv_info, method);
+ g.add_connection(input.node_id, input.index, conv_nid, 0);
+ g.add_connection(w_nid, 0, conv_nid, 1);
+ if(has_bias)
+ {
+ g.add_connection(b_nid, 0, conv_nid, 2);
+ }
+ set_node_params(g, conv_nid, params);
+
+ return conv_nid;
+ }
+ else
+ {
+ return create_grouped_convolution(g, params, input, w_nid, b_nid, conv_info, method, num_groups);
+ }
+}
+
+NodeID GraphBuilder::add_depth_concatenate_node(Graph &g, NodeParams params, std::vector<NodeIdxPair> inputs)
+{
+ ARM_COMPUTE_ERROR_ON(inputs.size() == 0);
+
+ NodeID nid = g.add_node<DepthConcatenateLayerNode>(inputs.size());
+
+ unsigned int i = 0;
+ for(const auto &input : inputs)
+ {
+ CHECK_NODEIDX_PAIR(input, g);
+ g.add_connection(input.node_id, input.index, nid, i++);
+ }
+ set_node_params(g, nid, params);
+
+ return nid;
+}
+
+NodeID GraphBuilder::add_depthwise_convolution_node(Graph &g, NodeParams params, NodeIdxPair input, Size2D kernel_spatial_extend, PadStrideInfo conv_info,
+ DepthwiseConvolutionMethod method,
+ ITensorAccessorUPtr weights_accessor, ITensorAccessorUPtr bias_accessor)
+{
+ CHECK_NODEIDX_PAIR(input, g);
+ ARM_COMPUTE_ERROR_ON((kernel_spatial_extend.width == 0) || (kernel_spatial_extend.height == 0));
+
+ bool has_bias = (bias_accessor != nullptr);
+
+ // Get input tensor descriptor
+ const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]);
+
+ // Create weights node
+ TensorDescriptor w_desc = input_tensor_desc;
+ w_desc.shape = TensorShape(kernel_spatial_extend.width, kernel_spatial_extend.height, w_desc.shape.z());
+ NodeID w_nid = add_const_node_with_name(g, params, "Weights", w_desc, std::move(weights_accessor));
+
+ // Create bias nodes
+ NodeID b_nid = EmptyNodeID;
+ if(has_bias)
+ {
+ TensorDescriptor b_desc = input_tensor_desc;
+ b_desc.shape = TensorShape(b_desc.shape.z());
+ b_nid = add_const_node_with_name(g, params, "Bias", b_desc, std::move(bias_accessor));
+ }
+
+ // Create convolution node and connect
+ NodeID conv_nid = g.add_node<DepthwiseConvolutionLayerNode>(conv_info, method);
+ g.add_connection(input.node_id, input.index, conv_nid, 0);
+ g.add_connection(w_nid, 0, conv_nid, 1);
+ if(has_bias)
+ {
+ g.add_connection(b_nid, 0, conv_nid, 2);
+ }
+ set_node_params(g, conv_nid, params);
+
+ return conv_nid;
+}
+
+NodeID GraphBuilder::add_elementwise_node(Graph &g, NodeParams params, NodeIdxPair input0, NodeIdxPair input1, EltwiseOperation operation)
+{
+ CHECK_NODEIDX_PAIR(input0, g);
+ CHECK_NODEIDX_PAIR(input1, g);
+
+ NodeID nid = g.add_node<EltwiseLayerNode>(operation);
+
+ g.add_connection(input0.node_id, input0.index, nid, 0);
+ g.add_connection(input1.node_id, input1.index, nid, 1);
+
+ set_node_params(g, nid, params);
+
+ return nid;
+}
+
+NodeID GraphBuilder::add_flatten_node(Graph &g, NodeParams params, NodeIdxPair input)
+{
+ return create_simple_single_input_output_node<FlattenLayerNode>(g, params, input);
+}
+
+NodeID GraphBuilder::add_fully_connected_layer(Graph &g, NodeParams params, NodeIdxPair input, unsigned int num_outputs,
+ ITensorAccessorUPtr weights_accessor, ITensorAccessorUPtr bias_accessor)
+{
+ CHECK_NODEIDX_PAIR(input, g);
+ ARM_COMPUTE_ERROR_ON(num_outputs == 0);
+
+ bool has_bias = (bias_accessor != nullptr);
+
+ // Get input tensor descriptor
+ const TensorDescriptor input_tensor_desc = get_tensor_descriptor(g, g.node(input.node_id)->outputs()[0]);
+
+ // Create weights node
+ TensorDescriptor w_desc = input_tensor_desc;
+ w_desc.shape = FullyConnectedLayerNode::compute_weights_shape(input_tensor_desc.shape, num_outputs);
+ NodeID w_nid = add_const_node_with_name(g, params, "Weights", w_desc, std::move(weights_accessor));
+
+ // Create bias nodes
+ NodeID b_nid = EmptyNodeID;
+ if(has_bias)
+ {
+ TensorDescriptor b_desc = input_tensor_desc;
+ b_desc.shape = TensorShape(num_outputs);
+ b_nid = add_const_node_with_name(g, params, "Bias", b_desc, std::move(bias_accessor));
+ }
+
+ // Create convolution node and connect
+ NodeID fc_nid = g.add_node<FullyConnectedLayerNode>(num_outputs);
+ g.add_connection(input.node_id, input.index, fc_nid, 0);
+ g.add_connection(w_nid, 0, fc_nid, 1);
+ if(has_bias)
+ {
+ g.add_connection(b_nid, 0, fc_nid, 2);
+ }
+
+ set_node_params(g, fc_nid, params);
+
+ return fc_nid;
+}
+
+NodeID GraphBuilder::add_normalization_node(Graph &g, NodeParams params, NodeIdxPair input, NormalizationLayerInfo norm_info)
+{
+ return create_simple_single_input_output_node<NormalizationLayerNode>(g, params, input, norm_info);
+}
+
+NodeID GraphBuilder::add_pooling_node(Graph &g, NodeParams params, NodeIdxPair input, PoolingLayerInfo pool_info)
+{
+ return create_simple_single_input_output_node<PoolingLayerNode>(g, params, input, pool_info);
+}
+
+NodeID GraphBuilder::add_reshape_node(Graph &g, NodeParams params, NodeIdxPair input, TensorShape shape)
+{
+ return create_simple_single_input_output_node<ReshapeLayerNode>(g, params, input, shape);
+}
+
+NodeID GraphBuilder::add_softmax_node(Graph &g, NodeParams params, NodeIdxPair input, float beta)
+{
+ return create_simple_single_input_output_node<SoftmaxLayerNode>(g, params, input, beta);
+}
+
+NodeID GraphBuilder::add_split_node(Graph &g, NodeParams params, NodeIdxPair input, unsigned int num_splits, unsigned int axis)
+{
+ return create_simple_single_input_output_node<SplitLayerNode>(g, params, input, num_splits, axis);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/GraphContext.cpp b/src/graph/GraphContext.cpp
index bfc6fcdfca..6fc45c0aa7 100644
--- a/src/graph/GraphContext.cpp
+++ b/src/graph/GraphContext.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -22,45 +22,53 @@
* SOFTWARE.
*/
#include "arm_compute/graph/GraphContext.h"
+#include <arm_compute/graph.h>
-using namespace arm_compute::graph;
-
-GraphHints::GraphHints(TargetHint target_hint, ConvolutionMethodHint conv_method_hint)
- : _target_hint(target_hint), _convolution_method_hint(conv_method_hint)
+namespace arm_compute
{
-}
-
-void GraphHints::set_target_hint(TargetHint target_hint)
+namespace graph
{
- _target_hint = target_hint;
-}
-
-void GraphHints::set_convolution_method_hint(ConvolutionMethodHint convolution_method)
+GraphContext::GraphContext()
+ : _config(), _memory_managers()
{
- _convolution_method_hint = convolution_method;
}
-TargetHint GraphHints::target_hint() const
+const GraphConfig &GraphContext::config() const
{
- return _target_hint;
+ return _config;
}
-ConvolutionMethodHint GraphHints::convolution_method_hint() const
+void GraphContext::set_config(const GraphConfig &config)
{
- return _convolution_method_hint;
+ _config = config;
}
-GraphContext::GraphContext()
- : _hints()
+bool GraphContext::insert_memory_management_ctx(MemoryManagerContext &&memory_ctx)
{
+ Target target = memory_ctx.target;
+ if(target == Target::UNSPECIFIED || _memory_managers.find(target) != std::end(_memory_managers))
+ {
+ return false;
+ }
+
+ _memory_managers[target] = std::move(memory_ctx);
+ return true;
}
-GraphHints &GraphContext::hints()
+MemoryManagerContext *GraphContext::memory_management_ctx(Target target)
{
- return _hints;
+ return (_memory_managers.find(target) != std::end(_memory_managers)) ? &_memory_managers[target] : nullptr;
}
-const GraphHints &GraphContext::hints() const
+void GraphContext::finalize()
{
- return _hints;
-} \ No newline at end of file
+ for(auto &mm_obj : _memory_managers)
+ {
+ if(mm_obj.second.mm != nullptr)
+ {
+ mm_obj.second.mm->finalize();
+ }
+ }
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/GraphManager.cpp b/src/graph/GraphManager.cpp
new file mode 100644
index 0000000000..759300e0c9
--- /dev/null
+++ b/src/graph/GraphManager.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/GraphManager.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/PassManager.h"
+#include "arm_compute/graph/Utils.h"
+#include "arm_compute/graph/detail/ExecutionHelpers.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+GraphManager::GraphManager()
+ : _workloads()
+{
+ detail::default_initialize_backends();
+}
+
+void GraphManager::finalize_graph(Graph &graph, GraphContext &ctx, PassManager &pm, Target target)
+{
+ // Setup graph context if not done manually
+ setup_default_graph_context(ctx);
+
+ // Check if graph has been registered
+ ARM_COMPUTE_ERROR_ON_MSG(_workloads.find(graph.id()) != std::end(_workloads), "Graph is already registered!");
+
+ // Force target to all graph construct
+ // TODO (geopin01) : Support heterogeneous execution
+ Target forced_target = is_target_supported(target) ? target : get_default_target();
+ force_target_to_graph(graph, forced_target);
+
+ // Configure all tensors
+ detail::configure_all_tensors(graph);
+
+ // Apply all mutating passes
+ pm.run_all(graph);
+
+ // TODO (geopin01): Perform a graph validation
+
+ // Perform topological sort
+ // FIXME : Sort nodes and pass sorted indices in configure all nodes
+
+ // Validate all nodes
+ detail::validate_all_nodes(graph);
+
+ // Configure all nodes
+ auto workload = detail::configure_all_nodes(graph, ctx);
+ ARM_COMPUTE_ERROR_ON_MSG(workload.tasks.empty(), "Could not configure all nodes!");
+
+ // Allocate all tensors
+ detail::allocate_all_tensors(graph);
+
+ // Call accessors on all Const nodes
+ detail::call_all_const_node_accessors(graph);
+
+ _workloads.insert(std::make_pair(graph.id(), std::move(workload)));
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Created workload for graph with ID : " << graph.id().get() << std::endl);
+
+ // Finalize Graph context
+ ctx.finalize();
+
+ // Make first run
+ execute_graph(graph);
+
+ // Release all unused const nodes
+ detail::release_unused_tensors(graph);
+}
+
+void GraphManager::execute_graph(Graph &graph)
+{
+ // Check if graph is finalized
+ auto it = _workloads.find(graph.id());
+ ARM_COMPUTE_ERROR_ON_MSG(it == std::end(_workloads), "Graph is not registered!");
+
+ // Call input accessors
+ detail::call_all_input_node_accessors(it->second);
+
+ // Run graph
+ detail::call_all_tasks(it->second);
+
+ // Call output accessors
+ detail::call_all_output_node_accessors(it->second);
+}
+
+void GraphManager::invalidate_graph(Graph &graph)
+{
+ auto it = _workloads.find(graph.id());
+ ARM_COMPUTE_ERROR_ON_MSG(it == std::end(_workloads), "Graph is not registered!");
+
+ _workloads.erase(it);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/INode.cpp b/src/graph/INode.cpp
index c753f66b43..c1c18e5853 100644
--- a/src/graph/INode.cpp
+++ b/src/graph/INode.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -23,33 +23,171 @@
*/
#include "arm_compute/graph/INode.h"
-#include "arm_compute/core/CL/OpenCL.h"
-#include "arm_compute/core/Validate.h"
+#include "arm_compute/core/Error.h"
+#include "arm_compute/graph/Edge.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/Tensor.h"
-using namespace arm_compute::graph;
+namespace arm_compute
+{
+namespace graph
+{
+// *INDENT-OFF*
+// clang-format off
+INode::INode()
+ : _graph(nullptr), _id(EmptyNodeID), _common_params({ "", Target::UNSPECIFIED}),
+ _outputs(), _input_edges(), _output_edges(), _assigned_target(Target::UNSPECIFIED)
+{
+}
+// clang-format on
+// *INDENT-ON*
-TargetHint INode::override_target_hint(TargetHint target_hint) const
+void INode::set_graph(Graph *g)
{
- if(target_hint == TargetHint::OPENCL && !opencl_is_available())
+ ARM_COMPUTE_ERROR_ON(g == nullptr);
+ _graph = g;
+}
+
+void INode::set_id(NodeID id)
+{
+ _id = id;
+}
+
+void INode::set_common_node_parameters(NodeParams common_params)
+{
+ _common_params = std::move(common_params);
+}
+
+void INode::set_requested_target(Target target)
+{
+ _common_params.target = target;
+}
+
+void INode::set_assigned_target(Target target)
+{
+ _assigned_target = target;
+}
+
+void INode::set_output_tensor(TensorID tid, size_t idx)
+{
+ if(tid != NullTensorID && (idx < _outputs.size()) && (_graph->tensor(tid) != nullptr))
{
- target_hint = TargetHint::DONT_CARE;
+ ARM_COMPUTE_ERROR_ON(_graph == nullptr);
+ Tensor *updated_tensor = _graph->tensor(tid);
+ _outputs[idx] = tid;
+
+ // Set tensor to all output edges of the node
+ for(auto &output_edge_id : _output_edges)
+ {
+ auto output_edge = _graph->edge(output_edge_id);
+ if(output_edge != nullptr)
+ {
+ // Unbind edge from current tensor
+ auto current_output_tensor = output_edge->tensor();
+ current_output_tensor->unbind_edge(output_edge->id());
+
+ // Update tensor to edge and rebind tensor
+ output_edge->update_bound_tensor(updated_tensor);
+ updated_tensor->bind_edge(output_edge->id());
+ }
+ }
}
- GraphHints hints{ target_hint };
- target_hint = node_override_hints(hints).target_hint();
- ARM_COMPUTE_ERROR_ON(target_hint == TargetHint::OPENCL && !opencl_is_available());
- return target_hint;
}
-bool INode::supports_in_place() const
+
+NodeID INode::id() const
+{
+ return _id;
+}
+
+std::string INode::name() const
+{
+ return _common_params.name;
+}
+
+const Graph *INode::graph() const
+{
+ return _graph;
+}
+
+Graph *INode::graph()
+{
+ return _graph;
+}
+
+const std::vector<TensorID> &INode::outputs() const
{
- return _supports_in_place;
+ return _outputs;
}
-void INode::set_supports_in_place(bool value)
+
+const std::vector<EdgeID> &INode::input_edges() const
+{
+ return _input_edges;
+}
+
+const std::set<EdgeID> &INode::output_edges() const
+{
+ return _output_edges;
+}
+
+TensorID INode::input_id(size_t idx) const
+{
+ ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size());
+ Edge *e = _graph->edge(_input_edges[idx]);
+ return (e != nullptr) ? e->tensor_id() : NullTensorID;
+}
+
+TensorID INode::output_id(size_t idx) const
+{
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+ return _outputs[idx];
+}
+
+Tensor *INode::input(size_t idx) const
+{
+ ARM_COMPUTE_ERROR_ON(_graph == nullptr);
+ ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size());
+ Edge *e = _graph->edge(_input_edges[idx]);
+ return (e != nullptr) ? e->tensor() : nullptr;
+}
+
+Tensor *INode::output(size_t idx) const
{
- _supports_in_place = value;
+ ARM_COMPUTE_ERROR_ON(_graph == nullptr);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+ return _graph->tensor(_outputs[idx]);
}
-GraphHints INode::node_override_hints(GraphHints hints) const
+
+EdgeID INode::input_edge_id(size_t idx) const
+{
+ ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size());
+ return _input_edges[idx];
+}
+
+Edge *INode::input_edge(size_t idx) const
+{
+ ARM_COMPUTE_ERROR_ON(_graph == nullptr);
+ ARM_COMPUTE_ERROR_ON(idx >= _input_edges.size());
+ return _graph->edge(_input_edges[idx]);
+}
+
+size_t INode::num_inputs() const
+{
+ return _input_edges.size();
+}
+
+size_t INode::num_outputs() const
+{
+ return _outputs.size();
+}
+
+Target INode::requested_target() const
+{
+ return _common_params.target;
+}
+
+Target INode::assigned_target() const
{
- TargetHint target_hint = hints.target_hint();
- hints.set_target_hint((target_hint == TargetHint::DONT_CARE) ? TargetHint::NEON : target_hint);
- return hints;
+ return _assigned_target;
}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/OperationRegistry.cpp b/src/graph/OperationRegistry.cpp
deleted file mode 100644
index 651653f19c..0000000000
--- a/src/graph/OperationRegistry.cpp
+++ /dev/null
@@ -1,61 +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 "arm_compute/graph/OperationRegistry.h"
-
-using namespace arm_compute::graph;
-
-OperationRegistry::OperationRegistry()
- : _registered_ops()
-{
-}
-
-OperationRegistry &OperationRegistry::get()
-{
- static OperationRegistry instance;
- return instance;
-}
-
-IOperation *OperationRegistry::find_operation(OperationType operation, TargetHint target)
-{
- ARM_COMPUTE_ERROR_ON(!contains(operation, target));
- auto it = std::find_if(_registered_ops[operation].begin(), _registered_ops[operation].end(), [&](const std::unique_ptr<IOperation> &op)
- {
- return (op->target() == target);
- });
- ARM_COMPUTE_ERROR_ON(it == _registered_ops[operation].end());
- return (*it).get();
-}
-
-bool OperationRegistry::contains(OperationType operation, TargetHint target) const
-{
- auto it = _registered_ops.find(operation);
- if(it != _registered_ops.end())
- {
- return std::any_of(it->second.begin(), it->second.end(), [&](const std::unique_ptr<IOperation> &op)
- {
- return (op->target() == target);
- });
- }
- return false;
-}
diff --git a/src/graph/PassManager.cpp b/src/graph/PassManager.cpp
new file mode 100644
index 0000000000..8ed68bd99b
--- /dev/null
+++ b/src/graph/PassManager.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/PassManager.h"
+
+#include "arm_compute/graph/Logger.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+PassManager::PassManager()
+ : _passes()
+{
+}
+
+const std::vector<std::unique_ptr<IGraphMutator>> &PassManager::passes() const
+{
+ return _passes;
+}
+
+IGraphMutator *PassManager::pass(size_t index)
+{
+ return (index >= _passes.size()) ? nullptr : _passes.at(index).get();
+}
+
+void PassManager::append(std::unique_ptr<IGraphMutator> pass)
+{
+ if(pass)
+ {
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Appending mutating pass : " << pass->name() << std::endl);
+ _passes.push_back(std::move(pass));
+ }
+}
+
+void PassManager::clear()
+{
+ _passes.clear();
+}
+
+void PassManager::run_all(Graph &g)
+{
+ for(auto &pass : _passes)
+ {
+ if(pass)
+ {
+ ARM_COMPUTE_LOG_GRAPH_INFO("Running mutating pass : " << pass->name() << std::endl);
+ pass->mutate(g);
+ }
+ }
+}
+
+void PassManager::run(Graph &g, size_t index)
+{
+ if(index >= _passes.size())
+ {
+ return;
+ }
+
+ auto &pass = _passes.at(index);
+
+ if(pass != nullptr)
+ {
+ pass->mutate(g);
+ }
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/SubGraph.cpp b/src/graph/SubGraph.cpp
deleted file mode 100644
index b1cbb9cc95..0000000000
--- a/src/graph/SubGraph.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/SubGraph.h"
-
-#include "arm_compute/graph/Graph.h"
-#include "arm_compute/graph/INode.h"
-#include "arm_compute/graph/Tensor.h"
-
-using namespace arm_compute::graph;
-
-SubGraph::SubGraph()
- : _nodes(), _input(nullptr), _output(nullptr)
-{
-}
-
-void SubGraph::add_node(std::unique_ptr<INode> node)
-{
- _nodes.push_back(std::move(node));
-}
-
-void SubGraph::add_tensor_object(std::unique_ptr<ITensorObject> tensor)
-{
- // If it's the first Tensor added then it will be the input of the Graph.
- if(_input == nullptr)
- {
- _input = std::move(tensor);
- }
- else
- {
- _output = std::move(tensor);
- }
-}
-
-std::unique_ptr<Graph> SubGraph::construct(const GraphContext &ctx, std::unique_ptr<ITensorObject> input, std::unique_ptr<ITensorObject> output)
-{
- auto graph = arm_compute::support::cpp14::make_unique<Graph>();
-
- // Set hint
- // TODO(geopin01): store hints of sub-graph
- graph->hints() = ctx.hints();
-
- // Configure input
- if(_input == nullptr)
- {
- _input = std::move(input);
- }
- graph->add_tensor_object(std::move(_input));
-
- // Make sure first and last nodes of the subgraph always do operations out-of-place
- _nodes.front()->set_supports_in_place(false);
- _nodes.back()->set_supports_in_place(false);
-
- // Construct nodes
- for(auto &node : _nodes)
- {
- graph->add_node(std::move(node));
- }
-
- // Configure output
- if(_output == nullptr)
- {
- _output = std::move(output);
- }
- graph->add_tensor_object(std::move(_output));
-
- return graph;
-}
-
-bool SubGraph::has_input() const
-{
- return _input != nullptr;
-}
-
-bool SubGraph::has_output() const
-{
- return _output != nullptr;
-}
-
-SubGraph &arm_compute::graph::operator<<(SubGraph &graph, Tensor &&tensor)
-{
- graph.add_tensor_object(arm_compute::support::cpp14::make_unique<Tensor>(std::move(tensor)));
- return graph;
-}
-
-SubGraph &arm_compute::graph::operator<<(SubGraph &graph, SubTensor &&sub_tensor)
-{
- graph.add_tensor_object(arm_compute::support::cpp14::make_unique<SubTensor>(std::move(sub_tensor)));
- return graph;
-}
diff --git a/src/graph/SubTensor.cpp b/src/graph/SubTensor.cpp
deleted file mode 100644
index 2e640dd93c..0000000000
--- a/src/graph/SubTensor.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/SubTensor.h"
-
-#include "arm_compute/core/Error.h"
-#include "arm_compute/core/Helpers.h"
-#include "arm_compute/core/Validate.h"
-#include "arm_compute/runtime/CL/CLSubTensor.h"
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/SubTensor.h"
-#include "arm_compute/runtime/Tensor.h"
-#include "utils/TypePrinter.h"
-
-using namespace arm_compute::graph;
-
-namespace
-{
-template <typename SubTensorType, typename ParentTensorType>
-std::unique_ptr<arm_compute::ITensor> initialise_subtensor(arm_compute::ITensor *parent, TensorShape shape, Coordinates coords, bool extend_parent)
-{
- auto ptensor = dynamic_cast<ParentTensorType *>(parent);
- auto subtensor = arm_compute::support::cpp14::make_unique<SubTensorType>(ptensor, shape, coords, extend_parent);
- return std::move(subtensor);
-}
-} // namespace
-
-SubTensor::SubTensor()
- : _target(TargetHint::DONT_CARE), _tensor_shape(), _coords(), _parent(nullptr), _subtensor(nullptr), _extend_parent(false)
-{
-}
-
-SubTensor::SubTensor(Tensor &parent, TensorShape tensor_shape, Coordinates coords, bool extend_parent)
- : _target(TargetHint::DONT_CARE), _tensor_shape(tensor_shape), _coords(coords), _parent(nullptr), _subtensor(nullptr), _extend_parent(extend_parent)
-{
- ARM_COMPUTE_ERROR_ON(parent.tensor() == nullptr);
- _parent = parent.tensor();
- _target = parent.target();
-
- instantiate_subtensor();
-}
-
-SubTensor::SubTensor(arm_compute::ITensor *parent, TensorShape tensor_shape, Coordinates coords, TargetHint target, bool extend_parent)
- : _target(target), _tensor_shape(tensor_shape), _coords(coords), _parent(parent), _subtensor(nullptr), _extend_parent(extend_parent)
-{
- ARM_COMPUTE_ERROR_ON(parent == nullptr);
- instantiate_subtensor();
-}
-
-bool SubTensor::call_accessor()
-{
- return true;
-}
-
-bool SubTensor::has_accessor() const
-{
- return false;
-}
-
-arm_compute::ITensor *SubTensor::set_target(TargetHint target)
-{
- ARM_COMPUTE_ERROR_ON(target != _target);
- return (target == _target) ? _subtensor.get() : nullptr;
-}
-
-arm_compute::ITensor *SubTensor::tensor()
-{
- return _subtensor.get();
-}
-
-const arm_compute::ITensor *SubTensor::tensor() const
-{
- return _subtensor.get();
-}
-
-TargetHint SubTensor::target() const
-{
- return _target;
-}
-
-void SubTensor::allocate()
-{
- // NOP for sub-tensors
-}
-
-void SubTensor::instantiate_subtensor()
-{
- switch(_target)
- {
- case TargetHint::OPENCL:
- _subtensor = initialise_subtensor<arm_compute::CLSubTensor, arm_compute::ICLTensor>(_parent, _tensor_shape, _coords, _extend_parent);
- break;
- case TargetHint::NEON:
- _subtensor = initialise_subtensor<arm_compute::SubTensor, arm_compute::ITensor>(_parent, _tensor_shape, _coords, _extend_parent);
- break;
- default:
- ARM_COMPUTE_ERROR("Invalid TargetHint");
- }
-}
diff --git a/src/graph/Tensor.cpp b/src/graph/Tensor.cpp
index 4db79e93ad..47fb5c65bc 100644
--- a/src/graph/Tensor.cpp
+++ b/src/graph/Tensor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -23,138 +23,89 @@
*/
#include "arm_compute/graph/Tensor.h"
-#include "arm_compute/core/Error.h"
-#include "arm_compute/core/Helpers.h"
-#include "arm_compute/core/Validate.h"
-#include "arm_compute/runtime/CL/CLTensor.h"
-#include "arm_compute/runtime/Tensor.h"
-#include "utils/TypePrinter.h"
-
-using namespace arm_compute::graph;
-
-namespace
+namespace arm_compute
{
-template <typename TensorType>
-std::unique_ptr<arm_compute::ITensor> initialise_tensor(TensorInfo &info)
+namespace graph
{
- auto tensor = arm_compute::support::cpp14::make_unique<TensorType>();
- tensor->allocator()->init(info);
- return std::move(tensor);
-}
-
-template <typename TensorType>
-void tensor_allocate(arm_compute::ITensor &tensor)
+Tensor::Tensor(TensorID id, TensorDescriptor desc)
+ : _id(id), _desc(desc), _handle(nullptr), _accessor(nullptr), _bound_edges()
{
- auto itensor = dynamic_cast<TensorType *>(&tensor);
- ARM_COMPUTE_ERROR_ON_NULLPTR(itensor);
- itensor->allocator()->allocate();
}
-} // namespace
-Tensor::Tensor(TensorInfo &&info)
- : _target(TargetHint::DONT_CARE), _info(info), _accessor(nullptr), _tensor(nullptr)
+TensorID Tensor::id() const
{
+ return _id;
}
-Tensor::Tensor(Tensor &&src) noexcept
- : _target(src._target),
- _info(std::move(src._info)),
- _accessor(std::move(src._accessor)),
- _tensor(std::move(src._tensor))
+TensorDescriptor &Tensor::desc()
{
+ return _desc;
}
-void Tensor::set_info(TensorInfo &&info)
+const TensorDescriptor &Tensor::desc() const
{
- _info = info;
-}
-
-bool Tensor::call_accessor()
-{
- ARM_COMPUTE_ERROR_ON_NULLPTR(_accessor.get());
- auto cl_tensor = dynamic_cast<arm_compute::CLTensor *>(_tensor.get());
- if(cl_tensor != nullptr && cl_tensor->buffer() == nullptr)
- {
- cl_tensor->map();
- }
- bool retval = _accessor->access_tensor(*_tensor);
- if(cl_tensor != nullptr)
- {
- cl_tensor->unmap();
- }
- return retval;
+ return _desc;
}
-bool Tensor::has_accessor() const
+void Tensor::set_handle(std::unique_ptr<ITensorHandle> backend_tensor)
{
- return (_accessor != nullptr);
+ _handle = std::move(backend_tensor);
}
-arm_compute::ITensor *Tensor::tensor()
+ITensorHandle *Tensor::handle()
{
- return _tensor.get();
+ return _handle.get();
}
-const arm_compute::ITensor *Tensor::tensor() const
+void Tensor::set_accessor(std::unique_ptr<ITensorAccessor> accessor)
{
- return _tensor.get();
+ _accessor = std::move(accessor);
}
-const TensorInfo &Tensor::info() const
+ITensorAccessor *Tensor::accessor()
{
- return _info;
+ return _accessor.get();
}
-arm_compute::ITensor *Tensor::set_target(TargetHint target)
+bool Tensor::call_accessor()
{
- if(_tensor != nullptr)
+ // Early exit guard
+ if(!_accessor || !_handle)
{
- ARM_COMPUTE_ERROR_ON(target != _target);
+ return false;
}
- else
+
+ // Map tensor
+ _handle->map(true);
+
+ // Return in case of null backend buffer
+ if(_handle->tensor().buffer() == nullptr)
{
- switch(target)
- {
- case TargetHint::OPENCL:
- _tensor = initialise_tensor<arm_compute::CLTensor>(_info);
- break;
- case TargetHint::NEON:
- _tensor = initialise_tensor<arm_compute::Tensor>(_info);
- break;
- default:
- ARM_COMPUTE_ERROR("Invalid TargetHint");
- }
- _target = target;
+ return false;
}
- return _tensor.get();
+
+ // Call accessor
+ _accessor->access_tensor(_handle->tensor());
+
+ // Unmap tensor
+ _handle->unmap();
+
+ return true;
}
-void Tensor::allocate()
+void Tensor::bind_edge(EdgeID eid)
{
- ARM_COMPUTE_ERROR_ON_NULLPTR(_tensor.get());
- switch(_target)
- {
- case TargetHint::OPENCL:
- tensor_allocate<arm_compute::CLTensor>(*_tensor);
- break;
- case TargetHint::NEON:
- tensor_allocate<arm_compute::Tensor>(*_tensor);
- break;
- default:
- ARM_COMPUTE_ERROR("Invalid TargetHint");
- }
+ _bound_edges.insert(eid);
}
-void Tensor::allocate_and_fill_if_needed()
+void Tensor::unbind_edge(EdgeID eid)
{
- allocate();
- if(_accessor != nullptr)
- {
- call_accessor();
- }
+ _bound_edges.erase(eid);
}
-TargetHint Tensor::target() const
+const std::set<EdgeID> Tensor::bound_edges() const
{
- return _target;
+ return _bound_edges;
}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/Utils.cpp b/src/graph/Utils.cpp
new file mode 100644
index 0000000000..8537bbfb2a
--- /dev/null
+++ b/src/graph/Utils.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/Utils.h"
+
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/backends/BackendRegistry.h"
+#include "arm_compute/graph/mutators/GraphMutators.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+bool is_target_supported(Target target)
+{
+ return backends::BackendRegistry::get().contains(target) && backends::BackendRegistry::get().find_backend(target)->is_backend_supported();
+}
+
+Target get_default_target()
+{
+ if(is_target_supported(Target::NEON))
+ {
+ return Target::NEON;
+ }
+ if(is_target_supported(Target::CL))
+ {
+ return Target::CL;
+ }
+ if(is_target_supported(Target::GC))
+ {
+ return Target::GC;
+ }
+ ARM_COMPUTE_ERROR("No backend exists!");
+}
+
+void force_target_to_graph(Graph &g, Target target)
+{
+ auto &nodes = g.nodes();
+ for(auto &node : nodes)
+ {
+ if(node)
+ {
+ node->set_assigned_target(target);
+ }
+ }
+
+ auto &tensors = g.tensors();
+ for(auto &tensor : tensors)
+ {
+ if(tensor)
+ {
+ tensor->desc().target = target;
+ }
+ }
+}
+
+PassManager create_default_pass_manager(Target target)
+{
+ PassManager pm;
+
+ if(target != Target::GC)
+ {
+ pm.append(support::cpp14::make_unique<InPlaceOperationMutator>());
+ pm.append(support::cpp14::make_unique<NodeFusionMutator>());
+ pm.append(support::cpp14::make_unique<SplitLayerSubTensorMutator>());
+ pm.append(support::cpp14::make_unique<DepthConcatSubTensorMutator>());
+ }
+
+ return pm;
+}
+
+/** Default setups a graph Context
+ *
+ * @param[in] ctx Context to default initialize
+ */
+void setup_default_graph_context(GraphContext &ctx)
+{
+ for(const auto &backend : backends::BackendRegistry::get().backends())
+ {
+ backend.second->setup_backend_context(ctx);
+ }
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/CL/CLUnmap.cpp b/src/graph/Workload.cpp
index 31f2f19e9c..c53a8a42da 100644
--- a/src/graph/CL/CLUnmap.cpp
+++ b/src/graph/Workload.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,23 +21,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/graph/CL/CLUnmap.h"
+#include "arm_compute/graph/Workload.h"
-#include "arm_compute/core/CL/ICLTensor.h"
-#include "arm_compute/core/Helpers.h"
-#include "arm_compute/core/Validate.h"
-#include "arm_compute/graph/ITensorObject.h"
-#include "arm_compute/runtime/CL/CLScheduler.h"
+#include "arm_compute/graph/INode.h"
+#include "arm_compute/graph/ITensorHandle.h"
-using namespace arm_compute::graph;
-
-CLUnmap::CLUnmap(ITensorObject *tensor)
- : _tensor(dynamic_cast<arm_compute::ICLTensor *>(tensor->tensor()))
+namespace arm_compute
{
- ARM_COMPUTE_ERROR_ON_NULLPTR(_tensor);
-}
-
-void CLUnmap::run()
+namespace graph
+{
+void ExecutionTask::operator()()
{
- _tensor->unmap(arm_compute::CLScheduler::get().queue());
+ if(task)
+ {
+ task->run();
+ }
}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/QuantizationLayer.cpp b/src/graph/backends/BackendRegistry.cpp
index c102f47633..2803322e64 100644
--- a/src/graph/nodes/QuantizationLayer.cpp
+++ b/src/graph/backends/BackendRegistry.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,28 +21,43 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/graph/nodes/QuantizationLayer.h"
+#include "arm_compute/graph/backends/BackendRegistry.h"
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
+using namespace arm_compute::graph::backends;
-using namespace arm_compute::graph;
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+BackendRegistry::BackendRegistry()
+ : _registered_backends()
+{
+}
-std::unique_ptr<arm_compute::IFunction> QuantizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
+BackendRegistry &BackendRegistry::get()
{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
+ static BackendRegistry instance;
+ return instance;
+}
- _target_hint = ctx.hints().target_hint();
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
+IDeviceBackend *BackendRegistry::find_backend(Target target)
+{
+ ARM_COMPUTE_ERROR_ON(!contains(target));
+ return _registered_backends[target].get();
+}
- // Create node context
- NodeContext node_ctx(OperationType::QuantizationLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_output(out);
+bool BackendRegistry::contains(Target target) const
+{
+ auto it = _registered_backends.find(target);
+ return (it != _registered_backends.end());
+}
- // Get function
- return OperationRegistry::get().find_operation(OperationType::QuantizationLayer, _target_hint)->configure(node_ctx);
+const std::map<Target, std::unique_ptr<IDeviceBackend>> &BackendRegistry::backends() const
+{
+ return _registered_backends;
}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/backends/CL/CLDeviceBackend.cpp b/src/graph/backends/CL/CLDeviceBackend.cpp
new file mode 100644
index 0000000000..f10eb33a98
--- /dev/null
+++ b/src/graph/backends/CL/CLDeviceBackend.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/CL/CLDeviceBackend.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/INode.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/Tensor.h"
+#include "arm_compute/graph/backends/BackendRegistrar.h"
+#include "arm_compute/graph/backends/CL/CLFunctionFactory.h"
+#include "arm_compute/graph/backends/CL/CLNodeValidator.h"
+#include "arm_compute/graph/backends/CL/CLSubTensorHandle.h"
+#include "arm_compute/graph/backends/CL/CLTensorHandle.h"
+
+#include "arm_compute/core/TensorInfo.h"
+#include "arm_compute/runtime/BlobLifetimeManager.h"
+#include "arm_compute/runtime/CL/CLBufferAllocator.h"
+#include "arm_compute/runtime/CL/CLScheduler.h"
+#include "arm_compute/runtime/MemoryManagerOnDemand.h"
+#include "arm_compute/runtime/PoolManager.h"
+
+#include "support/ToolchainSupport.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+namespace
+{
+bool file_exists(const std::string &filename)
+{
+ std::ifstream file(filename);
+ return file.good();
+}
+} // namespace
+
+/** Register CL backend */
+static detail::BackendRegistrar<CLDeviceBackend> CLDeviceBackend_registrar(Target::CL);
+
+/** Tuner export file */
+static const std::string tuner_data_filename = "acl_tuner.csv";
+
+CLDeviceBackend::CLDeviceBackend()
+ : _tuner(), _allocator(cl::Context::getDefault())
+{
+}
+
+CLDeviceBackend::~CLDeviceBackend()
+{
+ // TODO (geopin01) : Shouldn't call non exception safe stuff here
+ if(_tuner.tune_new_kernels() && !_tuner.lws_table().empty())
+ {
+ _tuner.save_to_file(tuner_data_filename);
+ }
+}
+
+void CLDeviceBackend::set_kernel_tuning(bool enable_tuning)
+{
+ _tuner.set_tune_new_kernels(enable_tuning);
+}
+
+void CLDeviceBackend::initialize_backend()
+{
+ // Load tuner data if available
+ if(_tuner.lws_table().empty() && file_exists(tuner_data_filename))
+ {
+ _tuner.load_from_file(tuner_data_filename);
+ }
+
+ // Setup Scheduler
+ CLScheduler::get().default_init(&_tuner);
+
+ // Create allocator with new context
+ _allocator = CLBufferAllocator();
+}
+
+void CLDeviceBackend::setup_backend_context(GraphContext &ctx)
+{
+ // Setup tuner
+ set_kernel_tuning(ctx.config().use_tuner);
+
+ // Setup a management backend
+ if(ctx.memory_management_ctx(Target::CL) == nullptr)
+ {
+ MemoryManagerContext mm_ctx;
+ mm_ctx.target = Target::CL;
+ mm_ctx.mm = create_memory_manager(MemoryManagerAffinity::Buffer);
+
+ ctx.insert_memory_management_ctx(std::move(mm_ctx));
+ }
+}
+
+bool CLDeviceBackend::is_backend_supported()
+{
+ return arm_compute::opencl_is_available();
+}
+
+std::unique_ptr<ITensorHandle> CLDeviceBackend::create_tensor(const Tensor &tensor)
+{
+ // Get tensor descriptor
+ const TensorDescriptor &tensor_desc = tensor.desc();
+ ARM_COMPUTE_ERROR_ON(tensor_desc.target != Target::CL);
+
+ // Create backend tensor handle
+ TensorInfo info(tensor_desc.shape, 1, tensor_desc.data_type);
+ auto backend_tensor_handle = support::cpp14::make_unique<CLTensorHandle>(info);
+
+ return std::move(backend_tensor_handle);
+}
+
+std::unique_ptr<ITensorHandle> CLDeviceBackend::create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent)
+{
+ if(parent == nullptr)
+ {
+ return nullptr;
+ }
+
+ return support::cpp14::make_unique<CLSubTensorHandle>(parent, shape, coords, extend_parent);
+}
+
+std::unique_ptr<arm_compute::IFunction> CLDeviceBackend::configure_node(INode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Configuring CL node with ID : " << node.id() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::CL);
+
+ // Configure node
+ return CLFunctionFactory::create(&node, ctx);
+}
+
+arm_compute::Status CLDeviceBackend::validate_node(INode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating CL node with ID : " << node.id() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::CL);
+
+ return CLNodeValidator::validate(&node);
+}
+
+std::shared_ptr<arm_compute::IMemoryManager> CLDeviceBackend::create_memory_manager(MemoryManagerAffinity affinity)
+{
+ if(affinity == MemoryManagerAffinity::Offset)
+ {
+ ARM_COMPUTE_LOG_GRAPH_WARNING("CL Backend does not support offset affinity memory management!");
+ return nullptr;
+ }
+
+ auto lifetime_mgr = std::make_shared<BlobLifetimeManager>();
+ auto pool_mgr = std::make_shared<PoolManager>();
+ auto mm = std::make_shared<MemoryManagerOnDemand>(lifetime_mgr, pool_mgr);
+
+ mm->set_allocator(&_allocator);
+
+ return mm;
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/CL/CLFunctionsFactory.cpp b/src/graph/backends/CL/CLFunctionsFactory.cpp
new file mode 100644
index 0000000000..1b448fefd2
--- /dev/null
+++ b/src/graph/backends/CL/CLFunctionsFactory.cpp
@@ -0,0 +1,590 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/CL/CLFunctionFactory.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/TypePrinter.h"
+#include "arm_compute/graph/Types.h"
+#include "arm_compute/graph/backends/Utils.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+#include "arm_compute/runtime/CL/CLFunctions.h"
+
+#include "support/ToolchainSupport.h"
+
+using namespace arm_compute::utils::cast;
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+namespace
+{
+/** Returns backing tensor of a given tensor
+ *
+ * @param[in] tensor Tensor to extract the backing tensor from
+ *
+ * @return Backing tensor if present else nullptr
+ */
+arm_compute::ICLTensor *get_backing_tensor(arm_compute::graph::Tensor *tensor)
+{
+ arm_compute::ICLTensor *backing_tensor = nullptr;
+ if(tensor != nullptr)
+ {
+ ARM_COMPUTE_ERROR_ON(tensor->desc().target != arm_compute::graph::Target::CL);
+ // Get backing tensor handle
+ ITensorHandle *tensor_handle = tensor->handle();
+ // Get backing tensor
+ backing_tensor = (tensor_handle != nullptr) ? polymorphic_cast<ICLTensor *>(&tensor_handle->tensor()) : nullptr;
+ }
+
+ return backing_tensor;
+}
+
+/** Create a backend activation layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend activation layer function
+ */
+std::unique_ptr<IFunction> create_activation_layer(ActivationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL ActivationLayerNode node with ID : " << node.id() << " and Name: " << node.name()
+ << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const ActivationLayerInfo act_info = node.activation_info();
+
+ // Create function
+ auto func = support::cpp14::make_unique<CLActivationLayer>();
+ func->configure(input, output, act_info);
+
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLActivationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Shape: " << input->info()->tensor_shape()
+ << " Activation function: " << act_info.activation()
+ << " a: " << act_info.a()
+ << " b: " << act_info.b()
+ << " InPlace : " << is_in_place_operation(input, output)
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend batch normalization layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend batch normalization layer function
+ */
+std::unique_ptr<IFunction> create_batch_normalization_layer(BatchNormalizationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating CL BatchNormalization node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+
+ // TODO (geopin01) : Var and mean are compulsory, switch function to accept nullptr as beta and/or gamma
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 5);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *mean = get_backing_tensor(node.input(1));
+ ICLTensor *var = get_backing_tensor(node.input(2));
+ ICLTensor *beta = get_backing_tensor(node.input(3));
+ ICLTensor *gamma = get_backing_tensor(node.input(4));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const float epsilon = node.epsilon();
+ const ActivationLayerInfo fused_act = node.fused_activation();
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLBatchNormalizationLayer>();
+ func->configure(input, output, mean, var, beta, gamma, epsilon, fused_act);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLBatchNormalizationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Shape: " << input->info()->tensor_shape()
+ << " Epsilon: " << epsilon << " "
+ << (fused_act.enabled() ? to_string(fused_act.activation()) : "")
+ << " InPlace : " << is_in_place_operation(input, output)
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend convolution layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend convolution layer function
+ */
+std::unique_ptr<IFunction> create_convolution_layer(ConvolutionLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating CL ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *weights = get_backing_tensor(node.input(1));
+ ICLTensor *biases = get_backing_tensor(node.input(2));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const PadStrideInfo conv_info = node.convolution_info();
+ const ConvolutionMethod conv_algorithm = node.convolution_method();
+
+ // Create and configure function (we assume that functions have been validated before creation)
+ std::shared_ptr<IMemoryManager> mm = get_memory_manager(ctx, Target::CL);
+ std::unique_ptr<IFunction> func;
+ std::string func_name;
+
+ if(conv_algorithm == ConvolutionMethod::WINOGRAD)
+ {
+ std::tie(func, func_name) = create_named_function<CLWinogradConvolutionLayer>(
+ std::string("CLWinogradConvolutionLayer"), input, weights, biases, output, conv_info);
+ }
+ else if(conv_algorithm == ConvolutionMethod::DIRECT)
+ {
+ std::tie(func, func_name) = create_named_function<CLDirectConvolutionLayer>(
+ std::string("CLDirectConvolutionLayer"), input, weights, biases, output, conv_info);
+ }
+ else if(conv_algorithm == ConvolutionMethod::GEMM)
+ {
+ std::tie(func, func_name) = create_named_memory_managed_function<CLGEMMConvolutionLayer>(std::string("CLGEMMConvolutionLayer"), mm,
+ input, weights, biases, output, conv_info);
+ }
+ else
+ {
+ std::tie(func, func_name) = create_named_memory_managed_function<CLConvolutionLayer>(std::string("CLConvolutionLayer"), mm,
+ input, weights, biases, output, conv_info);
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+ return func;
+}
+
+/** Create a backend layer depth concatenate function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend depth concatenate layer function
+ */
+std::unique_ptr<arm_compute::IFunction> create_depth_concatenate_layer(DepthConcatenateLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating CL DepthConcatenate node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Return nullptr if depth concatenate is switched off
+ if(!node.is_enabled())
+ {
+ return nullptr;
+ }
+
+ // Extract IO and info
+ std::vector<arm_compute::ICLTensor *> inputs;
+ for(unsigned int i = 0; i < node.num_inputs(); ++i)
+ {
+ inputs.push_back(get_backing_tensor(node.input(i)));
+ }
+ ICLTensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLDepthConcatenateLayer>();
+ func->configure(inputs, output);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLDepthConcatenateLayer"
+ << " Data Type: " << output->info()->data_type()
+ << " Shape: " << output->info()->tensor_shape()
+ << " Num Inputs: " << inputs.size()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend layer depth-wise convolution function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend depth-wise convolution layer function
+ */
+std::unique_ptr<IFunction> create_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL DepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name()
+ << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *weights = get_backing_tensor(node.input(1));
+ ICLTensor *biases = get_backing_tensor(node.input(2));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const PadStrideInfo conv_info = node.convolution_info();
+ const DepthwiseConvolutionMethod dwc_algorithm = node.depthwise_convolution_method();
+
+ // Create and configure function (we assume that functions have been validated before creation)
+ std::unique_ptr<IFunction> func;
+ std::string func_name;
+ if(dwc_algorithm == DepthwiseConvolutionMethod::OPTIMIZED_3x3)
+ {
+ std::tie(func, func_name) = create_named_function<CLDepthwiseConvolutionLayer3x3>(
+ std::string("CLDepthwiseConvolutionLayer3x3"), input, weights, biases, output, conv_info);
+ }
+ else
+ {
+ std::tie(func, func_name) = create_named_function<CLDepthwiseConvolutionLayer>(
+ std::string("CLDepthwiseConvolutionLayer"), input, weights, biases, output, conv_info);
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+ return func;
+}
+
+/** Create a backend element-wise operation layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend element-wise operation layer function
+ */
+std::unique_ptr<IFunction> create_eltwise_layer(EltwiseLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 2);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input1 = get_backing_tensor(node.input(0));
+ ICLTensor *input2 = get_backing_tensor(node.input(1));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const EltwiseOperation eltwise_op = node.eltwise_operation();
+ ARM_COMPUTE_ERROR_ON(input1 == nullptr);
+ ARM_COMPUTE_ERROR_ON(input2 == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ std::unique_ptr<IFunction> func = nullptr;
+ std::string func_name;
+ if(eltwise_op == EltwiseOperation::ADD)
+ {
+ std::tie(func, func_name) = create_named_function<CLArithmeticAddition>(std::string("CLArithmeticAddition"),
+ input1, input2, output,
+ ConvertPolicy::SATURATE);
+ }
+ else if(eltwise_op == EltwiseOperation::SUB)
+ {
+ std::tie(func, func_name) = create_named_function<CLArithmeticSubtraction>(
+ std::string("CLArithmeticSubtraction"), input1, input2, output, ConvertPolicy::SATURATE);
+ }
+ else if(eltwise_op == EltwiseOperation::MUL)
+ {
+ std::tie(func, func_name) = create_named_function<CLPixelWiseMultiplication>(
+ std::string("CLPixelWiseMultiplication"), input1, input2, output, 1.f, ConvertPolicy::SATURATE,
+ RoundingPolicy::TO_NEAREST_EVEN);
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("Unsupported element-wise operation!");
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input1->info()->data_type()
+ << " Shape : " << input1->info()->tensor_shape()
+ << std::endl);
+
+ return func;
+}
+
+/** Create a backend flatten layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend flatten layer function
+ */
+std::unique_ptr<IFunction> create_flatten_layer(FlattenLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL FlattenLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLFlattenLayer>();
+ func->configure(input, output);
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLFlattenLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend fully connected layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend fully connected layer function
+ */
+std::unique_ptr<IFunction> create_fully_connected_layer(FullyConnectedLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL FullyConnectedLayer node with ID : " << node.id() << " and Name: " << node.name()
+ << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *weights = get_backing_tensor(node.input(1));
+ ICLTensor *biases = get_backing_tensor(node.input(2));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLFullyConnectedLayer>(get_memory_manager(ctx, Target::CL));
+ func->configure(input, weights, biases, output);
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(weights == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLFullyConnectedLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Biases Shape: " << biases->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend normalization layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend normalization layer function
+ */
+std::unique_ptr<IFunction> create_normalization_layer(NormalizationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL NormalizationLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const NormalizationLayerInfo norm_info = node.normalization_info();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLNormalizationLayer>();
+ func->configure(input, output, norm_info);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLNormalizationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << " Normalization info: " << norm_info.type()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend pooling layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend pooling layer function
+ */
+std::unique_ptr<IFunction> create_pooling_layer(PoolingLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL PoolingLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const PoolingLayerInfo pool_info = node.pooling_info();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLPoolingLayer>();
+ func->configure(input, output, pool_info);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLPoolingLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << " Pooling info: " << pool_info.pool_type()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend reshape layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend reshape layer function
+ */
+std::unique_ptr<IFunction> create_reshape_layer(ReshapeLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL ReshapeLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLReshapeLayer>();
+ func->configure(input, output);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLReshapeLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend softmax layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend softmax layer function
+ */
+std::unique_ptr<IFunction> create_softmax_layer(SoftmaxLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating CL SoftmaxLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ICLTensor *input = get_backing_tensor(node.input(0));
+ ICLTensor *output = get_backing_tensor(node.output(0));
+ const float beta = node.beta();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<CLSoftmaxLayer>(get_memory_manager(ctx, Target::CL));
+ func->configure(input, output, beta);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated CLSoftmaxLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+} // namespace
+
+std::unique_ptr<IFunction> CLFunctionFactory::create(INode *node, GraphContext &ctx)
+{
+ if(node == nullptr)
+ {
+ return nullptr;
+ }
+
+ NodeType type = node->type();
+ switch(type)
+ {
+ case NodeType::ActivationLayer:
+ return create_activation_layer(*polymorphic_downcast<ActivationLayerNode *>(node));
+ case NodeType::BatchNormalizationLayer:
+ return create_batch_normalization_layer(*polymorphic_downcast<BatchNormalizationLayerNode *>(node));
+ case NodeType::ConvolutionLayer:
+ return create_convolution_layer(*polymorphic_downcast<ConvolutionLayerNode *>(node), ctx);
+ case NodeType::DepthConcatenateLayer:
+ return create_depth_concatenate_layer(*polymorphic_downcast<DepthConcatenateLayerNode *>(node));
+ case NodeType::DepthwiseConvolutionLayer:
+ return create_depthwise_convolution_layer(*polymorphic_downcast<DepthwiseConvolutionLayerNode *>(node));
+ case NodeType::EltwiseLayer:
+ return create_eltwise_layer(*polymorphic_downcast<EltwiseLayerNode *>(node));
+ case NodeType::FlattenLayer:
+ return create_flatten_layer(*polymorphic_downcast<FlattenLayerNode *>(node));
+ case NodeType::FullyConnectedLayer:
+ return create_fully_connected_layer(*polymorphic_downcast<FullyConnectedLayerNode *>(node), ctx);
+ case NodeType::NormalizationLayer:
+ return create_normalization_layer(*polymorphic_downcast<NormalizationLayerNode *>(node));
+ case NodeType::PoolingLayer:
+ return create_pooling_layer(*polymorphic_downcast<PoolingLayerNode *>(node));
+ case NodeType::ReshapeLayer:
+ return create_reshape_layer(*polymorphic_downcast<ReshapeLayerNode *>(node));
+ case NodeType::SoftmaxLayer:
+ return create_softmax_layer(*polymorphic_downcast<SoftmaxLayerNode *>(node), ctx);
+ default:
+ return nullptr;
+ }
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/CL/CLNodeValidator.cpp b/src/graph/backends/CL/CLNodeValidator.cpp
new file mode 100644
index 0000000000..c16b2e67df
--- /dev/null
+++ b/src/graph/backends/CL/CLNodeValidator.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/CL/CLNodeValidator.h"
+
+#include "arm_compute/graph/backends/ValidateHelpers.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/runtime/CL/CLFunctions.h"
+
+using namespace arm_compute::utils::cast;
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+Status CLNodeValidator::validate(INode *node)
+{
+ if(node == nullptr)
+ {
+ return Status{};
+ }
+
+ NodeType type = node->type();
+ switch(type)
+ {
+ case NodeType::ConvolutionLayer:
+ return detail::validate_convolution_layer<CLConvolutionLayer,
+ CLDirectConvolutionLayer,
+ CLGEMMConvolutionLayer,
+ CLWinogradConvolutionLayer>(*polymorphic_downcast<ConvolutionLayerNode *>(node));
+ case NodeType::DepthwiseConvolutionLayer:
+ return detail::validate_depthwise_convolution_layer<CLDepthwiseConvolutionLayer,
+ CLDepthwiseConvolutionLayer3x3>(*polymorphic_downcast<DepthwiseConvolutionLayerNode *>(node));
+ default:
+ return Status{};
+ }
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/CL/CLSubTensorHandle.cpp b/src/graph/backends/CL/CLSubTensorHandle.cpp
new file mode 100644
index 0000000000..a1bc8a1dd3
--- /dev/null
+++ b/src/graph/backends/CL/CLSubTensorHandle.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/CL/CLSubTensorHandle.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+CLSubTensorHandle::CLSubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords, bool extend_parent)
+ : _sub_tensor()
+{
+ ARM_COMPUTE_ERROR_ON(!parent_handle);
+ auto parent_tensor = arm_compute::utils::cast::polymorphic_downcast<ICLTensor *>(&parent_handle->tensor());
+ _sub_tensor = arm_compute::CLSubTensor(parent_tensor, shape, coords, extend_parent);
+}
+
+void CLSubTensorHandle::allocate()
+{
+ // noop
+}
+
+const arm_compute::ITensor &CLSubTensorHandle::tensor() const
+{
+ return _sub_tensor;
+}
+
+arm_compute::ITensor &CLSubTensorHandle::tensor()
+{
+ return _sub_tensor;
+}
+
+void CLSubTensorHandle::map(bool blocking)
+{
+ _sub_tensor.map(blocking);
+}
+
+void CLSubTensorHandle::unmap()
+{
+ _sub_tensor.unmap();
+}
+
+void CLSubTensorHandle::release_if_unused()
+{
+ // noop
+}
+
+bool CLSubTensorHandle::is_subtensor() const
+{
+ return true;
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/CL/CLTensorHandle.cpp b/src/graph/backends/CL/CLTensorHandle.cpp
new file mode 100644
index 0000000000..563c4d9ac6
--- /dev/null
+++ b/src/graph/backends/CL/CLTensorHandle.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/CL/CLTensorHandle.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+CLTensorHandle::CLTensorHandle(const ITensorInfo &info)
+ : _tensor()
+{
+ _tensor.allocator()->init(info);
+}
+
+void CLTensorHandle::allocate()
+{
+ _tensor.allocator()->allocate();
+}
+
+const arm_compute::ITensor &CLTensorHandle::tensor() const
+{
+ return _tensor;
+}
+
+arm_compute::ITensor &CLTensorHandle::tensor()
+{
+ return _tensor;
+}
+
+void CLTensorHandle::map(bool blocking)
+{
+ _tensor.map(blocking);
+}
+
+void CLTensorHandle::unmap()
+{
+ _tensor.unmap();
+}
+
+void CLTensorHandle::release_if_unused()
+{
+ // TODO (geopin01): Release tensor only if all sub-tensors are marked as not used
+ if(!_tensor.is_used())
+ {
+ _tensor.allocator()->free();
+ }
+}
+
+bool CLTensorHandle::is_subtensor() const
+{
+ return false;
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/GLES/GCDeviceBackend.cpp b/src/graph/backends/GLES/GCDeviceBackend.cpp
new file mode 100644
index 0000000000..8cd9994744
--- /dev/null
+++ b/src/graph/backends/GLES/GCDeviceBackend.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/GLES/GCDeviceBackend.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/INode.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/Tensor.h"
+#include "arm_compute/graph/backends/BackendRegistrar.h"
+#include "arm_compute/graph/backends/GLES/GCFunctionFactory.h"
+#include "arm_compute/graph/backends/GLES/GCNodeValidator.h"
+#include "arm_compute/graph/backends/GLES/GCTensorHandle.h"
+
+#include "arm_compute/core/TensorInfo.h"
+#include "arm_compute/runtime/BlobLifetimeManager.h"
+#include "arm_compute/runtime/GLES_COMPUTE/GCBufferAllocator.h"
+#include "arm_compute/runtime/GLES_COMPUTE/GCScheduler.h"
+#include "arm_compute/runtime/MemoryManagerOnDemand.h"
+#include "arm_compute/runtime/PoolManager.h"
+
+#include "support/ToolchainSupport.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+/** Register GLES backend */
+static detail::BackendRegistrar<GCDeviceBackend> GCDeviceBackend_registrar(Target::GC);
+
+GCDeviceBackend::GCDeviceBackend()
+ : _allocator()
+{
+}
+
+void GCDeviceBackend::initialize_backend()
+{
+ // Setup Scheduler
+ GCScheduler::get().default_init();
+}
+
+void GCDeviceBackend::setup_backend_context(GraphContext &ctx)
+{
+ // Setup a management backend
+ if(ctx.memory_management_ctx(Target::GC) == nullptr)
+ {
+ MemoryManagerContext mm_ctx;
+ mm_ctx.target = Target::GC;
+ mm_ctx.mm = create_memory_manager(MemoryManagerAffinity::Buffer);
+
+ ctx.insert_memory_management_ctx(std::move(mm_ctx));
+ }
+}
+
+bool GCDeviceBackend::is_backend_supported()
+{
+ return arm_compute::opengles31_is_available();
+}
+
+std::unique_ptr<ITensorHandle> GCDeviceBackend::create_tensor(const Tensor &tensor)
+{
+ // Get tensor descriptor
+ const TensorDescriptor &tensor_desc = tensor.desc();
+ ARM_COMPUTE_ERROR_ON(tensor_desc.target != Target::GC);
+
+ // Create backend tensor handle
+ TensorInfo info(tensor_desc.shape, 1, tensor_desc.data_type);
+ auto backend_tensor_handle = support::cpp14::make_unique<GCTensorHandle>(info);
+
+ return std::move(backend_tensor_handle);
+}
+
+std::unique_ptr<ITensorHandle> GCDeviceBackend::create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent)
+{
+ ARM_COMPUTE_UNUSED(parent, shape, coords, extend_parent);
+ ARM_COMPUTE_ERROR("GLES backend has no sub-tensor support!");
+ return nullptr;
+}
+
+std::unique_ptr<arm_compute::IFunction> GCDeviceBackend::configure_node(INode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Configuring GC node with ID : " << node.id() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::GC);
+
+ // Configure node
+ return GCFunctionFactory::create(&node, ctx);
+}
+
+arm_compute::Status GCDeviceBackend::validate_node(INode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating GC node with ID : " << node.id() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::GC);
+
+ return GCNodeValidator::validate(&node);
+}
+
+std::shared_ptr<arm_compute::IMemoryManager> GCDeviceBackend::create_memory_manager(MemoryManagerAffinity affinity)
+{
+ if(affinity == MemoryManagerAffinity::Offset)
+ {
+ ARM_COMPUTE_LOG_GRAPH_WARNING("GC Backend does not support offset affinity memory management!");
+ return nullptr;
+ }
+
+ auto lifetime_mgr = std::make_shared<BlobLifetimeManager>();
+ auto pool_mgr = std::make_shared<PoolManager>();
+ auto mm = std::make_shared<MemoryManagerOnDemand>(lifetime_mgr, pool_mgr);
+
+ mm->set_allocator(&_allocator);
+
+ return mm;
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/GLES/GCFunctionsFactory.cpp b/src/graph/backends/GLES/GCFunctionsFactory.cpp
new file mode 100644
index 0000000000..12e7c042d4
--- /dev/null
+++ b/src/graph/backends/GLES/GCFunctionsFactory.cpp
@@ -0,0 +1,507 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/GLES/GCFunctionFactory.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/TypePrinter.h"
+#include "arm_compute/graph/Types.h"
+#include "arm_compute/graph/backends/Utils.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+#include "arm_compute/runtime/GLES_COMPUTE/GCFunctions.h"
+
+#include "support/ToolchainSupport.h"
+
+using namespace arm_compute::utils::cast;
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+namespace
+{
+/** Returns backing tensor of a given tensor
+ *
+ * @param[in] tensor Tensor to extract the backing tensor from
+ *
+ * @return Backing tensor if present else nullptr
+ */
+arm_compute::IGCTensor *get_backing_tensor(arm_compute::graph::Tensor *tensor)
+{
+ arm_compute::IGCTensor *backing_tensor = nullptr;
+ if(tensor != nullptr)
+ {
+ ARM_COMPUTE_ERROR_ON(tensor->desc().target != arm_compute::graph::Target::GC);
+ // Get backing tensor handle
+ ITensorHandle *tensor_handle = tensor->handle();
+ // Get backing tensor
+ backing_tensor = (tensor_handle != nullptr) ? polymorphic_cast<IGCTensor *>(&tensor_handle->tensor()) : nullptr;
+ }
+
+ return backing_tensor;
+}
+
+/** Create a backend activation layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend activation layer function
+ */
+std::unique_ptr<IFunction> create_activation_layer(ActivationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating GC ActivationLayerNode node with ID : " << node.id() << " and Name: " << node.name()
+ << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const ActivationLayerInfo act_info = node.activation_info();
+
+ // Create function
+ auto func = support::cpp14::make_unique<GCActivationLayer>();
+ func->configure(input, output, act_info);
+
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCActivationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Shape: " << input->info()->tensor_shape()
+ << " Activation function: " << act_info.activation()
+ << " a: " << act_info.a()
+ << " b: " << act_info.b()
+ << " InPlace : " << is_in_place_operation(input, output)
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend batch normalization layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend batch normalization layer function
+ */
+std::unique_ptr<IFunction> create_batch_normalization_layer(BatchNormalizationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating GC BatchNormalization node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+
+ // TODO (geopin01) : Var and mean are compulsory, switch function to accept nullptr as beta and/or gamma
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 5);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *mean = get_backing_tensor(node.input(1));
+ IGCTensor *var = get_backing_tensor(node.input(2));
+ IGCTensor *beta = get_backing_tensor(node.input(3));
+ IGCTensor *gamma = get_backing_tensor(node.input(4));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const float epsilon = node.epsilon();
+ const ActivationLayerInfo fused_act = node.fused_activation();
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<GCBatchNormalizationLayer>();
+ func->configure(input, output, mean, var, beta, gamma, epsilon, fused_act);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCBatchNormalizationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Shape: " << input->info()->tensor_shape()
+ << " Epsilon: " << epsilon << " "
+ << (fused_act.enabled() ? to_string(fused_act.activation()) : "")
+ << " InPlace : " << is_in_place_operation(input, output)
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend convolution layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend convolution layer function
+ */
+std::unique_ptr<IFunction> create_convolution_layer(ConvolutionLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating GC ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *weights = get_backing_tensor(node.input(1));
+ IGCTensor *biases = get_backing_tensor(node.input(2));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const PadStrideInfo conv_info = node.convolution_info();
+ const ConvolutionMethod conv_algorithm = node.convolution_method();
+
+ // Create and configure function (we assume that functions have been validated before creation)
+ std::shared_ptr<IMemoryManager> mm = get_memory_manager(ctx, Target::GC);
+ std::unique_ptr<IFunction> func;
+ std::string func_name;
+
+ if(conv_algorithm == ConvolutionMethod::DIRECT)
+ {
+ std::tie(func, func_name) = create_named_function<GCDirectConvolutionLayer>(
+ std::string("GCDirectConvolutionLayer"), input, weights, biases, output, conv_info);
+ }
+ else
+ {
+ std::tie(func, func_name) = create_named_memory_managed_function<GCConvolutionLayer>(std::string("GCConvolutionLayer"), mm,
+ input, weights, biases, output, conv_info);
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+ return func;
+}
+
+/** Create a backend layer depth concatenate function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend depth concatenate layer function
+ */
+std::unique_ptr<arm_compute::IFunction> create_depth_concatenate_layer(DepthConcatenateLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating GC DepthConcatenate node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Return nullptr if depth concatenate is switched off
+ if(!node.is_enabled())
+ {
+ return nullptr;
+ }
+
+ // Extract IO and info
+ std::vector<arm_compute::IGCTensor *> inputs;
+ for(unsigned int i = 0; i < node.num_inputs(); ++i)
+ {
+ inputs.push_back(get_backing_tensor(node.input(i)));
+ }
+ IGCTensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<GCDepthConcatenateLayer>();
+ func->configure(inputs, output);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCDepthConcatenateLayer"
+ << " Data Type: " << output->info()->data_type()
+ << " Shape: " << output->info()->tensor_shape()
+ << " Num Inputs: " << inputs.size()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend layer depth-wise convolution function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend depth-wise convolution layer function
+ */
+std::unique_ptr<IFunction> create_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating GC DepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name()
+ << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *weights = get_backing_tensor(node.input(1));
+ IGCTensor *biases = get_backing_tensor(node.input(2));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const PadStrideInfo conv_info = node.convolution_info();
+ const DepthwiseConvolutionMethod dwc_algorithm = node.depthwise_convolution_method();
+
+ // Create and configure function (we assume that functions have been validated before creation)
+ std::unique_ptr<IFunction> func;
+ std::string func_name;
+ if(dwc_algorithm == DepthwiseConvolutionMethod::OPTIMIZED_3x3)
+ {
+ std::tie(func, func_name) = create_named_function<GCDepthwiseConvolutionLayer3x3>(
+ std::string("GCDepthwiseConvolutionLayer3x3"), input, weights, biases, output, conv_info);
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("Generic DepthwiseConvolutionLayer is not supported in GLES backend");
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+ return func;
+}
+
+/** Create a backend element-wise operation layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend element-wise operation layer function
+ */
+std::unique_ptr<IFunction> create_eltwise_layer(EltwiseLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating GC EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 2);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input1 = get_backing_tensor(node.input(0));
+ IGCTensor *input2 = get_backing_tensor(node.input(1));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const EltwiseOperation eltwise_op = node.eltwise_operation();
+ ARM_COMPUTE_ERROR_ON(input1 == nullptr);
+ ARM_COMPUTE_ERROR_ON(input2 == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ std::unique_ptr<IFunction> func = nullptr;
+ std::string func_name;
+ if(eltwise_op == EltwiseOperation::ADD)
+ {
+ std::tie(func, func_name) = create_named_function<GCArithmeticAddition>(std::string("GCArithmeticAddition"),
+ input1, input2, output,
+ ConvertPolicy::SATURATE);
+ }
+ else if(eltwise_op == EltwiseOperation::SUB)
+ {
+ ARM_COMPUTE_ERROR("Arithmetic subtraction is not supported in GLES backend");
+ }
+ else if(eltwise_op == EltwiseOperation::MUL)
+ {
+ std::tie(func, func_name) = create_named_function<GCPixelWiseMultiplication>(
+ std::string("GCPixelWiseMultiplication"), input1, input2, output, 1.f);
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("Unsupported element-wise operation!");
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input1->info()->data_type()
+ << " Shape : " << input1->info()->tensor_shape()
+ << std::endl);
+
+ return func;
+}
+
+/** Create a backend fully connected layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend fully connected layer function
+ */
+std::unique_ptr<IFunction> create_fully_connected_layer(FullyConnectedLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating GC FullyConnectedLayer node with ID : " << node.id() << " and Name: " << node.name()
+ << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *weights = get_backing_tensor(node.input(1));
+ IGCTensor *biases = get_backing_tensor(node.input(2));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<GCFullyConnectedLayer>(get_memory_manager(ctx, Target::GC));
+ func->configure(input, weights, biases, output);
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(weights == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCFullyConnectedLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Biases Shape: " << biases->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend normalization layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend normalization layer function
+ */
+std::unique_ptr<IFunction> create_normalization_layer(NormalizationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating GC NormalizationLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const NormalizationLayerInfo norm_info = node.normalization_info();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<GCNormalizationLayer>();
+ func->configure(input, output, norm_info);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCNormalizationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << " Normalization info: " << norm_info.type()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend pooling layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend pooling layer function
+ */
+std::unique_ptr<IFunction> create_pooling_layer(PoolingLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating GC PoolingLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const PoolingLayerInfo pool_info = node.pooling_info();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<GCPoolingLayer>();
+ func->configure(input, output, pool_info);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCPoolingLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << " Pooling info: " << pool_info.pool_type()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend softmax layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend softmax layer function
+ */
+std::unique_ptr<IFunction> create_softmax_layer(SoftmaxLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE(
+ "Creating GC SoftmaxLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ IGCTensor *input = get_backing_tensor(node.input(0));
+ IGCTensor *output = get_backing_tensor(node.output(0));
+ const float beta = node.beta();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<GCSoftmaxLayer>(get_memory_manager(ctx, Target::CL));
+ func->configure(input, output, beta);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated GCSoftmaxLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+} // namespace
+
+std::unique_ptr<IFunction> GCFunctionFactory::create(INode *node, GraphContext &ctx)
+{
+ if(node == nullptr)
+ {
+ return nullptr;
+ }
+
+ NodeType type = node->type();
+ switch(type)
+ {
+ case NodeType::ActivationLayer:
+ return create_activation_layer(*polymorphic_downcast<ActivationLayerNode *>(node));
+ case NodeType::BatchNormalizationLayer:
+ return create_batch_normalization_layer(*polymorphic_downcast<BatchNormalizationLayerNode *>(node));
+ case NodeType::ConvolutionLayer:
+ return create_convolution_layer(*polymorphic_downcast<ConvolutionLayerNode *>(node), ctx);
+ case NodeType::DepthConcatenateLayer:
+ return create_depth_concatenate_layer(*polymorphic_downcast<DepthConcatenateLayerNode *>(node));
+ case NodeType::DepthwiseConvolutionLayer:
+ return create_depthwise_convolution_layer(*polymorphic_downcast<DepthwiseConvolutionLayerNode *>(node));
+ case NodeType::EltwiseLayer:
+ return create_eltwise_layer(*polymorphic_downcast<EltwiseLayerNode *>(node));
+ case NodeType::FullyConnectedLayer:
+ return create_fully_connected_layer(*polymorphic_downcast<FullyConnectedLayerNode *>(node), ctx);
+ case NodeType::NormalizationLayer:
+ return create_normalization_layer(*polymorphic_downcast<NormalizationLayerNode *>(node));
+ case NodeType::PoolingLayer:
+ return create_pooling_layer(*polymorphic_downcast<PoolingLayerNode *>(node));
+ case NodeType::SoftmaxLayer:
+ return create_softmax_layer(*polymorphic_downcast<SoftmaxLayerNode *>(node), ctx);
+ default:
+ return nullptr;
+ }
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/GLES/GCNodeValidator.cpp b/src/graph/backends/GLES/GCNodeValidator.cpp
new file mode 100644
index 0000000000..1e89265382
--- /dev/null
+++ b/src/graph/backends/GLES/GCNodeValidator.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/GLES/GCNodeValidator.h"
+
+#include "arm_compute/graph/backends/ValidateHelpers.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/runtime/GLES_COMPUTE/GCFunctions.h"
+
+using namespace arm_compute::utils::cast;
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+namespace
+{
+/** Validates a Depthwise Convolution layer node
+ *
+ * @param[in] node Node to validate
+ *
+ * @return Status
+ */
+Status validate_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating GCDepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ arm_compute::ITensorInfo *weights = detail::get_backing_tensor_info(node.input(1));
+ ARM_COMPUTE_ERROR_ON(weights == nullptr);
+
+ // TODO (geopin01) : Switch when validation is implemented
+ // Validate function
+ ARM_COMPUTE_RETURN_ERROR_ON_MSG(weights->tensor_shape().x() != 3 && weights->tensor_shape().y() != 3, "Unsupported depthwise convolution");
+ node.set_depthwise_convolution_method(DepthwiseConvolutionMethod::OPTIMIZED_3x3);
+
+ return Status{};
+}
+/** Validates a Convolution layer node
+ *
+ * @param[in] node Node to validate
+ *
+ * @return Status
+ */
+Status validate_convolution_layer(ConvolutionLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_RETURN_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ arm_compute::ITensorInfo *weights = detail::get_backing_tensor_info(node.input(1));
+ const PadStrideInfo conv_info = node.convolution_info();
+ const ConvolutionMethod conv_algorithm = node.convolution_method();
+
+ // Validate function
+ if(conv_algorithm == ConvolutionMethod::DIRECT)
+ {
+ bool is_square = weights->tensor_shape().x() == weights->tensor_shape().y();
+ bool is_direct = (weights->tensor_shape().x() == 1) || (weights->tensor_shape().x() == 3) || (weights->tensor_shape().x() == 5);
+ bool is_correct_stride = (conv_info.stride().first) <= 2 && (conv_info.stride().second <= 2);
+ if(!(is_square && is_direct && is_correct_stride))
+ {
+ node.set_convolution_method(ConvolutionMethod::DEFAULT);
+ }
+ }
+
+ return Status{};
+}
+} // namespace
+
+Status GCNodeValidator::validate(INode *node)
+{
+ if(node == nullptr)
+ {
+ return Status{};
+ }
+
+ NodeType type = node->type();
+ switch(type)
+ {
+ case NodeType::ConvolutionLayer:
+ return validate_convolution_layer(*polymorphic_downcast<ConvolutionLayerNode *>(node));
+ case NodeType::DepthwiseConvolutionLayer:
+ return validate_depthwise_convolution_layer(*polymorphic_downcast<DepthwiseConvolutionLayerNode *>(node));
+ case NodeType::FlattenLayer:
+ return ARM_COMPUTE_CREATE_ERROR(arm_compute::ErrorCode::RUNTIME_ERROR, "Unsupported operation");
+ case NodeType::ReshapeLayer:
+ return ARM_COMPUTE_CREATE_ERROR(arm_compute::ErrorCode::RUNTIME_ERROR, "Unsupported operation");
+ default:
+ return Status{};
+ }
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/NodeContext.cpp b/src/graph/backends/GLES/GCTensorHandle.cpp
index 2aa5aa13e8..ae7c778130 100644
--- a/src/graph/NodeContext.cpp
+++ b/src/graph/backends/GLES/GCTensorHandle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,55 +21,58 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/graph/NodeContext.h"
+#include "arm_compute/graph/backends/GLES/GCTensorHandle.h"
-using namespace arm_compute::graph;
-
-void NodeContext::set_target(TargetHint target)
+namespace arm_compute
{
- _target = target;
-}
-
-void NodeContext::add_input(arm_compute::ITensor *input)
+namespace graph
{
- ARM_COMPUTE_ERROR_ON(input == nullptr);
- _inputs.emplace_back(input);
+namespace backends
+{
+GCTensorHandle::GCTensorHandle(const ITensorInfo &info)
+ : _tensor()
+{
+ _tensor.allocator()->init(info);
}
-void NodeContext::add_output(arm_compute::ITensor *output)
+void GCTensorHandle::allocate()
{
- ARM_COMPUTE_ERROR_ON(output == nullptr);
- _outputs.emplace_back(output);
+ _tensor.allocator()->allocate();
}
-OperationType NodeContext::operation() const
+const arm_compute::ITensor &GCTensorHandle::tensor() const
{
- return _operation;
+ return _tensor;
}
-TargetHint NodeContext::target() const
+arm_compute::ITensor &GCTensorHandle::tensor()
{
- return _target;
+ return _tensor;
}
-arm_compute::ITensor *NodeContext::input(size_t idx) const
+void GCTensorHandle::map(bool blocking)
{
- ARM_COMPUTE_ERROR_ON(idx >= _inputs.size());
- return _inputs[idx];
+ _tensor.map(blocking);
}
-arm_compute::ITensor *NodeContext::output(size_t idx) const
+void GCTensorHandle::unmap()
{
- ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
- return _outputs[idx];
+ _tensor.unmap();
}
-size_t NodeContext::num_inputs() const
+void GCTensorHandle::release_if_unused()
{
- return _inputs.size();
+ // TODO (geopin01): Release tensor only if all sub-tensors are marked as not used
+ if(!_tensor.is_used())
+ {
+ _tensor.allocator()->free();
+ }
}
-size_t NodeContext::num_outputs() const
+bool GCTensorHandle::is_subtensor() const
{
- return _outputs.size();
-} \ No newline at end of file
+ return false;
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/NEON/NEDeviceBackend.cpp b/src/graph/backends/NEON/NEDeviceBackend.cpp
new file mode 100644
index 0000000000..87f88dffdf
--- /dev/null
+++ b/src/graph/backends/NEON/NEDeviceBackend.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/NEON/NEDeviceBackend.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/INode.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/Tensor.h"
+#include "arm_compute/graph/backends/BackendRegistrar.h"
+#include "arm_compute/graph/backends/NEON/NEFunctionFactory.h"
+#include "arm_compute/graph/backends/NEON/NENodeValidator.h"
+#include "arm_compute/graph/backends/NEON/NESubTensorHandle.h"
+#include "arm_compute/graph/backends/NEON/NETensorHandle.h"
+
+#include "arm_compute/core/TensorInfo.h"
+#include "arm_compute/runtime/Allocator.h"
+#include "arm_compute/runtime/BlobLifetimeManager.h"
+#include "arm_compute/runtime/MemoryManagerOnDemand.h"
+#include "arm_compute/runtime/OffsetLifetimeManager.h"
+#include "arm_compute/runtime/PoolManager.h"
+#include "arm_compute/runtime/Scheduler.h"
+
+#include "support/ToolchainSupport.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+/** Register NEON backend */
+static detail::BackendRegistrar<NEDeviceBackend> NEDeviceBackend_registrar(Target::NEON);
+
+NEDeviceBackend::NEDeviceBackend()
+ : _allocator()
+{
+}
+
+void NEDeviceBackend::initialize_backend()
+{
+}
+
+void NEDeviceBackend::setup_backend_context(GraphContext &ctx)
+{
+ // Set number of threads
+ Scheduler::get().set_num_threads(ctx.config().num_threads);
+
+ // Create function level memory manager
+ if(ctx.memory_management_ctx(Target::NEON) == nullptr)
+ {
+ MemoryManagerContext mm_ctx;
+ mm_ctx.target = Target::NEON;
+ mm_ctx.mm = create_memory_manager(MemoryManagerAffinity::Buffer);
+
+ ctx.insert_memory_management_ctx(std::move(mm_ctx));
+ }
+}
+
+bool NEDeviceBackend::is_backend_supported()
+{
+ return true;
+}
+
+std::unique_ptr<ITensorHandle> NEDeviceBackend::create_tensor(const Tensor &tensor)
+{
+ // Get tensor descriptor
+ const TensorDescriptor &tensor_desc = tensor.desc();
+ ARM_COMPUTE_ERROR_ON(tensor_desc.target != Target::NEON);
+
+ // Create backend tensor handle
+ TensorInfo info(tensor_desc.shape, 1, tensor_desc.data_type);
+ auto backend_tensor_handle = support::cpp14::make_unique<NETensorHandle>(info);
+
+ return std::move(backend_tensor_handle);
+}
+
+std::unique_ptr<ITensorHandle> NEDeviceBackend::create_subtensor(ITensorHandle *parent, TensorShape shape, Coordinates coords, bool extend_parent)
+{
+ if(parent == nullptr)
+ {
+ return nullptr;
+ }
+
+ return support::cpp14::make_unique<NESubTensorHandle>(parent, shape, coords, extend_parent);
+}
+
+std::unique_ptr<arm_compute::IFunction> NEDeviceBackend::configure_node(INode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Configuring NEON node with ID : " << node.id() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::NEON);
+
+ // Configure node
+ return NEFunctionFactory::create(&node, ctx);
+}
+
+arm_compute::Status NEDeviceBackend::validate_node(INode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Validating NEON node with ID : " << node.id() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.assigned_target() != Target::NEON);
+
+ return NENodeValidator::validate(&node);
+}
+
+std::shared_ptr<arm_compute::IMemoryManager> NEDeviceBackend::create_memory_manager(MemoryManagerAffinity affinity)
+{
+ std::shared_ptr<ILifetimeManager> lifetime_mgr = nullptr;
+ if(affinity == MemoryManagerAffinity::Buffer)
+ {
+ lifetime_mgr = std::make_shared<BlobLifetimeManager>();
+ }
+ else
+ {
+ lifetime_mgr = std::make_shared<OffsetLifetimeManager>();
+ }
+ auto pool_mgr = std::make_shared<PoolManager>();
+ auto mm = std::make_shared<MemoryManagerOnDemand>(lifetime_mgr, pool_mgr);
+
+ mm->set_allocator(&_allocator);
+
+ return mm;
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/NEON/NEFunctionFactory.cpp b/src/graph/backends/NEON/NEFunctionFactory.cpp
new file mode 100644
index 0000000000..228af9ca6f
--- /dev/null
+++ b/src/graph/backends/NEON/NEFunctionFactory.cpp
@@ -0,0 +1,563 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/NEON/NEFunctionFactory.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/TypePrinter.h"
+#include "arm_compute/graph/backends/Utils.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+#include "arm_compute/runtime/NEON/NEFunctions.h"
+#include "support/ToolchainSupport.h"
+
+using namespace arm_compute::utils::cast;
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+namespace
+{
+/** Returns backing tensor of a given tensor
+ *
+ * @param[in] tensor Tensor to extract the backing tensor from
+ *
+ * @return Backing tensor if present else nullptr
+ */
+arm_compute::ITensor *get_backing_tensor(arm_compute::graph::Tensor *tensor)
+{
+ return ((tensor == nullptr) || (tensor->handle() == nullptr)) ? nullptr : &tensor->handle()->tensor();
+}
+
+/** Create a backend activation layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend activation layer function
+ */
+std::unique_ptr<IFunction> create_activation_layer(ActivationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON ActivationLayerNode node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const ActivationLayerInfo act_info = node.activation_info();
+
+ // Create function
+ auto func = support::cpp14::make_unique<NEActivationLayer>();
+ func->configure(input, output, act_info);
+
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEActivationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Shape: " << input->info()->tensor_shape()
+ << " Activation function: " << act_info.activation()
+ << " a: " << act_info.a()
+ << " b: " << act_info.b()
+ << " InPlace : " << is_in_place_operation(input, output)
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend batch normalization layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend batch normalization layer function
+ */
+std::unique_ptr<IFunction> create_batch_normalization_layer(BatchNormalizationLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON BatchNormalization node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+
+ // TODO (geopin01) : Var and mean are compulsory, switch function to accept nullptr as beta and/or gamma
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 5);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *mean = get_backing_tensor(node.input(1));
+ ITensor *var = get_backing_tensor(node.input(2));
+ ITensor *beta = get_backing_tensor(node.input(3));
+ ITensor *gamma = get_backing_tensor(node.input(4));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const float epsilon = node.epsilon();
+ const ActivationLayerInfo fused_act = node.fused_activation();
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NEBatchNormalizationLayer>();
+ func->configure(input, output, mean, var, beta, gamma, epsilon, fused_act);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEBatchNormalizationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Shape: " << input->info()->tensor_shape()
+ << " Epsilon: " << epsilon << " "
+ << (fused_act.enabled() ? to_string(fused_act.activation()) : "")
+ << " InPlace : " << is_in_place_operation(input, output)
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend convolution layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend convolution layer function
+ */
+std::unique_ptr<IFunction> create_convolution_layer(ConvolutionLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON ConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *weights = get_backing_tensor(node.input(1));
+ ITensor *biases = get_backing_tensor(node.input(2));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const PadStrideInfo conv_info = node.convolution_info();
+ const ConvolutionMethod conv_algorithm = node.convolution_method();
+
+ // Create and configure function (we assume that functions have been validated before creation)
+ std::shared_ptr<IMemoryManager> mm = get_memory_manager(ctx, Target::NEON);
+ std::unique_ptr<IFunction> func;
+ std::string func_name;
+ if(conv_algorithm == ConvolutionMethod::DIRECT)
+ {
+ std::tie(func, func_name) = create_named_memory_managed_function<NEDirectConvolutionLayer>(std::string("NEDirectConvolutionLayer"), mm,
+ input, weights, biases, output, conv_info);
+ }
+ else if(conv_algorithm == ConvolutionMethod::GEMM)
+ {
+ std::tie(func, func_name) = create_named_memory_managed_function<NEGEMMConvolutionLayer>(std::string("NEGEMMConvolutionLayer"), mm,
+ input, weights, biases, output, conv_info);
+ }
+ else if(conv_algorithm == ConvolutionMethod::WINOGRAD)
+ {
+ std::tie(func, func_name) = create_named_memory_managed_function<NEWinogradLayer>(std::string("NEWinogradLayer"), mm,
+ input, weights, biases, output, conv_info);
+ }
+ else
+ {
+ std::tie(func, func_name) = create_named_memory_managed_function<NEConvolutionLayer>(std::string("NEConvolutionLayer"), mm,
+ input, weights, biases, output, conv_info);
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+ return func;
+}
+
+/** Create a backend layer depth concatenate function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend depth concatenate layer function
+ */
+std::unique_ptr<arm_compute::IFunction> create_depth_concatenate_layer(DepthConcatenateLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON DepthConcatenate node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Return nullptr if depth concatenate is switched off
+ if(!node.is_enabled())
+ {
+ return nullptr;
+ }
+
+ // Extract IO and info
+ std::vector<arm_compute::ITensor *> inputs;
+ for(unsigned int i = 0; i < node.num_inputs(); ++i)
+ {
+ inputs.push_back(get_backing_tensor(node.input(i)));
+ }
+ ITensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NEDepthConcatenateLayer>();
+ func->configure(inputs, output);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEDepthConcatenateLayer"
+ << " Data Type: " << output->info()->data_type()
+ << " Shape: " << output->info()->tensor_shape()
+ << " Num Inputs: " << inputs.size()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend layer depth-wise convolution function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend depth-wise convolution layer function
+ */
+std::unique_ptr<IFunction> create_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON DepthwiseConvolutionLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *weights = get_backing_tensor(node.input(1));
+ ITensor *biases = get_backing_tensor(node.input(2));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const PadStrideInfo conv_info = node.convolution_info();
+ const DepthwiseConvolutionMethod dwc_algorithm = node.depthwise_convolution_method();
+
+ // Create and configure function (we assume that functions have been validated before creation)
+ std::unique_ptr<IFunction> func;
+ std::string func_name;
+ if(dwc_algorithm == DepthwiseConvolutionMethod::OPTIMIZED_3x3)
+ {
+ std::tie(func, func_name) = create_named_function<NEDepthwiseConvolutionLayer3x3>(std::string("NEDepthwiseConvolutionLayer3x3"),
+ input, weights, biases, output, conv_info);
+ }
+ else
+ {
+ std::tie(func, func_name) = create_named_function<NEDepthwiseConvolutionLayer>(std::string("NEDepthwiseConvolutionLayer"),
+ input, weights, biases, output, conv_info);
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+ return func;
+}
+
+/** Create a backend element-wise operation layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend element-wise operation layer function
+ */
+std::unique_ptr<IFunction> create_eltwise_layer(EltwiseLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 2);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input1 = get_backing_tensor(node.input(0));
+ ITensor *input2 = get_backing_tensor(node.input(1));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const EltwiseOperation eltwise_op = node.eltwise_operation();
+ ARM_COMPUTE_ERROR_ON(input1 == nullptr);
+ ARM_COMPUTE_ERROR_ON(input2 == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ std::unique_ptr<IFunction> func = nullptr;
+ std::string func_name;
+ if(eltwise_op == EltwiseOperation::ADD)
+ {
+ std::tie(func, func_name) = create_named_function<NEArithmeticAddition>(std::string("NEArithmeticAddition"),
+ input1, input2, output, ConvertPolicy::SATURATE);
+ }
+ else if(eltwise_op == EltwiseOperation::SUB)
+ {
+ std::tie(func, func_name) = create_named_function<NEArithmeticSubtraction>(std::string("NEArithmeticSubtraction"),
+ input1, input2, output, ConvertPolicy::SATURATE);
+ }
+ else if(eltwise_op == EltwiseOperation::MUL)
+ {
+ std::tie(func, func_name) = create_named_function<NEPixelWiseMultiplication>(std::string("NEPixelWiseMultiplication"),
+ input1, input2, output, 1.f,
+ ConvertPolicy::SATURATE, RoundingPolicy::TO_NEAREST_EVEN);
+ }
+ else
+ {
+ ARM_COMPUTE_ERROR("Unsupported element-wise operation!");
+ }
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << func_name
+ << " Data Type: " << input1->info()->data_type()
+ << " Shape : " << input1->info()->tensor_shape()
+ << std::endl);
+
+ return func;
+}
+
+/** Create a backend flatten layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend flatten layer function
+ */
+std::unique_ptr<IFunction> create_flatten_layer(FlattenLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON FlattenLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NEFlattenLayer>();
+ func->configure(input, output);
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEFlattenLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend fully connected layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend fully connected layer function
+ */
+std::unique_ptr<IFunction> create_fully_connected_layer(FullyConnectedLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON FullyConnectedLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 3);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *weights = get_backing_tensor(node.input(1));
+ ITensor *biases = get_backing_tensor(node.input(2));
+ ITensor *output = get_backing_tensor(node.output(0));
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NEFullyConnectedLayer>(get_memory_manager(ctx, Target::NEON));
+ func->configure(input, weights, biases, output);
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(weights == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEFullyConnectedLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Weights shape: " << weights->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend normalization layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend normalization layer function
+ */
+std::unique_ptr<IFunction> create_normalization_layer(NormalizationLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON NormalizationLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const NormalizationLayerInfo norm_info = node.normalization_info();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NENormalizationLayer>(get_memory_manager(ctx, Target::NEON));
+ func->configure(input, output, norm_info);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NENormalizationLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << " Normalization info: " << norm_info.type()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend pooling layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend pooling layer function
+ */
+std::unique_ptr<IFunction> create_pooling_layer(PoolingLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON PoolingLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const PoolingLayerInfo pool_info = node.pooling_info();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NEPoolingLayer>();
+ func->configure(input, output, pool_info);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEPoolingLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << " Pooling info: " << pool_info.pool_type()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend reshape layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend reshape layer function
+ */
+std::unique_ptr<IFunction> create_reshape_layer(ReshapeLayerNode &node)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON ReshapeLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *output = get_backing_tensor(node.output(0));
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NEReshapeLayer>();
+ func->configure(input, output);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NEReshapeLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+
+/** Create a backend softmax layer function
+ *
+ * @param[in] node Node to create the backend function for
+ *
+ * @return Backend softmax layer function
+ */
+std::unique_ptr<IFunction> create_softmax_layer(SoftmaxLayerNode &node, GraphContext &ctx)
+{
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Creating NEON SoftmaxLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl);
+ ARM_COMPUTE_ERROR_ON(node.num_inputs() != 1);
+ ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1);
+
+ // Extract IO and info
+ ITensor *input = get_backing_tensor(node.input(0));
+ ITensor *output = get_backing_tensor(node.output(0));
+ const float beta = node.beta();
+ ARM_COMPUTE_ERROR_ON(input == nullptr);
+ ARM_COMPUTE_ERROR_ON(output == nullptr);
+
+ // Create and configure function
+ auto func = support::cpp14::make_unique<NESoftmaxLayer>(get_memory_manager(ctx, Target::NEON));
+ func->configure(input, output, beta);
+
+ // Log info
+ ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated NESoftmaxLayer"
+ << " Data Type: " << input->info()->data_type()
+ << " Input shape: " << input->info()->tensor_shape()
+ << " Output shape: " << output->info()->tensor_shape()
+ << std::endl);
+
+ return std::move(func);
+}
+} // namespace
+
+std::unique_ptr<IFunction> NEFunctionFactory::create(INode *node, GraphContext &ctx)
+{
+ if(node == nullptr)
+ {
+ return nullptr;
+ }
+
+ NodeType type = node->type();
+ switch(type)
+ {
+ case NodeType::ActivationLayer:
+ return create_activation_layer(*polymorphic_downcast<ActivationLayerNode *>(node));
+ case NodeType::BatchNormalizationLayer:
+ return create_batch_normalization_layer(*polymorphic_downcast<BatchNormalizationLayerNode *>(node));
+ case NodeType::ConvolutionLayer:
+ return create_convolution_layer(*polymorphic_downcast<ConvolutionLayerNode *>(node), ctx);
+ case NodeType::DepthConcatenateLayer:
+ return create_depth_concatenate_layer(*polymorphic_downcast<DepthConcatenateLayerNode *>(node));
+ case NodeType::DepthwiseConvolutionLayer:
+ return create_depthwise_convolution_layer(*polymorphic_downcast<DepthwiseConvolutionLayerNode *>(node));
+ case NodeType::EltwiseLayer:
+ return create_eltwise_layer(*polymorphic_downcast<EltwiseLayerNode *>(node));
+ case NodeType::FlattenLayer:
+ return create_flatten_layer(*polymorphic_downcast<FlattenLayerNode *>(node));
+ case NodeType::FullyConnectedLayer:
+ return create_fully_connected_layer(*polymorphic_downcast<FullyConnectedLayerNode *>(node), ctx);
+ case NodeType::NormalizationLayer:
+ return create_normalization_layer(*polymorphic_downcast<NormalizationLayerNode *>(node), ctx);
+ case NodeType::PoolingLayer:
+ return create_pooling_layer(*polymorphic_downcast<PoolingLayerNode *>(node));
+ case NodeType::ReshapeLayer:
+ return create_reshape_layer(*polymorphic_downcast<ReshapeLayerNode *>(node));
+ case NodeType::SoftmaxLayer:
+ return create_softmax_layer(*polymorphic_downcast<SoftmaxLayerNode *>(node), ctx);
+ default:
+ return nullptr;
+ }
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/NEON/NENodeValidator.cpp b/src/graph/backends/NEON/NENodeValidator.cpp
new file mode 100644
index 0000000000..074f03580f
--- /dev/null
+++ b/src/graph/backends/NEON/NENodeValidator.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/NEON/NENodeValidator.h"
+
+#include "arm_compute/graph/backends/ValidateHelpers.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/runtime/NEON/NEFunctions.h"
+
+using namespace arm_compute::utils::cast;
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+Status NENodeValidator::validate(INode *node)
+{
+ if(node == nullptr)
+ {
+ return Status{};
+ }
+
+ NodeType type = node->type();
+ switch(type)
+ {
+ case NodeType::ConvolutionLayer:
+ return detail::validate_convolution_layer<NEConvolutionLayer,
+ NEDirectConvolutionLayer,
+ NEGEMMConvolutionLayer,
+ NEWinogradLayer>(*polymorphic_downcast<ConvolutionLayerNode *>(node));
+ case NodeType::DepthwiseConvolutionLayer:
+ return detail::validate_depthwise_convolution_layer<NEDepthwiseConvolutionLayer,
+ NEDepthwiseConvolutionLayer3x3>(*polymorphic_downcast<DepthwiseConvolutionLayerNode *>(node));
+
+ default:
+ return Status{};
+ }
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/backends/NEON/NESubTensorHandle.cpp b/src/graph/backends/NEON/NESubTensorHandle.cpp
new file mode 100644
index 0000000000..c48ba6b9d6
--- /dev/null
+++ b/src/graph/backends/NEON/NESubTensorHandle.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/backends/NEON/NESubTensorHandle.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+NESubTensorHandle::NESubTensorHandle(ITensorHandle *parent_handle, const TensorShape &shape, const Coordinates &coords, bool extend_parent)
+ : _sub_tensor()
+{
+ ARM_COMPUTE_ERROR_ON(!parent_handle);
+ _sub_tensor = arm_compute::SubTensor(&parent_handle->tensor(), shape, coords, extend_parent);
+}
+
+void NESubTensorHandle::allocate()
+{
+ // noop
+}
+
+const arm_compute::ITensor &NESubTensorHandle::tensor() const
+{
+ return _sub_tensor;
+}
+
+arm_compute::ITensor &NESubTensorHandle::tensor()
+{
+ return _sub_tensor;
+}
+
+void NESubTensorHandle::map(bool blocking)
+{
+ ARM_COMPUTE_UNUSED(blocking);
+}
+
+void NESubTensorHandle::unmap()
+{
+ // noop
+}
+
+void NESubTensorHandle::release_if_unused()
+{
+ // noop
+}
+
+bool NESubTensorHandle::is_subtensor() const
+{
+ return true;
+}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/FloorLayer.cpp b/src/graph/backends/NEON/NETensorHandle.cpp
index 8750546ed9..8508ac9511 100644
--- a/src/graph/nodes/FloorLayer.cpp
+++ b/src/graph/backends/NEON/NETensorHandle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,29 +21,57 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/graph/nodes/FloorLayer.h"
+#include "arm_compute/graph/backends/NEON/NETensorHandle.h"
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
+namespace arm_compute
+{
+namespace graph
+{
+namespace backends
+{
+NETensorHandle::NETensorHandle(const ITensorInfo &info)
+ : _tensor()
+{
+ _tensor.allocator()->init(info);
+}
-using namespace arm_compute::graph;
+void NETensorHandle::allocate()
+{
+ _tensor.allocator()->allocate();
+}
-std::unique_ptr<arm_compute::IFunction> FloorLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
+const arm_compute::ITensor &NETensorHandle::tensor() const
{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
+ return _tensor;
+}
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
+arm_compute::ITensor &NETensorHandle::tensor()
+{
+ return _tensor;
+}
- // Create node context
- NodeContext node_ctx(OperationType::FloorLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_output(out);
+void NETensorHandle::map(bool blocking)
+{
+ ARM_COMPUTE_UNUSED(blocking);
+}
+
+void NETensorHandle::unmap()
+{
+}
- // Get function
- return OperationRegistry::get().find_operation(OperationType::FloorLayer, _target_hint)->configure(node_ctx);
+void NETensorHandle::release_if_unused()
+{
+ // TODO (geopin01): Release tensor only if all sub-tensors are marked as not used
+ if(!_tensor.is_used())
+ {
+ _tensor.allocator()->free();
+ }
+}
+
+bool NETensorHandle::is_subtensor() const
+{
+ return false;
}
+} // namespace backends
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/detail/ExecutionHelpers.cpp b/src/graph/detail/ExecutionHelpers.cpp
new file mode 100644
index 0000000000..5a50728164
--- /dev/null
+++ b/src/graph/detail/ExecutionHelpers.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/detail/ExecutionHelpers.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/GraphContext.h"
+#include "arm_compute/graph/GraphManager.h"
+#include "arm_compute/graph/Tensor.h"
+#include "arm_compute/graph/backends/BackendRegistry.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace detail
+{
+void default_initialize_backends()
+{
+ for(const auto &backend : backends::BackendRegistry::get().backends())
+ {
+ backend.second->initialize_backend();
+ }
+}
+
+void configure_all_tensors(Graph &g)
+{
+ auto &tensors = g.tensors();
+
+ for(auto &tensor : tensors)
+ {
+ if(tensor)
+ {
+ Target target = tensor->desc().target;
+ auto backend = backends::BackendRegistry::get().find_backend(target);
+ ARM_COMPUTE_ERROR_ON_MSG(!backend, "Requested backend doesn't exist!");
+ auto handle = backend->create_tensor(*tensor);
+ ARM_COMPUTE_ERROR_ON_MSG(!backend, "Couldn't create backend handle!");
+ tensor->set_handle(std::move(handle));
+ }
+ }
+}
+
+void allocate_all_tensors(Graph &g)
+{
+ auto &tensors = g.tensors();
+
+ for(auto &tensor : tensors)
+ {
+ if(tensor && !tensor->bound_edges().empty())
+ {
+ ARM_COMPUTE_ERROR_ON_MSG(!tensor->handle(), "Tensor handle is not configured!");
+ tensor->handle()->allocate();
+ }
+ }
+}
+
+void validate_all_nodes(Graph &g)
+{
+ auto &nodes = g.nodes();
+
+ // Create tasks
+ for(auto &node : nodes)
+ {
+ if(node != nullptr)
+ {
+ Target assigned_target = node->assigned_target();
+ auto backend = backends::BackendRegistry::get().find_backend(assigned_target);
+ ARM_COMPUTE_ERROR_ON_MSG(!backend, "Requested backend doesn't exist!");
+ Status status = backend->validate_node(*node);
+ ARM_COMPUTE_ERROR_ON_MSG(!bool(status), status.error_description().c_str());
+ }
+ }
+}
+
+ExecutionWorkload configure_all_nodes(Graph &g, GraphContext &ctx)
+{
+ ExecutionWorkload workload;
+ auto &nodes = g.nodes();
+
+ // Create tasks
+ for(auto &node : nodes)
+ {
+ if(node != nullptr)
+ {
+ Target assigned_target = node->assigned_target();
+ auto backend = backends::BackendRegistry::get().find_backend(assigned_target);
+ ARM_COMPUTE_ERROR_ON_MSG(!backend, "Requested backend doesn't exist!");
+ auto func = backend->configure_node(*node, ctx);
+ if(func != nullptr)
+ {
+ ExecutionTask task;
+ task.task = std::move(func);
+ task.node = node.get();
+ workload.tasks.push_back(std::move(task));
+ }
+ }
+ }
+
+ // Add inputs and outputs
+ for(auto &node : nodes)
+ {
+ if(node != nullptr && node->type() == NodeType::Input)
+ {
+ workload.inputs.push_back(node->output(0));
+ }
+
+ if(node != nullptr && node->type() == NodeType::Output)
+ {
+ workload.outputs.push_back(node->input(0));
+ continue;
+ }
+ }
+
+ return workload;
+}
+
+void release_unused_tensors(Graph &g)
+{
+ for(auto &tensor : g.tensors())
+ {
+ if(tensor != nullptr && tensor->handle() != nullptr)
+ {
+ tensor->handle()->release_if_unused();
+ }
+ }
+}
+
+void call_tensor_accessor(Tensor *tensor)
+{
+ ARM_COMPUTE_ERROR_ON(!tensor);
+ tensor->call_accessor();
+}
+
+void call_all_const_node_accessors(Graph &g)
+{
+ auto &nodes = g.nodes();
+
+ for(auto &node : nodes)
+ {
+ if(node != nullptr && node->type() == NodeType::Const)
+ {
+ call_tensor_accessor(node->output(0));
+ }
+ }
+}
+
+void call_all_input_node_accessors(ExecutionWorkload &workload)
+{
+ for(auto &input : workload.inputs)
+ {
+ if(input != nullptr)
+ {
+ input->call_accessor();
+ }
+ }
+}
+
+void call_all_tasks(ExecutionWorkload &workload)
+{
+ for(auto &task : workload.tasks)
+ {
+ task();
+ }
+}
+
+void call_all_output_node_accessors(ExecutionWorkload &workload)
+{
+ for(auto &output : workload.outputs)
+ {
+ if(output != nullptr)
+ {
+ output->call_accessor();
+ }
+ }
+}
+} // namespace detail
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/frontend/Stream.cpp b/src/graph/frontend/Stream.cpp
new file mode 100644
index 0000000000..96a166c79c
--- /dev/null
+++ b/src/graph/frontend/Stream.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/frontend/Stream.h"
+
+#include "arm_compute/graph/Utils.h"
+#include "arm_compute/graph/frontend/ILayer.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace frontend
+{
+Stream::Stream(size_t id, std::string name)
+ : _manager(), _ctx(), _g(id, std::move(name))
+{
+}
+
+void Stream::finalize(Target target, const GraphConfig &config)
+{
+ PassManager pm = create_default_pass_manager(target);
+ _ctx.set_config(config);
+ _manager.finalize_graph(_g, _ctx, pm, target);
+}
+
+void Stream::run()
+{
+ _manager.execute_graph(_g);
+}
+
+void Stream::add_layer(ILayer &layer)
+{
+ auto nid = layer.create_layer(*this);
+ _tail_node = nid;
+}
+
+const Graph &Stream::graph() const
+{
+ return _g;
+}
+
+Graph &Stream::graph()
+{
+ return _g;
+}
+} // namespace frontend
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/CL/CLMap.cpp b/src/graph/frontend/SubStream.cpp
index 5289ea9a04..e8bd23a557 100644
--- a/src/graph/CL/CLMap.cpp
+++ b/src/graph/frontend/SubStream.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,23 +21,39 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/graph/CL/CLMap.h"
+#include "arm_compute/graph/frontend/SubStream.h"
-#include "arm_compute/core/CL/ICLTensor.h"
-#include "arm_compute/core/Helpers.h"
-#include "arm_compute/core/Validate.h"
-#include "arm_compute/graph/ITensorObject.h"
-#include "arm_compute/runtime/CL/CLScheduler.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/frontend/ILayer.h"
-using namespace arm_compute::graph;
+namespace arm_compute
+{
+namespace graph
+{
+namespace frontend
+{
+SubStream::SubStream(IStream &s)
+ : _s(s)
+{
+ _hints = s.hints();
+ _tail_node = s.tail_node();
+}
+
+void SubStream::add_layer(ILayer &layer)
+{
+ auto nid = layer.create_layer(*this);
+ _tail_node = nid;
+}
-CLMap::CLMap(ITensorObject *tensor, bool blocking)
- : _tensor(dynamic_cast<arm_compute::ICLTensor *>(tensor->tensor())), _blocking(blocking)
+const Graph &SubStream::graph() const
{
- ARM_COMPUTE_ERROR_ON_NULLPTR(_tensor);
+ return _s.graph();
}
-void CLMap::run()
+Graph &SubStream::graph()
{
- _tensor->map(arm_compute::CLScheduler::get().queue(), _blocking);
+ return _s.graph();
}
+} // namespace frontend
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/mutators/DepthConcatSubTensorMutator.cpp b/src/graph/mutators/DepthConcatSubTensorMutator.cpp
new file mode 100644
index 0000000000..c56f4c5106
--- /dev/null
+++ b/src/graph/mutators/DepthConcatSubTensorMutator.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/mutators/DepthConcatSubTensorMutator.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/backends/BackendRegistry.h"
+#include "arm_compute/graph/nodes/DepthConcatenateLayerNode.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/core/utils/misc/Iterable.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+const char *DepthConcatSubTensorMutator::name()
+{
+ return "DepthConcatSubTensorMutator";
+}
+
+void DepthConcatSubTensorMutator::mutate(Graph &g)
+{
+ // Should be in reverse order of execution
+ for(auto &node : arm_compute::utils::iterable::reverse_iterate(g.nodes()))
+ {
+ if(node && node->type() == NodeType::DepthConcatenateLayer && node->output(0) != nullptr)
+ {
+ // Get output tensor
+ auto output_tensor = node->output(0);
+
+ // Check that all tensor have the same target and valid inputs
+ bool is_valid = std::all_of(node->input_edges().cbegin(), node->input_edges().cend(),
+ [&](const EdgeID & eid)
+ {
+ return (g.edge(eid) != nullptr) && (g.edge(eid)->tensor() != nullptr) && (g.edge(eid)->tensor()->desc().target == output_tensor->desc().target);
+ });
+
+ // Create subtensors
+ if(is_valid && backends::BackendRegistry::get().find_backend(output_tensor->desc().target) != nullptr)
+ {
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Using sub-tensors for the node with ID : "
+ << node->id() << " and name : " << node->name() << std::endl);
+ // Create sub-tensor handles
+ unsigned depth = 0;
+ for(unsigned int i = 0; i < node->input_edges().size(); ++i)
+ {
+ auto input_tensor = node->input(i);
+ const auto input_shape = input_tensor->desc().shape;
+
+ auto backend = backends::BackendRegistry::get().find_backend(input_tensor->desc().target);
+ auto handle = backend->create_subtensor(output_tensor->handle(), input_shape, Coordinates(0, 0, depth), false);
+ input_tensor->set_handle(std::move(handle));
+
+ depth += input_shape.z();
+ }
+
+ auto *dc_node = arm_compute::utils::cast::polymorphic_downcast<DepthConcatenateLayerNode *>(node.get());
+ dc_node->set_enabled(false);
+ }
+ }
+ }
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/mutators/InPlaceOperationMutator.cpp b/src/graph/mutators/InPlaceOperationMutator.cpp
new file mode 100644
index 0000000000..bd3f098965
--- /dev/null
+++ b/src/graph/mutators/InPlaceOperationMutator.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/mutators/InPlaceOperationMutator.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/Logger.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+const char *InPlaceOperationMutator::name()
+{
+ return "InPlaceOperationMutator";
+}
+
+void InPlaceOperationMutator::mutate(Graph &g)
+{
+ std::set<NodeType> in_place_nodes = { NodeType::BatchNormalizationLayer, NodeType::ActivationLayer };
+
+ // Not interested in the order of nodes
+ for(auto &node : g.nodes())
+ {
+ if(node && in_place_nodes.find(node->type()) != std::end(in_place_nodes))
+ {
+ // Get input edge
+ Edge *input_edge = node->input_edge(0);
+
+ // Check if parent has a single output if yes then force in place calculation else not
+ if((input_edge != nullptr) && (input_edge->producer() != nullptr) && (input_edge->producer()->output_edges().size() == 1))
+ {
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Switching to in-place computation for the node with ID : "
+ << node->id() << " and name : " << node->name() << std::endl);
+ // Update output
+ auto tensor = input_edge->tensor();
+ node->set_output_tensor(tensor->id(), 0);
+ }
+ }
+ }
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/mutators/NodeFusionMutator.cpp b/src/graph/mutators/NodeFusionMutator.cpp
new file mode 100644
index 0000000000..2e893c2e07
--- /dev/null
+++ b/src/graph/mutators/NodeFusionMutator.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/mutators/NodeFusionMutator.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+namespace detail
+{
+void fuse_batch_norm_with_activation(Graph &g)
+{
+ // Not interested in the order of nodes
+ for(auto &node : g.nodes())
+ {
+ // Check if the node is batch norm and not a branching node
+ if(node && node->type() == NodeType::BatchNormalizationLayer && node->output_edges().size() == 1)
+ {
+ auto output_edge_id = *node->output_edges().begin();
+ auto output_edge = g.edge(output_edge_id);
+ // Check if following node is an activation layer node
+ if((output_edge != nullptr) && (output_edge->consumer() != nullptr) && (output_edge->consumer()->type() == NodeType::ActivationLayer))
+ {
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Fusing Batch Normalization node with ID : " << output_edge->producer_id()
+ << " with Activation Layer node with ID : " << output_edge->consumer_id() << std::endl);
+
+ auto *bn_node = arm_compute::utils::cast::polymorphic_downcast<BatchNormalizationLayerNode *>(output_edge->producer());
+ auto *act_node = arm_compute::utils::cast::polymorphic_downcast<ActivationLayerNode *>(output_edge->consumer());
+
+ // Get driving nodes of activation node
+ std::vector<NodeIdxPair> act_driving_nodes;
+ for(auto &act_output_edge_id : act_node->output_edges())
+ {
+ auto act_output_edge = g.edge(act_output_edge_id);
+ if(act_output_edge != nullptr)
+ {
+ ARM_COMPUTE_ERROR_ON(act_output_edge->consumer() == nullptr);
+ act_driving_nodes.push_back({ act_output_edge->consumer_id(), act_output_edge->consumer_idx() });
+ }
+ }
+
+ // Set activation info to batch normalization
+ bn_node->set_fused_activation(act_node->activation_info());
+
+ // Remove activation node
+ g.remove_node(act_node->id());
+
+ // Update batch normalization node outputs
+ for(auto &driving_node : act_driving_nodes)
+ {
+ g.add_connection(bn_node->id(), 0, driving_node.node_id, driving_node.index);
+ }
+ }
+ }
+ }
+}
+} // namespace detail
+
+const char *NodeFusionMutator::name()
+{
+ return "NodeFusionMutator";
+}
+
+void NodeFusionMutator::mutate(Graph &g)
+{
+ detail::fuse_batch_norm_with_activation(g);
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/mutators/SplitLayerSubTensorMutator.cpp b/src/graph/mutators/SplitLayerSubTensorMutator.cpp
new file mode 100644
index 0000000000..179a6c35fb
--- /dev/null
+++ b/src/graph/mutators/SplitLayerSubTensorMutator.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/mutators/SplitLayerSubTensorMutator.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/Logger.h"
+#include "arm_compute/graph/backends/BackendRegistry.h"
+#include "arm_compute/graph/nodes/SplitLayerNode.h"
+
+#include "arm_compute/core/utils/misc/Cast.h"
+#include "arm_compute/core/utils/misc/Iterable.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+const char *SplitLayerSubTensorMutator::name()
+{
+ return "SplitLayerSubTensorMutator";
+}
+
+void SplitLayerSubTensorMutator::mutate(Graph &g)
+{
+ // Should be in reverse order of execution
+ for(auto &node : arm_compute::utils::iterable::reverse_iterate(g.nodes()))
+ {
+ if(node && node->type() == NodeType::SplitLayer && node->input(0) != nullptr)
+ {
+ // Get output tensor
+ Tensor *input_tensor = node->input(0);
+
+ // Check that all tensor have the same target and are valid
+ bool is_valid = std::all_of(node->outputs().cbegin(), node->outputs().cend(),
+ [&](const TensorID & tid)
+ {
+ return (g.tensor(tid) != nullptr) && (g.tensor(tid)->desc().target == input_tensor->desc().target);
+ });
+
+ // Create subtensors
+ if(is_valid && backends::BackendRegistry::get().find_backend(input_tensor->desc().target) != nullptr)
+ {
+ ARM_COMPUTE_LOG_GRAPH_VERBOSE("Using sub-tensors for the node with ID : "
+ << node->id() << " and name : " << node->name() << std::endl);
+
+ auto *split_node = arm_compute::utils::cast::polymorphic_downcast<SplitLayerNode *>(node.get());
+
+ const unsigned int axis = split_node->axis();
+ const unsigned int num_splits = split_node->num_splits();
+ const bool extend_parent = (axis < 2);
+
+ // Create sub-tensor handles
+ for(unsigned int i = 0; i < node->outputs().size(); ++i)
+ {
+ Tensor *output_tensor = node->output(i);
+ const TensorShape output_shape = output_tensor->desc().shape;
+ Coordinates coords;
+ std::tie(std::ignore, coords) = SplitLayerNode::compute_output_shape(input_tensor->desc().shape, num_splits, axis, i);
+
+ backends::IDeviceBackend *backend = backends::BackendRegistry::get().find_backend(output_tensor->desc().target);
+ std::unique_ptr<ITensorHandle> handle = backend->create_subtensor(input_tensor->handle(), output_shape, coords, extend_parent);
+ output_tensor->set_handle(std::move(handle));
+ }
+ }
+ }
+ }
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/nodes/ActivationLayer.cpp b/src/graph/nodes/ActivationLayer.cpp
deleted file mode 100644
index 546c42a1e5..0000000000
--- a/src/graph/nodes/ActivationLayer.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/nodes/ActivationLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-ActivationLayer::ActivationLayer(const ActivationLayerInfo activation_info)
- : _activation_info(activation_info)
-{
- set_supports_in_place(true);
-}
-
-std::unique_ptr<arm_compute::IFunction> ActivationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
-
- // Create node context
- 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
- return OperationRegistry::get().find_operation(OperationType::ActivationLayer, _target_hint)->configure(node_ctx);
-}
diff --git a/src/graph/nodes/ActivationLayerNode.cpp b/src/graph/nodes/ActivationLayerNode.cpp
new file mode 100644
index 0000000000..9996d2ce3f
--- /dev/null
+++ b/src/graph/nodes/ActivationLayerNode.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/ActivationLayerNode.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+ActivationLayerNode::ActivationLayerNode(ActivationLayerInfo info)
+ : _info(info)
+{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+ActivationLayerInfo ActivationLayerNode::activation_info() const
+{
+ return _info;
+}
+
+bool ActivationLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor ActivationLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ return src->desc();
+}
+
+Status ActivationLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType ActivationLayerNode::type() const
+{
+ return NodeType::ActivationLayer;
+}
+
+void ActivationLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/BatchNormalizationLayer.cpp b/src/graph/nodes/BatchNormalizationLayer.cpp
deleted file mode 100644
index 24287ac61a..0000000000
--- a/src/graph/nodes/BatchNormalizationLayer.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/nodes/BatchNormalizationLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-std::unique_ptr<arm_compute::IFunction> BatchNormalizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- 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)
- {
- _mean.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()));
- }
- if(_var.tensor() == nullptr)
- {
- _var.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()));
- }
- if(_beta.tensor() == nullptr)
- {
- _beta.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()));
- }
- if(_gamma.tensor() == nullptr)
- {
- _gamma.set_info(TensorInfo(TensorShape(batch_norm_size), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()));
- }
-
- bool mean_is_loaded = _mean.tensor() != nullptr;
- bool var_is_loaded = _var.tensor() != nullptr;
- bool gamma_is_loaded = _gamma.tensor() != nullptr;
- bool beta_is_loaded = _beta.tensor() != nullptr;
-
- // Set mean, var, gamma and beta target
- _mean.set_target(_target_hint);
- _var.set_target(_target_hint);
- _gamma.set_target(_target_hint);
- _beta.set_target(_target_hint);
-
- // 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);
- node_ctx.add_parameter<ActivationLayerInfo>("act_info", _act_info);
-
- // Configure operation
- auto func = OperationRegistry::get().find_operation(OperationType::BatchNormalizationLayer, _target_hint)->configure(node_ctx);
-
- // Fill tensors
- if(!mean_is_loaded)
- {
- _mean.allocate_and_fill_if_needed();
- }
- if(!var_is_loaded)
- {
- _var.allocate_and_fill_if_needed();
- }
- if(!gamma_is_loaded)
- {
- _gamma.allocate_and_fill_if_needed();
- }
- if(!beta_is_loaded)
- {
- _beta.allocate_and_fill_if_needed();
- }
-
- // Get function
- return func;
-} \ No newline at end of file
diff --git a/src/graph/nodes/BatchNormalizationLayerNode.cpp b/src/graph/nodes/BatchNormalizationLayerNode.cpp
new file mode 100644
index 0000000000..f7b041c828
--- /dev/null
+++ b/src/graph/nodes/BatchNormalizationLayerNode.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/BatchNormalizationLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+BatchNormalizationLayerNode::BatchNormalizationLayerNode(float epsilon, ActivationLayerInfo fused_activation)
+ : _epsilon(epsilon), _fused_activation(fused_activation)
+{
+ _input_edges.resize(5, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+float BatchNormalizationLayerNode::epsilon() const
+{
+ return _epsilon;
+}
+
+ActivationLayerInfo BatchNormalizationLayerNode::fused_activation() const
+{
+ return _fused_activation;
+}
+
+void BatchNormalizationLayerNode::set_fused_activation(ActivationLayerInfo fused_activation)
+{
+ _fused_activation = fused_activation;
+}
+
+bool BatchNormalizationLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor BatchNormalizationLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ return src->desc();
+}
+
+Status BatchNormalizationLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType BatchNormalizationLayerNode::type() const
+{
+ return NodeType::BatchNormalizationLayer;
+}
+
+void BatchNormalizationLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/BranchLayer.cpp b/src/graph/nodes/BranchLayer.cpp
deleted file mode 100644
index 7a20a565b8..0000000000
--- a/src/graph/nodes/BranchLayer.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/nodes/BranchLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/Graph.h"
-#include "arm_compute/graph/SubGraph.h"
-#include "arm_compute/graph/Tensor.h"
-#include "arm_compute/runtime/IFunction.h"
-#include "support/ToolchainSupport.h"
-#include "utils/TypePrinter.h"
-
-#include <memory>
-#include <tuple>
-#include <vector>
-
-using namespace arm_compute::graph;
-
-/** Branch function */
-class BranchFunction final : public arm_compute::IFunction
-{
-public:
- /** Default Constructor */
- BranchFunction()
- : _graphs()
- {
- }
- /** Registers graph to be executed by the branch function
- *
- * @param[in] graph Graph to register
- */
- void register_graph(std::unique_ptr<Graph> graph)
- {
- _graphs.push_back(std::move(graph));
- }
- // Inherited methods overriden:
- void run() override
- {
- for(auto &g : _graphs)
- {
- ARM_COMPUTE_ERROR_ON(g.get() == nullptr);
- g->run();
- }
- }
-
-private:
- std::vector<std::unique_ptr<Graph>> _graphs;
-};
-
-std::unique_ptr<arm_compute::IFunction> BranchLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON(_branch_merge_method != BranchMergeMethod::DEPTH_CONCATENATE);
- ARM_COMPUTE_UNUSED(_branch_merge_method);
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- // Create branch function
- auto func = arm_compute::support::cpp14::make_unique<BranchFunction>();
-
- // Track output depth
- int depth = 0;
-
- // Constuct all sub-graphs given the input/output
- for(auto &sg : _sub_graphs)
- {
- ARM_COMPUTE_ERROR_ON(sg.get() == nullptr);
-
- // IO buffers
- std::unique_ptr<ITensorObject> in;
- std::unique_ptr<ITensorObject> out;
- SubTensor *out_sub_tensor = nullptr;
-
- // Create input sub-tensor
- if(!sg->has_input())
- {
- ARM_COMPUTE_ERROR_ON(dynamic_cast<Tensor *>(input) == nullptr);
- in = arm_compute::support::cpp14::make_unique<SubTensor>(*dynamic_cast<Tensor *>(input),
- input->tensor()->info()->tensor_shape(),
- Coordinates());
- }
-
- // Create output sub-tensor
- if(!sg->has_output())
- {
- ARM_COMPUTE_ERROR_ON((dynamic_cast<Tensor *>(output) == nullptr) && (dynamic_cast<SubTensor *>(output) == nullptr));
-
- out = arm_compute::support::cpp14::make_unique<SubTensor>(output->tensor(),
- TensorShape(),
- Coordinates(0, 0, depth),
- output->target(),
- true);
- out_sub_tensor = dynamic_cast<SubTensor *>(out.get());
- }
-
- // Construct sub_graph
- auto g = sg->construct(ctx, std::move(in), std::move(out));
-
- // Register graph to function
- func->register_graph(std::move(g));
-
- // Update and track depth
- if(out_sub_tensor != nullptr)
- {
- ARM_COMPUTE_ERROR_ON(out_sub_tensor->tensor() == nullptr);
- depth += out_sub_tensor->tensor()->info()->tensor_shape()[2];
- }
- }
-
- return std::move(func);
-} \ No newline at end of file
diff --git a/src/graph/nodes/ConstNode.cpp b/src/graph/nodes/ConstNode.cpp
new file mode 100644
index 0000000000..631971c98f
--- /dev/null
+++ b/src/graph/nodes/ConstNode.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/ConstNode.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+ConstNode::ConstNode(TensorDescriptor desc)
+ : _desc(desc)
+{
+ _outputs.resize(1, NullTensorID);
+}
+
+bool ConstNode::forward_descriptors()
+{
+ if(output_id(0) != NullTensorID)
+ {
+ Tensor *t = output(0);
+ ARM_COMPUTE_ERROR_ON(t == nullptr);
+ t->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor ConstNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ return _desc;
+}
+
+Status ConstNode::validate()
+{
+ return Status{};
+}
+
+NodeType ConstNode::type() const
+{
+ return NodeType::Const;
+}
+
+void ConstNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/nodes/ConvolutionLayer.cpp b/src/graph/nodes/ConvolutionLayer.cpp
deleted file mode 100644
index 5b3a84a4ad..0000000000
--- a/src/graph/nodes/ConvolutionLayer.cpp
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/nodes/ConvolutionLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/runtime/CL/functions/CLConvolutionLayer.h"
-#include "arm_compute/runtime/CL/functions/CLDirectConvolutionLayer.h"
-#include "arm_compute/runtime/CL/functions/CLWinogradConvolutionLayer.h"
-#include "arm_compute/runtime/IFunction.h"
-#include "arm_compute/runtime/NEON/functions/NEConvolutionLayer.h"
-#include "arm_compute/runtime/NEON/functions/NEDirectConvolutionLayer.h"
-#include "arm_compute/runtime/NEON/functions/NEWinogradLayer.h"
-#include "support/ToolchainSupport.h"
-#include "utils/GraphTypePrinter.h"
-#include "utils/TypePrinter.h"
-
-#include <tuple>
-#include <vector>
-
-using namespace arm_compute::graph;
-
-namespace
-{
-/** Calculates the output shaped of the convolution layer
- *
- * @param[in] input_shape Input tensor shape
- * @param[in] weights_shape Weights shape
- * @param[in] conv_info Convolution information (padding, stride, etc.)
- *
- * @return The expected output tensor shape
- */
-TensorShape calculate_convolution_layer_output_shape(const TensorShape &input_shape, const TensorShape &weights_shape, const PadStrideInfo &conv_info)
-{
- unsigned int output_width = 0;
- unsigned int output_height = 0;
-
- // Get output width and height
- std::tie(output_width, output_height) = arm_compute::scaled_dimensions(input_shape.x(), input_shape.y(), weights_shape.x(), weights_shape.y(), conv_info);
-
- // Create output shape
- TensorShape output_shape = input_shape;
- output_shape.set(0, output_width);
- output_shape.set(1, output_height);
- output_shape.set(2, weights_shape[3]);
-
- return output_shape;
-}
-
-// Instantiate GEMM based convolution layer
-template <typename ConvolutionType, typename TensorType, TargetHint target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_function(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output,
- const PadStrideInfo &conv_info, const WeightsInfo &weights_info)
-{
- auto conv = arm_compute::support::cpp14::make_unique<ConvolutionType>();
- conv->configure(
- dynamic_cast<TensorType *>(input),
- dynamic_cast<TensorType *>(weights),
- dynamic_cast<TensorType *>(biases),
- dynamic_cast<TensorType *>(output),
- conv_info, weights_info);
- return std::move(conv);
-}
-
-// Instantiate direct convolution layer
-template <typename ConvolutionType, typename TensorType, TargetHint target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate_direct_function(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output,
- const PadStrideInfo &conv_info)
-{
- auto conv = arm_compute::support::cpp14::make_unique<ConvolutionType>();
- conv->configure(
- dynamic_cast<TensorType *>(input),
- dynamic_cast<TensorType *>(weights),
- dynamic_cast<TensorType *>(biases),
- dynamic_cast<TensorType *>(output),
- conv_info);
- return std::move(conv);
-}
-
-template <TargetHint target_hint>
-std::unique_ptr<arm_compute::IFunction> instantiate(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output,
- const PadStrideInfo &conv_info, const WeightsInfo &weights_info,
- ConvolutionMethodHint conv_method);
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::OPENCL>(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output,
- const PadStrideInfo &conv_info,
- const WeightsInfo &weights_info,
- ConvolutionMethodHint conv_method)
-{
- if((conv_method == ConvolutionMethodHint::WINOGRAD)
- && arm_compute::CLWinogradConvolutionLayer::validate(input->info(), weights->info(), biases != nullptr ? biases->info() : nullptr, output->info(), conv_info)) // NOLINT
- {
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLWinogradConvolutionLayer");
- return instantiate_direct_function<arm_compute::CLWinogradConvolutionLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, weights, biases, output, conv_info);
- }
- else if((conv_method == ConvolutionMethodHint::DIRECT)
- && arm_compute::CLDirectConvolutionLayer::validate(input->info(), weights->info(), biases != nullptr ? biases->info() : nullptr, output->info(), conv_info)) // NOLINT
- {
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDirectConvolutionLayer");
- return instantiate_direct_function<arm_compute::CLDirectConvolutionLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, weights, biases, output, conv_info);
- }
- else
- {
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLConvolutionLayer");
- return instantiate_function<arm_compute::CLConvolutionLayer, arm_compute::ICLTensor, TargetHint::OPENCL>(input, weights, biases, output, conv_info, weights_info);
- }
-}
-
-template <>
-std::unique_ptr<arm_compute::IFunction> instantiate<TargetHint::NEON>(arm_compute::ITensor *input, arm_compute::ITensor *weights, arm_compute::ITensor *biases, arm_compute::ITensor *output,
- const PadStrideInfo &conv_info,
- const WeightsInfo &weights_info,
- ConvolutionMethodHint conv_method)
-{
- const unsigned int kernel_size_x = weights->info()->tensor_shape().x();
- const unsigned int kernel_size_y = weights->info()->tensor_shape().y();
- const unsigned int conv_stride_x = conv_info.stride().first;
- const unsigned int conv_stride_y = conv_info.stride().second;
-
- bool is_square_kernel = (kernel_size_x == kernel_size_y);
- bool has_same_stride = (conv_stride_x == conv_stride_y);
-
- // TODO (COMPID-765) : Winograd should have a validate function
- if(conv_method == ConvolutionMethodHint::WINOGRAD && is_square_kernel && ((kernel_size_x == 3) || (kernel_size_x == 5)) && has_same_stride && (conv_info.stride().first == 1))
- {
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEWinogradConvolutionLayer");
- return instantiate_direct_function<arm_compute::NEWinogradLayer, arm_compute::ITensor, TargetHint::NEON>(input, weights, biases, output, conv_info);
- }
- else if((conv_method == ConvolutionMethodHint::DIRECT)
- && arm_compute::NEDirectConvolutionLayer::validate(input->info(), weights->info(), biases != nullptr ? biases->info() : nullptr, output->info(), conv_info)) // NOLINT
- {
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDirectConvolutionLayer");
- return instantiate_direct_function<arm_compute::NEDirectConvolutionLayer, arm_compute::ITensor, TargetHint::NEON>(input, weights, biases, output, conv_info);
- }
- else
- {
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEConvolutionLayer");
- return instantiate_function<arm_compute::NEConvolutionLayer, arm_compute::ITensor, TargetHint::NEON>(input, weights, biases, output, conv_info, weights_info);
- }
-}
-} // namespace
-
-/** Grouped Convolution function */
-class GroupedConvolutionFunction final : public arm_compute::IFunction
-{
-public:
- /** Default Constructor */
- GroupedConvolutionFunction() = default;
- /** Default Destructor */
- ~GroupedConvolutionFunction() final = default;
- /** Prevent instances from being copy constructed */
- GroupedConvolutionFunction(const GroupedConvolutionFunction &) = delete;
- /** Prevent instances from being copy assigned */
- GroupedConvolutionFunction &operator=(const GroupedConvolutionFunction &) = delete;
- /** Allow instances to be move constructed */
- GroupedConvolutionFunction(GroupedConvolutionFunction &&) noexcept = default;
- /** Allow instances to be move assigned */
- GroupedConvolutionFunction &operator=(GroupedConvolutionFunction &&) noexcept = default;
- /** Adds a convolution
- *
- * @param convolution Convolution function to add
- */
- void add_convolution_function(std::unique_ptr<IFunction> convolution) // NOLINT
- {
- _convolutions.emplace_back(std::move(convolution));
- }
-
- // Inherited methods overridden:
- void run() override
- {
- for(auto &c : _convolutions)
- {
- c->run();
- }
- }
-
-private:
- std::vector<std::unique_ptr<IFunction>> _convolutions{};
-};
-
-std::unique_ptr<arm_compute::IFunction> ConvolutionLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
-
- // Set weights and biases info
- if(_weights.tensor() == nullptr)
- {
- TensorInfo info = TensorInfo(TensorShape(_conv_width, _conv_height, in->info()->dimension(2) / _num_groups, _ofm),
- in->info()->num_channels(),
- in->info()->data_type(),
- in->info()->fixed_point_position());
- info.set_quantization_info(_weights_quant_info);
- _weights.set_info(std::move(info));
- }
- if(_biases.has_accessor() && _biases.tensor() == nullptr)
- {
- DataType dt = in->info()->data_type();
- _biases.set_info(TensorInfo(TensorShape(_ofm), in->info()->num_channels(), is_data_type_quantized_asymmetric(dt) ? DataType::S32 : dt, in->info()->fixed_point_position()));
- }
-
- std::unique_ptr<arm_compute::IFunction> func;
- _target_hint = ctx.hints().target_hint();
- const ConvolutionMethodHint conv_method_hint = ctx.hints().convolution_method_hint();
-
- // Check if the weights and biases are loaded
- bool weights_are_loaded = _weights.tensor() != nullptr;
- bool biases_are_loaded = _biases.has_accessor() ? _biases.tensor() != nullptr : true;
-
- // Set bias and weights target
- _weights.set_target(_target_hint);
- if(_biases.has_accessor())
- {
- _biases.set_target(_target_hint);
- }
-
- // Calculate output shape
- TensorShape output_shape = calculate_convolution_layer_output_shape(in->info()->tensor_shape(), _weights.info().tensor_shape(), _conv_info);
-
- // Output auto inizialitation if not yet initialized
- arm_compute::auto_init_if_empty(*out->info(), output_shape, 1, in->info()->data_type(), in->info()->fixed_point_position(),
- (_out_quant_info.empty()) ? in->info()->quantization_info() : _out_quant_info);
-
- // Create appropriate convolution function
- if(_num_groups == 1)
- {
- func = instantiate_convolution(in, out, conv_method_hint);
- }
- else
- {
- func = instantiate_grouped_convolution(in, out, conv_method_hint);
- }
-
- // Fill weights
- if(!weights_are_loaded)
- {
- _weights.allocate_and_fill_if_needed();
- }
- // Fill biases
- if(!biases_are_loaded)
- {
- _biases.allocate_and_fill_if_needed();
- }
-
- ARM_COMPUTE_LOG_GRAPH_INFO(" Data 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()
- << " PadStrideInfo: " << _conv_info
- << " Groups: " << _num_groups
- << " WeightsInfo: " << _weights_info
- << std::endl);
-
- return func;
-}
-
-std::unique_ptr<arm_compute::IFunction> ConvolutionLayer::instantiate_convolution(ITensor *input, ITensor *output, ConvolutionMethodHint conv_method_hint)
-{
- std::unique_ptr<arm_compute::IFunction> func;
- if(_target_hint == TargetHint::OPENCL)
- {
- func = instantiate<TargetHint::OPENCL>(input, _weights.tensor(), _biases.tensor(), output, _conv_info, _weights_info, conv_method_hint);
- }
- else
- {
- func = instantiate<TargetHint::NEON>(input, _weights.tensor(), _biases.tensor(), output, _conv_info, _weights_info, conv_method_hint);
- }
- return func;
-}
-
-std::unique_ptr<arm_compute::IFunction> ConvolutionLayer::instantiate_grouped_convolution(ITensor *input, ITensor *output, ConvolutionMethodHint conv_method_hint)
-{
- // Get tensor shapes
- TensorShape input_shape = input->info()->tensor_shape();
- TensorShape output_shape = output->info()->tensor_shape();
- TensorShape weights_shape = _weights.info().tensor_shape();
- TensorShape biases_shape = _biases.info().tensor_shape();
-
- ARM_COMPUTE_ERROR_ON_MSG((input_shape.z() % _num_groups) != 0, "Input depth not multiple of the number of groups!");
- ARM_COMPUTE_ERROR_ON_MSG((output_shape.z() % _num_groups) != 0, "Output depth not multiple of the number of groups!");
- ARM_COMPUTE_ERROR_ON_MSG((weights_shape[3] % _num_groups) != 0, "Number of kernels not multiple of the number of groups!");
- ARM_COMPUTE_ERROR_ON_MSG((biases_shape.x() % _num_groups) != 0, "Biases not multiple of the number of groups!");
-
- // Create a grouped convolution function
- auto grouped_conv = arm_compute::support::cpp14::make_unique<GroupedConvolutionFunction>();
-
- // Create sub-tensors vectors
- _is = arm_compute::support::cpp14::make_unique<SubTensor[]>(_num_groups);
- _os = arm_compute::support::cpp14::make_unique<SubTensor[]>(_num_groups);
- _ws = arm_compute::support::cpp14::make_unique<SubTensor[]>(_num_groups);
- _bs = arm_compute::support::cpp14::make_unique<SubTensor[]>(_num_groups);
-
- // Calculate sub-tensor splits
- const int input_split = input_shape.z() / _num_groups;
- const int output_split = output_shape.z() / _num_groups;
- const int weights_split = weights_shape[3] / _num_groups;
- const int biases_split = biases_shape.x() / _num_groups;
-
- // Calculate sub-tensor shapes
- input_shape.set(2, input_split);
- output_shape.set(2, output_split);
- weights_shape.set(3, weights_split);
- biases_shape.set(0, biases_split);
-
- // Configure sub-tensors
- for(int i = 0; i < static_cast<int>(_num_groups); ++i)
- {
- // Create convolution function
- std::unique_ptr<arm_compute::IFunction> func;
-
- // Calculate sub-tensors starting coordinates
- Coordinates input_coord(0, 0, input_split * i);
- Coordinates output_coord(0, 0, output_split * i);
- Coordinates weights_coord(0, 0, 0, weights_split * i);
- Coordinates biases_coord(biases_split * i);
-
- // Create sub-tensors for input, output, weights and bias
- auto hint_to_use = (_target_hint == TargetHint::OPENCL) ? TargetHint::OPENCL : TargetHint::NEON;
- _is[i] = SubTensor(input, input_shape, input_coord, hint_to_use);
- _os[i] = SubTensor(output, output_shape, output_coord, hint_to_use);
- _ws[i] = SubTensor(_weights.tensor(), weights_shape, weights_coord, hint_to_use);
- _bs[i] = SubTensor(_biases.tensor(), biases_shape, biases_coord, hint_to_use);
-
- // Instantiate convolution function
- if(_target_hint == TargetHint::OPENCL)
- {
- func = instantiate<TargetHint::OPENCL>(_is[i].tensor(), _ws[i].tensor(), _bs[i].tensor(), _os[i].tensor(), _conv_info, _weights_info, conv_method_hint);
- }
- else
- {
- func = instantiate<TargetHint::NEON>(_is[i].tensor(), _ws[i].tensor(), _bs[i].tensor(), _os[i].tensor(), _conv_info, _weights_info, conv_method_hint);
- }
-
- // Add convolution function to the list of convolutions for the grouped convolution
- grouped_conv->add_convolution_function(std::move(func));
- }
-
- return std::move(grouped_conv);
-}
diff --git a/src/graph/nodes/ConvolutionLayerNode.cpp b/src/graph/nodes/ConvolutionLayerNode.cpp
new file mode 100644
index 0000000000..461728487f
--- /dev/null
+++ b/src/graph/nodes/ConvolutionLayerNode.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/ConvolutionLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+ConvolutionLayerNode::ConvolutionLayerNode(PadStrideInfo info, ConvolutionMethod method)
+ : _info(std::move(info)), _method(method)
+{
+ _input_edges.resize(3, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+void ConvolutionLayerNode::set_convolution_method(ConvolutionMethod method)
+{
+ _method = method;
+}
+
+ConvolutionMethod ConvolutionLayerNode::convolution_method() const
+{
+ return _method;
+}
+
+PadStrideInfo ConvolutionLayerNode::convolution_info() const
+{
+ return _info;
+}
+
+TensorShape ConvolutionLayerNode::compute_output_shape(TensorShape input_shape, TensorShape weights_shape, PadStrideInfo info)
+{
+ unsigned int output_width = 0;
+ unsigned int output_height = 0;
+ std::tie(output_width, output_height) = scaled_dimensions(input_shape.x(), input_shape.y(), weights_shape.x(), weights_shape.y(), info);
+
+ TensorShape output_shape{ input_shape };
+ output_shape.set(0, output_width);
+ output_shape.set(1, output_height);
+ output_shape.set(2, weights_shape[3]);
+
+ return output_shape;
+}
+
+bool ConvolutionLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (input_id(1) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor ConvolutionLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ const Tensor *src = input(0);
+ const Tensor *weights = input(1);
+
+ ARM_COMPUTE_ERROR_ON(src == nullptr || weights == nullptr);
+
+ TensorDescriptor output_info = src->desc();
+ TensorShape output_shape = compute_output_shape(src->desc().shape, weights->desc().shape, _info);
+ output_info.shape = output_shape;
+ return output_info;
+}
+
+Status ConvolutionLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType ConvolutionLayerNode::type() const
+{
+ return NodeType::ConvolutionLayer;
+}
+
+void ConvolutionLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/DeQuantizationLayer.cpp b/src/graph/nodes/DeQuantizationLayer.cpp
deleted file mode 100644
index af9ecee157..0000000000
--- a/src/graph/nodes/DeQuantizationLayer.cpp
+++ /dev/null
@@ -1,68 +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 "arm_compute/graph/nodes/DequantizationLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-
-using namespace arm_compute::graph;
-
-std::unique_ptr<arm_compute::IFunction> DequantizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- _target_hint = ctx.hints().target_hint();
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
-
- if(_min_max.tensor() == nullptr)
- {
- TensorShape shape = in->info()->tensor_shape();
- shape.set(Window::DimX, 2);
- shape.remove_dimension(1);
- shape.remove_dimension(1);
-
- _min_max.set_info(TensorInfo(shape, in->info()->num_channels(), DataType::F32));
- _min_max.set_target(_target_hint);
- }
-
- bool minmax_is_loaded = _min_max.tensor() != nullptr;
-
- // Create node context
- NodeContext node_ctx(OperationType::DequantizationLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_output(_min_max.tensor());
- node_ctx.add_output(out);
-
- // Fill min max
- if(!minmax_is_loaded)
- {
- _min_max.allocate_and_fill_if_needed();
- }
-
- // Get function
- return OperationRegistry::get().find_operation(OperationType::DequantizationLayer, _target_hint)->configure(node_ctx);
-}
diff --git a/src/graph/nodes/DepthConcatenateLayerNode.cpp b/src/graph/nodes/DepthConcatenateLayerNode.cpp
new file mode 100644
index 0000000000..1c0539744f
--- /dev/null
+++ b/src/graph/nodes/DepthConcatenateLayerNode.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/DepthConcatenateLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+DepthConcatenateLayerNode::DepthConcatenateLayerNode(unsigned int total_nodes)
+ : _total_nodes(total_nodes), _is_enabled(true)
+{
+ _input_edges.resize(total_nodes, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+void DepthConcatenateLayerNode::set_enabled(bool is_enabled)
+{
+ _is_enabled = is_enabled;
+}
+
+bool DepthConcatenateLayerNode::is_enabled() const
+{
+ return _is_enabled;
+}
+
+TensorShape DepthConcatenateLayerNode::compute_output_shape(const std::vector<TensorShape> &input_shapes)
+{
+ ARM_COMPUTE_ERROR_ON(input_shapes.size() == 0);
+
+ TensorShape output_shape = input_shapes[0];
+
+ size_t max_x = 0;
+ size_t max_y = 0;
+ size_t depth = 0;
+
+ for(const auto &shape : input_shapes)
+ {
+ max_x = std::max(shape.x(), max_x);
+ max_y = std::max(shape.y(), max_y);
+ depth += shape.z();
+ }
+
+ output_shape.set(0, max_x);
+ output_shape.set(1, max_y);
+ output_shape.set(2, depth);
+
+ return output_shape;
+}
+
+bool DepthConcatenateLayerNode::forward_descriptors()
+{
+ if(_outputs[0] != NullTensorID)
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor DepthConcatenateLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ // Check if all input tensors are set
+ bool are_all_inputs_set = std::all_of(std::begin(_input_edges), std::end(_input_edges), [](const EdgeID & eid)
+ {
+ return eid != EmptyEdgeID;
+ });
+
+ TensorDescriptor output_info = {};
+
+ if(are_all_inputs_set)
+ {
+ std::vector<TensorShape> inputs_shapes;
+ for(unsigned int i = 0; i < _input_edges.size(); ++i)
+ {
+ const Tensor *t = _graph->tensor(input_id(i));
+ ARM_COMPUTE_ERROR_ON(t == nullptr);
+ inputs_shapes.push_back(t->desc().shape);
+ }
+ output_info = input(0)->desc();
+ TensorShape output_shape = compute_output_shape(inputs_shapes);
+ output_info.shape = output_shape;
+ }
+
+ return output_info;
+}
+
+Status DepthConcatenateLayerNode::validate()
+{
+ ARM_COMPUTE_UNUSED(_total_nodes);
+ return Status{};
+}
+
+NodeType DepthConcatenateLayerNode::type() const
+{
+ return NodeType::DepthConcatenateLayer;
+}
+
+void DepthConcatenateLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/DepthConvertLayer.cpp b/src/graph/nodes/DepthConvertLayer.cpp
deleted file mode 100644
index 9b328e7b3e..0000000000
--- a/src/graph/nodes/DepthConvertLayer.cpp
+++ /dev/null
@@ -1,58 +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 "arm_compute/graph/nodes/DepthConvertLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-
-using namespace arm_compute::graph;
-
-DepthConvertLayer::DepthConvertLayer(const ConvertPolicy policy, uint32_t shift, DataType output_datatype)
- : _policy(policy), _shift(shift), _output_datatype(output_datatype)
-{
-}
-
-std::unique_ptr<arm_compute::IFunction> DepthConvertLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- _target_hint = ctx.hints().target_hint();
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
-
- // Auto configure output
- arm_compute::auto_init_if_empty(*out->info(), in->info()->tensor_shape(), 1, _output_datatype, in->info()->fixed_point_position());
-
- // Create node context
- NodeContext node_ctx(OperationType::DepthConvertLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_output(out);
- node_ctx.add_parameter<ConvertPolicy>("ConvertPolicy", _policy);
- node_ctx.add_parameter<uint32_t>("shift", _shift);
-
- // Get function
- return OperationRegistry::get().find_operation(OperationType::DepthConvertLayer, _target_hint)->configure(node_ctx);
-}
diff --git a/src/graph/nodes/DepthwiseConvolutionLayer.cpp b/src/graph/nodes/DepthwiseConvolutionLayer.cpp
deleted file mode 100644
index e5101cc33c..0000000000
--- a/src/graph/nodes/DepthwiseConvolutionLayer.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/nodes/DepthwiseConvolutionLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-std::unique_ptr<arm_compute::IFunction> DepthwiseConvolutionLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
-
- if(_weights.tensor() == nullptr)
- {
- TensorShape weights_shape(_conv_width, _conv_height, input->tensor()->info()->tensor_shape().z());
- TensorInfo info = TensorInfo(TensorShape(weights_shape), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position());
- info.set_quantization_info(_quant_info);
- _weights.set_info(std::move(info));
- }
- if(_biases.has_accessor() && _biases.tensor() == nullptr)
- {
- DataType dt = in->info()->data_type();
- _biases.set_info(TensorInfo(TensorShape(in->info()->dimension(2)), in->info()->num_channels(), is_data_type_quantized_asymmetric(dt) ? DataType::S32 : dt, in->info()->fixed_point_position()));
- }
-
- bool weights_is_loaded = _weights.tensor() != nullptr;
- bool biases_is_loaded = _biases.has_accessor() ? _biases.tensor() != nullptr : true;
-
- _weights.set_target(_target_hint);
- if(_biases.has_accessor())
- {
- _biases.set_target(_target_hint);
- }
-
- // Create node context
- NodeContext node_ctx(OperationType::DepthwiseConvolutionLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_input(_weights.tensor());
- if(_biases.has_accessor())
- {
- node_ctx.add_input(_biases.tensor());
- }
- node_ctx.add_output(out);
- node_ctx.add_parameter<PadStrideInfo>("ConvolutionInfo", _conv_info);
- node_ctx.add_parameter<bool>("Optimized3x3", _opt3x3);
-
- // Configure operation
- auto func = OperationRegistry::get().find_operation(OperationType::DepthwiseConvolutionLayer, _target_hint)->configure(node_ctx);
-
- // Fill tensors
- if(!weights_is_loaded)
- {
- _weights.allocate_and_fill_if_needed();
- }
- if(!biases_is_loaded)
- {
- _biases.allocate_and_fill_if_needed();
- }
-
- // Get function
- return func;
-}
diff --git a/src/graph/nodes/DepthwiseConvolutionLayerNode.cpp b/src/graph/nodes/DepthwiseConvolutionLayerNode.cpp
new file mode 100644
index 0000000000..67a39029e6
--- /dev/null
+++ b/src/graph/nodes/DepthwiseConvolutionLayerNode.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/DepthwiseConvolutionLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+DepthwiseConvolutionLayerNode::DepthwiseConvolutionLayerNode(PadStrideInfo info, DepthwiseConvolutionMethod method)
+ : _info(std::move(info)), _method(method)
+{
+ _input_edges.resize(3, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+void DepthwiseConvolutionLayerNode::set_depthwise_convolution_method(DepthwiseConvolutionMethod method)
+{
+ _method = method;
+}
+
+DepthwiseConvolutionMethod DepthwiseConvolutionLayerNode::depthwise_convolution_method() const
+{
+ return _method;
+}
+
+PadStrideInfo DepthwiseConvolutionLayerNode::convolution_info() const
+{
+ return _info;
+}
+
+TensorShape DepthwiseConvolutionLayerNode::compute_output_shape(TensorShape input_shape, TensorShape weights_shape, PadStrideInfo info)
+{
+ unsigned int output_width = 0;
+ unsigned int output_height = 0;
+ std::tie(output_width, output_height) = scaled_dimensions(input_shape.x(), input_shape.y(), weights_shape.x(), weights_shape.y(), info);
+
+ TensorShape output_shape{ input_shape };
+ output_shape.set(0, output_width);
+ output_shape.set(1, output_height);
+
+ return output_shape;
+}
+
+bool DepthwiseConvolutionLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (input_id(1) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor DepthwiseConvolutionLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ const Tensor *src = input(0);
+ const Tensor *weights = input(1);
+
+ ARM_COMPUTE_ERROR_ON(src == nullptr || weights == nullptr);
+
+ TensorDescriptor output_info = src->desc();
+ TensorShape output_shape = compute_output_shape(src->desc().shape, weights->desc().shape, _info);
+ output_info.shape = output_shape;
+ return output_info;
+}
+
+Status DepthwiseConvolutionLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType DepthwiseConvolutionLayerNode::type() const
+{
+ return NodeType::DepthwiseConvolutionLayer;
+}
+
+void DepthwiseConvolutionLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/EltwiseLayerNode.cpp b/src/graph/nodes/EltwiseLayerNode.cpp
new file mode 100644
index 0000000000..b794043f2f
--- /dev/null
+++ b/src/graph/nodes/EltwiseLayerNode.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/EltwiseLayerNode.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+EltwiseLayerNode::EltwiseLayerNode(EltwiseOperation op)
+ : _op(op)
+{
+ _input_edges.resize(2, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+EltwiseOperation EltwiseLayerNode::eltwise_operation() const
+{
+ return _op;
+}
+
+bool EltwiseLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor EltwiseLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_UNUSED(_op);
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ return src->desc();
+}
+
+Status EltwiseLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType EltwiseLayerNode::type() const
+{
+ return NodeType::EltwiseLayer;
+}
+
+void EltwiseLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/nodes/FlattenLayer.cpp b/src/graph/nodes/FlattenLayer.cpp
deleted file mode 100644
index ea08296ba3..0000000000
--- a/src/graph/nodes/FlattenLayer.cpp
+++ /dev/null
@@ -1,54 +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 "arm_compute/graph/nodes/FlattenLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-std::unique_ptr<arm_compute::IFunction> FlattenLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- _target_hint = ctx.hints().target_hint();
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
-
- // Auto configure output
- TensorShape tensor_shape = in->info()->tensor_shape();
- tensor_shape.collapse(in->info()->num_dimensions());
- arm_compute::auto_init_if_empty(*out->info(), tensor_shape, 1, in->info()->data_type(), in->info()->fixed_point_position());
-
- // Create node context
- NodeContext node_ctx(OperationType::FlattenLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_output(out);
-
- // Get function
- return OperationRegistry::get().find_operation(OperationType::FlattenLayer, _target_hint)->configure(node_ctx);
-} \ No newline at end of file
diff --git a/src/graph/nodes/FlattenLayerNode.cpp b/src/graph/nodes/FlattenLayerNode.cpp
new file mode 100644
index 0000000000..8b847c7056
--- /dev/null
+++ b/src/graph/nodes/FlattenLayerNode.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/FlattenLayerNode.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+FlattenLayerNode::FlattenLayerNode()
+{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+bool FlattenLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor FlattenLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ TensorDescriptor output_desc = src->desc();
+ output_desc.shape.collapse(src->desc().shape.num_dimensions());
+
+ return output_desc;
+}
+
+Status FlattenLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType FlattenLayerNode::type() const
+{
+ return NodeType::FlattenLayer;
+}
+
+void FlattenLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/FullyConnectedLayer.cpp b/src/graph/nodes/FullyConnectedLayer.cpp
index 3742150d37..cbf2b35ddd 100644
--- a/src/graph/nodes/FullyConnectedLayer.cpp
+++ b/src/graph/nodes/FullyConnectedLayer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,18 +21,40 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/graph/nodes/FullyConnectedLayer.h"
+#include "arm_compute/graph/nodes/FullyConnectedLayerNode.h"
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
-using namespace arm_compute::graph;
+namespace arm_compute
+{
+namespace graph
+{
+FullyConnectedLayerNode::FullyConnectedLayerNode(unsigned int num_outputs)
+ : _num_outputs(num_outputs)
+{
+ _input_edges.resize(3, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
-namespace
+TensorShape FullyConnectedLayerNode::compute_weights_shape(TensorShape input_shape, unsigned int num_outputs)
{
-TensorShape calculate_fullyconnected_layer_output_shape(const TensorShape &input_shape, unsigned int output_neurons)
+ unsigned int num_weights = 1;
+ unsigned int num_dimensions = input_shape.num_dimensions();
+ // Ignore the batch dimension if there is one:
+ if(num_dimensions == 2 || num_dimensions == 4)
+ {
+ num_dimensions--;
+ }
+ for(unsigned int i = 0; i < num_dimensions; i++)
+ {
+ num_weights *= input_shape[i];
+ }
+ return TensorShape(num_weights, num_outputs);
+}
+
+TensorShape FullyConnectedLayerNode::compute_output_shape(TensorShape input_shape, unsigned int num_outputs)
{
// Note: Only 1D batch space is supported at the moment
unsigned int batches = input_shape[1];
@@ -40,67 +62,46 @@ TensorShape calculate_fullyconnected_layer_output_shape(const TensorShape &input
{
batches = input_shape[3];
}
- return TensorShape(output_neurons, batches);
+ return TensorShape(num_outputs, batches);
}
-} // namespace
-std::unique_ptr<arm_compute::IFunction> FullyConnectedLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
+bool FullyConnectedLayerNode::forward_descriptors()
{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
-
- if(_weights.tensor() == nullptr)
- {
- unsigned int num_weights = 1;
- unsigned int num_dimensions = in->info()->num_dimensions();
- // Ignore the batch dimension if there is one:
- if(num_dimensions == 2 || num_dimensions == 4)
- {
- num_dimensions--;
- }
- for(unsigned int i = 0; i < num_dimensions; i++)
- {
- num_weights *= in->info()->dimension(i);
- }
- _weights.set_info(TensorInfo(TensorShape(num_weights, _num_neurons), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()));
- }
- if(_biases.tensor() == nullptr)
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
{
- _biases.set_info(TensorInfo(TensorShape(_num_neurons), in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position()));
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
}
+ return false;
+}
- // Auto configure output
- arm_compute::auto_init_if_empty(*out->info(),
- calculate_fullyconnected_layer_output_shape(in->info()->tensor_shape(), _num_neurons),
- in->info()->num_channels(), in->info()->data_type(), in->info()->fixed_point_position());
-
- bool weights_are_loaded = _weights.tensor() != nullptr;
- bool biases_are_loaded = _biases.tensor() != nullptr;
+TensorDescriptor FullyConnectedLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
- // 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);
+ TensorDescriptor output_info = src->desc();
+ TensorShape output_shape = compute_output_shape(src->desc().shape, _num_outputs);
+ output_info.shape = output_shape;
+ return output_info;
+}
- // Configure operation
- auto func = OperationRegistry::get().find_operation(OperationType::FullyConnectedLayer, _target_hint)->configure(node_ctx);
+Status FullyConnectedLayerNode::validate()
+{
+ return Status{};
+}
- // Fill biases
- if(!weights_are_loaded)
- {
- _weights.allocate_and_fill_if_needed();
- }
- if(!biases_are_loaded)
- {
- _biases.allocate_and_fill_if_needed();
- }
+NodeType FullyConnectedLayerNode::type() const
+{
+ return NodeType::FullyConnectedLayer;
+}
- // Get function
- return func;
+void FullyConnectedLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/InputNode.cpp b/src/graph/nodes/InputNode.cpp
new file mode 100644
index 0000000000..e912633a66
--- /dev/null
+++ b/src/graph/nodes/InputNode.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/InputNode.h"
+
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+InputNode::InputNode(TensorDescriptor desc)
+ : _desc(desc)
+{
+ _outputs.resize(1, NullTensorID);
+}
+
+bool InputNode::forward_descriptors()
+{
+ if(output_id(0) != NullTensorID)
+ {
+ Tensor *t = output(0);
+ ARM_COMPUTE_ERROR_ON(t == nullptr);
+ t->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor InputNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ return _desc;
+}
+
+Status InputNode::validate()
+{
+ return Status{};
+}
+
+NodeType InputNode::type() const
+{
+ return NodeType::Input;
+}
+
+void InputNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/nodes/L2NormalizeLayer.cpp b/src/graph/nodes/L2NormalizeLayer.cpp
deleted file mode 100644
index 9813ba4450..0000000000
--- a/src/graph/nodes/L2NormalizeLayer.cpp
+++ /dev/null
@@ -1,56 +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 "arm_compute/graph/nodes/L2NormalizeLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-L2NormalizeLayer::L2NormalizeLayer(unsigned int axis, float epsilon)
- : _axis(axis), _epsilon(epsilon)
-{
-}
-
-std::unique_ptr<arm_compute::IFunction> L2NormalizeLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
-
- // 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);
-
- // 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
deleted file mode 100644
index a489329243..0000000000
--- a/src/graph/nodes/NormalizationLayer.cpp
+++ /dev/null
@@ -1,55 +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 "arm_compute/graph/nodes/NormalizationLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-NormalizationLayer::NormalizationLayer(const NormalizationLayerInfo norm_info)
- : _norm_info(norm_info)
-{
-}
-
-std::unique_ptr<arm_compute::IFunction> NormalizationLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
-
- // 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);
-
- // Get function
- return OperationRegistry::get().find_operation(OperationType::NormalizationLayer, _target_hint)->configure(node_ctx);
-}
diff --git a/src/graph/nodes/NormalizationLayerNode.cpp b/src/graph/nodes/NormalizationLayerNode.cpp
new file mode 100644
index 0000000000..a9f2fbd066
--- /dev/null
+++ b/src/graph/nodes/NormalizationLayerNode.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/NormalizationLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+NormalizationLayerNode::NormalizationLayerNode(NormalizationLayerInfo norm_info)
+ : _info(norm_info)
+{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+NormalizationLayerInfo NormalizationLayerNode::normalization_info() const
+{
+ return _info;
+}
+
+bool NormalizationLayerNode::forward_descriptors()
+{
+ if(input_id(0) != NullTensorID && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor NormalizationLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ return src->desc();
+}
+
+Status NormalizationLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType NormalizationLayerNode::type() const
+{
+ return NodeType::NormalizationLayer;
+}
+
+void NormalizationLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/OutputNode.cpp b/src/graph/nodes/OutputNode.cpp
new file mode 100644
index 0000000000..4c63bfa20c
--- /dev/null
+++ b/src/graph/nodes/OutputNode.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/OutputNode.h"
+
+#include "arm_compute/core/Error.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+#include "arm_compute/graph/Tensor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+OutputNode::OutputNode()
+{
+ _input_edges.resize(1, EmptyEdgeID);
+}
+
+bool OutputNode::forward_descriptors()
+{
+ return true;
+}
+
+TensorDescriptor OutputNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ return TensorDescriptor();
+}
+
+Status OutputNode::validate()
+{
+ return Status{};
+}
+
+NodeType OutputNode::type() const
+{
+ return NodeType::Output;
+}
+
+void OutputNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute
diff --git a/src/graph/nodes/PoolingLayer.cpp b/src/graph/nodes/PoolingLayer.cpp
deleted file mode 100644
index 2c151194f3..0000000000
--- a/src/graph/nodes/PoolingLayer.cpp
+++ /dev/null
@@ -1,55 +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 "arm_compute/graph/nodes/PoolingLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-PoolingLayer::PoolingLayer(const PoolingLayerInfo pool_info)
- : _pool_info(pool_info)
-{
-}
-
-std::unique_ptr<arm_compute::IFunction> PoolingLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
-
- // 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);
-
- // Get function
- return OperationRegistry::get().find_operation(OperationType::PoolingLayer, _target_hint)->configure(node_ctx);
-}
diff --git a/src/graph/nodes/PoolingLayerNode.cpp b/src/graph/nodes/PoolingLayerNode.cpp
new file mode 100644
index 0000000000..a7b6b3679a
--- /dev/null
+++ b/src/graph/nodes/PoolingLayerNode.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/PoolingLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+PoolingLayerNode::PoolingLayerNode(PoolingLayerInfo pool_info)
+ : _info(std::move(pool_info))
+{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+PoolingLayerInfo PoolingLayerNode::pooling_info() const
+{
+ return _info;
+}
+
+TensorShape PoolingLayerNode::compute_output_shape(TensorShape input_shape, PoolingLayerInfo info)
+{
+ const int pool_size_x = info.is_global_pooling() ? input_shape.x() : info.pool_size().width;
+ const int pool_size_y = info.is_global_pooling() ? input_shape.y() : info.pool_size().height;
+
+ unsigned int pooled_width = 0;
+ unsigned int pooled_height = 0;
+ std::tie(pooled_width, pooled_height) = scaled_dimensions(input_shape.x(), input_shape.y(), pool_size_x, pool_size_y, info.pad_stride_info());
+
+ TensorShape output_shape{ input_shape };
+ output_shape.set(0, pooled_width);
+ output_shape.set(1, pooled_height);
+
+ return output_shape;
+}
+
+bool PoolingLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor PoolingLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ TensorDescriptor output_info = src->desc();
+ TensorShape output_shape = compute_output_shape(src->desc().shape, _info);
+ output_info.shape = output_shape;
+ return output_info;
+}
+
+Status PoolingLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType PoolingLayerNode::type() const
+{
+ return NodeType::PoolingLayer;
+}
+
+void PoolingLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/ReshapeLayer.cpp b/src/graph/nodes/ReshapeLayer.cpp
index b0c117e418..2757f06bd3 100644
--- a/src/graph/nodes/ReshapeLayer.cpp
+++ b/src/graph/nodes/ReshapeLayer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2018 ARM Limited.
*
* SPDX-License-Identifier: MIT
*
@@ -21,37 +21,61 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "arm_compute/graph/nodes/ReshapeLayer.h"
+#include "arm_compute/graph/nodes/ReshapeLayerNode.h"
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
-using namespace arm_compute::graph;
-
-ReshapeLayer::ReshapeLayer(TensorShape shape)
+namespace arm_compute
+{
+namespace graph
+{
+ReshapeLayerNode::ReshapeLayerNode(TensorShape shape)
: _shape(shape)
{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
}
-std::unique_ptr<arm_compute::IFunction> ReshapeLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
+bool ReshapeLayerNode::forward_descriptors()
{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor ReshapeLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
- _target_hint = ctx.hints().target_hint();
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
+ TensorDescriptor output_desc = src->desc();
+ output_desc.shape = _shape;
- // Auto configure output
- arm_compute::auto_init_if_empty(*out->info(), _shape, 1, in->info()->data_type(), in->info()->fixed_point_position(), in->info()->quantization_info());
+ return output_desc;
+}
- // Create node context
- NodeContext node_ctx(OperationType::ReshapeLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_output(out);
+Status ReshapeLayerNode::validate()
+{
+ return Status{};
+}
- // Get function
- return OperationRegistry::get().find_operation(OperationType::ReshapeLayer, _target_hint)->configure(node_ctx);
+NodeType ReshapeLayerNode::type() const
+{
+ return NodeType::ReshapeLayer;
+}
+
+void ReshapeLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/ResidualLayer.cpp b/src/graph/nodes/ResidualLayer.cpp
deleted file mode 100644
index 87404f9e1f..0000000000
--- a/src/graph/nodes/ResidualLayer.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/graph/nodes/ResidualLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/Graph.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "arm_compute/graph/SubGraph.h"
-#include "arm_compute/graph/Tensor.h"
-#include "arm_compute/runtime/IFunction.h"
-#include "support/ToolchainSupport.h"
-#include "utils/Utils.h"
-
-#include <memory>
-#include <tuple>
-#include <vector>
-
-using namespace arm_compute::graph;
-
-/** Residual function */
-class ResidualFunction final : public arm_compute::IFunction
-{
-public:
- /** Default Constructor */
- ResidualFunction(GraphContext &ctx, ITensorObject *output)
- : _ctx(ctx), _input(nullptr), _output(output), _func(nullptr), _graphs(), _graph_outputs()
- {
- }
-
- /** Prevent instances from being copy constructed */
- ResidualFunction(const ResidualFunction &) = delete;
- /** Prevent instances from being copy assigned */
- const ResidualFunction &operator=(const ResidualFunction &) = delete;
- /** Prevent instances from being move constructed */
- ResidualFunction(ResidualFunction &&) = delete;
- /** Prevent instances from being move assigned */
- ResidualFunction &operator=(ResidualFunction &&) = delete;
- /** Default destructor */
- ~ResidualFunction() override = default;
-
- /** Set the input (when using only one sub graph)
- *
- * @param[in] input Input to set
- */
- void set_input(std::unique_ptr<ITensorObject> input)
- {
- _input = std::move(input);
- }
-
- /** Registers graph to be executed by the residual function
- *
- * @param[in] graph Graph to register
- * @param[in] output Output to register
- */
- void register_graph(std::unique_ptr<Graph> graph, std::unique_ptr<ITensorObject> output)
- {
- _graphs.push_back(std::move(graph));
- _graph_outputs.push_back(std::move(output));
- }
-
- /** Configure the function */
- void configure()
- {
- ARM_COMPUTE_ERROR_ON(_graphs.size() < 1 || _graphs.size() > 2);
- TargetHint target_hint = _ctx.hints().target_hint();
-
- // Create node context
- NodeContext node_ctx(OperationType::ArithmeticAddition);
- node_ctx.set_target(target_hint);
-
- if(_graphs.size() == 1)
- {
- arm_compute::ITensor *in = _input->tensor();
- node_ctx.add_input(in);
- }
-
- for(auto &o : _graph_outputs)
- {
- arm_compute::ITensor *in = o->tensor();
- node_ctx.add_input(in);
- }
-
- arm_compute::ITensor *out = _output->tensor();
- auto_init_if_empty(*out->info(), *_graph_outputs[0]->tensor()->info());
- node_ctx.add_output(out);
-
- _func = OperationRegistry::get().find_operation(OperationType::ArithmeticAddition, target_hint)->configure(node_ctx);
-
- for(auto &o : _graph_outputs)
- {
- o->allocate();
- }
- }
-
- // Inherited methods overriden:
- void run() override
- {
- ARM_COMPUTE_ERROR_ON(_graphs.size() < 1 || _graphs.size() > 2);
-
- for(auto &g : _graphs)
- {
- ARM_COMPUTE_ERROR_ON(g.get() == nullptr);
- g->run();
- }
-
- _func->run();
- }
-
-private:
- GraphContext _ctx;
- std::unique_ptr<ITensorObject> _input;
- ITensorObject *_output;
- std::unique_ptr<arm_compute::IFunction> _func;
- std::vector<std::unique_ptr<Graph>> _graphs;
- std::vector<std::unique_ptr<ITensorObject>> _graph_outputs;
-};
-
-std::unique_ptr<arm_compute::IFunction> ResidualLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<Tensor *>(input) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<Tensor *>(output) == nullptr);
-
- // Create residual function
- auto func = arm_compute::support::cpp14::make_unique<ResidualFunction>(ctx, output);
-
- if(_sub_graphs.size() == 1)
- {
- std::unique_ptr<ITensorObject> original_in;
- original_in = arm_compute::support::cpp14::make_unique<SubTensor>(*dynamic_cast<Tensor *>(input),
- input->tensor()->info()->tensor_shape(),
- Coordinates());
- func->set_input(std::move(original_in));
- }
-
- // Constuct all sub-graphs given the input/output
- for(auto &sg : _sub_graphs)
- {
- ARM_COMPUTE_ERROR_ON(sg.get() == nullptr);
-
- // IO buffers
- std::unique_ptr<ITensorObject> in;
- std::unique_ptr<ITensorObject> out;
- std::unique_ptr<ITensorObject> func_in;
-
- // Create input sub-tensor
- if(!sg->has_input())
- {
- in = arm_compute::support::cpp14::make_unique<SubTensor>(*dynamic_cast<Tensor *>(input),
- input->tensor()->info()->tensor_shape(),
- Coordinates());
- }
-
- // Create output sub-tensor
- if(!sg->has_output())
- {
- ITensorInfo *info = input->tensor()->info();
- func_in = arm_compute::support::cpp14::make_unique<Tensor>(TensorInfo(info->num_channels(), info->data_type(), info->fixed_point_position()));
- func_in->set_target(ctx.hints().target_hint());
- out = arm_compute::support::cpp14::make_unique<SubTensor>(func_in->tensor(),
- TensorShape(),
- Coordinates(0, 0, 0),
- func_in->target(),
- true);
- }
-
- // Construct sub_graph
- auto g = sg->construct(ctx, std::move(in), std::move(out));
-
- // Register graph to function
- func->register_graph(std::move(g), std::move(func_in));
- }
-
- func->configure();
-
- return std::move(func);
-}
diff --git a/src/graph/nodes/SoftmaxLayer.cpp b/src/graph/nodes/SoftmaxLayer.cpp
deleted file mode 100644
index 7f2325b312..0000000000
--- a/src/graph/nodes/SoftmaxLayer.cpp
+++ /dev/null
@@ -1,49 +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 "arm_compute/graph/nodes/SoftmaxLayer.h"
-
-#include "arm_compute/graph/Error.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistry.h"
-#include "support/ToolchainSupport.h"
-
-using namespace arm_compute::graph;
-
-std::unique_ptr<arm_compute::IFunction> SoftmaxLayer::instantiate_node(GraphContext &ctx, ITensorObject *input, ITensorObject *output)
-{
- ARM_COMPUTE_ERROR_ON_UNALLOCATED_TENSOR_OBJECT(input, output);
-
- arm_compute::ITensor *in = input->tensor();
- arm_compute::ITensor *out = output->tensor();
- _target_hint = ctx.hints().target_hint();
-
- // Create node context
- NodeContext node_ctx(OperationType::SoftmaxLayer);
- node_ctx.set_target(_target_hint);
- node_ctx.add_input(in);
- node_ctx.add_output(out);
-
- // Get function
- return OperationRegistry::get().find_operation(OperationType::SoftmaxLayer, _target_hint)->configure(node_ctx);
-}
diff --git a/src/graph/nodes/SoftmaxLayerNode.cpp b/src/graph/nodes/SoftmaxLayerNode.cpp
new file mode 100644
index 0000000000..4c21ac6ad0
--- /dev/null
+++ b/src/graph/nodes/SoftmaxLayerNode.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/SoftmaxLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+SoftmaxLayerNode::SoftmaxLayerNode(float beta)
+ : _beta(beta)
+{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(1, NullTensorID);
+}
+
+float SoftmaxLayerNode::beta() const
+{
+ return _beta;
+}
+
+bool SoftmaxLayerNode::forward_descriptors()
+{
+ if((input_id(0) != NullTensorID) && (output_id(0) != NullTensorID))
+ {
+ Tensor *dst = output(0);
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ dst->desc() = configure_output(0);
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor SoftmaxLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ return src->desc();
+}
+
+Status SoftmaxLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType SoftmaxLayerNode::type() const
+{
+ return NodeType::SoftmaxLayer;
+}
+
+void SoftmaxLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/nodes/SplitLayerNode.cpp b/src/graph/nodes/SplitLayerNode.cpp
new file mode 100644
index 0000000000..c8fb43c2a1
--- /dev/null
+++ b/src/graph/nodes/SplitLayerNode.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/nodes/SplitLayerNode.h"
+
+#include "arm_compute/core/Utils.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/INodeVisitor.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+SplitLayerNode::SplitLayerNode(unsigned int num_splits, unsigned int axis)
+ : _num_splits(num_splits), _axis(axis)
+{
+ _input_edges.resize(1, EmptyEdgeID);
+ _outputs.resize(num_splits, NullTensorID);
+}
+
+unsigned int SplitLayerNode::num_splits() const
+{
+ return _num_splits;
+}
+
+unsigned int SplitLayerNode::axis() const
+{
+ return _axis;
+}
+
+std::pair<TensorShape, Coordinates> SplitLayerNode::compute_output_shape(TensorShape input_shape, unsigned int num_splits, unsigned int axis, unsigned int idx)
+{
+ ARM_COMPUTE_ERROR_ON(axis >= input_shape.num_dimensions());
+ ARM_COMPUTE_ERROR_ON_MSG(input_shape[axis] % num_splits, "Split should be exact");
+
+ const unsigned int split_size = input_shape[axis] / num_splits;
+
+ TensorShape output_shape = input_shape;
+ output_shape.set(axis, split_size);
+
+ Coordinates coords;
+ coords.set(axis, idx * split_size);
+
+ return std::make_pair(output_shape, coords);
+}
+
+bool SplitLayerNode::forward_descriptors()
+{
+ if(input_id(0) != NullTensorID)
+ {
+ for(unsigned int i = 0; i < _outputs.size(); ++i)
+ {
+ if(output_id(i) != NullTensorID)
+ {
+ Tensor *dst_i = output(i);
+ ARM_COMPUTE_ERROR_ON(dst_i == nullptr);
+ dst_i->desc() = configure_output(i);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+TensorDescriptor SplitLayerNode::configure_output(size_t idx) const
+{
+ ARM_COMPUTE_UNUSED(idx);
+ ARM_COMPUTE_ERROR_ON(idx >= _outputs.size());
+
+ const Tensor *src = input(0);
+ ARM_COMPUTE_ERROR_ON(src == nullptr);
+
+ TensorShape output_shape;
+
+ TensorDescriptor output_info = src->desc();
+ std::tie(output_shape, std::ignore) = compute_output_shape(src->desc().shape, _num_splits, _axis, idx);
+ output_info.shape = output_shape;
+
+ return output_info;
+}
+
+Status SplitLayerNode::validate()
+{
+ return Status{};
+}
+
+NodeType SplitLayerNode::type() const
+{
+ return NodeType::SplitLayer;
+}
+
+void SplitLayerNode::accept(INodeVisitor &v)
+{
+ v.visit(*this);
+}
+} // namespace graph
+} // namespace arm_compute \ No newline at end of file
diff --git a/src/graph/operations/CLSimpleOperations.cpp b/src/graph/operations/CLSimpleOperations.cpp
deleted file mode 100644
index fe56122009..0000000000
--- a/src/graph/operations/CLSimpleOperations.cpp
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/core/CL/ICLTensor.h"
-#include "arm_compute/core/Error.h"
-#include "arm_compute/graph/IOperation.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistrar.h"
-#include "arm_compute/graph/Types.h"
-#include "arm_compute/runtime/CL/CLFunctions.h"
-#include "support/ToolchainSupport.h"
-#include "utils/GraphTypePrinter.h"
-#include "utils/TypePrinter.h"
-
-#include <memory>
-
-using namespace arm_compute::graph;
-
-/* Activation Layer */
-REGISTER_SIMPLE_OPERATION(CLActivationLayerOperation, OPENCL, OperationType::ActivationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- const auto act_info = ctx.parameter<ActivationLayerInfo>("ActivationLayerInfo");
-
- // Create and configure function
- auto activation = arm_compute::support::cpp14::make_unique<arm_compute::CLActivationLayer>();
- activation->configure(in, out, act_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLActivationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Activation function: " << act_info.activation()
- << " a: " << act_info.a()
- << " b: " << act_info.b()
- << std::endl);
-
- return std::move(activation);
-}
-
-/* Arithmetic addition */
-REGISTER_SIMPLE_OPERATION(CLArithmeticAdditionOperation, OPENCL, OperationType::ArithmeticAddition)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(1)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in1 = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *in2 = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(1));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
-
- auto addition = arm_compute::support::cpp14::make_unique<arm_compute::CLArithmeticAddition>();
- addition->configure(in1, in2, out, ConvertPolicy::SATURATE);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLArithmeticAddition"
- << " Data Type: " << in1->info()->data_type()
- << " Input 1 shape: " << in1->info()->tensor_shape()
- << " Input 2 shape: " << in2->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(addition);
-}
-
-/* Batch Normalization Layer */
-REGISTER_SIMPLE_OPERATION(CLBatchNormalizationLayerOperation, OPENCL, OperationType::BatchNormalizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 5);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(1)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(2)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(3)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(4)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *mean = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(1));
- auto *var = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(2));
- auto *beta = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(3));
- auto *gamma = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(4));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- const auto epsilon = ctx.parameter<float>("epsilon");
- const auto act_info = ctx.parameter<ActivationLayerInfo>("act_info");
-
- // Create and configure function
- auto batch_norm = arm_compute::support::cpp14::make_unique<arm_compute::CLBatchNormalizationLayer>();
- batch_norm->configure(in, out, mean, var, beta, gamma, epsilon, act_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLBatchNormalizationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Mean shape: " << mean->info()->tensor_shape()
- << " Var shape: " << var->info()->tensor_shape()
- << " Beta shape: " << beta->info()->tensor_shape()
- << " Gamma shape: " << gamma->info()->tensor_shape()
- << " Epsilon: " << epsilon
- << " Activation function: " << act_info.activation()
- << " a: " << act_info.a()
- << " b: " << act_info.b()
- << std::endl);
-
- return std::move(batch_norm);
-}
-
-/* DepthConvertLayer Layer */
-REGISTER_SIMPLE_OPERATION(CLDepthConvertLayerOperation, OPENCL, OperationType::DepthConvertLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- const auto conv_policy = ctx.parameter<ConvertPolicy>("ConvertPolicy");
- const auto shift = ctx.parameter<uint32_t>("shift");
-
- // Create and configure function
- auto depthconvert = arm_compute::support::cpp14::make_unique<arm_compute::CLDepthConvertLayer>();
- depthconvert->configure(in, out, conv_policy, shift);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDepthConvertLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " shift: " << shift
- << std::endl);
-
- return std::move(depthconvert);
-}
-
-/* DepthwiseConvolutionLayer Layer */
-REGISTER_SIMPLE_OPERATION(CLDepthwiseConvolutionOperation, OPENCL, OperationType::DepthwiseConvolutionLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2 && ctx.num_inputs() != 3);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *weights = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(1));
- auto *biases = ctx.num_inputs() == 3 ? dynamic_cast<arm_compute::ICLTensor *>(ctx.input(2)) : nullptr;
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- const auto conv_info = ctx.parameter<PadStrideInfo>("ConvolutionInfo");
- const auto opt3x3 = ctx.parameter<bool>("Optimized3x3");
-
- // Create and configure function
- std::unique_ptr<arm_compute::IFunction> func;
- bool run_3x3_opt = opt3x3 && weights->info()->dimension(0) == 3;
- if(run_3x3_opt)
- {
- auto depwthwise_conv = arm_compute::support::cpp14::make_unique<arm_compute::CLDepthwiseConvolutionLayer3x3>();
- depwthwise_conv->configure(in, weights, biases, out, conv_info);
- func = std::move(depwthwise_conv);
- }
- else
- {
- auto depwthwise_conv = arm_compute::support::cpp14::make_unique<arm_compute::CLDepthwiseConvolutionLayer>();
- depwthwise_conv->configure(in, weights, biases, out, conv_info);
- func = std::move(depwthwise_conv);
- }
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDepthwiseConvolutionLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Weights shape: " << weights->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape());
- if(biases == nullptr)
- {
- ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: No biases provided" << std::endl);
- }
- else
- {
- ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: " << biases->info()->tensor_shape() << std::endl);
- }
-
- return func;
-}
-
-/* DeQuantizationLayer Layer */
-REGISTER_SIMPLE_OPERATION(CLDequantizationLayerOperation, OPENCL, OperationType::DequantizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 2);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(1)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- auto *min_max = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(1));
-
- // Create and configure function
- auto dequantization = arm_compute::support::cpp14::make_unique<arm_compute::CLDequantizationLayer>();
- dequantization->configure(in, out, min_max);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLDequantizationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Min max shape: " << min_max->info()->tensor_shape()
- << std::endl);
-
- return std::move(dequantization);
-}
-
-/* Flatten Layer */
-REGISTER_SIMPLE_OPERATION(CLFlattenLayerOperation, OPENCL, OperationType::FlattenLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
-
- // Create and configure function
- auto flatten = arm_compute::support::cpp14::make_unique<arm_compute::CLFlattenLayer>();
- flatten->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFlattenLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(flatten);
-}
-
-/* Floor Layer */
-REGISTER_SIMPLE_OPERATION(CLFloorLayerOperation, OPENCL, OperationType::FloorLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
-
- // Create and configure function
- auto floor = arm_compute::support::cpp14::make_unique<arm_compute::CLFloor>();
- floor->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFloorLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(floor);
-}
-
-/* Fully Connected Layer */
-REGISTER_SIMPLE_OPERATION(CLFullyConnectedLayer, OPENCL, OperationType::FullyConnectedLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 3);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(1)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(2)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *weights = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(1));
- auto *biases = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(2));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
-
- // Create and configure function
- auto fc = arm_compute::support::cpp14::make_unique<arm_compute::CLFullyConnectedLayer>();
- fc->configure(in, weights, biases, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFullyConnectedLayer"
- << " Data 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 std::move(fc);
-}
-
-/* L2 Normalize Layer */
-REGISTER_SIMPLE_OPERATION(CLL2NormalizeLayerOperation, OPENCL, OperationType::L2NormalizeLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- const auto axis = ctx.parameter<unsigned int>("axis");
- const auto epsilon = ctx.parameter<float>("epsilon");
-
- // Create and configure function
- auto l2_norm = arm_compute::support::cpp14::make_unique<arm_compute::CLL2NormalizeLayer>();
- l2_norm->configure(in, out, axis, epsilon);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLL2NormalizeLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Axis: " << axis
- << " Epsilon: " << epsilon
- << std::endl);
-
- return std::move(l2_norm);
-}
-
-/* Normalization Layer */
-REGISTER_SIMPLE_OPERATION(CLNormalizationLayerOperation, OPENCL, OperationType::NormalizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- const auto norm_info = ctx.parameter<NormalizationLayerInfo>("NormalizationLayerInfo");
-
- // Create and configure function
- auto norm = arm_compute::support::cpp14::make_unique<arm_compute::CLNormalizationLayer>();
- norm->configure(in, out, norm_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLNormalizationLayer"
- << " 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 std::move(norm);
-}
-
-/* Pooling Layer */
-REGISTER_SIMPLE_OPERATION(CLPoolingLayerOperation, OPENCL, OperationType::PoolingLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
- const auto pool_info = ctx.parameter<PoolingLayerInfo>("PoolingLayerInfo");
-
- // Create and configure function
- auto pool = arm_compute::support::cpp14::make_unique<arm_compute::CLPoolingLayer>();
- pool->configure(in, out, pool_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLPoolingLayer"
- << " 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 std::move(pool);
-}
-
-/* Quantization Layer */
-REGISTER_SIMPLE_OPERATION(CLQuantizationLayerOperation, OPENCL, OperationType::QuantizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
-
- // Create and configure function
- auto quantization = arm_compute::support::cpp14::make_unique<arm_compute::CLQuantizationLayer>();
- quantization->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLQuantizationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(quantization);
-}
-
-/* Reshape Layer */
-REGISTER_SIMPLE_OPERATION(CLReshapeLayerOperation, OPENCL, OperationType::ReshapeLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
-
- // Create and configure function
- auto reshape = arm_compute::support::cpp14::make_unique<arm_compute::CLReshapeLayer>();
- reshape->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLReshapeLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(reshape);
-}
-
-/* Softmax Layer */
-REGISTER_SIMPLE_OPERATION(CLSoftmaxLayerOperation, OPENCL, OperationType::SoftmaxLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ICLTensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ICLTensor *>(ctx.output(0));
-
- // Create and configure function
- auto smx = arm_compute::support::cpp14::make_unique<arm_compute::CLSoftmaxLayer>();
- smx->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLSoftmaxLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(smx);
-}
diff --git a/src/graph/operations/NESimpleOperations.cpp b/src/graph/operations/NESimpleOperations.cpp
deleted file mode 100644
index 4154b9a59c..0000000000
--- a/src/graph/operations/NESimpleOperations.cpp
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * Copyright (c) 2017-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.
- */
-#include "arm_compute/core/Error.h"
-#include "arm_compute/core/ITensor.h"
-#include "arm_compute/graph/IOperation.h"
-#include "arm_compute/graph/NodeContext.h"
-#include "arm_compute/graph/OperationRegistrar.h"
-#include "arm_compute/graph/Types.h"
-#include "arm_compute/runtime/NEON/NEFunctions.h"
-#include "support/ToolchainSupport.h"
-#include "utils/GraphTypePrinter.h"
-#include "utils/TypePrinter.h"
-
-#include <memory>
-
-using namespace arm_compute::graph;
-
-/* Activation Layer */
-REGISTER_SIMPLE_OPERATION(NEActivationLayerOperation, NEON, OperationType::ActivationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- const auto act_info = ctx.parameter<ActivationLayerInfo>("ActivationLayerInfo");
-
- // Create and configure function
- auto activation = arm_compute::support::cpp14::make_unique<arm_compute::NEActivationLayer>();
- activation->configure(in, out, act_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEActivationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Activation function: " << act_info.activation()
- << " a: " << act_info.a()
- << " b: " << act_info.b()
- << std::endl);
-
- return std::move(activation);
-}
-
-/* Arithmetic addition */
-REGISTER_SIMPLE_OPERATION(NEArithmeticAdditionOperation, NEON, OperationType::ArithmeticAddition)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(1)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in1 = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *in2 = dynamic_cast<arm_compute::ITensor *>(ctx.input(1));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
-
- auto addition = arm_compute::support::cpp14::make_unique<arm_compute::NEArithmeticAddition>();
- addition->configure(in1, in2, out, ConvertPolicy::SATURATE);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEArithmeticAddition"
- << " Data Type: " << in1->info()->data_type()
- << " Input 1 shape: " << in1->info()->tensor_shape()
- << " Input 2 shape: " << in2->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(addition);
-}
-
-/* Batch Normalization Layer */
-REGISTER_SIMPLE_OPERATION(NEBatchNormalizationLayerOperation, NEON, OperationType::BatchNormalizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 5);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(1)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(2)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(3)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(4)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *mean = dynamic_cast<arm_compute::ITensor *>(ctx.input(1));
- auto *var = dynamic_cast<arm_compute::ITensor *>(ctx.input(2));
- auto *beta = dynamic_cast<arm_compute::ITensor *>(ctx.input(3));
- auto *gamma = dynamic_cast<arm_compute::ITensor *>(ctx.input(4));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- const auto epsilon = ctx.parameter<float>("epsilon");
- const auto act_info = ctx.parameter<ActivationLayerInfo>("act_info");
-
- // Create and configure function
- auto batch_norm = arm_compute::support::cpp14::make_unique<arm_compute::NEBatchNormalizationLayer>();
- batch_norm->configure(in, out, mean, var, beta, gamma, epsilon, act_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEBatchNormalizationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Mean shape: " << mean->info()->tensor_shape()
- << " Var shape: " << var->info()->tensor_shape()
- << " Beta shape: " << beta->info()->tensor_shape()
- << " Gamma shape: " << gamma->info()->tensor_shape()
- << " Epsilon: " << epsilon
- << " Activation function: " << act_info.activation()
- << " a: " << act_info.a()
- << " b: " << act_info.b()
- << std::endl);
-
- return std::move(batch_norm);
-}
-
-/* DepthConvertLayer Layer */
-REGISTER_SIMPLE_OPERATION(NEDepthConvertLayerOperation, NEON, OperationType::DepthConvertLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- const auto conv_policy = ctx.parameter<ConvertPolicy>("ConvertPolicy");
- const auto shift = ctx.parameter<uint32_t>("shift");
-
- // Create and configure function
- auto depthconvert = arm_compute::support::cpp14::make_unique<arm_compute::NEDepthConvertLayer>();
- depthconvert->configure(in, out, conv_policy, shift);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDepthConvertLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " shift: " << shift
- << std::endl);
-
- return std::move(depthconvert);
-}
-
-/* DepthwiseConvolutionLayer Layer */
-REGISTER_SIMPLE_OPERATION(NEDepthwiseConvolutionOperation, NEON, OperationType::DepthwiseConvolutionLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 2 && ctx.num_inputs() != 3);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *weights = dynamic_cast<arm_compute::ITensor *>(ctx.input(1));
- auto *biases = ctx.num_inputs() == 3 ? dynamic_cast<arm_compute::ITensor *>(ctx.input(2)) : nullptr;
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- const auto conv_info = ctx.parameter<PadStrideInfo>("ConvolutionInfo");
- const auto opt3x3 = ctx.parameter<bool>("Optimized3x3");
-
- // Create and configure function
- std::unique_ptr<arm_compute::IFunction> func;
- bool run_3x3_opt = opt3x3 && weights->info()->dimension(0) == 3;
- if(run_3x3_opt)
- {
- auto depwthwise_conv = arm_compute::support::cpp14::make_unique<arm_compute::NEDepthwiseConvolutionLayer3x3>();
- depwthwise_conv->configure(in, weights, biases, out, conv_info);
- func = std::move(depwthwise_conv);
- }
- else
- {
- auto depwthwise_conv = arm_compute::support::cpp14::make_unique<arm_compute::NEDepthwiseConvolutionLayer>();
- depwthwise_conv->configure(in, weights, biases, out, conv_info);
- func = std::move(depwthwise_conv);
- }
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDepthwiseConvolutionLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Weights shape: " << weights->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape());
- if(biases == nullptr)
- {
- ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: No biases provided" << std::endl);
- }
- else
- {
- ARM_COMPUTE_LOG_GRAPH_INFO(" Biases shape: " << biases->info()->tensor_shape() << std::endl);
- }
-
- return func;
-}
-
-/* DeQuantizationLayer Layer */
-REGISTER_SIMPLE_OPERATION(NEDequantizationLayerOperation, NEON, OperationType::DequantizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 2);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(1)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- auto *min_max = dynamic_cast<arm_compute::ITensor *>(ctx.output(1));
-
- // Create and configure function
- auto dequantization = arm_compute::support::cpp14::make_unique<arm_compute::NEDequantizationLayer>();
- dequantization->configure(in, out, min_max);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEDequantizationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Min max shape: " << min_max->info()->tensor_shape()
- << std::endl);
-
- return std::move(dequantization);
-}
-
-/* Flatten Layer */
-REGISTER_SIMPLE_OPERATION(NEFlattenLayerOperation, NEON, OperationType::FlattenLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
-
- // Create and configure function
- auto flatten = arm_compute::support::cpp14::make_unique<arm_compute::NEFlattenLayer>();
- flatten->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFlattenLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(flatten);
-}
-
-/* Floor Layer */
-REGISTER_SIMPLE_OPERATION(NEFloorLayerOperation, NEON, OperationType::FloorLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
-
- // Create and configure function
- auto floor = arm_compute::support::cpp14::make_unique<arm_compute::NEFloor>();
- floor->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFloorLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(floor);
-}
-
-/* Fully Connected Layer */
-REGISTER_SIMPLE_OPERATION(NEFullyConnectedLayer, NEON, OperationType::FullyConnectedLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 3);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(1)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(2)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *weights = dynamic_cast<arm_compute::ITensor *>(ctx.input(1));
- auto *biases = dynamic_cast<arm_compute::ITensor *>(ctx.input(2));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
-
- // Create and configure function
- auto fc = arm_compute::support::cpp14::make_unique<arm_compute::NEFullyConnectedLayer>();
- fc->configure(in, weights, biases, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFullyConnectedLayer"
- << " Data 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 std::move(fc);
-}
-
-/* L2 Normalize Layer */
-REGISTER_SIMPLE_OPERATION(NEL2NormalizeLayerOperation, NEON, OperationType::L2NormalizeLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- const auto axis = ctx.parameter<unsigned int>("axis");
- const auto epsilon = ctx.parameter<float>("epsilon");
-
- // Create and configure function
- auto l2_norm = arm_compute::support::cpp14::make_unique<arm_compute::NEL2NormalizeLayer>();
- l2_norm->configure(in, out, axis, epsilon);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEL2NormalizeLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << " Axis: " << axis
- << " Epsilon: " << epsilon
- << std::endl);
-
- return std::move(l2_norm);
-}
-
-/* Normalization Layer */
-REGISTER_SIMPLE_OPERATION(NENormalizationLayerOperation, NEON, OperationType::NormalizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- const auto norm_info = ctx.parameter<NormalizationLayerInfo>("NormalizationLayerInfo");
-
- // Create and configure function
- auto norm = arm_compute::support::cpp14::make_unique<arm_compute::NENormalizationLayer>();
- norm->configure(in, out, norm_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NENormalizationLayer"
- << " 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 std::move(norm);
-}
-
-/* Pooling Layer */
-REGISTER_SIMPLE_OPERATION(NEPoolingLayerOperation, NEON, OperationType::PoolingLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
- const auto pool_info = ctx.parameter<PoolingLayerInfo>("PoolingLayerInfo");
-
- // Create and configure function
- auto pool = arm_compute::support::cpp14::make_unique<arm_compute::NEPoolingLayer>();
- pool->configure(in, out, pool_info);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEPoolingLayer"
- << " 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 std::move(pool);
-}
-
-/* Quantization Layer */
-REGISTER_SIMPLE_OPERATION(NEQuantizationLayerOperation, NEON, OperationType::QuantizationLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
-
- // Create and configure function
- auto quantization = arm_compute::support::cpp14::make_unique<arm_compute::NEQuantizationLayer>();
- quantization->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEQuantizationLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(quantization);
-}
-
-/* Reshape Layer */
-REGISTER_SIMPLE_OPERATION(NEReshapeLayerOperation, NEON, OperationType::ReshapeLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
-
- // Create and configure function
- auto reshape = arm_compute::support::cpp14::make_unique<arm_compute::NEReshapeLayer>();
- reshape->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEReshapeLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(reshape);
-}
-
-/* Softmax Layer */
-REGISTER_SIMPLE_OPERATION(NESoftmaxLayerOperation, NEON, OperationType::SoftmaxLayer)
-{
- ARM_COMPUTE_ERROR_ON(ctx.num_inputs() != 1);
- ARM_COMPUTE_ERROR_ON(ctx.num_outputs() != 1);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.input(0)) == nullptr);
- ARM_COMPUTE_ERROR_ON(dynamic_cast<arm_compute::ITensor *>(ctx.output(0)) == nullptr);
-
- // Extract IO and info
- auto *in = dynamic_cast<arm_compute::ITensor *>(ctx.input(0));
- auto *out = dynamic_cast<arm_compute::ITensor *>(ctx.output(0));
-
- // Create and configure function
- auto smx = arm_compute::support::cpp14::make_unique<arm_compute::NESoftmaxLayer>();
- smx->configure(in, out);
-
- // Log info
- ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NESoftmaxLayer"
- << " Data Type: " << in->info()->data_type()
- << " Input shape: " << in->info()->tensor_shape()
- << " Output shape: " << out->info()->tensor_shape()
- << std::endl);
-
- return std::move(smx);
-}
diff --git a/src/graph/printers/DotGraphPrinter.cpp b/src/graph/printers/DotGraphPrinter.cpp
new file mode 100644
index 0000000000..47b1bb56bf
--- /dev/null
+++ b/src/graph/printers/DotGraphPrinter.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+#include "arm_compute/graph/printers/DotGraphPrinter.h"
+
+#include "arm_compute/core/Error.h"
+#include "arm_compute/graph/Graph.h"
+#include "arm_compute/graph/Tensor.h"
+#include "arm_compute/graph/TypePrinter.h"
+#include "arm_compute/graph/nodes/Nodes.h"
+
+namespace arm_compute
+{
+namespace graph
+{
+void DotGraphVisitor::visit(ActivationLayerNode &n)
+{
+ std::stringstream ss;
+ ss << n.activation_info().activation();
+ _info = ss.str();
+}
+
+void DotGraphVisitor::visit(BatchNormalizationLayerNode &n)
+{
+ std::stringstream ss;
+ ss << (n.fused_activation().enabled() ? to_string(n.fused_activation().activation()) : "");
+ _info = ss.str();
+}
+
+void DotGraphVisitor::visit(ConvolutionLayerNode &n)
+{
+ std::stringstream ss;
+ ss << n.convolution_method();
+ _info = ss.str();
+}
+
+void DotGraphVisitor::visit(DepthConcatenateLayerNode &n)
+{
+ std::stringstream ss;
+ ss << "Enabled: " << n.is_enabled();
+ _info = ss.str();
+}
+
+void DotGraphVisitor::visit(DepthwiseConvolutionLayerNode &n)
+{
+ std::stringstream ss;
+ ss << n.depthwise_convolution_method();
+ _info = ss.str();
+}
+
+void DotGraphVisitor::visit(EltwiseLayerNode &n)
+{
+ std::stringstream ss;
+ ss << n.eltwise_operation();
+ _info = ss.str();
+}
+
+void DotGraphVisitor::visit(NormalizationLayerNode &n)
+{
+ std::stringstream ss;
+ ss << n.normalization_info().type();
+ _info = ss.str();
+}
+
+void DotGraphVisitor::visit(PoolingLayerNode &n)
+{
+ std::stringstream ss;
+ ss << n.pooling_info().pool_type();
+ ss << R"( \n )";
+ ss << n.pooling_info().pool_size();
+ ss << R"( \n )";
+ ss << n.pooling_info().pad_stride_info();
+ _info = ss.str();
+}
+
+void DotGraphVisitor::default_visit()
+{
+ _info.clear();
+}
+
+const std::string &DotGraphVisitor::info() const
+{
+ return _info;
+}
+
+void DotGraphPrinter::print(const Graph &g, std::ostream &os)
+{
+ // Print header
+ print_header(g, os);
+
+ // Print nodes
+ print_nodes(g, os);
+
+ // Print edges
+ print_edges(g, os);
+
+ // Print footer
+ print_footer(g, os);
+}
+
+void DotGraphPrinter::print_header(const Graph &g, std::ostream &os)
+{
+ // Print graph name
+ std::string graph_name = (g.name().empty()) ? "Graph" : g.name();
+ os << "digraph " << graph_name << "{\n";
+}
+
+void DotGraphPrinter::print_footer(const Graph &g, std::ostream &os)
+{
+ ARM_COMPUTE_UNUSED(g);
+ os << "}\n";
+}
+
+void DotGraphPrinter::print_nodes(const Graph &g, std::ostream &os)
+{
+ for(const auto &n : g.nodes())
+ {
+ if(n)
+ {
+ // Output node id
+ std::string node_id = std::string("n") + support::cpp11::to_string(n->id());
+ os << node_id << " ";
+
+ // Output label
+ n->accept(_dot_node_visitor);
+
+ std::string name = n->name().empty() ? node_id : n->name();
+ auto node_description = _dot_node_visitor.info();
+
+ os << R"([label = ")" << name << R"( \n )" << n->assigned_target() << R"( \n )" << node_description << R"("])";
+ os << ";\n";
+ }
+ }
+}
+
+void DotGraphPrinter::print_edges(const Graph &g, std::ostream &os)
+{
+ for(const auto &e : g.edges())
+ {
+ if(e)
+ {
+ std::string source_node_id = std::string("n") + support::cpp11::to_string(e->producer_id());
+ std::string sink_node_id = std::string("n") + support::cpp11::to_string(e->consumer_id());
+ os << source_node_id << " -> " << sink_node_id << " ";
+ const Tensor *t = e->tensor();
+ ARM_COMPUTE_ERROR_ON(t == nullptr);
+ os << R"([label = ")" << t->desc().shape << R"( \n )" << t->desc().data_type << R"("])";
+ os << ";\n";
+ }
+ }
+}
+} // namespace graph
+} // namespace arm_compute