ArmNN
 21.05
OptimizerTests.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "TestUtils.hpp"
7 
8 #include <BackendSettings.hpp>
9 #include <Graph.hpp>
10 #include <Network.hpp>
11 #include <Optimizer.hpp>
12 
13 #include <armnn/BackendHelper.hpp>
15 #include <armnn/INetwork.hpp>
17 
20 
24 
25 #include <boost/test/unit_test.hpp>
26 
27 using namespace armnn;
28 
29 namespace
30 {
31 
32 void CreateLSTMLayerHelper(Graph &graph, bool CifgEnabled)
33 {
34  LstmDescriptor layerDesc;
35  layerDesc.m_ActivationFunc = 4;
36  layerDesc.m_ClippingThresCell = 0.2f;
37  layerDesc.m_ClippingThresProj = 0.4f;
38  layerDesc.m_CifgEnabled = CifgEnabled;
39  layerDesc.m_PeepholeEnabled = false;
40  layerDesc.m_ProjectionEnabled = false;
41 
42  LstmLayer* const layer = graph.AddLayer<LstmLayer>(layerDesc, "layer");
43  unsigned int batchSize = 3;
44  unsigned int inputSize = 2;
45  unsigned int numUnits = 4;
46  unsigned int outputSize = 4;
47 
48  layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedTensorHandle>
49  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
50  layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedTensorHandle>
51  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
52  layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedTensorHandle>
53  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
54  layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedTensorHandle>
55  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
56  layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedTensorHandle>
57  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
58  layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedTensorHandle>
59  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
60  layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedTensorHandle>
61  (TensorInfo({ numUnits }, DataType::Float32));
62  layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedTensorHandle>
63  (TensorInfo({ numUnits }, DataType::Float32));
64  layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedTensorHandle>
65  (TensorInfo({ numUnits }, DataType::Float32));
66 
67  layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
68  layer->m_BasicParameters.m_InputToCellWeights->Allocate();
69  layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
73  layer->m_BasicParameters.m_ForgetGateBias->Allocate();
74  layer->m_BasicParameters.m_CellBias->Allocate();
75  layer->m_BasicParameters.m_OutputGateBias->Allocate();
76 
77  if (!layerDesc.m_CifgEnabled)
78  {
79  layer->m_CifgParameters.m_InputToInputWeights = std::make_unique<ScopedTensorHandle>
80  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
81  layer->m_CifgParameters.m_RecurrentToInputWeights = std::make_unique<ScopedTensorHandle>
82  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
83  layer->m_CifgParameters.m_InputGateBias = std::make_unique<ScopedTensorHandle>
84  (TensorInfo({ numUnits }, DataType::Float32));
85  layer->m_CifgParameters.m_InputToInputWeights->Allocate();
87  layer->m_CifgParameters.m_InputGateBias->Allocate();
88  }
89 
90  if (layerDesc.m_ProjectionEnabled)
91  {
92  layer->m_ProjectionParameters.m_ProjectionWeights = std::make_unique<ScopedTensorHandle>
93  (TensorInfo({ outputSize, numUnits }, DataType::Float32));
94  layer->m_ProjectionParameters.m_ProjectionBias = std::make_unique<ScopedTensorHandle>
95  (TensorInfo({ outputSize }, DataType::Float32));
97  layer->m_ProjectionParameters.m_ProjectionBias->Allocate();
98  }
99 
100  if (layerDesc.m_PeepholeEnabled)
101  {
102  if (!layerDesc.m_CifgEnabled)
103  {
104  layer->m_PeepholeParameters.m_CellToInputWeights = std::make_unique<ScopedTensorHandle>
105  (TensorInfo({ numUnits }, DataType::Float32));
106  layer->m_PeepholeParameters.m_CellToInputWeights->Allocate();
107  }
108  layer->m_PeepholeParameters.m_CellToForgetWeights = std::make_unique<ScopedTensorHandle>
109  (TensorInfo({ numUnits }, DataType::Float32));
110  layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedTensorHandle>
111  (TensorInfo({ numUnits }, DataType::Float32));
112  layer->m_PeepholeParameters.m_CellToForgetWeights->Allocate();
113  layer->m_PeepholeParameters.m_CellToOutputWeights->Allocate();
114  }
115 
116  // create input and output layers
117  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
118  Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
119  Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
120  Layer* const scratchBuffer = graph.AddLayer<OutputLayer>(0, "scratchBuffer");
121  Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
122  Layer* const cellStateOut = graph.AddLayer<OutputLayer>(2, "cellStateOut");
123  Layer* const output = graph.AddLayer<OutputLayer>(3, "output");
124 
125  // connect up
126  armnn::TensorInfo lstmTensorInfo1({ batchSize, inputSize }, DataType::Float32);
127  armnn::TensorInfo lstmTensorInfo2({ batchSize, numUnits}, DataType::Float32);
128  armnn::TensorInfo lstmTensorInfo3({ batchSize, outputSize }, DataType::Float32);
129  armnn::TensorInfo lstmTensorInfoScratchBuff({ batchSize, numUnits * (layerDesc.m_CifgEnabled ? 3 : 4) },
131 
132  Connect(input, layer, lstmTensorInfo1, 0, 0);
133  Connect(cellStateIn, layer, lstmTensorInfo2, 0, 1);
134  Connect(outputStateIn, layer, lstmTensorInfo3, 0, 2);
135  Connect(layer, scratchBuffer, lstmTensorInfoScratchBuff, 0, 0);
136  Connect(layer, outputStateOut, lstmTensorInfo3, 1, 0);
137  Connect(layer, cellStateOut, lstmTensorInfo2, 2, 0);
138  Connect(layer, output, lstmTensorInfo3, 3, 0);
139 }
140 
141 } // namespace
142 
144 using namespace armnn::optimizations;
145 
146 BOOST_AUTO_TEST_CASE(LSTMValidateTensorShapesFromInputsCIFGDisabledTest)
147 {
148  Graph graph;
149 
150  //Helper function creates graph containing LSTM layer with required input and output layers
151  CreateLSTMLayerHelper(graph, false);
152 
153  //This function used to call ValidateShapesFromInputs();
154  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
155 }
156 
157 BOOST_AUTO_TEST_CASE(LSTMValidateTensorShapesFromInputsCIFGEnabledTest)
158 {
159  Graph graph;
160 
161  //Helper function creates graph containing LSTM layer with required input and output layers
162  CreateLSTMLayerHelper(graph, true);
163 
164  //This function used to call ValidateShapesFromInputs();
165  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
166 }
167 
168 BOOST_AUTO_TEST_CASE(InsertConvertersTest)
169 {
170  const armnn::TensorInfo info({ 1, 5, 2, 3 }, armnn::DataType::Float16);
171 
172  armnn::Graph graph;
173 
174  armnn::LayerBindingId inputId = 0;
175 
176  armnn::Layer* head = graph.AddLayer<armnn::OutputLayer>(0, "output");
177 
178  head = graph.InsertNewLayer<armnn::AdditionLayer>(head->GetInputSlot(0), "");
180 
181  graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
182  ->GetOutputHandler().SetTensorInfo(info);
183 
184  head = graph.InsertNewLayer<armnn::FloorLayer>(head->GetInputSlot(0), "");
186 
187  head = graph.InsertNewLayer<armnn::MemCopyLayer>(head->GetInputSlot(0), "");
189 
190  graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(0), inputId++, "")
191  ->GetOutputHandler().SetTensorInfo(info);
192 
193  // Check graph layer sequence before inserting convert layers
194  BOOST_TEST(CheckSequence(graph.cbegin(),
195  graph.cend(),
196  &IsLayerOfType<armnn::InputLayer>,
197  &IsLayerOfType<armnn::InputLayer>,
198  &IsLayerOfType<armnn::MemCopyLayer>,
199  &IsLayerOfType<armnn::FloorLayer>,
200  &IsLayerOfType<armnn::AdditionLayer>,
201  &IsLayerOfType<armnn::OutputLayer>));
202 
203  // Check layers have Float16 DataType
204  for (auto& layer : graph)
205  {
206  if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
207  {
210  }
211  }
212 
213  // Insert convert layers either side of unsupported layer
214  for (auto& layer : graph)
215  {
216  if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
217  {
219  InsertConvertFp32ToFp16LayersAfter(graph, *layer);
220  }
221  }
222 
223  // Check layers have correct DataType after inserting convert layers
224  for (auto& layer : graph)
225  {
226  if (layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
227  {
230  }
231  else if (layer->GetType() == LayerType::ConvertFp16ToFp32)
232  {
235  }
236  else if (layer->GetType() == LayerType::ConvertFp32ToFp16)
237  {
240  }
241  }
242 
243  // Check sequence of layers after inserting convert layers
244  BOOST_TEST(CheckSequence(graph.cbegin(),
245  graph.cend(),
246  &IsLayerOfType<armnn::InputLayer>,
247  &IsLayerOfType<armnn::InputLayer>,
248  &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
249  &IsLayerOfType<armnn::MemCopyLayer>,
250  &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
251  &IsLayerOfType<armnn::FloorLayer>,
252  &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
253  &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
254  &IsLayerOfType<armnn::AdditionLayer>,
255  &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
256  &IsLayerOfType<armnn::OutputLayer>));
257 }
258 
259 void CreateConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
260  const unsigned int* weightsShape, const unsigned int* outputShape,
261  DataLayout dataLayout = DataLayout::NCHW)
262 {
263  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
264  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
265 
266  std::vector<float> weightsVector(90);
267  armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
268 
270  desc.m_BiasEnabled = false;
271  desc.m_StrideX = 1;
272  desc.m_StrideY = 1;
273  desc.m_DataLayout = dataLayout;
274 
275  Layer* input = graph.AddLayer<InputLayer>(0, "input");
276  input->GetOutputSlot().SetTensorInfo(inputInfo);
277 
278  Convolution2dLayer* layer = graph.AddLayer<Convolution2dLayer>(desc, "conv2d");
279  layer->m_Weight = std::make_unique<armnn::ScopedTensorHandle>(weights);
280  layer->GetOutputSlot().SetTensorInfo(outputInfo);
281 
282  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
283  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
284  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
285 }
286 
287 BOOST_AUTO_TEST_CASE(Conv2dValidateTensorShapesFromInputs)
288 {
289  Graph graph;
290  const unsigned int inputShape[] = { 1, 3, 8, 16 };
291  const unsigned int weightsShape[] = { 2, 3, 5, 3 };
292  const unsigned int outputShape[] = { 1, 2, 4, 14 };
293  CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
294 
295  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
296 }
297 
298 BOOST_AUTO_TEST_CASE(Conv2dValidateTensorShapesFromInputsNhwc)
299 {
300  Graph graph;
301  const unsigned int inputShape[] = { 1, 8, 16, 3 };
302  const unsigned int weightsShape[] = { 2, 5, 3, 3 };
303  const unsigned int outputShape[] = { 1, 4, 14, 2 };
304  CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
305 
306  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
307 }
308 
309 void CreateDepthwiseConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
310  const unsigned int* weightsShape, const unsigned int* outputShape,
311  DataLayout dataLayout = DataLayout::NCHW)
312 {
313  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
314  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
315 
316  std::vector<float> weightsVector(18);
317  armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
318 
320  desc.m_BiasEnabled = false;
321  desc.m_StrideX = 1;
322  desc.m_StrideY = 1;
323  desc.m_DataLayout = dataLayout;
324 
325  Layer* input = graph.AddLayer<InputLayer>(0, "input");
326  input->GetOutputSlot().SetTensorInfo(inputInfo);
327 
328  DepthwiseConvolution2dLayer* layer = graph.AddLayer<DepthwiseConvolution2dLayer>(desc, "depthwiseConv2d");
329  layer->m_Weight = std::make_unique<armnn::ScopedTensorHandle>(weights);
330  layer->GetOutputSlot().SetTensorInfo(outputInfo);
331 
332  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
333  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
334  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
335 }
336 
337 BOOST_AUTO_TEST_CASE(DepthwiseConv2dValidateTensorShapesFromInputs)
338 {
339  Graph graph;
340  const unsigned int inputShape[] = { 1, 2, 3, 3 };
341  const unsigned int weightsShape[] = { 1, 2, 3, 3 };
342  const unsigned int outputShape[] = { 1, 2, 1, 1 };
343  CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
344 
345  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
346 }
347 
348 BOOST_AUTO_TEST_CASE(DepthwiseConv2dValidateTensorShapesFromInputsNhwc)
349 {
350  Graph graph;
351  const unsigned int inputShape[] = { 1, 3, 3, 2 };
352  const unsigned int weightsShape[] = { 1, 2, 3, 3 };
353  const unsigned int outputShape[] = { 1, 1, 1, 2 };
354  CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
355 
356  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
357 }
358 
359 void CreatePooling2dGraph(Graph& graph, const unsigned int* inputShape, const unsigned int* outputShape,
360  DataLayout dataLayout = DataLayout::NCHW)
361 {
362  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
363  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
364 
365  Pooling2dDescriptor desc;
367  desc.m_PoolWidth = desc.m_PoolHeight = 100;
368  desc.m_StrideX = desc.m_StrideY = 5;
369  desc.m_PadLeft = 50;
370  desc.m_PadRight = 50;
371  desc.m_PadTop = 50;
372  desc.m_PadBottom = 50;
374  desc.m_DataLayout = dataLayout;
375 
376  Layer* input = graph.AddLayer<InputLayer>(0, "input");
377  input->GetOutputSlot().SetTensorInfo(inputInfo);
378 
379  Pooling2dLayer* layer = graph.AddLayer<Pooling2dLayer>(desc, "pooling2d");
380  layer->GetOutputSlot().SetTensorInfo(outputInfo);
381 
382  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
383  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
384  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
385 }
386 
387 BOOST_AUTO_TEST_CASE(Pooling2dValidateTensorShapesFromInputs)
388 {
389  Graph graph;
390  const unsigned int inputShape[] = { 5, 3, 52, 60 };
391  const unsigned int outputShape[] = { 5, 3, 11, 13 };
392  CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NCHW);
393 
394  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
395 }
396 
397 BOOST_AUTO_TEST_CASE(Pooling2dValidateTensorShapesFromInputsNhwc)
398 {
399  Graph graph;
400  const unsigned int inputShape[] = { 5, 52, 60, 3 };
401  const unsigned int outputShape[] = { 5, 11, 13, 3 };
402  CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NHWC);
403 
404  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
405 }
406 
408  const unsigned int* inputShape,
409  const unsigned int* outputShape,
410  DataLayout dataLayout = DataLayout::NCHW)
411 {
412  TensorInfo inputInfo(4, inputShape, DataType::Float32);
413  TensorInfo outputInfo(4, outputShape, DataType::Float32);
414 
415  ResizeDescriptor desc;
417  desc.m_TargetHeight = 3;
418  desc.m_TargetWidth = 4;
419  desc.m_DataLayout = dataLayout;
420 
421  Layer* input = graph.AddLayer<InputLayer>(0, "input");
422  input->GetOutputSlot().SetTensorInfo(inputInfo);
423 
424  ResizeLayer* layer = graph.AddLayer<ResizeLayer>(desc, "resizeBilinear");
425  layer->GetOutputSlot().SetTensorInfo(outputInfo);
426 
427  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
428  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
429  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
430 }
431 
432 BOOST_AUTO_TEST_CASE(ResizeBilinearValidateTensorShapesFromInputs)
433 {
434  Graph graph;
435  const unsigned int inputShape[] = { 1, 2, 4, 5 };
436  const unsigned int outputShape[] = { 1, 2, 3, 4 };
437  CreateResizeBilinearGraph(graph, inputShape, outputShape);
438 
439  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
440 }
441 
442 BOOST_AUTO_TEST_CASE(ResizeBilinearValidateTensorShapesFromInputsNhwc)
443 {
444  Graph graph;
445  const unsigned int inputShape[] = { 1, 4, 5, 2 };
446  const unsigned int outputShape[] = { 1, 3, 4, 2 };
447  CreateResizeBilinearGraph(graph, inputShape, outputShape, DataLayout::NHWC);
448 
449  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
450 }
451 
453  const armnn::TensorInfo& paramsInfo,
454  const armnn::TensorInfo& indicesInfo,
455  const armnn::TensorInfo& outputInfo)
456 {
457  Layer* input0 = graph.AddLayer<InputLayer>(0, "params");
458  input0->GetOutputSlot().SetTensorInfo(paramsInfo);
459 
460  Layer* input1 = graph.AddLayer<InputLayer>(1, "indices");
461  input1->GetOutputSlot().SetTensorInfo(indicesInfo);
462 
463  GatherDescriptor descriptor;
464  GatherLayer* layer = graph.AddLayer<GatherLayer>(descriptor, "gather");
465  layer->GetOutputSlot().SetTensorInfo(outputInfo);
466 
467  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
468  input0->GetOutputSlot().Connect(layer->GetInputSlot(0));
469  input1->GetOutputSlot().Connect(layer->GetInputSlot(1));
470  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
471 }
472 
473 BOOST_AUTO_TEST_CASE(GatherValidateTensorShapesFromInputs)
474 {
475  Graph graph;
476  armnn::TensorInfo paramsInfo({10, 5}, DataType::Float32);
477  armnn::TensorInfo indicesInfo({3}, DataType::Signed32);
478  armnn::TensorInfo outputInfo({3, 5}, DataType::Float32);
479 
480  CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
481 
482  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
483 }
484 
485 BOOST_AUTO_TEST_CASE(GatherValidateTensorShapesFromInputs1DParams)
486 {
487  Graph graph;
488  armnn::TensorInfo paramsInfo({8}, DataType::Float32);
489  armnn::TensorInfo indicesInfo({5}, DataType::Signed32);
490  armnn::TensorInfo outputInfo( {5}, DataType::Float32);
491 
492  CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
493 
494  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
495 }
496 
497 BOOST_AUTO_TEST_CASE(GatherValidateTensorShapesFromInputsMultiDimIndices)
498 {
499  Graph graph;
500  armnn::TensorInfo paramsInfo({3, 2, 5}, DataType::Float32);
501  armnn::TensorInfo indicesInfo({2, 2}, DataType::Signed32);
502  armnn::TensorInfo outputInfo({2, 2, 2, 5}, DataType::Float32);
503 
504  CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
505 
506  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
507 }
508 
509 BOOST_AUTO_TEST_CASE(DetectionPostProcessValidateTensorShapes)
510 {
511  Graph graph;
512  armnn::TensorInfo boxEncodingsInfo({1, 10, 4}, DataType::QAsymmU8);
514  std::vector<uint8_t> anchorsVector(40);
516 
517  armnn::TensorInfo detectionBoxesInfo({1, 3, 4}, DataType::QAsymmU8);
518  armnn::TensorInfo detectionScoresInfo({1, 3}, DataType::QAsymmU8);
519  armnn::TensorInfo detectionClassesInfo({1, 3}, DataType::QAsymmU8);
520  armnn::TensorInfo numDetectionInfo({1}, DataType::QAsymmU8);
521 
522  Layer* input0 = graph.AddLayer<InputLayer>(0, "boxEncodings");
523  input0->GetOutputSlot().SetTensorInfo(boxEncodingsInfo);
524 
525  Layer* input1 = graph.AddLayer<InputLayer>(1, "score");
527 
529  descriptor.m_MaxDetections = 3;
530 
531  DetectionPostProcessLayer* layer = graph.AddLayer<DetectionPostProcessLayer>(descriptor, "detectionPostProcess");
532  layer->m_Anchors = std::make_unique<armnn::ScopedTensorHandle>(anchors);
533  layer->GetOutputSlot(0).SetTensorInfo(detectionBoxesInfo);
534  layer->GetOutputSlot(1).SetTensorInfo(detectionScoresInfo);
535  layer->GetOutputSlot(2).SetTensorInfo(detectionClassesInfo);
536  layer->GetOutputSlot(3).SetTensorInfo(numDetectionInfo);
537 
538  input0->GetOutputSlot().Connect(layer->GetInputSlot(0));
539  input1->GetOutputSlot().Connect(layer->GetInputSlot(1));
540 
541  BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
542 }
543 
544 BOOST_AUTO_TEST_CASE(FoldPadLayerIntoConvolution2dLayer)
545 {
546  Graph graph;
547  const unsigned int inputShape[] = { 1, 2, 2, 3 };
548  const unsigned int paddedShape[] = { 1, 6, 6, 3 };
549  const unsigned int weightsShape[] = { 1, 2, 3, 3 };
550  const unsigned int outputShape[] = { 1, 2, 1, 1 };
551 
552  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
553  armnn::TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
554  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
555 
556  Layer* input = graph.AddLayer<InputLayer>(0, "input");
557  input->GetOutputSlot().SetTensorInfo(inputInfo);
558 
559  PadDescriptor padDescriptor({ { 0, 0 }, { 2, 2 }, { 2, 2 }, { 0, 0 } });
560 
561  PadLayer* padLayer = graph.AddLayer<PadLayer>(padDescriptor, "pad");
562  padLayer->GetOutputSlot().SetTensorInfo(paddedInfo);
563 
564  Convolution2dDescriptor convolution2dDescriptor;
565  convolution2dDescriptor.m_BiasEnabled = false;
566  convolution2dDescriptor.m_StrideX = 1;
567  convolution2dDescriptor.m_StrideY = 1;
568  convolution2dDescriptor.m_DataLayout = DataLayout::NHWC;
569 
570  std::vector<float> weightsVector(18);
571  armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
572 
573  Convolution2dLayer* conv2dLayer = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "conv2d");
574  conv2dLayer->m_Weight = std::make_unique<armnn::ScopedTensorHandle>(weights);
575  conv2dLayer->GetOutputSlot().SetTensorInfo(outputInfo);
576 
577  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
578 
579  // Connect up layers - input -> pad -> conv2d -> output
580  input->GetOutputSlot().Connect(padLayer->GetInputSlot(0));
581  padLayer->GetOutputSlot().Connect(conv2dLayer->GetInputSlot(0));
582  conv2dLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
583 
584  auto checkSimpleConv2d = [](const armnn::Layer* const layer) -> bool {
585  const auto conv2dLayer = static_cast<const armnn::Convolution2dLayer*>(layer);
586  const auto conv2dLayerParams = conv2dLayer->GetParameters();
587  return IsLayerOfType<armnn::Convolution2dLayer>(layer) && (layer->GetNameStr() == "conv2d") &&
588  (conv2dLayerParams.m_PadLeft == 0) && (conv2dLayerParams.m_PadRight == 0) &&
589  (conv2dLayerParams.m_PadTop == 0) && (conv2dLayerParams.m_PadBottom == 0) &&
590  (conv2dLayerParams.m_BiasEnabled == false) && (conv2dLayerParams.m_StrideX == 1) &&
591  (conv2dLayerParams.m_StrideY == 1) && (conv2dLayerParams.m_DataLayout == DataLayout::NHWC);
592  };
593 
594  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
595  &IsLayerOfType<armnn::InputLayer>,
596  &IsLayerOfType<armnn::PadLayer>,
597  checkSimpleConv2d,
598  &IsLayerOfType<armnn::OutputLayer>));
599 
601 
602  auto checkPadFoldedIntoConv2d = [](const armnn::Layer* const layer) -> bool {
603  const auto conv2dLayer = static_cast<const armnn::Convolution2dLayer*>(layer);
604  const auto conv2dLayerParams = conv2dLayer->GetParameters();
605  return IsLayerOfType<armnn::Convolution2dLayer>(layer) && (layer->GetNameStr() == "folded-pad-into-conv2d") &&
606  (conv2dLayerParams.m_PadLeft == 2) && (conv2dLayerParams.m_PadRight == 2) &&
607  (conv2dLayerParams.m_PadTop == 2) && (conv2dLayerParams.m_PadBottom == 2) &&
608  (conv2dLayerParams.m_BiasEnabled == false) && (conv2dLayerParams.m_StrideX == 1) &&
609  (conv2dLayerParams.m_StrideY == 1) && (conv2dLayerParams.m_DataLayout == DataLayout::NHWC);
610  };
611 
612  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
613  &IsLayerOfType<armnn::InputLayer>,
614  checkPadFoldedIntoConv2d,
615  &IsLayerOfType<armnn::OutputLayer>));
616 }
617 
618 BOOST_AUTO_TEST_CASE(FoldPadLayerIntoPooling2dLayer)
619 {
620  Graph graph;
621  const unsigned int inputShape[] = { 1, 2, 2, 3 };
622  const unsigned int paddedShape[] = { 1, 4, 4, 3 };
623  const unsigned int outputShape[] = { 1, 2, 2, 3 };
624 
625  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
626  armnn::TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
627  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
628 
629  Layer* input = graph.AddLayer<InputLayer>(0, "input");
630  input->GetOutputSlot().SetTensorInfo(inputInfo);
631 
632  PadDescriptor padDescriptor({ { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 } });
633 
634  PadLayer* padLayer = graph.AddLayer<PadLayer>(padDescriptor, "pad");
635  padLayer->GetOutputSlot().SetTensorInfo(paddedInfo);
636 
637  Pooling2dDescriptor pooling2dDescriptor;
638  pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
639  pooling2dDescriptor.m_PoolWidth = 3;
640  pooling2dDescriptor.m_PoolHeight = 3;
641  pooling2dDescriptor.m_StrideX = 1;
642  pooling2dDescriptor.m_StrideY = 1;
643  pooling2dDescriptor.m_DataLayout = DataLayout::NHWC;
644 
645  Pooling2dLayer* pool2dLayer = graph.AddLayer<Pooling2dLayer>(pooling2dDescriptor, "pool2d");
646  pool2dLayer->GetOutputSlot().SetTensorInfo(outputInfo);
647 
648  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
649 
650  // Connect up layers - input -> pad -> pool2d -> output
651  input->GetOutputSlot().Connect(padLayer->GetInputSlot(0));
652  padLayer->GetOutputSlot().Connect(pool2dLayer->GetInputSlot(0));
653  pool2dLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
654 
655  auto checkSimplePool2d = [&](const armnn::Layer* const layer) {
656  const auto pool2dLayer = static_cast<const armnn::Pooling2dLayer*>(layer);
657  return IsLayerOfType<armnn::Pooling2dLayer>(layer) && (layer->GetNameStr() == "pool2d") &&
658  (pool2dLayer->GetParameters() == pooling2dDescriptor);
659  };
660 
661  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
662  &IsLayerOfType<armnn::InputLayer>,
663  &IsLayerOfType<armnn::PadLayer>,
664  checkSimplePool2d,
665  &IsLayerOfType<armnn::OutputLayer>));
666 
668 
669  auto checkPadFoldedIntoPool2d = [&](const armnn::Layer* const layer) {
670  if (!IsLayerOfType<armnn::Pooling2dLayer>(layer) || (layer->GetNameStr() != "folded-pad-into-pool2d"))
671  {
672  return false;
673  }
674 
675  const auto pool2dLayer = static_cast<const armnn::Pooling2dLayer*>(layer);
676  const Pooling2dDescriptor pool2dLayerParams = pool2dLayer->GetParameters();
677 
678  Pooling2dDescriptor pool2dLayerParamsNoPad = pool2dLayerParams;
679  pool2dLayerParamsNoPad.m_PadLeft = 0;
680  pool2dLayerParamsNoPad.m_PadRight = 0;
681  pool2dLayerParamsNoPad.m_PadTop = 0;
682  pool2dLayerParamsNoPad.m_PadBottom = 0;
683  // If we fold then PaddingMethod will be set to Ignore. The original will be Exclude.
684  pool2dLayerParamsNoPad.m_PaddingMethod = PaddingMethod::Exclude;
685 
686  return (pool2dLayerParamsNoPad == pooling2dDescriptor) && (pool2dLayerParams.m_PadLeft == 1) &&
687  (pool2dLayerParams.m_PadRight == 1) && (pool2dLayerParams.m_PadTop == 1) &&
688  (pool2dLayerParams.m_PadBottom == 1) &&
689  (pool2dLayerParams.m_PaddingMethod == PaddingMethod::IgnoreValue);
690  };
691 
692  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
693  &IsLayerOfType<armnn::InputLayer>,
694  checkPadFoldedIntoPool2d,
695  &IsLayerOfType<armnn::OutputLayer>));
696 }
697 
698 BOOST_AUTO_TEST_CASE(FoldPadLayerIntoPooling2d_PadWithMultipleOutputsShouldNotBeOptimized)
699 {
700  // In this test case we'll setup a pad layer with two outputs. One goes to a polling layers and the other
701  // goes to an output layer. FoldPadLayerIntoPooling2d should not optimize this graph as it uses the
702  // OptimizeForExclusiveConnection method.
703  Graph graph;
704  const unsigned int inputShape[] = { 1, 2, 2, 3 };
705  const unsigned int paddedShape[] = { 1, 4, 4, 3 };
706  const unsigned int outputShape[] = { 1, 2, 2, 3 };
707 
708  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
709  armnn::TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
710  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
711 
712  Layer* input = graph.AddLayer<InputLayer>(0, "input");
713  input->GetOutputSlot().SetTensorInfo(inputInfo);
714 
715  PadDescriptor padDescriptor({ { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 } });
716 
717  PadLayer* padLayer = graph.AddLayer<PadLayer>(padDescriptor, "pad");
718  padLayer->GetOutputSlot().SetTensorInfo(paddedInfo);
719 
720  Pooling2dDescriptor pooling2dDescriptor;
721  pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
722  pooling2dDescriptor.m_PoolWidth = 3;
723  pooling2dDescriptor.m_PoolHeight = 3;
724  pooling2dDescriptor.m_StrideX = 1;
725  pooling2dDescriptor.m_StrideY = 1;
726  pooling2dDescriptor.m_DataLayout = DataLayout::NHWC;
727 
728  Pooling2dLayer* pool2dLayer = graph.AddLayer<Pooling2dLayer>(pooling2dDescriptor, "pool2d");
729  pool2dLayer->GetOutputSlot().SetTensorInfo(outputInfo);
730 
731  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
732 
733  // Connect up layers - input -> pad -> pool2d -> output
734  input->GetOutputSlot().Connect(padLayer->GetInputSlot(0));
735  padLayer->GetOutputSlot().Connect(pool2dLayer->GetInputSlot(0));
736  pool2dLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
737 
738  // Add the alternative branch from the pas layer to an output layer.
739  Layer* secondOutput = graph.AddLayer<OutputLayer>(1, "dummy output");
740  padLayer->GetOutputSlot().Connect(secondOutput->GetInputSlot(0));
741 
742  auto checkSimplePool2d = [&](const armnn::Layer* const layer) {
743  const auto pool2dLayer = static_cast<const armnn::Pooling2dLayer*>(layer);
744  return IsLayerOfType<armnn::Pooling2dLayer>(layer) && (layer->GetNameStr() == "pool2d") &&
745  (pool2dLayer->GetParameters() == pooling2dDescriptor);
746  };
747 
748  // Initial sequence.
749  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
750  &IsLayerOfType<armnn::InputLayer>,
751  &IsLayerOfType<armnn::PadLayer>,
752  checkSimplePool2d,
753  &IsLayerOfType<armnn::OutputLayer>,
754  &IsLayerOfType<armnn::OutputLayer>));
755 
757 
758  // The network should not change.
759  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
760  &IsLayerOfType<armnn::InputLayer>,
761  &IsLayerOfType<armnn::PadLayer>,
762  checkSimplePool2d,
763  &IsLayerOfType<armnn::OutputLayer>,
764  &IsLayerOfType<armnn::OutputLayer>));
765 }
766 
767 BOOST_AUTO_TEST_CASE(FoldPadLayerIntoPooling2dLayer_PoolingLayerWithExcludePaddingShouldNotTakeMorePadding)
768 {
769  // In this test setup input, Pad layer, Pooling layer that includes padding, output layer. The optimization
770  // should not work as the pooling layer already includes and existing pad and specifies PaddingMethod::Exclude.
771  Graph graph;
772  const unsigned int inputShape[] = { 1, 2, 2, 3 };
773  const unsigned int paddedShape[] = { 1, 4, 4, 3 };
774  const unsigned int outputShape[] = { 1, 2, 2, 3 };
775 
776  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
777  armnn::TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
778  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
779 
780  Layer* input = graph.AddLayer<InputLayer>(0, "input");
781  input->GetOutputSlot().SetTensorInfo(inputInfo);
782 
783  PadDescriptor padDescriptor({ { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 } });
784 
785  PadLayer* padLayer = graph.AddLayer<PadLayer>(padDescriptor, "pad");
786  padLayer->GetOutputSlot().SetTensorInfo(paddedInfo);
787 
788  Pooling2dDescriptor pooling2dDescriptor;
789  pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
790  pooling2dDescriptor.m_PoolWidth = 3;
791  pooling2dDescriptor.m_PoolHeight = 3;
792  pooling2dDescriptor.m_StrideX = 1;
793  pooling2dDescriptor.m_StrideY = 1;
794  pooling2dDescriptor.m_DataLayout = DataLayout::NHWC;
795  // Include a pad with the pooling layer. This should prevent the optimization working.
796  pooling2dDescriptor.m_PadLeft = 1;
797  pooling2dDescriptor.m_PadRight = 1;
798  pooling2dDescriptor.m_PadTop = 1;
799  pooling2dDescriptor.m_PadBottom = 1;
800  pooling2dDescriptor.m_PaddingMethod = PaddingMethod::Exclude;
801 
802  Pooling2dLayer* pool2dLayer = graph.AddLayer<Pooling2dLayer>(pooling2dDescriptor, "pool2d");
803  pool2dLayer->GetOutputSlot().SetTensorInfo(outputInfo);
804 
805  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
806 
807  // Connect up layers - input -> pad -> pool2d -> output
808  input->GetOutputSlot().Connect(padLayer->GetInputSlot(0));
809  padLayer->GetOutputSlot().Connect(pool2dLayer->GetInputSlot(0));
810  pool2dLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
811 
812  auto checkSimplePool2d = [&](const armnn::Layer* const layer) {
813  const auto pool2dLayer = static_cast<const armnn::Pooling2dLayer*>(layer);
814  return IsLayerOfType<armnn::Pooling2dLayer>(layer) && (layer->GetNameStr() == "pool2d") &&
815  (pool2dLayer->GetParameters() == pooling2dDescriptor);
816  };
817 
818  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
819  &IsLayerOfType<armnn::InputLayer>,
820  &IsLayerOfType<armnn::PadLayer>,
821  checkSimplePool2d,
822  &IsLayerOfType<armnn::OutputLayer>));
823 
825 
826  // The optimization should not have modified the graph.
827  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
828  &IsLayerOfType<armnn::InputLayer>,
829  &IsLayerOfType<armnn::PadLayer>,
830  checkSimplePool2d,
831  &IsLayerOfType<armnn::OutputLayer>));
832 }
833 
834 BOOST_AUTO_TEST_CASE(FoldPadLayerIntoPooling2dLayer_MaxPoolingLayerWithLargePadValueShouldNotBeFolded)
835 {
836  // In this test setup input, Pad layer with a large pad value, Max Pooling layer, output layer. The optimization
837  // should not work as the pad value will modify the result of the max pooling layer.
838  Graph graph;
839  const unsigned int inputShape[] = { 1, 2, 2, 3 };
840  const unsigned int paddedShape[] = { 1, 4, 4, 3 };
841  const unsigned int outputShape[] = { 1, 2, 2, 3 };
842 
843  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
844  armnn::TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
845  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
846 
847  Layer* input = graph.AddLayer<InputLayer>(0, "input");
848  input->GetOutputSlot().SetTensorInfo(inputInfo);
849 
850  PadDescriptor padDescriptor({ { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 } });
851  // For Max pooling of a float a pad value of 0 is more than enough to stop the fold happening.
852  // Set this to -std::numeric_limits<float>::infinity() to make the fold happen.
853  padDescriptor.m_PadValue = 0;
854 
855  PadLayer* padLayer = graph.AddLayer<PadLayer>(padDescriptor, "pad");
856  padLayer->GetOutputSlot().SetTensorInfo(paddedInfo);
857 
858  Pooling2dDescriptor pooling2dDescriptor;
859  pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Max;
860  pooling2dDescriptor.m_PoolWidth = 3;
861  pooling2dDescriptor.m_PoolHeight = 3;
862  pooling2dDescriptor.m_StrideX = 1;
863  pooling2dDescriptor.m_StrideY = 1;
864  pooling2dDescriptor.m_DataLayout = DataLayout::NHWC;
865 
866  Pooling2dLayer* pool2dLayer = graph.AddLayer<Pooling2dLayer>(pooling2dDescriptor, "pool2d");
867  pool2dLayer->GetOutputSlot().SetTensorInfo(outputInfo);
868 
869  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
870 
871  // Connect up layers - input -> pad -> pool2d -> output
872  input->GetOutputSlot().Connect(padLayer->GetInputSlot(0));
873  padLayer->GetOutputSlot().Connect(pool2dLayer->GetInputSlot(0));
874  pool2dLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
875 
876  auto checkSimplePool2d = [&](const armnn::Layer* const layer) {
877  const auto pool2dLayer = static_cast<const armnn::Pooling2dLayer*>(layer);
878  return IsLayerOfType<armnn::Pooling2dLayer>(layer) && (layer->GetNameStr() == "pool2d") &&
879  (pool2dLayer->GetParameters() == pooling2dDescriptor);
880  };
881 
882  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
883  &IsLayerOfType<armnn::InputLayer>,
884  &IsLayerOfType<armnn::PadLayer>,
885  checkSimplePool2d,
886  &IsLayerOfType<armnn::OutputLayer>));
887 
889 
890  // The optimization should not have modified the graph.
891  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
892  &IsLayerOfType<armnn::InputLayer>,
893  &IsLayerOfType<armnn::PadLayer>,
894  checkSimplePool2d,
895  &IsLayerOfType<armnn::OutputLayer>));
896 }
897 
898 #if defined(ARMNNREF_ENABLED)
899 BOOST_AUTO_TEST_CASE(FoldPadLayerIntoPooling2dLayer_ExecuteInferenceWithAndWithoutOptimization)
900 {
901  // The idea of this test to run a simple pad+pool2d network twice. Once
902  // with FoldPadLayerIntoPooling2dLayer enabled and a second time with it
903  // avoided. The output tensors of each should match.
904  const unsigned int inputShape[] = { 1, 4, 4, 2 };
905  const unsigned int paddedShape[] = { 1, 6, 6, 2 };
906  const unsigned int outputShape[] = { 1, 4, 4, 2 };
907  std::vector<float> inputData({
908  2.0f, 2.0f, 6.0f, 6.0f, 4.0f, 4.0f, 8.0f, 8.0f, 10.0f, 12.0f, 14.0f, 16.0f, 10.0f, 12.0f, 16.0f, 14.0f,
909 
910  18.0f, 20.0f, 24.0f, 22.0f, 20.0f, 18.0f, 22.0f, 24.0f, 26.0f, 28.0f, 0.0f, 0.0f, 26.0f, 28.0f, 0.0f, 0.0f,
911  });
912  try
913  {
914  // Create a network of input, pad, pooling 2D, output.
915  INetworkPtr network = INetwork::Create();
916 
917  IConnectableLayer* inputLayer = network->AddInputLayer(0);
918  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
919  inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
920 
921  PadDescriptor padDescriptor({ { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 } });
922  IConnectableLayer* padLayer = network->AddPadLayer(padDescriptor, "Pad");
923  armnn::TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
924  padLayer->GetOutputSlot(0).SetTensorInfo(paddedInfo);
925 
926  Pooling2dDescriptor pooling2dDescriptor;
927  pooling2dDescriptor.m_PoolType = PoolingAlgorithm::Average;
928  pooling2dDescriptor.m_PoolWidth = 3;
929  pooling2dDescriptor.m_PoolHeight = 3;
930  pooling2dDescriptor.m_StrideX = 1;
931  pooling2dDescriptor.m_StrideY = 1;
932  pooling2dDescriptor.m_DataLayout = DataLayout::NHWC;
933  IConnectableLayer* pool2dLayer = network->AddPooling2dLayer(pooling2dDescriptor, "Pool2D");
934  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
935  pool2dLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
936 
937  IConnectableLayer* outputLayer = network->AddOutputLayer(0);
938 
939  // Connect layers
940  inputLayer->GetOutputSlot(0).Connect(padLayer->GetInputSlot(0));
941  padLayer->GetOutputSlot(0).Connect(pool2dLayer->GetInputSlot(0));
942  pool2dLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
943 
944  // Create ArmNN runtime
945  IRuntimePtr run = IRuntime::Create(IRuntime::CreationOptions()); // default options
946  // Optimise the network
947  IOptimizedNetworkPtr optimizedNetwork = Optimize(*network, { Compute::CpuRef }, run->GetDeviceSpec());
948  // Load network into runtime
949  NetworkId networkIdentifier;
950  BOOST_TEST(run->LoadNetwork(networkIdentifier, std::move(optimizedNetwork)) == Status::Success);
951 
952  InputTensors inputTensors{ { 0,
953  ConstTensor(run->GetInputTensorInfo(networkIdentifier, 0), inputData.data()) } };
954 
955  // Set the initial values of the data to different values to the golden data just in case the inference fails.
956  std::vector<float> optimizedData(32, -std::numeric_limits<float>::infinity());
957  armnn::OutputTensors outputTensors{ { 0, armnn::Tensor(outputInfo, optimizedData.data()) } };
958  // Execute network
959  run->EnqueueWorkload(networkIdentifier, inputTensors, outputTensors);
960  // Unload it.
961  run->UnloadNetwork(networkIdentifier);
962 
963  // In this second case the pad will have two outputs, one connected to the pooling layer the second connected to
964  // a second output layer. This will prevent the FoldPadLayerIntoPooling2dLayer optimization from working.
965  // A previous test, FoldPadLayerIntoPooling2d_PadWithMultipleOutputsShouldNotBeOptimized, has proved that doing
966  // this will avoid the optimization.
967  IConnectableLayer* dummyOutputLayer = network->AddOutputLayer(1);
968  padLayer->GetOutputSlot(0).Connect(dummyOutputLayer->GetInputSlot(0));
969 
970  // Optimize and load and execute it a second time.
971  optimizedNetwork = Optimize(*network, { Compute::CpuRef }, run->GetDeviceSpec());
972  BOOST_TEST(run->LoadNetwork(networkIdentifier, std::move(optimizedNetwork)) == Status::Success);
973  std::vector<float> goldenData(32, 0.0f);
974  std::vector<float> padOutputData(72, 0.0f);
975  armnn::OutputTensors goldenTensors{ { 0, armnn::Tensor(outputInfo, goldenData.data()) },
976  { 1, armnn::Tensor(paddedInfo, padOutputData.data()) } };
977  run->EnqueueWorkload(networkIdentifier, inputTensors, goldenTensors);
978 
979  // Now we can compare goldenData against optimizedData. They should be the same.
980  BOOST_TEST(std::equal(goldenData.begin(), goldenData.end(), optimizedData.begin()));
981  }
982  catch (const std::exception& e)
983  {
984  std::cerr << e.what() << std::endl;
985  ARMNN_ASSERT_MSG(false, e.what());
986  }
987 }
988 #endif
989 
990 class MockLayerSupport : public LayerSupportBase
991 {
992 public:
993  bool IsInputSupported(const TensorInfo& /*input*/,
994  Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
995  {
996  return true;
997  }
998 
999  bool IsOutputSupported(const TensorInfo& /*input*/,
1000  Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
1001  {
1002  return true;
1003  }
1004 
1005  bool IsActivationSupported(const TensorInfo& /*input0*/,
1006  const TensorInfo& /*output*/,
1007  const ActivationDescriptor& /*descriptor*/,
1008  Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
1009  {
1010  return true;
1011  }
1012 };
1013 
1014 template <typename NamePolicy>
1015 class MockBackend : public IBackendInternal
1016 {
1017 public:
1018  MockBackend() = default;
1019  ~MockBackend() = default;
1020 
1021  static const BackendId& GetIdStatic()
1022  {
1023  return NamePolicy::GetIdStatic();
1024  }
1025  const BackendId& GetId() const override
1026  {
1027  return GetIdStatic();
1028  }
1029 
1030  IBackendInternal::IMemoryManagerUniquePtr CreateMemoryManager() const override
1031  {
1032  return nullptr;
1033  };
1034 
1036  CreateWorkloadFactory(const IBackendInternal::IMemoryManagerSharedPtr&) const override
1037  {
1038  return nullptr;
1039  }
1040 
1041  IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override
1042  {
1043  return nullptr;
1044  }
1045 
1046  IBackendInternal::Optimizations GetOptimizations() const override
1047  {
1048  return {};
1049  }
1050  IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
1051  {
1052  return std::make_shared<MockLayerSupport>();
1053  }
1054 
1055  OptimizationViews OptimizeSubgraphView(const SubgraphView&) const override
1056  {
1057  return {};
1058  };
1059 };
1060 
1061 BOOST_AUTO_TEST_CASE(BackendCapabilityTest)
1062 {
1063  BackendId backendId = "MockBackend";
1064  // MockBackend does not support the NonConstWeights capability
1066 
1067  // MockBackend does not support the AsyncExecution capability
1069 }
1070 
1071 BOOST_AUTO_TEST_CASE(BackendHintTest)
1072 {
1073  class TestBackendAssignment : public LayerVisitorBase<VisitorNoThrowPolicy>
1074  {
1075  public:
1076  void VisitInputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name = nullptr) override
1077  {
1078  IgnoreUnused(id, name);
1079  auto inputLayer = PolymorphicDowncast<const InputLayer*>(layer);
1080  BOOST_TEST((inputLayer->GetBackendId() == "MockBackend"));
1081  }
1082 
1083  void VisitOutputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name = nullptr) override
1084  {
1085  IgnoreUnused(id, name);
1086  auto outputLayer = PolymorphicDowncast<const OutputLayer*>(layer);
1087  BOOST_TEST((outputLayer->GetBackendId() == "MockBackend"));
1088  }
1089 
1090  void VisitActivationLayer(const IConnectableLayer* layer,
1091  const ActivationDescriptor& activationDescriptor,
1092  const char* name = nullptr) override
1093  {
1094  IgnoreUnused(activationDescriptor, name);
1095  auto activation = PolymorphicDowncast<const ActivationLayer*>(layer);
1096  BOOST_TEST((activation->GetBackendId() == "CustomBackend"));
1097  }
1098  };
1099 
1100  struct CustomPolicy
1101  {
1102  static const BackendId& GetIdStatic()
1103  {
1104  static BackendId id = "CustomBackend";
1105  return id;
1106  }
1107  };
1108 
1109  struct MockPolicy
1110  {
1111  static const BackendId& GetIdStatic()
1112  {
1113  static BackendId id = "MockBackend";
1114  return id;
1115  }
1116  };
1117 
1118  auto& backendRegistry = BackendRegistryInstance();
1119 
1120  backendRegistry.Register("MockBackend", []() { return std::make_unique<MockBackend<MockPolicy>>(); });
1121 
1122  backendRegistry.Register("CustomBackend", []() { return std::make_unique<MockBackend<CustomPolicy>>(); });
1123 
1124  // Define the network
1125  auto network = INetwork::Create();
1126  ActivationDescriptor desc;
1128 
1129  std::unique_ptr<Graph> graph = std::make_unique<Graph>();
1130  auto input = graph->AddLayer<InputLayer>(0, "input");
1131  auto act = graph->AddLayer<ActivationLayer>(desc, "activation");
1132  auto output = graph->AddLayer<OutputLayer>(0, "output");
1133 
1134  BackendId customBackendId("CustomBackend");
1135  act->BackendSelectionHint(customBackendId);
1136 
1137  input->GetOutputSlot(0).Connect(act->GetInputSlot(0));
1138  act->GetOutputSlot(0).Connect(output->GetInputSlot(0));
1139 
1140  OptimizedNetworkImpl optNet(std::move(graph));
1141 
1142  // Get the optimized graph
1143  Graph& optGraph = optNet.GetGraph();
1144 
1145  std::vector<BackendId> prefs{ "MockBackend", "CustomBackend" };
1146 
1147  BackendIdSet availableBackends = { "CustomBackend", "MockBackend" };
1148  DeviceSpec spec(availableBackends);
1149 
1150  BackendSettings backendSettings(prefs, spec);
1151 
1152  // Assign an available backend to each layer
1153  Graph::Iterator firstLayer = optGraph.begin();
1154  Graph::Iterator lastLayer = optGraph.end();
1155 
1156  OptimizedNetworkImpl* optNetObjPtr = &optNet;
1157  OptimizationResult res = AssignBackends(optNetObjPtr,
1158  backendSettings,
1159  firstLayer,
1160  lastLayer,
1161  EmptyOptional());
1162 
1163  BOOST_TEST(res.IsOk());
1164 
1165  TestBackendAssignment visitor;
1166  for (auto it = firstLayer; it != lastLayer; ++it)
1167  {
1168  (*it)->Accept(visitor);
1169  }
1170 }
1171 
1172 // Tests that OptimizeForExclusiveConnections works, fusing when needed, using BatchNorm fusing as example
1173 BOOST_AUTO_TEST_CASE(OptimizeForExclusiveConnectionsFuseTest)
1174 {
1175  using namespace armnn;
1176  // Define layers information
1177  Convolution2dDescriptor convolution2dDescriptor;
1178  convolution2dDescriptor.m_BiasEnabled = false;
1179  convolution2dDescriptor.m_DataLayout = DataLayout::NHWC;
1180  BatchNormalizationDescriptor batchNormDescriptor;
1181  batchNormDescriptor.m_DataLayout = DataLayout::NHWC;
1182 
1183  const unsigned int inputDimensionSizes[] = { 1, 4, 4, 3 }; // NHWCin
1184  const unsigned int weightsDimensionSizes[] = { 1, 2, 2, 3 }; // CoutHWCin
1185  const unsigned int outputDimensionSizes[] = { 1, 3, 3, 1 }; // NHWCout
1186  const unsigned int outputChannelSize[] = { outputDimensionSizes[3] }; // Cout
1187 
1188  TensorInfo inputInfo(4, inputDimensionSizes, DataType::Float32);
1189  TensorInfo outputInfo(4, outputDimensionSizes, DataType::Float32);
1190 
1191  std::vector<float> weightsVector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
1192  ConstTensor weights(TensorInfo(4, weightsDimensionSizes, DataType::Float32), weightsVector);
1193 
1194  std::vector<float> betaVector = { 0.1f };
1195  std::vector<float> gammaVector = { 0.5f };
1196  std::vector<float> meanVector = { 0 };
1197  std::vector<float> varianceVector = { 1 };
1198  ConstTensor beta(TensorInfo(1, outputChannelSize, DataType::Float32), betaVector);
1199  ConstTensor gamma(TensorInfo(1, outputChannelSize, DataType::Float32), gammaVector);
1200  ConstTensor mean(TensorInfo(1, outputChannelSize, DataType::Float32), meanVector);
1201  ConstTensor variance(TensorInfo(1, outputChannelSize, DataType::Float32), varianceVector);
1202 
1203  // Define the network
1204  Graph graph;
1205  auto input = graph.AddLayer<InputLayer>(0, "input");
1206  auto conv = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "convolution");
1207  auto batchNorm = graph.AddLayer<BatchNormalizationLayer>(batchNormDescriptor, "batchNorm");
1208  auto output = graph.AddLayer<OutputLayer>(0, "output");
1209 
1210  // Set layer information
1211  input->GetOutputSlot().SetTensorInfo(inputInfo);
1212  conv->GetOutputSlot().SetTensorInfo(outputInfo);
1213  batchNorm->GetOutputSlot().SetTensorInfo(outputInfo);
1214  conv->m_Weight = std::make_unique<ScopedTensorHandle>(weights);
1215  batchNorm->m_Beta = std::make_unique<ScopedTensorHandle>(beta);
1216  batchNorm->m_Gamma = std::make_unique<ScopedTensorHandle>(gamma);
1217  batchNorm->m_Mean = std::make_unique<ScopedTensorHandle>(mean);
1218  batchNorm->m_Variance = std::make_unique<ScopedTensorHandle>(variance);
1219  if (convolution2dDescriptor.m_BiasEnabled)
1220  {
1221  std::vector<float> biasVector = { 11 };
1222  ConstTensor bias(TensorInfo(1, outputChannelSize, DataType::Float32), biasVector);
1223  conv->m_Bias = std::make_unique<ScopedTensorHandle>(bias);
1224  }
1225 
1226  // Connect layers
1227  input->GetOutputSlot(0).Connect(conv->GetInputSlot(0));
1228  conv->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0));
1229  batchNorm->GetOutputSlot(0).Connect(output->GetInputSlot(0));
1230 
1231  BOOST_CHECK(4 == graph.GetNumLayers());
1232  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
1233  &IsLayerOfType<InputLayer>,
1234  &IsLayerOfType<Convolution2dLayer>,
1235  &IsLayerOfType<BatchNormalizationLayer>,
1236  &IsLayerOfType<OutputLayer>));
1237 
1238  // Optimize graph
1240 
1241  auto checkFusedConv2d = [](const armnn::Layer* const layer) -> bool {
1242  return IsLayerOfType<armnn::Convolution2dLayer>(layer) &&
1243  (layer->GetNameStr() == "fused-batchNorm-into-convolution");
1244  };
1245 
1246  BOOST_CHECK(3 == graph.GetNumLayers());
1247  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
1248  &IsLayerOfType<InputLayer>,
1249  checkFusedConv2d,
1250  &IsLayerOfType<OutputLayer>));
1251 }
1252 
1253 // Tests that OptimizeForExclusiveConnections works, not fusing when not needed, using BatchNorm fusing as example
1254 BOOST_AUTO_TEST_CASE(OptimizeForExclusiveConnectionsWithoutFuseTest)
1255 {
1256  // Define the network
1257  Graph graph;
1258  Convolution2dDescriptor convolution2dDescriptor;
1259  BatchNormalizationDescriptor batchNormDescriptor;
1260 
1261  auto input = graph.AddLayer<InputLayer>(0, "input");
1262  auto conv = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "convolution");
1263  auto batchNorm = graph.AddLayer<BatchNormalizationLayer>(batchNormDescriptor, "batchNorm");
1264  auto output = graph.AddLayer<OutputLayer>(0, "output");
1265  auto output2 = graph.AddLayer<OutputLayer>(1, "output2");
1266 
1267  // Connect layers
1268  input->GetOutputSlot(0).Connect(conv->GetInputSlot(0));
1269  conv->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0));
1270  batchNorm->GetOutputSlot(0).Connect(output->GetInputSlot(0));
1271  conv->GetOutputSlot(0).Connect(output2->GetInputSlot(0));
1272 
1273  BOOST_CHECK(5 == graph.GetNumLayers());
1274  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
1275  &IsLayerOfType<armnn::InputLayer>,
1276  &IsLayerOfType<armnn::Convolution2dLayer>,
1277  &IsLayerOfType<armnn::BatchNormalizationLayer>,
1278  &IsLayerOfType<armnn::OutputLayer>,
1279  &IsLayerOfType<armnn::OutputLayer>));
1280  // Optimize graph
1282 
1283  BOOST_CHECK(5 == graph.GetNumLayers());
1284  BOOST_TEST(CheckSequence(graph.cbegin(), graph.cend(),
1285  &IsLayerOfType<armnn::InputLayer>,
1286  &IsLayerOfType<armnn::Convolution2dLayer>,
1287  &IsLayerOfType<armnn::BatchNormalizationLayer>,
1288  &IsLayerOfType<armnn::OutputLayer>,
1289  &IsLayerOfType<armnn::OutputLayer>));
1290 }
BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
std::shared_ptr< ConstTensorHandle > m_ForgetGateBias
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:69
Iterator begin()
Returns iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:162
std::shared_ptr< ConstTensorHandle > m_OutputGateBias
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:73
bool m_BiasEnabled
Enable/disable bias.
bool m_ProjectionEnabled
Enable/disable the projection layer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
OptimizationResult AssignBackends(OptimizedNetworkImpl *optNetObjPtr, BackendSettings &backendSettings, Graph::Iterator &firstLayer, Graph::Iterator &lastLayer, Optional< std::vector< std::string > &> errMessages)
Definition: Network.cpp:888
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:37
LstmBasicParameters m_BasicParameters
Definition: LstmLayer.hpp:81
This layer represents a batch normalization operation.
std::unique_ptr< IWorkloadFactory > IWorkloadFactoryPtr
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:62
uint32_t m_PadBottom
Padding bottom value in the height dimension.
std::vector< ConvertFp32ToFp16Layer * > InsertConvertFp32ToFp16LayersAfter(Graph &graph, Layer &layer)
bool m_BiasEnabled
Enable/disable bias.
std::shared_ptr< ConstTensorHandle > m_CellToForgetWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:49
std::vector< OptimizationPtr > Optimizations
const Parameters & GetParameters() const
DataLayout
Definition: Types.hpp:54
std::vector< ConvertFp16ToFp32Layer * > InsertConvertFp16ToFp32LayersBefore(Graph &graph, Layer &layer, bool expectCorrectInputType)
CPU Execution: Reference C++ kernels.
uint32_t m_PadLeft
Padding left value in the width dimension.
OptimizeForExclusiveConnection< PadLayer, Convolution2dLayer, pad_fold::FoldPadIntoConvolution2dImpl > FoldPadIntoConvolution2d
Optimizer::Optimizations MakeOptimizations(Args &&... args)
Definition: Optimizer.hpp:43
float m_ClippingThresProj
Clipping threshold value for the projection.
std::unordered_set< BackendId > BackendIdSet
Definition: BackendId.hpp:191
void BackendSelectionHint(Optional< BackendId > backend) final
Provide a hint for the optimizer as to which backend to prefer for this layer.
Definition: Layer.hpp:330
float m_PadValue
Optional value to use for padding, defaults to 0.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
This layer represents a depthwise convolution 2d operation.
LayerT * AddLayer(Args &&... args)
Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
Definition: Graph.hpp:402
uint32_t m_PoolWidth
Pooling width value.
ConstIterator cbegin() const
Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:172
A Convolution2dDescriptor for the Convolution2dLayer.
void CreateResizeBilinearGraph(Graph &graph, const unsigned int *inputShape, const unsigned int *outputShape, DataLayout dataLayout=DataLayout::NCHW)
int Connect(InputSlot &destination)
Definition: Layer.cpp:83
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
static void Pass(Graph &graph, const Optimizations &optimizations)
Definition: Optimizer.cpp:16
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:28
The padding fields don&#39;t count and are ignored.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
OptimizeForExclusiveConnection< Convolution2dLayer, BatchNormalizationLayer, FuseBatchNorm< Convolution2dLayer, armnn::DataType::Float32 > > FuseBatchNormIntoConvolution2DFloat32
std::shared_ptr< ConstTensorHandle > m_Weight
A unique pointer to store Weight values.
This layer represents an activation operation with the specified activation function.
BackendRegistry & BackendRegistryInstance()
uint32_t m_PadTop
Padding top value in the height dimension.
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:340
std::shared_ptr< ConstTensorHandle > m_ProjectionWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:39
This layer represents a detection postprocess operator.
void CreatePooling2dGraph(Graph &graph, const unsigned int *inputShape, const unsigned int *outputShape, DataLayout dataLayout=DataLayout::NCHW)
Copyright (c) 2021 ARM Limited and Contributors.
std::shared_ptr< ConstTensorHandle > m_InputToCellWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:59
std::unique_ptr< IMemoryManager > IMemoryManagerUniquePtr
This layer represents a pad operation.
Definition: PadLayer.hpp:14
This layer represents a LSTM operation.
Definition: LstmLayer.hpp:77
void IgnoreUnused(Ts &&...)
LayerList::const_iterator Iterator
Definition: Graph.hpp:50
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:243
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
A ResizeDescriptor for the ResizeLayer.
std::shared_ptr< ConstTensorHandle > m_InputGateBias
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:33
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:306
bool IsInputSupported(const BackendId &backend, const TensorInfo &input, char *reasonIfUnsupported=nullptr, size_t reasonIfUnsupportedMaxLength=1024)
Deprecated in favor of IBackend and ILayerSupport interfaces.
std::shared_ptr< ConstTensorHandle > m_CellToOutputWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:51
The SubgraphView class represents a subgraph of a Graph.
uint32_t m_PoolHeight
Pooling height value.
Constant weights can be accessed through the descriptors, On the other hand, non-const weights can be...
uint32_t m_MaxDetections
Maximum numbers of detections.
A PadDescriptor for the PadLayer.
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:316
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
std::shared_ptr< ConstTensorHandle > m_RecurrentToCellWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:65
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: OutputLayer.hpp:13
std::shared_ptr< ConstTensorHandle > m_CellBias
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:71
This layer represents a Gather operator.
Definition: GatherLayer.hpp:14
An LstmDescriptor for the LstmLayer.
uint32_t m_PadRight
Padding right value in the width dimension.
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1568
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
void CreateGatherGraph(Graph &graph, const armnn::TensorInfo &paramsInfo, const armnn::TensorInfo &indicesInfo, const armnn::TensorInfo &outputInfo)
std::shared_ptr< IMemoryManager > IMemoryManagerSharedPtr
bool IsOutputSupported(const BackendId &backend, const TensorInfo &output, char *reasonIfUnsupported=nullptr, size_t reasonIfUnsupportedMaxLength=1024)
Deprecated in favor of IBackend and ILayerSupport interfaces.
DataType GetDataType() const
Definition: Tensor.hpp:194
int NetworkId
Definition: IRuntime.hpp:22
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:314
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:341
const std::string & GetNameStr() const
Definition: Layer.hpp:220
LayerType GetType() const override
Returns the armnn::LayerType of this layer.
Definition: Layer.hpp:265
uint32_t m_TargetWidth
Target width value.
A GatherDescriptor for the GatherLayer.
std::shared_ptr< ConstTensorHandle > m_RecurrentToInputWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:31
bool m_PeepholeEnabled
Enable/disable peephole.
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:174
This layer represents a memory copy operation.
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
std::shared_ptr< ConstTensorHandle > m_RecurrentToOutputWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:67
BOOST_AUTO_TEST_CASE(CheckConvolution2dLayer)
std::shared_ptr< ConstTensorHandle > m_InputToInputWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:29
bool IsActivationSupported(const BackendId &backend, const TensorInfo &input, const TensorInfo &output, const ActivationDescriptor &descriptor, char *reasonIfUnsupported=nullptr, size_t reasonIfUnsupportedMaxLength=1024)
Deprecated in favor of IBackend and ILayerSupport interfaces.
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:25
This layer represents a floor operation.
Definition: FloorLayer.hpp:13
std::shared_ptr< ConstTensorHandle > m_ProjectionBias
A unique pointer to represent 1D weights tensor with dimensions [output_size].
Definition: LstmLayer.hpp:41
uint32_t m_TargetHeight
Target height value.
uint32_t m_ActivationFunc
The activation function to use.
Visitor base class with empty implementations.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
This layer represents a pooling 2d operation.
float m_ClippingThresCell
Clipping threshold value for the cell state.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
This layer represents an addition operation.
LstmOptPeepholeParameters m_PeepholeParameters
Definition: LstmLayer.hpp:84
void SetTensorInfo(const TensorInfo &tensorInfo)
Sets the TensorInfo used by this output handler.
std::shared_ptr< ILayerSupport > ILayerSupportSharedPtr
LstmOptProjectionParameters m_ProjectionParameters
Definition: LstmLayer.hpp:83
BOOST_AUTO_TEST_SUITE_END()
bool m_CifgEnabled
Enable/disable cifg (coupled input & forget gate).
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
std::shared_ptr< ConstTensorHandle > m_InputToForgetWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:57
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
std::shared_ptr< ConstTensorHandle > m_RecurrentToForgetWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:63
The padding fields count, but are ignored.
void InferTensorInfos()
Definition: Graph.cpp:529
std::shared_ptr< ConstTensorHandle > m_Weight
A unique pointer to store Weight values.
bool IsCapabilitySupported(const armnn::BackendId &backend, armnn::BackendCapability capability)
Convenience function to check a capability on a backend.
const OutputHandler & GetOutputHandler(unsigned int i=0) const
Definition: Layer.hpp:225
OptimizeForExclusiveConnection< PadLayer, Pooling2dLayer, pad_fold::FoldPadIntoPooling2dImpl > FoldPadIntoPooling2d
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
bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
Definition: TestUtils.hpp:21
void CreateConvolution2dGraph(Graph &graph, const unsigned int *inputShape, const unsigned int *weightsShape, const unsigned int *outputShape, DataLayout dataLayout=DataLayout::NCHW)
void SetTensorInfo(const TensorInfo &tensorInfo) override
Definition: Layer.cpp:58
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
armnn::TensorInfo scoresInfo({ 1, 6, 3 }, armnn::DataType::Float32)
DataType GetDataType() const
Definition: Layer.cpp:284
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
Definition: Layer.hpp:318
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
LstmOptCifgParameters m_CifgParameters
Definition: LstmLayer.hpp:82
ConstIterator cend() const
Returns const iterator pointing to the end of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:174
This layer represents a convolution 2d operation.
void Connect(armnn::IConnectableLayer *from, armnn::IConnectableLayer *to, const armnn::TensorInfo &tensorInfo, unsigned int fromIndex, unsigned int toIndex)
Definition: TestUtils.cpp:12
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:173
virtual int Connect(IInputSlot &destination)=0
A Pooling2dDescriptor for the Pooling2dLayer.
std::shared_ptr< ConstTensorHandle > m_CellToInputWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:47
size_t GetNumLayers() const
Definition: Graph.hpp:191
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::shared_ptr< ConstTensorHandle > m_Anchors
A unique pointer to store Anchor values.
void CreateDepthwiseConvolution2dGraph(Graph &graph, const unsigned int *inputShape, const unsigned int *weightsShape, const unsigned int *outputShape, DataLayout dataLayout=DataLayout::NCHW)
const TensorInfo & GetTensorInfo() const override
Definition: Layer.cpp:63
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:529
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square, Elu).
Definition: Descriptors.hpp:48
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
A BatchNormalizationDescriptor for the BatchNormalizationLayer.
This layer represents a resize operation.
Definition: ResizeLayer.hpp:13
std::shared_ptr< ConstTensorHandle > m_InputToOutputWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:61
std::vector< float > anchors({ 0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 10.5f, 1.0f, 1.0f, 0.5f, 10.5f, 1.0f, 1.0f, 0.5f, 100.5f, 1.0f, 1.0f })
std::unique_ptr< IBackendContext > IBackendContextPtr