blob: 0846d21388009c4ecad1c2f7a2517f4eba320409 [file] [log] [blame]
Sadik Armagana097d2a2021-11-24 15:47:28 +00001//
Mike Kellyec67a0f2022-11-25 13:55:24 +00002// Copyright © 2017,2022 Arm Ltd and Contributors. All rights reserved.
Sadik Armagana097d2a2021-11-24 15:47:28 +00003// 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;
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100486 layerDesc.m_BiasEnabled = false;
Sadik Armagana097d2a2021-11-24 15:47:28 +0000487 layerDesc.m_DataLayout = dataLayout;
488
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100489 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
490 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
491
Sadik Armagana097d2a2021-11-24 15:47:28 +0000492 Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
493
494 TensorShape weightShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 5, 3} : TensorShape{2, 5, 3, 3};
495 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 8, 16} : TensorShape{2, 8, 16, 3};
496 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 2, 2, 10} : TensorShape{2, 2, 10, 2};
497
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100498 armnn::TensorInfo weightsTensorInfo(weightShape, DataType, inputsQScale);
499 weightsTensorInfo.SetConstant();
Sadik Armagana097d2a2021-11-24 15:47:28 +0000500
501 // Creates extra layers.
502 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100503 auto const weights = graph.AddLayer<ConstantLayer>("weights");
Sadik Armagana097d2a2021-11-24 15:47:28 +0000504 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
505
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100506 weights->m_LayerOutput = std::make_unique<ScopedTensorHandle>(weightsTensorInfo);
507 weights->m_LayerOutput->Allocate();
508
Sadik Armagana097d2a2021-11-24 15:47:28 +0000509 // Connects up.
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100510 Connect(input, layer, TensorInfo(inputShape, DataType, inputsQScale));
511 Connect(weights, layer, weightsTensorInfo, 0, 1);
512 Connect(layer, output, TensorInfo(outputShape, DataType, outputQScale));
Sadik Armagana097d2a2021-11-24 15:47:28 +0000513 CreateTensorHandles(graph, factory);
514
515 // Makes the workload and checks it.
516 auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory, modelOptions);
517
518 Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
519 CHECK(queueDescriptor.m_Parameters.m_StrideX == 2);
520 CHECK(queueDescriptor.m_Parameters.m_StrideY == 4);
521 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 3);
522 CHECK(queueDescriptor.m_Parameters.m_PadRight == 3);
523 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
524 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100525 CHECK(!queueDescriptor.m_Parameters.m_BiasEnabled);
Sadik Armagana097d2a2021-11-24 15:47:28 +0000526 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
527
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100528 CHECK(queueDescriptor.m_Inputs.size() == 2);
Sadik Armagana097d2a2021-11-24 15:47:28 +0000529 CHECK(queueDescriptor.m_Outputs.size() == 1);
Sadik Armagana097d2a2021-11-24 15:47:28 +0000530
531 // Returns so we can do extra, backend-specific tests.
532 return workload;
533}
534
535template<typename Convolution2dWorkload, armnn::DataType DataType>
536std::unique_ptr<Convolution2dWorkload> CreateConvolution2dFusedActivationWithBlobWorkloadTest(
537 armnn::IWorkloadFactory& factory,
538 armnn::Graph& graph,
539 DataLayout dataLayout = DataLayout::NCHW,
540 const ModelOptions& modelOptions = {})
541{
542 // Creates the layer we're testing.
543 Convolution2dDescriptor layerDesc;
544 layerDesc.m_PadLeft = 3;
545 layerDesc.m_PadRight = 3;
546 layerDesc.m_PadTop = 1;
547 layerDesc.m_PadBottom = 1;
548 layerDesc.m_StrideX = 2;
549 layerDesc.m_StrideY = 4;
550 layerDesc.m_BiasEnabled = true;
551 layerDesc.m_DataLayout = dataLayout;
552
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100553 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
554 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
Sadik Armagana097d2a2021-11-24 15:47:28 +0000555
556 Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
557
558 TensorShape weightShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 5, 3} : TensorShape{2, 5, 3, 3};
559 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 8, 16} : TensorShape{2, 8, 16, 3};
560 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 2, 2, 10} : TensorShape{2, 2, 10, 2};
Sadik Armagana097d2a2021-11-24 15:47:28 +0000561
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100562 armnn::TensorInfo weightsTensorInfo(weightShape, DataType, inputsQScale);
563 weightsTensorInfo.SetConstant();
564 armnn::TensorInfo biasTensorInfo({2}, DataType, inputsQScale);
565 biasTensorInfo.SetConstant();
566
Sadik Armagana097d2a2021-11-24 15:47:28 +0000567 auto activationDesc = std::make_shared<ActivationDescriptor>();
568 activationDesc->m_A = 10.0f;
569 activationDesc->m_B = 5.0f;
570 activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
571
572 layer->SetAdditionalInfoForObject(activationDesc);
573
574 // Check that the additional information can be queried from the layer
575 std::shared_ptr<ActivationDescriptor> activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
576
577 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
578 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
579 ARMNN_ASSERT(
580 static_cast<ActivationFunction>(activationDescPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
581 );
582
583 // Creates extra layers.
584 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100585 auto const weights = graph.AddLayer<ConstantLayer>("weights");
586 auto const bias = graph.AddLayer<ConstantLayer>("bias");
Sadik Armagana097d2a2021-11-24 15:47:28 +0000587 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
588
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100589 weights->m_LayerOutput = std::make_unique<ScopedTensorHandle>(weightsTensorInfo);
590 weights->m_LayerOutput->Allocate();
591 bias->m_LayerOutput = std::make_unique<ScopedTensorHandle>(biasTensorInfo);
592 bias->m_LayerOutput->Allocate();
593
Sadik Armagana097d2a2021-11-24 15:47:28 +0000594 // Connects up.
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100595 Connect(input, layer, TensorInfo(inputShape, DataType, inputsQScale));
596 Connect(weights, layer, weightsTensorInfo, 0, 1);
597 Connect(bias, layer, biasTensorInfo, 0, 2);
598 Connect(layer, output, TensorInfo(outputShape, DataType, outputQScale));
Sadik Armagana097d2a2021-11-24 15:47:28 +0000599 CreateTensorHandles(graph, factory);
600
601 // Makes the workload and checks it.
602 auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory, modelOptions);
603
604 Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
605 const ActivationDescriptor* queueDescBlobPtr = queueDescriptor.GetAdditionalInformation<ActivationDescriptor>();
606 IgnoreUnused(queueDescBlobPtr);
607 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
608 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
609 ARMNN_ASSERT(
610 static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
611 );
612
613 CHECK(queueDescriptor.m_Parameters.m_StrideX == 2);
614 CHECK(queueDescriptor.m_Parameters.m_StrideY == 4);
615 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 3);
616 CHECK(queueDescriptor.m_Parameters.m_PadRight == 3);
617 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
618 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
619 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled);
620 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100621
Sadik Armagana097d2a2021-11-24 15:47:28 +0000622 CHECK(queueDescriptor.m_Outputs.size() == 1);
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100623 CHECK(queueDescriptor.m_Inputs.size() == 3);
Sadik Armagana097d2a2021-11-24 15:47:28 +0000624
625 // Returns so we can do extra, backend-specific tests.
626 return workload;
627}
628
629template <typename Convolution2dWorkload, armnn::DataType DataType>
630std::unique_ptr<Convolution2dWorkload> CreateConvolution2dWorkloadFastMathTest(armnn::IWorkloadFactory& factory,
631 armnn::Graph& graph,
632 DataLayout dataLayout = DataLayout::NCHW,
633 const ModelOptions& modelOptions = {})
634{
635 // Creates the layer we're testing.
636 Convolution2dDescriptor layerDesc;
637 layerDesc.m_PadLeft = 0;
638 layerDesc.m_PadRight = 0;
639 layerDesc.m_PadTop = 0;
640 layerDesc.m_PadBottom = 0;
641 layerDesc.m_StrideX = 1;
642 layerDesc.m_StrideY = 1;
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100643 layerDesc.m_BiasEnabled = true;
Sadik Armagana097d2a2021-11-24 15:47:28 +0000644 layerDesc.m_DataLayout = dataLayout;
645
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100646 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
647 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
648
Sadik Armagana097d2a2021-11-24 15:47:28 +0000649 Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
650
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100651 TensorShape weightShape = TensorShape{ 32, 32, 3, 3 };
652 TensorShape biasShape = TensorShape{ 32 };
653 TensorShape inputShape = TensorShape{ 1, 32, 149, 149 };
654 TensorShape outputShape = TensorShape{ 1, 32, 147, 147 };
Sadik Armagana097d2a2021-11-24 15:47:28 +0000655
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100656 armnn::TensorInfo weightsTensorInfo(weightShape, DataType, inputsQScale);
657 weightsTensorInfo.SetConstant();
658 armnn::TensorInfo biasTensorInfo(biasShape, DataType, inputsQScale);
659 biasTensorInfo.SetConstant();
660
Sadik Armagana097d2a2021-11-24 15:47:28 +0000661 // Creates extra layers.
662 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100663 auto const weights = graph.AddLayer<ConstantLayer>("weights");
664 auto const bias = graph.AddLayer<ConstantLayer>("bias");
Sadik Armagana097d2a2021-11-24 15:47:28 +0000665 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
666
667 // Connects up.
668 Connect(input, layer, TensorInfo(inputShape, DataType));
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100669 Connect(weights, layer, weightsTensorInfo, 0, 1);
670 Connect(bias, layer, biasTensorInfo, 0, 2);
671 Connect(layer, output, TensorInfo(outputShape, DataType, outputQScale));
Sadik Armagana097d2a2021-11-24 15:47:28 +0000672 CreateTensorHandles(graph, factory);
673
674 // Makes the workload and checks it.
675 auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory, modelOptions);
676
677 Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
678 CHECK(queueDescriptor.m_Parameters.m_StrideX == 1);
679 CHECK(queueDescriptor.m_Parameters.m_StrideY == 1);
680 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 0);
681 CHECK(queueDescriptor.m_Parameters.m_PadRight == 0);
682 CHECK(queueDescriptor.m_Parameters.m_PadTop == 0);
683 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 0);
684 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
685
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100686 CHECK(queueDescriptor.m_Inputs.size() == 3);
Sadik Armagana097d2a2021-11-24 15:47:28 +0000687 CHECK(queueDescriptor.m_Outputs.size() == 1);
Sadik Armagana097d2a2021-11-24 15:47:28 +0000688
689 // Returns so we can do extra, backend-specific tests.
690 return workload;
691}
692
693template <typename LstmWorkload>
694std::unique_ptr<LstmWorkload> CreateLstmWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
695{
696 // This parameter setting is for withCifgWithPeepholeNoProjection
697 LstmDescriptor layerDesc;
698 layerDesc.m_ActivationFunc = 4;
699 layerDesc.m_ClippingThresCell = 0.0f;
700 layerDesc.m_ClippingThresProj = 0.0f;
701 layerDesc.m_CifgEnabled = true;
702 layerDesc.m_PeepholeEnabled = true;
703 layerDesc.m_ProjectionEnabled = false;
704
705 LstmLayer* const layer = graph.AddLayer<LstmLayer>(layerDesc, "layer");
706 unsigned int batchSize = 2;
707 unsigned int inputSize = 2;
708 unsigned int numUnits = 4;
709 unsigned int outputSize = 4;
710
711 layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedTensorHandle>
712 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
713 layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedTensorHandle>
714 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
715 layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedTensorHandle>
716 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
717 layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedTensorHandle>
718 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
719 layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedTensorHandle>
720 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
721 layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedTensorHandle>
722 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
723 layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedTensorHandle>
724 (TensorInfo({ numUnits }, DataType::Float32));
725 layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedTensorHandle>
726 (TensorInfo({ numUnits }, DataType::Float32));
727 layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedTensorHandle>
728 (TensorInfo({ numUnits }, DataType::Float32));
729
730 layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
731 layer->m_BasicParameters.m_InputToCellWeights->Allocate();
732 layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
733 layer->m_BasicParameters.m_RecurrentToForgetWeights->Allocate();
734 layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
735 layer->m_BasicParameters.m_RecurrentToOutputWeights->Allocate();
736 layer->m_BasicParameters.m_ForgetGateBias->Allocate();
737 layer->m_BasicParameters.m_CellBias->Allocate();
738 layer->m_BasicParameters.m_OutputGateBias->Allocate();
739
740
741 if (layerDesc.m_PeepholeEnabled)
742 {
743 layer->m_PeepholeParameters.m_CellToForgetWeights = std::make_unique<ScopedTensorHandle>
744 (TensorInfo({ numUnits }, DataType::Float32));
745 layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedTensorHandle>
746 (TensorInfo({ numUnits }, DataType::Float32));
747 layer->m_PeepholeParameters.m_CellToForgetWeights->Allocate();
748 layer->m_PeepholeParameters.m_CellToOutputWeights->Allocate();
749 }
750
751 // create input and output layers
752 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
753 Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
754 Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
755 Layer* const scratchBuffer = graph.AddLayer<OutputLayer>(0, "scratchBuffer");
756 Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
757 Layer* const cellStateOut = graph.AddLayer<OutputLayer>(2, "cellStateOut");
758 Layer* const output = graph.AddLayer<OutputLayer>(3, "output");
759
760 // connect up
761 armnn::TensorInfo lstmTensorInfo1({ batchSize, inputSize }, DataType::Float32);
762 armnn::TensorInfo lstmTensorInfo2({ batchSize, numUnits}, DataType::Float32);
763 armnn::TensorInfo lstmTensorInfo3({ batchSize, outputSize }, DataType::Float32);
764 armnn::TensorInfo lstmTensorInfoScratchBuff({ batchSize, numUnits * (layerDesc.m_CifgEnabled ? 3 : 4) },
765 DataType::Float32);
766 Connect(input, layer, lstmTensorInfo1, 0, 0);
767 Connect(cellStateIn, layer, lstmTensorInfo2, 0, 1);
768 Connect(outputStateIn, layer, lstmTensorInfo3, 0, 2);
769 Connect(layer, scratchBuffer, lstmTensorInfoScratchBuff, 0, 0);
770 Connect(layer, outputStateOut, lstmTensorInfo3, 1, 0);
771 Connect(layer, cellStateOut, lstmTensorInfo2, 2, 0);
772 Connect(layer, output, lstmTensorInfo3, 3, 0);
773
774 CreateTensorHandles(graph, factory);
775
776 // make the workload and check it
777 auto workload = MakeAndCheckWorkload<LstmWorkload>(*layer, factory);
778 LstmQueueDescriptor queueDescriptor = workload->GetData();
779 CHECK(queueDescriptor.m_Parameters.m_ActivationFunc == 4);
780 CHECK(queueDescriptor.m_Parameters.m_ClippingThresCell == 0.0f);
781 CHECK(queueDescriptor.m_Parameters.m_ClippingThresProj == 0.0f);
782 CHECK(queueDescriptor.m_Inputs.size() == 3);
783 CHECK(queueDescriptor.m_Outputs.size() == 4);
784
785 CHECK((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == TensorInfo({ numUnits, inputSize },
786 DataType::Float32)));
787 CHECK((queueDescriptor.m_OutputGateBias->GetTensorInfo() == TensorInfo({ numUnits },
788 DataType::Float32)));
789 CHECK((queueDescriptor.m_CellBias->GetTensorInfo() == TensorInfo({ numUnits }, DataType::Float32)));
790 return workload;
791}
792
793template <typename QuantizedLstmWorkload>
794std::unique_ptr<QuantizedLstmWorkload> CreateQuantizedLstmWorkloadTest(armnn::IWorkloadFactory& factory,
795 armnn::Graph& graph)
796{
797 auto layer = graph.AddLayer<QuantizedLstmLayer>("quantizedLstmlayer");
798 unsigned int numBatches = 2;
799 unsigned int inputSize = 2;
800 unsigned int outputSize = 4;
801
802 // Scale/Offset for input/output, cellState In/Out, weights, bias
803 float inputOutputScale = 0.0078125f;
804 int32_t inputOutputOffset = 128;
805
806 float cellStateScale = 0.00048828125f;
807 int32_t cellStateOffset = 0;
808
809 float weightsScale = 0.00408021f;
810 int32_t weightsOffset = 100;
811
812 float biasScale = 3.1876640625e-05f;
813 int32_t biasOffset = 0;
814
815 // Weights and bias tensor and quantization info
816 armnn::TensorInfo inputWeightsInfo({outputSize, inputSize},
817 armnn::DataType::QAsymmU8,
818 weightsScale,
819 weightsOffset);
820
821 armnn::TensorInfo recurrentWeightsInfo({outputSize, outputSize},
822 armnn::DataType::QAsymmU8,
823 weightsScale,
824 weightsOffset);
825
826 armnn::TensorInfo biasInfo({outputSize},
827 armnn::DataType::Signed32,
828 biasScale,
829 biasOffset);
830
831 // Weights and bias
832 layer->m_QuantizedLstmParameters.m_InputToInputWeights =
833 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
834 layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
835 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
836 layer->m_QuantizedLstmParameters.m_InputToCellWeights =
837 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
838 layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
839 std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
840
841 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
842 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
843 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
844 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
845 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
846 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
847 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
848 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
849
850 layer->m_QuantizedLstmParameters.m_InputGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
851 layer->m_QuantizedLstmParameters.m_ForgetGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
852 layer->m_QuantizedLstmParameters.m_CellBias = std::make_unique<ScopedTensorHandle>(biasInfo);
853 layer->m_QuantizedLstmParameters.m_OutputGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
854
855 // Allocate weights and bias
856 layer->m_QuantizedLstmParameters.m_InputToInputWeights->Allocate();
857 layer->m_QuantizedLstmParameters.m_InputToForgetWeights->Allocate();
858 layer->m_QuantizedLstmParameters.m_InputToCellWeights->Allocate();
859 layer->m_QuantizedLstmParameters.m_InputToOutputWeights->Allocate();
860
861 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights->Allocate();
862 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights->Allocate();
863 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights->Allocate();
864 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights->Allocate();
865
866 layer->m_QuantizedLstmParameters.m_InputGateBias->Allocate();
867 layer->m_QuantizedLstmParameters.m_ForgetGateBias->Allocate();
868 layer->m_QuantizedLstmParameters.m_CellBias->Allocate();
869 layer->m_QuantizedLstmParameters.m_OutputGateBias->Allocate();
870
871 // Create input and output layers
872 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
873 Layer* const cellStateIn = graph.AddLayer<InputLayer>(1, "cellStateIn");
874 Layer* const outputStateIn = graph.AddLayer<InputLayer>(2, "outputStateIn");
875
876 Layer* const cellStateOut = graph.AddLayer<OutputLayer>(0, "cellStateOut");
877 Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
878
879 // Input/output tensor info and quantization info
880 armnn::TensorInfo inputInfo({numBatches , inputSize},
881 armnn::DataType::QAsymmU8,
882 inputOutputScale,
883 inputOutputOffset);
884
885 armnn::TensorInfo cellStateInfo({numBatches , outputSize},
886 armnn::DataType::QSymmS16,
887 cellStateScale,
888 cellStateOffset);
889
890 armnn::TensorInfo outputStateInfo({numBatches , outputSize},
891 armnn::DataType::QAsymmU8,
892 inputOutputScale,
893 inputOutputOffset);
894
895 // Connect input/output slots
896 Connect(input, layer, inputInfo, 0, 0);
897 Connect(cellStateIn, layer, cellStateInfo, 0, 1);
898 Connect(outputStateIn, layer, outputStateInfo, 0, 2);
899
900 Connect(layer, cellStateOut, cellStateInfo, 0, 0);
901 Connect(layer, outputStateOut, outputStateInfo, 1, 0);
902
903 CreateTensorHandles(graph, factory);
904
905 // Create workload and check layer support
906 auto workload = MakeAndCheckWorkload<QuantizedLstmWorkload>(*layer, factory);
907 QuantizedLstmQueueDescriptor queueDescriptor = workload->GetData();
908
909 // Validate input/output sizes
910 CHECK(queueDescriptor.m_Inputs.size() == 3);
911 CHECK(queueDescriptor.m_Outputs.size() == 2);
912
913 // Validate weight tensor info
914 CHECK((queueDescriptor.m_InputToInputWeights->GetTensorInfo() == inputWeightsInfo));
915 CHECK((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == inputWeightsInfo));
916 CHECK((queueDescriptor.m_InputToCellWeights->GetTensorInfo() == inputWeightsInfo));
917 CHECK((queueDescriptor.m_InputToOutputWeights->GetTensorInfo() == inputWeightsInfo));
918
919 CHECK((queueDescriptor.m_RecurrentToInputWeights->GetTensorInfo() == recurrentWeightsInfo));
920 CHECK((queueDescriptor.m_RecurrentToForgetWeights->GetTensorInfo() == recurrentWeightsInfo));
921 CHECK((queueDescriptor.m_RecurrentToCellWeights->GetTensorInfo() == recurrentWeightsInfo));
922 CHECK((queueDescriptor.m_RecurrentToOutputWeights->GetTensorInfo() == recurrentWeightsInfo));
923
924 CHECK((queueDescriptor.m_InputGateBias->GetTensorInfo() == biasInfo));
925 CHECK((queueDescriptor.m_ForgetGateBias->GetTensorInfo() == biasInfo));
926 CHECK((queueDescriptor.m_CellBias->GetTensorInfo() == biasInfo));
927 CHECK((queueDescriptor.m_OutputGateBias->GetTensorInfo() == biasInfo));
928
929 return workload;
930}
931
932template <typename QLstmWorkload>
933std::unique_ptr<QLstmWorkload> CreateQLstmWorkloadTest(armnn::IWorkloadFactory& factory,
934 armnn::Graph& graph)
935{
936 QLstmDescriptor layerDesc;
937 layerDesc.m_CifgEnabled = true;
938 layerDesc.m_PeepholeEnabled = false;
939 layerDesc.m_ProjectionEnabled = false;
940 layerDesc.m_LayerNormEnabled = true;
941
942 layerDesc.m_CellClip = 0.0f;
943 layerDesc.m_ProjectionClip = 0.0f;
944
945 layerDesc.m_HiddenStateZeroPoint = 0;
946 layerDesc.m_HiddenStateScale = 0.007f;
947
948 layerDesc.m_InputIntermediateScale = 0.007059f;
949 layerDesc.m_ForgetIntermediateScale = 0.007812f;
950 layerDesc.m_CellIntermediateScale = 0.007059f;
951 layerDesc.m_OutputIntermediateScale = 0.007812f;
952
953 QLstmLayer* const layer = graph.AddLayer<QLstmLayer>(layerDesc, "qLstm");
954
955 unsigned int numBatches = 2;
956 unsigned int inputSize = 4;
957 unsigned int numUnits = 4;
958 unsigned int outputSize = 4;
959
960 // Scale/Offset quantization info
961 float inputScale = 0.0078125f;
962 int32_t inputOffset = 0;
963
964 // if (!projectionEnabled) outputScale == hiddenStateScale
965 float outputScale = layerDesc.m_HiddenStateScale;
966 int32_t outputOffset = layerDesc.m_HiddenStateZeroPoint;
967
968 float cellStateScale = 3.05176e-05f;
969 int32_t cellStateOffset = 0;
970
971 float weightsScale = 0.00784314f;
972 int32_t weightsOffset = 0;
973
974 float layerNormScale = 3.05182e-05f;
975 int32_t layerNormOffset = 0;
976
977 float biasScale = layerNormScale / 1024;
978 int32_t biasOffset = 0;
979
980 // Weights and bias tensor and quantization info
981 armnn::TensorInfo inputWeightsInfo({outputSize, inputSize},
982 armnn::DataType::QSymmS8,
983 weightsScale,
984 weightsOffset);
985
986 armnn::TensorInfo recurrentWeightsInfo({outputSize, outputSize},
987 armnn::DataType::QSymmS8,
988 weightsScale,
989 weightsOffset);
990
991 armnn::TensorInfo biasInfo({outputSize}, armnn::DataType::Signed32, biasScale, biasOffset);
992
993 armnn::TensorInfo layerNormWeightsInfo({numUnits}, armnn::DataType::QSymmS16, layerNormScale, layerNormOffset);
994
995 // Create and allocate tensors
996 layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
997 layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
998 layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedTensorHandle>(inputWeightsInfo);
999
1000 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1001 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
1002 layer->m_BasicParameters.m_RecurrentToCellWeights =
1003 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
1004 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1005 std::make_unique<ScopedTensorHandle>(recurrentWeightsInfo);
1006
1007 layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
1008 layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedTensorHandle>(biasInfo);
1009 layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedTensorHandle>(biasInfo);
1010
1011 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1012 std::make_unique<ScopedTensorHandle>(layerNormWeightsInfo);
1013 layer->m_LayerNormParameters.m_CellLayerNormWeights =
1014 std::make_unique<ScopedTensorHandle>(layerNormWeightsInfo);
1015 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1016 std::make_unique<ScopedTensorHandle>(layerNormWeightsInfo);
1017
1018 layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
1019 layer->m_BasicParameters.m_InputToCellWeights->Allocate();
1020 layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
1021
1022 layer->m_BasicParameters.m_RecurrentToForgetWeights->Allocate();
1023 layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
1024 layer->m_BasicParameters.m_RecurrentToOutputWeights->Allocate();
1025
1026 layer->m_BasicParameters.m_ForgetGateBias->Allocate();
1027 layer->m_BasicParameters.m_CellBias->Allocate();
1028 layer->m_BasicParameters.m_OutputGateBias->Allocate();
1029
1030 layer->m_LayerNormParameters.m_ForgetLayerNormWeights->Allocate();
1031 layer->m_LayerNormParameters.m_CellLayerNormWeights->Allocate();
1032 layer->m_LayerNormParameters.m_OutputLayerNormWeights->Allocate();
1033
1034 // Input and output layers
1035 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1036 Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
1037 Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
1038
1039 Layer* const outputStateOut = graph.AddLayer<OutputLayer>(0, "outputStateOut");
1040 Layer* const cellStateOut = graph.AddLayer<OutputLayer>(1, "cellStateOut");
1041 Layer* const output = graph.AddLayer<OutputLayer>(2, "output");
1042
1043 // Input/Output tensor info
1044 armnn::TensorInfo inputInfo({numBatches , inputSize},
1045 armnn::DataType::QAsymmS8,
1046 inputScale,
1047 inputOffset);
1048
1049 armnn::TensorInfo cellStateInfo({numBatches , numUnits},
1050 armnn::DataType::QSymmS16,
1051 cellStateScale,
1052 cellStateOffset);
1053
1054 armnn::TensorInfo outputStateInfo({numBatches , outputSize},
1055 armnn::DataType::QAsymmS8,
1056 outputScale,
1057 outputOffset);
1058
1059 // Connect layers to slots
1060 Connect(input, layer, inputInfo, 0, 0);
1061 Connect(outputStateIn, layer, outputStateInfo, 0, 1);
1062 Connect(cellStateIn, layer, cellStateInfo, 0, 2);
1063
1064 Connect(layer, outputStateOut, outputStateInfo, 0, 0);
1065 Connect(layer, cellStateOut, cellStateInfo, 1, 0);
1066 Connect(layer, output, outputStateInfo, 2, 0);
1067
1068 CreateTensorHandles(graph, factory);
1069
1070 // Create and check workload
1071 auto workload = MakeAndCheckWorkload<QLstmWorkload>(*layer, factory);
1072 QLstmQueueDescriptor queueDescriptor = workload->GetData();
1073 CHECK(queueDescriptor.m_Parameters.m_CellClip == 0.0f);
1074 CHECK(queueDescriptor.m_Parameters.m_ProjectionClip == 0.0f);
1075 CHECK(queueDescriptor.m_Inputs.size() == 3);
1076 CHECK(queueDescriptor.m_Outputs.size() == 3);
1077
1078 CHECK((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == inputWeightsInfo));
1079 CHECK((queueDescriptor.m_InputToCellWeights->GetTensorInfo() == inputWeightsInfo));
1080 CHECK((queueDescriptor.m_InputToOutputWeights->GetTensorInfo() == inputWeightsInfo));
1081
1082 CHECK((queueDescriptor.m_RecurrentToForgetWeights->GetTensorInfo() == recurrentWeightsInfo));
1083 CHECK((queueDescriptor.m_RecurrentToCellWeights->GetTensorInfo() == recurrentWeightsInfo));
1084 CHECK((queueDescriptor.m_RecurrentToOutputWeights->GetTensorInfo() == recurrentWeightsInfo));
1085
1086 CHECK((queueDescriptor.m_ForgetGateBias->GetTensorInfo() == biasInfo));
1087 CHECK((queueDescriptor.m_CellBias->GetTensorInfo() == biasInfo));
1088 CHECK((queueDescriptor.m_OutputGateBias->GetTensorInfo() == biasInfo));
1089
1090 return workload;
1091}
1092
Keith Davisb4dd5cc2022-04-07 11:32:00 +01001093template<typename Convolution2dWorkload, armnn::DataType DataType>
Sadik Armagana097d2a2021-11-24 15:47:28 +00001094std::unique_ptr<Convolution2dWorkload> CreateDirectConvolution2dWorkloadTest(armnn::IWorkloadFactory& factory,
Keith Davisb4dd5cc2022-04-07 11:32:00 +01001095 armnn::Graph& graph)
Sadik Armagana097d2a2021-11-24 15:47:28 +00001096{
1097 // Creates the layer we're testing.
1098 Convolution2dDescriptor layerDesc;
1099 layerDesc.m_PadLeft = 1;
1100 layerDesc.m_PadRight = 1;
1101 layerDesc.m_PadTop = 1;
1102 layerDesc.m_PadBottom = 1;
1103 layerDesc.m_StrideX = 1;
1104 layerDesc.m_StrideY = 1;
1105 layerDesc.m_BiasEnabled = true;
1106
1107 Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
1108
1109 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1110 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1111
Keith Davisb4dd5cc2022-04-07 11:32:00 +01001112 TensorShape biasShape = TensorShape{ 2 };
1113 TensorShape weightShape = TensorShape{ 2, 3, 3, 3 };
1114 armnn::TensorInfo weightsTensorInfo(weightShape, DataType, inputsQScale);
1115 weightsTensorInfo.SetConstant();
1116 armnn::TensorInfo biasTensorInfo(biasShape, GetBiasDataType(DataType), inputsQScale);
1117 biasTensorInfo.SetConstant();
1118
Sadik Armagana097d2a2021-11-24 15:47:28 +00001119 // Creates extra layers.
1120 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
Keith Davisb4dd5cc2022-04-07 11:32:00 +01001121 auto const weights = graph.AddLayer<ConstantLayer>("weights");
1122 auto const bias = graph.AddLayer<ConstantLayer>("bias");
Sadik Armagana097d2a2021-11-24 15:47:28 +00001123 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1124
Keith Davisb4dd5cc2022-04-07 11:32:00 +01001125 weights->m_LayerOutput = std::make_unique<ScopedTensorHandle>(weightsTensorInfo);
1126 weights->m_LayerOutput->Allocate();
1127 bias->m_LayerOutput = std::make_unique<ScopedTensorHandle>(biasTensorInfo);
1128 bias->m_LayerOutput->Allocate();
1129
Sadik Armagana097d2a2021-11-24 15:47:28 +00001130 // Connects up.
1131 Connect(input, layer, TensorInfo({2, 3, 6, 6}, DataType, inputsQScale));
Keith Davisb4dd5cc2022-04-07 11:32:00 +01001132 Connect(weights, layer, weightsTensorInfo, 0, 1);
1133 Connect(bias, layer, biasTensorInfo, 0, 2);
Sadik Armagana097d2a2021-11-24 15:47:28 +00001134 Connect(layer, output, TensorInfo({2, 2, 6, 6}, DataType, outputQScale));
1135 CreateTensorHandles(graph, factory);
1136
1137 // Makes the workload and checks it.
1138 auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, factory);
1139
1140 Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
1141 CHECK(queueDescriptor.m_Parameters.m_StrideX == 1);
1142 CHECK(queueDescriptor.m_Parameters.m_StrideY == 1);
1143 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 1);
1144 CHECK(queueDescriptor.m_Parameters.m_PadRight == 1);
1145 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
1146 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
1147 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1148
Keith Davisb4dd5cc2022-04-07 11:32:00 +01001149 CHECK(queueDescriptor.m_Inputs.size() == 3);
Sadik Armagana097d2a2021-11-24 15:47:28 +00001150 CHECK(queueDescriptor.m_Outputs.size() == 1);
Sadik Armagana097d2a2021-11-24 15:47:28 +00001151
1152 // Returns so we can do extra, backend-specific tests.
1153 return workload;
1154}
1155
1156template <typename DepthwiseConvolution2dFloat32Workload, armnn::DataType DataType>
1157std::unique_ptr<DepthwiseConvolution2dFloat32Workload> CreateDepthwiseConvolution2dWorkloadTest(
1158 armnn::IWorkloadFactory& factory, armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
1159{
1160 // Creates the layer we're testing.
1161 DepthwiseConvolution2dDescriptor layerDesc;
1162 layerDesc.m_PadLeft = 1;
1163 layerDesc.m_PadRight = 2;
1164 layerDesc.m_PadTop = 1;
1165 layerDesc.m_PadBottom = 2;
1166 layerDesc.m_StrideX = 1;
1167 layerDesc.m_StrideY = 1;
1168 layerDesc.m_BiasEnabled = false;
1169 layerDesc.m_DataLayout = dataLayout;
1170
Cathal Corbett06902652022-04-14 17:55:11 +01001171 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1172 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1173
1174 TensorShape weightShape({1, 4, 4, 2});
1175 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1176 TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
1177 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1178 TensorShape{ 2, 2, 5, 5 } : TensorShape{ 2, 5, 5, 2 };
1179
Sadik Armagana097d2a2021-11-24 15:47:28 +00001180 DepthwiseConvolution2dLayer* const layer = graph.AddLayer<DepthwiseConvolution2dLayer>(layerDesc, "layer");
1181
Sadik Armagana097d2a2021-11-24 15:47:28 +00001182
1183 // Creates extra layers.
1184 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
Cathal Corbett06902652022-04-14 17:55:11 +01001185 Layer* const weights = graph.AddLayer<ConstantLayer>("weights");
Sadik Armagana097d2a2021-11-24 15:47:28 +00001186 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1187
Sadik Armagana097d2a2021-11-24 15:47:28 +00001188 // Connects up.
Cathal Corbett06902652022-04-14 17:55:11 +01001189 Connect(input, layer, TensorInfo(inputShape, DataType, inputsQScale));
1190 Connect(weights, layer, TensorInfo(weightShape, DataType, inputsQScale, 0.0f, true), 0, 1);
1191 Connect(layer, output, TensorInfo(outputShape, DataType, outputQScale));
Sadik Armagana097d2a2021-11-24 15:47:28 +00001192 CreateTensorHandles(graph, factory);
1193
1194 // Makes the workload and checks it.
1195 auto workload = MakeAndCheckWorkload<DepthwiseConvolution2dFloat32Workload>(*layer, factory);
1196
1197 DepthwiseConvolution2dQueueDescriptor queueDescriptor = workload->GetData();
1198 CHECK(queueDescriptor.m_Parameters.m_StrideX == 1);
1199 CHECK(queueDescriptor.m_Parameters.m_StrideY == 1);
1200 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 1);
1201 CHECK(queueDescriptor.m_Parameters.m_PadRight == 2);
1202 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
1203 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 2);
1204 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == false);
1205 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1206
Cathal Corbett06902652022-04-14 17:55:11 +01001207 CHECK(queueDescriptor.m_Inputs.size() == 2);
Sadik Armagana097d2a2021-11-24 15:47:28 +00001208 CHECK(queueDescriptor.m_Outputs.size() == 1);
Sadik Armagana097d2a2021-11-24 15:47:28 +00001209
1210 // Returns so we can do extra, backend-specific tests.
1211 return workload;
1212}
1213
1214template <typename FullyConnectedWorkload, armnn::DataType DataType>
1215std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWorkloadTest(armnn::IWorkloadFactory& factory,
1216 armnn::Graph& graph)
1217{
1218 // Creates the layer we're testing.
1219 FullyConnectedDescriptor layerDesc;
1220 layerDesc.m_BiasEnabled = false;
1221 layerDesc.m_TransposeWeightMatrix = true;
1222
1223 FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1224
1225 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1226 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1227
Sadik Armagana097d2a2021-11-24 15:47:28 +00001228 armnn::TensorInfo weightsTensorInfo({7, 20}, DataType, inputsQScale);
1229 weightsTensorInfo.SetConstant();
1230
1231 // Creates extra layers.
1232 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1233 auto const weights = graph.AddLayer<ConstantLayer>("weights");
1234 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1235
1236 weights->m_LayerOutput = std::make_unique<ScopedTensorHandle>(weightsTensorInfo);
1237 weights->m_LayerOutput->Allocate();
1238
1239 // Connects up.
1240 Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0);
1241 Connect(weights, layer, weightsTensorInfo, 0, 1);
1242 Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1243 CreateTensorHandles(graph, factory);
1244
1245 // Makes the workload and checks it.
1246 auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1247
1248 FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1249 CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1250
1251 CHECK(queueDescriptor.m_Inputs.size() == 2);
1252 CHECK(queueDescriptor.m_Outputs.size() == 1);
1253
1254 // Returns so we can do extra, backend-specific tests.
1255 return workload;
1256}
1257
1258template <typename FullyConnectedWorkload, armnn::DataType DataType>
1259std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWithBlobWorkloadTest
1260 (armnn::IWorkloadFactory& factory,
1261 armnn::Graph& graph)
1262{
1263 // Creates the layer we're testing.
1264 FullyConnectedDescriptor layerDesc;
1265 layerDesc.m_BiasEnabled = true;
1266 layerDesc.m_TransposeWeightMatrix = true;
1267
1268 FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1269
1270 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1271 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1272
Sadik Armagana097d2a2021-11-24 15:47:28 +00001273 armnn::TensorInfo weightsTensorInfo({7, 20}, DataType, inputsQScale);
1274 armnn::TensorInfo biasesTensorInfo({7}, GetBiasDataType(DataType), inputsQScale);
1275 weightsTensorInfo.SetConstant();
1276 biasesTensorInfo.SetConstant();
1277
1278 auto activationDesc = std::make_shared<ActivationDescriptor>();
1279 activationDesc->m_A = 10.0f;
1280 activationDesc->m_B = 5.0f;
1281 activationDesc->m_Function = armnn::ActivationFunction::BoundedReLu;
1282
1283 layer->SetAdditionalInfoForObject(activationDesc);
1284
1285 // Check that the additional information can be queried from the layer
1286 std::shared_ptr<ActivationDescriptor> activationDescPtr = layer->GetAdditionalInformation<ActivationDescriptor>();
1287 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_A) == 10.0f);
1288 ARMNN_ASSERT(static_cast<float>(activationDescPtr->m_B) == 5.0f);
1289 ARMNN_ASSERT(static_cast<ActivationFunction>(activationDescPtr->m_Function) ==
1290 armnn::ActivationFunction::BoundedReLu);
1291
1292 // Creates extra layers.
1293 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1294 auto const weights = graph.AddLayer<ConstantLayer>("weights");
1295 auto const biases = graph.AddLayer<ConstantLayer>("biases");
1296 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1297
1298 weights->m_LayerOutput = std::make_unique<ScopedTensorHandle>(weightsTensorInfo);
1299 weights->m_LayerOutput->Allocate();
1300 biases->m_LayerOutput = std::make_unique<ScopedTensorHandle>(biasesTensorInfo);
1301 biases->m_LayerOutput->Allocate();
1302
1303 // Connects up.
1304 Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0);
1305 Connect(weights, layer, weightsTensorInfo, 0, 1);
1306 Connect(biases, layer, biasesTensorInfo, 0, 2);
1307 Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1308 CreateTensorHandles(graph, factory);
1309
1310 // Makes the workload and checks it.
1311 auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1312
1313 FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1314
1315 const ActivationDescriptor* queueDescBlobPtr = queueDescriptor.GetAdditionalInformation<ActivationDescriptor>();
1316 IgnoreUnused(queueDescBlobPtr);
1317
1318 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_A) == 10.0f);
1319 ARMNN_ASSERT(static_cast<float>(queueDescBlobPtr->m_B) == 5.0f);
1320 ARMNN_ASSERT(
1321 static_cast<ActivationFunction>(queueDescBlobPtr->m_Function) == armnn::ActivationFunction::BoundedReLu
1322 );
1323
1324 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1325 CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1326 CHECK(queueDescriptor.m_Inputs.size() == 3);
1327 CHECK(queueDescriptor.m_Outputs.size() == 1);
1328
1329 // Returns so we can do extra, backend-specific tests.
1330 return workload;
1331}
1332
1333template <typename FullyConnectedWorkload, armnn::DataType DataType>
1334std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWorkloadWeightsBiasesAsInputsTest
1335 (armnn::IWorkloadFactory& factory,
1336 armnn::Graph& graph)
1337{
1338 // Creates the layer we're testing.
1339 FullyConnectedDescriptor layerDesc;
1340 layerDesc.m_BiasEnabled = true;
1341 layerDesc.m_TransposeWeightMatrix = true;
1342 layerDesc.m_ConstantWeights = false;
1343
1344 FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
1345
1346 float inputsQScale = DataType == armnn::DataType::QAsymmU8 ? 1.0f : 0.0;
1347 float outputQScale = DataType == armnn::DataType::QAsymmU8 ? 2.0f : 0.0;
1348
1349 // Creates extra layers with weights and biases as input layers.
1350 Layer* const input = graph.AddLayer<InputLayer>(1, "input");
1351 Layer* const weights = graph.AddLayer<InputLayer>(2, "weights");
1352 Layer* const biases = graph.AddLayer<InputLayer>(3, "biases");
1353 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1354
1355 // Connects up.
1356 Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale), 0, 0);
1357 Connect(weights, layer, TensorInfo({7, 20}, DataType, inputsQScale), 0, 1);
1358 Connect(biases, layer, TensorInfo({7}, GetBiasDataType(DataType), inputsQScale), 0, 2);
1359 Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
1360 CreateTensorHandles(graph, factory);
1361
1362 // Makes the workload and checks it.
1363 auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, factory);
1364
1365 FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
1366
1367 CHECK(queueDescriptor.m_Parameters.m_BiasEnabled == true);
1368 CHECK(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
1369 CHECK(queueDescriptor.m_Parameters.m_ConstantWeights == false);
1370 CHECK(queueDescriptor.m_Inputs.size() == 3);
1371 CHECK(queueDescriptor.m_Outputs.size() == 1);
1372
1373 // Returns so we can do extra, backend-specific tests.
1374 return workload;
1375}
1376
1377
1378template <typename NormalizationWorkload, armnn::DataType DataType>
1379std::unique_ptr<NormalizationWorkload> CreateNormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
1380 armnn::Graph& graph,
1381 DataLayout dataLayout = DataLayout::NCHW)
1382{
1383 // Creates the layer we're testing.
1384 NormalizationDescriptor layerDesc;
1385 layerDesc.m_NormChannelType = NormalizationAlgorithmChannel::Across;
1386 layerDesc.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
1387 layerDesc.m_NormSize = 3;
1388 layerDesc.m_Alpha = 0.5f;
1389 layerDesc.m_Beta = -1.0f;
1390 layerDesc.m_K = 0.2f;
1391 layerDesc.m_DataLayout = dataLayout;
1392
1393 NormalizationLayer* layer = graph.AddLayer<NormalizationLayer>(layerDesc, "layer");
1394
1395 // Creates extra layers.
1396 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1397 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1398
1399 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1400 TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
1401 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1402 TensorShape{ 3, 5, 5, 1 } : TensorShape{ 3, 1, 5, 5 };
1403
1404 // Connects up.
1405 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1406 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1407 Connect(input, layer, inputTensorInfo);
1408 Connect(layer, output, outputTensorInfo);
1409 CreateTensorHandles(graph, factory);
1410
1411 // Makes the workload and checks it.
1412 auto workload = MakeAndCheckWorkload<NormalizationWorkload>(*layer, factory);
1413
1414 NormalizationQueueDescriptor queueDescriptor = workload->GetData();
1415 CHECK((queueDescriptor.m_Parameters.m_NormChannelType == NormalizationAlgorithmChannel::Across));
1416 CHECK((queueDescriptor.m_Parameters.m_NormMethodType == NormalizationAlgorithmMethod::LocalBrightness));
1417 CHECK(queueDescriptor.m_Parameters.m_NormSize == 3);
1418 CHECK(queueDescriptor.m_Parameters.m_Alpha == 0.5f);
1419 CHECK(queueDescriptor.m_Parameters.m_Beta == -1.0f);
1420 CHECK(queueDescriptor.m_Parameters.m_K == 0.2f);
1421 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1422
1423 CHECK(queueDescriptor.m_Inputs.size() == 1);
1424 CHECK(queueDescriptor.m_Outputs.size() == 1);
1425
1426 // Returns so we can do extra, backend-specific tests.
1427 return workload;
1428}
1429
1430template <typename Pooling2dWorkload, armnn::DataType DataType>
1431std::unique_ptr<Pooling2dWorkload> CreatePooling2dWorkloadTest(armnn::IWorkloadFactory& factory,
1432 armnn::Graph& graph,
1433 DataLayout dataLayout = DataLayout::NCHW)
1434{
1435 // Creates the layer we're testing.
1436 Pooling2dDescriptor layerDesc;
1437 layerDesc.m_PoolType = PoolingAlgorithm::Average;
1438 layerDesc.m_PoolWidth = 3;
1439 layerDesc.m_PoolHeight = 3;
1440 layerDesc.m_PadLeft = 2;
1441 layerDesc.m_PadRight = 2;
1442 layerDesc.m_PadTop = 1;
1443 layerDesc.m_PadBottom = 1;
1444 layerDesc.m_StrideX = 2;
1445 layerDesc.m_StrideY = 3;
1446 layerDesc.m_OutputShapeRounding = OutputShapeRounding::Floor;
1447 layerDesc.m_DataLayout = dataLayout;
1448
1449 Pooling2dLayer* const layer = graph.AddLayer<Pooling2dLayer>(layerDesc, "layer");
1450
1451 // Create extra layers
1452 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1453 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1454
1455 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 5, 5} : TensorShape{3, 5, 5, 2};
1456 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 2, 4} : TensorShape{3, 2, 4, 2};
1457
1458 // Connect up
1459 Connect(input, layer, TensorInfo(inputShape, DataType));
1460 Connect(layer, output, TensorInfo(outputShape, DataType));
1461 CreateTensorHandles(graph, factory);
1462
1463 // Make the workload and checks it
1464 auto workload = MakeAndCheckWorkload<Pooling2dWorkload>(*layer, factory);
1465
1466 Pooling2dQueueDescriptor queueDescriptor = workload->GetData();
1467 CHECK((queueDescriptor.m_Parameters.m_PoolType == PoolingAlgorithm::Average));
1468 CHECK((queueDescriptor.m_Parameters.m_OutputShapeRounding == OutputShapeRounding::Floor));
1469 CHECK(queueDescriptor.m_Parameters.m_PoolWidth == 3);
1470 CHECK(queueDescriptor.m_Parameters.m_PoolHeight == 3);
1471 CHECK(queueDescriptor.m_Parameters.m_StrideX == 2);
1472 CHECK(queueDescriptor.m_Parameters.m_StrideY == 3);
1473 CHECK(queueDescriptor.m_Parameters.m_PadLeft == 2);
1474 CHECK(queueDescriptor.m_Parameters.m_PadRight == 2);
1475 CHECK(queueDescriptor.m_Parameters.m_PadTop == 1);
1476 CHECK(queueDescriptor.m_Parameters.m_PadBottom == 1);
1477 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1478
1479 CHECK(queueDescriptor.m_Inputs.size() == 1);
1480 CHECK(queueDescriptor.m_Outputs.size() == 1);
1481
1482 // Return so we can do extra, backend-specific tests
1483 return workload;
1484}
1485
1486template <typename SoftmaxWorkload, armnn::DataType DataType>
1487std::unique_ptr<SoftmaxWorkload> CreateSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
1488 armnn::Graph& graph)
1489{
1490 // Create the layer we're testing.
1491 SoftmaxDescriptor softmaxDescriptor;
1492 // Set Axis to -1 if CL or Neon until further Axes are supported.
1493 if (factory.GetBackendId() == armnn::Compute::CpuAcc || factory.GetBackendId() == armnn::Compute::GpuAcc)
1494 {
1495 softmaxDescriptor.m_Axis = -1;
1496 }
1497
1498 Layer* const layer = graph.AddLayer<SoftmaxLayer>(softmaxDescriptor, "layer");
1499 // Create extra layers.
1500 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1501 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1502
1503 // Connect up
1504 armnn::TensorInfo tensorInfo({4, 1}, DataType);
1505 if (DataType == armnn::DataType::QAsymmU8)
1506 {
1507 tensorInfo.SetQuantizationOffset(0);
1508 tensorInfo.SetQuantizationScale(1.f / 256);
1509 }
1510 else if (DataType == armnn::DataType::QAsymmS8)
1511 {
1512 tensorInfo.SetQuantizationOffset(-128);
1513 tensorInfo.SetQuantizationScale(1.f / 256);
1514 }
1515
1516 Connect(input, layer, tensorInfo);
1517 Connect(layer, output, tensorInfo);
1518 CreateTensorHandles(graph, factory);
1519
1520 // Make the workload and checks it.
1521 auto workload = MakeAndCheckWorkload<SoftmaxWorkload>(*layer, factory);
1522
1523 SoftmaxQueueDescriptor queueDescriptor = workload->GetData();
1524 CHECK(queueDescriptor.m_Inputs.size() == 1);
1525 CHECK(queueDescriptor.m_Outputs.size() == 1);
1526
1527 // Return so we can do extra, backend-specific tests.
1528 return workload;
1529}
1530
1531template<typename SplitterWorkload, armnn::DataType DataType>
1532std::unique_ptr<SplitterWorkload>
1533 CreateSplitterWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1534{
1535 // Create the layer we're testing.
1536 // NOTE: need three dimensions channels, height/y, width/x because the Compute
1537 // library restricts subtensors to have the same x and y dimensions as
1538 // their parent tensors, and therefore the origin on the x and y dimension
1539 // has to be zero for any view. So we need a third dimension to split...
1540 // NOTE: arguments are: number of views, number of dimensions.
1541 ViewsDescriptor layerDesc(3, 3);
1542 // NOTE: arguments are: view, dimension, value.
1543 layerDesc.SetViewOriginCoord(0, 0, 0);
1544 layerDesc.SetViewOriginCoord(1, 0, 1);
1545 layerDesc.SetViewOriginCoord(2, 0, 3);
1546
1547 Layer* const layer = graph.AddLayer<SplitterLayer>(layerDesc, "layer");
1548
1549 // Adds extra layers.
1550 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1551 Layer* const output0 = graph.AddLayer<OutputLayer>(0, "output0");
1552 Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
1553 Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
1554
1555 // Connects up.
1556 armnn::TensorInfo tensorInfo({5, 7, 7}, DataType);
1557 Connect(input, layer, tensorInfo);
1558
1559 armnn::TensorInfo output0Info({1, 7, 7}, DataType);
1560 armnn::TensorInfo output1Info({2, 7, 7}, DataType);
1561 armnn::TensorInfo output2Info({2, 7, 7}, DataType);
1562
1563 Connect(layer, output0, output0Info, 0, 0);
1564 Connect(layer, output1, output1Info, 1, 0);
1565 Connect(layer, output2, output2Info, 2, 0);
1566
1567 CreateTensorHandles(graph, factory);
1568
1569 // Makes the workload and checks it.
1570 auto workload = MakeAndCheckWorkload<SplitterWorkload>(*layer, factory);
1571
1572 SplitterQueueDescriptor queueDescriptor = workload->GetData();
1573 CHECK(queueDescriptor.m_Inputs.size() == 1);
1574 CHECK(queueDescriptor.m_Outputs.size() == 3);
1575 CHECK(queueDescriptor.m_ViewOrigins.size() == 3);
1576
1577 CHECK(queueDescriptor.m_ViewOrigins[0].m_Origin[0] == 0);
1578 CHECK(queueDescriptor.m_ViewOrigins[1].m_Origin[0] == 1);
1579 CHECK(queueDescriptor.m_ViewOrigins[2].m_Origin[0] == 3);
1580 CHECK(queueDescriptor.m_ViewOrigins[0].m_Origin[1] == 0);
1581 CHECK(queueDescriptor.m_ViewOrigins[1].m_Origin[1] == 0);
1582 CHECK(queueDescriptor.m_ViewOrigins[2].m_Origin[1] == 0);
1583 CHECK(queueDescriptor.m_ViewOrigins[0].m_Origin[2] == 0);
1584 CHECK(queueDescriptor.m_ViewOrigins[1].m_Origin[2] == 0);
1585 CHECK(queueDescriptor.m_ViewOrigins[2].m_Origin[2] == 0);
1586
1587 // Returns so we can do extra, backend-specific tests.
1588 return workload;
1589}
1590
1591/// This function constructs a graph with both a splitter and a concat, and returns a pair of the workloads.
1592template<typename SplitterWorkload, typename ConcatWorkload, armnn::DataType DataType>
1593std::pair<std::unique_ptr<SplitterWorkload>, std::unique_ptr<ConcatWorkload>>
1594 CreateSplitterConcatWorkloadTest(armnn::IWorkloadFactory &factory, armnn::Graph &graph)
1595{
1596 armnn::TensorInfo inputTensorInfo({ 1, 2, 100, 10 }, DataType);
1597
1598 armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 10 }, DataType);
1599 armnn::TensorInfo splitTensorInfo2({ 1, 1, 100, 10 }, DataType);
1600
1601 //Constructs the graph.
1602 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1603
1604 armnn::ViewsDescriptor splitterViews(2);
1605 splitterViews.SetViewOriginCoord(0, 0, 0);
1606 splitterViews.SetViewOriginCoord(0, 1, 0);
1607 splitterViews.SetViewOriginCoord(0, 2, 0);
1608 splitterViews.SetViewOriginCoord(0, 3, 0);
1609
1610 splitterViews.SetViewOriginCoord(1, 0, 0);
1611 splitterViews.SetViewOriginCoord(1, 1, 1);
1612 splitterViews.SetViewOriginCoord(1, 2, 0);
1613 splitterViews.SetViewOriginCoord(1, 3, 0);
1614
1615 // create splitter layer
1616 Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
1617 CHECK(splitter);
1618
1619 armnn::OriginsDescriptor concatViews(2);
1620 concatViews.SetViewOriginCoord(0, 0, 0);
1621 concatViews.SetViewOriginCoord(0, 1, 1);
1622 concatViews.SetViewOriginCoord(0, 2, 0);
1623 concatViews.SetViewOriginCoord(0, 3, 0);
1624
1625 concatViews.SetViewOriginCoord(1, 0, 0);
1626 concatViews.SetViewOriginCoord(1, 1, 0);
1627 concatViews.SetViewOriginCoord(1, 2, 0);
1628 concatViews.SetViewOriginCoord(1, 3, 0);
1629
1630 // create concat layer
1631 Layer* const concat = graph.AddLayer<ConcatLayer>(concatViews, "concat");
1632 CHECK(concat);
1633
1634 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1635
1636 // Adds connections.
1637 // connect input to splitter
1638 Connect(input, splitter, inputTensorInfo, 0, 0);
1639 // connect splitter[0] to concat[1]
1640 Connect(splitter, concat, splitTensorInfo1, 0, 1); // The splitter & concat are connected up.
1641 // connect splitter[1] to concat[0]
1642 Connect(splitter, concat, splitTensorInfo2, 1, 0); // So that the outputs are flipped round.
1643 // connect concat to output
1644 Connect(concat, output, inputTensorInfo, 0, 0);
1645
1646 // created tensor handles
1647 CreateTensorHandles(graph, factory);
1648
1649 // created splitter workload
1650 auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
1651 CHECK(workloadSplitter);
1652 // created concat workload
1653 auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
1654 CHECK(workloadConcat);
1655
1656 return {std::move(workloadSplitter), std::move(workloadConcat)};
1657}
1658
1659
1660/// This function constructs a graph with a splitter with two outputs. Each of the outputs is then
1661/// connected to two different activation layers
1662template<typename SplitterWorkload, typename ActivationWorkload, armnn::DataType DataType>
1663void CreateSplitterMultipleInputsOneOutputWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph,
1664 std::unique_ptr<SplitterWorkload>& wlSplitter,
1665 std::unique_ptr<ActivationWorkload>& wlActiv0_0,
1666 std::unique_ptr<ActivationWorkload>& wlActiv0_1,
1667 std::unique_ptr<ActivationWorkload>& wlActiv1_0,
1668 std::unique_ptr<ActivationWorkload>& wlActiv1_1)
1669{
1670 armnn::TensorInfo inputTensorInfo ({ 1, 3, 100, 50 }, DataType);
1671 armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 50 }, DataType);
1672 armnn::TensorInfo splitTensorInfo2({ 1, 2, 100, 50 }, DataType);
1673
1674 //Constructs the graph.
1675 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1676
1677 armnn::ViewsDescriptor splitterViews(2);
1678
1679 splitterViews.SetViewOriginCoord(0, 0, 0);
1680 splitterViews.SetViewOriginCoord(0, 1, 0);
1681 splitterViews.SetViewOriginCoord(0, 2, 0);
1682 splitterViews.SetViewOriginCoord(0, 3, 0);
1683
1684 splitterViews.SetViewOriginCoord(1, 0, 0);
1685 splitterViews.SetViewOriginCoord(1, 1, 1);
1686 splitterViews.SetViewOriginCoord(1, 2, 0);
1687 splitterViews.SetViewOriginCoord(1, 3, 0);
1688
1689 Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
1690
1691 armnn::ActivationDescriptor activationDesc;
1692
1693 Layer* const activ0_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_0");
1694 Layer* const activ0_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_1");
1695 Layer* const activ1_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_0");
1696 Layer* const activ1_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_1");
1697
1698 Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
1699 Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
1700 Layer* const output3 = graph.AddLayer<OutputLayer>(3, "output3");
1701 Layer* const output4 = graph.AddLayer<OutputLayer>(4, "output4");
1702
1703 // Adds connections.
1704 Connect(input, splitter, inputTensorInfo, 0, 0);
1705 Connect(splitter, activ0_0, splitTensorInfo1, 0, 0);
1706 Connect(splitter, activ0_1, splitTensorInfo1, 0, 0);
1707
1708 Connect(splitter, activ1_0, splitTensorInfo2, 1, 0);
1709 Connect(splitter, activ1_1, splitTensorInfo2, 1, 0);
1710
1711 Connect(activ0_0, output1, splitTensorInfo1, 0, 0);
1712 Connect(activ0_1, output2, splitTensorInfo1, 0, 0);
1713 Connect(activ1_0, output3, splitTensorInfo2, 0, 0);
1714 Connect(activ1_1, output4, splitTensorInfo2, 0, 0);
1715
1716 CreateTensorHandles(graph, factory);
1717
1718 auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, factory);
1719 auto workloadActiv0_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_0, factory);
1720 auto workloadActiv0_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_1, factory);
1721 auto workloadActiv1_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_0, factory);
1722 auto workloadActiv1_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_1, factory);
1723
1724 wlSplitter = std::move(workloadSplitter);
1725 wlActiv0_0 = std::move(workloadActiv0_0);
1726 wlActiv0_1 = std::move(workloadActiv0_1);
1727 wlActiv1_0 = std::move(workloadActiv1_0);
1728 wlActiv1_1 = std::move(workloadActiv1_1);
1729}
1730
1731template <typename ResizeWorkload, armnn::DataType DataType>
1732std::unique_ptr<ResizeWorkload> CreateResizeBilinearWorkloadTest(armnn::IWorkloadFactory& factory,
1733 armnn::Graph& graph,
1734 DataLayout dataLayout = DataLayout::NCHW)
1735{
1736 TensorShape inputShape;
1737 TensorShape outputShape;
1738
1739 switch (dataLayout) {
1740 case DataLayout::NHWC:
1741 inputShape = { 2, 4, 4, 3 };
1742 outputShape = { 2, 2, 2, 3 };
1743 break;
1744 case DataLayout::NCHW:
1745 default:
1746 inputShape = { 2, 3, 4, 4 };
1747 outputShape = { 2, 3, 2, 2 };
1748 }
1749
1750 // Creates the layer we're testing.
1751 ResizeDescriptor resizeDesc;
1752 armnnUtils::DataLayoutIndexed dimensionIndices = dataLayout;
1753 resizeDesc.m_Method = ResizeMethod::Bilinear;
1754 resizeDesc.m_TargetWidth = outputShape[dimensionIndices.GetWidthIndex()];
1755 resizeDesc.m_TargetHeight = outputShape[dimensionIndices.GetHeightIndex()];
1756 resizeDesc.m_DataLayout = dataLayout;
1757 Layer* const layer = graph.AddLayer<ResizeLayer>(resizeDesc, "resize");
1758
1759 // Creates extra layers.
1760 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1761 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1762
1763 // Connects up.
1764 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1765 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1766 Connect(input, layer, inputTensorInfo);
1767 Connect(layer, output, outputTensorInfo);
1768 CreateTensorHandles(graph, factory);
1769
1770 // Makes the workload and checks it.
1771 auto workload = MakeAndCheckWorkload<ResizeWorkload>(*layer, factory);
1772
1773 auto queueDescriptor = workload->GetData();
1774 CHECK(queueDescriptor.m_Inputs.size() == 1);
1775 CHECK(queueDescriptor.m_Outputs.size() == 1);
1776 CHECK(queueDescriptor.m_Parameters.m_DataLayout == dataLayout);
1777
1778 // Returns so we can do extra, backend-specific tests.
1779 return workload;
1780}
1781
1782template <typename BatchToSpaceNdWorkload, armnn::DataType DataType>
1783std::unique_ptr<BatchToSpaceNdWorkload> CreateBatchToSpaceNdWorkloadTest(armnn::IWorkloadFactory& factory,
1784 armnn::Graph& graph)
1785{
1786 BatchToSpaceNdDescriptor desc;
1787 Layer* const layer = graph.AddLayer<BatchToSpaceNdLayer>(desc, "batchToSpace");
1788
1789 // Creates extra layers.
1790 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1791 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1792
1793 // Connects up.
1794 armnn::TensorInfo tensorInfo({1, 1, 1, 1}, DataType);
1795
1796 Connect(input, layer, tensorInfo);
1797 Connect(layer, output, tensorInfo);
1798
1799 CreateTensorHandles(graph, factory);
1800
1801 // Makes the workload and checks it.
1802 auto workload = MakeAndCheckWorkload<BatchToSpaceNdWorkload>(*layer, factory);
1803
1804 BatchToSpaceNdQueueDescriptor queueDescriptor = workload->GetData();
1805 CHECK(queueDescriptor.m_Inputs.size() == 1);
1806 CHECK(queueDescriptor.m_Outputs.size() == 1);
1807
1808 return workload;
1809}
1810
1811template <typename LogSoftmaxWorkload, armnn::DataType DataType>
1812std::unique_ptr<LogSoftmaxWorkload> CreateLogSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
1813 armnn::Graph& graph)
1814{
1815 // Create the layer we're testing.
1816 LogSoftmaxDescriptor logSoftmaxDescriptor;
1817 // Set Axis to -1 if CL or Neon until further Axes are supported.
1818 if (factory.GetBackendId() == armnn::Compute::CpuAcc || factory.GetBackendId() == armnn::Compute::GpuAcc)
1819 {
1820 logSoftmaxDescriptor.m_Axis = -1;
1821 }
1822
1823 Layer* const layer = graph.AddLayer<LogSoftmaxLayer>(logSoftmaxDescriptor, "layer");
1824 // Create extra layers.
1825 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1826 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1827
1828 // Connect up
1829 armnn::TensorInfo tensorInfo({4, 1}, DataType);
1830
1831 Connect(input, layer, tensorInfo);
1832 Connect(layer, output, tensorInfo);
1833 CreateTensorHandles(graph, factory);
1834
1835 // Make the workload and checks it.
1836 auto workload = MakeAndCheckWorkload<LogSoftmaxWorkload>(*layer, factory);
1837
1838 LogSoftmaxQueueDescriptor queueDescriptor = workload->GetData();
1839 CHECK(queueDescriptor.m_Inputs.size() == 1);
1840 CHECK(queueDescriptor.m_Outputs.size() == 1);
1841
1842 // Return so we can do extra, backend-specific tests.
1843 return workload;
1844}
1845
1846template <typename L2NormalizationWorkload, armnn::DataType DataType>
1847std::unique_ptr<L2NormalizationWorkload> CreateL2NormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
1848 armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
1849{
1850 // Creates the layer we're testing.
1851 L2NormalizationDescriptor layerDesc;
1852 layerDesc.m_DataLayout = dataLayout;
1853
1854 Layer* const layer = graph.AddLayer<L2NormalizationLayer>(layerDesc, "l2norm");
1855
1856 // Creates extra layers.
1857 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1858 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1859
1860 TensorShape inputShape = (dataLayout == DataLayout::NCHW) ?
1861 TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1862 TensorShape outputShape = (dataLayout == DataLayout::NCHW) ?
1863 TensorShape{ 5, 20, 50, 67 } : TensorShape{ 5, 50, 67, 20 };
1864
1865 // Connects up.
1866 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
1867 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1868 Connect(input, layer, inputTensorInfo);
1869 Connect(layer, output, outputTensorInfo);
1870 CreateTensorHandles(graph, factory);
1871
1872 // Makes the workload and checks it.
1873 auto workload = MakeAndCheckWorkload<L2NormalizationWorkload>(*layer, factory);
1874
1875 L2NormalizationQueueDescriptor queueDescriptor = workload->GetData();
1876 CHECK((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
1877 CHECK(queueDescriptor.m_Inputs.size() == 1);
1878 CHECK(queueDescriptor.m_Outputs.size() == 1);
1879
1880 // Returns so we can do extra, backend-specific tests.
1881 return workload;
1882}
1883
1884template <typename ReshapeWorkload, armnn::DataType DataType>
1885std::unique_ptr<ReshapeWorkload> CreateReshapeWorkloadTest(armnn::IWorkloadFactory& factory,
1886 armnn::Graph& graph)
1887{
1888 // Creates the layer we're testing.
1889 TensorShape outputShape({ 1, 4 });
1890 ReshapeDescriptor reshapeDesc;
1891 reshapeDesc.m_TargetShape = outputShape;
1892 Layer* const layer = graph.AddLayer<ReshapeLayer>(reshapeDesc, "layer");
1893
1894 // Creates extra layers.
1895 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1896 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1897
1898 // Connects up.
1899 armnn::TensorInfo inputTensorInfo({ 4, 1 }, DataType);
1900 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
1901 Connect(input, layer, inputTensorInfo);
1902 Connect(layer, output, outputTensorInfo);
1903 CreateTensorHandles(graph, factory);
1904
1905 // Makes the workload and checks it.
1906 auto workload = MakeAndCheckWorkload<ReshapeWorkload>(*layer, factory);
1907
1908 ReshapeQueueDescriptor queueDescriptor = workload->GetData();
1909 CHECK(queueDescriptor.m_Inputs.size() == 1);
1910 CHECK(queueDescriptor.m_Outputs.size() == 1);
1911
1912 // Returns so we can do extra, backend-specific tests.
1913 return workload;
1914}
1915
1916template <typename ConvertFp16ToFp32Float32Workload>
1917std::unique_ptr<ConvertFp16ToFp32Float32Workload> CreateConvertFp16ToFp32WorkloadTest(
1918 armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1919{
1920 // Creates the layer we're testing.
1921 ConvertFp16ToFp32Layer* const layer = graph.AddLayer<ConvertFp16ToFp32Layer>("Fp16ToFp32Converter");
1922
1923 // Creates extra layers.
1924 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1925 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1926
1927 // Connects up.
1928 armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1929 armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1930 Connect(input, layer, inputTensorInfo);
1931 Connect(layer, output, outputTensorInfo);
1932 CreateTensorHandles(graph, factory);
1933
1934 // Makes the workload and checks it.
1935 auto workload = MakeAndCheckWorkload<ConvertFp16ToFp32Float32Workload>(*layer, factory);
1936
1937 ConvertFp16ToFp32QueueDescriptor queueDescriptor = workload->GetData();
1938 CHECK(queueDescriptor.m_Inputs.size() == 1);
1939 CHECK(queueDescriptor.m_Outputs.size() == 1);
1940
1941 // Returns so we can do extra, backend-specific tests.
1942 return workload;
1943}
1944
1945template <typename ConvertFp32ToFp16Float16Workload>
1946std::unique_ptr<ConvertFp32ToFp16Float16Workload> CreateConvertFp32ToFp16WorkloadTest(
1947 armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1948{
1949 // Creates the layer we're testing.
1950 ConvertFp32ToFp16Layer* const layer = graph.AddLayer<ConvertFp32ToFp16Layer>("Fp32ToFp16Converter");
1951
1952 // Creates extra layers.
1953 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1954 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1955
1956 // Connects up.
1957 armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
1958 armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
1959 Connect(input, layer, inputTensorInfo);
1960 Connect(layer, output, outputTensorInfo);
1961 CreateTensorHandles(graph, factory);
1962
1963 // Makes the workload and checks it.
1964 auto workload = MakeAndCheckWorkload<ConvertFp32ToFp16Float16Workload>(*layer, factory);
1965
1966 ConvertFp32ToFp16QueueDescriptor queueDescriptor = workload->GetData();
1967 CHECK(queueDescriptor.m_Inputs.size() == 1);
1968 CHECK(queueDescriptor.m_Outputs.size() == 1);
1969
1970 // Returns so we can do extra, backend-specific tests.
1971 return workload;
1972}
1973
1974template <typename MeanWorkload, armnn::DataType DataType>
1975std::unique_ptr<MeanWorkload> CreateMeanWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
1976{
1977 // Reduce along the first and second dimensions, and do not keep the reduced dimensions.
1978 MeanDescriptor descriptor({ 1, 2 }, false);
1979
1980 // Creates the layer we're testing.
1981 Layer* const layer = graph.AddLayer<MeanLayer>(descriptor, "mean");
1982
1983 // Creates extra layers.
1984 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
1985 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
1986
1987 // Connects up.
1988 armnn::TensorInfo inputTensorInfo({ 1, 3, 7, 4 }, DataType);
1989 armnn::TensorInfo outputTensorInfo({ 1, 4 }, DataType);
1990 Connect(input, layer, inputTensorInfo);
1991 Connect(layer, output, outputTensorInfo);
1992 CreateTensorHandles(graph, factory);
1993
1994 // Makes the workload and checks it.
1995 auto workload = MakeAndCheckWorkload<MeanWorkload>(*layer, factory);
1996
1997 MeanQueueDescriptor queueDescriptor = workload->GetData();
1998 CHECK(queueDescriptor.m_Parameters.m_Axis == descriptor.m_Axis);
1999 CHECK(queueDescriptor.m_Parameters.m_KeepDims == descriptor.m_KeepDims);
2000 CHECK(queueDescriptor.m_Inputs.size() == 1);
2001 CHECK(queueDescriptor.m_Outputs.size() == 1);
2002
2003 // Returns so we can do extra, backend-specific tests.
2004 return workload;
2005}
2006
2007template<typename ConcatWorkload, armnn::DataType DataType>
2008std::unique_ptr<ConcatWorkload> CreateConcatWorkloadTest(armnn::IWorkloadFactory &factory,
2009 armnn::Graph &graph,
2010 const armnn::TensorShape &outputShape,
2011 unsigned int concatAxis)
2012{
2013 armnn::TensorInfo inputTensorInfo({ 2, 3, 2, 5 }, DataType);
2014 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2015
2016 // Constructs the graph.
2017 Layer* const input0 = graph.AddLayer<InputLayer>(0, "input0");
2018 Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
2019 armnn::OriginsDescriptor descriptor;
2020
2021 std::vector<armnn::TensorShape> inputShapes{{ 2, 3, 2, 5 }, { 2, 3, 2, 5 }};
2022
2023 descriptor = CreateDescriptorForConcatenation(inputShapes.begin(),
2024 inputShapes.end(),
2025 concatAxis);
2026
2027 // create concat layer
2028 Layer* const concat = graph.AddLayer<ConcatLayer>(descriptor, "concat");
2029 CHECK(concat);
2030
2031 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2032
2033 // Adds connections.
2034 // connect input0 to concat
2035 Connect(input0, concat, inputTensorInfo, 0, 0);
2036 // connect input1 to concat
2037 Connect(input1, concat, inputTensorInfo, 0, 1);
2038 // connect concat to output
2039 Connect(concat, output, outputTensorInfo, 0, 0);
2040
2041 // create tensor handles
2042 CreateTensorHandles(graph, factory);
2043
2044 // create concat workload
2045 auto workloadConcat = MakeAndCheckWorkload<ConcatWorkload>(*concat, factory);
2046 CHECK(workloadConcat);
2047
2048 return workloadConcat;
2049}
2050
2051template <typename PreCompiledWorkload, armnn::DataType dataType>
2052std::pair<armnn::IOptimizedNetworkPtr, std::unique_ptr<PreCompiledWorkload>> CreatePreCompiledWorkloadTest(
2053 armnn::IWorkloadFactory& factory,
2054 armnn::Graph& graph,
2055 bool biasEnabled = false)
2056{
2057 IgnoreUnused(graph);
2058
2059 // build up the structure of the network
2060 armnn::INetworkPtr net(armnn::INetwork::Create());
2061
2062 // Add an input layer
2063 armnn::IConnectableLayer* const inputLayer = net->AddInputLayer(0, "input layer");
2064 CHECK(inputLayer);
2065
2066 // ArmNN weights tensor shape is OIHW (out channels, in channels, height, width) for NCHW
2067 // ArmNN weights tensor shape is OHWI (out channels, height, width, in channels) for NHWC
2068 // this test is using NHWC, so the weights shape is OHWI
2069 TensorInfo weightsTensorInfo(TensorShape({16, 1, 1, 16}), dataType, 0.9f, 0, true);
2070 unsigned int weightsLength = weightsTensorInfo.GetNumElements();
2071
2072 using WeightType = armnn::ResolveType<dataType>;
2073 std::vector<WeightType> convWeightsData(weightsLength);
2074 for (unsigned int i = 0; i < weightsLength; ++i)
2075 {
2076 convWeightsData[i] = static_cast<WeightType>(i);
2077 }
2078
2079 armnn::ConstTensor weights(weightsTensorInfo, convWeightsData);
2080
2081 // Add a layer that can be used in the PreCompiled layer
2082 armnn::Convolution2dDescriptor convDesc2d;
2083 convDesc2d.m_StrideX = 1;
2084 convDesc2d.m_StrideY = 1;
2085 convDesc2d.m_BiasEnabled = biasEnabled;
2086 convDesc2d.m_DataLayout = armnn::DataLayout::NHWC;
2087
Keith Davis721e6292022-05-17 10:06:53 +01002088
Sadik Armagana097d2a2021-11-24 15:47:28 +00002089 const std::string convLayerName("conv layer");
2090
Keith Davis721e6292022-05-17 10:06:53 +01002091 armnn::IConnectableLayer* convLayer = net->AddConvolution2dLayer(convDesc2d, convLayerName.c_str());
2092
2093 IConnectableLayer* weightsLayer = net->AddConstantLayer(weights);
2094 weightsLayer->GetOutputSlot(0).SetTensorInfo(weights.GetInfo());
2095 weightsLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(1u));
2096
Sadik Armagana097d2a2021-11-24 15:47:28 +00002097 if (biasEnabled)
2098 {
2099 constexpr armnn::DataType biasDataType = ( dataType == armnn::DataType::QAsymmU8) ?
2100 armnn::DataType::Signed32 : armnn::DataType::Float32;
2101
2102 TensorInfo biasTensorInfo(TensorShape({16}), biasDataType, 0.9f * 0.9f, 0, true);
2103 unsigned int biasLength = biasTensorInfo.GetNumElements();
2104
2105 using BiasType = armnn::ResolveType<biasDataType>;
2106 std::vector<BiasType> biasData(biasLength);
2107 std::fill(biasData.begin(), biasData.end(), static_cast<BiasType>(0));
2108
2109 armnn::ConstTensor biases(biasTensorInfo, biasData);
2110
Keith Davis721e6292022-05-17 10:06:53 +01002111 IConnectableLayer* biasLayer = net->AddConstantLayer(biases);
2112
2113 biasLayer->GetOutputSlot(0).SetTensorInfo(biases.GetInfo());
2114 biasLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(2u));
Sadik Armagana097d2a2021-11-24 15:47:28 +00002115 }
2116
2117 CHECK(convLayer);
2118
2119 // Add an output layer
2120 armnn::IConnectableLayer* const outputLayer = net->AddOutputLayer(0, "output layer");
2121 CHECK(outputLayer);
2122
2123 // set the tensors in the network (NHWC format)
2124 TensorInfo inputTensorInfo(TensorShape({ 1, 16, 16, 16 }), dataType);
2125 if (dataType == armnn::DataType::QAsymmU8)
2126 {
2127 inputTensorInfo.SetQuantizationOffset(0);
2128 inputTensorInfo.SetQuantizationScale(0.9f);
2129 }
2130
2131 TensorInfo outputTensorInfo(TensorShape({1, 16, 16, 16}), dataType);
2132 if (dataType == armnn::DataType::QAsymmU8)
2133 {
2134 outputTensorInfo.SetQuantizationOffset(0);
2135 outputTensorInfo.SetQuantizationScale(0.9f);
2136 }
2137
2138 // Connect the layers
2139 inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
2140 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
2141
2142 convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
2143 convLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2144
2145 // Optimize the network for the backend supported by the factory
2146 std::vector<armnn::BackendId> backends = {factory.GetBackendId()};
2147 armnn::IRuntime::CreationOptions options;
2148 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
2149 armnn::OptimizerOptions optimizerOptions;
2150 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec(),
2151 optimizerOptions);
2152 CHECK(optimizedNet != nullptr);
2153
2154 // Find the PreCompiled layer in the optimised graph
2155 armnn::Graph& optimisedGraph = GetGraphForTesting(optimizedNet.get());
2156 Layer* preCompiledLayer = nullptr;
2157 for (auto& layer : optimisedGraph)
2158 {
2159 if (layer->GetType() == LayerType::PreCompiled)
2160 {
2161 preCompiledLayer = layer;
2162 }
2163 }
2164 CHECK(preCompiledLayer != nullptr);
2165
2166 // Create the TensorHandles.
2167 CreateTensorHandles(optimisedGraph, factory);
2168
2169 // Make the workload and check it.
2170 auto workload = MakeAndCheckWorkload<PreCompiledWorkload>(*preCompiledLayer, factory);
2171
2172 PreCompiledQueueDescriptor queueDescriptor = workload->GetData();
2173 CHECK(queueDescriptor.m_Inputs.size() == 1);
2174 CHECK(queueDescriptor.m_Outputs.size() == 1);
2175
2176 // Returns the workload so we can do extra, backend-specific tests.
2177 // NOTE: We need to return the optimised network as well, otherwise it gets
2178 // out of scope and the tensor handles get destructed
2179 return std::make_pair(std::move(optimizedNet), std::move(workload));
2180}
2181
2182template<typename ConstantWorkload, armnn::DataType DataType>
2183std::unique_ptr<ConstantWorkload> CreateConstantWorkloadTest(armnn::IWorkloadFactory& factory,
2184 armnn::Graph& graph,
2185 const armnn::TensorShape& outputShape)
2186{
2187 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2188
2189 // create constant layer
2190 auto constant = graph.AddLayer<ConstantLayer>("constant");
2191 CHECK(constant);
2192 constant->m_LayerOutput = std::make_unique<ScopedTensorHandle>(outputTensorInfo);
2193
2194 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2195
2196 // Adds connections.
2197 // connect constant to output
2198 Connect(constant, output, outputTensorInfo, 0, 0);
2199
2200 // create tensor handles
2201 CreateTensorHandles(graph, factory);
2202
2203 // create Constant workload"
2204 auto workloadConstant = MakeAndCheckWorkload<ConstantWorkload>(*constant, factory);
2205 CHECK(workloadConstant);
2206
2207 return workloadConstant;
2208}
2209
2210template <typename PreluWorkload>
2211std::unique_ptr<PreluWorkload> CreatePreluWorkloadTest(armnn::IWorkloadFactory& factory,
2212 armnn::Graph& graph,
2213 const armnn::TensorShape& inputShape,
2214 const armnn::TensorShape& alphaShape,
2215 const armnn::TensorShape& outputShape,
2216 armnn::DataType dataType)
2217{
2218 // Creates the PReLU layer
2219 Layer* const layer = graph.AddLayer<PreluLayer>("prelu");
2220 CHECK(layer != nullptr);
2221
2222 // Creates extra layers
2223 Layer* const input = graph.AddLayer<InputLayer> (0, "input");
2224 Layer* const alpha = graph.AddLayer<InputLayer> (1, "alpha");
2225 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2226 CHECK(input != nullptr);
2227 CHECK(alpha != nullptr);
2228 CHECK(output != nullptr);
2229
2230 // Connects up
2231 armnn::TensorInfo inputTensorInfo (inputShape, dataType);
2232 armnn::TensorInfo alphaTensorInfo (alphaShape, dataType);
2233 armnn::TensorInfo outputTensorInfo(outputShape, dataType);
2234 Connect(input, layer, inputTensorInfo, 0, 0);
2235 Connect(alpha, layer, alphaTensorInfo, 0, 1);
2236 Connect(layer, output, outputTensorInfo, 0, 0);
2237 CreateTensorHandles(graph, factory);
2238
2239 // Makes the workload and checks it
2240 auto workload = MakeAndCheckWorkload<PreluWorkload>(*layer, factory);
2241
2242 PreluQueueDescriptor queueDescriptor = workload->GetData();
2243 CHECK(queueDescriptor.m_Inputs.size() == 2);
2244 CHECK(queueDescriptor.m_Outputs.size() == 1);
2245
2246 // Returns so we can do extra, backend-specific tests.
2247 return workload;
2248}
2249
2250template <typename SpaceToDepthWorkload, armnn::DataType DataType>
2251std::unique_ptr<SpaceToDepthWorkload> CreateSpaceToDepthWorkloadTest(armnn::IWorkloadFactory& factory,
2252 armnn::Graph& graph)
2253{
2254 SpaceToDepthDescriptor desc;
2255 desc.m_BlockSize = 2;
2256 Layer* const layer = graph.AddLayer<SpaceToDepthLayer>(desc, "spaceToDepth");
2257
2258 // Creates extra layers.
2259 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
2260 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2261
2262 // Connects up.
2263 armnn::TensorInfo inputTensorInfo({ 1, 2, 2, 1 }, DataType);
2264 armnn::TensorInfo outputTensorInfo({ 1, 1, 1, 4 }, DataType);
2265
2266 Connect(input, layer, inputTensorInfo);
2267 Connect(layer, output, outputTensorInfo);
2268
2269 CreateTensorHandles(graph, factory);
2270
2271 // Makes the workload and checks it.
2272 auto workload = MakeAndCheckWorkload<SpaceToDepthWorkload>(*layer, factory);
2273
2274 SpaceToDepthQueueDescriptor queueDescriptor = workload->GetData();
2275 CHECK(queueDescriptor.m_Inputs.size() == 1);
2276 CHECK(queueDescriptor.m_Outputs.size() == 1);
2277
2278 return workload;
2279}
2280
2281template <typename StackWorkload, armnn::DataType DataType>
2282std::unique_ptr<StackWorkload> CreateStackWorkloadTest(armnn::IWorkloadFactory& factory,
2283 armnn::Graph& graph,
2284 const armnn::TensorShape& inputShape,
2285 const armnn::TensorShape& outputShape,
2286 unsigned int axis,
2287 unsigned int numInputs)
2288{
2289 armnn::TensorInfo inputTensorInfo(inputShape, DataType);
2290 armnn::TensorInfo outputTensorInfo(outputShape, DataType);
2291
2292 // Constructs the Stack layer.
2293 armnn::StackDescriptor descriptor(axis, numInputs, inputShape);
2294 Layer* const stackLayer = graph.AddLayer<StackLayer>(descriptor, "stack");
2295 CHECK(stackLayer != nullptr);
2296
2297 // Constructs layer inputs and output.
2298 std::vector<Layer*> inputs;
2299 for (unsigned int i=0; i<numInputs; ++i)
2300 {
2301 inputs.push_back(graph.AddLayer<InputLayer>(
2302 static_cast<int>(i),
2303 ("input" + std::to_string(i)).c_str()
2304 ));
2305 CHECK(inputs[i] != nullptr);
2306 }
2307 Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
2308 CHECK(output != nullptr);
2309
2310 // Adds connections.
2311 for (unsigned int i=0; i<numInputs; ++i)
2312 {
2313 Connect(inputs[i], stackLayer, inputTensorInfo, 0, i);
2314 }
2315 Connect(stackLayer, output, outputTensorInfo, 0, 0);
2316
2317 CreateTensorHandles(graph, factory);
2318
2319 auto stackWorkload = MakeAndCheckWorkload<StackWorkload>(*stackLayer, factory);
2320 StackQueueDescriptor queueDescriptor = stackWorkload->GetData();
2321 CHECK(queueDescriptor.m_Inputs.size() == numInputs);
2322 CHECK(queueDescriptor.m_Outputs.size() == 1);
2323
2324 return stackWorkload;
2325}
2326
2327} // Anonymous namespace