xref: /aosp_15_r20/external/armnn/src/armnn/SubgraphViewSelector.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1*89c4ff92SAndroid Build Coastguard Worker //
2*89c4ff92SAndroid Build Coastguard Worker // Copyright © 2017, 2023 Arm Ltd and Contributors. All rights reserved.
3*89c4ff92SAndroid Build Coastguard Worker // SPDX-License-Identifier: MIT
4*89c4ff92SAndroid Build Coastguard Worker //
5*89c4ff92SAndroid Build Coastguard Worker 
6*89c4ff92SAndroid Build Coastguard Worker #include "SubgraphViewSelector.hpp"
7*89c4ff92SAndroid Build Coastguard Worker #include "Graph.hpp"
8*89c4ff92SAndroid Build Coastguard Worker 
9*89c4ff92SAndroid Build Coastguard Worker #include <armnn/utility/Assert.hpp>
10*89c4ff92SAndroid Build Coastguard Worker #include <armnn/utility/IgnoreUnused.hpp>
11*89c4ff92SAndroid Build Coastguard Worker #include <armnn/utility/PolymorphicDowncast.hpp>
12*89c4ff92SAndroid Build Coastguard Worker 
13*89c4ff92SAndroid Build Coastguard Worker #include <algorithm>
14*89c4ff92SAndroid Build Coastguard Worker #include <map>
15*89c4ff92SAndroid Build Coastguard Worker #include <queue>
16*89c4ff92SAndroid Build Coastguard Worker #include <unordered_set>
17*89c4ff92SAndroid Build Coastguard Worker 
18*89c4ff92SAndroid Build Coastguard Worker namespace armnn
19*89c4ff92SAndroid Build Coastguard Worker {
20*89c4ff92SAndroid Build Coastguard Worker 
21*89c4ff92SAndroid Build Coastguard Worker namespace
22*89c4ff92SAndroid Build Coastguard Worker {
23*89c4ff92SAndroid Build Coastguard Worker 
24*89c4ff92SAndroid Build Coastguard Worker /// Intermediate data-structure to store the subgraph that a layer has been assigned to.
25*89c4ff92SAndroid Build Coastguard Worker /// This is a "disjoint set" data structure that allows efficient merging of subgraphs,
26*89c4ff92SAndroid Build Coastguard Worker /// which is a key part of the algorithm. Subgraphs are arranged in singly-linked trees
27*89c4ff92SAndroid Build Coastguard Worker /// (with each node storing a pointer to its parent). Subgraphs in the same tree are considered
28*89c4ff92SAndroid Build Coastguard Worker /// to have been merged. Merging subgraphs is performed by attaching one tree to another,
29*89c4ff92SAndroid Build Coastguard Worker /// which is a simple pointer update.
30*89c4ff92SAndroid Build Coastguard Worker ///
31*89c4ff92SAndroid Build Coastguard Worker /// NOTE: Due to the way this is stored, it is almost never correct to directly compare pointers
32*89c4ff92SAndroid Build Coastguard Worker /// to two PartialSubgraphs to check if two layers belong in the same subgraph. Instead you
33*89c4ff92SAndroid Build Coastguard Worker /// should use IsMergedWith().
34*89c4ff92SAndroid Build Coastguard Worker ///
35*89c4ff92SAndroid Build Coastguard Worker /// This structure also stores information about the dependencies of each subgraph, which is needed
36*89c4ff92SAndroid Build Coastguard Worker /// to determine whether certain subgraphs can be merged. Checking whether a subgraph
37*89c4ff92SAndroid Build Coastguard Worker /// depends on another subgraph is a frequent operation in the algorithm (see AssignSplitId) and so this is optimized
38*89c4ff92SAndroid Build Coastguard Worker /// in preference to the merging of subgraphs. This leads to an approach where each subgraph stores
39*89c4ff92SAndroid Build Coastguard Worker /// a set of all the subgraphs it depends on (for a fast lookup). In order to efficiently update this
40*89c4ff92SAndroid Build Coastguard Worker /// set as subgraphs are merged means we also store a set of subgraphs which *depend on us* (i.e. the
41*89c4ff92SAndroid Build Coastguard Worker /// complement of our dependencies).
42*89c4ff92SAndroid Build Coastguard Worker class PartialSubgraph
43*89c4ff92SAndroid Build Coastguard Worker {
44*89c4ff92SAndroid Build Coastguard Worker public:
45*89c4ff92SAndroid Build Coastguard Worker     /// If this subgraph has been merged with another then there is an agreed "representative" for the combined
46*89c4ff92SAndroid Build Coastguard Worker     /// subgraph, which uniquely identifies the subgraph.
GetRepresentative()47*89c4ff92SAndroid Build Coastguard Worker     PartialSubgraph* GetRepresentative()
48*89c4ff92SAndroid Build Coastguard Worker     {
49*89c4ff92SAndroid Build Coastguard Worker         // Recurse up the tree to find the root node.
50*89c4ff92SAndroid Build Coastguard Worker         if (m_Parent == nullptr)
51*89c4ff92SAndroid Build Coastguard Worker         {
52*89c4ff92SAndroid Build Coastguard Worker             return this;
53*89c4ff92SAndroid Build Coastguard Worker         }
54*89c4ff92SAndroid Build Coastguard Worker         else
55*89c4ff92SAndroid Build Coastguard Worker         {
56*89c4ff92SAndroid Build Coastguard Worker             PartialSubgraph* result = m_Parent->GetRepresentative();
57*89c4ff92SAndroid Build Coastguard Worker             // Update our parent pointer to point directly to the root in order to speed up future calls to this method.
58*89c4ff92SAndroid Build Coastguard Worker             // This essentially "flattens" the tree.
59*89c4ff92SAndroid Build Coastguard Worker             m_Parent = result;
60*89c4ff92SAndroid Build Coastguard Worker             return result;
61*89c4ff92SAndroid Build Coastguard Worker         }
62*89c4ff92SAndroid Build Coastguard Worker     }
63*89c4ff92SAndroid Build Coastguard Worker 
64*89c4ff92SAndroid Build Coastguard Worker     /// Merges this subgraph with another.
MergeWith(PartialSubgraph * other)65*89c4ff92SAndroid Build Coastguard Worker     void MergeWith(PartialSubgraph* other)
66*89c4ff92SAndroid Build Coastguard Worker     {
67*89c4ff92SAndroid Build Coastguard Worker         if (m_Parent == nullptr)
68*89c4ff92SAndroid Build Coastguard Worker         {
69*89c4ff92SAndroid Build Coastguard Worker             other = other->GetRepresentative();
70*89c4ff92SAndroid Build Coastguard Worker             if (this == other)
71*89c4ff92SAndroid Build Coastguard Worker             {
72*89c4ff92SAndroid Build Coastguard Worker                 // Already merged - no-op
73*89c4ff92SAndroid Build Coastguard Worker                 return;
74*89c4ff92SAndroid Build Coastguard Worker             }
75*89c4ff92SAndroid Build Coastguard Worker             m_Parent = other;
76*89c4ff92SAndroid Build Coastguard Worker 
77*89c4ff92SAndroid Build Coastguard Worker             // Update others' dependency sets to point to the new representative rather than us.
78*89c4ff92SAndroid Build Coastguard Worker             // Keeping these up-to-date means we can rely on these sets containing representatives when
79*89c4ff92SAndroid Build Coastguard Worker             // we perform a lookup in HasAntecedent() and so don't need to resolve the representative for each element
80*89c4ff92SAndroid Build Coastguard Worker             // of the set. See description at the top of this class for more rationale.
81*89c4ff92SAndroid Build Coastguard Worker             for (PartialSubgraph* a : m_Antecedents)
82*89c4ff92SAndroid Build Coastguard Worker             {
83*89c4ff92SAndroid Build Coastguard Worker                 size_t numErased = a->m_Dependants.erase(this);
84*89c4ff92SAndroid Build Coastguard Worker                 ARMNN_ASSERT(numErased == 1);
85*89c4ff92SAndroid Build Coastguard Worker                 IgnoreUnused(numErased);
86*89c4ff92SAndroid Build Coastguard Worker                 a->m_Dependants.insert(m_Parent);
87*89c4ff92SAndroid Build Coastguard Worker             }
88*89c4ff92SAndroid Build Coastguard Worker             for (PartialSubgraph* a : m_Dependants)
89*89c4ff92SAndroid Build Coastguard Worker             {
90*89c4ff92SAndroid Build Coastguard Worker                 size_t numErased = a->m_Antecedents.erase(this);
91*89c4ff92SAndroid Build Coastguard Worker                 ARMNN_ASSERT(numErased == 1);
92*89c4ff92SAndroid Build Coastguard Worker                 IgnoreUnused(numErased);
93*89c4ff92SAndroid Build Coastguard Worker                 a->m_Antecedents.insert(m_Parent);
94*89c4ff92SAndroid Build Coastguard Worker             }
95*89c4ff92SAndroid Build Coastguard Worker 
96*89c4ff92SAndroid Build Coastguard Worker             // Merge our dependency sets into our new representative.
97*89c4ff92SAndroid Build Coastguard Worker             // We no longer need to maintain our own sets, as requests will always be forwarded to the representative.
98*89c4ff92SAndroid Build Coastguard Worker             m_Parent->m_Antecedents.insert(m_Antecedents.begin(), m_Antecedents.end());
99*89c4ff92SAndroid Build Coastguard Worker             m_Antecedents.clear();
100*89c4ff92SAndroid Build Coastguard Worker             m_Parent->m_Dependants.insert(m_Dependants.begin(), m_Dependants.end());
101*89c4ff92SAndroid Build Coastguard Worker             m_Dependants.clear();
102*89c4ff92SAndroid Build Coastguard Worker         }
103*89c4ff92SAndroid Build Coastguard Worker         else
104*89c4ff92SAndroid Build Coastguard Worker         {
105*89c4ff92SAndroid Build Coastguard Worker             // Defer request to the representative
106*89c4ff92SAndroid Build Coastguard Worker             GetRepresentative()->MergeWith(other);
107*89c4ff92SAndroid Build Coastguard Worker         }
108*89c4ff92SAndroid Build Coastguard Worker     }
109*89c4ff92SAndroid Build Coastguard Worker 
110*89c4ff92SAndroid Build Coastguard Worker     /// Checks if this subgraph has been merged with the given subgraph.
IsMergedWith(PartialSubgraph * other)111*89c4ff92SAndroid Build Coastguard Worker     bool IsMergedWith(PartialSubgraph* other)
112*89c4ff92SAndroid Build Coastguard Worker     {
113*89c4ff92SAndroid Build Coastguard Worker         return GetRepresentative() == other->GetRepresentative();
114*89c4ff92SAndroid Build Coastguard Worker     }
115*89c4ff92SAndroid Build Coastguard Worker 
116*89c4ff92SAndroid Build Coastguard Worker     /// Marks the given subgraph as a direct antecedent (dependency) of this one.
AddDirectAntecedent(PartialSubgraph * antecedent)117*89c4ff92SAndroid Build Coastguard Worker     void AddDirectAntecedent(PartialSubgraph* antecedent)
118*89c4ff92SAndroid Build Coastguard Worker     {
119*89c4ff92SAndroid Build Coastguard Worker         if (m_Parent == nullptr)
120*89c4ff92SAndroid Build Coastguard Worker         {
121*89c4ff92SAndroid Build Coastguard Worker             antecedent = antecedent->GetRepresentative();
122*89c4ff92SAndroid Build Coastguard Worker 
123*89c4ff92SAndroid Build Coastguard Worker             m_Antecedents.insert(antecedent);
124*89c4ff92SAndroid Build Coastguard Worker             // Also record all of its antecedents, so that we end up with direct and indirect antecedents.
125*89c4ff92SAndroid Build Coastguard Worker             // This makes the lookup in HasAntecedent() faster.
126*89c4ff92SAndroid Build Coastguard Worker             m_Antecedents.insert(antecedent->m_Antecedents.begin(), antecedent->m_Antecedents.end());
127*89c4ff92SAndroid Build Coastguard Worker             // All of our dependents also need to include the new antecedents
128*89c4ff92SAndroid Build Coastguard Worker             for (PartialSubgraph* d : m_Dependants)
129*89c4ff92SAndroid Build Coastguard Worker             {
130*89c4ff92SAndroid Build Coastguard Worker                 d->m_Antecedents.insert(antecedent);
131*89c4ff92SAndroid Build Coastguard Worker                 d->m_Antecedents.insert(antecedent->m_Antecedents.begin(), antecedent->m_Antecedents.end());
132*89c4ff92SAndroid Build Coastguard Worker             }
133*89c4ff92SAndroid Build Coastguard Worker 
134*89c4ff92SAndroid Build Coastguard Worker             // Store reverse dependencies as well, required so that we can efficiently navigate the graph
135*89c4ff92SAndroid Build Coastguard Worker             // when making updates.
136*89c4ff92SAndroid Build Coastguard Worker             antecedent->m_Dependants.insert(this);
137*89c4ff92SAndroid Build Coastguard Worker             antecedent->m_Dependants.insert(m_Dependants.begin(), m_Dependants.end());
138*89c4ff92SAndroid Build Coastguard Worker             for (PartialSubgraph* a : antecedent->m_Antecedents)
139*89c4ff92SAndroid Build Coastguard Worker             {
140*89c4ff92SAndroid Build Coastguard Worker                 a->m_Dependants.insert(this);
141*89c4ff92SAndroid Build Coastguard Worker                 a->m_Dependants.insert(m_Dependants.begin(), m_Dependants.end());
142*89c4ff92SAndroid Build Coastguard Worker             }
143*89c4ff92SAndroid Build Coastguard Worker         }
144*89c4ff92SAndroid Build Coastguard Worker         else
145*89c4ff92SAndroid Build Coastguard Worker         {
146*89c4ff92SAndroid Build Coastguard Worker             // Defer request to the representative
147*89c4ff92SAndroid Build Coastguard Worker             GetRepresentative()->AddDirectAntecedent(antecedent);
148*89c4ff92SAndroid Build Coastguard Worker         }
149*89c4ff92SAndroid Build Coastguard Worker     }
150*89c4ff92SAndroid Build Coastguard Worker 
151*89c4ff92SAndroid Build Coastguard Worker     /// Checks if this subgraph is dependent on the given subgraph, either directly or indirectly.
HasAntecedent(PartialSubgraph * antecedent)152*89c4ff92SAndroid Build Coastguard Worker     bool HasAntecedent(PartialSubgraph* antecedent)
153*89c4ff92SAndroid Build Coastguard Worker     {
154*89c4ff92SAndroid Build Coastguard Worker         if (m_Parent == nullptr)
155*89c4ff92SAndroid Build Coastguard Worker         {
156*89c4ff92SAndroid Build Coastguard Worker             antecedent = antecedent->GetRepresentative();
157*89c4ff92SAndroid Build Coastguard Worker             // Thanks to keeping this set updated in MergeWith and AddDirectAntecedent, we can do an efficient lookup.
158*89c4ff92SAndroid Build Coastguard Worker             return m_Antecedents.count(antecedent) > 0;
159*89c4ff92SAndroid Build Coastguard Worker         }
160*89c4ff92SAndroid Build Coastguard Worker         else
161*89c4ff92SAndroid Build Coastguard Worker         {
162*89c4ff92SAndroid Build Coastguard Worker             // Defer request to the representative
163*89c4ff92SAndroid Build Coastguard Worker             return GetRepresentative()->HasAntecedent(antecedent);
164*89c4ff92SAndroid Build Coastguard Worker         }
165*89c4ff92SAndroid Build Coastguard Worker     }
166*89c4ff92SAndroid Build Coastguard Worker 
167*89c4ff92SAndroid Build Coastguard Worker private:
168*89c4ff92SAndroid Build Coastguard Worker     /// Pointer to the parent node in the tree. If this is null then we are the representative for our merged subgraph.
169*89c4ff92SAndroid Build Coastguard Worker     PartialSubgraph* m_Parent;
170*89c4ff92SAndroid Build Coastguard Worker     /// The representatives of all the subgraphs which we depend on, either directly or indirectly.
171*89c4ff92SAndroid Build Coastguard Worker     std::unordered_set<PartialSubgraph*> m_Antecedents;
172*89c4ff92SAndroid Build Coastguard Worker     /// The representatives of all the subgraphs which depend on us, either directly or indirectly.
173*89c4ff92SAndroid Build Coastguard Worker     std::unordered_set<PartialSubgraph*> m_Dependants;
174*89c4ff92SAndroid Build Coastguard Worker };
175*89c4ff92SAndroid Build Coastguard Worker 
176*89c4ff92SAndroid Build Coastguard Worker /// Intermediate data structure to store information associated with a particular layer.
177*89c4ff92SAndroid Build Coastguard Worker struct LayerSelectionInfo
178*89c4ff92SAndroid Build Coastguard Worker {
179*89c4ff92SAndroid Build Coastguard Worker     using LayerInfoContainer = std::map<IConnectableLayer*, LayerSelectionInfo>;
180*89c4ff92SAndroid Build Coastguard Worker     using LayerInfoQueue = std::queue<LayerSelectionInfo*>;
181*89c4ff92SAndroid Build Coastguard Worker 
LayerSelectionInfoarmnn::__anon04db257d0111::LayerSelectionInfo182*89c4ff92SAndroid Build Coastguard Worker     LayerSelectionInfo(Layer* layer, const SubgraphViewSelector::LayerSelectorFunction& selector)
183*89c4ff92SAndroid Build Coastguard Worker     : m_Layer{layer}
184*89c4ff92SAndroid Build Coastguard Worker     , m_Subgraph{nullptr}
185*89c4ff92SAndroid Build Coastguard Worker     , m_IsSelected{selector(*layer)}
186*89c4ff92SAndroid Build Coastguard Worker     , m_IsProcessed(false)
187*89c4ff92SAndroid Build Coastguard Worker     {
188*89c4ff92SAndroid Build Coastguard Worker     }
189*89c4ff92SAndroid Build Coastguard Worker 
IsInputLayerarmnn::__anon04db257d0111::LayerSelectionInfo190*89c4ff92SAndroid Build Coastguard Worker     bool IsInputLayer() const
191*89c4ff92SAndroid Build Coastguard Worker     {
192*89c4ff92SAndroid Build Coastguard Worker         return m_Layer->GetType() == armnn::LayerType::Input || m_Layer->GetType() == armnn::LayerType::Constant;
193*89c4ff92SAndroid Build Coastguard Worker     }
194*89c4ff92SAndroid Build Coastguard Worker 
CollectNonSelectedInputsarmnn::__anon04db257d0111::LayerSelectionInfo195*89c4ff92SAndroid Build Coastguard Worker     void CollectNonSelectedInputs(LayerSelectionInfo::LayerInfoContainer& layerInfos,
196*89c4ff92SAndroid Build Coastguard Worker                                   SubgraphView::IInputSlots& inputSlots)
197*89c4ff92SAndroid Build Coastguard Worker     {
198*89c4ff92SAndroid Build Coastguard Worker         for (auto&& slot = PolymorphicDowncast<Layer*>(m_Layer)->BeginInputSlots();
199*89c4ff92SAndroid Build Coastguard Worker              slot != PolymorphicDowncast<Layer*>(m_Layer)->EndInputSlots();
200*89c4ff92SAndroid Build Coastguard Worker              ++slot)
201*89c4ff92SAndroid Build Coastguard Worker         {
202*89c4ff92SAndroid Build Coastguard Worker             OutputSlot* parentLayerOutputSlot = slot->GetConnectedOutputSlot();
203*89c4ff92SAndroid Build Coastguard Worker             ARMNN_ASSERT_MSG(parentLayerOutputSlot != nullptr, "The input slots must be connected here.");
204*89c4ff92SAndroid Build Coastguard Worker             if (parentLayerOutputSlot)
205*89c4ff92SAndroid Build Coastguard Worker             {
206*89c4ff92SAndroid Build Coastguard Worker                 Layer& parentLayer = parentLayerOutputSlot->GetOwningLayer();
207*89c4ff92SAndroid Build Coastguard Worker                 auto parentInfo = layerInfos.find(&parentLayer);
208*89c4ff92SAndroid Build Coastguard Worker                 if (parentInfo == layerInfos.end() ||
209*89c4ff92SAndroid Build Coastguard Worker                         !m_Subgraph->IsMergedWith(parentInfo->second.m_Subgraph.get()))
210*89c4ff92SAndroid Build Coastguard Worker                 {
211*89c4ff92SAndroid Build Coastguard Worker                     // Avoid collecting duplicate input slots
212*89c4ff92SAndroid Build Coastguard Worker                     InputSlot* inputSlot = &(*slot);
213*89c4ff92SAndroid Build Coastguard Worker                     if (std::find(inputSlots.begin(), inputSlots.end(), inputSlot) == inputSlots.end())
214*89c4ff92SAndroid Build Coastguard Worker                     {
215*89c4ff92SAndroid Build Coastguard Worker                         inputSlots.push_back(inputSlot);
216*89c4ff92SAndroid Build Coastguard Worker                     }
217*89c4ff92SAndroid Build Coastguard Worker                 }
218*89c4ff92SAndroid Build Coastguard Worker             }
219*89c4ff92SAndroid Build Coastguard Worker         }
220*89c4ff92SAndroid Build Coastguard Worker     }
221*89c4ff92SAndroid Build Coastguard Worker 
CollectNonSelectedOutputSlotsarmnn::__anon04db257d0111::LayerSelectionInfo222*89c4ff92SAndroid Build Coastguard Worker     void CollectNonSelectedOutputSlots(LayerSelectionInfo::LayerInfoContainer& layerInfos,
223*89c4ff92SAndroid Build Coastguard Worker                                        SubgraphView::IOutputSlots& outputSlots)
224*89c4ff92SAndroid Build Coastguard Worker     {
225*89c4ff92SAndroid Build Coastguard Worker         for (auto&& slot = PolymorphicDowncast<Layer*>(m_Layer)->BeginOutputSlots();
226*89c4ff92SAndroid Build Coastguard Worker              slot != PolymorphicDowncast<Layer*>(m_Layer)->EndOutputSlots();
227*89c4ff92SAndroid Build Coastguard Worker              ++slot)
228*89c4ff92SAndroid Build Coastguard Worker         {
229*89c4ff92SAndroid Build Coastguard Worker             for (InputSlot* childLayerInputSlot : slot->GetConnections())
230*89c4ff92SAndroid Build Coastguard Worker             {
231*89c4ff92SAndroid Build Coastguard Worker                 Layer& childLayer = childLayerInputSlot->GetOwningLayer();
232*89c4ff92SAndroid Build Coastguard Worker                 auto childInfo = layerInfos.find(&childLayer);
233*89c4ff92SAndroid Build Coastguard Worker                 if (childInfo == layerInfos.end() ||
234*89c4ff92SAndroid Build Coastguard Worker                         !m_Subgraph->IsMergedWith(childInfo->second.m_Subgraph.get()))
235*89c4ff92SAndroid Build Coastguard Worker                 {
236*89c4ff92SAndroid Build Coastguard Worker                     // Avoid collecting duplicate output slots
237*89c4ff92SAndroid Build Coastguard Worker                     OutputSlot* outputSlot = &(*slot);
238*89c4ff92SAndroid Build Coastguard Worker                     if (std::find(outputSlots.begin(), outputSlots.end(), outputSlot) == outputSlots.end())
239*89c4ff92SAndroid Build Coastguard Worker                     {
240*89c4ff92SAndroid Build Coastguard Worker                         outputSlots.push_back(outputSlot);
241*89c4ff92SAndroid Build Coastguard Worker                     }
242*89c4ff92SAndroid Build Coastguard Worker                 }
243*89c4ff92SAndroid Build Coastguard Worker             }
244*89c4ff92SAndroid Build Coastguard Worker         }
245*89c4ff92SAndroid Build Coastguard Worker     }
246*89c4ff92SAndroid Build Coastguard Worker 
247*89c4ff92SAndroid Build Coastguard Worker     IConnectableLayer* m_Layer;
248*89c4ff92SAndroid Build Coastguard Worker     /// Which subgraph this layer has been assigned to. Only valid once m_IsProcessed is true.
249*89c4ff92SAndroid Build Coastguard Worker     /// Two layers with different m_Subgraph pointers may in fact have been merged into the same subgraph -
250*89c4ff92SAndroid Build Coastguard Worker     /// see the description of the PartialSubgraph class.
251*89c4ff92SAndroid Build Coastguard Worker     std::shared_ptr<PartialSubgraph> m_Subgraph;
252*89c4ff92SAndroid Build Coastguard Worker     bool m_IsSelected;
253*89c4ff92SAndroid Build Coastguard Worker     bool m_IsProcessed;
254*89c4ff92SAndroid Build Coastguard Worker };
255*89c4ff92SAndroid Build Coastguard Worker 
256*89c4ff92SAndroid Build Coastguard Worker } // namespace <anonymous>
257*89c4ff92SAndroid Build Coastguard Worker 
258*89c4ff92SAndroid Build Coastguard Worker SubgraphViewSelector::Subgraphs
SelectSubgraphs(Graph & graph,const LayerSelectorFunction & selector)259*89c4ff92SAndroid Build Coastguard Worker SubgraphViewSelector::SelectSubgraphs(Graph& graph, const LayerSelectorFunction& selector)
260*89c4ff92SAndroid Build Coastguard Worker {
261*89c4ff92SAndroid Build Coastguard Worker     SubgraphView subgraph(graph);
262*89c4ff92SAndroid Build Coastguard Worker     return SubgraphViewSelector::SelectSubgraphs(subgraph, selector);
263*89c4ff92SAndroid Build Coastguard Worker }
264*89c4ff92SAndroid Build Coastguard Worker 
265*89c4ff92SAndroid Build Coastguard Worker 
266*89c4ff92SAndroid Build Coastguard Worker template<typename Delegate>
ForEachLayerInput(LayerSelectionInfo::LayerInfoContainer & layerInfos,LayerSelectionInfo & layerInfo,Delegate function)267*89c4ff92SAndroid Build Coastguard Worker void ForEachLayerInput(LayerSelectionInfo::LayerInfoContainer& layerInfos,
268*89c4ff92SAndroid Build Coastguard Worker                        LayerSelectionInfo& layerInfo,
269*89c4ff92SAndroid Build Coastguard Worker                        Delegate function)
270*89c4ff92SAndroid Build Coastguard Worker {
271*89c4ff92SAndroid Build Coastguard Worker     Layer& layer = *PolymorphicDowncast<Layer*>(layerInfo.m_Layer);
272*89c4ff92SAndroid Build Coastguard Worker 
273*89c4ff92SAndroid Build Coastguard Worker     for (auto inputSlot : layer.GetInputSlots())
274*89c4ff92SAndroid Build Coastguard Worker     {
275*89c4ff92SAndroid Build Coastguard Worker         auto connectedInput = PolymorphicDowncast<OutputSlot*>(inputSlot.GetConnection());
276*89c4ff92SAndroid Build Coastguard Worker         ARMNN_ASSERT_MSG(connectedInput, "Dangling input slot detected.");
277*89c4ff92SAndroid Build Coastguard Worker         Layer& inputLayer = connectedInput->GetOwningLayer();
278*89c4ff92SAndroid Build Coastguard Worker 
279*89c4ff92SAndroid Build Coastguard Worker         auto parentInfo = layerInfos.find(&inputLayer);
280*89c4ff92SAndroid Build Coastguard Worker         if (parentInfo != layerInfos.end())
281*89c4ff92SAndroid Build Coastguard Worker         {
282*89c4ff92SAndroid Build Coastguard Worker             function(parentInfo->second);
283*89c4ff92SAndroid Build Coastguard Worker         }
284*89c4ff92SAndroid Build Coastguard Worker     }
285*89c4ff92SAndroid Build Coastguard Worker }
286*89c4ff92SAndroid Build Coastguard Worker 
287*89c4ff92SAndroid Build Coastguard Worker template<typename Delegate>
ForEachLayerOutput(LayerSelectionInfo::LayerInfoContainer & layerInfos,LayerSelectionInfo & layerInfo,Delegate function)288*89c4ff92SAndroid Build Coastguard Worker void ForEachLayerOutput(LayerSelectionInfo::LayerInfoContainer& layerInfos,
289*89c4ff92SAndroid Build Coastguard Worker                         LayerSelectionInfo& layerInfo,
290*89c4ff92SAndroid Build Coastguard Worker                         Delegate function)
291*89c4ff92SAndroid Build Coastguard Worker {
292*89c4ff92SAndroid Build Coastguard Worker     Layer& layer = *PolymorphicDowncast<Layer*>(layerInfo.m_Layer);
293*89c4ff92SAndroid Build Coastguard Worker 
294*89c4ff92SAndroid Build Coastguard Worker     for (auto& outputSlot : layer.GetOutputSlots())
295*89c4ff92SAndroid Build Coastguard Worker     {
296*89c4ff92SAndroid Build Coastguard Worker         for (auto& output : outputSlot.GetConnections())
297*89c4ff92SAndroid Build Coastguard Worker         {
298*89c4ff92SAndroid Build Coastguard Worker             Layer& childLayer = output->GetOwningLayer();
299*89c4ff92SAndroid Build Coastguard Worker 
300*89c4ff92SAndroid Build Coastguard Worker             auto childInfo = layerInfos.find(&childLayer);
301*89c4ff92SAndroid Build Coastguard Worker             if (childInfo != layerInfos.end())
302*89c4ff92SAndroid Build Coastguard Worker             {
303*89c4ff92SAndroid Build Coastguard Worker                 function(childInfo->second);
304*89c4ff92SAndroid Build Coastguard Worker             }
305*89c4ff92SAndroid Build Coastguard Worker         }
306*89c4ff92SAndroid Build Coastguard Worker     }
307*89c4ff92SAndroid Build Coastguard Worker }
308*89c4ff92SAndroid Build Coastguard Worker 
AssignSplitId(LayerSelectionInfo::LayerInfoContainer & layerInfos,LayerSelectionInfo & layerInfo)309*89c4ff92SAndroid Build Coastguard Worker void AssignSplitId(LayerSelectionInfo::LayerInfoContainer& layerInfos, LayerSelectionInfo& layerInfo)
310*89c4ff92SAndroid Build Coastguard Worker {
311*89c4ff92SAndroid Build Coastguard Worker     // Check each input to see if we can attach ourselves to any of the subgraphs that have already been assigned.
312*89c4ff92SAndroid Build Coastguard Worker     ForEachLayerInput(layerInfos, layerInfo, [&](LayerSelectionInfo& parentInfo)
313*89c4ff92SAndroid Build Coastguard Worker     {
314*89c4ff92SAndroid Build Coastguard Worker         // We can only attach ourselves to the subgraph from this input if there isn't a cut here.
315*89c4ff92SAndroid Build Coastguard Worker         if (layerInfo.m_IsSelected == parentInfo.m_IsSelected)
316*89c4ff92SAndroid Build Coastguard Worker         {
317*89c4ff92SAndroid Build Coastguard Worker             // We also need to check that merging into this subgraph won't cause a dependency cycle between subgraphs.
318*89c4ff92SAndroid Build Coastguard Worker             // This will be the case if the subgraph that we will become part of is already a dependency
319*89c4ff92SAndroid Build Coastguard Worker             // of one of the subgraphs that are input to this layer, e.g:
320*89c4ff92SAndroid Build Coastguard Worker             //
321*89c4ff92SAndroid Build Coastguard Worker             //    0     |  The numbers (0, 1) are the subgraph IDs of each layer and we are looking at layer X.
322*89c4ff92SAndroid Build Coastguard Worker             //   / \    |
323*89c4ff92SAndroid Build Coastguard Worker             //  1   0   |  We can't merge X into subgraph 0, because the left-hand input already depends on subgraph 0.
324*89c4ff92SAndroid Build Coastguard Worker             //   \ /    |  We can however merge X into subgraph 1.
325*89c4ff92SAndroid Build Coastguard Worker             //    X     |
326*89c4ff92SAndroid Build Coastguard Worker             //
327*89c4ff92SAndroid Build Coastguard Worker             bool dependenciesOk = true;
328*89c4ff92SAndroid Build Coastguard Worker             ForEachLayerInput(layerInfos, layerInfo, [&](LayerSelectionInfo& otherParentInfo)
329*89c4ff92SAndroid Build Coastguard Worker             {
330*89c4ff92SAndroid Build Coastguard Worker                 // We call HasAntecedent() ~ n^2 times, where n is the number of inputs to this layer.
331*89c4ff92SAndroid Build Coastguard Worker                 // Hence it is important that this is efficient - see PartialSubgraph class description.
332*89c4ff92SAndroid Build Coastguard Worker                 if (otherParentInfo.m_Subgraph->HasAntecedent(parentInfo.m_Subgraph.get()))
333*89c4ff92SAndroid Build Coastguard Worker                 {
334*89c4ff92SAndroid Build Coastguard Worker                     dependenciesOk = false;
335*89c4ff92SAndroid Build Coastguard Worker                 }
336*89c4ff92SAndroid Build Coastguard Worker             });
337*89c4ff92SAndroid Build Coastguard Worker 
338*89c4ff92SAndroid Build Coastguard Worker             if (dependenciesOk)
339*89c4ff92SAndroid Build Coastguard Worker             {
340*89c4ff92SAndroid Build Coastguard Worker                 // Merge into the subgraph of this input. If we have already been merged into another subgraph
341*89c4ff92SAndroid Build Coastguard Worker                 // (from another input of this layer), then merge both of them together.
342*89c4ff92SAndroid Build Coastguard Worker                 if (layerInfo.m_Subgraph == nullptr)
343*89c4ff92SAndroid Build Coastguard Worker                 {
344*89c4ff92SAndroid Build Coastguard Worker                     layerInfo.m_Subgraph = parentInfo.m_Subgraph;
345*89c4ff92SAndroid Build Coastguard Worker                 }
346*89c4ff92SAndroid Build Coastguard Worker                 else
347*89c4ff92SAndroid Build Coastguard Worker                 {
348*89c4ff92SAndroid Build Coastguard Worker                     // We call MergeWith() ~ n times, where n is the number of inputs to this layer.
349*89c4ff92SAndroid Build Coastguard Worker                     // Therefore it does not need to be as performant as HasAntecedent().
350*89c4ff92SAndroid Build Coastguard Worker                     layerInfo.m_Subgraph->MergeWith(parentInfo.m_Subgraph.get());
351*89c4ff92SAndroid Build Coastguard Worker                 }
352*89c4ff92SAndroid Build Coastguard Worker             }
353*89c4ff92SAndroid Build Coastguard Worker         }
354*89c4ff92SAndroid Build Coastguard Worker     });
355*89c4ff92SAndroid Build Coastguard Worker 
356*89c4ff92SAndroid Build Coastguard Worker     // If we weren't able to merge into an existing subgraph then we need to make a new one
357*89c4ff92SAndroid Build Coastguard Worker     if (layerInfo.m_Subgraph == nullptr)
358*89c4ff92SAndroid Build Coastguard Worker     {
359*89c4ff92SAndroid Build Coastguard Worker         layerInfo.m_Subgraph = std::make_shared<PartialSubgraph>();
360*89c4ff92SAndroid Build Coastguard Worker     }
361*89c4ff92SAndroid Build Coastguard Worker 
362*89c4ff92SAndroid Build Coastguard Worker     // Record dependencies of the chosen subgraph based on the inputs of this layer.
363*89c4ff92SAndroid Build Coastguard Worker     ForEachLayerInput(layerInfos, layerInfo, [&](LayerSelectionInfo& parentInfo)
364*89c4ff92SAndroid Build Coastguard Worker     {
365*89c4ff92SAndroid Build Coastguard Worker         // These functions are called ~n times, where n is the number of inputs to this layer.
366*89c4ff92SAndroid Build Coastguard Worker         // Therefore it does not need to be as performant as HasAntecedent().
367*89c4ff92SAndroid Build Coastguard Worker         if (!layerInfo.m_Subgraph->IsMergedWith(parentInfo.m_Subgraph.get()))
368*89c4ff92SAndroid Build Coastguard Worker         {
369*89c4ff92SAndroid Build Coastguard Worker             layerInfo.m_Subgraph->AddDirectAntecedent(parentInfo.m_Subgraph.get());
370*89c4ff92SAndroid Build Coastguard Worker         }
371*89c4ff92SAndroid Build Coastguard Worker     });
372*89c4ff92SAndroid Build Coastguard Worker }
373*89c4ff92SAndroid Build Coastguard Worker 
IsReadyForSplitAssignment(LayerSelectionInfo::LayerInfoContainer & layerInfos,LayerSelectionInfo & layerInfo)374*89c4ff92SAndroid Build Coastguard Worker bool IsReadyForSplitAssignment(LayerSelectionInfo::LayerInfoContainer& layerInfos, LayerSelectionInfo& layerInfo)
375*89c4ff92SAndroid Build Coastguard Worker {
376*89c4ff92SAndroid Build Coastguard Worker     bool ready = true;
377*89c4ff92SAndroid Build Coastguard Worker     ForEachLayerInput(layerInfos, layerInfo,
378*89c4ff92SAndroid Build Coastguard Worker                       [&ready](LayerSelectionInfo& parentInfo)
379*89c4ff92SAndroid Build Coastguard Worker                           {
380*89c4ff92SAndroid Build Coastguard Worker                               if (!parentInfo.m_IsProcessed)
381*89c4ff92SAndroid Build Coastguard Worker                               {
382*89c4ff92SAndroid Build Coastguard Worker                                   ready = false;
383*89c4ff92SAndroid Build Coastguard Worker                               }
384*89c4ff92SAndroid Build Coastguard Worker                           });
385*89c4ff92SAndroid Build Coastguard Worker     return ready;
386*89c4ff92SAndroid Build Coastguard Worker }
387*89c4ff92SAndroid Build Coastguard Worker 
388*89c4ff92SAndroid Build Coastguard Worker SubgraphViewSelector::Subgraphs
SelectSubgraphs(SubgraphView & subgraph,const LayerSelectorFunction & selector)389*89c4ff92SAndroid Build Coastguard Worker SubgraphViewSelector::SelectSubgraphs(SubgraphView& subgraph, const LayerSelectorFunction& selector)
390*89c4ff92SAndroid Build Coastguard Worker {
391*89c4ff92SAndroid Build Coastguard Worker     LayerSelectionInfo::LayerInfoContainer layerInfos;
392*89c4ff92SAndroid Build Coastguard Worker 
393*89c4ff92SAndroid Build Coastguard Worker     LayerSelectionInfo::LayerInfoQueue processQueue;
394*89c4ff92SAndroid Build Coastguard Worker     const SubgraphView::IConnectableLayers& subgraphLayers = subgraph.GetIConnectableLayers();
395*89c4ff92SAndroid Build Coastguard Worker     for (auto& layer : subgraphLayers)
396*89c4ff92SAndroid Build Coastguard Worker     {
397*89c4ff92SAndroid Build Coastguard Worker 
398*89c4ff92SAndroid Build Coastguard Worker         auto emplaced = layerInfos.emplace(layer, LayerSelectionInfo{PolymorphicDowncast<Layer*>(layer), selector});
399*89c4ff92SAndroid Build Coastguard Worker         LayerSelectionInfo& layerInfo = emplaced.first->second;
400*89c4ff92SAndroid Build Coastguard Worker 
401*89c4ff92SAndroid Build Coastguard Worker         // Start with Input type layers
402*89c4ff92SAndroid Build Coastguard Worker         if (layerInfo.IsInputLayer())
403*89c4ff92SAndroid Build Coastguard Worker         {
404*89c4ff92SAndroid Build Coastguard Worker             processQueue.push(&layerInfo);
405*89c4ff92SAndroid Build Coastguard Worker         }
406*89c4ff92SAndroid Build Coastguard Worker     }
407*89c4ff92SAndroid Build Coastguard Worker 
408*89c4ff92SAndroid Build Coastguard Worker     const SubgraphView::IInputSlots& subgraphInputSlots = subgraph.GetIInputSlots();
409*89c4ff92SAndroid Build Coastguard Worker     for (auto& inputSlot : subgraphInputSlots)
410*89c4ff92SAndroid Build Coastguard Worker     {
411*89c4ff92SAndroid Build Coastguard Worker         Layer& layer = PolymorphicDowncast<InputSlot*>(inputSlot)->GetOwningLayer();
412*89c4ff92SAndroid Build Coastguard Worker         auto emplaced = layerInfos.emplace(&layer, LayerSelectionInfo{&layer, selector});
413*89c4ff92SAndroid Build Coastguard Worker         LayerSelectionInfo& layerInfo = emplaced.first->second;
414*89c4ff92SAndroid Build Coastguard Worker 
415*89c4ff92SAndroid Build Coastguard Worker         processQueue.push(&layerInfo);
416*89c4ff92SAndroid Build Coastguard Worker     }
417*89c4ff92SAndroid Build Coastguard Worker 
418*89c4ff92SAndroid Build Coastguard Worker     while (!processQueue.empty())
419*89c4ff92SAndroid Build Coastguard Worker     {
420*89c4ff92SAndroid Build Coastguard Worker         LayerSelectionInfo& layerInfo = *processQueue.front();
421*89c4ff92SAndroid Build Coastguard Worker         processQueue.pop(); // remove front from queue
422*89c4ff92SAndroid Build Coastguard Worker 
423*89c4ff92SAndroid Build Coastguard Worker         // This layerInfo may have been added to the queue multiple times, so skip if we have already processed it
424*89c4ff92SAndroid Build Coastguard Worker         if (!layerInfo.m_IsProcessed)
425*89c4ff92SAndroid Build Coastguard Worker         {
426*89c4ff92SAndroid Build Coastguard Worker             // Only process this layerInfo if all inputs have been processed
427*89c4ff92SAndroid Build Coastguard Worker             if (!IsReadyForSplitAssignment(layerInfos, layerInfo))
428*89c4ff92SAndroid Build Coastguard Worker             {
429*89c4ff92SAndroid Build Coastguard Worker                 // Put back of the process queue if we can't process it just yet
430*89c4ff92SAndroid Build Coastguard Worker                 processQueue.push(&layerInfo);
431*89c4ff92SAndroid Build Coastguard Worker                 continue; // Skip to next iteration
432*89c4ff92SAndroid Build Coastguard Worker             }
433*89c4ff92SAndroid Build Coastguard Worker 
434*89c4ff92SAndroid Build Coastguard Worker             // Now we do the processing
435*89c4ff92SAndroid Build Coastguard Worker             AssignSplitId(layerInfos, layerInfo);
436*89c4ff92SAndroid Build Coastguard Worker 
437*89c4ff92SAndroid Build Coastguard Worker             // Queue any child nodes for processing
438*89c4ff92SAndroid Build Coastguard Worker             ForEachLayerOutput(layerInfos, layerInfo, [&processQueue](LayerSelectionInfo& childInfo)
439*89c4ff92SAndroid Build Coastguard Worker                 {
440*89c4ff92SAndroid Build Coastguard Worker                     processQueue.push(&childInfo);
441*89c4ff92SAndroid Build Coastguard Worker                 });
442*89c4ff92SAndroid Build Coastguard Worker 
443*89c4ff92SAndroid Build Coastguard Worker             // We don't need to process this node again
444*89c4ff92SAndroid Build Coastguard Worker             layerInfo.m_IsProcessed = true;
445*89c4ff92SAndroid Build Coastguard Worker         }
446*89c4ff92SAndroid Build Coastguard Worker     }
447*89c4ff92SAndroid Build Coastguard Worker 
448*89c4ff92SAndroid Build Coastguard Worker     // Collect all selected layers keyed by subgraph representative into a map
449*89c4ff92SAndroid Build Coastguard Worker     using SelectionInfoPtrs = std::vector<LayerSelectionInfo*>;
450*89c4ff92SAndroid Build Coastguard Worker     std::map<PartialSubgraph*, SelectionInfoPtrs> splitMap;
451*89c4ff92SAndroid Build Coastguard Worker     for (auto& info : layerInfos)
452*89c4ff92SAndroid Build Coastguard Worker     {
453*89c4ff92SAndroid Build Coastguard Worker         if (info.second.m_IsSelected)
454*89c4ff92SAndroid Build Coastguard Worker         {
455*89c4ff92SAndroid Build Coastguard Worker             auto it = splitMap.find(info.second.m_Subgraph->GetRepresentative());
456*89c4ff92SAndroid Build Coastguard Worker             if (it == splitMap.end())
457*89c4ff92SAndroid Build Coastguard Worker             {
458*89c4ff92SAndroid Build Coastguard Worker                 splitMap.insert(
459*89c4ff92SAndroid Build Coastguard Worker                     std::make_pair(info.second.m_Subgraph->GetRepresentative(), SelectionInfoPtrs{&info.second}));
460*89c4ff92SAndroid Build Coastguard Worker             }
461*89c4ff92SAndroid Build Coastguard Worker             else
462*89c4ff92SAndroid Build Coastguard Worker             {
463*89c4ff92SAndroid Build Coastguard Worker                 it->second.push_back(&info.second);
464*89c4ff92SAndroid Build Coastguard Worker             }
465*89c4ff92SAndroid Build Coastguard Worker         }
466*89c4ff92SAndroid Build Coastguard Worker     }
467*89c4ff92SAndroid Build Coastguard Worker 
468*89c4ff92SAndroid Build Coastguard Worker     // Now each entry in splitMap represents a subgraph
469*89c4ff92SAndroid Build Coastguard Worker     Subgraphs result;
470*89c4ff92SAndroid Build Coastguard Worker     for (auto& splitGraph : splitMap)
471*89c4ff92SAndroid Build Coastguard Worker     {
472*89c4ff92SAndroid Build Coastguard Worker         SubgraphView::IInputSlots inputs;
473*89c4ff92SAndroid Build Coastguard Worker         SubgraphView::IOutputSlots outputs;
474*89c4ff92SAndroid Build Coastguard Worker         SubgraphView::IConnectableLayers layers;
475*89c4ff92SAndroid Build Coastguard Worker         for (auto&& infoPtr : splitGraph.second)
476*89c4ff92SAndroid Build Coastguard Worker         {
477*89c4ff92SAndroid Build Coastguard Worker             infoPtr->CollectNonSelectedInputs(layerInfos, inputs);
478*89c4ff92SAndroid Build Coastguard Worker             infoPtr->CollectNonSelectedOutputSlots(layerInfos, outputs);
479*89c4ff92SAndroid Build Coastguard Worker             layers.push_back(infoPtr->m_Layer);
480*89c4ff92SAndroid Build Coastguard Worker         }
481*89c4ff92SAndroid Build Coastguard Worker 
482*89c4ff92SAndroid Build Coastguard Worker         // Sort lists into deterministic order, not relying on pointer values which may be different on each execution.
483*89c4ff92SAndroid Build Coastguard Worker         // This makes debugging the optimised graph much easier as subsequent stages can also be deterministic.
484*89c4ff92SAndroid Build Coastguard Worker         std::sort(inputs.begin(), inputs.end(), [](const IInputSlot* a, const IInputSlot* b)
485*89c4ff92SAndroid Build Coastguard Worker         {
486*89c4ff92SAndroid Build Coastguard Worker             auto* castA = PolymorphicDowncast<const InputSlot*>(a);
487*89c4ff92SAndroid Build Coastguard Worker             auto* castB = PolymorphicDowncast<const InputSlot*>(b);
488*89c4ff92SAndroid Build Coastguard Worker             const LayerGuid guidA = castA->GetOwningLayer().GetGuid();
489*89c4ff92SAndroid Build Coastguard Worker             const LayerGuid guidB = castB->GetOwningLayer().GetGuid();
490*89c4ff92SAndroid Build Coastguard Worker             if (guidA < guidB)
491*89c4ff92SAndroid Build Coastguard Worker             {
492*89c4ff92SAndroid Build Coastguard Worker                 return true;
493*89c4ff92SAndroid Build Coastguard Worker             }
494*89c4ff92SAndroid Build Coastguard Worker             else if (guidA == guidB)
495*89c4ff92SAndroid Build Coastguard Worker             {
496*89c4ff92SAndroid Build Coastguard Worker                 return (castA->GetSlotIndex() < castB->GetSlotIndex());
497*89c4ff92SAndroid Build Coastguard Worker             }
498*89c4ff92SAndroid Build Coastguard Worker             return false;
499*89c4ff92SAndroid Build Coastguard Worker         });
500*89c4ff92SAndroid Build Coastguard Worker         std::sort(outputs.begin(), outputs.end(), [](const IOutputSlot* a, const IOutputSlot* b)
501*89c4ff92SAndroid Build Coastguard Worker         {
502*89c4ff92SAndroid Build Coastguard Worker             auto* castA = PolymorphicDowncast<const OutputSlot*>(a);
503*89c4ff92SAndroid Build Coastguard Worker             auto* castB = PolymorphicDowncast<const OutputSlot*>(b);
504*89c4ff92SAndroid Build Coastguard Worker             const LayerGuid guidA = castA->GetOwningLayer().GetGuid();
505*89c4ff92SAndroid Build Coastguard Worker             const LayerGuid guidB = castB->GetOwningLayer().GetGuid();
506*89c4ff92SAndroid Build Coastguard Worker             if (guidA < guidB)
507*89c4ff92SAndroid Build Coastguard Worker             {
508*89c4ff92SAndroid Build Coastguard Worker                 return true;
509*89c4ff92SAndroid Build Coastguard Worker             }
510*89c4ff92SAndroid Build Coastguard Worker             else if (guidA == guidB)
511*89c4ff92SAndroid Build Coastguard Worker             {
512*89c4ff92SAndroid Build Coastguard Worker                 return (a->CalculateIndexOnOwner() < b->CalculateIndexOnOwner());
513*89c4ff92SAndroid Build Coastguard Worker             }
514*89c4ff92SAndroid Build Coastguard Worker             return false;
515*89c4ff92SAndroid Build Coastguard Worker         });
516*89c4ff92SAndroid Build Coastguard Worker         layers.sort([](const IConnectableLayer* a, const IConnectableLayer* b) { return a->GetGuid() < b->GetGuid(); });
517*89c4ff92SAndroid Build Coastguard Worker 
518*89c4ff92SAndroid Build Coastguard Worker         // Create a new sub-graph with the new lists of input/output slots and layer
519*89c4ff92SAndroid Build Coastguard Worker         result.emplace_back(std::make_unique<SubgraphView>(std::move(layers),
520*89c4ff92SAndroid Build Coastguard Worker                                                            std::move(inputs),
521*89c4ff92SAndroid Build Coastguard Worker                                                            std::move(outputs)));
522*89c4ff92SAndroid Build Coastguard Worker     }
523*89c4ff92SAndroid Build Coastguard Worker 
524*89c4ff92SAndroid Build Coastguard Worker     // Sort subgraphs list into deterministic order, not relying on pointer values which may be different on each
525*89c4ff92SAndroid Build Coastguard Worker     // execution. This makes debugging the optimised graph much easier as subsequent stages can also be
526*89c4ff92SAndroid Build Coastguard Worker     // deterministic.
527*89c4ff92SAndroid Build Coastguard Worker     std::sort(result.begin(), result.end(), [](const SubgraphView::SubgraphViewPtr& a,
528*89c4ff92SAndroid Build Coastguard Worker                                                const SubgraphView::SubgraphViewPtr& b)
529*89c4ff92SAndroid Build Coastguard Worker     {
530*89c4ff92SAndroid Build Coastguard Worker         return a->GetIConnectableLayers().front()->GetGuid() < b->GetIConnectableLayers().front()->GetGuid();
531*89c4ff92SAndroid Build Coastguard Worker     });
532*89c4ff92SAndroid Build Coastguard Worker 
533*89c4ff92SAndroid Build Coastguard Worker     return result;
534*89c4ff92SAndroid Build Coastguard Worker }
535*89c4ff92SAndroid Build Coastguard Worker 
536*89c4ff92SAndroid Build Coastguard Worker } // namespace armnn
537