ArmNN
 20.02
CreateWorkload.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include "TestUtils.hpp"
8 
9 #include <Graph.hpp>
10 #include <Network.hpp>
11 #include <ResolveType.hpp>
12 
15 
19 
20 #include <boost/test/unit_test.hpp>
21 #include <boost/cast.hpp>
22 
23 #include <utility>
24 
25 using namespace armnn;
26 
27 namespace
28 {
29 
30 using namespace std;
31 
32 // Calls CreateWorkload for a layer, and checks the returned pointer is of the correct type.
33 template<typename Workload>
34 std::unique_ptr<Workload> MakeAndCheckWorkload(Layer& layer, const IWorkloadFactory& factory)
35 {
36  std::unique_ptr<IWorkload> workload = layer.CreateWorkload(factory);
37  BOOST_TEST(workload.get() == boost::polymorphic_downcast<Workload*>(workload.get()),
38  "Cannot convert to derived class");
39  std::string reasonIfUnsupported;
40  layer.SetBackendId(factory.GetBackendId());
41  BOOST_TEST(factory.IsLayerSupported(layer, layer.GetDataType(), reasonIfUnsupported));
42  return std::unique_ptr<Workload>(static_cast<Workload*>(workload.release()));
43 }
44 
45 // Helper function to create tensor handlers for workloads, assuming they all use the same factory.
46 void CreateTensorHandles(armnn::Graph& graph,
47  armnn::IWorkloadFactory& factory)
48 {
49  TensorHandleFactoryRegistry tmpRegistry;
50  for (auto&& layer : graph.TopologicalSort())
51  {
52  layer->CreateTensorHandles(tmpRegistry, factory);
53  }
54 }
55 
56 /////////////////////////////////////////////////////////////////////////////////////////////
57 // The following functions are called by backendsCommon/test/CreateWorkload*.cpp
58 // They build very simple graphs, and then create a workload.
59 // Some checks are performed on the workload to ensure parameters have been passed correctly.
60 // They return the created workloads so that backend-specific checks can be performed.
61 /////////////////////////////////////////////////////////////////////////////////////////////
62 
63 template <typename ActivationWorkload, armnn::DataType DataType>
64 std::unique_ptr<ActivationWorkload> CreateActivationWorkloadTest(armnn::IWorkloadFactory& factory,
65  armnn::Graph& graph)
66 {
67  // Creates the layer we're testing.
68  ActivationDescriptor layerDesc;
70  layerDesc.m_A = 3.5f;
71  layerDesc.m_B = -10.0f;
72 
73  ActivationLayer* const layer = graph.AddLayer<ActivationLayer>(layerDesc, "layer");
74 
75  // Creates extra layers.
76  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
77  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
78 
79  // Connects up.
80  armnn::TensorInfo tensorInfo({1, 1}, DataType);
81 
82  Connect(input, layer, tensorInfo);
83  Connect(layer, output, tensorInfo);
84 
85  CreateTensorHandles(graph, factory);
86 
87  // Makes the workload and checks it.
88  auto workload = MakeAndCheckWorkload<ActivationWorkload>(*layer, factory);
89 
90  ActivationQueueDescriptor queueDescriptor = workload->GetData();
91  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
92  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
93  BOOST_TEST(queueDescriptor.m_Parameters.m_A == 3.5f);
94  BOOST_TEST(queueDescriptor.m_Parameters.m_B == -10.0f);
95  BOOST_TEST((queueDescriptor.m_Parameters.m_Function == ActivationFunction::Abs));
96 
97  // Returns so we can do extra, backend-specific tests.
98  return workload;
99 }
100 
101 template <typename WorkloadType,
102  typename DescriptorType,
103  typename LayerType,
105 std::unique_ptr<WorkloadType> CreateElementwiseWorkloadTest(armnn::IWorkloadFactory & factory,
106  armnn::Graph & graph)
107 {
108  // Creates the layer we're testing.
109  Layer* const layer = graph.AddLayer<LayerType>("layer");
110 
111  // Creates extra layers.
112  Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
113  Layer* const input2 = graph.AddLayer<InputLayer>(2, "input2");
114  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
115 
116  // Connects up.
117  armnn::TensorInfo tensorInfo({2, 3}, DataType);
118  Connect(input1, layer, tensorInfo, 0, 0);
119  Connect(input2, layer, tensorInfo, 0, 1);
120  Connect(layer, output, tensorInfo);
121  CreateTensorHandles(graph, factory);
122 
123  // Makes the workload and checks it.
124  auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, factory);
125 
126  DescriptorType queueDescriptor = workload->GetData();
127  BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
128  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
129 
130  // Returns so we can do extra, backend-specific tests.
131  return workload;
132 }
133 
134 template <typename WorkloadType,
135  typename DescriptorType,
137 std::unique_ptr<WorkloadType> CreateElementwiseUnaryWorkloadTest(armnn::IWorkloadFactory & factory,
138  armnn::Graph & graph,
140 {
142  Layer* const layer = graph.AddLayer<armnn::ElementwiseUnaryLayer>(desc, "layer");
143 
144  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
145  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
146 
147  armnn::TensorInfo tensorInfo({ 2, 3 }, DataType);
148  Connect(input, layer, tensorInfo, 0, 0);
149  Connect(layer, output, tensorInfo, 0, 0);
150  CreateTensorHandles(graph, factory);
151 
152  auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, factory);
153  DescriptorType queueDescriptor = workload->GetData();
154 
155  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
156  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
157 
158  return workload;
159 }
160 
161 template <typename BatchNormalizationWorkloadType, armnn::DataType DataType>
162 std::unique_ptr<BatchNormalizationWorkloadType> CreateBatchNormalizationWorkloadTest(
163  armnn::IWorkloadFactory& factory, armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
164 {
165  TensorShape tensorShape;
166  switch (dataLayout)
167  {
168  case DataLayout::NHWC:
169  tensorShape = { 2, 4, 4, 3 };
170  break;
171  case DataLayout::NCHW:
172  default:
173  tensorShape = { 2, 3, 4, 4 };
174  }
175 
176  // Creates the layer we're testing.
178  layerDesc.m_Eps = 0.05f;
179  layerDesc.m_DataLayout = dataLayout;
180 
181  BatchNormalizationLayer* const layer = graph.AddLayer<BatchNormalizationLayer>(layerDesc, "layer");
182 
183  armnn::TensorInfo weightInfo({3}, DataType);
184  layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
185  layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
186  layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
187  layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
188  layer->m_Mean->Allocate();
189  layer->m_Variance->Allocate();
190  layer->m_Beta->Allocate();
191  layer->m_Gamma->Allocate();
192 
193  // Creates extra layers.
194  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
195  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
196 
197  // Connects up.
198  armnn::TensorInfo tensorInfo(tensorShape, DataType);
199  Connect(input, layer, tensorInfo);
200  Connect(layer, output, tensorInfo);
201  CreateTensorHandles(graph, factory);
202 
203  // Makes the workload and checks it.
204  auto workload = MakeAndCheckWorkload<BatchNormalizationWorkloadType>(*layer, factory);
205  BatchNormalizationQueueDescriptor queueDescriptor = workload->GetData();
206  BOOST_TEST(queueDescriptor.m_Parameters.m_Eps == 0.05f);
207  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
208  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
209  BOOST_TEST((queueDescriptor.m_Mean->GetTensorInfo() == TensorInfo({3}, DataType)));
210  BOOST_TEST((queueDescriptor.m_Variance->GetTensorInfo() == TensorInfo({3}, DataType)));
211  BOOST_TEST((queueDescriptor.m_Gamma->GetTensorInfo() == TensorInfo({3}, DataType)));
212  BOOST_TEST((queueDescriptor.m_Beta->GetTensorInfo() == TensorInfo({3}, DataType)));
213  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
214 
215  // Returns so we can do extra, backend-specific tests.
216  return workload;
217 }
218 
219 template <typename Convolution2dWorkload, armnn::DataType DataType>
220 std::unique_ptr<Convolution2dWorkload> CreateConvolution2dWorkloadTest(armnn::IWorkloadFactory& factory,
221  armnn::Graph& graph,
222  DataLayout dataLayout = DataLayout::NCHW)
223 {
224  // Creates the layer we're testing.
225  Convolution2dDescriptor layerDesc;
226  layerDesc.m_PadLeft = 3;
227  layerDesc.m_PadRight = 3;
228  layerDesc.m_PadTop = 1;
229  layerDesc.m_PadBottom = 1;
230  layerDesc.m_StrideX = 2;
231  layerDesc.m_StrideY = 4;
232  layerDesc.m_BiasEnabled = true;
233  layerDesc.m_DataLayout = dataLayout;
234 
235  Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
236 
237  TensorShape weightShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 5, 3} : TensorShape{2, 5, 3, 3};
238  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 8, 16} : TensorShape{2, 8, 16, 3};
239  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 2, 2, 10} : TensorShape{2, 2, 10, 2};
240 
241  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo(weightShape, DataType));
242  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({2}, GetBiasDataType(DataType)));
243 
244  layer->m_Weight->Allocate();
245  layer->m_Bias->Allocate();
246 
247  // Creates extra layers.
248  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
249  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
250 
251  // Connects up.
252  Connect(input, layer, TensorInfo(inputShape, DataType));
253  Connect(layer, output, TensorInfo(outputShape, DataType));
254  CreateTensorHandles(graph, factory);
255 
256  // Makes the workload and checks it.
257  auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory);
258 
259  Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
260  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
261  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 4);
262  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 3);
263  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 3);
264  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
265  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
266  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled);
267  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
268 
269  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
270  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
271  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
272  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() ==
274 
275  // Returns so we can do extra, backend-specific tests.
276  return workload;
277 }
278 
279 template <typename LstmWorkload>
280 std::unique_ptr<LstmWorkload> CreateLstmWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
281 {
282  // This parameter setting is for withCifgWithPeepholeNoProjection
283  LstmDescriptor layerDesc;
284  layerDesc.m_ActivationFunc = 4;
285  layerDesc.m_ClippingThresCell = 0.0f;
286  layerDesc.m_ClippingThresProj = 0.0f;
287  layerDesc.m_CifgEnabled = true;
288  layerDesc.m_PeepholeEnabled = true;
289  layerDesc.m_ProjectionEnabled = false;
290 
291  LstmLayer* const layer = graph.AddLayer<LstmLayer>(layerDesc, "layer");
292  unsigned int batchSize = 2;
293  unsigned int inputSize = 2;
294  unsigned int numUnits = 4;
295  unsigned int outputSize = 4;
296 
297  layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
298  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
299  layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedCpuTensorHandle>
300  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
301  layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
302  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
303  layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
304  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
305  layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedCpuTensorHandle>
306  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
307  layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
308  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
309  layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>
310  (TensorInfo({ numUnits }, DataType::Float32));
311  layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>
312  (TensorInfo({ numUnits }, DataType::Float32));
313  layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>
314  (TensorInfo({ numUnits }, DataType::Float32));
315 
316  layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
317  layer->m_BasicParameters.m_InputToCellWeights->Allocate();
318  layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
320  layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
322  layer->m_BasicParameters.m_ForgetGateBias->Allocate();
323  layer->m_BasicParameters.m_CellBias->Allocate();
324  layer->m_BasicParameters.m_OutputGateBias->Allocate();
325 
326 
327  if (layerDesc.m_PeepholeEnabled)
328  {
329  layer->m_PeepholeParameters.m_CellToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
330  (TensorInfo({ numUnits }, DataType::Float32));
331  layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
332  (TensorInfo({ numUnits }, DataType::Float32));
333  layer->m_PeepholeParameters.m_CellToForgetWeights->Allocate();
334  layer->m_PeepholeParameters.m_CellToOutputWeights->Allocate();
335  }
336 
337  // create input and output layers
338  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
339  Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
340  Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
341  Layer* const scratchBuffer = graph.AddLayer<OutputLayer>(0, "scratchBuffer");
342  Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
343  Layer* const cellStateOut = graph.AddLayer<OutputLayer>(2, "cellStateOut");
344  Layer* const output = graph.AddLayer<OutputLayer>(3, "output");
345 
346  // connect up
347  armnn::TensorInfo lstmTensorInfo1({ batchSize, inputSize }, DataType::Float32);
348  armnn::TensorInfo lstmTensorInfo2({ batchSize, numUnits}, DataType::Float32);
349  armnn::TensorInfo lstmTensorInfo3({ batchSize, outputSize }, DataType::Float32);
350  armnn::TensorInfo lstmTensorInfoScratchBuff({ batchSize, numUnits * (layerDesc.m_CifgEnabled ? 3 : 4) },
352  Connect(input, layer, lstmTensorInfo1, 0, 0);
353  Connect(cellStateIn, layer, lstmTensorInfo2, 0, 1);
354  Connect(outputStateIn, layer, lstmTensorInfo3, 0, 2);
355  Connect(layer, scratchBuffer, lstmTensorInfoScratchBuff, 0, 0);
356  Connect(layer, outputStateOut, lstmTensorInfo3, 1, 0);
357  Connect(layer, cellStateOut, lstmTensorInfo2, 2, 0);
358  Connect(layer, output, lstmTensorInfo3, 3, 0);
359 
360  CreateTensorHandles(graph, factory);
361 
362  // make the workload and check it
363  auto workload = MakeAndCheckWorkload<LstmWorkload>(*layer, factory);
364  LstmQueueDescriptor queueDescriptor = workload->GetData();
365  BOOST_TEST(queueDescriptor.m_Parameters.m_ActivationFunc == 4);
366  BOOST_TEST(queueDescriptor.m_Parameters.m_ClippingThresCell == 0.0f);
367  BOOST_TEST(queueDescriptor.m_Parameters.m_ClippingThresProj == 0.0f);
368  BOOST_TEST(queueDescriptor.m_Inputs.size() == 3);
369  BOOST_TEST(queueDescriptor.m_Outputs.size() == 4);
370 
371  BOOST_TEST((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == TensorInfo({ numUnits, inputSize },
373  BOOST_TEST((queueDescriptor.m_OutputGateBias->GetTensorInfo() == TensorInfo({ numUnits },
375  BOOST_TEST((queueDescriptor.m_CellBias->GetTensorInfo() == TensorInfo({ numUnits }, DataType::Float32)));
376  return workload;
377 }
378 
379 template <typename QuantizedLstmWorkload>
380 std::unique_ptr<QuantizedLstmWorkload> CreateQuantizedLstmWorkloadTest(armnn::IWorkloadFactory& factory,
381  armnn::Graph& graph)
382 {
383  auto layer = graph.AddLayer<QuantizedLstmLayer>("quantizedLstmlayer");
384  unsigned int numBatches = 2;
385  unsigned int inputSize = 2;
386  unsigned int outputSize = 4;
387 
388  // Scale/Offset for input/output, cellState In/Out, weights, bias
389  float inputOutputScale = 0.0078125f;
390  int32_t inputOutputOffset = 128;
391 
392  float cellStateScale = 0.00048828125f;
393  int32_t cellStateOffset = 0;
394 
395  float weightsScale = 0.00408021f;
396  int32_t weightsOffset = 100;
397 
398  float biasScale = 3.1876640625e-05f;
399  int32_t biasOffset = 0;
400 
401  // Weights and bias tensor and quantization info
402  armnn::TensorInfo inputWeightsInfo({outputSize, inputSize},
404  weightsScale,
405  weightsOffset);
406 
407  armnn::TensorInfo recurrentWeightsInfo({outputSize, outputSize},
409  weightsScale,
410  weightsOffset);
411 
412  armnn::TensorInfo biasInfo({outputSize},
414  biasScale,
415  biasOffset);
416 
417  // Weights and bias
418  layer->m_QuantizedLstmParameters.m_InputToInputWeights =
419  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
420  layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
421  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
422  layer->m_QuantizedLstmParameters.m_InputToCellWeights =
423  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
424  layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
425  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
426 
427  layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
428  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
429  layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
430  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
431  layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
432  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
433  layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
434  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
435 
436  layer->m_QuantizedLstmParameters.m_InputGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
437  layer->m_QuantizedLstmParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
438  layer->m_QuantizedLstmParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
439  layer->m_QuantizedLstmParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
440 
441  // Allocate weights and bias
442  layer->m_QuantizedLstmParameters.m_InputToInputWeights->Allocate();
443  layer->m_QuantizedLstmParameters.m_InputToForgetWeights->Allocate();
444  layer->m_QuantizedLstmParameters.m_InputToCellWeights->Allocate();
445  layer->m_QuantizedLstmParameters.m_InputToOutputWeights->Allocate();
446 
447  layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights->Allocate();
448  layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights->Allocate();
449  layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights->Allocate();
450  layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights->Allocate();
451 
452  layer->m_QuantizedLstmParameters.m_InputGateBias->Allocate();
453  layer->m_QuantizedLstmParameters.m_ForgetGateBias->Allocate();
454  layer->m_QuantizedLstmParameters.m_CellBias->Allocate();
455  layer->m_QuantizedLstmParameters.m_OutputGateBias->Allocate();
456 
457  // Create input and output layers
458  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
459  Layer* const cellStateIn = graph.AddLayer<InputLayer>(1, "cellStateIn");
460  Layer* const outputStateIn = graph.AddLayer<InputLayer>(2, "outputStateIn");
461 
462  Layer* const cellStateOut = graph.AddLayer<OutputLayer>(0, "cellStateOut");
463  Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
464 
465  // Input/output tensor info and quantization info
466  armnn::TensorInfo inputInfo({numBatches , inputSize},
468  inputOutputScale,
469  inputOutputOffset);
470 
471  armnn::TensorInfo cellStateInfo({numBatches , outputSize},
473  cellStateScale,
474  cellStateOffset);
475 
476  armnn::TensorInfo outputStateInfo({numBatches , outputSize},
478  inputOutputScale,
479  inputOutputOffset);
480 
481  // Connect input/output slots
482  Connect(input, layer, inputInfo, 0, 0);
483  Connect(cellStateIn, layer, cellStateInfo, 0, 1);
484  Connect(outputStateIn, layer, outputStateInfo, 0, 2);
485 
486  Connect(layer, cellStateOut, cellStateInfo, 0, 0);
487  Connect(layer, outputStateOut, outputStateInfo, 1, 0);
488 
489  CreateTensorHandles(graph, factory);
490 
491  // Create workload and check layer support
492  auto workload = MakeAndCheckWorkload<QuantizedLstmWorkload>(*layer, factory);
493  QuantizedLstmQueueDescriptor queueDescriptor = workload->GetData();
494 
495  // Validate input/output sizes
496  BOOST_TEST(queueDescriptor.m_Inputs.size() == 3);
497  BOOST_TEST(queueDescriptor.m_Outputs.size() == 2);
498 
499  // Validate weight tensor info
500  BOOST_TEST((queueDescriptor.m_InputToInputWeights->GetTensorInfo() == inputWeightsInfo));
501  BOOST_TEST((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == inputWeightsInfo));
502  BOOST_TEST((queueDescriptor.m_InputToCellWeights->GetTensorInfo() == inputWeightsInfo));
503  BOOST_TEST((queueDescriptor.m_InputToOutputWeights->GetTensorInfo() == inputWeightsInfo));
504 
505  BOOST_TEST((queueDescriptor.m_RecurrentToInputWeights->GetTensorInfo() == recurrentWeightsInfo));
506  BOOST_TEST((queueDescriptor.m_RecurrentToForgetWeights->GetTensorInfo() == recurrentWeightsInfo));
507  BOOST_TEST((queueDescriptor.m_RecurrentToCellWeights->GetTensorInfo() == recurrentWeightsInfo));
508  BOOST_TEST((queueDescriptor.m_RecurrentToOutputWeights->GetTensorInfo() == recurrentWeightsInfo));
509 
510  BOOST_TEST((queueDescriptor.m_InputGateBias->GetTensorInfo() == biasInfo));
511  BOOST_TEST((queueDescriptor.m_ForgetGateBias->GetTensorInfo() == biasInfo));
512  BOOST_TEST((queueDescriptor.m_CellBias->GetTensorInfo() == biasInfo));
513  BOOST_TEST((queueDescriptor.m_OutputGateBias->GetTensorInfo() == biasInfo));
514 
515  return workload;
516 }
517 
518 template <typename Convolution2dWorkload, armnn::DataType DataType>
519 std::unique_ptr<Convolution2dWorkload> CreateDirectConvolution2dWorkloadTest(armnn::IWorkloadFactory& factory,
520  armnn::Graph& graph)
521 {
522  // Creates the layer we're testing.
523  Convolution2dDescriptor layerDesc;
524  layerDesc.m_PadLeft = 1;
525  layerDesc.m_PadRight = 1;
526  layerDesc.m_PadTop = 1;
527  layerDesc.m_PadBottom = 1;
528  layerDesc.m_StrideX = 1;
529  layerDesc.m_StrideY = 1;
530  layerDesc.m_BiasEnabled = true;
531 
532  Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
533 
534  float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
535  float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
536 
537  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({ 2, 3, 3, 3 }, DataType, inputsQScale));
538  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>
539  (TensorInfo({2}, GetBiasDataType(DataType), inputsQScale));
540  layer->m_Weight->Allocate();
541  layer->m_Bias->Allocate();
542 
543  // Creates extra layers.
544  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
545  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
546 
547  // Connects up.
548  Connect(input, layer, TensorInfo({2, 3, 6, 6}, DataType, inputsQScale));
549  Connect(layer, output, TensorInfo({2, 2, 6, 6}, DataType, outputQScale));
550  CreateTensorHandles(graph, factory);
551 
552  // Makes the workload and checks it.
553  auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory);
554 
555  Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
556  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 1);
557  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 1);
558  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 1);
559  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 1);
560  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
561  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
562  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
563 
564  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
565  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
566  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({2, 3, 3, 3},
567  DataType, inputsQScale)));
568  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo()
569  == TensorInfo({2}, GetBiasDataType(DataType), inputsQScale)));
570 
571  // Returns so we can do extra, backend-specific tests.
572  return workload;
573 }
574 
575 template <typename DepthwiseConvolution2dFloat32Workload, armnn::DataType DataType>
576 std::unique_ptr<DepthwiseConvolution2dFloat32Workload> CreateDepthwiseConvolution2dWorkloadTest(
577  armnn::IWorkloadFactory& factory, armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
578 {
579  // Creates the layer we're testing.
581  layerDesc.m_PadLeft = 1;
582  layerDesc.m_PadRight = 2;
583  layerDesc.m_PadTop = 1;
584  layerDesc.m_PadBottom = 2;
585  layerDesc.m_StrideX = 1;
586  layerDesc.m_StrideY = 1;
587  layerDesc.m_BiasEnabled = false;
588  layerDesc.m_DataLayout = dataLayout;
589 
590  DepthwiseConvolution2dLayer* const layer = graph.AddLayer<DepthwiseConvolution2dLayer>(layerDesc, "layer");
591 
592  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({1, 2, 4, 4}, DataType)); // [ M, I, H, W ]
593  layer->m_Weight->Allocate();
594 
595  // Creates extra layers.
596  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
597  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
598 
599  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
600  TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
601  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
602  TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
603 
604  // Connects up.
605  Connect(input, layer, TensorInfo(inputShape, DataType));
606  Connect(layer, output, TensorInfo(outputShape, DataType));
607  CreateTensorHandles(graph, factory);
608 
609  // Makes the workload and checks it.
610  auto workload = MakeAndCheckWorkload<DepthwiseConvolution2dFloat32Workload>(*layer, factory);
611 
612  DepthwiseConvolution2dQueueDescriptor queueDescriptor = workload->GetData();
613  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 1);
614  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 1);
615  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 1);
616  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 2);
617  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
618  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 2);
619  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == false);
620  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
621 
622  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
623  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
624  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({1, 2, 4, 4}, DataType)));
625 
626  // Returns so we can do extra, backend-specific tests.
627  return workload;
628 }
629 
630 template <typename FullyConnectedWorkload, armnn::DataType DataType>
631 std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWorkloadTest(armnn::IWorkloadFactory& factory,
632  armnn::Graph& graph)
633 {
634  // Creates the layer we're testing.
635  FullyConnectedDescriptor layerDesc;
636  layerDesc.m_BiasEnabled = true;
637  layerDesc.m_TransposeWeightMatrix = true;
638 
639  FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
640 
641  float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
642  float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
643 
644  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7, 20}, DataType, inputsQScale, 0));
645  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7}, GetBiasDataType(DataType), inputsQScale));
646  layer->m_Weight->Allocate();
647  layer->m_Bias->Allocate();
648 
649  // Creates extra layers.
650  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
651  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
652 
653  // Connects up.
654  Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale));
655  Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
656  CreateTensorHandles(graph, factory);
657 
658  // Makes the workload and checks it.
659  auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
660 
661  FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
662  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
663  BOOST_TEST(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
664 
665  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
666  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
667  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({7, 20}, DataType, inputsQScale)));
668  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() == TensorInfo({7}, GetBiasDataType(DataType), inputsQScale)));
669 
670  // Returns so we can do extra, backend-specific tests.
671  return workload;
672 }
673 
674 template <typename NormalizationWorkload, armnn::DataType DataType>
675 std::unique_ptr<NormalizationWorkload> CreateNormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
676  armnn::Graph& graph,
677  DataLayout dataLayout = DataLayout::NCHW)
678 {
679  // Creates the layer we're testing.
680  NormalizationDescriptor layerDesc;
683  layerDesc.m_NormSize = 3;
684  layerDesc.m_Alpha = 0.5f;
685  layerDesc.m_Beta = -1.0f;
686  layerDesc.m_K = 0.2f;
687  layerDesc.m_DataLayout = dataLayout;
688 
689  NormalizationLayer* layer = graph.AddLayer<NormalizationLayer>(layerDesc, "layer");
690 
691  // Creates extra layers.
692  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
693  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
694 
695  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
696  TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
697  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
698  TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
699 
700  // Connects up.
701  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
702  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
703  Connect(input, layer, inputTensorInfo);
704  Connect(layer, output, outputTensorInfo);
705  CreateTensorHandles(graph, factory);
706 
707  // Makes the workload and checks it.
708  auto workload = MakeAndCheckWorkload<NormalizationWorkload>(*layer, factory);
709 
710  NormalizationQueueDescriptor queueDescriptor = workload->GetData();
711  BOOST_TEST((queueDescriptor.m_Parameters.m_NormChannelType == NormalizationAlgorithmChannel::Across));
712  BOOST_TEST((queueDescriptor.m_Parameters.m_NormMethodType == NormalizationAlgorithmMethod::LocalBrightness));
713  BOOST_TEST(queueDescriptor.m_Parameters.m_NormSize == 3);
714  BOOST_TEST(queueDescriptor.m_Parameters.m_Alpha == 0.5f);
715  BOOST_TEST(queueDescriptor.m_Parameters.m_Beta == -1.0f);
716  BOOST_TEST(queueDescriptor.m_Parameters.m_K == 0.2f);
717  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
718 
719  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
720  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
721 
722  // Returns so we can do extra, backend-specific tests.
723  return workload;
724 }
725 
726 template <typename Pooling2dWorkload, armnn::DataType DataType>
727 std::unique_ptr<Pooling2dWorkload> CreatePooling2dWorkloadTest(armnn::IWorkloadFactory& factory,
728  armnn::Graph& graph,
729  DataLayout dataLayout = DataLayout::NCHW)
730 {
731  // Creates the layer we're testing.
732  Pooling2dDescriptor layerDesc;
734  layerDesc.m_PoolWidth = 3;
735  layerDesc.m_PoolHeight = 3;
736  layerDesc.m_PadLeft = 2;
737  layerDesc.m_PadRight = 2;
738  layerDesc.m_PadTop = 1;
739  layerDesc.m_PadBottom = 1;
740  layerDesc.m_StrideX = 2;
741  layerDesc.m_StrideY = 3;
743  layerDesc.m_DataLayout = dataLayout;
744 
745  Pooling2dLayer* const layer = graph.AddLayer<Pooling2dLayer>(layerDesc, "layer");
746 
747  // Create extra layers
748  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
749  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
750 
751  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 5, 5} : TensorShape{3, 5, 5, 2};
752  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 2, 4} : TensorShape{3, 2, 4, 2};
753 
754  // Connect up
755  Connect(input, layer, TensorInfo(inputShape, DataType));
756  Connect(layer, output, TensorInfo(outputShape, DataType));
757  CreateTensorHandles(graph, factory);
758 
759  // Make the workload and checks it
760  auto workload = MakeAndCheckWorkload<Pooling2dWorkload>(*layer, factory);
761 
762  Pooling2dQueueDescriptor queueDescriptor = workload->GetData();
763  BOOST_TEST((queueDescriptor.m_Parameters.m_PoolType == PoolingAlgorithm::Average));
764  BOOST_TEST((queueDescriptor.m_Parameters.m_OutputShapeRounding == OutputShapeRounding::Floor));
765  BOOST_TEST(queueDescriptor.m_Parameters.m_PoolWidth == 3);
766  BOOST_TEST(queueDescriptor.m_Parameters.m_PoolHeight == 3);
767  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
768  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 3);
769  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 2);
770  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 2);
771  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
772  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
773  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
774 
775  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
776  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
777 
778  // Return so we can do extra, backend-specific tests
779  return workload;
780 }
781 
782 template <typename SoftmaxWorkload, armnn::DataType DataType>
783 std::unique_ptr<SoftmaxWorkload> CreateSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
784  armnn::Graph& graph)
785 {
786  // Create the layer we're testing.
787  SoftmaxDescriptor softmaxDescriptor;
788  // Set Axis to 1 if CL or Neon until further Axes are supported.
790  {
791  softmaxDescriptor.m_Axis = 1;
792  }
793 
794  Layer* const layer = graph.AddLayer<SoftmaxLayer>(softmaxDescriptor, "layer");
795  // Create extra layers.
796  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
797  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
798 
799  // Connect up
800  armnn::TensorInfo tensorInfo({4, 1}, DataType);
801  Connect(input, layer, tensorInfo);
802  Connect(layer, output, tensorInfo);
803  CreateTensorHandles(graph, factory);
804 
805  // Make the workload and checks it.
806  auto workload = MakeAndCheckWorkload<SoftmaxWorkload>(*layer, factory);
807 
808  SoftmaxQueueDescriptor queueDescriptor = workload->GetData();
809  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
810  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
811 
812  // Return so we can do extra, backend-specific tests.
813  return workload;
814 }
815 
816 template<typename SplitterWorkload, armnn::DataType DataType>
817 std::unique_ptr<SplitterWorkload>
818  CreateSplitterWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
819 {
820  // Create the layer we're testing.
821  // NOTE: need three dimensions channels, height/y, width/x because the Compute
822  // library restricts subtensors to have the same x and y dimensions as
823  // their parent tensors, and therefore the origin on the x and y dimension
824  // has to be zero for any view. So we need a third dimension to split...
825  // NOTE: arguments are: number of views, number of dimensions.
826  ViewsDescriptor layerDesc(3, 3);
827  // NOTE: arguments are: view, dimension, value.
828  layerDesc.SetViewOriginCoord(0, 0, 0);
829  layerDesc.SetViewOriginCoord(1, 0, 1);
830  layerDesc.SetViewOriginCoord(2, 0, 3);
831 
832  Layer* const layer = graph.AddLayer<SplitterLayer>(layerDesc, "layer");
833 
834  // Adds extra layers.
835  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
836  Layer* const output0 = graph.AddLayer<OutputLayer>(0, "output0");
837  Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
838  Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
839 
840  // Connects up.
841  armnn::TensorInfo tensorInfo({5, 7, 7}, DataType);
842  Connect(input, layer, tensorInfo);
843 
844  armnn::TensorInfo output0Info({1, 7, 7}, DataType);
845  armnn::TensorInfo output1Info({2, 7, 7}, DataType);
846  armnn::TensorInfo output2Info({2, 7, 7}, DataType);
847 
848  Connect(layer, output0, output0Info, 0, 0);
849  Connect(layer, output1, output1Info, 1, 0);
850  Connect(layer, output2, output2Info, 2, 0);
851 
852  CreateTensorHandles(graph, factory);
853 
854  // Makes the workload and checks it.
855  auto workload = MakeAndCheckWorkload<SplitterWorkload>(*layer, factory);
856 
857  SplitterQueueDescriptor queueDescriptor = workload->GetData();
858  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
859  BOOST_TEST(queueDescriptor.m_Outputs.size() == 3);
860  BOOST_TEST(queueDescriptor.m_ViewOrigins.size() == 3);
861 
862  BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[0] == 0);
863  BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[0] == 1);
864  BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[0] == 3);
865  BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[1] == 0);
866  BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[1] == 0);
867  BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[1] == 0);
868  BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[2] == 0);
869  BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[2] == 0);
870  BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[2] == 0);
871 
872  // Returns so we can do extra, backend-specific tests.
873  return workload;
874 }
875 
876 /// This function constructs a graph with both a splitter and a concat, and returns a pair of the workloads.
877 template<typename SplitterWorkload, typename ConcatWorkload, armnn::DataType DataType>
878 std::pair<std::unique_ptr<SplitterWorkload>, std::unique_ptr<ConcatWorkload>>
879  CreateSplitterConcatWorkloadTest(armnn::IWorkloadFactory &factory, armnn::Graph &graph)
880 {
881  armnn::TensorInfo inputTensorInfo({ 1, 2, 100, 10 }, DataType);
882 
883  armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 10 }, DataType);
884  armnn::TensorInfo splitTensorInfo2({ 1, 1, 100, 10 }, DataType);
885 
886  //Constructs the graph.
887  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
888 
889  armnn::ViewsDescriptor splitterViews(2);
890  splitterViews.SetViewOriginCoord(0, 0, 0);
891  splitterViews.SetViewOriginCoord(0, 1, 0);
892  splitterViews.SetViewOriginCoord(0, 2, 0);
893  splitterViews.SetViewOriginCoord(0, 3, 0);
894 
895  splitterViews.SetViewOriginCoord(1, 0, 0);
896  splitterViews.SetViewOriginCoord(1, 1, 1);
897  splitterViews.SetViewOriginCoord(1, 2, 0);
898  splitterViews.SetViewOriginCoord(1, 3, 0);
899 
900  Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
901  BOOST_TEST_CHECKPOINT("created splitter layer");
902 
903  armnn::OriginsDescriptor concatViews(2);
904  concatViews.SetViewOriginCoord(0, 0, 0);
905  concatViews.SetViewOriginCoord(0, 1, 1);
906  concatViews.SetViewOriginCoord(0, 2, 0);
907  concatViews.SetViewOriginCoord(0, 3, 0);
908 
909  concatViews.SetViewOriginCoord(1, 0, 0);
910  concatViews.SetViewOriginCoord(1, 1, 0);
911  concatViews.SetViewOriginCoord(1, 2, 0);
912  concatViews.SetViewOriginCoord(1, 3, 0);
913 
914  Layer* const concat = graph.AddLayer<ConcatLayer>(concatViews, "concat");
915  BOOST_TEST_CHECKPOINT("created concat layer");
916 
917  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
918 
919  // Adds connections.
920  Connect(input, splitter, inputTensorInfo, 0, 0);
921  BOOST_TEST_CHECKPOINT("connect input to splitter");
922  Connect(splitter, concat, splitTensorInfo1, 0, 1); // The splitter & concat are connected up.
923  BOOST_TEST_CHECKPOINT("connect splitter[0] to concat[1]");
924  Connect(splitter, concat, splitTensorInfo2, 1, 0); // So that the outputs are flipped round.
925  BOOST_TEST_CHECKPOINT("connect splitter[1] to concat[0]");
926  Connect(concat, output, inputTensorInfo, 0, 0);
927  BOOST_TEST_CHECKPOINT("connect concat to output");
928 
929  CreateTensorHandles(graph, factory);
930  BOOST_TEST_CHECKPOINT("created tensor handles");
931 
932  auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
933  BOOST_TEST_CHECKPOINT("created splitter workload");
934  auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
935  BOOST_TEST_CHECKPOINT("created concat workload");
936 
937  return {std::move(workloadSplitter), std::move(workloadConcat)};
938 }
939 
940 
941 /// This function constructs a graph with a splitter with two outputs. Each of the outputs is then
942 /// connected to two different activation layers
943 template<typename SplitterWorkload, typename ActivationWorkload, armnn::DataType DataType>
944 void CreateSplitterMultipleInputsOneOutputWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph,
945  std::unique_ptr<SplitterWorkload>& wlSplitter,
946  std::unique_ptr<ActivationWorkload>& wlActiv0_0,
947  std::unique_ptr<ActivationWorkload>& wlActiv0_1,
948  std::unique_ptr<ActivationWorkload>& wlActiv1_0,
949  std::unique_ptr<ActivationWorkload>& wlActiv1_1)
950 {
951  armnn::TensorInfo inputTensorInfo ({ 1, 3, 100, 50 }, DataType);
952  armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 50 }, DataType);
953  armnn::TensorInfo splitTensorInfo2({ 1, 2, 100, 50 }, DataType);
954 
955  //Constructs the graph.
956  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
957 
958  armnn::ViewsDescriptor splitterViews(2);
959 
960  splitterViews.SetViewOriginCoord(0, 0, 0);
961  splitterViews.SetViewOriginCoord(0, 1, 0);
962  splitterViews.SetViewOriginCoord(0, 2, 0);
963  splitterViews.SetViewOriginCoord(0, 3, 0);
964 
965  splitterViews.SetViewOriginCoord(1, 0, 0);
966  splitterViews.SetViewOriginCoord(1, 1, 1);
967  splitterViews.SetViewOriginCoord(1, 2, 0);
968  splitterViews.SetViewOriginCoord(1, 3, 0);
969 
970  Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
971 
972  armnn::ActivationDescriptor activationDesc;
973 
974  Layer* const activ0_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_0");
975  Layer* const activ0_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_1");
976  Layer* const activ1_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_0");
977  Layer* const activ1_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_1");
978 
979  Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
980  Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
981  Layer* const output3 = graph.AddLayer<OutputLayer>(3, "output3");
982  Layer* const output4 = graph.AddLayer<OutputLayer>(4, "output4");
983 
984  // Adds connections.
985  Connect(input, splitter, inputTensorInfo, 0, 0);
986  Connect(splitter, activ0_0, splitTensorInfo1, 0, 0);
987  Connect(splitter, activ0_1, splitTensorInfo1, 0, 0);
988 
989  Connect(splitter, activ1_0, splitTensorInfo2, 1, 0);
990  Connect(splitter, activ1_1, splitTensorInfo2, 1, 0);
991 
992  Connect(activ0_0, output1, splitTensorInfo1, 0, 0);
993  Connect(activ0_1, output2, splitTensorInfo1, 0, 0);
994  Connect(activ1_0, output3, splitTensorInfo2, 0, 0);
995  Connect(activ1_1, output4, splitTensorInfo2, 0, 0);
996 
997  CreateTensorHandles(graph, factory);
998 
999  auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
1000  auto workloadActiv0_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_0, factory);
1001  auto workloadActiv0_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_1, factory);
1002  auto workloadActiv1_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_0, factory);
1003  auto workloadActiv1_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_1, factory);
1004 
1005  wlSplitter = std::move(workloadSplitter);
1006  wlActiv0_0 = std::move(workloadActiv0_0);
1007  wlActiv0_1 = std::move(workloadActiv0_1);
1008  wlActiv1_0 = std::move(workloadActiv1_0);
1009  wlActiv1_1 = std::move(workloadActiv1_1);
1010 }
1011 
1012 template <typename ResizeWorkload, armnn::DataType DataType>
1013 std::unique_ptr<ResizeWorkload> CreateResizeBilinearWorkloadTest(armnn::IWorkloadFactory& factory,
1014  armnn::Graph& graph,
1015  DataLayout dataLayout = DataLayout::NCHW)
1016 {
1017  TensorShape inputShape;
1018  TensorShape outputShape;
1019 
1020  switch (dataLayout) {
1021  case DataLayout::NHWC:
1022  inputShape = { 2, 4, 4, 3 };
1023  outputShape = { 2, 2, 2, 3 };
1024  break;
1025  case DataLayout::NCHW:
1026  default:
1027  inputShape = { 2, 3, 4, 4 };
1028  outputShape = { 2, 3, 2, 2 };
1029  }
1030 
1031  // Creates the layer we're testing.
1032  ResizeDescriptor resizeDesc;
1033  armnnUtils::DataLayoutIndexed dimensionIndices = dataLayout;
1034  resizeDesc.m_Method = ResizeMethod::Bilinear;
1035  resizeDesc.m_TargetWidth = outputShape[dimensionIndices.GetWidthIndex()];
1036  resizeDesc.m_TargetHeight = outputShape[dimensionIndices.GetHeightIndex()];
1037  resizeDesc.m_DataLayout = dataLayout;
1038  Layer* const layer = graph.AddLayer<ResizeLayer>(resizeDesc, "resize");
1039 
1040  // Creates extra layers.
1041  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1042  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1043 
1044  // Connects up.
1045  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1046  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1047  Connect(input, layer, inputTensorInfo);
1048  Connect(layer, output, outputTensorInfo);
1049  CreateTensorHandles(graph, factory);
1050 
1051  // Makes the workload and checks it.
1052  auto workload = MakeAndCheckWorkload<ResizeWorkload>(*layer, factory);
1053 
1054  auto queueDescriptor = workload->GetData();
1055  BOOST_CHECK(queueDescriptor.m_Inputs.size() == 1);
1056  BOOST_CHECK(queueDescriptor.m_Outputs.size() == 1);
1057  BOOST_CHECK(queueDescriptor.m_Parameters.m_DataLayout == dataLayout);
1058 
1059  // Returns so we can do extra, backend-specific tests.
1060  return workload;
1061 }
1062 
1063 template <typename BatchToSpaceNdWorkload, armnn::DataType DataType>
1064 std::unique_ptr<BatchToSpaceNdWorkload> CreateBatchToSpaceNdWorkloadTest(armnn::IWorkloadFactory& factory,
1065  armnn::Graph& graph)
1066 {
1068  Layer* const layer = graph.AddLayer<BatchToSpaceNdLayer>(desc, "batchToSpace");
1069 
1070  // Creates extra layers.
1071  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1072  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1073 
1074  // Connects up.
1075  armnn::TensorInfo tensorInfo({1, 1, 1, 1}, DataType);
1076 
1077  Connect(input, layer, tensorInfo);
1078  Connect(layer, output, tensorInfo);
1079 
1080  CreateTensorHandles(graph, factory);
1081 
1082  // Makes the workload and checks it.
1083  auto workload = MakeAndCheckWorkload<BatchToSpaceNdWorkload>(*layer, factory);
1084 
1085  BatchToSpaceNdQueueDescriptor queueDescriptor = workload->GetData();
1086  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1087  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1088 
1089  return workload;
1090 }
1091 
1092 template <typename L2NormalizationWorkload, armnn::DataType DataType>
1093 std::unique_ptr<L2NormalizationWorkload> CreateL2NormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
1094  armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
1095 {
1096  // Creates the layer we're testing.
1097  L2NormalizationDescriptor layerDesc;
1098  layerDesc.m_DataLayout = dataLayout;
1099 
1100  Layer* const layer = graph.AddLayer<L2NormalizationLayer>(layerDesc, "l2norm");
1101 
1102  // Creates extra layers.
1103  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1104  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1105 
1106  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1107  TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1108  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1109  TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1110 
1111  // Connects up.
1112  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1113  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1114  Connect(input, layer, inputTensorInfo);
1115  Connect(layer, output, outputTensorInfo);
1116  CreateTensorHandles(graph, factory);
1117 
1118  // Makes the workload and checks it.
1119  auto workload = MakeAndCheckWorkload<L2NormalizationWorkload>(*layer, factory);
1120 
1121  L2NormalizationQueueDescriptor queueDescriptor = workload->GetData();
1122  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1123  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1124  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1125 
1126  // Returns so we can do extra, backend-specific tests.
1127  return workload;
1128 }
1129 
1130 template <typename ReshapeWorkload, armnn::DataType DataType>
1131 std::unique_ptr<ReshapeWorkload> CreateReshapeWorkloadTest(armnn::IWorkloadFactory& factory,
1132  armnn::Graph& graph)
1133 {
1134  // Creates the layer we're testing.
1135  TensorShape outputShape({ 1, 4 });
1136  ReshapeDescriptor reshapeDesc;
1137  reshapeDesc.m_TargetShape = outputShape;
1138  Layer* const layer = graph.AddLayer<ReshapeLayer>(reshapeDesc, "layer");
1139 
1140  // Creates extra layers.
1141  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1142  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1143 
1144  // Connects up.
1145  armnn::TensorInfo inputTensorInfo({ 4, 1 }, DataType);
1146  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1147  Connect(input, layer, inputTensorInfo);
1148  Connect(layer, output, outputTensorInfo);
1149  CreateTensorHandles(graph, factory);
1150 
1151  // Makes the workload and checks it.
1152  auto workload = MakeAndCheckWorkload<ReshapeWorkload>(*layer, factory);
1153 
1154  ReshapeQueueDescriptor queueDescriptor = workload->GetData();
1155  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1156  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1157 
1158  // Returns so we can do extra, backend-specific tests.
1159  return workload;
1160 }
1161 
1162 template <typename ConvertFp16ToFp32Float32Workload>
1163 std::unique_ptr<ConvertFp16ToFp32Float32Workload> CreateConvertFp16ToFp32WorkloadTest(
1164  armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1165 {
1166  // Creates the layer we're testing.
1167  ConvertFp16ToFp32Layer* const layer = graph.AddLayer<ConvertFp16ToFp32Layer>("Fp16ToFp32Converter");
1168 
1169  // Creates extra layers.
1170  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1171  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1172 
1173  // Connects up.
1174  armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1175  armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1176  Connect(input, layer, inputTensorInfo);
1177  Connect(layer, output, outputTensorInfo);
1178  CreateTensorHandles(graph, factory);
1179 
1180  // Makes the workload and checks it.
1181  auto workload = MakeAndCheckWorkload<ConvertFp16ToFp32Float32Workload>(*layer, factory);
1182 
1183  ConvertFp16ToFp32QueueDescriptor queueDescriptor = workload->GetData();
1184  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1185  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1186 
1187  // Returns so we can do extra, backend-specific tests.
1188  return workload;
1189 }
1190 
1191 template <typename ConvertFp32ToFp16Float16Workload>
1192 std::unique_ptr<ConvertFp32ToFp16Float16Workload> CreateConvertFp32ToFp16WorkloadTest(
1193  armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1194 {
1195  // Creates the layer we're testing.
1196  ConvertFp32ToFp16Layer* const layer = graph.AddLayer<ConvertFp32ToFp16Layer>("Fp32ToFp16Converter");
1197 
1198  // Creates extra layers.
1199  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1200  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1201 
1202  // Connects up.
1203  armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1204  armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1205  Connect(input, layer, inputTensorInfo);
1206  Connect(layer, output, outputTensorInfo);
1207  CreateTensorHandles(graph, factory);
1208 
1209  // Makes the workload and checks it.
1210  auto workload = MakeAndCheckWorkload<ConvertFp32ToFp16Float16Workload>(*layer, factory);
1211 
1212  ConvertFp32ToFp16QueueDescriptor queueDescriptor = workload->GetData();
1213  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1214  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1215 
1216  // Returns so we can do extra, backend-specific tests.
1217  return workload;
1218 }
1219 
1220 template <typename MeanWorkload, armnn::DataType DataType>
1221 std::unique_ptr<MeanWorkload> CreateMeanWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1222 {
1223  // Reduce along the first and second dimensions, and do not keep the reduced dimensions.
1224  MeanDescriptor descriptor({ 1, 2 }, false);
1225 
1226  // Creates the layer we're testing.
1227  Layer* const layer = graph.AddLayer<MeanLayer>(descriptor, "mean");
1228 
1229  // Creates extra layers.
1230  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1231  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1232 
1233  // Connects up.
1234  armnn::TensorInfo inputTensorInfo({ 1, 3, 7, 4 }, DataType);
1235  armnn::TensorInfo outputTensorInfo({ 1, 4 }, DataType);
1236  Connect(input, layer, inputTensorInfo);
1237  Connect(layer, output, outputTensorInfo);
1238  CreateTensorHandles(graph, factory);
1239 
1240  // Makes the workload and checks it.
1241  auto workload = MakeAndCheckWorkload<MeanWorkload>(*layer, factory);
1242 
1243  MeanQueueDescriptor queueDescriptor = workload->GetData();
1244  BOOST_TEST(queueDescriptor.m_Parameters.m_Axis == descriptor.m_Axis);
1245  BOOST_TEST(queueDescriptor.m_Parameters.m_KeepDims == descriptor.m_KeepDims);
1246  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1247  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1248 
1249  // Returns so we can do extra, backend-specific tests.
1250  return workload;
1251 }
1252 
1253 template<typename ConcatWorkload, armnn::DataType DataType>
1254 std::unique_ptr<ConcatWorkload> CreateConcatWorkloadTest(armnn::IWorkloadFactory &factory,
1255  armnn::Graph &graph,
1256  const armnn::TensorShape &outputShape,
1257  unsigned int concatAxis)
1258 {
1259  armnn::TensorInfo inputTensorInfo({ 2, 3, 2, 5 }, DataType);
1260  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1261 
1262  // Constructs the graph.
1263  Layer* const input0 = graph.AddLayer<InputLayer>(0, "input0");
1264  Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
1265  armnn::OriginsDescriptor descriptor;
1266 
1267  std::vector<armnn::TensorShape> inputShapes{{ 2, 3, 2, 5 }, { 2, 3, 2, 5 }};
1268 
1269  descriptor = CreateDescriptorForConcatenation(inputShapes.begin(),
1270  inputShapes.end(),
1271  concatAxis);
1272 
1273  Layer* const concat = graph.AddLayer<ConcatLayer>(descriptor, "concat");
1274  BOOST_TEST_CHECKPOINT("created concat layer");
1275 
1276  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1277 
1278  // Adds connections.
1279  Connect(input0, concat, inputTensorInfo, 0, 0);
1280  BOOST_TEST_CHECKPOINT("connect input0 to concat");
1281  Connect(input1, concat, inputTensorInfo, 0, 1);
1282  BOOST_TEST_CHECKPOINT("connect input1 to concat");
1283  Connect(concat, output, outputTensorInfo, 0, 0);
1284  BOOST_TEST_CHECKPOINT("connect concat to output");
1285 
1286  CreateTensorHandles(graph, factory);
1287  BOOST_TEST_CHECKPOINT("created tensor handles");
1288 
1289  auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
1290  BOOST_TEST_CHECKPOINT("created concat workload");
1291 
1292  return workloadConcat;
1293 }
1294 
1295 template <typename PreCompiledWorkload, armnn::DataType dataType>
1296 std::pair<armnn::IOptimizedNetworkPtr, std::unique_ptr<PreCompiledWorkload>> CreatePreCompiledWorkloadTest(
1297  armnn::IWorkloadFactory& factory,
1298  armnn::Graph& graph,
1299  bool biasEnabled = false)
1300 {
1301  IgnoreUnused(graph);
1302 
1303  // To create a PreCompiled layer, create a network and Optimize it.
1304  armnn::Network net;
1305 
1306  // Add an input layer
1307  armnn::IConnectableLayer* const inputLayer = net.AddInputLayer(0, "input layer");
1308  BOOST_TEST(inputLayer);
1309 
1310  // ArmNN weights tensor shape is OIHW (out channels, in channels, height, width) for NCHW
1311  // ArmNN weights tensor shape is OHWI (out channels, height, width, in channels) for NHWC
1312  // this test is using NHWC, so the weights shape is OHWI
1313  TensorInfo weightsTensorInfo(TensorShape({16, 1, 1, 16}), dataType, 0.9f, 0);
1314  unsigned int weightsLength = weightsTensorInfo.GetNumElements();
1315 
1316  using WeightType = armnn::ResolveType<dataType>;
1317  std::vector<WeightType> convWeightsData(weightsLength);
1318  for (unsigned int i = 0; i < weightsLength; ++i)
1319  {
1320  convWeightsData[i] = static_cast<WeightType>(i);
1321  }
1322 
1323  armnn::ConstTensor weights(weightsTensorInfo, convWeightsData);
1324 
1325  // Add a layer that can be used in the PreCompiled layer
1326  armnn::Convolution2dDescriptor convDesc2d;
1327  convDesc2d.m_StrideX = 1;
1328  convDesc2d.m_StrideY = 1;
1329  convDesc2d.m_BiasEnabled = biasEnabled;
1331 
1332  armnn::IConnectableLayer* convLayer = nullptr;
1333  const std::string convLayerName("conv layer");
1334 
1335  if (biasEnabled)
1336  {
1337  constexpr armnn::DataType biasDataType = ( dataType == armnn::DataType::QAsymmU8) ?
1338  armnn::DataType::Signed32 : armnn::DataType::Float32;
1339 
1340  TensorInfo biasTensorInfo(TensorShape({16}), biasDataType, 0.9f * 0.9f, 0);
1341  unsigned int biasLength = biasTensorInfo.GetNumElements();
1342 
1343  using BiasType = armnn::ResolveType<biasDataType>;
1344  std::vector<BiasType> biasData(biasLength);
1345  std::fill(biasData.begin(), biasData.end(), static_cast<BiasType>(0));
1346 
1347  armnn::ConstTensor biases(biasTensorInfo, biasData);
1348 
1349  // Create convolution layer with biases
1350  convLayer = net.AddConvolution2dLayer(convDesc2d,
1351  weights,
1352  Optional<ConstTensor>(biases),
1353  convLayerName.c_str());
1354  }
1355  else
1356  {
1357  // Create convolution layer without biases
1358  convLayer = net.AddConvolution2dLayer(convDesc2d,
1359  weights,
1360  EmptyOptional(),
1361  convLayerName.c_str());
1362  }
1363 
1364  BOOST_TEST(convLayer);
1365 
1366  // Add an output layer
1367  armnn::IConnectableLayer* const outputLayer = net.AddOutputLayer(0, "output layer");
1368  BOOST_TEST(outputLayer);
1369 
1370  // set the tensors in the network (NHWC format)
1371  TensorInfo inputTensorInfo(TensorShape({ 1, 16, 16, 16 }), dataType);
1372  if (dataType == armnn::DataType::QAsymmU8)
1373  {
1374  inputTensorInfo.SetQuantizationOffset(0);
1375  inputTensorInfo.SetQuantizationScale(0.9f);
1376  }
1377 
1378  TensorInfo outputTensorInfo(TensorShape({1, 16, 16, 16}), dataType);
1379  if (dataType == armnn::DataType::QAsymmU8)
1380  {
1381  outputTensorInfo.SetQuantizationOffset(0);
1382  outputTensorInfo.SetQuantizationScale(0.9f);
1383  }
1384 
1385  // Connect the layers
1386  inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
1387  inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
1388 
1389  convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
1390  convLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1391 
1392  // Optimize the network for the backend supported by the factory
1393  std::vector<armnn::BackendId> backends = {factory.GetBackendId()};
1395  armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
1396  armnn::OptimizerOptions optimizerOptions;
1397  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec(),
1398  optimizerOptions);
1399  BOOST_CHECK(optimizedNet != nullptr);
1400 
1401  // Find the PreCompiled layer in the optimised graph
1402  armnn::Graph& optimisedGraph = static_cast<armnn::OptimizedNetwork*>(optimizedNet.get())->GetGraph();
1403  Layer* preCompiledLayer = nullptr;
1404  for (auto& layer : optimisedGraph)
1405  {
1406  if (layer->GetType() == LayerType::PreCompiled)
1407  {
1408  preCompiledLayer = layer;
1409  }
1410  }
1411  BOOST_CHECK(preCompiledLayer != nullptr);
1412 
1413  // Create the TensorHandles.
1414  CreateTensorHandles(optimisedGraph, factory);
1415 
1416  // Make the workload and check it.
1417  auto workload = MakeAndCheckWorkload<PreCompiledWorkload>(*preCompiledLayer, factory);
1418 
1419  PreCompiledQueueDescriptor queueDescriptor = workload->GetData();
1420  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1421  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1422 
1423  // Returns the workload so we can do extra, backend-specific tests.
1424  // NOTE: We need to return the optimised network as well, otherwise it gets
1425  // out of scope and the tensor handles get destructed
1426  return std::make_pair(std::move(optimizedNet), std::move(workload));
1427 }
1428 
1429 template<typename ConstantWorkload, armnn::DataType DataType>
1430 std::unique_ptr<ConstantWorkload> CreateConstantWorkloadTest(armnn::IWorkloadFactory& factory,
1431  armnn::Graph& graph,
1432  const armnn::TensorShape& outputShape)
1433 {
1434  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1435 
1436  auto constant = graph.AddLayer<ConstantLayer>("constant");
1437  constant->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(outputTensorInfo);
1438  BOOST_TEST_CHECKPOINT("created constant layer");
1439 
1440  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1441 
1442  // Adds connections.
1443  Connect(constant, output, outputTensorInfo, 0, 0);
1444  BOOST_TEST_CHECKPOINT("connect constant to output");
1445 
1446  CreateTensorHandles(graph, factory);
1447  BOOST_TEST_CHECKPOINT("created tensor handles");
1448 
1449  auto workloadConstant = MakeAndCheckWorkload<ConstantWorkload>(*constant, factory);
1450  BOOST_TEST_CHECKPOINT("created Constant workload");
1451 
1452  return workloadConstant;
1453 }
1454 
1455 template <typename PreluWorkload>
1456 std::unique_ptr<PreluWorkload> CreatePreluWorkloadTest(armnn::IWorkloadFactory& factory,
1457  armnn::Graph& graph,
1458  const armnn::TensorShape& inputShape,
1459  const armnn::TensorShape& alphaShape,
1460  const armnn::TensorShape& outputShape,
1461  armnn::DataType dataType)
1462 {
1463  // Creates the PReLU layer
1464  Layer* const layer = graph.AddLayer<PreluLayer>("prelu");
1465  BOOST_CHECK(layer != nullptr);
1466 
1467  // Creates extra layers
1468  Layer* const input = graph.AddLayer<InputLayer> (0, "input");
1469  Layer* const alpha = graph.AddLayer<InputLayer> (1, "alpha");
1470  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1471  BOOST_CHECK(input != nullptr);
1472  BOOST_CHECK(alpha != nullptr);
1473  BOOST_CHECK(output != nullptr);
1474 
1475  // Connects up
1476  armnn::TensorInfo inputTensorInfo (inputShape, dataType);
1477  armnn::TensorInfo alphaTensorInfo (alphaShape, dataType);
1478  armnn::TensorInfo outputTensorInfo(outputShape, dataType);
1479  Connect(input, layer, inputTensorInfo, 0, 0);
1480  Connect(alpha, layer, alphaTensorInfo, 0, 1);
1481  Connect(layer, output, outputTensorInfo, 0, 0);
1482  CreateTensorHandles(graph, factory);
1483 
1484  // Makes the workload and checks it
1485  auto workload = MakeAndCheckWorkload<PreluWorkload>(*layer, factory);
1486 
1487  PreluQueueDescriptor queueDescriptor = workload->GetData();
1488  BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
1489  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1490 
1491  // Returns so we can do extra, backend-specific tests.
1492  return workload;
1493 }
1494 
1495 template <typename SpaceToDepthWorkload, armnn::DataType DataType>
1496 std::unique_ptr<SpaceToDepthWorkload> CreateSpaceToDepthWorkloadTest(armnn::IWorkloadFactory& factory,
1497  armnn::Graph& graph)
1498 {
1500  desc.m_BlockSize = 2;
1501  Layer* const layer = graph.AddLayer<SpaceToDepthLayer>(desc, "spaceToDepth");
1502 
1503  // Creates extra layers.
1504  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1505  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1506 
1507  // Connects up.
1508  armnn::TensorInfo inputTensorInfo({ 1, 2, 2, 1 }, DataType);
1509  armnn::TensorInfo outputTensorInfo({ 1, 1, 1, 4 }, DataType);
1510 
1511  Connect(input, layer, inputTensorInfo);
1512  Connect(layer, output, outputTensorInfo);
1513 
1514  CreateTensorHandles(graph, factory);
1515 
1516  // Makes the workload and checks it.
1517  auto workload = MakeAndCheckWorkload<SpaceToDepthWorkload>(*layer, factory);
1518 
1519  SpaceToDepthQueueDescriptor queueDescriptor = workload->GetData();
1520  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1521  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1522 
1523  return workload;
1524 }
1525 
1526 template <typename StackWorkload, armnn::DataType DataType>
1527 std::unique_ptr<StackWorkload> CreateStackWorkloadTest(armnn::IWorkloadFactory& factory,
1528  armnn::Graph& graph,
1529  const armnn::TensorShape& inputShape,
1530  const armnn::TensorShape& outputShape,
1531  unsigned int axis,
1532  unsigned int numInputs)
1533 {
1534  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1535  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1536 
1537  // Constructs the Stack layer.
1538  armnn::StackDescriptor descriptor(axis, numInputs, inputShape);
1539  Layer* const stackLayer = graph.AddLayer<StackLayer>(descriptor, "stack");
1540  BOOST_CHECK(stackLayer != nullptr);
1541 
1542  // Constructs layer inputs and output.
1543  std::vector<Layer*> inputs;
1544  for (unsigned int i=0; i<numInputs; ++i)
1545  {
1546  inputs.push_back(graph.AddLayer<InputLayer>(
1547  static_cast<int>(i),
1548  ("input" + std::to_string(i)).c_str()
1549  ));
1550  BOOST_CHECK(inputs[i] != nullptr);
1551  }
1552  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1553  BOOST_CHECK(output != nullptr);
1554 
1555  // Adds connections.
1556  for (unsigned int i=0; i<numInputs; ++i)
1557  {
1558  Connect(inputs[i], stackLayer, inputTensorInfo, 0, i);
1559  }
1560  Connect(stackLayer, output, outputTensorInfo, 0, 0);
1561 
1562  CreateTensorHandles(graph, factory);
1563 
1564  auto stackWorkload = MakeAndCheckWorkload<StackWorkload>(*stackLayer, factory);
1565  StackQueueDescriptor queueDescriptor = stackWorkload->GetData();
1566  BOOST_TEST(queueDescriptor.m_Inputs.size() == numInputs);
1567  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1568 
1569  return stackWorkload;
1570 }
1571 
1572 } // Anonymous namespace
A layer that the constant data can be bound to.
std::unique_ptr< ScopedCpuTensorHandle > m_ForgetGateBias
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:69
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
std::unique_ptr< ScopedCpuTensorHandle > m_InputToOutputWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:61
std::unique_ptr< ScopedCpuTensorHandle > m_RecurrentToCellWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:65
bool m_ProjectionEnabled
Enable/disable the projection layer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
This layer represents a split operation.
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:32
virtual const BackendId & GetBackendId() const =0
LstmBasicParameters m_BasicParameters
Definition: LstmLayer.hpp:81
This layer represents a batch normalization operation.
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:61
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
IConnectableLayer * AddOutputLayer(LayerBindingId id, const char *name=nullptr) override
Adds an output layer to the network.
Definition: Network.cpp:1310
DataLayout
Definition: Types.hpp:49
unsigned int GetWidthIndex() const
float m_K
Kappa value used for the across channel normalization equation.
int m_Axis
Scalar, defaulted to the last index (-1), specifying the dimension the activation will be performed o...
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
std::unique_ptr< ScopedCpuTensorHandle > m_Weight
A unique pointer to store Weight values.
float m_ClippingThresProj
Clipping threshold value for the projection.
A ReshapeDescriptor for the ReshapeLayer.
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:397
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
std::unique_ptr< ScopedCpuTensorHandle > m_Bias
A unique pointer to store Bias values.
uint32_t m_PoolWidth
Pooling width value.
A Convolution2dDescriptor for the Convolution2dLayer.
float m_Alpha
Alpha value for the normalization equation.
uint32_t m_PadLeft
Padding left value in the width dimension.
std::unique_ptr< ScopedCpuTensorHandle > m_RecurrentToForgetWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:63
This layer converts data type Float 16 to Float 32.
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:24
float m_Eps
Value to add to the variance. Used to avoid dividing by zero.
This layer represents a SpaceToDepth operation.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
IConnectableLayer * AddInputLayer(LayerBindingId id, const char *name=nullptr) override
Adds an input layer to the network.
Definition: Network.cpp:1041
std::unique_ptr< ScopedCpuTensorHandle > m_Gamma
A unique pointer to store Gamma values.
std::unique_ptr< ScopedCpuTensorHandle > m_OutputGateBias
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:73
This layer represents a reshape operation.
std::unique_ptr< ScopedCpuTensorHandle > m_Variance
A unique pointer to store Variance values.
typename ResolveTypeImpl< DT >::Type ResolveType
Definition: ResolveType.hpp:73
This layer represents an activation operation with the specified activation function.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_PadRight
Padding right value in the width dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
Copyright (c) 2020 ARM Limited.
This layer represents a LSTM operation.
Definition: LstmLayer.hpp:77
void IgnoreUnused(Ts &&...)
void SetBackendId(const BackendId &id)
Definition: Layer.hpp:264
A SpaceToDepthDescriptor for the SpaceToDepthLayer.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
BOOST_CHECK(profilingService.GetCurrentState()==ProfilingState::WaitingForAck)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
std::unique_ptr< ScopedCpuTensorHandle > m_CellBias
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:71
unsigned int GetHeightIndex() const
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
NormalizationAlgorithmMethod m_NormMethodType
Normalization method algorithm to use (LocalBrightness, LocalContrast).
This layer represents a elementwiseUnary operation.
A ResizeDescriptor for the ResizeLayer.
std::unique_ptr< ScopedCpuTensorHandle > m_Beta
A unique pointer to store Beta values.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
IConnectableLayer * AddConvolution2dLayer(const Convolution2dDescriptor &convolution2dDescriptor, const ConstTensor &weights, const Optional< ConstTensor > &biases, const char *name=nullptr) override
Adds a 2D convolution layer to the network.
Definition: Network.cpp:1139
A StackDescriptor for the StackLayer.
TensorShape m_TargetShape
Target shape value.
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
std::unique_ptr< ScopedCpuTensorHandle > m_CellToForgetWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:49
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
std::unique_ptr< ScopedCpuTensorHandle > m_LayerOutput
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
DataType
Definition: Types.hpp:32
This layer represents a fully connected operation.
An LstmDescriptor for the LstmLayer.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_PadTop
Padding top value in the height 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:890
This layer represents a QuantizedLstm operation.
std::unique_ptr< ScopedCpuTensorHandle > m_Mean
A unique pointer to store Mean values.
A L2NormalizationDescriptor for the L2NormalizationLayer.
Provides access to the appropriate indexes for Channels, Height and Width based on DataLayout...
An OriginsDescriptor for the ConcatLayer.
A FullyConnectedDescriptor for the FullyConnectedLayer.
std::unique_ptr< ScopedCpuTensorHandle > m_Weight
A unique pointer to store Weight values.
bool m_BiasEnabled
Enable/disable bias.
This layer represents a stack operation.
Definition: StackLayer.hpp:13
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:199
This layer represents a merge operation.
Definition: ConcatLayer.hpp:13
This layer represents a softmax operation.
uint32_t m_TargetWidth
Target width value.
bool m_PeepholeEnabled
Enable/disable peephole.
This layer represents a BatchToSpaceNd operation.
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:566
GPU Execution: OpenCL: ArmCompute.
static bool IsLayerSupported(const BackendId &backendId, const IConnectableLayer &layer, Optional< DataType > dataType, std::string &outReasonIfUnsupported)
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:20
uint32_t m_TargetHeight
Target height value.
uint32_t m_ActivationFunc
The activation function to use.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
This layer represents a normalization operation.
This layer represents a pooling 2d operation.
float m_ClippingThresCell
Clipping threshold value for the cell state.
This layer converts data type Float 32 to Float 16.
unsigned int m_BlockSize
Scalar specifying the input block size. It must be >= 1.
DataType GetBiasDataType(DataType inputDataType)
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::unique_ptr< ScopedCpuTensorHandle > m_RecurrentToOutputWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:67
LstmOptPeepholeParameters m_PeepholeParameters
Definition: LstmLayer.hpp:84
Private implementation of INetwork.
Definition: Network.hpp:28
NormalizationAlgorithmChannel m_NormChannelType
Normalization channel algorithm to use (Across, Within).
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH).
Definition: Descriptors.hpp:37
bool m_CifgEnabled
Enable/disable cifg (coupled input & forget gate).
std::unique_ptr< ScopedCpuTensorHandle > m_InputToCellWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:59
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:82
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.
This layer represents a L2 normalization operation.
CPU Execution: NEON: ArmCompute.
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: InputLayer.hpp:13
std::unique_ptr< ScopedCpuTensorHandle > m_Bias
A unique pointer to store Bias values.
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
A MeanDescriptor for the MeanLayer.
UnaryOperation
Definition: Types.hpp:87
std::unique_ptr< ScopedCpuTensorHandle > m_CellToOutputWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units].
Definition: LstmLayer.hpp:51
DataType GetDataType() const
Definition: Layer.cpp:273
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
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
OriginsDescriptor CreateDescriptorForConcatenation(TensorShapeIt first, TensorShapeIt last, unsigned int concatenationDimension)
Convenience template to create an OriginsDescriptor to use when creating a ConcatLayer for performing...
Graph & TopologicalSort()
Sorts layers in topological order and return this.
Definition: Graph.hpp:173
This layer represents a mean operation.
Definition: MeanLayer.hpp:14
virtual int Connect(IInputSlot &destination)=0
Krichevsky 2012: Local Brightness Normalization.
std::unique_ptr< ScopedCpuTensorHandle > m_Weight
A unique pointer to store Weight values.
A Pooling2dDescriptor for the Pooling2dLayer.
armnn::Runtime::CreationOptions::ExternalProfilingOptions options
A NormalizationDescriptor for the NormalizationLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
virtual void CreateTensorHandles(const TensorHandleFactoryRegistry &registry, const IWorkloadFactory &factory, const bool IsMemoryManaged=true)
Definition: Layer.cpp:240
virtual std::unique_ptr< IWorkload > CreateWorkload(const IWorkloadFactory &factory) const =0
std::unique_ptr< ScopedCpuTensorHandle > m_InputToForgetWeights
A unique pointer to represent 2D weights tensor with dimensions [input_size, num_units].
Definition: LstmLayer.hpp:57
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
Definition: Descriptors.hpp:39
A SoftmaxDescriptor for the SoftmaxLayer.
float m_Beta
Beta value for the normalization equation.
uint32_t m_NormSize
Depth radius value.
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square).
Definition: Descriptors.hpp:35
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
A BatchNormalizationDescriptor for the BatchNormalizationLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
This layer represents a resize operation.
Definition: ResizeLayer.hpp:13
uint32_t m_PadRight
Padding right value in the width dimension.