Release 18.03
[platform/upstream/armnn.git] / src / armnn / Graph.hpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
4 //
5 #pragma once
6
7 #include "Layers.hpp"
8
9 #include <armnn/Types.hpp>
10 #include <armnn/TensorFwd.hpp>
11 #include <armnn/NetworkFwd.hpp>
12 #include <armnn/Exceptions.hpp>
13
14 #include <list>
15 #include <unordered_map>
16 #include <unordered_set>
17 #include <vector>
18
19 #include <boost/assert.hpp>
20 #include <boost/iterator/transform_iterator.hpp>
21
22 namespace armnn
23 {
24 class Graph
25 {
26 public:
27     template <typename CVLayerT>
28     static CVLayerT* PtrCast(Layer* const layer)
29     {
30         return boost::polymorphic_downcast<CVLayerT*>(layer);
31     }
32
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;
37
38     using ConstIteratorInputs = boost::transform_iterator<decltype(&PtrCast<const InputLayer>), Iterator>;
39     using ConstIteratorOutputs = boost::transform_iterator<decltype(&PtrCast<const OutputLayer>), Iterator>;
40
41     /// Wrapper class returned by Graph::GetInputLayers()
42     struct InputLayersAccessor
43     {
44         explicit InputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
45
46         ConstIteratorInputs begin() const
47         {
48             return { m_Graph.m_Layers.begin(), &PtrCast<const InputLayer> };
49         }
50
51         ConstIteratorInputs end() const
52         {
53             return { std::next(m_Graph.m_Layers.begin(), static_cast<IteratorDifference>(m_Graph.GetNumInputs())),
54                      &PtrCast<const InputLayer> };
55         }
56
57         const Graph& m_Graph;
58     };
59
60     /// Wrapper class returned by Graph::GetOutputLayers()
61     struct OutputLayersAccessor
62     {
63         explicit OutputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
64
65         ConstIteratorOutputs begin() const
66         {
67             return { std::prev(m_Graph.m_Layers.end(), static_cast<IteratorDifference>(m_Graph.GetNumOutputs())),
68                      &PtrCast<const OutputLayer> };
69         }
70
71         ConstIteratorOutputs end() const
72         {
73             return { m_Graph.m_Layers.end(), &PtrCast<const OutputLayer> };
74         }
75
76         const Graph& m_Graph;
77     };
78
79     Graph() : m_LayersInOrder(true) {}
80
81     Graph(const Graph& other);
82
83     Graph& operator=(const Graph& other) = delete;
84
85     ~Graph()
86     {
87         for (auto&& layer : m_Layers)
88         {
89             delete layer;
90         }
91     }
92
93     Status Print() const;
94
95     Status SerializeToDot(std::ostream& stream);
96
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);
100
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);
105
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);
109
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);
115
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(); }
120
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>}; }
125
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(); }
130
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;
134
135     size_t GetNumInputs() const { return m_InputIds.size(); }
136     size_t GetNumOutputs() const { return m_OutputIds.size(); }
137
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); }
141
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); }
145
146     size_t GetNumLayers() const { return m_Layers.size(); }
147
148     /// Allocate memory for all tensors under output tensor handers of each layer
149     Status AllocateDynamicBuffers();
150
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();
154
155     void InferTensorInfos();
156
157 private:
158     template <typename LayerT>
159     class LayerInGraphBase;
160
161     template <typename LayerT>
162     class LayerInGraph;
163
164     Iterator ForwardToEndOfInputs(Iterator it) const
165     {
166         while ((it != m_Layers.end()) && ((*it)->GetType() == LayerType::Input))
167         {
168             ++it;
169         }
170         return it;
171     }
172
173     Iterator RewindToBeginOfOutputs(Iterator it) const
174     {
175         while ((it != m_Layers.begin()) && ((*std::prev(it))->GetType() == LayerType::Output))
176         {
177             --it;
178         }
179         return it;
180     }
181
182     /// Get the position of a layer in the graph.
183     Iterator GetPosInGraph(Layer& layer);
184
185     std::unordered_set<LayerBindingId> m_InputIds;
186     std::unordered_set<LayerBindingId> m_OutputIds;
187     std::unordered_map<const Layer*, Iterator> m_PosInGraphMap;
188
189     /// Mutable to allow sorting on const object.
190     mutable LayersList m_Layers;
191     mutable bool m_LayersInOrder;
192 };
193
194 /// Common base class for layers in the graph
195 template <typename LayerT>
196 class Graph::LayerInGraphBase : public LayerT
197 {
198 protected:
199     template <typename... Args>
200     LayerInGraphBase(Graph& graph, Iterator insertBefore, Args&&... args)
201         : LayerT(std::forward<Args>(args)...), m_Graph(graph)
202     {
203         m_Graph.m_PosInGraphMap.emplace(this, m_Graph.m_Layers.emplace(insertBefore, this));
204     }
205     ~LayerInGraphBase()
206     {
207         const size_t numErased = m_Graph.m_PosInGraphMap.erase(this);
208         boost::ignore_unused(numErased);
209         BOOST_ASSERT(numErased == 1);
210     }
211
212     Graph& m_Graph;
213 };
214
215 /// Input/Output layers specialize this template
216 template <typename LayerT>
217 class Graph::LayerInGraph final : public LayerInGraphBase<LayerT>
218 {
219 public:
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)...)
226     {
227     }
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)...)
234     {
235     }
236 };
237
238 /// Inputs add/remove their binding id to m_InputIds in the graph.
239 template <>
240 class Graph::LayerInGraph<InputLayer> final : public LayerInGraphBase<InputLayer>
241 {
242 public:
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)...)
249     {
250         const bool isNewId = m_Graph.m_InputIds.emplace(GetBindingId()).second;
251         if (!isNewId)
252         {
253             throw InvalidArgumentException("A layer already exists with the specified id");
254         }
255     }
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)...)
260     {
261     }
262     ~LayerInGraph() override
263     {
264         const size_t numErased = m_Graph.m_InputIds.erase(GetBindingId());
265         boost::ignore_unused(numErased);
266         BOOST_ASSERT(numErased == 1);
267     }
268 };
269
270 /// Outputs add/remove their binding id to m_OutputIds in the graph.
271 template <>
272 class Graph::LayerInGraph<OutputLayer> final : public LayerInGraphBase<OutputLayer>
273 {
274 public:
275     template <typename... Args>
276     LayerInGraph(Graph& graph, Args&&... args)
277         : LayerInGraphBase<OutputLayer>(graph,
278                                         // Always add to the back of the outputs.
279                                         graph.end(),
280                                         std::forward<Args>(args)...)
281     {
282         const bool isNewId = m_Graph.m_OutputIds.emplace(GetBindingId()).second;
283         if (!isNewId)
284         {
285             throw InvalidArgumentException("A layer already exists with the specified id");
286         }
287     }
288     ~LayerInGraph() override
289     {
290         const size_t numErased = m_Graph.m_OutputIds.erase(GetBindingId());
291         boost::ignore_unused(numErased);
292         BOOST_ASSERT(numErased == 1);
293     }
294 };
295
296 inline Graph::Iterator Graph::GetPosInGraph(Layer& layer)
297 {
298     auto it = m_PosInGraphMap.find(&layer);
299     BOOST_ASSERT(it != m_PosInGraphMap.end());
300     return it->second;
301 }
302
303 template <typename LayerT, typename... Args>
304 inline LayerT* Graph::AddLayer(Args&&... args)
305 {
306     m_LayersInOrder = m_LayersInOrder &&
307         ((LayerEnumOf<LayerT>() == LayerType::Input) || (LayerEnumOf<LayerT>() == LayerType::Output));
308     return new LayerInGraph<LayerT>(*this, std::forward<Args>(args)...);
309 }
310
311 template <typename LayerT, typename... Args>
312 inline LayerT* Graph::InsertNewLayer(InputSlot& insertBefore, Args&&... args)
313 {
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);
321     return layer;
322 }
323
324 inline Graph::Iterator Graph::EraseLayer(Iterator pos)
325 {
326     delete *pos;
327     return m_Layers.erase(pos);
328 }
329
330 template <typename LayerT>
331 inline Graph::Iterator Graph::EraseLayer(LayerT*& layer)
332 {
333     BOOST_ASSERT(layer != nullptr);
334     Iterator next = EraseLayer(GetPosInGraph(*layer));
335     layer = nullptr;
336     return next;
337 }
338
339 } // namespace armnn