ArmNN
 22.02
SubgraphView.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
7 
8 #include <Graph.hpp>
9 
13 
14 #include <utility>
15 
16 namespace armnn
17 {
18 
19 namespace
20 {
21 
22 template <class C>
23 void AssertIfNullsOrDuplicates(const C& container, const std::string& errorMessage)
24 {
25  using T = typename C::value_type;
26  std::unordered_set<T> duplicateSet;
27  std::for_each(container.begin(), container.end(), [&duplicateSet, &errorMessage](const T& i)
28  {
29  // Ignore unused for release builds
30  IgnoreUnused(errorMessage);
31 
32  // Check if the item is valid
33  ARMNN_ASSERT_MSG(i, errorMessage.c_str());
34 
35  // Check if a duplicate has been found
36  ARMNN_ASSERT_MSG(duplicateSet.find(i) == duplicateSet.end(), errorMessage.c_str());
37 
38  duplicateSet.insert(i);
39  });
40 }
41 
42 } // anonymous namespace
43 
45  : m_InputSlots{}
46  , m_OutputSlots{}
47  , m_Layers(graph.begin(), graph.end())
48  , m_IConnectableLayers(graph.begin(), graph.end())
49 {
50  ArrangeBySortOrder();
51  CheckSubgraph();
52 }
53 
54 /// IConnectable Duplication to maintain backwards compatibility
56  : m_InputSlots{InputSlots{inputs.begin(), inputs.end()}}
57  , m_IInputSlots{IInputSlots{inputs.begin(), inputs.end()}}
58  , m_OutputSlots{OutputSlots{outputs.begin(), outputs.end()}}
59  , m_IOutputSlots{IOutputSlots{outputs.begin(), outputs.end()}}
60  , m_Layers(layers)
61  , m_IConnectableLayers(IConnectableLayers{layers.begin(), layers.end()})
62 {
63  ArrangeBySortOrder();
64  CheckSubgraph();
65 }
66 
67 /// IConnectable Duplication to maintain backwards compatibility
71  : m_IInputSlots{inputs}
72  , m_IOutputSlots{outputs}
73  , m_IConnectableLayers(IConnectableLayers{layers.begin(), layers.end()})
74 {
75  // Cast from IConnectableLayer to Layer for backward compatibility
76  auto f = [](IConnectableLayer* value)
77  {
78  return PolymorphicDowncast<Layer*>(value);
79  };
80  std::transform(layers.begin(), layers.end(), std::back_inserter(m_Layers), f);
81 
82  m_InputSlots.resize(inputs.size());
83  m_IInputSlots.resize(inputs.size());
84  for (unsigned int i = 0; i < inputs.size(); i++)
85  {
86  m_InputSlots.at(i) = PolymorphicDowncast<InputSlot*>(inputs[i]);
87  m_IInputSlots.at(i) = inputs[i];
88  }
89 
90  m_OutputSlots.resize(outputs.size());
91  m_IOutputSlots.resize(outputs.size());
92  for (unsigned int i = 0; i < outputs.size(); i++)
93  {
94  m_OutputSlots.at(i) = PolymorphicDowncast<OutputSlot*>(outputs[i]);
95  m_IOutputSlots.at(i) = outputs[i];
96  }
97 
98  ArrangeBySortOrder();
99  CheckSubgraph();
100 }
101 
102 /// IConnectable Duplication to maintain backwards compatibility
104  SubgraphView::IInputSlots&& inputs,
106  std::shared_ptr<SubgraphViewWorkingCopy> ptr)
107  : m_IInputSlots{inputs}
108  , m_IOutputSlots{outputs}
109  , m_IConnectableLayers(IConnectableLayers{layers.begin(), layers.end()})
110  , p_WorkingCopyImpl(std::move(ptr))
111 {
112  // Cast from IConnectableLayer to Layer for backward compatibility
113  auto f = [](IConnectableLayer* value)
114  {
115  return PolymorphicDowncast<Layer*>(value);
116  };
117  std::transform(layers.begin(), layers.end(), std::back_inserter(m_Layers), f);
118 
119  m_InputSlots.resize(inputs.size());
120  m_IInputSlots.resize(inputs.size());
121  for (unsigned int i = 0; i < inputs.size(); i++)
122  {
123  m_InputSlots.at(i) = PolymorphicDowncast<InputSlot*>(inputs[i]);
124  m_IInputSlots.at(i) = inputs[i];
125  }
126 
127  m_OutputSlots.resize(outputs.size());
128  m_IOutputSlots.resize(outputs.size());
129  for (unsigned int i = 0; i < outputs.size(); i++)
130  {
131  m_OutputSlots.at(i) = PolymorphicDowncast<OutputSlot*>(outputs[i]);
132  m_IOutputSlots.at(i) = outputs[i];
133  }
134 
135  ArrangeBySortOrder();
136  CheckSubgraph();
137 }
138 
140  : m_InputSlots(subgraph.m_InputSlots.begin(), subgraph.m_InputSlots.end())
141  , m_IInputSlots(subgraph.m_IInputSlots.begin(), subgraph.m_IInputSlots.end())
142  , m_OutputSlots(subgraph.m_OutputSlots.begin(), subgraph.m_OutputSlots.end())
143  , m_IOutputSlots(subgraph.m_IOutputSlots.begin(), subgraph.m_IOutputSlots.end())
144  , m_Layers(subgraph.m_Layers.begin(), subgraph.m_Layers.end())
145  , m_IConnectableLayers(IConnectableLayers{subgraph.m_IConnectableLayers.begin(),
146  subgraph.m_IConnectableLayers.end()})
147 {
148  ArrangeBySortOrder();
149  CheckSubgraph();
150 }
151 
153  : m_InputSlots(std::move(subgraph.m_InputSlots))
154  , m_IInputSlots(std::move(subgraph.m_IInputSlots))
155  , m_OutputSlots(std::move(subgraph.m_OutputSlots))
156  , m_IOutputSlots(std::move(subgraph.m_IOutputSlots))
157  , m_Layers(std::move(subgraph.m_Layers))
158  , m_IConnectableLayers(std::move(subgraph.m_IConnectableLayers))
159 {
160  ArrangeBySortOrder();
161  CheckSubgraph();
162 }
163 
165  : m_Layers{PolymorphicDowncast<Layer*>(layer)}
166  , m_IConnectableLayers{layer}
167 {
168  unsigned int numInputSlots = layer->GetNumInputSlots();
169  m_InputSlots.resize(numInputSlots);
170  m_IInputSlots.resize(numInputSlots);
171  for (unsigned int i = 0; i < numInputSlots; i++)
172  {
173  m_InputSlots.at(i) = PolymorphicDowncast<InputSlot*>(&(layer->GetInputSlot(i)));
174  m_IInputSlots.at(i) = &(layer->GetInputSlot(i));
175  }
176 
177  unsigned int numOutputSlots = layer->GetNumOutputSlots();
178  m_OutputSlots.resize(numOutputSlots);
179  m_IOutputSlots.resize(numOutputSlots);
180  for (unsigned int i = 0; i < numOutputSlots; i++)
181  {
182  m_OutputSlots.at(i) = PolymorphicDowncast<OutputSlot*>(&(layer->GetOutputSlot(i)));
183  m_IOutputSlots.at(i) = &(layer->GetOutputSlot(i));
184  }
185 
186  CheckSubgraph();
187 }
188 
190 {
191  m_InputSlots = std::move(other.m_InputSlots);
192  m_IInputSlots = std::move(other.m_IInputSlots);
193  m_OutputSlots = std::move(other.m_OutputSlots);
194  m_IOutputSlots = std::move(other.m_IOutputSlots);
195  m_Layers = std::move(other.m_Layers);
196  m_IConnectableLayers = std::move(other.m_IConnectableLayers);
197 
198  CheckSubgraph();
199 
200  return *this;
201 }
202 
203 void SubgraphView::CheckSubgraph()
204 {
205  // Check for invalid or duplicate input slots
206  AssertIfNullsOrDuplicates(m_InputSlots, "Sub-graphs cannot contain null or duplicate input slots");
207 
208  // Check for invalid or duplicate output slots
209  AssertIfNullsOrDuplicates(m_OutputSlots, "Sub-graphs cannot contain null or duplicate output slots");
210 
211  // Check for invalid or duplicate layers
212  AssertIfNullsOrDuplicates(m_Layers, "Sub-graphs cannot contain null or duplicate layers");
213 
214  // Check for invalid or duplicate input slots
215  AssertIfNullsOrDuplicates(m_IInputSlots, "Sub-graphs cannot contain null or duplicate IInputSlots");
216 
217  // Check for invalid or duplicate output slots
218  AssertIfNullsOrDuplicates(m_IOutputSlots, "Sub-graphs cannot contain null or duplicate IOutputSlots");
219 
220  // Check for invalid or duplicate layers
221  AssertIfNullsOrDuplicates(m_IConnectableLayers,
222  "Sub-graphs cannot contain null or duplicate IConnectableLayers");
223 }
224 
225 const SubgraphView::InputSlots& SubgraphView::GetInputSlots() const
226 {
227  return m_InputSlots;
228 }
229 
231 {
232  return m_IInputSlots;
233 }
234 
235 const SubgraphView::OutputSlots& SubgraphView::GetOutputSlots() const
236 {
237  return m_OutputSlots;
238 }
239 
241 {
242  return m_IOutputSlots;
243 }
244 
245 const InputSlot* SubgraphView::GetInputSlot(unsigned int index) const
246 {
247  return m_InputSlots.at(index);
248 }
249 
250 const IInputSlot* SubgraphView::GetIInputSlot(unsigned int index) const
251 {
252  return m_IInputSlots.at(index);
253 }
254 
255 InputSlot* SubgraphView::GetInputSlot(unsigned int index)
256 {
257  return m_InputSlots.at(index);
258 }
259 
261 {
262  return m_IInputSlots.at(index);
263 }
264 
265 const OutputSlot* SubgraphView::GetOutputSlot(unsigned int index) const
266 {
267  return m_OutputSlots.at(index);
268 }
269 
270 const IOutputSlot* SubgraphView::GetIOutputSlot(unsigned int index) const
271 {
272  return m_IOutputSlots.at(index);
273 }
274 
275 OutputSlot* SubgraphView::GetOutputSlot(unsigned int index)
276 {
277  return m_OutputSlots.at(index);
278 }
279 
281 {
282  return m_IOutputSlots.at(index);
283 }
284 
285 unsigned int SubgraphView::GetNumInputSlots() const
286 {
287  return armnn::numeric_cast<unsigned int>(m_IInputSlots.size());
288 }
289 
291 {
292  return armnn::numeric_cast<unsigned int>(m_IOutputSlots.size());
293 }
294 
295 const SubgraphView::Layers& SubgraphView::GetLayers() const
296 {
297  return m_Layers;
298 }
299 
301 {
302  return m_IConnectableLayers;
303 }
304 
305 SubgraphView::Iterator SubgraphView::begin()
306 {
307  return m_Layers.begin();
308 }
309 
310 SubgraphView::Iterator SubgraphView::end()
311 {
312  return m_Layers.end();
313 }
314 
315 // IConnectable Duplication to maintain backwards compatibility
317 {
318  return m_IConnectableLayers.begin();
319 }
320 
322 {
323  return m_IConnectableLayers.end();
324 }
325 
326 SubgraphView::ConstIterator SubgraphView::begin() const
327 {
328  return m_Layers.begin();
329 }
330 
331 SubgraphView::ConstIterator SubgraphView::end() const
332 {
333  return m_Layers.end();
334 }
335 
336 // IConnectable Duplication to maintain backwards compatibility
338 {
339  return m_IConnectableLayers.begin();
340 }
341 
343 {
344  return m_IConnectableLayers.end();
345 }
346 
347 SubgraphView::ConstIterator SubgraphView::cbegin() const
348 {
349  // Ignore deprecated call as this is internal to SubgraphView
351  return begin();
353 }
354 
355 SubgraphView::ConstIterator SubgraphView::cend() const
356 {
357  // Ignore deprecated call as this is internal to SubgraphView
359  return end();
361 }
362 
363 // IConnectable Duplication to maintain backwards compatibility
365 {
366  return beginIConnectable();
367 }
368 
370 {
371  return endIConnectable();
372 }
373 
375 {
376  m_InputSlots.clear();
377  m_OutputSlots.clear();
378  m_Layers.clear();
379 
380  m_IInputSlots.clear();
381  m_IOutputSlots.clear();
382  m_IConnectableLayers.clear();
383 }
384 
385 void SubgraphView::ArrangeBySortOrder()
386 {
387  using LayerList = std::list<Layer*>;
388  auto compareLayerPriority = [](const LayerList::value_type& layerA, const LayerList::value_type& layerB)
389  {
390  return layerA->GetPriority() < layerB->GetPriority();
391  };
392 
393  m_Layers.sort(compareLayerPriority);
394 
395  using IConnectableLayersList = std::list<IConnectableLayer*>;
396  auto compareIConnectableLayerPriority = [](const IConnectableLayersList::value_type& layerA,
397  const IConnectableLayersList::value_type& layerB)
398  {
399  return PolymorphicDowncast<Layer*>(layerA)->GetPriority() <
400  PolymorphicDowncast<Layer*>(layerB)->GetPriority();
401  };
402 
403  m_IConnectableLayers.sort(compareIConnectableLayerPriority);
404 }
405 
406 struct SubgraphView::SubgraphViewWorkingCopy
407 {
408 public:
409 
410  SubgraphViewWorkingCopy() = default;
411  SubgraphViewWorkingCopy(Graph graph)
412  : m_Graph(graph)
413  {};
414 
415  Graph m_Graph;
416 
417 };
418 
420 {
421  if (p_WorkingCopyImpl)
422  {
423  throw Exception("The SubgraphView calling GetWorkingCopy() is already a working copy. This function "
424  "should be called on original SubgraphView obtained from OptimizeSubgraphView()");
425  }
426 
427  // Create a cut down SubgraphView with underlying graph containing only the relevant layers.
428  // It needs its own underlying layers so that they can be replaced safely.
429  Graph newGraph = Graph();
430  std::unordered_map<const IConnectableLayer*, IConnectableLayer*> originalToClonedLayerMap;
431  std::list<armnn::IConnectableLayer*> originalSubgraphLayers = GetIConnectableLayers();
432 
433  auto ptr = std::make_shared<SubgraphViewWorkingCopy>(std::move(newGraph));
434  SubgraphView::IInputSlots workingCopyInputs;
435 
436  for (auto&& originalLayer : originalSubgraphLayers)
437  {
438  Layer* const layer = PolymorphicDowncast<const Layer*>(originalLayer)->Clone(ptr->m_Graph);
439  originalToClonedLayerMap.emplace(originalLayer, layer);
440  }
441 
442  // Add IInputSlots to workingCopy
443  std::vector<const IConnectableLayer*> processed;
444  for (auto originalSubgraphInputSlot : GetIInputSlots())
445  {
446  const IConnectableLayer& originalSubgraphLayer =
447  PolymorphicDowncast<InputSlot*>(originalSubgraphInputSlot)->GetOwningLayer();
448 
449  // Only need process Slots of layer once
450  if (std::find(processed.begin(), processed.end(), &originalSubgraphLayer) == processed.end())
451  {
452  IConnectableLayer* clonedLayer = originalToClonedLayerMap[&originalSubgraphLayer];
453 
454  // Add the InputSlot to WorkingCopy InputSlots
455  for (unsigned int i = 0; i < clonedLayer->GetNumInputSlots(); i++)
456  {
457  workingCopyInputs.push_back(&clonedLayer->GetInputSlot(i));
458  }
459  processed.push_back(&originalSubgraphLayer);
460  }
461  }
462  // Empty processed
463  processed.clear();
464 
465  for (auto originalSubgraphLayer : originalSubgraphLayers)
466  {
467  IConnectableLayer* const clonedLayer = originalToClonedLayerMap[originalSubgraphLayer];
468 
469  // connect all cloned layers as per original subgraph
470  for (unsigned int i = 0; i < clonedLayer->GetNumOutputSlots(); i++)
471  {
472  // OutputLayers have no OutputSlots to be connected
473  if (clonedLayer->GetType() != LayerType::Output)
474  {
475  auto& outputSlot = clonedLayer->GetOutputSlot(i);
476  for (unsigned int k = 0; k < originalSubgraphLayer->GetNumOutputSlots(); k++)
477  {
478  auto& originalOutputSlot = originalSubgraphLayer->GetOutputSlot(k);
479  for (unsigned int j = 0; j < originalOutputSlot.GetNumConnections(); j++)
480  {
481  // nextLayer is the layer with IInputSlot connected to IOutputSlot we are working on
482  const IConnectableLayer& nextLayer =
483  originalOutputSlot.GetConnection(j)->GetOwningIConnectableLayer();
484 
485  // Check the layer is in our map and so has a clonedLayer
486  if (originalToClonedLayerMap.find(&nextLayer) != originalToClonedLayerMap.end())
487  {
488  IConnectableLayer* newGraphTargetLayer = originalToClonedLayerMap[&nextLayer];
489 
490  IInputSlot& inputSlot =
491  newGraphTargetLayer->GetInputSlot(
492  PolymorphicDowncast<OutputSlot*>(
493  &originalOutputSlot)->GetConnection(j)->GetSlotIndex());
494 
495  // Then make the connection
496  outputSlot.Connect(inputSlot);
497  }
498  }
499  // Copy the tensorInfo to the clonedOutputSlot
500  outputSlot.SetTensorInfo(originalOutputSlot.GetTensorInfo());
501  }
502  }
503  }
504  }
505 
506  SubgraphView::IOutputSlots workingCopyOutputs;
507 
508  // Add IOutputSlots to workingCopy
509  for (auto outputSlot : GetIOutputSlots())
510  {
511 
512  const IConnectableLayer& originalSubgraphLayer = outputSlot->GetOwningIConnectableLayer();
513 
514  // OutputLayers have no OutputSlots to be connected
515  // Only need process Slots of layer once
516  if (originalSubgraphLayer.GetType() != LayerType::Output &&
517  std::find(processed.begin(), processed.end(), &originalSubgraphLayer) == processed.end())
518  {
519  IConnectableLayer* clonedLayer = originalToClonedLayerMap[&originalSubgraphLayer];
520 
521  // Add the OutputSlot to WorkingCopy InputSlots
522  for (unsigned int i = 0; i < clonedLayer->GetNumOutputSlots(); i++)
523  {
524  workingCopyOutputs.push_back(&clonedLayer->GetOutputSlot(i));
525  }
526  processed.push_back(&originalSubgraphLayer);
527  }
528  }
529  processed.clear();
530 
531  SubgraphView::IConnectableLayers workingCopyLayers;
532  for (auto& pair : originalToClonedLayerMap)
533  {
534  workingCopyLayers.push_back(pair.second);
535  }
536 
537  return {std::move(workingCopyLayers),
538  std::move(workingCopyInputs),
539  std::move(workingCopyOutputs),
540  ptr};
541 }
542 
544 {
545  ARMNN_ASSERT(substituteLayer != nullptr);
546  SubgraphView substituteSubgraph(substituteLayer);
547 
548  SubstituteSubgraph(subgraph, substituteSubgraph);
549 }
550 
551 void SubgraphView::SubstituteSubgraph(SubgraphView& patternSubgraph, const SubgraphView& substituteSubgraph)
552 {
553  if (!p_WorkingCopyImpl)
554  {
555  throw NullPointerException("The SubgraphView calling SubstituteSubgraphView is not a working copy. "
556  "Call this function on SubgraphView returned from SubgraphView::GetWorkingCopy()");
557  }
558 
559  // Add substitute layer to the Main graph i.e. graph in p_WorkingCopyImpl
560  auto workingCopyGraph = &p_WorkingCopyImpl->m_Graph;
561  substituteSubgraph.ForEachIConnectableLayer([workingCopyGraph](IConnectableLayer* iConnectableLayer)
562  {
563  // Search WorkingCopy Graph for substituteLayer and add if missing
564  if (std::find(std::begin(workingCopyGraph->m_Layers),
565  std::end(workingCopyGraph->m_Layers),
566  iConnectableLayer) ==
567  std::end(workingCopyGraph->m_Layers))
568  {
569  auto layer = PolymorphicDowncast<Layer*>(iConnectableLayer);
570 
571  layer->Reparent(*workingCopyGraph,
572  (workingCopyGraph->m_Layers).end());
573 
574  workingCopyGraph->m_LayersInOrder = false;
575  }
576  });
577 
578  // Replace the old connections with connections to new layer
579  workingCopyGraph->ReplaceSubgraphConnections(patternSubgraph, substituteSubgraph);
580 
581  // Update input/outputSlot pointers
582  m_IInputSlots = std::move(substituteSubgraph.m_IInputSlots);
583  m_IOutputSlots = std::move(substituteSubgraph.m_IOutputSlots);
584 
585  // Delete the old layers.
586  workingCopyGraph->EraseSubgraphLayers(patternSubgraph);
587 
588  // Sort
589  workingCopyGraph->TopologicalSort();
590 
591  // Update SubgraphView layer pointers to match those of the internal WorkingCopy layer pointers
592  m_IConnectableLayers = IConnectableLayers{ workingCopyGraph->m_Layers.begin(),
593  workingCopyGraph->m_Layers.end() };
594 }
595 
596 
597 } // namespace armnn
IConnectableLayers::iterator IConnectableLayerIterator
IConnectableLayerIterator endIConnectable()
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:66
Layers::iterator Iterator
const IOutputSlots & GetIOutputSlots() const
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
const IConnectableLayers & GetIConnectableLayers() const
const IInputSlots & GetIInputSlots() const
#define ARMNN_NO_DEPRECATE_WARN_BEGIN
Definition: Deprecated.hpp:33
SubgraphView & operator=(SubgraphView &&other)
Move-assignment operator.
OutputSlots && outputs
std::vector< OutputSlot * > OutputSlots
unsigned int GetNumInputSlots() const
IConnectableLayerIterator beginIConnectable()
Copyright (c) 2021 ARM Limited and Contributors.
void IgnoreUnused(Ts &&...)
ConstIConnectableIterator cendIConnectable() const
void SubstituteSubgraph(SubgraphView &, IConnectableLayer *)
These methods should be called on a working copy subgraph created from GetWorkingCopy.
const IOutputSlot * GetIOutputSlot(unsigned int index) const
The SubgraphView class represents a subgraph of a Graph.
OutputSlots Layers && layers
std::list< IConnectableLayer * > IConnectableLayers
#define ARMNN_NO_DEPRECATE_WARN_END
Definition: Deprecated.hpp:34
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
An output connection slot for a layer.
Definition: INetwork.hpp:40
std::vector< IOutputSlot * > IOutputSlots
void ForEachIConnectableLayer(Func func) const
std::vector< IInputSlot * > IInputSlots
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
virtual LayerType GetType() const =0
Returns the armnn::LayerType of this layer.
std::vector< InputSlot * > InputSlots
SubgraphView(Graph &graph)
Constructs a sub-graph from the entire given graph.
IConnectableLayers::const_iterator ConstIConnectableIterator
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
std::list< Layer * > Layers
SubgraphView GetWorkingCopy()
This method returns a copy of the original SubgraphView provided by OptimizeSubgraphView with a separ...
An input connection slot for a layer.
Definition: INetwork.hpp:26
Layers::const_iterator ConstIterator
const IInputSlot * GetIInputSlot(unsigned int index) const
unsigned int GetNumOutputSlots() const
ConstIConnectableIterator cbeginIConnectable() const