ArmNN
 20.02
Graph.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include "LayersFwd.hpp"
8 #include "IGraphObservable.hpp"
9 
10 #include <armnn/Types.hpp>
11 #include <armnn/TensorFwd.hpp>
12 #include <armnn/NetworkFwd.hpp>
13 #include <armnn/Exceptions.hpp>
14 
15 #include <list>
16 #include <map>
17 #include <unordered_map>
18 #include <unordered_set>
19 #include <vector>
20 
21 #include <boost/assert.hpp>
22 #include <boost/iterator/transform_iterator.hpp>
23 
24 namespace armnn
25 {
26 
27 class SubgraphView;
28 
29 class Graph
30 {
31 public:
32  template <typename LayerType>
33  static LayerType* PtrCast(Layer* const layer)
34  {
35  return boost::polymorphic_downcast<LayerType*>(layer);
36  }
37 
38  template <typename Func>
39  void ForEachLayer(Func func) const
40  {
41  for (auto it = m_Layers.begin(); it != m_Layers.end(); )
42  {
43  auto next = std::next(it);
44  func(*it);
45  it = next;
46  }
47  }
48 
49  using LayerList = std::list<Layer*>;
50  using Iterator = LayerList::const_iterator; // Const so pointers in the list can't be modified externally.
51  using IteratorDifference = Iterator::difference_type;
52 
53  using ConstIterator = boost::transform_iterator<decltype(&PtrCast<const Layer>), Iterator>;
54  using ConstIteratorInputs = boost::transform_iterator<decltype(&PtrCast<const InputLayer>), Iterator>;
55  using ConstIteratorOutputs = boost::transform_iterator<decltype(&PtrCast<const OutputLayer>), Iterator>;
56 
57  /// Wrapper class returned by Graph::GetInputLayers()
59  {
60  explicit InputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
61 
63  {
64  return { m_Graph.m_Layers.begin(), &(PtrCast<const InputLayer>) };
65  }
66 
68  {
69  return { std::next(m_Graph.m_Layers.begin(), static_cast<IteratorDifference>(m_Graph.GetNumInputs())),
70  &(PtrCast<const InputLayer>) };
71  }
72 
73  const Graph& m_Graph;
74  };
75 
76  /// Wrapper class returned by Graph::GetOutputLayers()
78  {
79  explicit OutputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
80 
82  {
83  return { std::prev(m_Graph.m_Layers.end(), static_cast<IteratorDifference>(m_Graph.GetNumOutputs())),
84  &(PtrCast<const OutputLayer>) };
85  }
86 
88  {
89  return { m_Graph.m_Layers.end(), &(PtrCast<const OutputLayer>) };
90  }
91 
92  const Graph& m_Graph;
93  };
94 
95  Graph() : m_LayersInOrder(true) {}
96 
97  Graph(const Graph& other);
98 
99  Graph& operator=(const Graph& other) = delete;
100 
101  Graph(Graph&& other)
102  {
103  *this = std::move(other);
104  }
105 
107  {
108  m_InputIds = std::move(other.m_InputIds);
109  m_OutputIds = std::move(other.m_OutputIds);
110  m_LayersInOrder = std::move(other.m_LayersInOrder);
111  m_Views = std::move(other.m_Views);
112 
113  other.ForEachLayer([this](Layer* otherLayer)
114  {
115  otherLayer->Reparent(*this, m_Layers.end());
116  });
117 
118  BOOST_ASSERT(other.m_PosInGraphMap.empty());
119  BOOST_ASSERT(other.m_Layers.empty());
120 
121  return *this;
122  }
123 
125  {
126  ForEachLayer([](Layer* layer)
127  {
128  delete layer;
129  });
130  }
131 
132  Status Print() const;
133 
134  Status SerializeToDot(std::ostream& stream);
135 
136  /// Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
137  template <typename LayerT, typename... Args>
138  LayerT* AddLayer(Args&&... args);
139 
140  /// Inserts a new layer between the output slot currently connected to insertBefore
141  /// and insertBefore itself.
142  template <typename LayerT, typename... Args>
143  LayerT* InsertNewLayer(InputSlot& insertBefore, Args&&... args);
144 
145  /// Inserts a new layer between insertAfter and the input slot(s) currently connected to it
146  template <typename LayerT, typename... Args>
147  LayerT* InsertNewLayer(OutputSlot& insertAfter, Args&&... args);
148 
149  /// Deletes the layer at the specified position.
150  void EraseLayer(Iterator pos);
151 
152  /// Deletes the layer. Sets @a layer to nullptr on return.
153  /// Templated to support pointers to any layer type.
154  template <typename LayerT>
155  void EraseLayer(LayerT*& layer);
156 
157  /// Returns iterator pointing to the beginning of the list. Lowercase for range-based for loops.
158  Iterator begin() { return m_Layers.begin(); }
159  /// Returns iterator pointing to the end of the list. Lowercase for range-based for loops.
160  Iterator end() { return m_Layers.end(); }
161 
162  /// Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops.
163  ConstIterator begin() const { return {m_Layers.begin(), &(PtrCast<const Layer>)}; }
164  /// Returns const iterator pointing to the end of the list. Lowercase for range-based for loops.
165  ConstIterator end() const { return {m_Layers.end(), &(PtrCast<const Layer>)}; }
166 
167  /// Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops.
168  ConstIterator cbegin() const { return begin(); }
169  /// Returns const iterator pointing to the end of the list. Lowercase for range-based for loops.
170  ConstIterator cend() const { return end(); }
171 
172  /// Sorts layers in topological order and return this.
173  Graph& TopologicalSort() { const_cast<const Graph*>(this)->TopologicalSort(); return *this; }
174  const Graph& TopologicalSort() const;
175 
176  size_t GetNumInputs() const { return m_InputIds.size(); }
177  size_t GetNumOutputs() const { return m_OutputIds.size(); }
178 
179  /// Returns a wrapper object with begin(), end() methods to iterate over the input layers
180  /// in a range-based for loop.
182 
183  /// Returns a wrapper object with begin(), end() methods to iterate over the output layers
184  /// in a range-based for loop.
186 
187  size_t GetNumLayers() const { return m_Layers.size(); }
188 
189  /// Allocates memory for all tensors under output tensor handers of each layer.
191 
192  /// Modifies the graph in-place, removing edges connecting layers using different compute devices,
193  /// and relinking them via an intermediary copy layers.
194  void AddCompatibilityLayers(std::map<BackendId, std::unique_ptr<class IBackendInternal>>& backends,
195  TensorHandleFactoryRegistry& registry);
196 
197  /// Substitutes the given sub-graph with either a new layer or a new sub-graph.
198  /// In either case, the given layer or all the layers in the given sub-graph must belong to this graph.
199  void SubstituteSubgraph(SubgraphView& subgraph, IConnectableLayer* substituteLayer);
200  void SubstituteSubgraph(SubgraphView& subgraph, const SubgraphView& substituteSubgraph);
201 
202  void InferTensorInfos();
203 
204  void AttachObservable(IGraphObservable* const observable, GraphEvent notifyOnEvent) {
205  m_Views[notifyOnEvent].emplace_back(observable);
206  }
207 
208  void DetachObservable(IGraphObservable* const observable, GraphEvent notifyOnEvent) {
209  m_Views[notifyOnEvent].remove(observable);
210  }
211 
212  /// Gets the position of a layer in the graph.
213  Iterator GetPosInGraph(Layer& layer);
214 
215 private:
216  template <typename LayerT>
217  class LayerInGraphBase;
218 
219  template <typename LayerT>
220  class LayerInGraph;
221 
222  Iterator ForwardToEndOfInputs(Iterator it) const
223  {
224  while ((it != m_Layers.end()) && ((*it)->GetType() == LayerType::Input))
225  {
226  ++it;
227  }
228  return it;
229  }
230 
231  Iterator RewindToBeginOfOutputs(Iterator it) const
232  {
233  while ((it != m_Layers.begin()) && ((*std::prev(it))->GetType() == LayerType::Output))
234  {
235  --it;
236  }
237  return it;
238  }
239 
240  void NotifyObservables(GraphEvent event, Layer* graphState)
241  {
242  // Iterate over all observables observing this event
243  for (auto& observable : m_Views[event])
244  {
245  observable->Update(graphState);
246  }
247  }
248 
249  std::unordered_set<LayerBindingId> m_InputIds;
250  std::unordered_set<LayerBindingId> m_OutputIds;
251  std::unordered_map<const Layer*, Iterator> m_PosInGraphMap;
252 
253  void ReplaceSubgraphConnections(const SubgraphView& subgraph, IConnectableLayer* substituteLayer);
254  void ReplaceSubgraphConnections(const SubgraphView& subgraph, const SubgraphView& substituteSubgraph);
255  void EraseSubgraphLayers(SubgraphView &subgraph);
256 
257  /// Mutable to allow sorting on const object.
258  mutable LayerList m_Layers;
259  mutable bool m_LayersInOrder;
260 
261  std::map<const GraphEvent, std::list<IGraphObservable*>> m_Views;
262 };
263 
264 /// Common base class for layers in the graph.
265 template <typename LayerT>
266 class Graph::LayerInGraphBase : public LayerT
267 {
268 protected:
269  template <typename... Args>
270  LayerInGraphBase(Graph& graph, Iterator insertBefore, Args&&... args)
271  : LayerT(std::forward<Args>(args)...), m_Graph(&graph)
272  {
273  Insert(*m_Graph, insertBefore);
274  }
275  ~LayerInGraphBase()
276  {
277  Remove(*m_Graph);
278  }
279 
280  void Reparent(Graph& destGraph, Iterator insertBefore) override
281  {
282  Insert(destGraph, insertBefore);
283  Remove(*m_Graph);
284 
285  m_Graph = &destGraph;
286  }
287 
288 private:
289  void Insert(Graph& graph, Iterator insertBefore)
290  {
291  graph.m_PosInGraphMap.emplace(this, graph.m_Layers.emplace(insertBefore, this));
292  }
293 
294  void Remove(Graph& graph)
295  {
296  auto layerIt = graph.GetPosInGraph(*this);
297  graph.m_Layers.erase(layerIt);
298 
299  const size_t numErased = graph.m_PosInGraphMap.erase(this);
300  IgnoreUnused(numErased);
301  BOOST_ASSERT(numErased == 1);
302  }
303 
304 protected:
305  Graph* m_Graph;
306 };
307 
308 /// Input/Output layers specialize this template.
309 template <typename LayerT>
310 class Graph::LayerInGraph final : public LayerInGraphBase<LayerT>
311 {
312 public:
313  template <typename... Args>
314  LayerInGraph(Graph& graph, Args&&... args)
315  : LayerInGraphBase<LayerT>(graph,
316  // Insert at the back of the intermediate layers (before outputs).
317  std::prev(graph.end(), IteratorDifference(graph.GetNumOutputs())),
318  std::forward<Args>(args)...)
319  {
320  }
321  template <typename... Args>
322  LayerInGraph(Graph& graph, Iterator insertBefore, Args&&... args)
323  : LayerInGraphBase<LayerT>(graph,
324  // Make sure it's inserted after all inputs and before all outputs.
325  graph.ForwardToEndOfInputs(graph.RewindToBeginOfOutputs(insertBefore)),
326  std::forward<Args>(args)...)
327  {
328  }
329 };
330 
331 /// Inputs add/remove their binding id to m_InputIds in the graph.
332 template <>
333 class Graph::LayerInGraph<InputLayer> final : public LayerInGraphBase<InputLayer>
334 {
335 public:
336  template <typename... Args>
337  LayerInGraph(Graph& graph, Args&&... args)
338  : LayerInGraphBase<InputLayer>(graph,
339  // Always add to the back of the inputs.
340  std::next(graph.begin(), IteratorDifference(graph.GetNumInputs())),
341  std::forward<Args>(args)...)
342  {
343  const bool isNewId = m_Graph->m_InputIds.emplace(GetBindingId()).second;
344  if (!isNewId)
345  {
346  throw InvalidArgumentException("A layer already exists with the specified id");
347  }
348  }
349  template <typename... Args>
350  LayerInGraph(Graph& graph, Iterator, Args&&... args)
351  // Ignore Iterator argument. Always add to the back of the inputs.
352  : LayerInGraph(graph, std::forward<Args>(args)...)
353  {
354  }
355  ~LayerInGraph() override
356  {
357  const size_t numErased = m_Graph->m_InputIds.erase(GetBindingId());
358  IgnoreUnused(numErased);
359  BOOST_ASSERT(numErased == 1);
360  }
361 };
362 
363 /// Outputs add/remove their binding id to m_OutputIds in the graph.
364 template <>
365 class Graph::LayerInGraph<OutputLayer> final : public LayerInGraphBase<OutputLayer>
366 {
367 public:
368  template <typename... Args>
369  LayerInGraph(Graph& graph, Args&&... args)
370  : LayerInGraphBase<OutputLayer>(graph,
371  // Always add to the back of the outputs.
372  graph.end(),
373  std::forward<Args>(args)...)
374  {
375  const bool isNewId = m_Graph->m_OutputIds.emplace(GetBindingId()).second;
376  if (!isNewId)
377  {
378  throw InvalidArgumentException("A layer already exists with the specified id");
379  }
380  }
381  ~LayerInGraph() override
382  {
383  const size_t numErased = m_Graph->m_OutputIds.erase(GetBindingId());
384  IgnoreUnused(numErased);
385  BOOST_ASSERT(numErased == 1);
386  }
387 };
388 
390 {
391  auto it = m_PosInGraphMap.find(&layer);
392  BOOST_ASSERT(it != m_PosInGraphMap.end());
393  return it->second;
394 }
395 
396 template <typename LayerT, typename... Args>
397 inline LayerT* Graph::AddLayer(Args&&... args)
398 {
399  m_LayersInOrder = m_LayersInOrder &&
400  ((LayerEnumOf<LayerT>() == LayerType::Input) || (LayerEnumOf<LayerT>() == LayerType::Output));
401  LayerT* const layer = new LayerInGraph<LayerT>(*this, std::forward<Args>(args)...);
402 
403  NotifyObservables(GraphEvent::LayerAdded, layer);
404 
405  return layer;
406 }
407 
408 template <typename LayerT, typename... Args>
409 inline LayerT* Graph::InsertNewLayer(InputSlot& insertBefore, Args&&... args)
410 {
411  // Insert after the parent if any, or before the child otherwise, so the topological order is kept.
412  OutputSlot* parentOut = insertBefore.GetConnectedOutputSlot();
413  const Iterator pos = (parentOut != nullptr)
414  ? std::next(GetPosInGraph(parentOut->GetOwningLayer()))
415  : GetPosInGraph(insertBefore.GetOwningLayer());
416  LayerT* const layer = new LayerInGraph<LayerT>(*this, pos, std::forward<Args>(args)...);
417  insertBefore.Insert(*layer);
418 
419  NotifyObservables(GraphEvent::LayerAdded, layer);
420 
421  return layer;
422 }
423 
424 template <typename LayerT, typename... Args>
425 inline LayerT* Graph::InsertNewLayer(OutputSlot& insertAfter, Args&&... args)
426 {
427  Layer& owningLayer = insertAfter.GetOwningLayer();
428 
429  const Iterator pos = std::next(GetPosInGraph(owningLayer));
430  LayerT* const layer = new LayerInGraph<LayerT>(*this, pos, std::forward<Args>(args)...);
431 
432  BOOST_ASSERT(layer->GetNumInputSlots() == 1);
433 
434  insertAfter.MoveAllConnections(layer->GetOutputSlot());
435  insertAfter.Connect(layer->GetInputSlot(0));
436 
437  NotifyObservables(GraphEvent::LayerAdded, layer);
438 
439  return layer;
440 }
441 
442 inline void Graph::EraseLayer(Iterator pos)
443 {
444  NotifyObservables(GraphEvent::LayerErased, *pos);
445 
446  delete *pos;
447 }
448 
449 template <typename LayerT>
450 inline void Graph::EraseLayer(LayerT*& layer)
451 {
452  BOOST_ASSERT(layer != nullptr);
453  EraseLayer(GetPosInGraph(*layer));
454  layer = nullptr;
455 }
456 
457 } // namespace armnn
Graph & operator=(const Graph &other)=delete
Iterator begin()
Returns iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:158
void Insert(Layer &layer)
Definition: Layer.cpp:20
ConstIteratorInputs begin() const
Definition: Graph.hpp:62
boost::transform_iterator< decltype(&PtrCast< const OutputLayer >), Iterator > ConstIteratorOutputs
Definition: Graph.hpp:55
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:61
Status SerializeToDot(std::ostream &stream)
Definition: Graph.cpp:81
void AttachObservable(IGraphObservable *const observable, GraphEvent notifyOnEvent)
Definition: Graph.hpp:204
virtual void Reparent(Graph &dest, std::list< Layer *>::const_iterator iterator)=0
LayerT * AddLayer(Args &&... args)
Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
Definition: Graph.hpp:397
LayerInGraph(Graph &graph, Args &&... args)
Definition: Graph.hpp:369
boost::transform_iterator< decltype(&PtrCast< const Layer >), Iterator > ConstIterator
Definition: Graph.hpp:53
ConstIterator cbegin() const
Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:168
Layer & GetOwningLayer() const
Definition: Layer.hpp:115
int Connect(InputSlot &destination)
Definition: Layer.cpp:79
LayerInGraph(Graph &graph, Iterator, Args &&... args)
Definition: Graph.hpp:350
void EraseLayer(Iterator pos)
Deletes the layer at the specified position.
Definition: Graph.hpp:442
static LayerType * PtrCast(Layer *const layer)
Definition: Graph.hpp:33
size_t GetNumOutputs() const
Definition: Graph.hpp:177
Copyright (c) 2020 ARM Limited.
void IgnoreUnused(Ts &&...)
DataLayout::NCHW DataLayout::NCHW DataLayout::NHWC DataLayout::NHWC true
LayerList::const_iterator Iterator
Definition: Graph.hpp:50
OutputLayersAccessor(const Graph &graph)
Definition: Graph.hpp:79
ConstIteratorOutputs begin() const
Definition: Graph.hpp:81
The SubgraphView class represents a subgraph of a Graph.
Iterator GetPosInGraph(Layer &layer)
Gets the position of a layer in the graph.
Definition: Graph.hpp:389
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: OutputLayer.hpp:13
void ForEachLayer(Func func) const
Definition: Graph.hpp:39
ConstIterator end() const
Returns const iterator pointing to the end of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:165
bool Remove(const char *path)
Definition: Filesystem.cpp:47
Wrapper class returned by Graph::GetInputLayers()
Definition: Graph.hpp:58
Status
enumeration
Definition: Types.hpp:26
const OutputSlot * GetConnectedOutputSlot() const
Definition: Layer.hpp:55
Iterator::difference_type IteratorDifference
Definition: Graph.hpp:51
Layer & GetOwningLayer() const
Definition: Layer.hpp:52
Wrapper class returned by Graph::GetOutputLayers()
Definition: Graph.hpp:77
InputLayersAccessor(const Graph &graph)
Definition: Graph.hpp:60
void SubstituteSubgraph(SubgraphView &subgraph, IConnectableLayer *substituteLayer)
Substitutes the given sub-graph with either a new layer or a new sub-graph.
Definition: Graph.cpp:396
OutputLayersAccessor GetOutputLayers() const
Returns a wrapper object with begin(), end() methods to iterate over the output layers in a range-bas...
Definition: Graph.hpp:185
Graph & operator=(Graph &&other)
Definition: Graph.hpp:106
void InferTensorInfos()
Definition: Graph.cpp:493
std::list< Layer * > LayerList
Definition: Graph.hpp:49
ConstIterator begin() const
Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:163
ConstIteratorOutputs end() const
Definition: Graph.hpp:87
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: InputLayer.hpp:13
Iterator end()
Returns iterator pointing to the end of the list. Lowercase for range-based for loops.
Definition: Graph.hpp:160
ConstIterator cend() const
Returns const iterator pointing to the end of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:170
Graph & TopologicalSort()
Sorts layers in topological order and return this.
Definition: Graph.hpp:173
InputLayersAccessor GetInputLayers() const
Returns a wrapper object with begin(), end() methods to iterate over the input layers in a range-base...
Definition: Graph.hpp:181
boost::transform_iterator< decltype(&PtrCast< const InputLayer >), Iterator > ConstIteratorInputs
Definition: Graph.hpp:54
size_t GetNumLayers() const
Definition: Graph.hpp:187
LayerT * InsertNewLayer(InputSlot &insertBefore, Args &&... args)
Inserts a new layer between the output slot currently connected to insertBefore and insertBefore itse...
Definition: Graph.hpp:409
ConstIteratorInputs end() const
Definition: Graph.hpp:67
Status AllocateDynamicBuffers()
Allocates memory for all tensors under output tensor handers of each layer.
Definition: Graph.cpp:142
Graph(Graph &&other)
Definition: Graph.hpp:101
size_t GetNumInputs() const
Definition: Graph.hpp:176
void AddCompatibilityLayers(std::map< BackendId, std::unique_ptr< class IBackendInternal >> &backends, TensorHandleFactoryRegistry &registry)
Modifies the graph in-place, removing edges connecting layers using different compute devices...
Definition: Graph.cpp:263
Status Print() const
Definition: Graph.cpp:61
void DetachObservable(IGraphObservable *const observable, GraphEvent notifyOnEvent)
Definition: Graph.hpp:208
void MoveAllConnections(OutputSlot &destination)
Moves all connections to another OutputSlot.
Definition: Layer.cpp:112
LayerInGraph(Graph &graph, Args &&... args)
Definition: Graph.hpp:337