xref: /aosp_15_r20/external/armnn/src/armnn/Graph.hpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
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