ArmNN
 21.02
SubgraphViewTests.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include <boost/test/unit_test.hpp>
6 
7 #include <Graph.hpp>
8 #include <SubgraphView.hpp>
10 
12 
14 #include <fstream>
15 #include <map>
16 #include <queue>
17 #include <random>
18 #include <chrono>
19 using namespace armnn;
20 
21 namespace
22 {
23 
24 bool AreAnySubgraphLayersPresentInGraph(const SubgraphView::Layers &subgraphLayers, const Graph &graph)
25 {
26  for(auto&& layer : subgraphLayers)
27  {
28  auto posInGraph = std::find(graph.begin(), graph.end(), layer);
29  if(posInGraph != graph.end())
30  {
31  return true;
32  }
33  }
34 
35  return false;
36 }
37 
38 //
39 // this helper only works if all layers where the inputs connect to are not selected
40 //
41 SubgraphView::InputSlots CreateInputsFrom(const std::vector<Layer*>& layers)
42 {
44  for (auto&& layer : layers)
45  {
46  for (auto&& it = layer->BeginInputSlots(); it != layer->EndInputSlots(); ++it)
47  {
48  result.push_back(&(*it));
49  }
50  }
51  return result;
52 }
53 
54 //
55 // this helper only works if all layers where the outputs connect to are not selected
56 //
57 SubgraphView::OutputSlots CreateOutputsFrom(const std::vector<Layer*>& layers)
58 {
60  for (auto && layer : layers)
61  {
62  for (auto&& it = layer->BeginOutputSlots(); it != layer->EndOutputSlots(); ++it)
63  {
64  result.push_back(&(*it));
65  }
66  }
67  return result;
68 }
69 
70 //
71 // this takes the inputs, outputs and layers as a copy and the move these copies into the
72 // resulting subgraph, so the pass by value is intentional
73 //
75  SubgraphView::OutputSlots&& outputs,
76  SubgraphView::Layers&& layers)
77 {
78  return std::make_unique<SubgraphView>(std::move(inputs), std::move(outputs), std::move(layers));
79 }
80 
81 template <typename T, typename Iterator>
82 std::vector<T> ToSortedArray(Iterator begin, Iterator end)
83 {
84  std::vector<T> result(begin, end);
85  std::sort(result.begin(), result.end());
86  return result;
87 }
88 
89 template <typename T>
90 void CompareVectors(const std::vector<T>& result, const std::vector<T>& expected)
91 {
92  BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
93 }
94 
95 void CompareSubgraphViews(SubgraphViewSelector::SubgraphViewPtr& result,
97 {
98  // expect both to be valid subgraphs
99  BOOST_TEST((result.get() != nullptr));
100  BOOST_TEST((expected.get() != nullptr));
101 
102  if (result.get() != nullptr && expected.get() != nullptr)
103  {
104  // try to detect all other obvious errors too, mainly because here
105  // we can get a nicer error message from boost, the collection test
106  // also report error for these
107  BOOST_TEST(result->GetInputSlots().size() == expected->GetInputSlots().size());
108  BOOST_TEST(result->GetOutputSlots().size() == expected->GetOutputSlots().size());
109  BOOST_TEST(result->GetLayers().size() == expected->GetLayers().size());
110 
111  auto resultLayers = ToSortedArray<Layer *>(result->GetLayers().begin(),
112  result->GetLayers().end());
113  auto expectedLayers = ToSortedArray<Layer *>(expected->GetLayers().begin(),
114  expected->GetLayers().end());
115  CompareVectors(resultLayers, expectedLayers);
116 
117  auto resultInputs = ToSortedArray<InputSlot *>(result->GetInputSlots().begin(),
118  result->GetInputSlots().end());
119  auto expectedInputs = ToSortedArray<InputSlot *>(expected->GetInputSlots().begin(),
120  expected->GetInputSlots().end());
121  CompareVectors(resultInputs, expectedInputs);
122 
123  auto resultOutputs = ToSortedArray<OutputSlot *>(result->GetOutputSlots().begin(),
124  result->GetOutputSlots().end());
125  auto expectedOutputs = ToSortedArray<OutputSlot *>(expected->GetOutputSlots().begin(),
126  expected->GetOutputSlots().end());
127  CompareVectors(resultOutputs, expectedOutputs);
128  }
129 }
130 
131 } // namespace <anonymous>
132 
133 BOOST_AUTO_TEST_SUITE(SubgraphSubstitution)
134 
135 BOOST_AUTO_TEST_CASE(SingleInputSingleOutput)
136 {
137  // Construct graph
138  Graph graph;
139 
140  Layer* const inputLayer = graph.AddLayer<InputLayer>(0, "input");
141 
142  Convolution2dDescriptor convDescriptor;
143  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
144  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
145 
146  Layer* const outputLayer = graph.AddLayer<OutputLayer>(0, "output");
147 
148  inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
149  convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
150  convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
151 
152  // Construct sub-graph
154  CreateOutputsFrom({convLayer2}),
155  {});
156 
157  // Save sub-graph connections for comparison after substitution
158  IOutputSlot* subgraphInputConn = subgraph->GetInputSlot(0)->GetConnection();
159  IInputSlot* subgraphOutputConn = subgraph->GetOutputSlot(0)->GetConnection(0);
160 
161  // Construct dummy pre-compiled layer
162  PreCompiledDescriptor preCompiledDescriptor(1, 1);
163  Layer* const preCompiledLayer = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor, "pre-compiled");
164 
165  // Substitute sub-graph with pre-compiled layer
166  graph.SubstituteSubgraph(*subgraph, preCompiledLayer);
167 
168  // Check that connections are correct after substitution
169  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn);
170  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn);
171 }
172 
173 BOOST_AUTO_TEST_CASE(SingleInputSingleOutputSubstituteGraph)
174 {
175  // Construct graph
176  Graph graph;
177 
178  Layer* const inputLayer = graph.AddLayer<InputLayer>(0, "input");
179 
180  Convolution2dDescriptor convDescriptor;
181  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
182  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
183 
184  Layer* const outputLayer = graph.AddLayer<OutputLayer>(0, "output");
185 
186  inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
187  convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
188  convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
189 
190  // Construct sub-graph
192  CreateOutputsFrom({convLayer2}),
193  {});
194 
195  // Save sub-graph connections for comparison after substitution
196  IOutputSlot* subgraphInputConn = subgraph->GetInputSlot(0)->GetConnection();
197  IInputSlot* subgraphOutputConn = subgraph->GetOutputSlot(0)->GetConnection(0);
198 
199  // Construct second graph with a single pre-compiled layer
200  Graph substituteGraph;
201  PreCompiledDescriptor preCompiledDescriptor(1, 1);
202  Layer* const preCompiledLayer = substituteGraph.AddLayer<PreCompiledLayer>(preCompiledDescriptor, "pre-compiled");
203 
204  SubgraphViewSelector::SubgraphViewPtr substituteSubgraph =
205  CreateSubgraphViewFrom(CreateInputsFrom({preCompiledLayer}),
206  CreateOutputsFrom({preCompiledLayer}),
207  {preCompiledLayer});
208  // Substitute subgraph with pre-compiled layer
209  graph.SubstituteSubgraph(*subgraph, *substituteSubgraph);
210 
211  // Check that connections are correct after substitution
212  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn);
213  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn);
214 }
215 
216 BOOST_AUTO_TEST_CASE(MultiInputSingleOutput)
217 {
218  // Construct graph
219  Graph graph;
220 
221  Layer* const inputLayer = graph.AddLayer<InputLayer>(0, "input");
222 
223  ViewsDescriptor splitterDescriptor(2);
224  Layer* const splitterLayer = graph.AddLayer<SplitterLayer>(splitterDescriptor, "splitter");
225 
226  Convolution2dDescriptor convDescriptor;
227  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
228  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
229 
230  OriginsDescriptor concatDescriptor(2);
231  Layer* const concatLayer = graph.AddLayer<ConcatLayer>(concatDescriptor, "concat");
232 
233  Layer* const outputLayer = graph.AddLayer<OutputLayer>(0, "output");
234 
235  inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0));
236  splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
237  splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0));
238  convLayer1->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(0));
239  convLayer2->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(1));
240  concatLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
241 
242  // Construct sub-graph
244  CreateOutputsFrom({concatLayer}),
245  {});
246 
247  // Save sub-graph connections for comparison after substitution
248  IOutputSlot* subgraphInputConn1 = subgraph->GetInputSlot(0)->GetConnection();
249  IOutputSlot* subgraphInputConn2 = subgraph->GetInputSlot(1)->GetConnection();
250 
251  IInputSlot* subgraphOutputConn = subgraph->GetOutputSlot(0)->GetConnection(0);
252 
253  // Construct dummy pre-compiled layer
254  PreCompiledDescriptor preCompiledDescriptor(2, 1);
255  Layer* const preCompiledLayer = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor, "pre-compiled");
256 
257  // Substitute sub-graph with pre-compiled layer
258  graph.SubstituteSubgraph(*subgraph, preCompiledLayer);
259 
260  // Check that connections are correct after substitution
261  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1);
262  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(1).GetConnection(), subgraphInputConn2);
263 
264  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn);
265 }
266 
267 BOOST_AUTO_TEST_CASE(SingleInputMultiOutput)
268 {
269  // Construct graph
270  Graph graph;
271 
272  Layer* const inputLayer = graph.AddLayer<InputLayer>(0, "input");
273 
274  Convolution2dDescriptor convDescriptor;
275  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
276  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
277  OriginsDescriptor concatDescriptor(2);
278  Layer* const concatLayer = graph.AddLayer<ConcatLayer>(concatDescriptor, "concat");
279  Layer* const outputLayer = graph.AddLayer<OutputLayer>(0, "output");
280 
281  ViewsDescriptor splitterDescriptor(2);
282  Layer* const splitterLayer = graph.AddLayer<SplitterLayer>(splitterDescriptor, "splitter");
283 
284  inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0));
285  splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
286  splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0));
287  convLayer1->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(0));
288  convLayer2->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(1));
289  concatLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
290 
291  // Construct sub-graph
293  CreateOutputsFrom({convLayer1, convLayer2}),
294  {});
295 
296  // Save sub-graph connections for comparison after substitution
297  IOutputSlot* subgraphInputConn1 = subgraph->GetInputSlot(0)->GetConnection();
298 
299  IInputSlot* subgraphOutputConn1 = subgraph->GetOutputSlot(0)->GetConnection(0);
300  IInputSlot* subgraphOutputConn2 = subgraph->GetOutputSlot(1)->GetConnection(0);
301 
302  // Construct dummy pre-compiled layer
303  PreCompiledDescriptor preCompiledDescriptor(1, 2);
304  Layer* const preCompiledLayer = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor, "pre-compiled");
305 
306  // Substitute sub-graph with pre-compiled layer
307  graph.SubstituteSubgraph(*subgraph, preCompiledLayer);
308 
309  // Check that connections are correct after substitution
310  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1);
311 
312  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn1);
313  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(1).GetConnection(0), subgraphOutputConn2);
314 }
315 
316 BOOST_AUTO_TEST_CASE(MultiInputMultiOutput)
317 {
318  // Construct graph
319  Graph graph;
320 
321  Layer* const inputLayer = graph.AddLayer<InputLayer>(0, "input");
322 
323  ViewsDescriptor splitterDescriptor(2);
324  Layer* const splitterLayer = graph.AddLayer<SplitterLayer>(splitterDescriptor, "splitter");
325 
326  Convolution2dDescriptor convDescriptor;
327  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
328  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
329 
330  OriginsDescriptor concatDescriptor(2);
331  Layer* const concatLayer = graph.AddLayer<ConcatLayer>(concatDescriptor, "concat");
332 
333  Layer* const outputLayer = graph.AddLayer<OutputLayer>(0, "output");
334 
335  inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0));
336  splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
337  splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0));
338  convLayer1->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(0));
339  convLayer2->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(1));
340  concatLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
341 
342  // Construct sub-graph
344  CreateOutputsFrom({convLayer1, convLayer2}),
345  {});
346 
347  // Save sub-graph connections for comparison after substitution
348  IOutputSlot* subgraphInputConn1 = subgraph->GetInputSlot(0)->GetConnection();
349  IOutputSlot* subgraphInputConn2 = subgraph->GetInputSlot(1)->GetConnection();
350 
351  IInputSlot* subgraphOutputConn1 = subgraph->GetOutputSlot(0)->GetConnection(0);
352  IInputSlot* subgraphOutputConn2 = subgraph->GetOutputSlot(1)->GetConnection(0);
353 
354  // Construct dummy pre-compiled layer
355  PreCompiledDescriptor preCompiledDescriptor(2, 2);
356  Layer* const preCompiledLayer = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor, "pre-compiled");
357 
358  // Substitute sub-graph with pre-compiled layer
359  graph.SubstituteSubgraph(*subgraph, preCompiledLayer);
360 
361  // Check that connections are correct after substitution
362  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1);
363  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(1).GetConnection(), subgraphInputConn2);
364 
365  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn1);
366  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(1).GetConnection(0), subgraphOutputConn2);
367 }
368 
369 BOOST_AUTO_TEST_CASE(EraseReplacedLayers)
370 {
371  // Construct graph
372  Graph graph;
373 
374  graph.AddLayer<InputLayer>(0, "input");
375 
376  ViewsDescriptor splitterDescriptor(2);
377  Layer* const splitterLayer = graph.AddLayer<SplitterLayer>(splitterDescriptor, "splitter");
378 
379  Convolution2dDescriptor convDescriptor;
380  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
381  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
382 
383  OriginsDescriptor concatDescriptor(2);
384  Layer* const concatLayer = graph.AddLayer<ConcatLayer>(concatDescriptor, "concat");
385 
386  graph.AddLayer<OutputLayer>(0, "output");
387 
388  // Construct sub-graph
390  {},
391  {splitterLayer,
392  convLayer1,
393  convLayer2,
394  concatLayer});
395 
396  // Construct dummy pre-compiled layer
397  PreCompiledDescriptor preCompiledDescriptor(0, 0);
398  Layer* const preCompiledLayer = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor, "pre-compiled");
399 
400  // Save sub-graph layers for later verification
401  const SubgraphView::Layers subgraphLayers = subgraph->GetLayers();
402 
403  // Substitute sub-graph with pre-compiled layer
404  graph.SubstituteSubgraph(*subgraph, preCompiledLayer);
405 
406  // Check that the layers belonging to the sub-graph have been erased from the graph after substitution
407  BOOST_CHECK(!AreAnySubgraphLayersPresentInGraph(subgraphLayers, graph));
408 }
409 
411 
412 BOOST_AUTO_TEST_SUITE(SubgraphSelection)
413 
414 BOOST_AUTO_TEST_CASE(SubgraphForEmptyGraph)
415 {
416  Graph graph;
417  SubgraphView subgraph(graph);
418 
419  BOOST_TEST(subgraph.GetInputSlots().empty());
420  BOOST_TEST(subgraph.GetOutputSlots().empty());
421  BOOST_TEST(subgraph.GetLayers().empty());
422 }
423 
424 BOOST_AUTO_TEST_CASE(SubgraphForEntireGraph)
425 {
426  Graph graph;
427 
428  auto output = graph.AddLayer<OutputLayer>(0, "output");
429  auto mid0 = graph.InsertNewLayer<ActivationLayer>(output->GetInputSlot(0),
431  "mid0");
432  auto mid1 = graph.InsertNewLayer<ActivationLayer>(mid0->GetInputSlot(0),
434  "mid1");
435  graph.InsertNewLayer<InputLayer>(mid1->GetInputSlot(0), 0, "input");
436 
437  SubgraphView subgraph(graph);
438 
439  BOOST_TEST(subgraph.GetInputSlots().empty());
440  BOOST_TEST(subgraph.GetOutputSlots().empty());
441  BOOST_TEST(subgraph.GetLayers().size() == graph.GetNumLayers());
442 }
443 
444 BOOST_AUTO_TEST_CASE(NoSubgraphsForNoMatch)
445 {
446  Graph graph;
447 
448  auto output = graph.AddLayer<OutputLayer>(0, "output");
449  graph.InsertNewLayer<InputLayer>(output->GetInputSlot(0), 0, "input");
450 
452  SubgraphViewSelector::SelectSubgraphs(graph, [](const Layer &) { return false; });
453 
454  BOOST_TEST(subgraphs.empty());
455 }
456 
457 BOOST_AUTO_TEST_CASE(OneSubgraphsSelectedASingleMatch)
458 {
459  Graph graph;
460 
461  auto output = graph.AddLayer<OutputLayer>(0, "output");
462  graph.InsertNewLayer<InputLayer>(output->GetInputSlot(0), 0, "input");
463 
466  graph,
467  // select the output layer only
468  [](const Layer & l)
469  {
470  bool isOutput = l.GetNameStr().compare("output") == 0;
471  return isOutput;
472  });
473 
474  BOOST_TEST(subgraphs.size() == 1);
475  if (subgraphs.size() == 1)
476  {
477  auto expected = CreateSubgraphViewFrom(CreateInputsFrom({output}),
478  // outputs of 'output' will be empty
479  CreateOutputsFrom({output}),
480  {output});
481 
482  CompareSubgraphViews(subgraphs[0], expected);
483  }
484 }
485 
486 BOOST_AUTO_TEST_CASE(MultipleLayersSelectedInTheMiddle)
487 {
488  Graph graph;
489 
490  auto output = graph.AddLayer<OutputLayer>(0, "output");
491  auto mid0 = graph.InsertNewLayer<ActivationLayer>(output->GetInputSlot(0),
493  "mid0");
494  auto mid1 = graph.InsertNewLayer<ActivationLayer>(mid0->GetInputSlot(0),
496  "mid1");
497  graph.InsertNewLayer<InputLayer>(mid1->GetInputSlot(0), 0, "input");
498 
501  graph,
502  // select the middle layers only
503  [](const Layer & l)
504  {
505  bool toSelect = (l.GetType() == LayerType::Activation);
506  return toSelect;
507  });
508 
509  BOOST_TEST(subgraphs.size() == 1);
510  if (subgraphs.size() == 1)
511  {
512  auto expected = CreateSubgraphViewFrom(CreateInputsFrom({mid1}),
513  CreateOutputsFrom({mid0}),
514  {mid1, mid0});
515 
516  CompareSubgraphViews(subgraphs[0], expected);
517  }
518 }
519 
520 BOOST_AUTO_TEST_CASE(DisjointGraphs)
521 {
522  // The input graph has two disjoint sections and all layers are selected.
523  // This should result in two subgraphs being produced.
524  Graph graph;
525 
526  // the graph is constructed in reverse order
527  auto o0 = graph.AddLayer<OutputLayer>(0, "output0");
528  auto n0 = graph.InsertNewLayer<ActivationLayer>(o0->GetInputSlot(0), ActivationDescriptor{}, "intermediate0");
529  auto i0 = graph.InsertNewLayer<InputLayer>(n0->GetInputSlot(0), 0, "input0");
530 
531  auto o1 = graph.AddLayer<OutputLayer>(1, "output1");
532  auto n1 = graph.InsertNewLayer<ActivationLayer>(o1->GetInputSlot(0), ActivationDescriptor{}, "intermediate1");
533  auto i1 = graph.InsertNewLayer<InputLayer>(n1->GetInputSlot(0), 1, "input1");
534 
537  // select the middle layers only
538  [](const Layer&) {
539  return true;
540  });
541 
542  // expected results to test against
543  auto expected1 = CreateSubgraphViewFrom({}, {}, { o0, n0, i0 });
544  auto expected2 = CreateSubgraphViewFrom({}, {}, { o1, n1, i1 });
545  BOOST_TEST(subgraphs.size() == 2);
546  if (subgraphs.size() == 2)
547  {
548  BOOST_TEST((subgraphs[0] != nullptr));
549  BOOST_TEST((subgraphs[1] != nullptr));
550  if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr)
551  {
552  if (std::find(subgraphs[0]->GetLayers().begin(), subgraphs[0]->GetLayers().end(), i0) !=
553  subgraphs[0]->GetLayers().end())
554  {
555  CompareSubgraphViews(subgraphs[0], expected1);
556  CompareSubgraphViews(subgraphs[1], expected2);
557  }
558  else
559  {
560  CompareSubgraphViews(subgraphs[0], expected2);
561  CompareSubgraphViews(subgraphs[1], expected1);
562  }
563  }
564  }
565 }
566 
567 BOOST_AUTO_TEST_CASE(IslandInTheMiddle)
568 {
569  // This case represent the scenario when a non-selected X1 node placed in the middle
570  // of the selected M* nodes.
571  // This checks that we don't merge M6 and M3 and create a dependency loop.
572  /*
573  M0
574  / \
575  M1 M4
576  | |
577  M2 X1 < the island in the middle !
578  | |
579  M3 M5
580  \ /
581  M6
582  */
583  Graph graph;
584 
585  OriginsDescriptor concatDescriptor(2);
586  auto m6 = graph.AddLayer<ConcatLayer>(concatDescriptor, "m6");
587  auto m3 = graph.InsertNewLayer<ActivationLayer>(m6->GetInputSlot(0),
589  "m3");
590  auto m2 = graph.InsertNewLayer<ActivationLayer>(m3->GetInputSlot(0),
592  "m2");
593  auto m1 = graph.InsertNewLayer<ActivationLayer>(m2->GetInputSlot(0),
595  "m1");
596  auto m0 = graph.InsertNewLayer<InputLayer>(m1->GetInputSlot(0), 0, "m0");
597 
598  auto m5 = graph.InsertNewLayer<ActivationLayer>(m6->GetInputSlot(1),
600  "m5");
601  auto x1 = graph.InsertNewLayer<ActivationLayer>(m5->GetInputSlot(0),
603  "x1");
604  auto m4 = graph.InsertNewLayer<ActivationLayer>(x1->GetInputSlot(0),
606  "m4");
607 
608  // Connect the other branch to the input layer
609  m0->GetOutputSlot(0).Connect(m4->GetInputSlot(0));
610 
611  // All selected 'M*' layers will be of Activation type
614  graph,
615  // select the middle layers only
616  [](const Layer& l)
617  {
618  bool toSelect = std::string(l.GetName())[0] == 'm';
619  return toSelect;
620  });
621 
622  // expected results to test against
623  auto largerSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({ m0 }),
624  CreateOutputsFrom({ m3, m4 }),
625  { m0, m1, m2, m3, m4 });
626 
627  auto smallerSubgraph =
628  CreateSubgraphViewFrom(std::vector<InputSlot*>{ &m5->GetInputSlot(0), & m6->GetInputSlot(0) },
629  std::vector<OutputSlot*>{},
630  { m5, m6 });
631 
632  BOOST_TEST(subgraphs.size() == 2);
633  if (subgraphs.size() == 2)
634  {
635  // we need to have valid subgraph pointers here
636  BOOST_TEST((subgraphs[0] != nullptr));
637  BOOST_TEST((subgraphs[1] != nullptr));
638 
639  if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr)
640  {
641  // sort the subgraphs by layer size, so it is simpler to test
642  std::sort(subgraphs.begin(), subgraphs.end(),
644  {
645  return (lhs->GetLayers().size() < rhs->GetLayers().size());
646  }
647  );
648 
649  BOOST_TEST(subgraphs[0]->GetLayers().size() == 2);
650  BOOST_TEST(subgraphs[1]->GetLayers().size() == 5);
651 
652  CompareSubgraphViews(subgraphs[0], smallerSubgraph);
653  CompareSubgraphViews(subgraphs[1], largerSubgraph);
654  }
655  }
656 }
657 
658 BOOST_AUTO_TEST_CASE(MultipleSimpleSubgraphs)
659 {
660  // This test case represents the scenario when we have two distinct subgraphs
661  // in a simple linear network. The selected nodes are the M* and the
662  // non-selected ones are the X*
663  //
664  // X1 -> M1 -> M2 -> X2 -> M3 -> X3
665  //
666  // The expected results is two subgraphs, one with {M1, M2} and another one
667  // with {M3}
668  //
669  Graph graph;
670 
671  // the graph is constructed in reverse order
672  auto x3 = graph.AddLayer<OutputLayer>(0, "output");
673  auto m3 = graph.InsertNewLayer<ActivationLayer>(x3->GetInputSlot(0),
675  "m3");
676  auto x2 = graph.InsertNewLayer<Convolution2dLayer>(m3->GetInputSlot(0),
678  "x2");
679  auto m2 = graph.InsertNewLayer<ActivationLayer>(x2->GetInputSlot(0),
681  "m2");
682  auto m1 = graph.InsertNewLayer<ActivationLayer>(m2->GetInputSlot(0),
684  "m1");
685  graph.InsertNewLayer<InputLayer>(m1->GetInputSlot(0), 0, "x1");
686 
687  // All selected 'M*' layers will be of Activation type
690  graph,
691  // select the middle layers only
692  [](const Layer & l)
693  {
694  bool toSelect = (l.GetType() == LayerType::Activation);
695  return toSelect;
696  });
697 
698  // expected results to test against
699  auto largerSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({m1}),
700  CreateOutputsFrom({m2}),
701  {m1, m2});
702 
703  auto smallerSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({m3}),
704  CreateOutputsFrom({m3}),
705  {m3});
706 
707  BOOST_TEST(subgraphs.size() == 2);
708  if (subgraphs.size() == 2)
709  {
710  // we need to have valid subgraph pointers here
711  BOOST_TEST((subgraphs[0] != nullptr));
712  BOOST_TEST((subgraphs[1] != nullptr));
713 
714  if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr)
715  {
716  // sort the subgraphs by layer size, so it is simpler to test
717  std::sort(subgraphs.begin(), subgraphs.end(),
719  {
720  return (lhs->GetLayers().size() < rhs->GetLayers().size());
721  }
722  );
723 
724  BOOST_TEST(subgraphs[0]->GetLayers().size() == 1);
725  BOOST_TEST(subgraphs[1]->GetLayers().size() == 2);
726 
727  CompareSubgraphViews(subgraphs[0], smallerSubgraph);
728  CompareSubgraphViews(subgraphs[1], largerSubgraph);
729  }
730  }
731 }
732 
733 BOOST_AUTO_TEST_CASE(SimpleLinearTest)
734 {
735  //X1 -> M1 -> M2 -> X2
736  //Where the input slots of M1 and the output slots of M2 are to be the sub graph boundaries.
737  Graph graph;
738 
739  ActivationDescriptor activationDefaults;
740 
741  auto layerX1 = graph.AddLayer<InputLayer>(0, "layerX1");
742  auto layerX2 = graph.AddLayer<OutputLayer>(0, "layerX2");
743  auto layerM1 = graph.AddLayer<ActivationLayer>(activationDefaults, "layerM1");
744  auto layerM2 = graph.AddLayer<ActivationLayer>(activationDefaults, "layerM2");
745 
746  // X1
747  // |
748  // M1
749  // |
750  // M2
751  // |
752  // X2
753 
754  layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0));
755  layerM1->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0));
756  layerM2->GetOutputSlot(0).Connect(layerX2->GetInputSlot(0));
757 
760  graph,
761  // select the activation layers M1 and M2
762  [](const Layer & l)
763  {
764  bool toSelect = (l.GetType() == LayerType::Activation);
765  return toSelect;
766  });
767 
768  BOOST_CHECK(subgraphs.size() == 1);
769  if(subgraphs.size() == 1)
770  {
771  auto expected = CreateSubgraphViewFrom(CreateInputsFrom({layerM1}),
772  CreateOutputsFrom({layerM2}),
773  {layerM1, layerM2});
774 
775  CompareSubgraphViews(subgraphs[0], expected);
776  }
777 }
778 
779 BOOST_AUTO_TEST_CASE(MultiInputSingleOutput)
780 {
781  //X1 -> M1 -> M3 -> X3
782  //X2 -> M2 -> M3 -> X3
783  //Where the input slots of {M1, M2} and the output slots of M3 are to be the subgraph boundaries.
784  Graph graph;
785 
786  ActivationDescriptor activationDefaults;
787 
788  auto layerX1 = graph.AddLayer<InputLayer>(0, "layerX1");
789  auto layerX2 = graph.AddLayer<InputLayer>(1, "layerX2");
790  auto layerM1 = graph.AddLayer<ActivationLayer>(activationDefaults, "layerM1");
791  auto layerM2 = graph.AddLayer<ActivationLayer>(activationDefaults, "layerM2");
792  auto layerM3 = graph.AddLayer<AdditionLayer>("layerM3");
793  auto layerX3 = graph.AddLayer<OutputLayer>(0, "layerX3");
794 
795  // X1 X2
796  // | |
797  // M1 M2
798  // \ |
799  // \ |
800  // \|
801  // M3
802  // |
803  // |
804  // X3
805 
806  layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0));
807  layerX2->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0));
808  layerM1->GetOutputSlot(0).Connect(layerM3->GetInputSlot(0));
809  layerM2->GetOutputSlot(0).Connect(layerM3->GetInputSlot(1));
810  layerM3->GetOutputSlot(0).Connect(layerX3->GetInputSlot(0));
811 
814  graph,
815  // select Activation and Addition Layers M1, M2 and M3
816  [](const Layer & l)
817  {
818  bool toSelect = (l.GetType() == LayerType::Activation
819  || l.GetType() == LayerType::Addition);
820  return toSelect;
821  });
822 
823  BOOST_CHECK(subgraphs.size() == 1);
824  if (subgraphs.size() == 1)
825  {
826  auto expected = CreateSubgraphViewFrom(CreateInputsFrom({layerM1, layerM2}),
827  CreateOutputsFrom({layerM3}),
828  {layerM1, layerM2, layerM3});
829 
830  CompareSubgraphViews(subgraphs[0], expected);
831  }
832 }
833 
834 BOOST_AUTO_TEST_CASE(SingleInputMultiOutput)
835 {
836  //X1 -> M1 -> M2 -> X2
837  //X1 -> M1 -> M3 -> X3
838  //Where the input slots of M1 and the output slots of {M2, M3} are to be the subgraph boundaries.
839  Graph graph;
840 
841  ActivationDescriptor activationDefaults;
842  ViewsDescriptor viewDefaults(2,4);
843 
844  Layer* layerX1 = graph.AddLayer<InputLayer>(0, "layerX1");
845  Layer* layerM1 = graph.AddLayer<SplitterLayer>(viewDefaults, "layerM1");
846  Layer* layerM2 = graph.AddLayer<ActivationLayer>(activationDefaults, "layerM2");
847  Layer* layerM3 = graph.AddLayer<ActivationLayer>(activationDefaults, "layerM3");
848  Layer* layerX2 = graph.AddLayer<OutputLayer>(0, "layerX2");
849  Layer* layerX3 = graph.AddLayer<OutputLayer>(1, "layerX3");
850 
851  // X1
852  // |
853  // M1
854  // /|
855  // / |
856  // / |
857  // M2 M3
858  // | |
859  // | |
860  // X2 X3
861 
862  layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0));
863  layerM1->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0));
864  layerM1->GetOutputSlot(1).Connect(layerM3->GetInputSlot(0));
865  layerM2->GetOutputSlot(0).Connect(layerX2->GetInputSlot(0));
866  layerM3->GetOutputSlot(0).Connect(layerX3->GetInputSlot(0));
867 
870  graph,
871  // select Activation and Splitter Layers M1, M2 and M3
872  [](const Layer & l)
873  {
874  bool toSelect = (l.GetType() == LayerType::Activation
875  || l.GetType() == LayerType::Splitter);
876  return toSelect;
877  });
878 
879  BOOST_CHECK(subgraphs.size() == 1);
880  if(subgraphs.size() == 1)
881  {
882  auto expected = CreateSubgraphViewFrom(CreateInputsFrom({layerM1}),
883  CreateOutputsFrom({layerM2, layerM3}),
884  {layerM1, layerM2, layerM3});
885 
886  CompareSubgraphViews(subgraphs[0], expected);
887  }
888 }
889 
890 BOOST_AUTO_TEST_CASE(MultiInputMultiOutput)
891 {
892  // This case represents the scenario with multiple inputs and multiple outputs
893  //
894  // X1 -> M1 -> M3 -> M4 -> X3
895  // X2 -> M2 -> M3 -> M5 -> X4
896  //
897  // Where the input slots of {M1, M2} and the output slots of {M4, M5} are to be the subgraph
898  // boundaries.
899 
900  Graph graph;
901 
902  ActivationDescriptor activationDefaults;
903  OriginsDescriptor concatDescriptor(2);
904 
905  auto x1 = graph.AddLayer<InputLayer>(0, "x1");
906  auto x2 = graph.AddLayer<InputLayer>(1, "x2");
907 
908  auto m1 = graph.AddLayer<ActivationLayer>(activationDefaults, "m1");
909  auto m2 = graph.AddLayer<ActivationLayer>(activationDefaults, "m2");
910  auto m3 = graph.AddLayer<ConcatLayer>(concatDescriptor, "m3");
911 
912  auto m4 = graph.AddLayer<ActivationLayer>(activationDefaults, "m4");
913  auto m5 = graph.AddLayer<ActivationLayer>(activationDefaults, "m5");
914 
915  auto x3 = graph.AddLayer<OutputLayer>(0, "x3");
916  auto x4 = graph.AddLayer<OutputLayer>(1, "x4");
917 
918  x1->GetOutputSlot(0).Connect(m1->GetInputSlot(0));
919  x2->GetOutputSlot(0).Connect(m2->GetInputSlot(0));
920 
921  m1->GetOutputSlot(0).Connect(m3->GetInputSlot(0));
922  m2->GetOutputSlot(0).Connect(m3->GetInputSlot(1));
923 
924  m3->GetOutputSlot(0).Connect(m4->GetInputSlot(0));
925  m3->GetOutputSlot(0).Connect(m5->GetInputSlot(0));
926 
927  m4->GetOutputSlot(0).Connect(x3->GetInputSlot(0));
928  m5->GetOutputSlot(0).Connect(x4->GetInputSlot(0));
929 
930 
933  graph,
934  // select Activation and Concat Layers M1, M2, M3, M4, M5
935  [](const Layer & l)
936  {
937  bool toSelect = (l.GetType() == LayerType::Activation
938  || l.GetType() == LayerType::Concat);
939  return toSelect;
940  });
941 
942 
943  BOOST_CHECK(subgraphs.size() == 1);
944  if (subgraphs.size() == 1)
945  {
946  auto expected = CreateSubgraphViewFrom(CreateInputsFrom({m1, m2}),
947  CreateOutputsFrom({m4, m5}),
948  {m1, m2, m3, m4, m5});
949 
950  CompareSubgraphViews(subgraphs[0], expected);
951  }
952 }
953 
955 {
956  // Checks that a node that has multiple choices for merge candidates (M3 in this case) correctly merges with the
957  // one that it can (M0), and doesn't merge with the ones it can't (X2 and M2).
958  //
959  // X1
960  // |
961  // M1
962  // / \'
963  // X2 M2 M0
964  // \ | /
965  // M3
966  //
967  Graph graph;
968 
969  ActivationDescriptor activationDefaults;
970  OriginsDescriptor concatDescriptor(3);
971 
972  auto x1 = graph.AddLayer<InputLayer>(0, "x1");
973  auto x2 = graph.AddLayer<ActivationLayer>(activationDefaults, "x2");
974  auto m0 = graph.AddLayer<InputLayer>(1, "m0");
975  auto m1 = graph.AddLayer<ActivationLayer>(activationDefaults, "m1");
976  auto m2 = graph.AddLayer<ActivationLayer>(activationDefaults, "m2");
977  auto m3 = graph.AddLayer<ConcatLayer>(concatDescriptor, "m3");
978 
979  x1->GetOutputSlot(0).Connect(m1->GetInputSlot(0));
980  m1->GetOutputSlot(0).Connect(x2->GetInputSlot(0));
981  m1->GetOutputSlot(0).Connect(m2->GetInputSlot(0));
982  x2->GetOutputSlot(0).Connect(m3->GetInputSlot(0));
983  m2->GetOutputSlot(0).Connect(m3->GetInputSlot(1));
984  m0->GetOutputSlot(0).Connect(m3->GetInputSlot(2));
985 
987  graph,
988  [](const Layer& l) {
989  return std::string(l.GetName())[0] == 'm';
990  });
991 
992  // expected results to test against
993  auto expectedSubgraph0 =
995  CreateInputsFrom({ m1 }),
996  std::vector<OutputSlot*>{ &m1->GetOutputSlot(0), &m2->GetOutputSlot(0) },
997  { m1, m2 });
998 
999  auto expectedSubgraph1 = CreateSubgraphViewFrom(
1000  std::vector<InputSlot*>{ &m3->GetInputSlot(0), & m3->GetInputSlot(1) },
1001  CreateOutputsFrom({ }),
1002  { m0, m3 });
1003 
1004  BOOST_TEST(subgraphs.size() == 2);
1005  if (subgraphs.size() == 2)
1006  {
1007  // we need to have valid subgraph pointers here
1008  BOOST_TEST((subgraphs[0] != nullptr));
1009  BOOST_TEST((subgraphs[1] != nullptr));
1010 
1011  if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr)
1012  {
1013  if (subgraphs[0]->GetInputSlots().size() == 1)
1014  {
1015  CompareSubgraphViews(subgraphs[0], expectedSubgraph0);
1016  CompareSubgraphViews(subgraphs[1], expectedSubgraph1);
1017  }
1018  else
1019  {
1020  CompareSubgraphViews(subgraphs[0], expectedSubgraph1);
1021  CompareSubgraphViews(subgraphs[1], expectedSubgraph0);
1022  }
1023  }
1024  }
1025 }
1026 
1027 BOOST_AUTO_TEST_CASE(PropagatedDependencies)
1028 {
1029  // Version of IslandInTheMiddle with longer chain
1030  // to make sure antecedents are propagated.
1031  /*
1032  M0
1033  / \
1034  M1 M4
1035  | |
1036  M2 X1 < the island in the middle !
1037  | |
1038  | M10
1039  | |
1040  | X2 < another island in the middle !
1041  | |
1042  M3 M5
1043  \ /
1044  M6
1045  */
1046  Graph graph;
1047 
1048  OriginsDescriptor concatDescriptor(2);
1049  auto m6 = graph.AddLayer<ConcatLayer>(concatDescriptor, "m6");
1050  auto m3 = graph.InsertNewLayer<ActivationLayer>(m6->GetInputSlot(0),
1052  "m3");
1053  auto m2 = graph.InsertNewLayer<ActivationLayer>(m3->GetInputSlot(0),
1055  "m2");
1056  auto m1 = graph.InsertNewLayer<ActivationLayer>(m2->GetInputSlot(0),
1058  "m1");
1059  auto m0 = graph.InsertNewLayer<InputLayer>(m1->GetInputSlot(0), 0, "m0");
1060 
1061  auto m5 = graph.InsertNewLayer<ActivationLayer>(m6->GetInputSlot(1),
1063  "m5");
1064  auto x2 = graph.InsertNewLayer<ActivationLayer>(m5->GetInputSlot(0), ActivationDescriptor{}, "x2");
1065  auto m10 = graph.InsertNewLayer<ActivationLayer>(x2->GetInputSlot(0), ActivationDescriptor{}, "m10");
1066  auto x1 = graph.InsertNewLayer<ActivationLayer>(m10->GetInputSlot(0),
1068  "x1");
1069  auto m4 = graph.InsertNewLayer<ActivationLayer>(x1->GetInputSlot(0),
1071  "m4");
1072 
1073  // Connect the other branch to the input layer
1074  m0->GetOutputSlot(0).Connect(m4->GetInputSlot(0));
1075 
1076  // All selected 'M*' layers will be of Activation type
1079  graph,
1080  // select the middle layers only
1081  [](const Layer& l)
1082  {
1083  bool toSelect = std::string(l.GetName())[0] == 'm';
1084  return toSelect;
1085  });
1086 
1087  // expected results to test against
1088  auto largerSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({ m0 }),
1089  CreateOutputsFrom({ m3, m4 }),
1090  { m0, m1, m2, m3, m4 });
1091 
1092  auto mediumSubgraph = CreateSubgraphViewFrom(std::vector<InputSlot*>{ &m5->GetInputSlot(0), &m6->GetInputSlot(0) },
1093  std::vector<OutputSlot*>{}, { m5, m6 });
1094 
1095  auto smallerSubgraph =
1096  CreateSubgraphViewFrom(CreateInputsFrom({ m10 }), CreateOutputsFrom({ m10 }), { m10 });
1097 
1098  BOOST_TEST(subgraphs.size() == 3);
1099  if (subgraphs.size() == 3)
1100  {
1101  // we need to have valid subgraph pointers here
1102  BOOST_TEST((subgraphs[0] != nullptr));
1103  BOOST_TEST((subgraphs[1] != nullptr));
1104  BOOST_TEST((subgraphs[2] != nullptr));
1105 
1106  if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr && subgraphs[2].get() != nullptr)
1107  {
1108  // sort the subgraphs by layer size, so it is simpler to test
1109  std::sort(subgraphs.begin(), subgraphs.end(),
1111  {
1112  return (lhs->GetLayers().size() < rhs->GetLayers().size());
1113  }
1114  );
1115 
1116  CompareSubgraphViews(subgraphs[0], smallerSubgraph);
1117  CompareSubgraphViews(subgraphs[1], mediumSubgraph);
1118  CompareSubgraphViews(subgraphs[2], largerSubgraph);
1119  }
1120  }
1121 }
1122 
1124 {
1125  // Creates random networks, splits them into subgraphs and checks the resulting subgraphs obey the required
1126  // dependency rules. We can easily generate very large networks which helps cover corner cases the other
1127  // small, manually crafted tests have missed. We can also use this to measure performance on large networks.
1128  constexpr bool debug = false; // Enable this to dump dot files and performance timings.
1129 
1130  std::mt19937 randomGenerator;
1131 
1132  // Helper function to get a random number in [0, maxExclusive)
1133  auto GetRandom = [&randomGenerator](auto maxExclusive) {
1134  // Note we could use uniform_int_distribution here, but that gives inconsistent results across platforms
1135  // which makes it harder to reproduce results.
1136  // It appears that uniform_real_distribution is consistent across MSVC and gcc so we use that and round it.
1137  std::uniform_real_distribution<float> uniform(0.0f, 1.0f);
1138  return static_cast<decltype(maxExclusive)>(uniform(randomGenerator) * static_cast<float>(maxExclusive));
1139  };
1140  // Helper function to get a bool that has probability 'trueProb' of being true.
1141  auto GetRandomFlag = [&randomGenerator](float trueProb) {
1142  std::uniform_real_distribution<float> uniform(0.0f, 1.0f);
1143  return uniform(randomGenerator) < trueProb;
1144  };
1145 
1146  constexpr uint32_t numTests = 100;
1147  for (uint32_t testIdx = 0; testIdx < numTests; ++testIdx)
1148  {
1149  randomGenerator.seed(testIdx); // Set a deterministic seed for reproducibility.
1150 
1151  // Create random graph
1152  Graph graph;
1153  {
1154  // First add the layers, without any connections. The following random constants determine the number of
1155  // each layer to add, along with the chance that each layer will be 'supported' (i.e. selected for
1156  // inclusion in the resulting subgraphs).
1157  uint32_t numInputs = 1 + GetRandom(4u);
1158  uint32_t numConstants = 1 + GetRandom(4u);
1159  uint32_t numOutputs = 1 + GetRandom(4u);
1160  uint32_t numConcats = 0 + GetRandom(500u);
1161  uint32_t numSplits = 0 + GetRandom(500u);
1162  float supportedProb = 0.7f;
1163 
1164  for (uint32_t i = 0; i < numInputs; ++i)
1165  {
1166  std::string name = "input" + std::to_string(i) + (GetRandomFlag(supportedProb) ? "S" : "N");
1167  graph.AddLayer<InputLayer>(static_cast<LayerBindingId>(i), name.c_str());
1168  }
1169  for (uint32_t i = 0; i < numConstants; ++i)
1170  {
1171  std::string name = "constant" + std::to_string(i) + (GetRandomFlag(supportedProb) ? "S" : "N");
1172  graph.AddLayer<ConstantLayer>(name.c_str());
1173  }
1174  for (uint32_t i = 0; i < numOutputs; ++i)
1175  {
1176  std::string name = "output" + std::to_string(i) + (GetRandomFlag(supportedProb) ? "S" : "N");
1177  graph.AddLayer<OutputLayer>(static_cast<LayerBindingId>(i), name.c_str());
1178  }
1179  for (uint32_t i = 0; i < numConcats; ++i)
1180  {
1181  std::string name = "concat" + std::to_string(i) + (GetRandomFlag(supportedProb) ? "S" : "N");
1182  uint32_t numInputs = 1 + GetRandom(3u);
1183  OriginsDescriptor concatDesc(numInputs);
1184  graph.AddLayer<ConcatLayer>(concatDesc, name.c_str());
1185  }
1186  for (uint32_t i = 0; i < numSplits; ++i)
1187  {
1188  std::string name = "split" + std::to_string(i) + (GetRandomFlag(supportedProb) ? "S" : "N");
1189  uint32_t numOutputs = 1 + GetRandom(3u);
1190  ViewsDescriptor splitDesc(numOutputs);
1191  graph.AddLayer<SplitterLayer>(splitDesc, name.c_str());
1192  }
1193 
1194  // Associate each layer with a "depth" parameter. This is used when creating connections to ensure
1195  // that we don't have any loops, by only connecting to layers with a lower "depth".
1196  // This can be thought of as distance from the "top" of the graph (assuming the graph flows top-to-bottom).
1197  // Unfortunately this approach ends up producing very "wide" graphs,
1198  // which probably isn't very representative of 'real' networks.
1199  uint32_t maxLayerDepth = 5 + GetRandom(2000u);
1200  std::map<Layer*, uint32_t> layerDepths;
1201  std::map<uint32_t, std::vector<Layer*>> layersAtDepth;
1202  for (Layer* layer : graph)
1203  {
1204  uint32_t depth;
1205  if (layer->GetType() == LayerType::Input || layer->GetType() == LayerType::Constant)
1206  {
1207  // There needs to be at least one input-like layer above everything else, otherwise would be
1208  // nothing for them to connect to!
1209  depth = 0;
1210  }
1211  else
1212  {
1213  // Other layers are randomly assigned to later depths.
1214  depth = 1 + GetRandom(maxLayerDepth);
1215  }
1216  layerDepths[layer] = depth;
1217  layersAtDepth[depth].push_back(layer);
1218  }
1219 
1220  // Connect layers to each other. Every input slot of every layer must be connected, but it doesn't
1221  // matter if an output slot goes unused.
1222  for (Layer* layer : graph)
1223  {
1224  for (uint32_t inputSlotIdx = 0; inputSlotIdx < layer->GetNumInputSlots(); ++inputSlotIdx)
1225  {
1226  InputSlot& inputSlot = layer->GetInputSlot(inputSlotIdx);
1227  uint32_t maxLayerDepthToConnectTo = layerDepths[layer]; // This prevents a connection causing a loop
1228  // Finding a layer to connect to may take multiple attempts, so keep trying until it works.
1229  while (inputSlot.GetConnectedOutputSlot() == nullptr)
1230  {
1231  uint32_t layerDepth = GetRandom(maxLayerDepthToConnectTo);
1232  const std::vector<Layer*>& layersToChooseFrom = layersAtDepth[layerDepth];
1233  if (layersToChooseFrom.size() == 0)
1234  {
1235  continue;
1236  }
1237  Layer* layerToConnectWith = layersToChooseFrom[GetRandom(layersToChooseFrom.size())];
1238  if (layerToConnectWith->GetNumOutputSlots() == 0)
1239  {
1240  continue;
1241  }
1242  uint32_t outputSlotIdx = GetRandom(layerToConnectWith->GetNumOutputSlots());
1243  layerToConnectWith->GetOutputSlot(outputSlotIdx).Connect(inputSlot);
1244  }
1245  }
1246  }
1247  }
1248 
1249  if (debug)
1250  {
1251  std::ofstream f("INPUT_" + std::to_string(testIdx) + ".dot");
1252  graph.SerializeToDot(f);
1253  }
1254 
1255  // Run the splitting algorithm, selecting all nodes ending in an 'S' (as randomly assigned above).
1256  auto startTime = std::chrono::high_resolution_clock::now();
1257 
1260  [](const Layer& l) { return std::string(l.GetName()).back() == 'S'; });
1261 
1262  auto endTime = std::chrono::high_resolution_clock::now();
1263  auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime);
1264  if (debug)
1265  {
1266  std::cout << "Test " << testIdx << ": " << duration.count() << " microseconds" << std::endl;
1267  }
1268 
1269  // Build a map of which subgraph is assigned to each layer.
1270  // This helps some of the following code.
1271  std::map<Layer*, SubgraphView*> layerToSubgraph;
1272  for (Layer* layer : graph)
1273  {
1274  size_t i = 0;
1275  for (std::unique_ptr<SubgraphView>& subgraph : subgraphs)
1276  {
1277  std::string name = std::to_string(i++);
1278  if (std::find(subgraph->begin(), subgraph->end(), layer) != subgraph->end())
1279  {
1280  layerToSubgraph[layer] = subgraph.get();
1281  break;
1282  }
1283  }
1284  }
1285 
1286  if (debug)
1287  {
1288  // Before dumping the dot file, set each Layer's BackendId property so that the dot file
1289  // shows the resulting subgraph assignments.
1290  for (Layer* layer : graph)
1291  {
1292  std::string name = "NotAssigned";
1293  auto subgraphIt = layerToSubgraph.find(layer);
1294  if (subgraphIt != layerToSubgraph.end())
1295  {
1296  auto subgraphIdx = std::distance(subgraphs.begin(),
1297  std::find_if(subgraphs.begin(), subgraphs.end(),
1298  [&](auto& s) { return s.get() == subgraphIt->second; }));
1299  name = std::to_string(subgraphIdx);
1300  }
1301  layer->SetBackendId(armnn::BackendId(name));
1302  }
1303 
1304  std::ofstream f("GRAPH_" + std::to_string(testIdx) + ".dot");
1305  graph.SerializeToDot(f);
1306  }
1307 
1308  // Check the dependencies between subgraphs to make sure that the algorithm has produced a valid result.
1309  // Starting from each of the input slots of each subgraph, recurse up the graph and ensure that we never
1310  // encounter a layer that belongs to the subgraph that we started from.
1311  for (std::unique_ptr<SubgraphView>& subgraph : subgraphs)
1312  {
1313  for (InputSlot* inputSlot : subgraph->GetInputSlots())
1314  {
1315  std::queue<Layer*> toProcess;
1316  toProcess.push(&inputSlot->GetConnectedOutputSlot()->GetOwningLayer());
1317  while (toProcess.size() > 0)
1318  {
1319  Layer* l = toProcess.front();
1320  toProcess.pop();
1321 
1322  BOOST_CHECK(layerToSubgraph[l] != subgraph.get());
1323 
1324  for (const InputSlot& is : l->GetInputSlots())
1325  {
1326  toProcess.push(&is.GetConnectedOutputSlot()->GetOwningLayer());
1327  }
1328  }
1329  }
1330  }
1331  }
1332 }
1333 
1335 
1336 BOOST_AUTO_TEST_SUITE(IntegrationTests)
1337 
1338 BOOST_AUTO_TEST_CASE(SingleSubgraph)
1339 {
1340  // This test case represents the scenario when we have one subgraph
1341  // in which two layers have GpuAcc backend assigned
1342 
1343  //Construct graph
1344  Graph graph;
1345 
1346  Layer* const inputLayer = graph.AddLayer<InputLayer>(0, "input");
1347 
1348  Convolution2dDescriptor convDescriptor;
1349  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
1350  convLayer1->SetBackendId(Compute::GpuAcc);
1351 
1352  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
1353  convLayer2->SetBackendId(Compute::GpuAcc);
1354 
1355  Layer* const outputLayer = graph.AddLayer<OutputLayer>(0, "output");
1356 
1357  inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
1358  convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
1359  convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
1360 
1361  // GpuAcc sub graph selector
1364  graph,
1365  // select the GpuAcc layers only
1366  [](const Layer & l){
1367  bool toSelect = (l.GetBackendId() == Compute::GpuAcc);
1368  return toSelect;
1369  });
1370 
1371  BOOST_TEST(subgraphs.size() == 1);
1372  if(subgraphs.size() == 1)
1373  {
1374  BOOST_TEST((subgraphs[0] != nullptr));
1375 
1376  if (subgraphs[0].get() != nullptr)
1377  {
1378  unsigned int numInputSlots = armnn::numeric_cast<unsigned int>(subgraphs[0]->GetInputSlots().size());
1379  unsigned int numOutputSlots = armnn::numeric_cast<unsigned int>(subgraphs[0]->GetOutputSlots().size());
1380 
1381  BOOST_TEST((numInputSlots == 1));
1382  BOOST_TEST((numOutputSlots == 1));
1383 
1384  // Save sub-graph connections for comparison after substitution
1385  IOutputSlot* subgraphInputConn1 = subgraphs[0]->GetInputSlot(0)->GetConnection();
1386  IInputSlot* subgraphOutputConn1 = subgraphs[0]->GetOutputSlot(0)->GetConnection(0);
1387 
1388  // Construct dummy pre-compiled layer
1389  PreCompiledDescriptor preCompiledDescriptor(numInputSlots, numOutputSlots);
1390  Layer* const preCompiledLayer = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor, "pre-compiled");
1391 
1392  // Substitute sub-graph with pre-compiled layer
1393  graph.SubstituteSubgraph(*subgraphs[0], preCompiledLayer);
1394 
1395  // Check that connections are correct after substitution
1396  BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1);
1397 
1398  BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn1);
1399  }
1400  }
1401 }
1402 
1403 BOOST_AUTO_TEST_CASE(MultipleSubgraphs)
1404 {
1405  // This test case represents the scenario when we have two subgraphs
1406  // in which two layers have CpuAcc backend assigned
1407 
1408  //Construct graph
1409  Graph graph;
1410 
1411  Layer* const inputLayer = graph.AddLayer<InputLayer>(0, "input");
1412 
1413  ViewsDescriptor splitterDescriptor(2);
1414  Layer* const splitterLayer = graph.AddLayer<SplitterLayer>(splitterDescriptor, "splitter");
1415  splitterLayer->SetBackendId(Compute::CpuAcc);
1416 
1417  Convolution2dDescriptor convDescriptor;
1418  Layer* const convLayer1 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
1419  Layer* const convLayer2 = graph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
1420 
1421  OriginsDescriptor concatDescriptor(2);
1422  Layer* const pConcatLayer = graph.AddLayer<ConcatLayer>(concatDescriptor, "concat");
1423  pConcatLayer->SetBackendId(Compute::CpuAcc);
1424 
1425  Layer* const outputLayer = graph.AddLayer<OutputLayer>(0, "output");
1426 
1427  inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0));
1428  splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
1429  splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0));
1430  convLayer1->GetOutputSlot(0).Connect(pConcatLayer->GetInputSlot(0));
1431  convLayer2->GetOutputSlot(0).Connect(pConcatLayer->GetInputSlot(1));
1432  pConcatLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
1433 
1434  // CpuAcc sub graph selector
1437  graph,
1438  // select the CpuAcc layers only
1439  [](const Layer & l){
1440  bool toSelect = (l.GetBackendId() == Compute::CpuAcc);
1441  return toSelect;
1442  });
1443 
1444  BOOST_TEST(subgraphs.size() == 2);
1445  if(subgraphs.size() == 2)
1446  {
1447  BOOST_TEST((subgraphs[0] != nullptr));
1448  BOOST_TEST((subgraphs[1] != nullptr));
1449 
1450  if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr)
1451  {
1452  //Sort subgraphs by their inputSlot size.
1453  std::sort(subgraphs.begin(), subgraphs.end(),
1455  {
1456  return (lhs->GetInputSlots().size() < rhs->GetInputSlots().size());
1457  }
1458  );
1459 
1460  unsigned int numInputSlots1 = armnn::numeric_cast<unsigned int>(subgraphs[0]->GetInputSlots().size());
1461  unsigned int numOutputSlots1 = armnn::numeric_cast<unsigned int>(subgraphs[0]->GetOutputSlots().size());
1462 
1463  unsigned int numInputSlots2 = armnn::numeric_cast<unsigned int>(subgraphs[1]->GetInputSlots().size());
1464  unsigned int numOutputSlots2 = armnn::numeric_cast<unsigned int>(subgraphs[1]->GetOutputSlots().size());
1465 
1466  // Save sub-graph connections for comparison after substitution
1467  IOutputSlot* subgraph1InputConn = subgraphs[0]->GetInputSlot(0)->GetConnection();
1468  IInputSlot* subgraph1OutputConn1 = subgraphs[0]->GetOutputSlot(0)->GetConnection(0);
1469  IInputSlot* subgraph1OutputConn2 = subgraphs[0]->GetOutputSlot(1)->GetConnection(0);
1470 
1471  // Save sub-graph connections for comparison after substitution
1472  IOutputSlot* subgraph2InputConn1 = subgraphs[1]->GetInputSlot(0)->GetConnection();
1473  IOutputSlot* subgraph2InputConn2 = subgraphs[1]->GetInputSlot(1)->GetConnection();
1474  IInputSlot* subgraph2OutputConn = subgraphs[1]->GetOutputSlot(0)->GetConnection(0);
1475 
1476  PreCompiledDescriptor preCompiledDescriptor1(numInputSlots1, numOutputSlots1);
1477  Layer* const preCompiledLayer1 = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor1, "pre-compiled1");
1478 
1479  PreCompiledDescriptor preCompiledDescriptor2(numInputSlots2, numOutputSlots2);
1480  Layer* const preCompiledLayer2 = graph.AddLayer<PreCompiledLayer>(preCompiledDescriptor2, "pre-compiled2");
1481 
1482  // Substitute sub-graph with pre-compiled layer
1483  graph.SubstituteSubgraph(*subgraphs[0], preCompiledLayer1);
1484  graph.SubstituteSubgraph(*subgraphs[1], preCompiledLayer2);
1485 
1486  // Check that connections are correct after substitution
1487  BOOST_CHECK_EQUAL(preCompiledLayer1->GetInputSlot(0).GetConnection(), subgraph1InputConn);
1488  BOOST_CHECK_EQUAL(preCompiledLayer1->GetOutputSlot(0).GetConnection(0), subgraph1OutputConn1);
1489  BOOST_CHECK_EQUAL(preCompiledLayer1->GetOutputSlot(1).GetConnection(0), subgraph1OutputConn2);
1490 
1491  BOOST_CHECK_EQUAL(preCompiledLayer2->GetInputSlot(0).GetConnection(), subgraph2InputConn1);
1492  BOOST_CHECK_EQUAL(preCompiledLayer2->GetInputSlot(1).GetConnection(), subgraph2InputConn2);
1493  BOOST_CHECK_EQUAL(preCompiledLayer2->GetOutputSlot(0).GetConnection(0), subgraph2OutputConn);
1494  }
1495  }
1496 }
1497 
1498 BOOST_AUTO_TEST_CASE(SubgraphCycles)
1499 {
1500  // This case represent the scenario when a naive split could lead to a cyclic dependency between two subgraphs
1501  //
1502  // X0 -> M0 -> X1 -> M2 -> X2
1503  // X0 -> M0 -> M1 -> M2 -> X2
1504  //
1505  /*
1506  X0
1507  |
1508  |
1509  M0
1510  / |
1511  / |
1512  X1 M1
1513  \ /
1514  M2
1515  |
1516  X2
1517  */
1518  // The expected result for this is that M0,M1 will be part of one subgraph and M2 in another and the
1519  // input and output slots in the subgraphs will be set accordingly.
1520  //
1521  Graph graph;
1522 
1523  OriginsDescriptor originsDescriptor(2);
1524  auto x0 = graph.AddLayer<InputLayer>(0, "x0");
1525  auto m0 = graph.AddLayer<ActivationLayer>(ActivationDescriptor{}, "m0");
1526  auto x1 = graph.AddLayer<ActivationLayer>(ActivationDescriptor{}, "x1");
1527  auto m1 = graph.AddLayer<ActivationLayer>(ActivationDescriptor{}, "m1");
1528  auto m2 = graph.AddLayer<AdditionLayer>("m2");
1529  auto x2 = graph.AddLayer<ActivationLayer>(ActivationDescriptor{}, "x2");
1530 
1531  x0->GetOutputSlot(0).Connect(m0->GetInputSlot(0));
1532  m0->GetOutputSlot(0).Connect(x1->GetInputSlot(0));
1533  m0->GetOutputSlot(0).Connect(m1->GetInputSlot(0));
1534  x1->GetOutputSlot(0).Connect(m2->GetInputSlot(0));
1535  m1->GetOutputSlot(0).Connect(m2->GetInputSlot(1));
1536  m2->GetOutputSlot(0).Connect(x2->GetInputSlot(0));
1537 
1538  // All selected 'M*' layers will be have 'm' in the name
1541  graph,
1542  // select the middle layers only
1543  [](const Layer & l)
1544  {
1545  bool toSelect = (l.GetNameStr().find('m') != std::string::npos);
1546  return toSelect;
1547  });
1548 
1549  // expected results to test against
1550  auto inputSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({m0}),
1551  CreateOutputsFrom({m0, m1}),
1552  {m0, m1});
1553 
1554  auto outputSubgraph = CreateSubgraphViewFrom(CreateInputsFrom({m2}),
1555  CreateOutputsFrom({m2}),
1556  {m2});
1557 
1558  BOOST_TEST(subgraphs.size() == 2);
1559  if (subgraphs.size() == 2)
1560  {
1561  // we need to have valid subgraph pointers here
1562  BOOST_TEST((subgraphs[0] != nullptr));
1563  BOOST_TEST((subgraphs[1] != nullptr));
1564 
1565  if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr)
1566  {
1567  // sort the subgraphs by layer size, so it is simpler to test
1568  std::sort(subgraphs.begin(), subgraphs.end(),
1570  {
1571  return (lhs->GetLayers().size() < rhs->GetLayers().size());
1572  }
1573  );
1574 
1575  // one subgraph needs to be size=1 and the other one is 4
1576  BOOST_TEST(subgraphs[0]->GetLayers().size() == 1);
1577  BOOST_TEST(subgraphs[1]->GetLayers().size() == 2);
1578 
1579  CompareSubgraphViews(subgraphs[0], outputSubgraph);
1580  CompareSubgraphViews(subgraphs[1], inputSubgraph);
1581  }
1582  }
1583 }
1584 
1585 BOOST_AUTO_TEST_CASE(SubgraphOrder)
1586 {
1587  Graph graph;
1588 
1589  auto input = graph.AddLayer<InputLayer>(0, "Input");
1590  auto activation = graph.AddLayer<ActivationLayer>(ActivationDescriptor{}, "Activation");
1591  auto output = graph.AddLayer<OutputLayer>(1, "Output");
1592 
1593  input->GetOutputSlot(0).Connect(activation->GetInputSlot(0));
1594  activation->GetOutputSlot(0).Connect(output->GetInputSlot(0));
1595 
1596  //Add in out of order
1597  auto view = CreateSubgraphViewFrom({},
1598  {},
1599  {output, input, activation});
1600 
1601  // Check the layers are sorted topologically in the view
1602  int idx=0;
1604  view->ForEachLayer([&idx, &expectedSorted](const Layer* l)
1605  {
1606  BOOST_TEST((expectedSorted[idx] == l->GetType()));
1607  idx++;
1608  }
1609  );
1610 }
1611 
A layer that the constant data can be bound to.
BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
Iterator begin()
Returns iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:162
This layer represents a split operation.
A ViewsDescriptor for the SplitterLayer.
Status SerializeToDot(std::ostream &stream)
Definition: Graph.cpp:118
LayerT * AddLayer(Args &&... args)
Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
Definition: Graph.hpp:402
A Convolution2dDescriptor for the Convolution2dLayer.
int Connect(InputSlot &destination)
Definition: Layer.cpp:83
std::vector< OutputSlot * > OutputSlots
This layer represents an activation operation with the specified activation function.
Copyright (c) 2021 ARM Limited and Contributors.
void SetBackendId(const BackendId &id)
Definition: Layer.hpp:270
const std::vector< InputSlot > & GetInputSlots() const
Definition: Layer.hpp:237
virtual const IInputSlot * GetConnection(unsigned int index) const =0
const IOutputSlot * GetConnection() const override
Definition: Layer.hpp:199
unsigned int GetNumOutputSlots() const override
Returns the number of connectable output slots.
Definition: Layer.hpp:314
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:210
The SubgraphView class represents a subgraph of a Graph.
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:316
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: OutputLayer.hpp:13
An output connection slot for a layer.
Definition: INetwork.hpp:38
SubgraphView::InputSlots CreateInputsFrom(const std::vector< Layer *> &layers)
An OriginsDescriptor for the ConcatLayer.
This layer represents a merge operation.
Definition: ConcatLayer.hpp:13
const std::string & GetNameStr() const
Definition: Layer.hpp:220
LayerType GetType() const override
Returns the armnn::LayerType of this layer.
Definition: Layer.hpp:265
std::vector< SubgraphViewPtr > Subgraphs
const OutputSlot * GetConnectedOutputSlot() const
Definition: Layer.hpp:55
GPU Execution: OpenCL: ArmCompute.
BOOST_AUTO_TEST_CASE(CheckConvolution2dLayer)
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:25
const BackendId & GetBackendId() const
Definition: Layer.hpp:269
std::vector< InputSlot * > InputSlots
This layer represents an addition operation.
void SubstituteSubgraph(SubgraphView &subgraph, IConnectableLayer *substituteLayer)
Substitutes the given sub-graph with either a new layer or a new sub-graph.
Definition: Graph.cpp:432
const InputSlots & GetInputSlots() const
std::unique_ptr< SubgraphView > SubgraphViewPtr
BOOST_AUTO_TEST_SUITE_END()
SubgraphView::SubgraphViewPtr CreateSubgraphViewFrom(SubgraphView::InputSlots &&inputs, SubgraphView::OutputSlots &&outputs, SubgraphView::Layers &&layers)
SubgraphView::OutputSlots CreateOutputsFrom(const std::vector< Layer *> &layers)
static Subgraphs SelectSubgraphs(Graph &graph, const LayerSelectorFunction &selector)
Selects subgraphs from a graph based on the selector function and the algorithm.
const OutputSlots & GetOutputSlots() const
CPU Execution: NEON: ArmCompute.
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: InputLayer.hpp:13
Iterator end()
Returns iterator pointing to the end of the list. Lowercase for range-based for loops.
Definition: Graph.hpp:164
const Layers & GetLayers() const
virtual const IOutputSlot * GetConnection() const =0
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
Definition: Layer.hpp:318
const char * GetName() const override
Returns the name of the layer.
Definition: Layer.hpp:311
This layer represents a convolution 2d operation.
A PreCompiledDescriptor for the PreCompiledLayer.
std::list< Layer * > Layers
size_t GetNumLayers() const
Definition: Graph.hpp:191
LayerT * InsertNewLayer(InputSlot &insertBefore, Args &&... args)
Inserts a new layer between the output slot currently connected to insertBefore and insertBefore itse...
Definition: Graph.hpp:416
An input connection slot for a layer.
Definition: INetwork.hpp:25
const InputSlot * GetConnection(unsigned int index) const override
Definition: Layer.cpp:46
LayerType
When adding a new layer, adapt also the LastLayer enum value in the enum class LayerType below...
Definition: Types.hpp:419