ArmNN
 21.02
CreateWorkload.hpp
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 #pragma once
6 
7 #include "TestUtils.hpp"
8 
9 #include <Graph.hpp>
10 #include <Network.hpp>
11 #include <ResolveType.hpp>
12 
16 
20 
21 #include <boost/test/unit_test.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,
35  const IWorkloadFactory& factory,
36  const ModelOptions& modelOptions = {})
37 {
38  std::unique_ptr<IWorkload> workload = layer.CreateWorkload(factory);
39  BOOST_TEST(workload.get() == PolymorphicDowncast<Workload*>(workload.get()),
40  "Cannot convert to derived class");
41  std::string reasonIfUnsupported;
42  layer.SetBackendId(factory.GetBackendId());
43  BOOST_TEST(factory.IsLayerSupported(layer, layer.GetDataType(), reasonIfUnsupported, modelOptions));
44  return std::unique_ptr<Workload>(static_cast<Workload*>(workload.release()));
45 }
46 
47 // Helper function to create tensor handlers for workloads, assuming they all use the same factory.
48 void CreateTensorHandles(armnn::Graph& graph,
49  armnn::IWorkloadFactory& factory)
50 {
51  TensorHandleFactoryRegistry tmpRegistry;
52  for (auto&& layer : graph.TopologicalSort())
53  {
54  layer->CreateTensorHandles(tmpRegistry, factory);
55  }
56 }
57 
58 /////////////////////////////////////////////////////////////////////////////////////////////
59 // The following functions are called by backendsCommon/test/CreateWorkload*.cpp
60 // They build very simple graphs, and then create a workload.
61 // Some checks are performed on the workload to ensure parameters have been passed correctly.
62 // They return the created workloads so that backend-specific checks can be performed.
63 /////////////////////////////////////////////////////////////////////////////////////////////
64 
65 template <typename ActivationWorkload, armnn::DataType DataType>
66 std::unique_ptr<ActivationWorkload> CreateActivationWorkloadTest(armnn::IWorkloadFactory& factory,
67  armnn::Graph& graph)
68 {
69  // Creates the layer we're testing.
70  ActivationDescriptor layerDesc;
72  layerDesc.m_A = 3.5f;
73  layerDesc.m_B = -10.0f;
74 
75  ActivationLayer* const layer = graph.AddLayer<ActivationLayer>(layerDesc, "layer");
76 
77  // Creates extra layers.
78  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
79  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
80 
81  // Connects up.
82  armnn::TensorInfo tensorInfo({1, 1}, DataType);
83 
84  Connect(input, layer, tensorInfo);
85  Connect(layer, output, tensorInfo);
86 
87  CreateTensorHandles(graph, factory);
88 
89  // Makes the workload and checks it.
90  auto workload = MakeAndCheckWorkload<ActivationWorkload>(*layer, factory);
91 
92  ActivationQueueDescriptor queueDescriptor = workload->GetData();
93  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
94  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
95  BOOST_TEST(queueDescriptor.m_Parameters.m_A == 3.5f);
96  BOOST_TEST(queueDescriptor.m_Parameters.m_B == -10.0f);
97  BOOST_TEST((queueDescriptor.m_Parameters.m_Function == ActivationFunction::Abs));
98 
99  // Returns so we can do extra, backend-specific tests.
100  return workload;
101 }
102 
103 template <typename WorkloadType,
104  typename DescriptorType,
105  typename LayerType,
107 std::unique_ptr<WorkloadType> CreateElementwiseWorkloadTest(armnn::IWorkloadFactory & factory,
108  armnn::Graph & graph)
109 {
110  // Creates the layer we're testing.
111  Layer* const layer = graph.AddLayer<LayerType>("layer");
112 
113  // Creates extra layers.
114  Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
115  Layer* const input2 = graph.AddLayer<InputLayer>(2, "input2");
116  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
117 
118  // Connects up.
119  armnn::TensorInfo tensorInfo({2, 3}, DataType);
120  Connect(input1, layer, tensorInfo, 0, 0);
121  Connect(input2, layer, tensorInfo, 0, 1);
122  Connect(layer, output, tensorInfo);
123  CreateTensorHandles(graph, factory);
124 
125  // Makes the workload and checks it.
126  auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, factory);
127 
128  DescriptorType queueDescriptor = workload->GetData();
129  BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
130  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
131 
132  // Returns so we can do extra, backend-specific tests.
133  return workload;
134 }
135 
136 template<typename WorkloadType,
137  typename DescriptorType,
139 std::unique_ptr<WorkloadType> CreateSubtractionWithBlobWorkloadTest(armnn::IWorkloadFactory& factory,
140  armnn::Graph& graph)
141 {
142  // Creates the layer we're testing.
143  SubtractionLayer* const layer = graph.AddLayer<SubtractionLayer>("layer");
144 
145  auto activationDesc = std::make_shared<ActivationDescriptor>();
146  activationDesc->m_A = 10.0f;
147  activationDesc->m_B = 5.0f;
148  activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
149 
150  layer->SetAdditionalInfoForObject(activationDesc);
151 
152  // Creates extra layers.
153  Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
154  Layer* const input2 = graph.AddLayer<InputLayer>(2, "input2");
155  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
156 
157  // Connects up.
158  armnn::TensorInfo tensorInfo({2, 3}, DataType);
159  Connect(input1, layer, tensorInfo, 0, 0);
160  Connect(input2, layer, tensorInfo, 0, 1);
161  Connect(layer, output, tensorInfo);
162  CreateTensorHandles(graph, factory);
163 
164  // Check that the additional information can be queried from the layer
165  std::shared_ptr<ActivationDescriptor>
166  activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
167 
168  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
169  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
170  BOOST_ASSERT(
171  static_cast<ActivationFunction>(activationDescPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
172  );
173 
174  // Makes the workload and checks it.
175  auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, factory);
176 
177  DescriptorType queueDescriptor = workload->GetData();
178 
179  const ActivationDescriptor* queueDescBlobPtr =
180  queueDescriptor.template GetAdditionalInformation<ActivationDescriptor>();
181  IgnoreUnused(queueDescBlobPtr);
182  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
183  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
184  BOOST_ASSERT(
185  static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
186  );
187 
188  BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
189  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
190 
191  return workload;
192 }
193 
194 template<typename WorkloadType,
195  typename DescriptorType,
197 std::unique_ptr<WorkloadType> CreateMultiplicationWithBlobWorkloadTest(armnn::IWorkloadFactory& factory,
198  armnn::Graph& graph)
199 {
200  // Creates the layer we're testing.
201  MultiplicationLayer* const layer = graph.AddLayer<MultiplicationLayer>("layer");
202 
203  auto activationDesc = std::make_shared<ActivationDescriptor>();
204  activationDesc->m_A = 10.0f;
205  activationDesc->m_B = 5.0f;
206  activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
207 
208  layer->SetAdditionalInfoForObject(activationDesc);
209 
210  // Creates extra layers.
211  Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
212  Layer* const input2 = graph.AddLayer<InputLayer>(2, "input2");
213  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
214 
215  // Connects up.
216  armnn::TensorInfo tensorInfo({2, 3}, DataType);
217  Connect(input1, layer, tensorInfo, 0, 0);
218  Connect(input2, layer, tensorInfo, 0, 1);
219  Connect(layer, output, tensorInfo);
220  CreateTensorHandles(graph, factory);
221 
222  // Check that the additional information can be queried from the layer
223  std::shared_ptr<ActivationDescriptor>
224  activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
225 
226  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
227  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
228  BOOST_ASSERT(
229  static_cast<ActivationFunction>(activationDescPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
230  );
231 
232  // Makes the workload and checks it.
233  auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, factory);
234 
235  DescriptorType queueDescriptor = workload->GetData();
236  BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
237  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
238  const ActivationDescriptor* queueDescBlobPtr =
239  queueDescriptor.template GetAdditionalInformation<ActivationDescriptor>();
240  IgnoreUnused(queueDescBlobPtr);
241  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
242  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
243  BOOST_ASSERT(
244  static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
245  );
246 
247  return workload;// Returns so we can do extra, backend-specific tests.
248 }
249 
250 template<typename WorkloadType,
251  typename DescriptorType,
253 std::unique_ptr<WorkloadType> CreateAdditionWithBlobWorkloadTest(armnn::IWorkloadFactory& factory,
254  armnn::Graph& graph)
255 {
256  // Creates the layer we're testing.
257  AdditionLayer* const layer = graph.AddLayer<AdditionLayer>("layer");
258 
259  auto activationDesc = std::make_shared<ActivationDescriptor>();
260  activationDesc->m_A = 10.0f;
261  activationDesc->m_B = 5.0f;
262  activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
263 
264  layer->SetAdditionalInfoForObject(activationDesc);
265 
266  // Creates extra layers.
267  Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
268  Layer* const input2 = graph.AddLayer<InputLayer>(2, "input2");
269  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
270 
271  // Connects up.
272  armnn::TensorInfo tensorInfo({2, 3}, DataType);
273  Connect(input1, layer, tensorInfo, 0, 0);
274  Connect(input2, layer, tensorInfo, 0, 1);
275  Connect(layer, output, tensorInfo);
276  CreateTensorHandles(graph, factory);
277 
278  // Check that the additional information can be queried from the layer
279  std::shared_ptr<ActivationDescriptor>
280  activationDescPtr = layer->template GetAdditionalInformation<ActivationDescriptor>();
281 
282  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
283  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
284  BOOST_ASSERT(
285  static_cast<ActivationFunction>(activationDescPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
286  );
287 
288  // Makes the workload and checks it.
289  auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, factory);
290 
291  DescriptorType queueDescriptor = workload->GetData();
292  const ActivationDescriptor* queueDescBlobPtr =
293  queueDescriptor.template GetAdditionalInformation<ActivationDescriptor>();
294  IgnoreUnused(queueDescBlobPtr);
295  BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
296  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
297  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
298  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
299  BOOST_ASSERT(
300  static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
301  );
302 
303  return workload;
304 }
305 
306 template <typename WorkloadType,
307  typename DescriptorType,
309 std::unique_ptr<WorkloadType> CreateElementwiseUnaryWorkloadTest(armnn::IWorkloadFactory & factory,
310  armnn::Graph & graph,
312 {
314  Layer* const layer = graph.AddLayer<armnn::ElementwiseUnaryLayer>(desc, "layer");
315 
316  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
317  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
318 
319  armnn::TensorInfo tensorInfo({ 2, 3 }, DataType);
320  Connect(input, layer, tensorInfo, 0, 0);
321  Connect(layer, output, tensorInfo, 0, 0);
322  CreateTensorHandles(graph, factory);
323 
324  auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, factory);
325  DescriptorType queueDescriptor = workload->GetData();
326 
327  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
328  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
329 
330  return workload;
331 }
332 
333 template <typename BatchNormalizationWorkloadType, armnn::DataType DataType>
334 std::unique_ptr<BatchNormalizationWorkloadType> CreateBatchNormalizationWorkloadTest(
335  armnn::IWorkloadFactory& factory, armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
336 {
337  TensorShape tensorShape;
338  switch (dataLayout)
339  {
340  case DataLayout::NHWC:
341  tensorShape = { 2, 4, 4, 3 };
342  break;
343  case DataLayout::NCHW:
344  default:
345  tensorShape = { 2, 3, 4, 4 };
346  }
347 
348  // Creates the layer we're testing.
350  layerDesc.m_Eps = 0.05f;
351  layerDesc.m_DataLayout = dataLayout;
352 
353  BatchNormalizationLayer* const layer = graph.AddLayer<BatchNormalizationLayer>(layerDesc, "layer");
354 
355  armnn::TensorInfo weightInfo({3}, DataType);
356  layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
357  layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
358  layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
359  layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
360  layer->m_Mean->Allocate();
361  layer->m_Variance->Allocate();
362  layer->m_Beta->Allocate();
363  layer->m_Gamma->Allocate();
364 
365  // Creates extra layers.
366  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
367  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
368 
369  // Connects up.
370  armnn::TensorInfo tensorInfo(tensorShape, DataType);
371  Connect(input, layer, tensorInfo);
372  Connect(layer, output, tensorInfo);
373  CreateTensorHandles(graph, factory);
374 
375  // Makes the workload and checks it.
376  auto workload = MakeAndCheckWorkload<BatchNormalizationWorkloadType>(*layer, factory);
377  BatchNormalizationQueueDescriptor queueDescriptor = workload->GetData();
378  BOOST_TEST(queueDescriptor.m_Parameters.m_Eps == 0.05f);
379  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
380  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
381  BOOST_TEST((queueDescriptor.m_Mean->GetTensorInfo() == TensorInfo({3}, DataType)));
382  BOOST_TEST((queueDescriptor.m_Variance->GetTensorInfo() == TensorInfo({3}, DataType)));
383  BOOST_TEST((queueDescriptor.m_Gamma->GetTensorInfo() == TensorInfo({3}, DataType)));
384  BOOST_TEST((queueDescriptor.m_Beta->GetTensorInfo() == TensorInfo({3}, DataType)));
385  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
386 
387  // Returns so we can do extra, backend-specific tests.
388  return workload;
389 }
390 
391 template <typename BatchNormalizationWorkloadType, armnn::DataType DataType>
392 std::unique_ptr<BatchNormalizationWorkloadType> CreateBatchNormalizationWithBlobWorkloadTest(
393  armnn::IWorkloadFactory& factory, armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
394 {
395  TensorShape tensorShape;
396  switch (dataLayout)
397  {
398  case DataLayout::NHWC:
399  tensorShape = { 2, 4, 4, 3 };
400  break;
401  case DataLayout::NCHW:
402  default:
403  tensorShape = { 2, 3, 4, 4 };
404  }
405 
406  // Creates the layer we're testing.
408  layerDesc.m_Eps = 0.05f;
409  layerDesc.m_DataLayout = dataLayout;
410 
411  BatchNormalizationLayer* const layer = graph.AddLayer<BatchNormalizationLayer>(layerDesc, "layer");
412 
413  armnn::TensorInfo weightInfo({3}, DataType);
414  layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
415  layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
416  layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
417  layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
418  layer->m_Mean->Allocate();
419  layer->m_Variance->Allocate();
420  layer->m_Beta->Allocate();
421  layer->m_Gamma->Allocate();
422 
423  auto activationDesc = std::make_shared<ActivationDescriptor>();
424  activationDesc->m_A = 10.0f;
425  activationDesc->m_B = 5.0f;
426  activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
427 
428  layer->SetAdditionalInfoForObject(activationDesc);
429 
430  // Check that the additional information can be queried from the layer
431  std::shared_ptr<ActivationDescriptor> activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
432  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
433  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
434  BOOST_ASSERT(
435  static_cast<ActivationFunction>(activationDescPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
436  );
437 
438  // Creates extra layers.
439  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
440  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
441 
442  // Connects up.
443  armnn::TensorInfo tensorInfo(tensorShape, DataType);
444  Connect(input, layer, tensorInfo);
445  Connect(layer, output, tensorInfo);
446  CreateTensorHandles(graph, factory);
447 
448  // Makes the workload and checks it.
449  auto workload = MakeAndCheckWorkload<BatchNormalizationWorkloadType>(*layer, factory);
450  BatchNormalizationQueueDescriptor queueDescriptor = workload->GetData();
451  const ActivationDescriptor* queueDescBlobPtr = queueDescriptor.GetAdditionalInformation<ActivationDescriptor>();
452  IgnoreUnused(queueDescBlobPtr);
453  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
454  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
455  BOOST_ASSERT(
456  static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
457  );
458 
459  BOOST_TEST(queueDescriptor.m_Parameters.m_Eps == 0.05f);
460  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
461  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
462  BOOST_TEST((queueDescriptor.m_Mean->GetTensorInfo() == TensorInfo({3}, DataType)));
463  BOOST_TEST((queueDescriptor.m_Variance->GetTensorInfo() == TensorInfo({3}, DataType)));
464  BOOST_TEST((queueDescriptor.m_Gamma->GetTensorInfo() == TensorInfo({3}, DataType)));
465  BOOST_TEST((queueDescriptor.m_Beta->GetTensorInfo() == TensorInfo({3}, DataType)));
466  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
467 
468  // Returns so we can do extra, backend-specific tests.
469  return workload;
470 }
471 
472 template <typename Convolution2dWorkload, armnn::DataType DataType>
473 std::unique_ptr<Convolution2dWorkload> CreateConvolution2dWorkloadTest(armnn::IWorkloadFactory& factory,
474  armnn::Graph& graph,
475  DataLayout dataLayout = DataLayout::NCHW,
476  const ModelOptions& modelOptions = {})
477 {
478  // Creates the layer we're testing.
479  Convolution2dDescriptor layerDesc;
480  layerDesc.m_PadLeft = 3;
481  layerDesc.m_PadRight = 3;
482  layerDesc.m_PadTop = 1;
483  layerDesc.m_PadBottom = 1;
484  layerDesc.m_StrideX = 2;
485  layerDesc.m_StrideY = 4;
486  layerDesc.m_BiasEnabled = true;
487  layerDesc.m_DataLayout = dataLayout;
488 
489  Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
490 
491  TensorShape weightShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 5, 3} : TensorShape{2, 5, 3, 3};
492  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 8, 16} : TensorShape{2, 8, 16, 3};
493  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 2, 2, 10} : TensorShape{2, 2, 10, 2};
494 
495  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo(weightShape, DataType));
496  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({2}, GetBiasDataType(DataType)));
497 
498  layer->m_Weight->Allocate();
499  layer->m_Bias->Allocate();
500 
501  // Creates extra layers.
502  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
503  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
504 
505  // Connects up.
506  Connect(input, layer, TensorInfo(inputShape, DataType));
507  Connect(layer, output, TensorInfo(outputShape, DataType));
508  CreateTensorHandles(graph, factory);
509 
510  // Makes the workload and checks it.
511  auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory, modelOptions);
512 
513  Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
514  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
515  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 4);
516  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 3);
517  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 3);
518  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
519  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
520  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled);
521  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
522 
523  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
524  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
525  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
526  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() ==
528 
529  // Returns so we can do extra, backend-specific tests.
530  return workload;
531 }
532 
533 template<typename Convolution2dWorkload, armnn::DataType DataType>
534 std::unique_ptr<Convolution2dWorkload> CreateConvolution2dFusedActivationWithBlobWorkloadTest(
535  armnn::IWorkloadFactory& factory,
536  armnn::Graph& graph,
537  DataLayout dataLayout = DataLayout::NCHW,
538  const ModelOptions& modelOptions = {})
539 {
540  // Creates the layer we're testing.
541  Convolution2dDescriptor layerDesc;
542  layerDesc.m_PadLeft = 3;
543  layerDesc.m_PadRight = 3;
544  layerDesc.m_PadTop = 1;
545  layerDesc.m_PadBottom = 1;
546  layerDesc.m_StrideX = 2;
547  layerDesc.m_StrideY = 4;
548  layerDesc.m_BiasEnabled = true;
549  layerDesc.m_DataLayout = dataLayout;
550 
551 
552  Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
553 
554  TensorShape weightShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 5, 3} : TensorShape{2, 5, 3, 3};
555  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 8, 16} : TensorShape{2, 8, 16, 3};
556  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 2, 2, 10} : TensorShape{2, 2, 10, 2};
557 
558  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo(weightShape, DataType));
559  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({2}, GetBiasDataType(DataType)));
560 
561  layer->m_Weight->Allocate();
562  layer->m_Bias->Allocate();
563 
564  auto activationDesc = std::make_shared<ActivationDescriptor>();
565  activationDesc->m_A = 10.0f;
566  activationDesc->m_B = 5.0f;
567  activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
568 
569  layer->SetAdditionalInfoForObject(activationDesc);
570 
571  // Check that the additional information can be queried from the layer
572  std::shared_ptr<ActivationDescriptor> activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
573 
574  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
575  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
576  BOOST_ASSERT(
577  static_cast<ActivationFunction>(activationDescPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
578  );
579 
580  // Creates extra layers.
581  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
582  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
583 
584  // Connects up.
585  Connect(input, layer, TensorInfo(inputShape, DataType));
586  Connect(layer, output, TensorInfo(outputShape, DataType));
587  CreateTensorHandles(graph, factory);
588 
589  // Makes the workload and checks it.
590  auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory, modelOptions);
591 
592  Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
593  const ActivationDescriptor* queueDescBlobPtr = queueDescriptor.GetAdditionalInformation<ActivationDescriptor>();
594  IgnoreUnused(queueDescBlobPtr);
595  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
596  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
597  BOOST_ASSERT(
598  static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
599  );
600 
601  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
602  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 4);
603  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 3);
604  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 3);
605  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
606  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
607  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled);
608  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
609  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
610  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
611  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() ==
613  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
614 
615  // Returns so we can do extra, backend-specific tests.
616  return workload;
617 }
618 
619 template <typename Convolution2dWorkload, armnn::DataType DataType>
620 std::unique_ptr<Convolution2dWorkload> CreateConvolution2dWorkloadFastMathTest(armnn::IWorkloadFactory& factory,
621  armnn::Graph& graph,
622  DataLayout dataLayout = DataLayout::NCHW,
623  const ModelOptions& modelOptions = {})
624 {
625  // Creates the layer we're testing.
626  Convolution2dDescriptor layerDesc;
627  layerDesc.m_PadLeft = 0;
628  layerDesc.m_PadRight = 0;
629  layerDesc.m_PadTop = 0;
630  layerDesc.m_PadBottom = 0;
631  layerDesc.m_StrideX = 1;
632  layerDesc.m_StrideY = 1;
633  layerDesc.m_BiasEnabled = false;
634  layerDesc.m_DataLayout = dataLayout;
635 
636  Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
637 
638  TensorShape weightShape = TensorShape{32, 32, 3, 3};
639  TensorShape inputShape = TensorShape{1, 32, 149, 149};
640  TensorShape outputShape = TensorShape{1, 32, 147, 147};
641 
642  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo(weightShape, DataType));
643  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({2}, GetBiasDataType(DataType)));
644 
645  layer->m_Weight->Allocate();
646  layer->m_Bias->Allocate();
647 
648  // Creates extra layers.
649  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
650  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
651 
652  // Connects up.
653  Connect(input, layer, TensorInfo(inputShape, DataType));
654  Connect(layer, output, TensorInfo(outputShape, DataType));
655  CreateTensorHandles(graph, factory);
656 
657  // Makes the workload and checks it.
658  auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory, modelOptions);
659 
660  Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
661  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 1);
662  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 1);
663  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 0);
664  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 0);
665  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 0);
666  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 0);
667  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
668 
669  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
670  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
671  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
672 
673  // Returns so we can do extra, backend-specific tests.
674  return workload;
675 }
676 
677 template <typename LstmWorkload>
678 std::unique_ptr<LstmWorkload> CreateLstmWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
679 {
680  // This parameter setting is for withCifgWithPeepholeNoProjection
681  LstmDescriptor layerDesc;
682  layerDesc.m_ActivationFunc = 4;
683  layerDesc.m_ClippingThresCell = 0.0f;
684  layerDesc.m_ClippingThresProj = 0.0f;
685  layerDesc.m_CifgEnabled = true;
686  layerDesc.m_PeepholeEnabled = true;
687  layerDesc.m_ProjectionEnabled = false;
688 
689  LstmLayer* const layer = graph.AddLayer<LstmLayer>(layerDesc, "layer");
690  unsigned int batchSize = 2;
691  unsigned int inputSize = 2;
692  unsigned int numUnits = 4;
693  unsigned int outputSize = 4;
694 
695  layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
696  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
697  layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedCpuTensorHandle>
698  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
699  layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
700  (TensorInfo({ numUnits, inputSize }, DataType::Float32));
701  layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
702  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
703  layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedCpuTensorHandle>
704  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
705  layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
706  (TensorInfo({ numUnits, outputSize }, DataType::Float32));
707  layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>
708  (TensorInfo({ numUnits }, DataType::Float32));
709  layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>
710  (TensorInfo({ numUnits }, DataType::Float32));
711  layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>
712  (TensorInfo({ numUnits }, DataType::Float32));
713 
714  layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
715  layer->m_BasicParameters.m_InputToCellWeights->Allocate();
716  layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
718  layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
720  layer->m_BasicParameters.m_ForgetGateBias->Allocate();
721  layer->m_BasicParameters.m_CellBias->Allocate();
722  layer->m_BasicParameters.m_OutputGateBias->Allocate();
723 
724 
725  if (layerDesc.m_PeepholeEnabled)
726  {
727  layer->m_PeepholeParameters.m_CellToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
728  (TensorInfo({ numUnits }, DataType::Float32));
729  layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
730  (TensorInfo({ numUnits }, DataType::Float32));
731  layer->m_PeepholeParameters.m_CellToForgetWeights->Allocate();
732  layer->m_PeepholeParameters.m_CellToOutputWeights->Allocate();
733  }
734 
735  // create input and output layers
736  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
737  Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
738  Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
739  Layer* const scratchBuffer = graph.AddLayer<OutputLayer>(0, "scratchBuffer");
740  Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
741  Layer* const cellStateOut = graph.AddLayer<OutputLayer>(2, "cellStateOut");
742  Layer* const output = graph.AddLayer<OutputLayer>(3, "output");
743 
744  // connect up
745  armnn::TensorInfo lstmTensorInfo1({ batchSize, inputSize }, DataType::Float32);
746  armnn::TensorInfo lstmTensorInfo2({ batchSize, numUnits}, DataType::Float32);
747  armnn::TensorInfo lstmTensorInfo3({ batchSize, outputSize }, DataType::Float32);
748  armnn::TensorInfo lstmTensorInfoScratchBuff({ batchSize, numUnits * (layerDesc.m_CifgEnabled ? 3 : 4) },
750  Connect(input, layer, lstmTensorInfo1, 0, 0);
751  Connect(cellStateIn, layer, lstmTensorInfo2, 0, 1);
752  Connect(outputStateIn, layer, lstmTensorInfo3, 0, 2);
753  Connect(layer, scratchBuffer, lstmTensorInfoScratchBuff, 0, 0);
754  Connect(layer, outputStateOut, lstmTensorInfo3, 1, 0);
755  Connect(layer, cellStateOut, lstmTensorInfo2, 2, 0);
756  Connect(layer, output, lstmTensorInfo3, 3, 0);
757 
758  CreateTensorHandles(graph, factory);
759 
760  // make the workload and check it
761  auto workload = MakeAndCheckWorkload<LstmWorkload>(*layer, factory);
762  LstmQueueDescriptor queueDescriptor = workload->GetData();
763  BOOST_TEST(queueDescriptor.m_Parameters.m_ActivationFunc == 4);
764  BOOST_TEST(queueDescriptor.m_Parameters.m_ClippingThresCell == 0.0f);
765  BOOST_TEST(queueDescriptor.m_Parameters.m_ClippingThresProj == 0.0f);
766  BOOST_TEST(queueDescriptor.m_Inputs.size() == 3);
767  BOOST_TEST(queueDescriptor.m_Outputs.size() == 4);
768 
769  BOOST_TEST((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == TensorInfo({ numUnits, inputSize },
771  BOOST_TEST((queueDescriptor.m_OutputGateBias->GetTensorInfo() == TensorInfo({ numUnits },
773  BOOST_TEST((queueDescriptor.m_CellBias->GetTensorInfo() == TensorInfo({ numUnits }, DataType::Float32)));
774  return workload;
775 }
776 
777 template <typename QuantizedLstmWorkload>
778 std::unique_ptr<QuantizedLstmWorkload> CreateQuantizedLstmWorkloadTest(armnn::IWorkloadFactory& factory,
779  armnn::Graph& graph)
780 {
781  auto layer = graph.AddLayer<QuantizedLstmLayer>("quantizedLstmlayer");
782  unsigned int numBatches = 2;
783  unsigned int inputSize = 2;
784  unsigned int outputSize = 4;
785 
786  // Scale/Offset for input/output, cellState In/Out, weights, bias
787  float inputOutputScale = 0.0078125f;
788  int32_t inputOutputOffset = 128;
789 
790  float cellStateScale = 0.00048828125f;
791  int32_t cellStateOffset = 0;
792 
793  float weightsScale = 0.00408021f;
794  int32_t weightsOffset = 100;
795 
796  float biasScale = 3.1876640625e-05f;
797  int32_t biasOffset = 0;
798 
799  // Weights and bias tensor and quantization info
800  armnn::TensorInfo inputWeightsInfo({outputSize, inputSize},
802  weightsScale,
803  weightsOffset);
804 
805  armnn::TensorInfo recurrentWeightsInfo({outputSize, outputSize},
807  weightsScale,
808  weightsOffset);
809 
810  armnn::TensorInfo biasInfo({outputSize},
812  biasScale,
813  biasOffset);
814 
815  // Weights and bias
816  layer->m_QuantizedLstmParameters.m_InputToInputWeights =
817  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
818  layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
819  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
820  layer->m_QuantizedLstmParameters.m_InputToCellWeights =
821  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
822  layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
823  std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
824 
825  layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
826  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
827  layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
828  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
829  layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
830  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
831  layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
832  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
833 
834  layer->m_QuantizedLstmParameters.m_InputGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
835  layer->m_QuantizedLstmParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
836  layer->m_QuantizedLstmParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
837  layer->m_QuantizedLstmParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
838 
839  // Allocate weights and bias
840  layer->m_QuantizedLstmParameters.m_InputToInputWeights->Allocate();
841  layer->m_QuantizedLstmParameters.m_InputToForgetWeights->Allocate();
842  layer->m_QuantizedLstmParameters.m_InputToCellWeights->Allocate();
843  layer->m_QuantizedLstmParameters.m_InputToOutputWeights->Allocate();
844 
845  layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights->Allocate();
846  layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights->Allocate();
847  layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights->Allocate();
848  layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights->Allocate();
849 
850  layer->m_QuantizedLstmParameters.m_InputGateBias->Allocate();
851  layer->m_QuantizedLstmParameters.m_ForgetGateBias->Allocate();
852  layer->m_QuantizedLstmParameters.m_CellBias->Allocate();
853  layer->m_QuantizedLstmParameters.m_OutputGateBias->Allocate();
854 
855  // Create input and output layers
856  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
857  Layer* const cellStateIn = graph.AddLayer<InputLayer>(1, "cellStateIn");
858  Layer* const outputStateIn = graph.AddLayer<InputLayer>(2, "outputStateIn");
859 
860  Layer* const cellStateOut = graph.AddLayer<OutputLayer>(0, "cellStateOut");
861  Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
862 
863  // Input/output tensor info and quantization info
864  armnn::TensorInfo inputInfo({numBatches , inputSize},
866  inputOutputScale,
867  inputOutputOffset);
868 
869  armnn::TensorInfo cellStateInfo({numBatches , outputSize},
871  cellStateScale,
872  cellStateOffset);
873 
874  armnn::TensorInfo outputStateInfo({numBatches , outputSize},
876  inputOutputScale,
877  inputOutputOffset);
878 
879  // Connect input/output slots
880  Connect(input, layer, inputInfo, 0, 0);
881  Connect(cellStateIn, layer, cellStateInfo, 0, 1);
882  Connect(outputStateIn, layer, outputStateInfo, 0, 2);
883 
884  Connect(layer, cellStateOut, cellStateInfo, 0, 0);
885  Connect(layer, outputStateOut, outputStateInfo, 1, 0);
886 
887  CreateTensorHandles(graph, factory);
888 
889  // Create workload and check layer support
890  auto workload = MakeAndCheckWorkload<QuantizedLstmWorkload>(*layer, factory);
891  QuantizedLstmQueueDescriptor queueDescriptor = workload->GetData();
892 
893  // Validate input/output sizes
894  BOOST_TEST(queueDescriptor.m_Inputs.size() == 3);
895  BOOST_TEST(queueDescriptor.m_Outputs.size() == 2);
896 
897  // Validate weight tensor info
898  BOOST_TEST((queueDescriptor.m_InputToInputWeights->GetTensorInfo() == inputWeightsInfo));
899  BOOST_TEST((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == inputWeightsInfo));
900  BOOST_TEST((queueDescriptor.m_InputToCellWeights->GetTensorInfo() == inputWeightsInfo));
901  BOOST_TEST((queueDescriptor.m_InputToOutputWeights->GetTensorInfo() == inputWeightsInfo));
902 
903  BOOST_TEST((queueDescriptor.m_RecurrentToInputWeights->GetTensorInfo() == recurrentWeightsInfo));
904  BOOST_TEST((queueDescriptor.m_RecurrentToForgetWeights->GetTensorInfo() == recurrentWeightsInfo));
905  BOOST_TEST((queueDescriptor.m_RecurrentToCellWeights->GetTensorInfo() == recurrentWeightsInfo));
906  BOOST_TEST((queueDescriptor.m_RecurrentToOutputWeights->GetTensorInfo() == recurrentWeightsInfo));
907 
908  BOOST_TEST((queueDescriptor.m_InputGateBias->GetTensorInfo() == biasInfo));
909  BOOST_TEST((queueDescriptor.m_ForgetGateBias->GetTensorInfo() == biasInfo));
910  BOOST_TEST((queueDescriptor.m_CellBias->GetTensorInfo() == biasInfo));
911  BOOST_TEST((queueDescriptor.m_OutputGateBias->GetTensorInfo() == biasInfo));
912 
913  return workload;
914 }
915 
916 template <typename QLstmWorkload>
917 std::unique_ptr<QLstmWorkload> CreateQLstmWorkloadTest(armnn::IWorkloadFactory& factory,
918  armnn::Graph& graph)
919 {
920  QLstmDescriptor layerDesc;
921  layerDesc.m_CifgEnabled = true;
922  layerDesc.m_PeepholeEnabled = false;
923  layerDesc.m_ProjectionEnabled = false;
924  layerDesc.m_LayerNormEnabled = true;
925 
926  layerDesc.m_CellClip = 0.0f;
927  layerDesc.m_ProjectionClip = 0.0f;
928 
929  layerDesc.m_HiddenStateZeroPoint = 0;
930  layerDesc.m_HiddenStateScale = 0.007f;
931 
932  layerDesc.m_InputIntermediateScale = 0.007059f;
933  layerDesc.m_ForgetIntermediateScale = 0.007812f;
934  layerDesc.m_CellIntermediateScale = 0.007059f;
935  layerDesc.m_OutputIntermediateScale = 0.007812f;
936 
937  QLstmLayer* const layer = graph.AddLayer<QLstmLayer>(layerDesc, "qLstm");
938 
939  unsigned int numBatches = 2;
940  unsigned int inputSize = 4;
941  unsigned int numUnits = 4;
942  unsigned int outputSize = 4;
943 
944  // Scale/Offset quantization info
945  float inputScale = 0.0078125f;
946  int32_t inputOffset = 0;
947 
948  // if (!projectionEnabled) outputScale == hiddenStateScale
949  float outputScale = layerDesc.m_HiddenStateScale;
950  int32_t outputOffset = layerDesc.m_HiddenStateZeroPoint;
951 
952  float cellStateScale = 3.05176e-05f;
953  int32_t cellStateOffset = 0;
954 
955  float weightsScale = 0.00784314f;
956  int32_t weightsOffset = 0;
957 
958  float layerNormScale = 3.05182e-05f;
959  int32_t layerNormOffset = 0;
960 
961  float biasScale = layerNormScale / 1024;
962  int32_t biasOffset = 0;
963 
964  // Weights and bias tensor and quantization info
965  armnn::TensorInfo inputWeightsInfo({outputSize, inputSize},
967  weightsScale,
968  weightsOffset);
969 
970  armnn::TensorInfo recurrentWeightsInfo({outputSize, outputSize},
972  weightsScale,
973  weightsOffset);
974 
975  armnn::TensorInfo biasInfo({outputSize}, armnn::DataType::Signed32, biasScale, biasOffset);
976 
977  armnn::TensorInfo layerNormWeightsInfo({numUnits}, armnn::DataType::QSymmS16, layerNormScale, layerNormOffset);
978 
979  // Create and allocate tensors
980  layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
981  layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
982  layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedCpuTensorHandle>(inputWeightsInfo);
983 
985  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
987  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
989  std::make_unique<ScopedCpuTensorHandle>(recurrentWeightsInfo);
990 
991  layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
992  layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
993  layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>(biasInfo);
994 
996  std::make_unique<ScopedCpuTensorHandle>(layerNormWeightsInfo);
998  std::make_unique<ScopedCpuTensorHandle>(layerNormWeightsInfo);
1000  std::make_unique<ScopedCpuTensorHandle>(layerNormWeightsInfo);
1001 
1002  layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
1003  layer->m_BasicParameters.m_InputToCellWeights->Allocate();
1004  layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
1005 
1006  layer->m_BasicParameters.m_RecurrentToForgetWeights->Allocate();
1007  layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
1008  layer->m_BasicParameters.m_RecurrentToOutputWeights->Allocate();
1009 
1010  layer->m_BasicParameters.m_ForgetGateBias->Allocate();
1011  layer->m_BasicParameters.m_CellBias->Allocate();
1012  layer->m_BasicParameters.m_OutputGateBias->Allocate();
1013 
1015  layer->m_LayerNormParameters.m_CellLayerNormWeights->Allocate();
1017 
1018  // Input and output layers
1019  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1020  Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
1021  Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
1022 
1023  Layer* const outputStateOut = graph.AddLayer<OutputLayer>(0, "outputStateOut");
1024  Layer* const cellStateOut = graph.AddLayer<OutputLayer>(1, "cellStateOut");
1025  Layer* const output = graph.AddLayer<OutputLayer>(2, "output");
1026 
1027  // Input/Output tensor info
1028  armnn::TensorInfo inputInfo({numBatches , inputSize},
1030  inputScale,
1031  inputOffset);
1032 
1033  armnn::TensorInfo cellStateInfo({numBatches , numUnits},
1035  cellStateScale,
1036  cellStateOffset);
1037 
1038  armnn::TensorInfo outputStateInfo({numBatches , outputSize},
1040  outputScale,
1041  outputOffset);
1042 
1043  // Connect layers to slots
1044  Connect(input, layer, inputInfo, 0, 0);
1045  Connect(outputStateIn, layer, outputStateInfo, 0, 1);
1046  Connect(cellStateIn, layer, cellStateInfo, 0, 2);
1047 
1048  Connect(layer, outputStateOut, outputStateInfo, 0, 0);
1049  Connect(layer, cellStateOut, cellStateInfo, 1, 0);
1050  Connect(layer, output, outputStateInfo, 2, 0);
1051 
1052  CreateTensorHandles(graph, factory);
1053 
1054  // Create and check workload
1055  auto workload = MakeAndCheckWorkload<QLstmWorkload>(*layer, factory);
1056  QLstmQueueDescriptor queueDescriptor = workload->GetData();
1057  BOOST_TEST(queueDescriptor.m_Parameters.m_CellClip == 0.0f);
1058  BOOST_TEST(queueDescriptor.m_Parameters.m_ProjectionClip == 0.0f);
1059  BOOST_TEST(queueDescriptor.m_Inputs.size() == 3);
1060  BOOST_TEST(queueDescriptor.m_Outputs.size() == 3);
1061 
1062  BOOST_TEST((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == inputWeightsInfo));
1063  BOOST_TEST((queueDescriptor.m_InputToCellWeights->GetTensorInfo() == inputWeightsInfo));
1064  BOOST_TEST((queueDescriptor.m_InputToOutputWeights->GetTensorInfo() == inputWeightsInfo));
1065 
1066  BOOST_TEST((queueDescriptor.m_RecurrentToForgetWeights->GetTensorInfo() == recurrentWeightsInfo));
1067  BOOST_TEST((queueDescriptor.m_RecurrentToCellWeights->GetTensorInfo() == recurrentWeightsInfo));
1068  BOOST_TEST((queueDescriptor.m_RecurrentToOutputWeights->GetTensorInfo() == recurrentWeightsInfo));
1069 
1070  BOOST_TEST((queueDescriptor.m_ForgetGateBias->GetTensorInfo() == biasInfo));
1071  BOOST_TEST((queueDescriptor.m_CellBias->GetTensorInfo() == biasInfo));
1072  BOOST_TEST((queueDescriptor.m_OutputGateBias->GetTensorInfo() == biasInfo));
1073 
1074  return workload;
1075 }
1076 
1077 template <typename Convolution2dWorkload, armnn::DataType DataType>
1078 std::unique_ptr<Convolution2dWorkload> CreateDirectConvolution2dWorkloadTest(armnn::IWorkloadFactory& factory,
1079  armnn::Graph& graph)
1080 {
1081  // Creates the layer we're testing.
1082  Convolution2dDescriptor layerDesc;
1083  layerDesc.m_PadLeft = 1;
1084  layerDesc.m_PadRight = 1;
1085  layerDesc.m_PadTop = 1;
1086  layerDesc.m_PadBottom = 1;
1087  layerDesc.m_StrideX = 1;
1088  layerDesc.m_StrideY = 1;
1089  layerDesc.m_BiasEnabled = true;
1090 
1091  Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
1092 
1093  float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1094  float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1095 
1096  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({ 2, 3, 3, 3 }, DataType, inputsQScale));
1097  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>
1098  (TensorInfo({2}, GetBiasDataType(DataType), inputsQScale));
1099  layer->m_Weight->Allocate();
1100  layer->m_Bias->Allocate();
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  // Connects up.
1107  Connect(input, layer, TensorInfo({2, 3, 6, 6}, DataType, inputsQScale));
1108  Connect(layer, output, TensorInfo({2, 2, 6, 6}, DataType, outputQScale));
1109  CreateTensorHandles(graph, factory);
1110 
1111  // Makes the workload and checks it.
1112  auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory);
1113 
1114  Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
1115  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 1);
1116  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 1);
1117  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 1);
1118  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 1);
1119  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
1120  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
1121  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1122 
1123  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1124  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1125  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({2, 3, 3, 3},
1126  DataType, inputsQScale)));
1127  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo()
1128  == TensorInfo({2}, GetBiasDataType(DataType), inputsQScale)));
1129 
1130  // Returns so we can do extra, backend-specific tests.
1131  return workload;
1132 }
1133 
1134 template <typename DepthwiseConvolution2dFloat32Workload, armnn::DataType DataType>
1135 std::unique_ptr<DepthwiseConvolution2dFloat32Workload> CreateDepthwiseConvolution2dWorkloadTest(
1136  armnn::IWorkloadFactory& factory, armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
1137 {
1138  // Creates the layer we're testing.
1140  layerDesc.m_PadLeft = 1;
1141  layerDesc.m_PadRight = 2;
1142  layerDesc.m_PadTop = 1;
1143  layerDesc.m_PadBottom = 2;
1144  layerDesc.m_StrideX = 1;
1145  layerDesc.m_StrideY = 1;
1146  layerDesc.m_BiasEnabled = false;
1147  layerDesc.m_DataLayout = dataLayout;
1148 
1149  DepthwiseConvolution2dLayer* const layer = graph.AddLayer<DepthwiseConvolution2dLayer>(layerDesc, "layer");
1150 
1151  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({1, 2, 4, 4}, DataType)); // [ M, I, H, W ]
1152  layer->m_Weight->Allocate();
1153 
1154  // Creates extra layers.
1155  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1156  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1157 
1158  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1159  TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
1160  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1161  TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
1162 
1163  // Connects up.
1164  Connect(input, layer, TensorInfo(inputShape, DataType));
1165  Connect(layer, output, TensorInfo(outputShape, DataType));
1166  CreateTensorHandles(graph, factory);
1167 
1168  // Makes the workload and checks it.
1169  auto workload = MakeAndCheckWorkload<DepthwiseConvolution2dFloat32Workload>(*layer, factory);
1170 
1171  DepthwiseConvolution2dQueueDescriptor queueDescriptor = workload->GetData();
1172  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 1);
1173  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 1);
1174  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 1);
1175  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 2);
1176  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
1177  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 2);
1178  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == false);
1179  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1180 
1181  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1182  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1183  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({1, 2, 4, 4}, DataType)));
1184 
1185  // Returns so we can do extra, backend-specific tests.
1186  return workload;
1187 }
1188 
1189 template <typename FullyConnectedWorkload, armnn::DataType DataType>
1190 std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWorkloadTest(armnn::IWorkloadFactory& factory,
1191  armnn::Graph& graph)
1192 {
1193  // Creates the layer we're testing.
1194  FullyConnectedDescriptor layerDesc;
1195  layerDesc.m_BiasEnabled = true;
1196  layerDesc.m_TransposeWeightMatrix = true;
1197 
1198  FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1199 
1200  float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1201  float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1202 
1203  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7, 20}, DataType, inputsQScale, 0));
1204  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7}, GetBiasDataType(DataType), inputsQScale));
1205  layer->m_Weight->Allocate();
1206  layer->m_Bias->Allocate();
1207 
1208  // Creates extra layers.
1209  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1210  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1211 
1212  // Connects up.
1213  Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale));
1214  Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1215  CreateTensorHandles(graph, factory);
1216 
1217  // Makes the workload and checks it.
1218  auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1219 
1220  FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1221  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1222  BOOST_TEST(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1223 
1224  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1225  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1226  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({7, 20}, DataType, inputsQScale)));
1227  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() == TensorInfo({7}, GetBiasDataType(DataType), inputsQScale)));
1228 
1229  // Returns so we can do extra, backend-specific tests.
1230  return workload;
1231 }
1232 
1233 template <typename FullyConnectedWorkload, armnn::DataType DataType>
1234 std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWithBlobWorkloadTest
1235  (armnn::IWorkloadFactory& factory,
1236  armnn::Graph& graph)
1237 {
1238  // Creates the layer we're testing.
1239  FullyConnectedDescriptor layerDesc;
1240  layerDesc.m_BiasEnabled = true;
1241  layerDesc.m_TransposeWeightMatrix = true;
1242 
1243  FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1244 
1245  float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1246  float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1247 
1248  layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7, 20}, DataType, inputsQScale, 0));
1249  layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7}, GetBiasDataType(DataType), inputsQScale));
1250  layer->m_Weight->Allocate();
1251  layer->m_Bias->Allocate();
1252 
1253  auto activationDesc = std::make_shared<ActivationDescriptor>();
1254  activationDesc->m_A = 10.0f;
1255  activationDesc->m_B = 5.0f;
1256  activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
1257 
1258  layer->SetAdditionalInfoForObject(activationDesc);
1259 
1260  // Check that the additional information can be queried from the layer
1261  std::shared_ptr<ActivationDescriptor> activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
1262  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
1263  BOOST_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
1264  BOOST_ASSERT(static_cast<ActivationFunction>(activationDescPtr->m_Function) ==
1266 
1267  // Creates extra layers.
1268  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1269  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1270 
1271  // Connects up.
1272  Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale));
1273  Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1274  CreateTensorHandles(graph, factory);
1275 
1276  // Makes the workload and checks it.
1277  auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1278 
1279  FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1280 
1281  const ActivationDescriptor* queueDescBlobPtr = queueDescriptor.GetAdditionalInformation<ActivationDescriptor>();
1282  IgnoreUnused(queueDescBlobPtr);
1283 
1284  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
1285  BOOST_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
1286  BOOST_ASSERT(
1287  static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
1288  );
1289 
1290  BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1291  BOOST_TEST(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1292  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1293  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1294  BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({7, 20}, DataType, inputsQScale)));
1295  BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() == TensorInfo({7}, GetBiasDataType(DataType), inputsQScale)));
1296 
1297  // Returns so we can do extra, backend-specific tests.
1298  return workload;
1299 }
1300 
1301 
1302 template <typename NormalizationWorkload, armnn::DataType DataType>
1303 std::unique_ptr<NormalizationWorkload> CreateNormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
1304  armnn::Graph& graph,
1305  DataLayout dataLayout = DataLayout::NCHW)
1306 {
1307  // Creates the layer we're testing.
1308  NormalizationDescriptor layerDesc;
1311  layerDesc.m_NormSize = 3;
1312  layerDesc.m_Alpha = 0.5f;
1313  layerDesc.m_Beta = -1.0f;
1314  layerDesc.m_K = 0.2f;
1315  layerDesc.m_DataLayout = dataLayout;
1316 
1317  NormalizationLayer* layer = graph.AddLayer<NormalizationLayer>(layerDesc, "layer");
1318 
1319  // Creates extra layers.
1320  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1321  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1322 
1323  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1324  TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
1325  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1326  TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
1327 
1328  // Connects up.
1329  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1330  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1331  Connect(input, layer, inputTensorInfo);
1332  Connect(layer, output, outputTensorInfo);
1333  CreateTensorHandles(graph, factory);
1334 
1335  // Makes the workload and checks it.
1336  auto workload = MakeAndCheckWorkload<NormalizationWorkload>(*layer, factory);
1337 
1338  NormalizationQueueDescriptor queueDescriptor = workload->GetData();
1339  BOOST_TEST((queueDescriptor.m_Parameters.m_NormChannelType == NormalizationAlgorithmChannel::Across));
1340  BOOST_TEST((queueDescriptor.m_Parameters.m_NormMethodType == NormalizationAlgorithmMethod::LocalBrightness));
1341  BOOST_TEST(queueDescriptor.m_Parameters.m_NormSize == 3);
1342  BOOST_TEST(queueDescriptor.m_Parameters.m_Alpha == 0.5f);
1343  BOOST_TEST(queueDescriptor.m_Parameters.m_Beta == -1.0f);
1344  BOOST_TEST(queueDescriptor.m_Parameters.m_K == 0.2f);
1345  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1346 
1347  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1348  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1349 
1350  // Returns so we can do extra, backend-specific tests.
1351  return workload;
1352 }
1353 
1354 template <typename Pooling2dWorkload, armnn::DataType DataType>
1355 std::unique_ptr<Pooling2dWorkload> CreatePooling2dWorkloadTest(armnn::IWorkloadFactory& factory,
1356  armnn::Graph& graph,
1357  DataLayout dataLayout = DataLayout::NCHW)
1358 {
1359  // Creates the layer we're testing.
1360  Pooling2dDescriptor layerDesc;
1362  layerDesc.m_PoolWidth = 3;
1363  layerDesc.m_PoolHeight = 3;
1364  layerDesc.m_PadLeft = 2;
1365  layerDesc.m_PadRight = 2;
1366  layerDesc.m_PadTop = 1;
1367  layerDesc.m_PadBottom = 1;
1368  layerDesc.m_StrideX = 2;
1369  layerDesc.m_StrideY = 3;
1371  layerDesc.m_DataLayout = dataLayout;
1372 
1373  Pooling2dLayer* const layer = graph.AddLayer<Pooling2dLayer>(layerDesc, "layer");
1374 
1375  // Create extra layers
1376  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1377  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1378 
1379  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 5, 5} : TensorShape{3, 5, 5, 2};
1380  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 2, 4} : TensorShape{3, 2, 4, 2};
1381 
1382  // Connect up
1383  Connect(input, layer, TensorInfo(inputShape, DataType));
1384  Connect(layer, output, TensorInfo(outputShape, DataType));
1385  CreateTensorHandles(graph, factory);
1386 
1387  // Make the workload and checks it
1388  auto workload = MakeAndCheckWorkload<Pooling2dWorkload>(*layer, factory);
1389 
1390  Pooling2dQueueDescriptor queueDescriptor = workload->GetData();
1391  BOOST_TEST((queueDescriptor.m_Parameters.m_PoolType == PoolingAlgorithm::Average));
1392  BOOST_TEST((queueDescriptor.m_Parameters.m_OutputShapeRounding == OutputShapeRounding::Floor));
1393  BOOST_TEST(queueDescriptor.m_Parameters.m_PoolWidth == 3);
1394  BOOST_TEST(queueDescriptor.m_Parameters.m_PoolHeight == 3);
1395  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
1396  BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 3);
1397  BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 2);
1398  BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 2);
1399  BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
1400  BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
1401  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1402 
1403  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1404  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1405 
1406  // Return so we can do extra, backend-specific tests
1407  return workload;
1408 }
1409 
1410 template <typename SoftmaxWorkload, armnn::DataType DataType>
1411 std::unique_ptr<SoftmaxWorkload> CreateSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
1412  armnn::Graph& graph)
1413 {
1414  // Create the layer we're testing.
1415  SoftmaxDescriptor softmaxDescriptor;
1416  // Set Axis to -1 if CL or Neon until further Axes are supported.
1418  {
1419  softmaxDescriptor.m_Axis = -1;
1420  }
1421 
1422  Layer* const layer = graph.AddLayer<SoftmaxLayer>(softmaxDescriptor, "layer");
1423  // Create extra layers.
1424  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1425  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1426 
1427  // Connect up
1428  armnn::TensorInfo tensorInfo({4, 1}, DataType);
1429  if (DataType == armnn::DataType::QAsymmU8)
1430  {
1431  tensorInfo.SetQuantizationOffset(0);
1432  tensorInfo.SetQuantizationScale(1.f / 256);
1433  }
1434  else if (DataType == armnn::DataType::QAsymmS8)
1435  {
1436  tensorInfo.SetQuantizationOffset(-128);
1437  tensorInfo.SetQuantizationScale(1.f / 256);
1438  }
1439 
1440  Connect(input, layer, tensorInfo);
1441  Connect(layer, output, tensorInfo);
1442  CreateTensorHandles(graph, factory);
1443 
1444  // Make the workload and checks it.
1445  auto workload = MakeAndCheckWorkload<SoftmaxWorkload>(*layer, factory);
1446 
1447  SoftmaxQueueDescriptor queueDescriptor = workload->GetData();
1448  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1449  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1450 
1451  // Return so we can do extra, backend-specific tests.
1452  return workload;
1453 }
1454 
1455 template<typename SplitterWorkload, armnn::DataType DataType>
1456 std::unique_ptr<SplitterWorkload>
1457  CreateSplitterWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1458 {
1459  // Create the layer we're testing.
1460  // NOTE: need three dimensions channels, height/y, width/x because the Compute
1461  // library restricts subtensors to have the same x and y dimensions as
1462  // their parent tensors, and therefore the origin on the x and y dimension
1463  // has to be zero for any view. So we need a third dimension to split...
1464  // NOTE: arguments are: number of views, number of dimensions.
1465  ViewsDescriptor layerDesc(3, 3);
1466  // NOTE: arguments are: view, dimension, value.
1467  layerDesc.SetViewOriginCoord(0, 0, 0);
1468  layerDesc.SetViewOriginCoord(1, 0, 1);
1469  layerDesc.SetViewOriginCoord(2, 0, 3);
1470 
1471  Layer* const layer = graph.AddLayer<SplitterLayer>(layerDesc, "layer");
1472 
1473  // Adds extra layers.
1474  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1475  Layer* const output0 = graph.AddLayer<OutputLayer>(0, "output0");
1476  Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
1477  Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
1478 
1479  // Connects up.
1480  armnn::TensorInfo tensorInfo({5, 7, 7}, DataType);
1481  Connect(input, layer, tensorInfo);
1482 
1483  armnn::TensorInfo output0Info({1, 7, 7}, DataType);
1484  armnn::TensorInfo output1Info({2, 7, 7}, DataType);
1485  armnn::TensorInfo output2Info({2, 7, 7}, DataType);
1486 
1487  Connect(layer, output0, output0Info, 0, 0);
1488  Connect(layer, output1, output1Info, 1, 0);
1489  Connect(layer, output2, output2Info, 2, 0);
1490 
1491  CreateTensorHandles(graph, factory);
1492 
1493  // Makes the workload and checks it.
1494  auto workload = MakeAndCheckWorkload<SplitterWorkload>(*layer, factory);
1495 
1496  SplitterQueueDescriptor queueDescriptor = workload->GetData();
1497  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1498  BOOST_TEST(queueDescriptor.m_Outputs.size() == 3);
1499  BOOST_TEST(queueDescriptor.m_ViewOrigins.size() == 3);
1500 
1501  BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[0] == 0);
1502  BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[0] == 1);
1503  BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[0] == 3);
1504  BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[1] == 0);
1505  BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[1] == 0);
1506  BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[1] == 0);
1507  BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[2] == 0);
1508  BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[2] == 0);
1509  BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[2] == 0);
1510 
1511  // Returns so we can do extra, backend-specific tests.
1512  return workload;
1513 }
1514 
1515 /// This function constructs a graph with both a splitter and a concat, and returns a pair of the workloads.
1516 template<typename SplitterWorkload, typename ConcatWorkload, armnn::DataType DataType>
1517 std::pair<std::unique_ptr<SplitterWorkload>, std::unique_ptr<ConcatWorkload>>
1518  CreateSplitterConcatWorkloadTest(armnn::IWorkloadFactory &factory, armnn::Graph &graph)
1519 {
1520  armnn::TensorInfo inputTensorInfo({ 1, 2, 100, 10 }, DataType);
1521 
1522  armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 10 }, DataType);
1523  armnn::TensorInfo splitTensorInfo2({ 1, 1, 100, 10 }, DataType);
1524 
1525  //Constructs the graph.
1526  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1527 
1528  armnn::ViewsDescriptor splitterViews(2);
1529  splitterViews.SetViewOriginCoord(0, 0, 0);
1530  splitterViews.SetViewOriginCoord(0, 1, 0);
1531  splitterViews.SetViewOriginCoord(0, 2, 0);
1532  splitterViews.SetViewOriginCoord(0, 3, 0);
1533 
1534  splitterViews.SetViewOriginCoord(1, 0, 0);
1535  splitterViews.SetViewOriginCoord(1, 1, 1);
1536  splitterViews.SetViewOriginCoord(1, 2, 0);
1537  splitterViews.SetViewOriginCoord(1, 3, 0);
1538 
1539  Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
1540  BOOST_TEST_CHECKPOINT("created splitter layer");
1541 
1542  armnn::OriginsDescriptor concatViews(2);
1543  concatViews.SetViewOriginCoord(0, 0, 0);
1544  concatViews.SetViewOriginCoord(0, 1, 1);
1545  concatViews.SetViewOriginCoord(0, 2, 0);
1546  concatViews.SetViewOriginCoord(0, 3, 0);
1547 
1548  concatViews.SetViewOriginCoord(1, 0, 0);
1549  concatViews.SetViewOriginCoord(1, 1, 0);
1550  concatViews.SetViewOriginCoord(1, 2, 0);
1551  concatViews.SetViewOriginCoord(1, 3, 0);
1552 
1553  Layer* const concat = graph.AddLayer<ConcatLayer>(concatViews, "concat");
1554  BOOST_TEST_CHECKPOINT("created concat layer");
1555 
1556  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1557 
1558  // Adds connections.
1559  Connect(input, splitter, inputTensorInfo, 0, 0);
1560  BOOST_TEST_CHECKPOINT("connect input to splitter");
1561  Connect(splitter, concat, splitTensorInfo1, 0, 1); // The splitter & concat are connected up.
1562  BOOST_TEST_CHECKPOINT("connect splitter[0] to concat[1]");
1563  Connect(splitter, concat, splitTensorInfo2, 1, 0); // So that the outputs are flipped round.
1564  BOOST_TEST_CHECKPOINT("connect splitter[1] to concat[0]");
1565  Connect(concat, output, inputTensorInfo, 0, 0);
1566  BOOST_TEST_CHECKPOINT("connect concat to output");
1567 
1568  CreateTensorHandles(graph, factory);
1569  BOOST_TEST_CHECKPOINT("created tensor handles");
1570 
1571  auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
1572  BOOST_TEST_CHECKPOINT("created splitter workload");
1573  auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
1574  BOOST_TEST_CHECKPOINT("created concat workload");
1575 
1576  return {std::move(workloadSplitter), std::move(workloadConcat)};
1577 }
1578 
1579 
1580 /// This function constructs a graph with a splitter with two outputs. Each of the outputs is then
1581 /// connected to two different activation layers
1582 template<typename SplitterWorkload, typename ActivationWorkload, armnn::DataType DataType>
1583 void CreateSplitterMultipleInputsOneOutputWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph,
1584  std::unique_ptr<SplitterWorkload>& wlSplitter,
1585  std::unique_ptr<ActivationWorkload>& wlActiv0_0,
1586  std::unique_ptr<ActivationWorkload>& wlActiv0_1,
1587  std::unique_ptr<ActivationWorkload>& wlActiv1_0,
1588  std::unique_ptr<ActivationWorkload>& wlActiv1_1)
1589 {
1590  armnn::TensorInfo inputTensorInfo ({ 1, 3, 100, 50 }, DataType);
1591  armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 50 }, DataType);
1592  armnn::TensorInfo splitTensorInfo2({ 1, 2, 100, 50 }, DataType);
1593 
1594  //Constructs the graph.
1595  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1596 
1597  armnn::ViewsDescriptor splitterViews(2);
1598 
1599  splitterViews.SetViewOriginCoord(0, 0, 0);
1600  splitterViews.SetViewOriginCoord(0, 1, 0);
1601  splitterViews.SetViewOriginCoord(0, 2, 0);
1602  splitterViews.SetViewOriginCoord(0, 3, 0);
1603 
1604  splitterViews.SetViewOriginCoord(1, 0, 0);
1605  splitterViews.SetViewOriginCoord(1, 1, 1);
1606  splitterViews.SetViewOriginCoord(1, 2, 0);
1607  splitterViews.SetViewOriginCoord(1, 3, 0);
1608 
1609  Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
1610 
1611  armnn::ActivationDescriptor activationDesc;
1612 
1613  Layer* const activ0_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_0");
1614  Layer* const activ0_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_1");
1615  Layer* const activ1_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_0");
1616  Layer* const activ1_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_1");
1617 
1618  Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
1619  Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
1620  Layer* const output3 = graph.AddLayer<OutputLayer>(3, "output3");
1621  Layer* const output4 = graph.AddLayer<OutputLayer>(4, "output4");
1622 
1623  // Adds connections.
1624  Connect(input, splitter, inputTensorInfo, 0, 0);
1625  Connect(splitter, activ0_0, splitTensorInfo1, 0, 0);
1626  Connect(splitter, activ0_1, splitTensorInfo1, 0, 0);
1627 
1628  Connect(splitter, activ1_0, splitTensorInfo2, 1, 0);
1629  Connect(splitter, activ1_1, splitTensorInfo2, 1, 0);
1630 
1631  Connect(activ0_0, output1, splitTensorInfo1, 0, 0);
1632  Connect(activ0_1, output2, splitTensorInfo1, 0, 0);
1633  Connect(activ1_0, output3, splitTensorInfo2, 0, 0);
1634  Connect(activ1_1, output4, splitTensorInfo2, 0, 0);
1635 
1636  CreateTensorHandles(graph, factory);
1637 
1638  auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
1639  auto workloadActiv0_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_0, factory);
1640  auto workloadActiv0_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_1, factory);
1641  auto workloadActiv1_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_0, factory);
1642  auto workloadActiv1_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_1, factory);
1643 
1644  wlSplitter = std::move(workloadSplitter);
1645  wlActiv0_0 = std::move(workloadActiv0_0);
1646  wlActiv0_1 = std::move(workloadActiv0_1);
1647  wlActiv1_0 = std::move(workloadActiv1_0);
1648  wlActiv1_1 = std::move(workloadActiv1_1);
1649 }
1650 
1651 template <typename ResizeWorkload, armnn::DataType DataType>
1652 std::unique_ptr<ResizeWorkload> CreateResizeBilinearWorkloadTest(armnn::IWorkloadFactory& factory,
1653  armnn::Graph& graph,
1654  DataLayout dataLayout = DataLayout::NCHW)
1655 {
1656  TensorShape inputShape;
1657  TensorShape outputShape;
1658 
1659  switch (dataLayout) {
1660  case DataLayout::NHWC:
1661  inputShape = { 2, 4, 4, 3 };
1662  outputShape = { 2, 2, 2, 3 };
1663  break;
1664  case DataLayout::NCHW:
1665  default:
1666  inputShape = { 2, 3, 4, 4 };
1667  outputShape = { 2, 3, 2, 2 };
1668  }
1669 
1670  // Creates the layer we're testing.
1671  ResizeDescriptor resizeDesc;
1672  armnnUtils::DataLayoutIndexed dimensionIndices = dataLayout;
1673  resizeDesc.m_Method = ResizeMethod::Bilinear;
1674  resizeDesc.m_TargetWidth = outputShape[dimensionIndices.GetWidthIndex()];
1675  resizeDesc.m_TargetHeight = outputShape[dimensionIndices.GetHeightIndex()];
1676  resizeDesc.m_DataLayout = dataLayout;
1677  Layer* const layer = graph.AddLayer<ResizeLayer>(resizeDesc, "resize");
1678 
1679  // Creates extra layers.
1680  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1681  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1682 
1683  // Connects up.
1684  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1685  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1686  Connect(input, layer, inputTensorInfo);
1687  Connect(layer, output, outputTensorInfo);
1688  CreateTensorHandles(graph, factory);
1689 
1690  // Makes the workload and checks it.
1691  auto workload = MakeAndCheckWorkload<ResizeWorkload>(*layer, factory);
1692 
1693  auto queueDescriptor = workload->GetData();
1694  BOOST_CHECK(queueDescriptor.m_Inputs.size() == 1);
1695  BOOST_CHECK(queueDescriptor.m_Outputs.size() == 1);
1696  BOOST_CHECK(queueDescriptor.m_Parameters.m_DataLayout == dataLayout);
1697 
1698  // Returns so we can do extra, backend-specific tests.
1699  return workload;
1700 }
1701 
1702 template <typename BatchToSpaceNdWorkload, armnn::DataType DataType>
1703 std::unique_ptr<BatchToSpaceNdWorkload> CreateBatchToSpaceNdWorkloadTest(armnn::IWorkloadFactory& factory,
1704  armnn::Graph& graph)
1705 {
1707  Layer* const layer = graph.AddLayer<BatchToSpaceNdLayer>(desc, "batchToSpace");
1708 
1709  // Creates extra layers.
1710  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1711  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1712 
1713  // Connects up.
1714  armnn::TensorInfo tensorInfo({1, 1, 1, 1}, DataType);
1715 
1716  Connect(input, layer, tensorInfo);
1717  Connect(layer, output, tensorInfo);
1718 
1719  CreateTensorHandles(graph, factory);
1720 
1721  // Makes the workload and checks it.
1722  auto workload = MakeAndCheckWorkload<BatchToSpaceNdWorkload>(*layer, factory);
1723 
1724  BatchToSpaceNdQueueDescriptor queueDescriptor = workload->GetData();
1725  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1726  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1727 
1728  return workload;
1729 }
1730 
1731 template <typename LogSoftmaxWorkload, armnn::DataType DataType>
1732 std::unique_ptr<LogSoftmaxWorkload> CreateLogSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
1733  armnn::Graph& graph)
1734 {
1735  // Create the layer we're testing.
1736  LogSoftmaxDescriptor logSoftmaxDescriptor;
1737  // Set Axis to -1 if CL or Neon until further Axes are supported.
1739  {
1740  logSoftmaxDescriptor.m_Axis = -1;
1741  }
1742 
1743  Layer* const layer = graph.AddLayer<LogSoftmaxLayer>(logSoftmaxDescriptor, "layer");
1744  // Create extra layers.
1745  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1746  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1747 
1748  // Connect up
1749  armnn::TensorInfo tensorInfo({4, 1}, DataType);
1750 
1751  Connect(input, layer, tensorInfo);
1752  Connect(layer, output, tensorInfo);
1753  CreateTensorHandles(graph, factory);
1754 
1755  // Make the workload and checks it.
1756  auto workload = MakeAndCheckWorkload<LogSoftmaxWorkload>(*layer, factory);
1757 
1758  LogSoftmaxQueueDescriptor queueDescriptor = workload->GetData();
1759  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1760  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1761 
1762  // Return so we can do extra, backend-specific tests.
1763  return workload;
1764 }
1765 
1766 template <typename L2NormalizationWorkload, armnn::DataType DataType>
1767 std::unique_ptr<L2NormalizationWorkload> CreateL2NormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
1768  armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
1769 {
1770  // Creates the layer we're testing.
1771  L2NormalizationDescriptor layerDesc;
1772  layerDesc.m_DataLayout = dataLayout;
1773 
1774  Layer* const layer = graph.AddLayer<L2NormalizationLayer>(layerDesc, "l2norm");
1775 
1776  // Creates extra layers.
1777  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1778  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1779 
1780  TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1781  TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1782  TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1783  TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1784 
1785  // Connects up.
1786  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1787  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1788  Connect(input, layer, inputTensorInfo);
1789  Connect(layer, output, outputTensorInfo);
1790  CreateTensorHandles(graph, factory);
1791 
1792  // Makes the workload and checks it.
1793  auto workload = MakeAndCheckWorkload<L2NormalizationWorkload>(*layer, factory);
1794 
1795  L2NormalizationQueueDescriptor queueDescriptor = workload->GetData();
1796  BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1797  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1798  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1799 
1800  // Returns so we can do extra, backend-specific tests.
1801  return workload;
1802 }
1803 
1804 template <typename ReshapeWorkload, armnn::DataType DataType>
1805 std::unique_ptr<ReshapeWorkload> CreateReshapeWorkloadTest(armnn::IWorkloadFactory& factory,
1806  armnn::Graph& graph)
1807 {
1808  // Creates the layer we're testing.
1809  TensorShape outputShape({ 1, 4 });
1810  ReshapeDescriptor reshapeDesc;
1811  reshapeDesc.m_TargetShape = outputShape;
1812  Layer* const layer = graph.AddLayer<ReshapeLayer>(reshapeDesc, "layer");
1813 
1814  // Creates extra layers.
1815  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1816  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1817 
1818  // Connects up.
1819  armnn::TensorInfo inputTensorInfo({ 4, 1 }, DataType);
1820  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1821  Connect(input, layer, inputTensorInfo);
1822  Connect(layer, output, outputTensorInfo);
1823  CreateTensorHandles(graph, factory);
1824 
1825  // Makes the workload and checks it.
1826  auto workload = MakeAndCheckWorkload<ReshapeWorkload>(*layer, factory);
1827 
1828  ReshapeQueueDescriptor queueDescriptor = workload->GetData();
1829  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1830  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1831 
1832  // Returns so we can do extra, backend-specific tests.
1833  return workload;
1834 }
1835 
1836 template <typename ConvertFp16ToFp32Float32Workload>
1837 std::unique_ptr<ConvertFp16ToFp32Float32Workload> CreateConvertFp16ToFp32WorkloadTest(
1838  armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1839 {
1840  // Creates the layer we're testing.
1841  ConvertFp16ToFp32Layer* const layer = graph.AddLayer<ConvertFp16ToFp32Layer>("Fp16ToFp32Converter");
1842 
1843  // Creates extra layers.
1844  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1845  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1846 
1847  // Connects up.
1848  armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1849  armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1850  Connect(input, layer, inputTensorInfo);
1851  Connect(layer, output, outputTensorInfo);
1852  CreateTensorHandles(graph, factory);
1853 
1854  // Makes the workload and checks it.
1855  auto workload = MakeAndCheckWorkload<ConvertFp16ToFp32Float32Workload>(*layer, factory);
1856 
1857  ConvertFp16ToFp32QueueDescriptor queueDescriptor = workload->GetData();
1858  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1859  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1860 
1861  // Returns so we can do extra, backend-specific tests.
1862  return workload;
1863 }
1864 
1865 template <typename ConvertFp32ToFp16Float16Workload>
1866 std::unique_ptr<ConvertFp32ToFp16Float16Workload> CreateConvertFp32ToFp16WorkloadTest(
1867  armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1868 {
1869  // Creates the layer we're testing.
1870  ConvertFp32ToFp16Layer* const layer = graph.AddLayer<ConvertFp32ToFp16Layer>("Fp32ToFp16Converter");
1871 
1872  // Creates extra layers.
1873  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1874  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1875 
1876  // Connects up.
1877  armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1878  armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1879  Connect(input, layer, inputTensorInfo);
1880  Connect(layer, output, outputTensorInfo);
1881  CreateTensorHandles(graph, factory);
1882 
1883  // Makes the workload and checks it.
1884  auto workload = MakeAndCheckWorkload<ConvertFp32ToFp16Float16Workload>(*layer, factory);
1885 
1886  ConvertFp32ToFp16QueueDescriptor queueDescriptor = workload->GetData();
1887  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1888  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1889 
1890  // Returns so we can do extra, backend-specific tests.
1891  return workload;
1892 }
1893 
1894 template <typename MeanWorkload, armnn::DataType DataType>
1895 std::unique_ptr<MeanWorkload> CreateMeanWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1896 {
1897  // Reduce along the first and second dimensions, and do not keep the reduced dimensions.
1898  MeanDescriptor descriptor({ 1, 2 }, false);
1899 
1900  // Creates the layer we're testing.
1901  Layer* const layer = graph.AddLayer<MeanLayer>(descriptor, "mean");
1902 
1903  // Creates extra layers.
1904  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1905  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1906 
1907  // Connects up.
1908  armnn::TensorInfo inputTensorInfo({ 1, 3, 7, 4 }, DataType);
1909  armnn::TensorInfo outputTensorInfo({ 1, 4 }, DataType);
1910  Connect(input, layer, inputTensorInfo);
1911  Connect(layer, output, outputTensorInfo);
1912  CreateTensorHandles(graph, factory);
1913 
1914  // Makes the workload and checks it.
1915  auto workload = MakeAndCheckWorkload<MeanWorkload>(*layer, factory);
1916 
1917  MeanQueueDescriptor queueDescriptor = workload->GetData();
1918  BOOST_TEST(queueDescriptor.m_Parameters.m_Axis == descriptor.m_Axis);
1919  BOOST_TEST(queueDescriptor.m_Parameters.m_KeepDims == descriptor.m_KeepDims);
1920  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
1921  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
1922 
1923  // Returns so we can do extra, backend-specific tests.
1924  return workload;
1925 }
1926 
1927 template<typename ConcatWorkload, armnn::DataType DataType>
1928 std::unique_ptr<ConcatWorkload> CreateConcatWorkloadTest(armnn::IWorkloadFactory &factory,
1929  armnn::Graph &graph,
1930  const armnn::TensorShape &outputShape,
1931  unsigned int concatAxis)
1932 {
1933  armnn::TensorInfo inputTensorInfo({ 2, 3, 2, 5 }, DataType);
1934  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1935 
1936  // Constructs the graph.
1937  Layer* const input0 = graph.AddLayer<InputLayer>(0, "input0");
1938  Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
1939  armnn::OriginsDescriptor descriptor;
1940 
1941  std::vector<armnn::TensorShape> inputShapes{{ 2, 3, 2, 5 }, { 2, 3, 2, 5 }};
1942 
1943  descriptor = CreateDescriptorForConcatenation(inputShapes.begin(),
1944  inputShapes.end(),
1945  concatAxis);
1946 
1947  Layer* const concat = graph.AddLayer<ConcatLayer>(descriptor, "concat");
1948  BOOST_TEST_CHECKPOINT("created concat layer");
1949 
1950  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1951 
1952  // Adds connections.
1953  Connect(input0, concat, inputTensorInfo, 0, 0);
1954  BOOST_TEST_CHECKPOINT("connect input0 to concat");
1955  Connect(input1, concat, inputTensorInfo, 0, 1);
1956  BOOST_TEST_CHECKPOINT("connect input1 to concat");
1957  Connect(concat, output, outputTensorInfo, 0, 0);
1958  BOOST_TEST_CHECKPOINT("connect concat to output");
1959 
1960  CreateTensorHandles(graph, factory);
1961  BOOST_TEST_CHECKPOINT("created tensor handles");
1962 
1963  auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
1964  BOOST_TEST_CHECKPOINT("created concat workload");
1965 
1966  return workloadConcat;
1967 }
1968 
1969 template <typename PreCompiledWorkload, armnn::DataType dataType>
1970 std::pair<armnn::IOptimizedNetworkPtr, std::unique_ptr<PreCompiledWorkload>> CreatePreCompiledWorkloadTest(
1971  armnn::IWorkloadFactory& factory,
1972  armnn::Graph& graph,
1973  bool biasEnabled = false)
1974 {
1975  IgnoreUnused(graph);
1976 
1977  // build up the structure of the network
1979 
1980  // Add an input layer
1981  armnn::IConnectableLayer* const inputLayer = net->AddInputLayer(0, "input layer");
1982  BOOST_TEST(inputLayer);
1983 
1984  // ArmNN weights tensor shape is OIHW (out channels, in channels, height, width) for NCHW
1985  // ArmNN weights tensor shape is OHWI (out channels, height, width, in channels) for NHWC
1986  // this test is using NHWC, so the weights shape is OHWI
1987  TensorInfo weightsTensorInfo(TensorShape({16, 1, 1, 16}), dataType, 0.9f, 0);
1988  unsigned int weightsLength = weightsTensorInfo.GetNumElements();
1989 
1990  using WeightType = armnn::ResolveType<dataType>;
1991  std::vector<WeightType> convWeightsData(weightsLength);
1992  for (unsigned int i = 0; i < weightsLength; ++i)
1993  {
1994  convWeightsData[i] = static_cast<WeightType>(i);
1995  }
1996 
1997  armnn::ConstTensor weights(weightsTensorInfo, convWeightsData);
1998 
1999  // Add a layer that can be used in the PreCompiled layer
2000  armnn::Convolution2dDescriptor convDesc2d;
2001  convDesc2d.m_StrideX = 1;
2002  convDesc2d.m_StrideY = 1;
2003  convDesc2d.m_BiasEnabled = biasEnabled;
2005 
2006  armnn::IConnectableLayer* convLayer = nullptr;
2007  const std::string convLayerName("conv layer");
2008 
2009  if (biasEnabled)
2010  {
2011  constexpr armnn::DataType biasDataType = ( dataType == armnn::DataType::QAsymmU8) ?
2012  armnn::DataType::Signed32 : armnn::DataType::Float32;
2013 
2014  TensorInfo biasTensorInfo(TensorShape({16}), biasDataType, 0.9f * 0.9f, 0);
2015  unsigned int biasLength = biasTensorInfo.GetNumElements();
2016 
2017  using BiasType = armnn::ResolveType<biasDataType>;
2018  std::vector<BiasType> biasData(biasLength);
2019  std::fill(biasData.begin(), biasData.end(), static_cast<BiasType>(0));
2020 
2021  armnn::ConstTensor biases(biasTensorInfo, biasData);
2022 
2023  // Create convolution layer with biases
2024  convLayer = net->AddConvolution2dLayer(convDesc2d,
2025  weights,
2026  Optional<ConstTensor>(biases),
2027  convLayerName.c_str());
2028  }
2029  else
2030  {
2031  // Create convolution layer without biases
2032  convLayer = net->AddConvolution2dLayer(convDesc2d,
2033  weights,
2034  EmptyOptional(),
2035  convLayerName.c_str());
2036  }
2037 
2038  BOOST_TEST(convLayer);
2039 
2040  // Add an output layer
2041  armnn::IConnectableLayer* const outputLayer = net->AddOutputLayer(0, "output layer");
2042  BOOST_TEST(outputLayer);
2043 
2044  // set the tensors in the network (NHWC format)
2045  TensorInfo inputTensorInfo(TensorShape({ 1, 16, 16, 16 }), dataType);
2046  if (dataType == armnn::DataType::QAsymmU8)
2047  {
2048  inputTensorInfo.SetQuantizationOffset(0);
2049  inputTensorInfo.SetQuantizationScale(0.9f);
2050  }
2051 
2052  TensorInfo outputTensorInfo(TensorShape({1, 16, 16, 16}), dataType);
2053  if (dataType == armnn::DataType::QAsymmU8)
2054  {
2055  outputTensorInfo.SetQuantizationOffset(0);
2056  outputTensorInfo.SetQuantizationScale(0.9f);
2057  }
2058 
2059  // Connect the layers
2060  inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
2061  inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
2062 
2063  convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
2064  convLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2065 
2066  // Optimize the network for the backend supported by the factory
2067  std::vector<armnn::BackendId> backends = {factory.GetBackendId()};
2069  armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
2070  armnn::OptimizerOptions optimizerOptions;
2071  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec(),
2072  optimizerOptions);
2073  BOOST_CHECK(optimizedNet != nullptr);
2074 
2075  // Find the PreCompiled layer in the optimised graph
2076  armnn::Graph& optimisedGraph = GetGraphForTesting(optimizedNet.get());
2077  Layer* preCompiledLayer = nullptr;
2078  for (auto& layer : optimisedGraph)
2079  {
2080  if (layer->GetType() == LayerType::PreCompiled)
2081  {
2082  preCompiledLayer = layer;
2083  }
2084  }
2085  BOOST_CHECK(preCompiledLayer != nullptr);
2086 
2087  // Create the TensorHandles.
2088  CreateTensorHandles(optimisedGraph, factory);
2089 
2090  // Make the workload and check it.
2091  auto workload = MakeAndCheckWorkload<PreCompiledWorkload>(*preCompiledLayer, factory);
2092 
2093  PreCompiledQueueDescriptor queueDescriptor = workload->GetData();
2094  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
2095  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
2096 
2097  // Returns the workload so we can do extra, backend-specific tests.
2098  // NOTE: We need to return the optimised network as well, otherwise it gets
2099  // out of scope and the tensor handles get destructed
2100  return std::make_pair(std::move(optimizedNet), std::move(workload));
2101 }
2102 
2103 template<typename ConstantWorkload, armnn::DataType DataType>
2104 std::unique_ptr<ConstantWorkload> CreateConstantWorkloadTest(armnn::IWorkloadFactory& factory,
2105  armnn::Graph& graph,
2106  const armnn::TensorShape& outputShape)
2107 {
2108  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2109 
2110  auto constant = graph.AddLayer<ConstantLayer>("constant");
2111  constant->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(outputTensorInfo);
2112  BOOST_TEST_CHECKPOINT("created constant layer");
2113 
2114  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2115 
2116  // Adds connections.
2117  Connect(constant, output, outputTensorInfo, 0, 0);
2118  BOOST_TEST_CHECKPOINT("connect constant to output");
2119 
2120  CreateTensorHandles(graph, factory);
2121  BOOST_TEST_CHECKPOINT("created tensor handles");
2122 
2123  auto workloadConstant = MakeAndCheckWorkload<ConstantWorkload>(*constant, factory);
2124  BOOST_TEST_CHECKPOINT("created Constant workload");
2125 
2126  return workloadConstant;
2127 }
2128 
2129 template <typename PreluWorkload>
2130 std::unique_ptr<PreluWorkload> CreatePreluWorkloadTest(armnn::IWorkloadFactory& factory,
2131  armnn::Graph& graph,
2132  const armnn::TensorShape& inputShape,
2133  const armnn::TensorShape& alphaShape,
2134  const armnn::TensorShape& outputShape,
2135  armnn::DataType dataType)
2136 {
2137  // Creates the PReLU layer
2138  Layer* const layer = graph.AddLayer<PreluLayer>("prelu");
2139  BOOST_CHECK(layer != nullptr);
2140 
2141  // Creates extra layers
2142  Layer* const input = graph.AddLayer<InputLayer> (0, "input");
2143  Layer* const alpha = graph.AddLayer<InputLayer> (1, "alpha");
2144  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2145  BOOST_CHECK(input != nullptr);
2146  BOOST_CHECK(alpha != nullptr);
2147  BOOST_CHECK(output != nullptr);
2148 
2149  // Connects up
2150  armnn::TensorInfo inputTensorInfo (inputShape, dataType);
2151  armnn::TensorInfo alphaTensorInfo (alphaShape, dataType);
2152  armnn::TensorInfo outputTensorInfo(outputShape, dataType);
2153  Connect(input, layer, inputTensorInfo, 0, 0);
2154  Connect(alpha, layer, alphaTensorInfo, 0, 1);
2155  Connect(layer, output, outputTensorInfo, 0, 0);
2156  CreateTensorHandles(graph, factory);
2157 
2158  // Makes the workload and checks it
2159  auto workload = MakeAndCheckWorkload<PreluWorkload>(*layer, factory);
2160 
2161  PreluQueueDescriptor queueDescriptor = workload->GetData();
2162  BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
2163  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
2164 
2165  // Returns so we can do extra, backend-specific tests.
2166  return workload;
2167 }
2168 
2169 template <typename SpaceToDepthWorkload, armnn::DataType DataType>
2170 std::unique_ptr<SpaceToDepthWorkload> CreateSpaceToDepthWorkloadTest(armnn::IWorkloadFactory& factory,
2171  armnn::Graph& graph)
2172 {
2174  desc.m_BlockSize = 2;
2175  Layer* const layer = graph.AddLayer<SpaceToDepthLayer>(desc, "spaceToDepth");
2176 
2177  // Creates extra layers.
2178  Layer* const input = graph.AddLayer<InputLayer>(0, "input");
2179  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2180 
2181  // Connects up.
2182  armnn::TensorInfo inputTensorInfo({ 1, 2, 2, 1 }, DataType);
2183  armnn::TensorInfo outputTensorInfo({ 1, 1, 1, 4 }, DataType);
2184 
2185  Connect(input, layer, inputTensorInfo);
2186  Connect(layer, output, outputTensorInfo);
2187 
2188  CreateTensorHandles(graph, factory);
2189 
2190  // Makes the workload and checks it.
2191  auto workload = MakeAndCheckWorkload<SpaceToDepthWorkload>(*layer, factory);
2192 
2193  SpaceToDepthQueueDescriptor queueDescriptor = workload->GetData();
2194  BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
2195  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
2196 
2197  return workload;
2198 }
2199 
2200 template <typename StackWorkload, armnn::DataType DataType>
2201 std::unique_ptr<StackWorkload> CreateStackWorkloadTest(armnn::IWorkloadFactory& factory,
2202  armnn::Graph& graph,
2203  const armnn::TensorShape& inputShape,
2204  const armnn::TensorShape& outputShape,
2205  unsigned int axis,
2206  unsigned int numInputs)
2207 {
2208  armnn::TensorInfo inputTensorInfo(inputShape, DataType);
2209  armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2210 
2211  // Constructs the Stack layer.
2212  armnn::StackDescriptor descriptor(axis, numInputs, inputShape);
2213  Layer* const stackLayer = graph.AddLayer<StackLayer>(descriptor, "stack");
2214  BOOST_CHECK(stackLayer != nullptr);
2215 
2216  // Constructs layer inputs and output.
2217  std::vector<Layer*> inputs;
2218  for (unsigned int i=0; i<numInputs; ++i)
2219  {
2220  inputs.push_back(graph.AddLayer<InputLayer>(
2221  static_cast<int>(i),
2222  ("input" + std::to_string(i)).c_str()
2223  ));
2224  BOOST_CHECK(inputs[i] != nullptr);
2225  }
2226  Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2227  BOOST_CHECK(output != nullptr);
2228 
2229  // Adds connections.
2230  for (unsigned int i=0; i<numInputs; ++i)
2231  {
2232  Connect(inputs[i], stackLayer, inputTensorInfo, 0, i);
2233  }
2234  Connect(stackLayer, output, outputTensorInfo, 0, 0);
2235 
2236  CreateTensorHandles(graph, factory);
2237 
2238  auto stackWorkload = MakeAndCheckWorkload<StackWorkload>(*stackLayer, factory);
2239  StackQueueDescriptor queueDescriptor = stackWorkload->GetData();
2240  BOOST_TEST(queueDescriptor.m_Inputs.size() == numInputs);
2241  BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
2242 
2243  return stackWorkload;
2244 }
2245 
2246 } // 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:37
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:62
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
DataLayout
Definition: Types.hpp:50
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).
std::unique_ptr< ScopedCpuTensorHandle > m_OutputGateBias
A unique pointer to represent 1D bias tensor with dimensions [num_units] (int32). ...
Definition: QLstmLayer.hpp:35
This layer represents a depthwise convolution 2d operation.
std::unique_ptr< ScopedCpuTensorHandle > m_CellLayerNormWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units] (QSymmS16).
Definition: QLstmLayer.hpp:73
LayerT * AddLayer(Args &&... args)
Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
Definition: Graph.hpp:402
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
std::unique_ptr< ScopedCpuTensorHandle > m_Bias
A unique pointer to store Bias values.
std::vector< BackendOptions > ModelOptions
uint32_t m_PoolWidth
Pooling width value.
bool m_PeepholeEnabled
Enable/disable peephole.
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.
float m_HiddenStateScale
Hidden State quantization scale.
float m_OutputIntermediateScale
Output intermediate quantization scale.
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:26
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).
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.
std::unique_ptr< ScopedCpuTensorHandle > m_InputToForgetWeights
A unique pointer to represent 2D weights tensor with dimensions [num_units, inputSize] (QSymmS8)...
Definition: QLstmLayer.hpp:17
std::unique_ptr< ScopedCpuTensorHandle > m_CellBias
A unique pointer to represent 1D bias tensor with dimensions [num_units] (int32). ...
Definition: QLstmLayer.hpp:33
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) 2021 ARM Limited and Contributors.
This layer represents a LSTM operation.
Definition: LstmLayer.hpp:77
void IgnoreUnused(Ts &&...)
std::unique_ptr< ScopedCpuTensorHandle > m_InputToOutputWeights
A unique pointer to represent 2D weights tensor with dimensions [num_units, inputSize] (QSymmS8)...
Definition: QLstmLayer.hpp:21
void SetBackendId(const BackendId &id)
Definition: Layer.hpp:270
A SpaceToDepthDescriptor for the SpaceToDepthLayer.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
std::unique_ptr< ScopedCpuTensorHandle > m_OutputLayerNormWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units] (QSymmS16).
Definition: QLstmLayer.hpp:75
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
QLstmOptLayerNormParameters m_LayerNormParameters
Definition: QLstmLayer.hpp:87
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).
A StackDescriptor for the StackLayer.
TensorShape m_TargetShape
Target shape value.
std::unique_ptr< ScopedCpuTensorHandle > m_RecurrentToForgetWeights
A unique pointer to represent 2D weights tensor with dimensions [num_units, outputSize] (QSymmS8)...
Definition: QLstmLayer.hpp:24
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
bool m_LayerNormEnabled
Enable/disable layer normalization.
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:1502
This layer represents a QuantizedLstm operation.
This layer represents a log softmax 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.
float m_ProjectionClip
Clipping threshold value for the projection.
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:314
float m_InputIntermediateScale
Input intermediate quantization scale.
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:174
A QLstmDescriptor for the QLstmLayer.
std::unique_ptr< ScopedCpuTensorHandle > m_ForgetLayerNormWeights
A unique pointer to represent 1D weights tensor with dimensions [num_units] (QSymmS16).
Definition: QLstmLayer.hpp:71
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:25
min(a, max(b, input)) ReLu1 & ReLu6.
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)
void SetAdditionalInfoForObject(const AdditionalInfoObjectPtr &additionalInfo)
Definition: Layer.hpp:347
std::unique_ptr< ScopedCpuTensorHandle > m_InputToCellWeights
A unique pointer to represent 2D weights tensor with dimensions [num_units, inputSize] (QSymmS8)...
Definition: QLstmLayer.hpp:19
float m_ForgetIntermediateScale
Forget intermediate quantization scale.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
This layer represents an addition operation.
std::unique_ptr< ScopedCpuTensorHandle > m_RecurrentToOutputWeights
A unique pointer to represent 2D weights tensor with dimensions [output_size, num_units].
Definition: LstmLayer.hpp:67
QLstmBasicParameters m_BasicParameters
Definition: QLstmLayer.hpp:83
LstmOptPeepholeParameters m_PeepholeParameters
Definition: LstmLayer.hpp:84
Graph & GetGraphForTesting(IOptimizedNetwork *optNet)
Definition: TestUtils.cpp:25
NormalizationAlgorithmChannel m_NormChannelType
Normalization channel algorithm to use (Across, Within).
This layer represents a QLstm operation.
Definition: QLstmLayer.hpp:79
float m_CellClip
Clipping threshold value for the cell state.
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
Definition: Descriptors.hpp:50
This layer represents a subtraction operation.
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
std::unique_ptr< ScopedCpuTensorHandle > m_ForgetGateBias
A unique pointer to represent 1D bias tensor with dimensions [num_units] (int32). ...
Definition: QLstmLayer.hpp:31
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:98
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.
std::unique_ptr< ScopedCpuTensorHandle > m_RecurrentToCellWeights
A unique pointer to represent 2D weights tensor with dimensions [num_units, outputSize] (QSymmS8)...
Definition: QLstmLayer.hpp:26
CPU Execution: NEON: ArmCompute.
bool m_ProjectionEnabled
Enable/disable the projection layer.
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:94
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:283
std::unique_ptr< ScopedCpuTensorHandle > m_RecurrentToOutputWeights
A unique pointer to represent 2D weights tensor with dimensions [num_units, outputSize] (QSymmS8)...
Definition: QLstmLayer.hpp:28
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 SetQuantizationOffset(int32_t offset)
Definition: Tensor.cpp:480
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:177
This layer represents a mean operation.
Definition: MeanLayer.hpp:14
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:173
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.
A NormalizationDescriptor for the NormalizationLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
This layer represents a multiplication operation.
virtual void CreateTensorHandles(const TensorHandleFactoryRegistry &registry, const IWorkloadFactory &factory, const bool IsMemoryManaged=true)
Definition: Layer.cpp:250
virtual std::unique_ptr< IWorkload > CreateWorkload(const IWorkloadFactory &factory) const =0
float m_CellIntermediateScale
Cell intermediate quantization scale.
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:510
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:52
A SoftmaxDescriptor for the SoftmaxLayer.
float m_Beta
Beta value for the normalization equation.
bool m_CifgEnabled
Enable/disable CIFG (coupled input & forget gate).
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, 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.
uint32_t m_PadLeft
Padding left value in the width dimension.
std::shared_ptr< T > GetAdditionalInformation() const
Definition: Layer.hpp:342
This layer represents a resize operation.
Definition: ResizeLayer.hpp:13
LayerType
When adding a new layer, adapt also the LastLayer enum value in the enum class LayerType below...
Definition: Types.hpp:419
uint32_t m_PadRight
Padding right value in the width dimension.
int32_t m_HiddenStateZeroPoint
Hidden State zero point.