blob: 2590ae89b2304401ab2f89563028893bdb79efc3 [file] [log] [blame]
Sadik Armagana097d2a2021-11-24 15:47:28 +00001//
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
13#include <armnnUtils/DataLayoutIndexed.hpp>
Colm Donelan0c479742021-12-10 12:43:54 +000014#include <armnn/backends/TensorHandle.hpp>
15#include <armnn/backends/WorkloadData.hpp>
16#include <armnn/backends/WorkloadFactory.hpp>
Sadik Armagana097d2a2021-11-24 15:47:28 +000017#include <armnn/utility/Assert.hpp>
18#include <armnn/utility/IgnoreUnused.hpp>
19#include <armnn/utility/PolymorphicDowncast.hpp>
20
Sadik Armagana097d2a2021-11-24 15:47:28 +000021#include <doctest/doctest.h>
22
23#include <utility>
24
25using namespace armnn;
26
27namespace
28{
29
30using namespace std;
31
32// Calls CreateWorkload for a layer, and checks the returned pointer is of the correct type.
33template<typename Workload>
34std::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 CHECK_MESSAGE(workload.get() == PolymorphicDowncast<Workload*>(workload.get()),
40 "Cannot convert to derived class");
41 std::string reasonIfUnsupported;
42 layer.SetBackendId(factory.GetBackendId());
43 CHECK(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.
48void 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
65template <typename ActivationWorkload, armnn::DataType DataType>
66std::unique_ptr<ActivationWorkload> CreateActivationWorkloadTest(armnn::IWorkloadFactory& factory,
67 armnn::Graph& graph)
68{
69 // Creates the layer we're testing.
70 ActivationDescriptor layerDesc;
Teresa Charlin98b0dcb2022-01-18 22:09:29 +000071 layerDesc.m_Function = ActivationFunction::ReLu;
Sadik Armagana097d2a2021-11-24 15:47:28 +000072 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 CHECK(queueDescriptor.m_Inputs.size() == 1);
94 CHECK(queueDescriptor.m_Outputs.size() == 1);
95 CHECK(queueDescriptor.m_Parameters.m_A == 3.5f);
96 CHECK(queueDescriptor.m_Parameters.m_B == -10.0f);
Teresa Charlin98b0dcb2022-01-18 22:09:29 +000097 CHECK((queueDescriptor.m_Parameters.m_Function == ActivationFunction::ReLu));
Sadik Armagana097d2a2021-11-24 15:47:28 +000098
99 // Returns so we can do extra, backend-specific tests.
100 return workload;
101}
102
103template <typename WorkloadType,
104 typename DescriptorType,
105 typename LayerType,
106 armnn::DataType DataType>
107std::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 CHECK(queueDescriptor.m_Inputs.size() == 2);
130 CHECK(queueDescriptor.m_Outputs.size() == 1);
131
132 // Returns so we can do extra, backend-specific tests.
133 return workload;
134}
135
136template<typename WorkloadType,
137 typename DescriptorType,
138 armnn::DataType DataType>
139std::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 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
169 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
170 ARMNN_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 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
183 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
184 ARMNN_ASSERT(
185 static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
186 );
187
188 CHECK(queueDescriptor.m_Inputs.size() == 2);
189 CHECK(queueDescriptor.m_Outputs.size() == 1);
190
191 return workload;
192}
193
194template<typename WorkloadType,
195 typename DescriptorType,
196 armnn::DataType DataType>
197std::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 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
227 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
228 ARMNN_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 CHECK(queueDescriptor.m_Inputs.size() == 2);
237 CHECK(queueDescriptor.m_Outputs.size() == 1);
238 const ActivationDescriptor* queueDescBlobPtr =
239 queueDescriptor.template GetAdditionalInformation<ActivationDescriptor>();
240 IgnoreUnused(queueDescBlobPtr);
241 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
242 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
243 ARMNN_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
250template<typename WorkloadType,
251 typename DescriptorType,
252 armnn::DataType DataType>
253std::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 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
283 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
284 ARMNN_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 CHECK(queueDescriptor.m_Inputs.size() == 2);
296 CHECK(queueDescriptor.m_Outputs.size() == 1);
297 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
298 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
299 ARMNN_ASSERT(
300 static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
301 );
302
303 return workload;
304}
305
306template <typename WorkloadType,
307 typename DescriptorType,
308 armnn::DataType DataType>
309std::unique_ptr<WorkloadType> CreateElementwiseUnaryWorkloadTest(armnn::IWorkloadFactory & factory,
310 armnn::Graph & graph,
311 armnn::UnaryOperation op)
312{
313 ElementwiseUnaryDescriptor desc = ElementwiseUnaryDescriptor(op);
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 CHECK(queueDescriptor.m_Inputs.size() == 1);
328 CHECK(queueDescriptor.m_Outputs.size() == 1);
329
330 return workload;
331}
332
333template <typename BatchNormalizationWorkloadType, armnn::DataType DataType>
334std::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.
349 BatchNormalizationDescriptor layerDesc;
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<ScopedTensorHandle>(weightInfo);
357 layer->m_Variance = std::make_unique<ScopedTensorHandle>(weightInfo);
358 layer->m_Beta = std::make_unique<ScopedTensorHandle>(weightInfo);
359 layer->m_Gamma = std::make_unique<ScopedTensorHandle>(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 CHECK(queueDescriptor.m_Parameters.m_Eps == 0.05f);
379 CHECK(queueDescriptor.m_Inputs.size() == 1);
380 CHECK(queueDescriptor.m_Outputs.size() == 1);
381 CHECK((queueDescriptor.m_Mean->GetTensorInfo() == TensorInfo({3}, DataType)));
382 CHECK((queueDescriptor.m_Variance->GetTensorInfo() == TensorInfo({3}, DataType)));
383 CHECK((queueDescriptor.m_Gamma->GetTensorInfo() == TensorInfo({3}, DataType)));
384 CHECK((queueDescriptor.m_Beta->GetTensorInfo() == TensorInfo({3}, DataType)));
385 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
386
387 // Returns so we can do extra, backend-specific tests.
388 return workload;
389}
390
391template <typename BatchNormalizationWorkloadType, armnn::DataType DataType>
392std::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.
407 BatchNormalizationDescriptor layerDesc;
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<ScopedTensorHandle>(weightInfo);
415 layer->m_Variance = std::make_unique<ScopedTensorHandle>(weightInfo);
416 layer->m_Beta = std::make_unique<ScopedTensorHandle>(weightInfo);
417 layer->m_Gamma = std::make_unique<ScopedTensorHandle>(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 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
433 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
434 ARMNN_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 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
454 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
455 ARMNN_ASSERT(
456 static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
457 );
458
459 CHECK(queueDescriptor.m_Parameters.m_Eps == 0.05f);
460 CHECK(queueDescriptor.m_Inputs.size() == 1);
461 CHECK(queueDescriptor.m_Outputs.size() == 1);
462 CHECK((queueDescriptor.m_Mean->GetTensorInfo() == TensorInfo({3}, DataType)));
463 CHECK((queueDescriptor.m_Variance->GetTensorInfo() == TensorInfo({3}, DataType)));
464 CHECK((queueDescriptor.m_Gamma->GetTensorInfo() == TensorInfo({3}, DataType)));
465 CHECK((queueDescriptor.m_Beta->GetTensorInfo() == TensorInfo({3}, DataType)));
466 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
467
468 // Returns so we can do extra, backend-specific tests.
469 return workload;
470}
471
472template <typename Convolution2dWorkload, armnn::DataType DataType>
473std::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<ScopedTensorHandle>(TensorInfo(weightShape, DataType));
496 layer->m_Bias = std::make_unique<ScopedTensorHandle>(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 CHECK(queueDescriptor.m_Parameters.m_StrideX == 2);
515 CHECK(queueDescriptor.m_Parameters.m_StrideY == 4);
516 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 3);
517 CHECK(queueDescriptor.m_Parameters.m_PadRight == 3);
518 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
519 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
520 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled);
521 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
522
523 CHECK(queueDescriptor.m_Inputs.size() == 1);
524 CHECK(queueDescriptor.m_Outputs.size() == 1);
525 CHECK((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
526 CHECK((queueDescriptor.m_Bias->GetTensorInfo() ==
527 TensorInfo({2}, GetBiasDataType(DataType))));
528
529 // Returns so we can do extra, backend-specific tests.
530 return workload;
531}
532
533template<typename Convolution2dWorkload, armnn::DataType DataType>
534std::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<ScopedTensorHandle>(TensorInfo(weightShape, DataType));
559 layer->m_Bias = std::make_unique<ScopedTensorHandle>(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 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
575 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
576 ARMNN_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 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
596 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
597 ARMNN_ASSERT(
598 static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
599 );
600
601 CHECK(queueDescriptor.m_Parameters.m_StrideX == 2);
602 CHECK(queueDescriptor.m_Parameters.m_StrideY == 4);
603 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 3);
604 CHECK(queueDescriptor.m_Parameters.m_PadRight == 3);
605 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
606 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
607 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled);
608 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
609 CHECK(queueDescriptor.m_Outputs.size() == 1);
610 CHECK((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
611 CHECK((queueDescriptor.m_Bias->GetTensorInfo() ==
612 TensorInfo({2}, GetBiasDataType(DataType))));
613 CHECK(queueDescriptor.m_Inputs.size() == 1);
614
615 // Returns so we can do extra, backend-specific tests.
616 return workload;
617}
618
619template <typename Convolution2dWorkload, armnn::DataType DataType>
620std::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<ScopedTensorHandle>(TensorInfo(weightShape, DataType));
643 layer->m_Bias = std::make_unique<ScopedTensorHandle>(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 CHECK(queueDescriptor.m_Parameters.m_StrideX == 1);
662 CHECK(queueDescriptor.m_Parameters.m_StrideY == 1);
663 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 0);
664 CHECK(queueDescriptor.m_Parameters.m_PadRight == 0);
665 CHECK(queueDescriptor.m_Parameters.m_PadTop == 0);
666 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 0);
667 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
668
669 CHECK(queueDescriptor.m_Inputs.size() == 1);
670 CHECK(queueDescriptor.m_Outputs.size() == 1);
671 CHECK((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
672
673 // Returns so we can do extra, backend-specific tests.
674 return workload;
675}
676
677template <typename LstmWorkload>
678std::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<ScopedTensorHandle>
696 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
697 layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedTensorHandle>
698 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
699 layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedTensorHandle>
700 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
701 layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedTensorHandle>
702 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
703 layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedTensorHandle>
704 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
705 layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedTensorHandle>
706 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
707 layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedTensorHandle>
708 (TensorInfo({ numUnits }, DataType::Float32));
709 layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedTensorHandle>
710 (TensorInfo({ numUnits }, DataType::Float32));
711 layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedTensorHandle>
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();
717 layer->m_BasicParameters.m_RecurrentToForgetWeights->Allocate();
718 layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
719 layer->m_BasicParameters.m_RecurrentToOutputWeights->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<ScopedTensorHandle>
728 (TensorInfo({ numUnits }, DataType::Float32));
729 layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedTensorHandle>
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) },
749 DataType::Float32);
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 CHECK(queueDescriptor.m_Parameters.m_ActivationFunc == 4);
764 CHECK(queueDescriptor.m_Parameters.m_ClippingThresCell == 0.0f);
765 CHECK(queueDescriptor.m_Parameters.m_ClippingThresProj == 0.0f);
766 CHECK(queueDescriptor.m_Inputs.size() == 3);
767 CHECK(queueDescriptor.m_Outputs.size() == 4);
768
769 CHECK((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == TensorInfo({ numUnits, inputSize },
770 DataType::Float32)));
771 CHECK((queueDescriptor.m_OutputGateBias->GetTensorInfo() == TensorInfo({ numUnits },
772 DataType::Float32)));
773 CHECK((queueDescriptor.m_CellBias->GetTensorInfo() == TensorInfo({ numUnits }, DataType::Float32)));
774 return workload;
775}
776
777template <typename QuantizedLstmWorkload>
778std::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},
801 armnn::DataType::QAsymmU8,
802 weightsScale,
803 weightsOffset);
804
805 armnn::TensorInfo recurrentWeightsInfo({outputSize, outputSize},
806 armnn::DataType::QAsymmU8,
807 weightsScale,
808 weightsOffset);
809
810 armnn::TensorInfo biasInfo({outputSize},
811 armnn::DataType::Signed32,
812 biasScale,
813 biasOffset);
814
815 // Weights and bias
816 layer->m_QuantizedLstmParameters.m_InputToInputWeights =
817 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
818 layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
819 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
820 layer->m_QuantizedLstmParameters.m_InputToCellWeights =
821 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
822 layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
823 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
824
825 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
826 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
827 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
828 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
829 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
830 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
831 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
832 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
833
834 layer->m_QuantizedLstmParameters.m_InputGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
835 layer->m_QuantizedLstmParameters.m_ForgetGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
836 layer->m_QuantizedLstmParameters.m_CellBias = std::make_unique<ScopedTensorHandle>(biasInfo);
837 layer->m_QuantizedLstmParameters.m_OutputGateBias = std::make_unique<ScopedTensorHandle>(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},
865 armnn::DataType::QAsymmU8,
866 inputOutputScale,
867 inputOutputOffset);
868
869 armnn::TensorInfo cellStateInfo({numBatches , outputSize},
870 armnn::DataType::QSymmS16,
871 cellStateScale,
872 cellStateOffset);
873
874 armnn::TensorInfo outputStateInfo({numBatches , outputSize},
875 armnn::DataType::QAsymmU8,
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 CHECK(queueDescriptor.m_Inputs.size() == 3);
895 CHECK(queueDescriptor.m_Outputs.size() == 2);
896
897 // Validate weight tensor info
898 CHECK((queueDescriptor.m_InputToInputWeights->GetTensorInfo() == inputWeightsInfo));
899 CHECK((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == inputWeightsInfo));
900 CHECK((queueDescriptor.m_InputToCellWeights->GetTensorInfo() == inputWeightsInfo));
901 CHECK((queueDescriptor.m_InputToOutputWeights->GetTensorInfo() == inputWeightsInfo));
902
903 CHECK((queueDescriptor.m_RecurrentToInputWeights->GetTensorInfo() == recurrentWeightsInfo));
904 CHECK((queueDescriptor.m_RecurrentToForgetWeights->GetTensorInfo() == recurrentWeightsInfo));
905 CHECK((queueDescriptor.m_RecurrentToCellWeights->GetTensorInfo() == recurrentWeightsInfo));
906 CHECK((queueDescriptor.m_RecurrentToOutputWeights->GetTensorInfo() == recurrentWeightsInfo));
907
908 CHECK((queueDescriptor.m_InputGateBias->GetTensorInfo() == biasInfo));
909 CHECK((queueDescriptor.m_ForgetGateBias->GetTensorInfo() == biasInfo));
910 CHECK((queueDescriptor.m_CellBias->GetTensorInfo() == biasInfo));
911 CHECK((queueDescriptor.m_OutputGateBias->GetTensorInfo() == biasInfo));
912
913 return workload;
914}
915
916template <typename QLstmWorkload>
917std::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},
966 armnn::DataType::QSymmS8,
967 weightsScale,
968 weightsOffset);
969
970 armnn::TensorInfo recurrentWeightsInfo({outputSize, outputSize},
971 armnn::DataType::QSymmS8,
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<ScopedTensorHandle>(inputWeightsInfo);
981 layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
982 layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
983
984 layer->m_BasicParameters.m_RecurrentToForgetWeights =
985 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
986 layer->m_BasicParameters.m_RecurrentToCellWeights =
987 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
988 layer->m_BasicParameters.m_RecurrentToOutputWeights =
989 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
990
991 layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
992 layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedTensorHandle>(biasInfo);
993 layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
994
995 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
996 std::make_unique<ScopedTensorHandle>(layerNormWeightsInfo);
997 layer->m_LayerNormParameters.m_CellLayerNormWeights =
998 std::make_unique<ScopedTensorHandle>(layerNormWeightsInfo);
999 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1000 std::make_unique<ScopedTensorHandle>(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
1014 layer->m_LayerNormParameters.m_ForgetLayerNormWeights->Allocate();
1015 layer->m_LayerNormParameters.m_CellLayerNormWeights->Allocate();
1016 layer->m_LayerNormParameters.m_OutputLayerNormWeights->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},
1029 armnn::DataType::QAsymmS8,
1030 inputScale,
1031 inputOffset);
1032
1033 armnn::TensorInfo cellStateInfo({numBatches , numUnits},
1034 armnn::DataType::QSymmS16,
1035 cellStateScale,
1036 cellStateOffset);
1037
1038 armnn::TensorInfo outputStateInfo({numBatches , outputSize},
1039 armnn::DataType::QAsymmS8,
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 CHECK(queueDescriptor.m_Parameters.m_CellClip == 0.0f);
1058 CHECK(queueDescriptor.m_Parameters.m_ProjectionClip == 0.0f);
1059 CHECK(queueDescriptor.m_Inputs.size() == 3);
1060 CHECK(queueDescriptor.m_Outputs.size() == 3);
1061
1062 CHECK((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == inputWeightsInfo));
1063 CHECK((queueDescriptor.m_InputToCellWeights->GetTensorInfo() == inputWeightsInfo));
1064 CHECK((queueDescriptor.m_InputToOutputWeights->GetTensorInfo() == inputWeightsInfo));
1065
1066 CHECK((queueDescriptor.m_RecurrentToForgetWeights->GetTensorInfo() == recurrentWeightsInfo));
1067 CHECK((queueDescriptor.m_RecurrentToCellWeights->GetTensorInfo() == recurrentWeightsInfo));
1068 CHECK((queueDescriptor.m_RecurrentToOutputWeights->GetTensorInfo() == recurrentWeightsInfo));
1069
1070 CHECK((queueDescriptor.m_ForgetGateBias->GetTensorInfo() == biasInfo));
1071 CHECK((queueDescriptor.m_CellBias->GetTensorInfo() == biasInfo));
1072 CHECK((queueDescriptor.m_OutputGateBias->GetTensorInfo() == biasInfo));
1073
1074 return workload;
1075}
1076
1077template <typename Convolution2dWorkload, armnn::DataType DataType>
1078std::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<ScopedTensorHandle>(TensorInfo({ 2, 3, 3, 3 }, DataType, inputsQScale));
1097 layer->m_Bias = std::make_unique<ScopedTensorHandle>
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 CHECK(queueDescriptor.m_Parameters.m_StrideX == 1);
1116 CHECK(queueDescriptor.m_Parameters.m_StrideY == 1);
1117 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 1);
1118 CHECK(queueDescriptor.m_Parameters.m_PadRight == 1);
1119 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
1120 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
1121 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1122
1123 CHECK(queueDescriptor.m_Inputs.size() == 1);
1124 CHECK(queueDescriptor.m_Outputs.size() == 1);
1125 CHECK((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({2, 3, 3, 3},
1126 DataType, inputsQScale)));
1127 CHECK((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
1134template <typename DepthwiseConvolution2dFloat32Workload, armnn::DataType DataType>
1135std::unique_ptr<DepthwiseConvolution2dFloat32Workload> CreateDepthwiseConvolution2dWorkloadTest(
1136 armnn::IWorkloadFactory& factory, armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
1137{
1138 // Creates the layer we're testing.
1139 DepthwiseConvolution2dDescriptor layerDesc;
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
Cathal Corbett06902652022-04-14 17:55:11 +01001149 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1150 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1151
1152 TensorShape weightShape({1, 4, 4, 2});
1153 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1154 TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
1155 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1156 TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
1157
Sadik Armagana097d2a2021-11-24 15:47:28 +00001158 DepthwiseConvolution2dLayer* const layer = graph.AddLayer<DepthwiseConvolution2dLayer>(layerDesc, "layer");
1159
Cathal Corbett06902652022-04-14 17:55:11 +01001160 // As optimization isn't run member variables need to be updated.
1161 layer->m_Weight = std::make_unique<ScopedTensorHandle>(TensorInfo(weightShape, DataType)); // [ 1, H, W, I*M ]
Sadik Armagana097d2a2021-11-24 15:47:28 +00001162 layer->m_Weight->Allocate();
1163
1164 // Creates extra layers.
1165 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
Cathal Corbett06902652022-04-14 17:55:11 +01001166 Layer* const weights = graph.AddLayer<ConstantLayer>("weights");
Sadik Armagana097d2a2021-11-24 15:47:28 +00001167 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1168
Sadik Armagana097d2a2021-11-24 15:47:28 +00001169 // Connects up.
Cathal Corbett06902652022-04-14 17:55:11 +01001170 Connect(input, layer, TensorInfo(inputShape, DataType, inputsQScale));
1171 Connect(weights, layer, TensorInfo(weightShape, DataType, inputsQScale, 0.0f, true), 0, 1);
1172 Connect(layer, output, TensorInfo(outputShape, DataType, outputQScale));
Sadik Armagana097d2a2021-11-24 15:47:28 +00001173 CreateTensorHandles(graph, factory);
1174
1175 // Makes the workload and checks it.
1176 auto workload = MakeAndCheckWorkload<DepthwiseConvolution2dFloat32Workload>(*layer, factory);
1177
1178 DepthwiseConvolution2dQueueDescriptor queueDescriptor = workload->GetData();
1179 CHECK(queueDescriptor.m_Parameters.m_StrideX == 1);
1180 CHECK(queueDescriptor.m_Parameters.m_StrideY == 1);
1181 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 1);
1182 CHECK(queueDescriptor.m_Parameters.m_PadRight == 2);
1183 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
1184 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 2);
1185 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == false);
1186 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1187
Cathal Corbett06902652022-04-14 17:55:11 +01001188 CHECK(queueDescriptor.m_Inputs.size() == 2);
Sadik Armagana097d2a2021-11-24 15:47:28 +00001189 CHECK(queueDescriptor.m_Outputs.size() == 1);
Sadik Armagana097d2a2021-11-24 15:47:28 +00001190
1191 // Returns so we can do extra, backend-specific tests.
1192 return workload;
1193}
1194
1195template <typename FullyConnectedWorkload, armnn::DataType DataType>
1196std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWorkloadTest(armnn::IWorkloadFactory& factory,
1197 armnn::Graph& graph)
1198{
1199 // Creates the layer we're testing.
1200 FullyConnectedDescriptor layerDesc;
1201 layerDesc.m_BiasEnabled = false;
1202 layerDesc.m_TransposeWeightMatrix = true;
1203
1204 FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1205
1206 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1207 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1208
1209 // As optimization isn't run member variables need to be updated.
1210 layer->m_Weight = std::make_unique<ScopedTensorHandle>(TensorInfo({7, 20}, DataType, inputsQScale, 0));
1211 layer->m_Weight->Allocate();
1212
1213 armnn::TensorInfo weightsTensorInfo({7, 20}, DataType, inputsQScale);
1214 weightsTensorInfo.SetConstant();
1215
1216 // Creates extra layers.
1217 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1218 auto const weights = graph.AddLayer<ConstantLayer>("weights");
1219 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1220
1221 weights->m_LayerOutput = std::make_unique<ScopedTensorHandle>(weightsTensorInfo);
1222 weights->m_LayerOutput->Allocate();
1223
1224 // Connects up.
1225 Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0);
1226 Connect(weights, layer, weightsTensorInfo, 0, 1);
1227 Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1228 CreateTensorHandles(graph, factory);
1229
1230 // Makes the workload and checks it.
1231 auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1232
1233 FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1234 CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1235
1236 CHECK(queueDescriptor.m_Inputs.size() == 2);
1237 CHECK(queueDescriptor.m_Outputs.size() == 1);
1238
1239 // Returns so we can do extra, backend-specific tests.
1240 return workload;
1241}
1242
1243template <typename FullyConnectedWorkload, armnn::DataType DataType>
1244std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWithBlobWorkloadTest
1245 (armnn::IWorkloadFactory& factory,
1246 armnn::Graph& graph)
1247{
1248 // Creates the layer we're testing.
1249 FullyConnectedDescriptor layerDesc;
1250 layerDesc.m_BiasEnabled = true;
1251 layerDesc.m_TransposeWeightMatrix = true;
1252
1253 FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1254
1255 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1256 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1257
1258 // As optimization isn't run member variables need to be updated.
1259 layer->m_Weight = std::make_unique<ScopedTensorHandle>(TensorInfo({7, 20}, DataType, inputsQScale, 0));
1260 layer->m_Bias = std::make_unique<ScopedTensorHandle>(TensorInfo({7}, GetBiasDataType(DataType), inputsQScale));
1261 layer->m_Weight->Allocate();
1262 layer->m_Bias->Allocate();
1263
1264 armnn::TensorInfo weightsTensorInfo({7, 20}, DataType, inputsQScale);
1265 armnn::TensorInfo biasesTensorInfo({7}, GetBiasDataType(DataType), inputsQScale);
1266 weightsTensorInfo.SetConstant();
1267 biasesTensorInfo.SetConstant();
1268
1269 auto activationDesc = std::make_shared<ActivationDescriptor>();
1270 activationDesc->m_A = 10.0f;
1271 activationDesc->m_B = 5.0f;
1272 activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
1273
1274 layer->SetAdditionalInfoForObject(activationDesc);
1275
1276 // Check that the additional information can be queried from the layer
1277 std::shared_ptr<ActivationDescriptor> activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
1278 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
1279 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
1280 ARMNN_ASSERT(static_cast<ActivationFunction>(activationDescPtr->m_Function) ==
1281 armnn::ActivationFunction::BoundedReLu);
1282
1283 // Creates extra layers.
1284 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1285 auto const weights = graph.AddLayer<ConstantLayer>("weights");
1286 auto const biases = graph.AddLayer<ConstantLayer>("biases");
1287 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1288
1289 weights->m_LayerOutput = std::make_unique<ScopedTensorHandle>(weightsTensorInfo);
1290 weights->m_LayerOutput->Allocate();
1291 biases->m_LayerOutput = std::make_unique<ScopedTensorHandle>(biasesTensorInfo);
1292 biases->m_LayerOutput->Allocate();
1293
1294 // Connects up.
1295 Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0);
1296 Connect(weights, layer, weightsTensorInfo, 0, 1);
1297 Connect(biases, layer, biasesTensorInfo, 0, 2);
1298 Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1299 CreateTensorHandles(graph, factory);
1300
1301 // Makes the workload and checks it.
1302 auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1303
1304 FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1305
1306 const ActivationDescriptor* queueDescBlobPtr = queueDescriptor.GetAdditionalInformation<ActivationDescriptor>();
1307 IgnoreUnused(queueDescBlobPtr);
1308
1309 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
1310 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
1311 ARMNN_ASSERT(
1312 static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
1313 );
1314
1315 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1316 CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1317 CHECK(queueDescriptor.m_Inputs.size() == 3);
1318 CHECK(queueDescriptor.m_Outputs.size() == 1);
1319
1320 // Returns so we can do extra, backend-specific tests.
1321 return workload;
1322}
1323
1324template <typename FullyConnectedWorkload, armnn::DataType DataType>
1325std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWorkloadWeightsBiasesAsInputsTest
1326 (armnn::IWorkloadFactory& factory,
1327 armnn::Graph& graph)
1328{
1329 // Creates the layer we're testing.
1330 FullyConnectedDescriptor layerDesc;
1331 layerDesc.m_BiasEnabled = true;
1332 layerDesc.m_TransposeWeightMatrix = true;
1333 layerDesc.m_ConstantWeights = false;
1334
1335 FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1336
1337 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1338 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1339
1340 // Creates extra layers with weights and biases as input layers.
1341 Layer* const input = graph.AddLayer<InputLayer>(1, "input");
1342 Layer* const weights = graph.AddLayer<InputLayer>(2, "weights");
1343 Layer* const biases = graph.AddLayer<InputLayer>(3, "biases");
1344 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1345
1346 // Connects up.
1347 Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0);
1348 Connect(weights, layer, TensorInfo({7, 20}, DataType, inputsQScale), 0, 1);
1349 Connect(biases, layer, TensorInfo({7}, GetBiasDataType(DataType), inputsQScale), 0, 2);
1350 Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1351 CreateTensorHandles(graph, factory);
1352
1353 // Makes the workload and checks it.
1354 auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1355
1356 FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1357
1358 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1359 CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1360 CHECK(queueDescriptor.m_Parameters.m_ConstantWeights == false);
1361 CHECK(queueDescriptor.m_Inputs.size() == 3);
1362 CHECK(queueDescriptor.m_Outputs.size() == 1);
1363
1364 // Returns so we can do extra, backend-specific tests.
1365 return workload;
1366}
1367
1368
1369template <typename NormalizationWorkload, armnn::DataType DataType>
1370std::unique_ptr<NormalizationWorkload> CreateNormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
1371 armnn::Graph& graph,
1372 DataLayout dataLayout = DataLayout::NCHW)
1373{
1374 // Creates the layer we're testing.
1375 NormalizationDescriptor layerDesc;
1376 layerDesc.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1377 layerDesc.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
1378 layerDesc.m_NormSize = 3;
1379 layerDesc.m_Alpha = 0.5f;
1380 layerDesc.m_Beta = -1.0f;
1381 layerDesc.m_K = 0.2f;
1382 layerDesc.m_DataLayout = dataLayout;
1383
1384 NormalizationLayer* layer = graph.AddLayer<NormalizationLayer>(layerDesc, "layer");
1385
1386 // Creates extra layers.
1387 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1388 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1389
1390 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1391 TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
1392 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1393 TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
1394
1395 // Connects up.
1396 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1397 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1398 Connect(input, layer, inputTensorInfo);
1399 Connect(layer, output, outputTensorInfo);
1400 CreateTensorHandles(graph, factory);
1401
1402 // Makes the workload and checks it.
1403 auto workload = MakeAndCheckWorkload<NormalizationWorkload>(*layer, factory);
1404
1405 NormalizationQueueDescriptor queueDescriptor = workload->GetData();
1406 CHECK((queueDescriptor.m_Parameters.m_NormChannelType == NormalizationAlgorithmChannel::Across));
1407 CHECK((queueDescriptor.m_Parameters.m_NormMethodType == NormalizationAlgorithmMethod::LocalBrightness));
1408 CHECK(queueDescriptor.m_Parameters.m_NormSize == 3);
1409 CHECK(queueDescriptor.m_Parameters.m_Alpha == 0.5f);
1410 CHECK(queueDescriptor.m_Parameters.m_Beta == -1.0f);
1411 CHECK(queueDescriptor.m_Parameters.m_K == 0.2f);
1412 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1413
1414 CHECK(queueDescriptor.m_Inputs.size() == 1);
1415 CHECK(queueDescriptor.m_Outputs.size() == 1);
1416
1417 // Returns so we can do extra, backend-specific tests.
1418 return workload;
1419}
1420
1421template <typename Pooling2dWorkload, armnn::DataType DataType>
1422std::unique_ptr<Pooling2dWorkload> CreatePooling2dWorkloadTest(armnn::IWorkloadFactory& factory,
1423 armnn::Graph& graph,
1424 DataLayout dataLayout = DataLayout::NCHW)
1425{
1426 // Creates the layer we're testing.
1427 Pooling2dDescriptor layerDesc;
1428 layerDesc.m_PoolType = PoolingAlgorithm::Average;
1429 layerDesc.m_PoolWidth = 3;
1430 layerDesc.m_PoolHeight = 3;
1431 layerDesc.m_PadLeft = 2;
1432 layerDesc.m_PadRight = 2;
1433 layerDesc.m_PadTop = 1;
1434 layerDesc.m_PadBottom = 1;
1435 layerDesc.m_StrideX = 2;
1436 layerDesc.m_StrideY = 3;
1437 layerDesc.m_OutputShapeRounding = OutputShapeRounding::Floor;
1438 layerDesc.m_DataLayout = dataLayout;
1439
1440 Pooling2dLayer* const layer = graph.AddLayer<Pooling2dLayer>(layerDesc, "layer");
1441
1442 // Create extra layers
1443 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1444 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1445
1446 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 5, 5} : TensorShape{3, 5, 5, 2};
1447 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 2, 4} : TensorShape{3, 2, 4, 2};
1448
1449 // Connect up
1450 Connect(input, layer, TensorInfo(inputShape, DataType));
1451 Connect(layer, output, TensorInfo(outputShape, DataType));
1452 CreateTensorHandles(graph, factory);
1453
1454 // Make the workload and checks it
1455 auto workload = MakeAndCheckWorkload<Pooling2dWorkload>(*layer, factory);
1456
1457 Pooling2dQueueDescriptor queueDescriptor = workload->GetData();
1458 CHECK((queueDescriptor.m_Parameters.m_PoolType == PoolingAlgorithm::Average));
1459 CHECK((queueDescriptor.m_Parameters.m_OutputShapeRounding == OutputShapeRounding::Floor));
1460 CHECK(queueDescriptor.m_Parameters.m_PoolWidth == 3);
1461 CHECK(queueDescriptor.m_Parameters.m_PoolHeight == 3);
1462 CHECK(queueDescriptor.m_Parameters.m_StrideX == 2);
1463 CHECK(queueDescriptor.m_Parameters.m_StrideY == 3);
1464 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 2);
1465 CHECK(queueDescriptor.m_Parameters.m_PadRight == 2);
1466 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
1467 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
1468 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1469
1470 CHECK(queueDescriptor.m_Inputs.size() == 1);
1471 CHECK(queueDescriptor.m_Outputs.size() == 1);
1472
1473 // Return so we can do extra, backend-specific tests
1474 return workload;
1475}
1476
1477template <typename SoftmaxWorkload, armnn::DataType DataType>
1478std::unique_ptr<SoftmaxWorkload> CreateSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
1479 armnn::Graph& graph)
1480{
1481 // Create the layer we're testing.
1482 SoftmaxDescriptor softmaxDescriptor;
1483 // Set Axis to -1 if CL or Neon until further Axes are supported.
1484 if (factory.GetBackendId() == armnn::Compute::CpuAcc || factory.GetBackendId() == armnn::Compute::GpuAcc)
1485 {
1486 softmaxDescriptor.m_Axis = -1;
1487 }
1488
1489 Layer* const layer = graph.AddLayer<SoftmaxLayer>(softmaxDescriptor, "layer");
1490 // Create extra layers.
1491 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1492 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1493
1494 // Connect up
1495 armnn::TensorInfo tensorInfo({4, 1}, DataType);
1496 if (DataType == armnn::DataType::QAsymmU8)
1497 {
1498 tensorInfo.SetQuantizationOffset(0);
1499 tensorInfo.SetQuantizationScale(1.f / 256);
1500 }
1501 else if (DataType == armnn::DataType::QAsymmS8)
1502 {
1503 tensorInfo.SetQuantizationOffset(-128);
1504 tensorInfo.SetQuantizationScale(1.f / 256);
1505 }
1506
1507 Connect(input, layer, tensorInfo);
1508 Connect(layer, output, tensorInfo);
1509 CreateTensorHandles(graph, factory);
1510
1511 // Make the workload and checks it.
1512 auto workload = MakeAndCheckWorkload<SoftmaxWorkload>(*layer, factory);
1513
1514 SoftmaxQueueDescriptor queueDescriptor = workload->GetData();
1515 CHECK(queueDescriptor.m_Inputs.size() == 1);
1516 CHECK(queueDescriptor.m_Outputs.size() == 1);
1517
1518 // Return so we can do extra, backend-specific tests.
1519 return workload;
1520}
1521
1522template<typename SplitterWorkload, armnn::DataType DataType>
1523std::unique_ptr<SplitterWorkload>
1524 CreateSplitterWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1525{
1526 // Create the layer we're testing.
1527 // NOTE: need three dimensions channels, height/y, width/x because the Compute
1528 // library restricts subtensors to have the same x and y dimensions as
1529 // their parent tensors, and therefore the origin on the x and y dimension
1530 // has to be zero for any view. So we need a third dimension to split...
1531 // NOTE: arguments are: number of views, number of dimensions.
1532 ViewsDescriptor layerDesc(3, 3);
1533 // NOTE: arguments are: view, dimension, value.
1534 layerDesc.SetViewOriginCoord(0, 0, 0);
1535 layerDesc.SetViewOriginCoord(1, 0, 1);
1536 layerDesc.SetViewOriginCoord(2, 0, 3);
1537
1538 Layer* const layer = graph.AddLayer<SplitterLayer>(layerDesc, "layer");
1539
1540 // Adds extra layers.
1541 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1542 Layer* const output0 = graph.AddLayer<OutputLayer>(0, "output0");
1543 Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
1544 Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
1545
1546 // Connects up.
1547 armnn::TensorInfo tensorInfo({5, 7, 7}, DataType);
1548 Connect(input, layer, tensorInfo);
1549
1550 armnn::TensorInfo output0Info({1, 7, 7}, DataType);
1551 armnn::TensorInfo output1Info({2, 7, 7}, DataType);
1552 armnn::TensorInfo output2Info({2, 7, 7}, DataType);
1553
1554 Connect(layer, output0, output0Info, 0, 0);
1555 Connect(layer, output1, output1Info, 1, 0);
1556 Connect(layer, output2, output2Info, 2, 0);
1557
1558 CreateTensorHandles(graph, factory);
1559
1560 // Makes the workload and checks it.
1561 auto workload = MakeAndCheckWorkload<SplitterWorkload>(*layer, factory);
1562
1563 SplitterQueueDescriptor queueDescriptor = workload->GetData();
1564 CHECK(queueDescriptor.m_Inputs.size() == 1);
1565 CHECK(queueDescriptor.m_Outputs.size() == 3);
1566 CHECK(queueDescriptor.m_ViewOrigins.size() == 3);
1567
1568 CHECK(queueDescriptor.m_ViewOrigins[0].m_Origin[0] == 0);
1569 CHECK(queueDescriptor.m_ViewOrigins[1].m_Origin[0] == 1);
1570 CHECK(queueDescriptor.m_ViewOrigins[2].m_Origin[0] == 3);
1571 CHECK(queueDescriptor.m_ViewOrigins[0].m_Origin[1] == 0);
1572 CHECK(queueDescriptor.m_ViewOrigins[1].m_Origin[1] == 0);
1573 CHECK(queueDescriptor.m_ViewOrigins[2].m_Origin[1] == 0);
1574 CHECK(queueDescriptor.m_ViewOrigins[0].m_Origin[2] == 0);
1575 CHECK(queueDescriptor.m_ViewOrigins[1].m_Origin[2] == 0);
1576 CHECK(queueDescriptor.m_ViewOrigins[2].m_Origin[2] == 0);
1577
1578 // Returns so we can do extra, backend-specific tests.
1579 return workload;
1580}
1581
1582/// This function constructs a graph with both a splitter and a concat, and returns a pair of the workloads.
1583template<typename SplitterWorkload, typename ConcatWorkload, armnn::DataType DataType>
1584std::pair<std::unique_ptr<SplitterWorkload>, std::unique_ptr<ConcatWorkload>>
1585 CreateSplitterConcatWorkloadTest(armnn::IWorkloadFactory &factory, armnn::Graph &graph)
1586{
1587 armnn::TensorInfo inputTensorInfo({ 1, 2, 100, 10 }, DataType);
1588
1589 armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 10 }, DataType);
1590 armnn::TensorInfo splitTensorInfo2({ 1, 1, 100, 10 }, DataType);
1591
1592 //Constructs the graph.
1593 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1594
1595 armnn::ViewsDescriptor splitterViews(2);
1596 splitterViews.SetViewOriginCoord(0, 0, 0);
1597 splitterViews.SetViewOriginCoord(0, 1, 0);
1598 splitterViews.SetViewOriginCoord(0, 2, 0);
1599 splitterViews.SetViewOriginCoord(0, 3, 0);
1600
1601 splitterViews.SetViewOriginCoord(1, 0, 0);
1602 splitterViews.SetViewOriginCoord(1, 1, 1);
1603 splitterViews.SetViewOriginCoord(1, 2, 0);
1604 splitterViews.SetViewOriginCoord(1, 3, 0);
1605
1606 // create splitter layer
1607 Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
1608 CHECK(splitter);
1609
1610 armnn::OriginsDescriptor concatViews(2);
1611 concatViews.SetViewOriginCoord(0, 0, 0);
1612 concatViews.SetViewOriginCoord(0, 1, 1);
1613 concatViews.SetViewOriginCoord(0, 2, 0);
1614 concatViews.SetViewOriginCoord(0, 3, 0);
1615
1616 concatViews.SetViewOriginCoord(1, 0, 0);
1617 concatViews.SetViewOriginCoord(1, 1, 0);
1618 concatViews.SetViewOriginCoord(1, 2, 0);
1619 concatViews.SetViewOriginCoord(1, 3, 0);
1620
1621 // create concat layer
1622 Layer* const concat = graph.AddLayer<ConcatLayer>(concatViews, "concat");
1623 CHECK(concat);
1624
1625 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1626
1627 // Adds connections.
1628 // connect input to splitter
1629 Connect(input, splitter, inputTensorInfo, 0, 0);
1630 // connect splitter[0] to concat[1]
1631 Connect(splitter, concat, splitTensorInfo1, 0, 1); // The splitter & concat are connected up.
1632 // connect splitter[1] to concat[0]
1633 Connect(splitter, concat, splitTensorInfo2, 1, 0); // So that the outputs are flipped round.
1634 // connect concat to output
1635 Connect(concat, output, inputTensorInfo, 0, 0);
1636
1637 // created tensor handles
1638 CreateTensorHandles(graph, factory);
1639
1640 // created splitter workload
1641 auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
1642 CHECK(workloadSplitter);
1643 // created concat workload
1644 auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
1645 CHECK(workloadConcat);
1646
1647 return {std::move(workloadSplitter), std::move(workloadConcat)};
1648}
1649
1650
1651/// This function constructs a graph with a splitter with two outputs. Each of the outputs is then
1652/// connected to two different activation layers
1653template<typename SplitterWorkload, typename ActivationWorkload, armnn::DataType DataType>
1654void CreateSplitterMultipleInputsOneOutputWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph,
1655 std::unique_ptr<SplitterWorkload>& wlSplitter,
1656 std::unique_ptr<ActivationWorkload>& wlActiv0_0,
1657 std::unique_ptr<ActivationWorkload>& wlActiv0_1,
1658 std::unique_ptr<ActivationWorkload>& wlActiv1_0,
1659 std::unique_ptr<ActivationWorkload>& wlActiv1_1)
1660{
1661 armnn::TensorInfo inputTensorInfo ({ 1, 3, 100, 50 }, DataType);
1662 armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 50 }, DataType);
1663 armnn::TensorInfo splitTensorInfo2({ 1, 2, 100, 50 }, DataType);
1664
1665 //Constructs the graph.
1666 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1667
1668 armnn::ViewsDescriptor splitterViews(2);
1669
1670 splitterViews.SetViewOriginCoord(0, 0, 0);
1671 splitterViews.SetViewOriginCoord(0, 1, 0);
1672 splitterViews.SetViewOriginCoord(0, 2, 0);
1673 splitterViews.SetViewOriginCoord(0, 3, 0);
1674
1675 splitterViews.SetViewOriginCoord(1, 0, 0);
1676 splitterViews.SetViewOriginCoord(1, 1, 1);
1677 splitterViews.SetViewOriginCoord(1, 2, 0);
1678 splitterViews.SetViewOriginCoord(1, 3, 0);
1679
1680 Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
1681
1682 armnn::ActivationDescriptor activationDesc;
1683
1684 Layer* const activ0_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_0");
1685 Layer* const activ0_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_1");
1686 Layer* const activ1_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_0");
1687 Layer* const activ1_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_1");
1688
1689 Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
1690 Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
1691 Layer* const output3 = graph.AddLayer<OutputLayer>(3, "output3");
1692 Layer* const output4 = graph.AddLayer<OutputLayer>(4, "output4");
1693
1694 // Adds connections.
1695 Connect(input, splitter, inputTensorInfo, 0, 0);
1696 Connect(splitter, activ0_0, splitTensorInfo1, 0, 0);
1697 Connect(splitter, activ0_1, splitTensorInfo1, 0, 0);
1698
1699 Connect(splitter, activ1_0, splitTensorInfo2, 1, 0);
1700 Connect(splitter, activ1_1, splitTensorInfo2, 1, 0);
1701
1702 Connect(activ0_0, output1, splitTensorInfo1, 0, 0);
1703 Connect(activ0_1, output2, splitTensorInfo1, 0, 0);
1704 Connect(activ1_0, output3, splitTensorInfo2, 0, 0);
1705 Connect(activ1_1, output4, splitTensorInfo2, 0, 0);
1706
1707 CreateTensorHandles(graph, factory);
1708
1709 auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
1710 auto workloadActiv0_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_0, factory);
1711 auto workloadActiv0_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_1, factory);
1712 auto workloadActiv1_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_0, factory);
1713 auto workloadActiv1_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_1, factory);
1714
1715 wlSplitter = std::move(workloadSplitter);
1716 wlActiv0_0 = std::move(workloadActiv0_0);
1717 wlActiv0_1 = std::move(workloadActiv0_1);
1718 wlActiv1_0 = std::move(workloadActiv1_0);
1719 wlActiv1_1 = std::move(workloadActiv1_1);
1720}
1721
1722template <typename ResizeWorkload, armnn::DataType DataType>
1723std::unique_ptr<ResizeWorkload> CreateResizeBilinearWorkloadTest(armnn::IWorkloadFactory& factory,
1724 armnn::Graph& graph,
1725 DataLayout dataLayout = DataLayout::NCHW)
1726{
1727 TensorShape inputShape;
1728 TensorShape outputShape;
1729
1730 switch (dataLayout) {
1731 case DataLayout::NHWC:
1732 inputShape = { 2, 4, 4, 3 };
1733 outputShape = { 2, 2, 2, 3 };
1734 break;
1735 case DataLayout::NCHW:
1736 default:
1737 inputShape = { 2, 3, 4, 4 };
1738 outputShape = { 2, 3, 2, 2 };
1739 }
1740
1741 // Creates the layer we're testing.
1742 ResizeDescriptor resizeDesc;
1743 armnnUtils::DataLayoutIndexed dimensionIndices = dataLayout;
1744 resizeDesc.m_Method = ResizeMethod::Bilinear;
1745 resizeDesc.m_TargetWidth = outputShape[dimensionIndices.GetWidthIndex()];
1746 resizeDesc.m_TargetHeight = outputShape[dimensionIndices.GetHeightIndex()];
1747 resizeDesc.m_DataLayout = dataLayout;
1748 Layer* const layer = graph.AddLayer<ResizeLayer>(resizeDesc, "resize");
1749
1750 // Creates extra layers.
1751 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1752 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1753
1754 // Connects up.
1755 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1756 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1757 Connect(input, layer, inputTensorInfo);
1758 Connect(layer, output, outputTensorInfo);
1759 CreateTensorHandles(graph, factory);
1760
1761 // Makes the workload and checks it.
1762 auto workload = MakeAndCheckWorkload<ResizeWorkload>(*layer, factory);
1763
1764 auto queueDescriptor = workload->GetData();
1765 CHECK(queueDescriptor.m_Inputs.size() == 1);
1766 CHECK(queueDescriptor.m_Outputs.size() == 1);
1767 CHECK(queueDescriptor.m_Parameters.m_DataLayout == dataLayout);
1768
1769 // Returns so we can do extra, backend-specific tests.
1770 return workload;
1771}
1772
1773template <typename BatchToSpaceNdWorkload, armnn::DataType DataType>
1774std::unique_ptr<BatchToSpaceNdWorkload> CreateBatchToSpaceNdWorkloadTest(armnn::IWorkloadFactory& factory,
1775 armnn::Graph& graph)
1776{
1777 BatchToSpaceNdDescriptor desc;
1778 Layer* const layer = graph.AddLayer<BatchToSpaceNdLayer>(desc, "batchToSpace");
1779
1780 // Creates extra layers.
1781 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1782 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1783
1784 // Connects up.
1785 armnn::TensorInfo tensorInfo({1, 1, 1, 1}, DataType);
1786
1787 Connect(input, layer, tensorInfo);
1788 Connect(layer, output, tensorInfo);
1789
1790 CreateTensorHandles(graph, factory);
1791
1792 // Makes the workload and checks it.
1793 auto workload = MakeAndCheckWorkload<BatchToSpaceNdWorkload>(*layer, factory);
1794
1795 BatchToSpaceNdQueueDescriptor queueDescriptor = workload->GetData();
1796 CHECK(queueDescriptor.m_Inputs.size() == 1);
1797 CHECK(queueDescriptor.m_Outputs.size() == 1);
1798
1799 return workload;
1800}
1801
1802template <typename LogSoftmaxWorkload, armnn::DataType DataType>
1803std::unique_ptr<LogSoftmaxWorkload> CreateLogSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
1804 armnn::Graph& graph)
1805{
1806 // Create the layer we're testing.
1807 LogSoftmaxDescriptor logSoftmaxDescriptor;
1808 // Set Axis to -1 if CL or Neon until further Axes are supported.
1809 if (factory.GetBackendId() == armnn::Compute::CpuAcc || factory.GetBackendId() == armnn::Compute::GpuAcc)
1810 {
1811 logSoftmaxDescriptor.m_Axis = -1;
1812 }
1813
1814 Layer* const layer = graph.AddLayer<LogSoftmaxLayer>(logSoftmaxDescriptor, "layer");
1815 // Create extra layers.
1816 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1817 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1818
1819 // Connect up
1820 armnn::TensorInfo tensorInfo({4, 1}, DataType);
1821
1822 Connect(input, layer, tensorInfo);
1823 Connect(layer, output, tensorInfo);
1824 CreateTensorHandles(graph, factory);
1825
1826 // Make the workload and checks it.
1827 auto workload = MakeAndCheckWorkload<LogSoftmaxWorkload>(*layer, factory);
1828
1829 LogSoftmaxQueueDescriptor queueDescriptor = workload->GetData();
1830 CHECK(queueDescriptor.m_Inputs.size() == 1);
1831 CHECK(queueDescriptor.m_Outputs.size() == 1);
1832
1833 // Return so we can do extra, backend-specific tests.
1834 return workload;
1835}
1836
1837template <typename L2NormalizationWorkload, armnn::DataType DataType>
1838std::unique_ptr<L2NormalizationWorkload> CreateL2NormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
1839 armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
1840{
1841 // Creates the layer we're testing.
1842 L2NormalizationDescriptor layerDesc;
1843 layerDesc.m_DataLayout = dataLayout;
1844
1845 Layer* const layer = graph.AddLayer<L2NormalizationLayer>(layerDesc, "l2norm");
1846
1847 // Creates extra layers.
1848 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1849 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1850
1851 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1852 TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1853 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1854 TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1855
1856 // Connects up.
1857 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1858 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1859 Connect(input, layer, inputTensorInfo);
1860 Connect(layer, output, outputTensorInfo);
1861 CreateTensorHandles(graph, factory);
1862
1863 // Makes the workload and checks it.
1864 auto workload = MakeAndCheckWorkload<L2NormalizationWorkload>(*layer, factory);
1865
1866 L2NormalizationQueueDescriptor queueDescriptor = workload->GetData();
1867 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1868 CHECK(queueDescriptor.m_Inputs.size() == 1);
1869 CHECK(queueDescriptor.m_Outputs.size() == 1);
1870
1871 // Returns so we can do extra, backend-specific tests.
1872 return workload;
1873}
1874
1875template <typename ReshapeWorkload, armnn::DataType DataType>
1876std::unique_ptr<ReshapeWorkload> CreateReshapeWorkloadTest(armnn::IWorkloadFactory& factory,
1877 armnn::Graph& graph)
1878{
1879 // Creates the layer we're testing.
1880 TensorShape outputShape({ 1, 4 });
1881 ReshapeDescriptor reshapeDesc;
1882 reshapeDesc.m_TargetShape = outputShape;
1883 Layer* const layer = graph.AddLayer<ReshapeLayer>(reshapeDesc, "layer");
1884
1885 // Creates extra layers.
1886 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1887 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1888
1889 // Connects up.
1890 armnn::TensorInfo inputTensorInfo({ 4, 1 }, DataType);
1891 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1892 Connect(input, layer, inputTensorInfo);
1893 Connect(layer, output, outputTensorInfo);
1894 CreateTensorHandles(graph, factory);
1895
1896 // Makes the workload and checks it.
1897 auto workload = MakeAndCheckWorkload<ReshapeWorkload>(*layer, factory);
1898
1899 ReshapeQueueDescriptor queueDescriptor = workload->GetData();
1900 CHECK(queueDescriptor.m_Inputs.size() == 1);
1901 CHECK(queueDescriptor.m_Outputs.size() == 1);
1902
1903 // Returns so we can do extra, backend-specific tests.
1904 return workload;
1905}
1906
1907template <typename ConvertFp16ToFp32Float32Workload>
1908std::unique_ptr<ConvertFp16ToFp32Float32Workload> CreateConvertFp16ToFp32WorkloadTest(
1909 armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1910{
1911 // Creates the layer we're testing.
1912 ConvertFp16ToFp32Layer* const layer = graph.AddLayer<ConvertFp16ToFp32Layer>("Fp16ToFp32Converter");
1913
1914 // Creates extra layers.
1915 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1916 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1917
1918 // Connects up.
1919 armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1920 armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1921 Connect(input, layer, inputTensorInfo);
1922 Connect(layer, output, outputTensorInfo);
1923 CreateTensorHandles(graph, factory);
1924
1925 // Makes the workload and checks it.
1926 auto workload = MakeAndCheckWorkload<ConvertFp16ToFp32Float32Workload>(*layer, factory);
1927
1928 ConvertFp16ToFp32QueueDescriptor queueDescriptor = workload->GetData();
1929 CHECK(queueDescriptor.m_Inputs.size() == 1);
1930 CHECK(queueDescriptor.m_Outputs.size() == 1);
1931
1932 // Returns so we can do extra, backend-specific tests.
1933 return workload;
1934}
1935
1936template <typename ConvertFp32ToFp16Float16Workload>
1937std::unique_ptr<ConvertFp32ToFp16Float16Workload> CreateConvertFp32ToFp16WorkloadTest(
1938 armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1939{
1940 // Creates the layer we're testing.
1941 ConvertFp32ToFp16Layer* const layer = graph.AddLayer<ConvertFp32ToFp16Layer>("Fp32ToFp16Converter");
1942
1943 // Creates extra layers.
1944 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1945 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1946
1947 // Connects up.
1948 armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1949 armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1950 Connect(input, layer, inputTensorInfo);
1951 Connect(layer, output, outputTensorInfo);
1952 CreateTensorHandles(graph, factory);
1953
1954 // Makes the workload and checks it.
1955 auto workload = MakeAndCheckWorkload<ConvertFp32ToFp16Float16Workload>(*layer, factory);
1956
1957 ConvertFp32ToFp16QueueDescriptor queueDescriptor = workload->GetData();
1958 CHECK(queueDescriptor.m_Inputs.size() == 1);
1959 CHECK(queueDescriptor.m_Outputs.size() == 1);
1960
1961 // Returns so we can do extra, backend-specific tests.
1962 return workload;
1963}
1964
1965template <typename MeanWorkload, armnn::DataType DataType>
1966std::unique_ptr<MeanWorkload> CreateMeanWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1967{
1968 // Reduce along the first and second dimensions, and do not keep the reduced dimensions.
1969 MeanDescriptor descriptor({ 1, 2 }, false);
1970
1971 // Creates the layer we're testing.
1972 Layer* const layer = graph.AddLayer<MeanLayer>(descriptor, "mean");
1973
1974 // Creates extra layers.
1975 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1976 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1977
1978 // Connects up.
1979 armnn::TensorInfo inputTensorInfo({ 1, 3, 7, 4 }, DataType);
1980 armnn::TensorInfo outputTensorInfo({ 1, 4 }, DataType);
1981 Connect(input, layer, inputTensorInfo);
1982 Connect(layer, output, outputTensorInfo);
1983 CreateTensorHandles(graph, factory);
1984
1985 // Makes the workload and checks it.
1986 auto workload = MakeAndCheckWorkload<MeanWorkload>(*layer, factory);
1987
1988 MeanQueueDescriptor queueDescriptor = workload->GetData();
1989 CHECK(queueDescriptor.m_Parameters.m_Axis == descriptor.m_Axis);
1990 CHECK(queueDescriptor.m_Parameters.m_KeepDims == descriptor.m_KeepDims);
1991 CHECK(queueDescriptor.m_Inputs.size() == 1);
1992 CHECK(queueDescriptor.m_Outputs.size() == 1);
1993
1994 // Returns so we can do extra, backend-specific tests.
1995 return workload;
1996}
1997
1998template<typename ConcatWorkload, armnn::DataType DataType>
1999std::unique_ptr<ConcatWorkload> CreateConcatWorkloadTest(armnn::IWorkloadFactory &factory,
2000 armnn::Graph &graph,
2001 const armnn::TensorShape &outputShape,
2002 unsigned int concatAxis)
2003{
2004 armnn::TensorInfo inputTensorInfo({ 2, 3, 2, 5 }, DataType);
2005 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2006
2007 // Constructs the graph.
2008 Layer* const input0 = graph.AddLayer<InputLayer>(0, "input0");
2009 Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
2010 armnn::OriginsDescriptor descriptor;
2011
2012 std::vector<armnn::TensorShape> inputShapes{{ 2, 3, 2, 5 }, { 2, 3, 2, 5 }};
2013
2014 descriptor = CreateDescriptorForConcatenation(inputShapes.begin(),
2015 inputShapes.end(),
2016 concatAxis);
2017
2018 // create concat layer
2019 Layer* const concat = graph.AddLayer<ConcatLayer>(descriptor, "concat");
2020 CHECK(concat);
2021
2022 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2023
2024 // Adds connections.
2025 // connect input0 to concat
2026 Connect(input0, concat, inputTensorInfo, 0, 0);
2027 // connect input1 to concat
2028 Connect(input1, concat, inputTensorInfo, 0, 1);
2029 // connect concat to output
2030 Connect(concat, output, outputTensorInfo, 0, 0);
2031
2032 // create tensor handles
2033 CreateTensorHandles(graph, factory);
2034
2035 // create concat workload
2036 auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
2037 CHECK(workloadConcat);
2038
2039 return workloadConcat;
2040}
2041
2042template <typename PreCompiledWorkload, armnn::DataType dataType>
2043std::pair<armnn::IOptimizedNetworkPtr, std::unique_ptr<PreCompiledWorkload>> CreatePreCompiledWorkloadTest(
2044 armnn::IWorkloadFactory& factory,
2045 armnn::Graph& graph,
2046 bool biasEnabled = false)
2047{
2048 IgnoreUnused(graph);
2049
2050 // build up the structure of the network
2051 armnn::INetworkPtr net(armnn::INetwork::Create());
2052
2053 // Add an input layer
2054 armnn::IConnectableLayer* const inputLayer = net->AddInputLayer(0, "input layer");
2055 CHECK(inputLayer);
2056
2057 // ArmNN weights tensor shape is OIHW (out channels, in channels, height, width) for NCHW
2058 // ArmNN weights tensor shape is OHWI (out channels, height, width, in channels) for NHWC
2059 // this test is using NHWC, so the weights shape is OHWI
2060 TensorInfo weightsTensorInfo(TensorShape({16, 1, 1, 16}), dataType, 0.9f, 0, true);
2061 unsigned int weightsLength = weightsTensorInfo.GetNumElements();
2062
2063 using WeightType = armnn::ResolveType<dataType>;
2064 std::vector<WeightType> convWeightsData(weightsLength);
2065 for (unsigned int i = 0; i < weightsLength; ++i)
2066 {
2067 convWeightsData[i] = static_cast<WeightType>(i);
2068 }
2069
2070 armnn::ConstTensor weights(weightsTensorInfo, convWeightsData);
2071
2072 // Add a layer that can be used in the PreCompiled layer
2073 armnn::Convolution2dDescriptor convDesc2d;
2074 convDesc2d.m_StrideX = 1;
2075 convDesc2d.m_StrideY = 1;
2076 convDesc2d.m_BiasEnabled = biasEnabled;
2077 convDesc2d.m_DataLayout = armnn::DataLayout::NHWC;
2078
2079 armnn::IConnectableLayer* convLayer = nullptr;
2080 const std::string convLayerName("conv layer");
2081
2082 if (biasEnabled)
2083 {
2084 constexpr armnn::DataType biasDataType = ( dataType == armnn::DataType::QAsymmU8) ?
2085 armnn::DataType::Signed32 : armnn::DataType::Float32;
2086
2087 TensorInfo biasTensorInfo(TensorShape({16}), biasDataType, 0.9f * 0.9f, 0, true);
2088 unsigned int biasLength = biasTensorInfo.GetNumElements();
2089
2090 using BiasType = armnn::ResolveType<biasDataType>;
2091 std::vector<BiasType> biasData(biasLength);
2092 std::fill(biasData.begin(), biasData.end(), static_cast<BiasType>(0));
2093
2094 armnn::ConstTensor biases(biasTensorInfo, biasData);
2095
2096 // Create convolution layer with biases
2097 convLayer = net->AddConvolution2dLayer(convDesc2d,
2098 weights,
2099 Optional<ConstTensor>(biases),
2100 convLayerName.c_str());
2101 }
2102 else
2103 {
2104 // Create convolution layer without biases
2105 convLayer = net->AddConvolution2dLayer(convDesc2d,
2106 weights,
2107 EmptyOptional(),
2108 convLayerName.c_str());
2109 }
2110
2111 CHECK(convLayer);
2112
2113 // Add an output layer
2114 armnn::IConnectableLayer* const outputLayer = net->AddOutputLayer(0, "output layer");
2115 CHECK(outputLayer);
2116
2117 // set the tensors in the network (NHWC format)
2118 TensorInfo inputTensorInfo(TensorShape({ 1, 16, 16, 16 }), dataType);
2119 if (dataType == armnn::DataType::QAsymmU8)
2120 {
2121 inputTensorInfo.SetQuantizationOffset(0);
2122 inputTensorInfo.SetQuantizationScale(0.9f);
2123 }
2124
2125 TensorInfo outputTensorInfo(TensorShape({1, 16, 16, 16}), dataType);
2126 if (dataType == armnn::DataType::QAsymmU8)
2127 {
2128 outputTensorInfo.SetQuantizationOffset(0);
2129 outputTensorInfo.SetQuantizationScale(0.9f);
2130 }
2131
2132 // Connect the layers
2133 inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
2134 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
2135
2136 convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
2137 convLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2138
2139 // Optimize the network for the backend supported by the factory
2140 std::vector<armnn::BackendId> backends = {factory.GetBackendId()};
2141 armnn::IRuntime::CreationOptions options;
2142 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
2143 armnn::OptimizerOptions optimizerOptions;
2144 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec(),
2145 optimizerOptions);
2146 CHECK(optimizedNet != nullptr);
2147
2148 // Find the PreCompiled layer in the optimised graph
2149 armnn::Graph& optimisedGraph = GetGraphForTesting(optimizedNet.get());
2150 Layer* preCompiledLayer = nullptr;
2151 for (auto& layer : optimisedGraph)
2152 {
2153 if (layer->GetType() == LayerType::PreCompiled)
2154 {
2155 preCompiledLayer = layer;
2156 }
2157 }
2158 CHECK(preCompiledLayer != nullptr);
2159
2160 // Create the TensorHandles.
2161 CreateTensorHandles(optimisedGraph, factory);
2162
2163 // Make the workload and check it.
2164 auto workload = MakeAndCheckWorkload<PreCompiledWorkload>(*preCompiledLayer, factory);
2165
2166 PreCompiledQueueDescriptor queueDescriptor = workload->GetData();
2167 CHECK(queueDescriptor.m_Inputs.size() == 1);
2168 CHECK(queueDescriptor.m_Outputs.size() == 1);
2169
2170 // Returns the workload so we can do extra, backend-specific tests.
2171 // NOTE: We need to return the optimised network as well, otherwise it gets
2172 // out of scope and the tensor handles get destructed
2173 return std::make_pair(std::move(optimizedNet), std::move(workload));
2174}
2175
2176template<typename ConstantWorkload, armnn::DataType DataType>
2177std::unique_ptr<ConstantWorkload> CreateConstantWorkloadTest(armnn::IWorkloadFactory& factory,
2178 armnn::Graph& graph,
2179 const armnn::TensorShape& outputShape)
2180{
2181 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2182
2183 // create constant layer
2184 auto constant = graph.AddLayer<ConstantLayer>("constant");
2185 CHECK(constant);
2186 constant->m_LayerOutput = std::make_unique<ScopedTensorHandle>(outputTensorInfo);
2187
2188 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2189
2190 // Adds connections.
2191 // connect constant to output
2192 Connect(constant, output, outputTensorInfo, 0, 0);
2193
2194 // create tensor handles
2195 CreateTensorHandles(graph, factory);
2196
2197 // create Constant workload"
2198 auto workloadConstant = MakeAndCheckWorkload<ConstantWorkload>(*constant, factory);
2199 CHECK(workloadConstant);
2200
2201 return workloadConstant;
2202}
2203
2204template <typename PreluWorkload>
2205std::unique_ptr<PreluWorkload> CreatePreluWorkloadTest(armnn::IWorkloadFactory& factory,
2206 armnn::Graph& graph,
2207 const armnn::TensorShape& inputShape,
2208 const armnn::TensorShape& alphaShape,
2209 const armnn::TensorShape& outputShape,
2210 armnn::DataType dataType)
2211{
2212 // Creates the PReLU layer
2213 Layer* const layer = graph.AddLayer<PreluLayer>("prelu");
2214 CHECK(layer != nullptr);
2215
2216 // Creates extra layers
2217 Layer* const input = graph.AddLayer<InputLayer> (0, "input");
2218 Layer* const alpha = graph.AddLayer<InputLayer> (1, "alpha");
2219 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2220 CHECK(input != nullptr);
2221 CHECK(alpha != nullptr);
2222 CHECK(output != nullptr);
2223
2224 // Connects up
2225 armnn::TensorInfo inputTensorInfo (inputShape, dataType);
2226 armnn::TensorInfo alphaTensorInfo (alphaShape, dataType);
2227 armnn::TensorInfo outputTensorInfo(outputShape, dataType);
2228 Connect(input, layer, inputTensorInfo, 0, 0);
2229 Connect(alpha, layer, alphaTensorInfo, 0, 1);
2230 Connect(layer, output, outputTensorInfo, 0, 0);
2231 CreateTensorHandles(graph, factory);
2232
2233 // Makes the workload and checks it
2234 auto workload = MakeAndCheckWorkload<PreluWorkload>(*layer, factory);
2235
2236 PreluQueueDescriptor queueDescriptor = workload->GetData();
2237 CHECK(queueDescriptor.m_Inputs.size() == 2);
2238 CHECK(queueDescriptor.m_Outputs.size() == 1);
2239
2240 // Returns so we can do extra, backend-specific tests.
2241 return workload;
2242}
2243
2244template <typename SpaceToDepthWorkload, armnn::DataType DataType>
2245std::unique_ptr<SpaceToDepthWorkload> CreateSpaceToDepthWorkloadTest(armnn::IWorkloadFactory& factory,
2246 armnn::Graph& graph)
2247{
2248 SpaceToDepthDescriptor desc;
2249 desc.m_BlockSize = 2;
2250 Layer* const layer = graph.AddLayer<SpaceToDepthLayer>(desc, "spaceToDepth");
2251
2252 // Creates extra layers.
2253 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
2254 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2255
2256 // Connects up.
2257 armnn::TensorInfo inputTensorInfo({ 1, 2, 2, 1 }, DataType);
2258 armnn::TensorInfo outputTensorInfo({ 1, 1, 1, 4 }, DataType);
2259
2260 Connect(input, layer, inputTensorInfo);
2261 Connect(layer, output, outputTensorInfo);
2262
2263 CreateTensorHandles(graph, factory);
2264
2265 // Makes the workload and checks it.
2266 auto workload = MakeAndCheckWorkload<SpaceToDepthWorkload>(*layer, factory);
2267
2268 SpaceToDepthQueueDescriptor queueDescriptor = workload->GetData();
2269 CHECK(queueDescriptor.m_Inputs.size() == 1);
2270 CHECK(queueDescriptor.m_Outputs.size() == 1);
2271
2272 return workload;
2273}
2274
2275template <typename StackWorkload, armnn::DataType DataType>
2276std::unique_ptr<StackWorkload> CreateStackWorkloadTest(armnn::IWorkloadFactory& factory,
2277 armnn::Graph& graph,
2278 const armnn::TensorShape& inputShape,
2279 const armnn::TensorShape& outputShape,
2280 unsigned int axis,
2281 unsigned int numInputs)
2282{
2283 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
2284 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2285
2286 // Constructs the Stack layer.
2287 armnn::StackDescriptor descriptor(axis, numInputs, inputShape);
2288 Layer* const stackLayer = graph.AddLayer<StackLayer>(descriptor, "stack");
2289 CHECK(stackLayer != nullptr);
2290
2291 // Constructs layer inputs and output.
2292 std::vector<Layer*> inputs;
2293 for (unsigned int i=0; i<numInputs; ++i)
2294 {
2295 inputs.push_back(graph.AddLayer<InputLayer>(
2296 static_cast<int>(i),
2297 ("input" + std::to_string(i)).c_str()
2298 ));
2299 CHECK(inputs[i] != nullptr);
2300 }
2301 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2302 CHECK(output != nullptr);
2303
2304 // Adds connections.
2305 for (unsigned int i=0; i<numInputs; ++i)
2306 {
2307 Connect(inputs[i], stackLayer, inputTensorInfo, 0, i);
2308 }
2309 Connect(stackLayer, output, outputTensorInfo, 0, 0);
2310
2311 CreateTensorHandles(graph, factory);
2312
2313 auto stackWorkload = MakeAndCheckWorkload<StackWorkload>(*stackLayer, factory);
2314 StackQueueDescriptor queueDescriptor = stackWorkload->GetData();
2315 CHECK(queueDescriptor.m_Inputs.size() == numInputs);
2316 CHECK(queueDescriptor.m_Outputs.size() == 1);
2317
2318 return stackWorkload;
2319}
2320
2321} // Anonymous namespace