1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6
7 #include "LayersFwd.hpp"
8 #include "IGraphObservable.hpp"
9 #include "Profiling.hpp"
10
11 #include <armnn/Types.hpp>
12 #include <armnn/TensorFwd.hpp>
13 #include <armnn/NetworkFwd.hpp>
14 #include <armnn/Exceptions.hpp>
15 #include <armnn/utility/Assert.hpp>
16 #include <armnn/utility/PolymorphicDowncast.hpp>
17 #include <armnn/utility/TransformIterator.hpp>
18
19 #include <list>
20 #include <map>
21 #include <unordered_map>
22 #include <unordered_set>
23 #include <vector>
24
25 namespace armnn
26 {
27
28 class SubgraphView;
29
30 class Graph
31 {
32 public:
33 template <typename LayerType>
PtrCast(Layer * const layer)34 static LayerType* PtrCast(Layer* const layer)
35 {
36 return PolymorphicDowncast<LayerType*>(layer);
37 }
38
39 template <typename Func>
ForEachLayer(Func func) const40 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
52 // Const so pointers in the list can't be modified externally.
53 using Iterator = LayerList::const_iterator;
54 using IteratorDifference = Iterator::difference_type;
55
56 using ConstIterator = TransformIterator<decltype(&PtrCast<const Layer>), Iterator>;
57 using ConstIteratorInputs = TransformIterator<decltype(&PtrCast<const InputLayer>), Iterator>;
58 using ConstIteratorOutputs = TransformIterator<decltype(&PtrCast<const OutputLayer>), Iterator>;
59
60 /// Wrapper class returned by Graph::GetInputLayers()
61 struct InputLayersAccessor
62 {
InputLayersAccessorarmnn::Graph::InputLayersAccessor63 explicit InputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
64
beginarmnn::Graph::InputLayersAccessor65 ConstIteratorInputs begin() const
66 {
67 return { m_Graph.m_Layers.begin(), &(PtrCast<const InputLayer>) };
68 }
69
endarmnn::Graph::InputLayersAccessor70 ConstIteratorInputs end() const
71 {
72 return { std::next(m_Graph.m_Layers.begin(), static_cast<IteratorDifference>(m_Graph.GetNumInputs())),
73 &(PtrCast<const InputLayer>) };
74 }
75
76 const Graph& m_Graph;
77 };
78
79 /// Wrapper class returned by Graph::GetOutputLayers()
80 struct OutputLayersAccessor
81 {
OutputLayersAccessorarmnn::Graph::OutputLayersAccessor82 explicit OutputLayersAccessor(const Graph& graph) : m_Graph(graph) {}
83
beginarmnn::Graph::OutputLayersAccessor84 ConstIteratorOutputs begin() const
85 {
86 return { std::prev(m_Graph.m_Layers.end(), static_cast<IteratorDifference>(m_Graph.GetNumOutputs())),
87 &(PtrCast<const OutputLayer>) };
88 }
89
endarmnn::Graph::OutputLayersAccessor90 ConstIteratorOutputs end() const
91 {
92 return { m_Graph.m_Layers.end(), &(PtrCast<const OutputLayer>) };
93 }
94
95 const Graph& m_Graph;
96 };
97
Graph(bool shapeInferenceMethod=false,bool allowExpandedDims=false)98 Graph(bool shapeInferenceMethod = false, bool allowExpandedDims = false)
99 : m_LayersInOrder(true)
100 , m_AllowExpandedDims(allowExpandedDims)
101 , m_ShapeInferenceMethod(shapeInferenceMethod ? ShapeInferenceMethod::InferAndValidate :
102 ShapeInferenceMethod::ValidateOnly)
103 , m_Profiler(std::make_shared<IProfiler>())
104 {}
105
106 Graph(const Graph& other);
107
108 Graph& operator=(const Graph& other) = delete;
109
Graph(Graph && other)110 Graph(Graph&& other)
111 {
112 *this = std::move(other);
113 }
114
operator =(Graph && other)115 Graph& operator=(Graph&& other)
116 {
117 m_InputIds = std::move(other.m_InputIds);
118 m_OutputIds = std::move(other.m_OutputIds);
119 m_LayersInOrder = std::move(other.m_LayersInOrder);
120 m_Views = std::move(other.m_Views);
121 m_Profiler = std::move(other.m_Profiler);
122 m_AllowExpandedDims = other.m_AllowExpandedDims;
123 m_ShapeInferenceMethod = other.m_ShapeInferenceMethod;
124 other.ForEachLayer([this](Layer* otherLayer)
125 {
126 otherLayer->Reparent(*this, m_Layers.end());
127 });
128
129 ARMNN_ASSERT(other.m_PosInGraphMap.empty());
130 ARMNN_ASSERT(other.m_Layers.empty());
131
132 return *this;
133 }
134
~Graph()135 ~Graph()
136 {
137 ForEachLayer([](Layer* layer)
138 {
139 delete layer;
140 });
141 }
142
143 Status Print() const;
144
145 Status SerializeToDot(std::ostream& stream);
146
147 /// Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
148 template <typename LayerT, typename... Args>
149 LayerT* AddLayer(Args&&... args);
150
151 /// Inserts a new layer between the output slot currently connected to insertBefore
152 /// and insertBefore itself.
153 template <typename LayerT, typename... Args>
154 LayerT* InsertNewLayer(InputSlot& insertBefore, Args&&... args);
155
156 /// Inserts a new layer between insertAfter and the input slot(s) currently connected to it
157 template <typename LayerT, typename... Args>
158 LayerT* InsertNewLayer(OutputSlot& insertAfter, Args&&... args);
159
160 /// Deletes the layer at the specified position.
161 void EraseLayer(Iterator pos);
162
163 /// Deletes the layer. Sets @a layer to nullptr on return.
164 /// Templated to support pointers to any layer type.
165 template <typename LayerT>
166 void EraseLayer(LayerT*& layer);
167
168 /// Returns iterator pointing to the beginning of the list. Lowercase for range-based for loops.
begin()169 Iterator begin() { return m_Layers.begin(); }
170 /// Returns iterator pointing to the end of the list. Lowercase for range-based for loops.
end()171 Iterator end() { return m_Layers.end(); }
172
173 /// Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops.
begin() const174 ConstIterator begin() const { return {m_Layers.begin(), &(PtrCast<const Layer>)}; }
175 /// Returns const iterator pointing to the end of the list. Lowercase for range-based for loops.
end() const176 ConstIterator end() const { return {m_Layers.end(), &(PtrCast<const Layer>)}; }
177
178 /// Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops.
cbegin() const179 ConstIterator cbegin() const { return begin(); }
180 /// Returns const iterator pointing to the end of the list. Lowercase for range-based for loops.
cend() const181 ConstIterator cend() const { return end(); }
182
183 /// Sorts layers in topological order and return this.
TopologicalSort()184 Graph& TopologicalSort() { const_cast<const Graph*>(this)->TopologicalSort(); return *this; }
185 const Graph& TopologicalSort() const;
186
GetNumInputs() const187 size_t GetNumInputs() const { return m_InputIds.size(); }
GetNumOutputs() const188 size_t GetNumOutputs() const { return m_OutputIds.size(); }
189
190 /// Returns a wrapper object with begin(), end() methods to iterate over the input layers
191 /// in a range-based for loop.
GetInputLayers() const192 InputLayersAccessor GetInputLayers() const { return InputLayersAccessor(*this); }
193
194 /// Returns a wrapper object with begin(), end() methods to iterate over the output layers
195 /// in a range-based for loop.
GetOutputLayers() const196 OutputLayersAccessor GetOutputLayers() const { return OutputLayersAccessor(*this); }
197
GetNumLayers() const198 size_t GetNumLayers() const { return m_Layers.size(); }
199
200 /// Allocates memory for all tensors under output tensor handers of each layer.
201 Status AllocateDynamicBuffers();
202
203 /// Modifies the graph in-place, removing edges connecting layers using different compute devices,
204 /// and relinking them via an intermediary copy layers.
205 void AddCompatibilityLayers(std::map<BackendId, std::unique_ptr<class IBackendInternal>>& backends,
206 TensorHandleFactoryRegistry& registry);
207
208 /// Substitutes the given sub-graph with either a new layer or a new sub-graph.
209 /// In either case, the given layer or all the layers in the given sub-graph must belong to this graph.
210 void SubstituteSubgraph(SubgraphView& subgraph, IConnectableLayer* substituteLayer);
211 void SubstituteSubgraph(SubgraphView& subgraph, const SubgraphView& substituteSubgraph);
212
213 /// For each ConstantLayer in Graph, ensures TensorInfo is set on all output slots.
214 /// LayerValidationException thrown if no TensorInfo is set.
215 void VerifyConstantLayerSetTensorInfo() const;
216
217 void InferTensorInfos();
218
AttachObservable(IGraphObservable * const observable,GraphEvent notifyOnEvent)219 void AttachObservable(IGraphObservable* const observable, GraphEvent notifyOnEvent) {
220 m_Views[notifyOnEvent].emplace_back(observable);
221 }
222
DetachObservable(IGraphObservable * const observable,GraphEvent notifyOnEvent)223 void DetachObservable(IGraphObservable* const observable, GraphEvent notifyOnEvent) {
224 m_Views[notifyOnEvent].remove(observable);
225 }
226
227 /// Gets the position of a layer in the graph.
228 Iterator GetPosInGraph(Layer& layer);
229
230 const std::shared_ptr<IProfiler>& GetProfiler() const;
231
232 void SetLayersOutOfOrder();
233
234 private:
235 template <typename LayerT>
236 class LayerInGraphBase;
237
238 template <typename LayerT>
239 class LayerInGraph;
240
ForwardToEndOfInputs(Iterator it) const241 Iterator ForwardToEndOfInputs(Iterator it) const
242 {
243 while ((it != m_Layers.end()) && ((*it)->GetType() == LayerType::Input))
244 {
245 ++it;
246 }
247 return it;
248 }
ForwardToEndOfInputsAndConstants(Iterator it) const249 Iterator ForwardToEndOfInputsAndConstants(Iterator it) const
250 {
251 while ((it != m_Layers.end()) &&
252 ((*it)->GetType() == LayerType::Input || (*it)->GetType() == LayerType::Constant))
253 {
254 ++it;
255 }
256 return it;
257 }
258
RewindToBeginOfOutputs(Iterator it) const259 Iterator RewindToBeginOfOutputs(Iterator it) const
260 {
261 while ((it != m_Layers.begin()) && ((*std::prev(it))->GetType() == LayerType::Output))
262 {
263 --it;
264 }
265 return it;
266 }
267
NotifyObservables(GraphEvent event,Layer * graphState)268 void NotifyObservables(GraphEvent event, Layer* graphState)
269 {
270 // Iterate over all observables observing this event
271 for (auto& observable : m_Views[event])
272 {
273 observable->Update(graphState);
274 }
275 }
276
277 std::unordered_set<LayerBindingId> m_InputIds;
278 std::unordered_set<LayerBindingId> m_OutputIds;
279 std::unordered_map<const Layer*, Iterator> m_PosInGraphMap;
280
281 void ReplaceSubgraphConnections(const SubgraphView& subgraph, const SubgraphView& substituteSubgraph);
282 void EraseSubgraphLayers(SubgraphView &subgraph);
283
284 /// Mutable to allow sorting on const object.
285 mutable LayerList m_Layers;
286 mutable bool m_LayersInOrder;
287
288 bool m_AllowExpandedDims;
289
290 std::map<const GraphEvent, std::list<IGraphObservable*>> m_Views;
291 ShapeInferenceMethod m_ShapeInferenceMethod;
292
293 std::shared_ptr<IProfiler> m_Profiler;
294
295 // Throws exception due to a layer input not being connected to an output slot.
296 /// Also verifies weights and bias are set for FullyConnected layers.
297 void ConstructErrorMessageForUnconnectedInputs(Layer* const layer,
298 unsigned int slotIndex);
299
300 friend class SubgraphView;
301 };
302
303 /// Common base class for layers in the graph.
304 template <typename LayerT>
305 class Graph::LayerInGraphBase : public LayerT
306 {
307 protected:
308 template <typename... Args>
LayerInGraphBase(Graph & graph,Iterator insertBefore,Args &&...args)309 LayerInGraphBase(Graph& graph, Iterator insertBefore, Args&&... args)
310 : LayerT(std::forward<Args>(args)...), m_Graph(&graph)
311 {
312 Insert(*m_Graph, insertBefore);
313 }
~LayerInGraphBase()314 ~LayerInGraphBase()
315 {
316 Remove(*m_Graph);
317 }
318
Reparent(Graph & destGraph,Iterator insertBefore)319 void Reparent(Graph& destGraph, Iterator insertBefore) override
320 {
321 Insert(destGraph, insertBefore);
322 Remove(*m_Graph);
323
324 m_Graph = &destGraph;
325 }
326
327 private:
Insert(Graph & graph,Iterator insertBefore)328 void Insert(Graph& graph, Iterator insertBefore)
329 {
330 graph.m_PosInGraphMap.emplace(this, graph.m_Layers.emplace(insertBefore, this));
331 }
332
Remove(Graph & graph)333 void Remove(Graph& graph)
334 {
335 auto layerIt = graph.GetPosInGraph(*this);
336 graph.m_Layers.erase(layerIt);
337
338 const size_t numErased = graph.m_PosInGraphMap.erase(this);
339 IgnoreUnused(numErased);
340 ARMNN_ASSERT(numErased == 1);
341 }
342
343 protected:
344 Graph* m_Graph;
345 };
346
347 /// Input/Output/Constant layers specialize this template.
348 template <typename LayerT>
349 class Graph::LayerInGraph final : public LayerInGraphBase<LayerT>
350 {
351 public:
352 template <typename... Args>
LayerInGraph(Graph & graph,Args &&...args)353 LayerInGraph(Graph& graph, Args&&... args)
354 : LayerInGraphBase<LayerT>(graph,
355 // Insert at the back of the intermediate layers (before outputs).
356 std::prev(graph.end(), IteratorDifference(graph.GetNumOutputs())),
357 std::forward<Args>(args)...)
358 {
359 }
360 template <typename... Args>
LayerInGraph(Graph & graph,Iterator insertBefore,Args &&...args)361 LayerInGraph(Graph& graph, Iterator insertBefore, Args&&... args)
362 : LayerInGraphBase<LayerT>(graph,
363 // Make sure it's inserted after all inputs and before all outputs.
364 graph.ForwardToEndOfInputsAndConstants(graph.RewindToBeginOfOutputs(insertBefore)),
365 std::forward<Args>(args)...)
366 {
367 }
368 };
369
370 template <>
371 class Graph::LayerInGraph<ConstantLayer> final : public LayerInGraphBase<ConstantLayer>
372 {
373 public:
374 template <typename... Args>
LayerInGraph(Graph & graph,Args &&...args)375 LayerInGraph(Graph& graph, Args&&... args)
376 : LayerInGraphBase<ConstantLayer>(graph,
377 // Always add to the back of the inputs.
378 std::next(graph.begin(), IteratorDifference(graph.GetNumInputs())),
379 std::forward<Args>(args)...)
380 {}
381 template <typename... Args>
LayerInGraph(Graph & graph,Iterator,Args &&...args)382 LayerInGraph(Graph& graph, Iterator, Args&&... args)
383 // Ignore Iterator argument. Always add to the back of the inputs.
384 : LayerInGraph(graph, std::forward<Args>(args)...)
385 {}
~LayerInGraph()386 ~LayerInGraph() override
387 {}
388 };
389
390 /// Inputs add/remove their binding id to m_InputIds in the graph.
391 template <>
392 class Graph::LayerInGraph<InputLayer> final : public LayerInGraphBase<InputLayer>
393 {
394 public:
395 template <typename... Args>
LayerInGraph(Graph & graph,Args &&...args)396 LayerInGraph(Graph& graph, Args&&... args)
397 : LayerInGraphBase<InputLayer>(graph,
398 // Always add to the back of the inputs.
399 std::next(graph.begin(), IteratorDifference(graph.GetNumInputs())),
400 std::forward<Args>(args)...)
401 {
402 const bool isNewId = m_Graph->m_InputIds.emplace(GetBindingId()).second;
403 if (!isNewId)
404 {
405 throw InvalidArgumentException("A layer already exists with the specified id");
406 }
407 }
408 template <typename... Args>
LayerInGraph(Graph & graph,Iterator,Args &&...args)409 LayerInGraph(Graph& graph, Iterator, Args&&... args)
410 // Ignore Iterator argument. Always add to the back of the inputs.
411 : LayerInGraph(graph, std::forward<Args>(args)...)
412 {
413 }
~LayerInGraph()414 ~LayerInGraph() override
415 {
416 const size_t numErased = m_Graph->m_InputIds.erase(GetBindingId());
417 IgnoreUnused(numErased);
418 ARMNN_ASSERT(numErased == 1);
419 }
420 };
421
422 /// Outputs add/remove their binding id to m_OutputIds in the graph.
423 template <>
424 class Graph::LayerInGraph<OutputLayer> final : public LayerInGraphBase<OutputLayer>
425 {
426 public:
427 template <typename... Args>
LayerInGraph(Graph & graph,Args &&...args)428 LayerInGraph(Graph& graph, Args&&... args)
429 : LayerInGraphBase<OutputLayer>(graph,
430 // Always add to the back of the outputs.
431 graph.end(),
432 std::forward<Args>(args)...)
433 {
434 const bool isNewId = m_Graph->m_OutputIds.emplace(GetBindingId()).second;
435 if (!isNewId)
436 {
437 throw InvalidArgumentException("A layer already exists with the specified id");
438 }
439 }
~LayerInGraph()440 ~LayerInGraph() override
441 {
442 const size_t numErased = m_Graph->m_OutputIds.erase(GetBindingId());
443 IgnoreUnused(numErased);
444 ARMNN_ASSERT(numErased == 1);
445 }
446 };
447
GetPosInGraph(Layer & layer)448 inline Graph::Iterator Graph::GetPosInGraph(Layer& layer)
449 {
450 auto it = m_PosInGraphMap.find(&layer);
451 ARMNN_ASSERT(it != m_PosInGraphMap.end());
452 return it->second;
453 }
454
455 template <typename LayerT, typename... Args>
AddLayer(Args &&...args)456 inline LayerT* Graph::AddLayer(Args&&... args)
457 {
458 m_LayersInOrder = m_LayersInOrder &&
459 ((LayerEnumOf<LayerT>() == LayerType::Input) || (LayerEnumOf<LayerT>() == LayerType::Output));
460 LayerT* const layer = new LayerInGraph<LayerT>(*this, std::forward<Args>(args)...);
461
462 layer->SetShapeInferenceMethod(m_ShapeInferenceMethod);
463 layer->SetAllowExpandedDims(m_AllowExpandedDims);
464
465 NotifyObservables(GraphEvent::LayerAdded, layer);
466
467 return layer;
468 }
469
470 template <typename LayerT, typename... Args>
InsertNewLayer(InputSlot & insertBefore,Args &&...args)471 inline LayerT* Graph::InsertNewLayer(InputSlot& insertBefore, Args&&... args)
472 {
473 // Insert after the parent if any, or before the child otherwise, so the topological order is kept.
474 OutputSlot* parentOut = insertBefore.GetConnectedOutputSlot();
475 const Iterator pos = (parentOut != nullptr)
476 ? std::next(GetPosInGraph(parentOut->GetOwningLayer()))
477 : GetPosInGraph(insertBefore.GetOwningLayer());
478 LayerT* const layer = new LayerInGraph<LayerT>(*this, pos, std::forward<Args>(args)...);
479 insertBefore.Insert(*layer);
480
481 NotifyObservables(GraphEvent::LayerAdded, layer);
482
483 return layer;
484 }
485
486 template <typename LayerT, typename... Args>
InsertNewLayer(OutputSlot & insertAfter,Args &&...args)487 inline LayerT* Graph::InsertNewLayer(OutputSlot& insertAfter, Args&&... args)
488 {
489 Layer& owningLayer = insertAfter.GetOwningLayer();
490
491 const Iterator pos = std::next(GetPosInGraph(owningLayer));
492 LayerT* const layer = new LayerInGraph<LayerT>(*this, pos, std::forward<Args>(args)...);
493
494 ARMNN_ASSERT(layer->GetNumInputSlots() == 1);
495
496 insertAfter.MoveAllConnections(layer->GetOutputSlot());
497 insertAfter.Connect(layer->GetInputSlot(0));
498
499 NotifyObservables(GraphEvent::LayerAdded, layer);
500
501 return layer;
502 }
503
EraseLayer(Iterator pos)504 inline void Graph::EraseLayer(Iterator pos)
505 {
506 NotifyObservables(GraphEvent::LayerErased, *pos);
507
508 delete *pos;
509 }
510
511 template <typename LayerT>
EraseLayer(LayerT * & layer)512 inline void Graph::EraseLayer(LayerT*& layer)
513 {
514 ARMNN_ASSERT(layer != nullptr);
515 EraseLayer(GetPosInGraph(*layer));
516 layer = nullptr;
517 }
518
519 } // namespace armnn
520