// // Copyright © 2017 Arm Ltd. All rights reserved. // See LICENSE file in the project root for full license information. // #pragma once #include "Layers.hpp" #include #include #include #include #include #include #include #include #include #include namespace armnn { class Graph { public: template static CVLayerT* PtrCast(Layer* const layer) { return boost::polymorphic_downcast(layer); } using LayersList = std::list; using Iterator = LayersList::const_iterator; // const so pointers in the list can't be modified externally using ConstIterator = boost::transform_iterator), Iterator>; using IteratorDifference = Iterator::difference_type; using ConstIteratorInputs = boost::transform_iterator), Iterator>; using ConstIteratorOutputs = boost::transform_iterator), Iterator>; /// Wrapper class returned by Graph::GetInputLayers() struct InputLayersAccessor { explicit InputLayersAccessor(const Graph& graph) : m_Graph(graph) {} ConstIteratorInputs begin() const { return { m_Graph.m_Layers.begin(), &PtrCast }; } ConstIteratorInputs end() const { return { std::next(m_Graph.m_Layers.begin(), static_cast(m_Graph.GetNumInputs())), &PtrCast }; } const Graph& m_Graph; }; /// Wrapper class returned by Graph::GetOutputLayers() struct OutputLayersAccessor { explicit OutputLayersAccessor(const Graph& graph) : m_Graph(graph) {} ConstIteratorOutputs begin() const { return { std::prev(m_Graph.m_Layers.end(), static_cast(m_Graph.GetNumOutputs())), &PtrCast }; } ConstIteratorOutputs end() const { return { m_Graph.m_Layers.end(), &PtrCast }; } const Graph& m_Graph; }; Graph() : m_LayersInOrder(true) {} Graph(const Graph& other); Graph& operator=(const Graph& other) = delete; ~Graph() { for (auto&& layer : m_Layers) { delete layer; } } Status Print() const; /// Adds a new layer of type LaterType to the graph constructed with the arguments passed. template LayerT* AddLayer(Args&&... args); /// Inserts a new layer between the output slot currently connected to insertBefore /// and insertBefore itself. template LayerT* InsertNewLayer(InputSlot& insertBefore, Args&&... args); /// Deletes the layer at the specified position and returns an iterator pointing /// to the next element after the one being deleted. Iterator EraseLayer(Iterator pos); /// Deletes the layer and returns an iterator pointing to the next layer in the graph /// (next in the list, after the one being deleted). Sets @a layer to nullptr on return. /// Templated to support pointers to any layer type. template Iterator EraseLayer(LayerT*& layer); /// Return iterator pointing to begin of list. Lowercase for range-based for loops. Iterator begin() { return m_Layers.begin(); } /// Return iterator pointing to end of list. Lowercase for range-based for loops. Iterator end() { return m_Layers.end(); } /// Return const iterator pointing to begin of list. Lowercase for range-based for loops. ConstIterator begin() const { return {m_Layers.begin(), &PtrCast}; } /// Return const iterator pointing to end of list. Lowercase for range-based for loops. ConstIterator end() const { return {m_Layers.end(), &PtrCast}; } /// Sort layers in topological order and return this. Graph& TopologicalSort() { const_cast(this)->TopologicalSort(); return *this; } const Graph& TopologicalSort() const; size_t GetNumInputs() const { return m_InputIds.size(); } size_t GetNumOutputs() const { return m_OutputIds.size(); } /// Returns a wrapper object with begin(), end() methods to iterate over the input layers /// in a range-based for loop InputLayersAccessor GetInputLayers() const { return InputLayersAccessor(*this); } /// Returns a wrapper object with begin(), end() methods to iterate over the output layers /// in a range-based for loop OutputLayersAccessor GetOutputLayers() const { return OutputLayersAccessor(*this); } size_t GetNumLayers() const { return m_Layers.size(); } /// Allocate memory for all tensors under output tensor handers of each layer Status AllocateDynamicBuffers(); /// Modifies the graph in-place, removing edges connecting layers using different compute devices, /// and relinking them via an intermediary copy layers. void AddCopyLayers(); void InferTensorInfos(); private: template class LayerInGraphBase; template class LayerInGraph; /// Get the position of a layer in the graph. Iterator GetPosInGraph(Layer& layer); /// Adds a new layer of type LaterType to the graph constructed with the arguments passed. template LayerInGraph* AddLayerImpl(Iterator insertBefore, Args&&... args); std::unordered_set m_InputIds; std::unordered_set m_OutputIds; std::unordered_map m_PosInGraphMap; /// Mutable to allow sorting on const object. mutable LayersList m_Layers; mutable bool m_LayersInOrder; }; /// Common base class for layers in the graph template class Graph::LayerInGraphBase : public LayerT { protected: template LayerInGraphBase(Graph& graph, Iterator insertBefore, Args&&... args) : LayerT(std::forward(args)...), m_Graph(graph) { m_Graph.m_PosInGraphMap.emplace(this, m_Graph.m_Layers.emplace(insertBefore, this)); } ~LayerInGraphBase() { const size_t numErased = m_Graph.m_PosInGraphMap.erase(this); boost::ignore_unused(numErased); BOOST_ASSERT(numErased == 1); } Graph& m_Graph; }; /// Input/Output layers specialize this template template class Graph::LayerInGraph final : public LayerInGraphBase { public: template LayerInGraph(Graph& graph, Iterator insertBefore, Args&&... args) : LayerInGraphBase(graph, insertBefore, std::forward(args)...) { } }; /// Inputs add/remove their binding id to m_InputIds in the graph. template <> class Graph::LayerInGraph final : public LayerInGraphBase { public: template LayerInGraph(Graph& graph, Iterator insertBefore, Args&&... args) : LayerInGraphBase(graph, insertBefore, std::forward(args)...) { const bool isNewId = m_Graph.m_InputIds.emplace(GetBindingId()).second; if (!isNewId) { throw InvalidArgumentException("A layer already exists with the specified id"); } } ~LayerInGraph() override { const size_t numErased = m_Graph.m_InputIds.erase(GetBindingId()); boost::ignore_unused(numErased); BOOST_ASSERT(numErased == 1); } }; /// Outputs add/remove their binding id to m_OutputIds in the graph. template <> class Graph::LayerInGraph final : public LayerInGraphBase { public: template LayerInGraph(Graph& graph, Iterator insertBefore, Args&&... args) : LayerInGraphBase(graph, insertBefore, std::forward(args)...) { const bool isNewId = m_Graph.m_OutputIds.emplace(GetBindingId()).second; if (!isNewId) { throw InvalidArgumentException("A layer already exists with the specified id"); } } ~LayerInGraph() override { const size_t numErased = m_Graph.m_OutputIds.erase(GetBindingId()); boost::ignore_unused(numErased); BOOST_ASSERT(numErased == 1); } }; inline Graph::Iterator Graph::GetPosInGraph(Layer& layer) { auto it = m_PosInGraphMap.find(&layer); BOOST_ASSERT(it != m_PosInGraphMap.end()); return it->second; } template inline Graph::LayerInGraph* Graph::AddLayerImpl(Iterator insertBefore, Args&&... args) { return new LayerInGraph(*this, insertBefore, std::forward(args)...); } /// Inputs are inserted at the front of the list, to keep the order correct if the list is sorted. /// Outputs are inserted at the back of the list, to keep the order correct if the list is sorted. /// Other layers are inserted before existing outputs, so the latter remain at the back of the list. template inline LayerT* Graph::AddLayer(Args&&... args) { switch (LayerEnumOf()) { case LayerType::Input: { return AddLayerImpl(begin(), std::forward(args)...); } case LayerType::Output: { return AddLayerImpl(end(), std::forward(args)...); } default: { m_LayersInOrder = false; const auto pos = std::prev(end(), IteratorDifference(GetNumOutputs())); return AddLayerImpl(pos, std::forward(args)...); } } } template inline LayerT* Graph::InsertNewLayer(InputSlot& insertBefore, Args&&... args) { // Insert before the child layer so topological order is kept. const Iterator pos = GetPosInGraph(insertBefore.GetOwningLayer()); LayerT* const layer = AddLayerImpl(pos, std::forward(args)...); insertBefore.Insert(*layer); return layer; } inline Graph::Iterator Graph::EraseLayer(Iterator pos) { delete *pos; return m_Layers.erase(pos); } template inline Graph::Iterator Graph::EraseLayer(LayerT*& layer) { BOOST_ASSERT(layer != nullptr); Iterator next = EraseLayer(GetPosInGraph(*layer)); layer = nullptr; return next; } } // namespace armnn