2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
9 #include <armnn/Types.hpp>
10 #include <armnn/TensorFwd.hpp>
11 #include <armnn/NetworkFwd.hpp>
12 #include <armnn/Exceptions.hpp>
15 #include <unordered_map>
16 #include <unordered_set>
19 #include <boost/assert.hpp>
20 #include <boost/iterator/transform_iterator.hpp>
27 template <typename CVLayerT>
28 static CVLayerT* PtrCast(Layer* const layer)
30 return boost::polymorphic_downcast<CVLayerT*>(layer);
33 using LayersList = std::list<Layer*>;
34 using Iterator = LayersList::const_iterator; // const so pointers in the list can't be modified externally
35 using ConstIterator = boost::transform_iterator<decltype(&PtrCast<const Layer>), Iterator>;
36 using IteratorDifference = Iterator::difference_type;
38 using ConstIteratorInputs = boost::transform_iterator<decltype(&PtrCast<const InputLayer>), Iterator>;
39 using ConstIteratorOutputs = boost::transform_iterator<decltype(&PtrCast<const OutputLayer>), Iterator>;
41 /// Wrapper class returned by Graph::GetInputLayers()
42 struct InputLayersAccessor
44 explicit InputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
46 ConstIteratorInputs begin() const
48 return { m_Graph.m_Layers.begin(), &PtrCast<const InputLayer> };
51 ConstIteratorInputs end() const
53 return { std::next(m_Graph.m_Layers.begin(), static_cast<IteratorDifference>(m_Graph.GetNumInputs())),
54 &PtrCast<const InputLayer> };
60 /// Wrapper class returned by Graph::GetOutputLayers()
61 struct OutputLayersAccessor
63 explicit OutputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
65 ConstIteratorOutputs begin() const
67 return { std::prev(m_Graph.m_Layers.end(), static_cast<IteratorDifference>(m_Graph.GetNumOutputs())),
68 &PtrCast<const OutputLayer> };
71 ConstIteratorOutputs end() const
73 return { m_Graph.m_Layers.end(), &PtrCast<const OutputLayer> };
79 Graph() : m_LayersInOrder(true) {}
81 Graph(const Graph& other);
83 Graph& operator=(const Graph& other) = delete;
87 for (auto&& layer : m_Layers)
95 Status SerializeToDot(std::ostream& stream);
97 /// Adds a new layer of type LaterType to the graph constructed with the arguments passed.
98 template <typename LayerT, typename... Args>
99 LayerT* AddLayer(Args&&... args);
101 /// Inserts a new layer between the output slot currently connected to insertBefore
102 /// and insertBefore itself.
103 template <typename LayerT, typename... Args>
104 LayerT* InsertNewLayer(InputSlot& insertBefore, Args&&... args);
106 /// Deletes the layer at the specified position and returns an iterator pointing
107 /// to the next element after the one being deleted.
108 Iterator EraseLayer(Iterator pos);
110 /// Deletes the layer and returns an iterator pointing to the next layer in the graph
111 /// (next in the list, after the one being deleted). Sets @a layer to nullptr on return.
112 /// Templated to support pointers to any layer type.
113 template <typename LayerT>
114 Iterator EraseLayer(LayerT*& layer);
116 /// Return iterator pointing to begin of list. Lowercase for range-based for loops.
117 Iterator begin() { return m_Layers.begin(); }
118 /// Return iterator pointing to end of list. Lowercase for range-based for loops.
119 Iterator end() { return m_Layers.end(); }
121 /// Return const iterator pointing to begin of list. Lowercase for range-based for loops.
122 ConstIterator begin() const { return {m_Layers.begin(), &PtrCast<const Layer>}; }
123 /// Return const iterator pointing to end of list. Lowercase for range-based for loops.
124 ConstIterator end() const { return {m_Layers.end(), &PtrCast<const Layer>}; }
126 /// Return const iterator pointing to begin of list. Lowercase for range-based for loops.
127 ConstIterator cbegin() const { return begin(); }
128 /// Return const iterator pointing to end of list. Lowercase for range-based for loops.
129 ConstIterator cend() const { return end(); }
131 /// Sort layers in topological order and return this.
132 Graph& TopologicalSort() { const_cast<const Graph*>(this)->TopologicalSort(); return *this; }
133 const Graph& TopologicalSort() const;
135 size_t GetNumInputs() const { return m_InputIds.size(); }
136 size_t GetNumOutputs() const { return m_OutputIds.size(); }
138 /// Returns a wrapper object with begin(), end() methods to iterate over the input layers
139 /// in a range-based for loop
140 InputLayersAccessor GetInputLayers() const { return InputLayersAccessor(*this); }
142 /// Returns a wrapper object with begin(), end() methods to iterate over the output layers
143 /// in a range-based for loop
144 OutputLayersAccessor GetOutputLayers() const { return OutputLayersAccessor(*this); }
146 size_t GetNumLayers() const { return m_Layers.size(); }
148 /// Allocate memory for all tensors under output tensor handers of each layer
149 Status AllocateDynamicBuffers();
151 /// Modifies the graph in-place, removing edges connecting layers using different compute devices,
152 /// and relinking them via an intermediary copy layers.
153 void AddCopyLayers();
155 void InferTensorInfos();
158 template <typename LayerT>
159 class LayerInGraphBase;
161 template <typename LayerT>
164 Iterator ForwardToEndOfInputs(Iterator it) const
166 while ((it != m_Layers.end()) && ((*it)->GetType() == LayerType::Input))
173 Iterator RewindToBeginOfOutputs(Iterator it) const
175 while ((it != m_Layers.begin()) && ((*std::prev(it))->GetType() == LayerType::Output))
182 /// Get the position of a layer in the graph.
183 Iterator GetPosInGraph(Layer& layer);
185 std::unordered_set<LayerBindingId> m_InputIds;
186 std::unordered_set<LayerBindingId> m_OutputIds;
187 std::unordered_map<const Layer*, Iterator> m_PosInGraphMap;
189 /// Mutable to allow sorting on const object.
190 mutable LayersList m_Layers;
191 mutable bool m_LayersInOrder;
194 /// Common base class for layers in the graph
195 template <typename LayerT>
196 class Graph::LayerInGraphBase : public LayerT
199 template <typename... Args>
200 LayerInGraphBase(Graph& graph, Iterator insertBefore, Args&&... args)
201 : LayerT(std::forward<Args>(args)...), m_Graph(graph)
203 m_Graph.m_PosInGraphMap.emplace(this, m_Graph.m_Layers.emplace(insertBefore, this));
207 const size_t numErased = m_Graph.m_PosInGraphMap.erase(this);
208 boost::ignore_unused(numErased);
209 BOOST_ASSERT(numErased == 1);
215 /// Input/Output layers specialize this template
216 template <typename LayerT>
217 class Graph::LayerInGraph final : public LayerInGraphBase<LayerT>
220 template <typename... Args>
221 LayerInGraph(Graph& graph, Args&&... args)
222 : LayerInGraphBase<LayerT>(graph,
223 // Insert at the back of the intermediate layers (before outputs).
224 std::prev(graph.end(), IteratorDifference(graph.GetNumOutputs())),
225 std::forward<Args>(args)...)
228 template <typename... Args>
229 LayerInGraph(Graph& graph, Iterator insertBefore, Args&&... args)
230 : LayerInGraphBase<LayerT>(graph,
231 // Make sure it's inserted after all inputs and before all outputs.
232 graph.ForwardToEndOfInputs(graph.RewindToBeginOfOutputs(insertBefore)),
233 std::forward<Args>(args)...)
238 /// Inputs add/remove their binding id to m_InputIds in the graph.
240 class Graph::LayerInGraph<InputLayer> final : public LayerInGraphBase<InputLayer>
243 template <typename... Args>
244 LayerInGraph(Graph& graph, Args&&... args)
245 : LayerInGraphBase<InputLayer>(graph,
246 // Always add to the back of the inputs.
247 std::next(graph.begin(), IteratorDifference(graph.GetNumInputs())),
248 std::forward<Args>(args)...)
250 const bool isNewId = m_Graph.m_InputIds.emplace(GetBindingId()).second;
253 throw InvalidArgumentException("A layer already exists with the specified id");
256 template <typename... Args>
257 LayerInGraph(Graph& graph, Iterator insertBefore, Args&&... args)
258 // Ignore insertBefore. Always add to the back of the inputs.
259 : LayerInGraph(graph, std::forward<Args>(args)...)
262 ~LayerInGraph() override
264 const size_t numErased = m_Graph.m_InputIds.erase(GetBindingId());
265 boost::ignore_unused(numErased);
266 BOOST_ASSERT(numErased == 1);
270 /// Outputs add/remove their binding id to m_OutputIds in the graph.
272 class Graph::LayerInGraph<OutputLayer> final : public LayerInGraphBase<OutputLayer>
275 template <typename... Args>
276 LayerInGraph(Graph& graph, Args&&... args)
277 : LayerInGraphBase<OutputLayer>(graph,
278 // Always add to the back of the outputs.
280 std::forward<Args>(args)...)
282 const bool isNewId = m_Graph.m_OutputIds.emplace(GetBindingId()).second;
285 throw InvalidArgumentException("A layer already exists with the specified id");
288 ~LayerInGraph() override
290 const size_t numErased = m_Graph.m_OutputIds.erase(GetBindingId());
291 boost::ignore_unused(numErased);
292 BOOST_ASSERT(numErased == 1);
296 inline Graph::Iterator Graph::GetPosInGraph(Layer& layer)
298 auto it = m_PosInGraphMap.find(&layer);
299 BOOST_ASSERT(it != m_PosInGraphMap.end());
303 template <typename LayerT, typename... Args>
304 inline LayerT* Graph::AddLayer(Args&&... args)
306 m_LayersInOrder = m_LayersInOrder &&
307 ((LayerEnumOf<LayerT>() == LayerType::Input) || (LayerEnumOf<LayerT>() == LayerType::Output));
308 return new LayerInGraph<LayerT>(*this, std::forward<Args>(args)...);
311 template <typename LayerT, typename... Args>
312 inline LayerT* Graph::InsertNewLayer(InputSlot& insertBefore, Args&&... args)
314 // Insert after the parent if any, or before the child otherwise, so topological order is kept.
315 OutputSlot* parentOut = insertBefore.GetConnectedOutputSlot();
316 const Iterator pos = (parentOut != nullptr)
317 ? std::next(GetPosInGraph(parentOut->GetOwningLayer()))
318 : GetPosInGraph(insertBefore.GetOwningLayer());
319 LayerT* const layer = new LayerInGraph<LayerT>(*this, pos, std::forward<Args>(args)...);
320 insertBefore.Insert(*layer);
324 inline Graph::Iterator Graph::EraseLayer(Iterator pos)
327 return m_Layers.erase(pos);
330 template <typename LayerT>
331 inline Graph::Iterator Graph::EraseLayer(LayerT*& layer)
333 BOOST_ASSERT(layer != nullptr);
334 Iterator next = EraseLayer(GetPosInGraph(*layer));