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