IVGCVSW-1988: Refactor backend-specific unit tests
[platform/upstream/armnn.git] / src / armnn / test / CreateWorkload.hpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6
7 #include <boost/test/unit_test.hpp>
8
9 #include <boost/cast.hpp>
10
11 #include <backends/WorkloadData.hpp>
12 #include <backends/WorkloadFactory.hpp>
13 #include <backends/CpuTensorHandle.hpp>
14
15 #include <Graph.hpp>
16
17 #include <utility>
18
19
20 using namespace armnn;
21
22 namespace
23 {
24
25 using namespace std;
26
27 // Calls CreateWorkload for a layer, and checks the returned pointer is of the correct type.
28 template<typename Workload>
29 std::unique_ptr<Workload> MakeAndCheckWorkload(Layer& layer, Graph& graph, const IWorkloadFactory& factory)
30 {
31     std::unique_ptr<IWorkload> workload = layer.CreateWorkload(graph, factory);
32     BOOST_TEST(workload.get() == boost::polymorphic_downcast<Workload*>(workload.get()),
33                "Cannot convert to derived class");
34     std::string reasonIfUnsupported;
35     layer.SetComputeDevice(factory.GetCompute());
36     BOOST_TEST(factory.IsLayerSupported(layer, layer.GetDataType(), reasonIfUnsupported));
37     return std::unique_ptr<Workload>(static_cast<Workload*>(workload.release()));
38 }
39
40 // Connects two layers.
41 void Connect(Layer* from, Layer* to, const TensorInfo& tensorInfo, unsigned int fromIndex = 0, unsigned int toIndex = 0)
42 {
43     from->GetOutputSlot(fromIndex).Connect(to->GetInputSlot(toIndex));
44     from->GetOutputHandler(fromIndex).SetTensorInfo(tensorInfo);
45 }
46
47 // Helper function to create tensor handlers for workloads, assuming they all use the same factory.
48 void CreateTensorHandles(armnn::Graph& graph, armnn::IWorkloadFactory& factory)
49 {
50     for (auto&& layer : graph.TopologicalSort())
51     {
52         layer->CreateTensorHandles(graph, factory);
53     }
54 }
55
56 /////////////////////////////////////////////////////////////////////////////////////////////
57 // The following functions are called by backends/test/CreateWorkload*.cpp
58 // They build very simple graphs, and then create a workload.
59 // Some checks are performed on the workload to ensure parameters have been passed correctly.
60 // They return the created workloads so that backend-specific checks can be performed.
61 /////////////////////////////////////////////////////////////////////////////////////////////
62
63 template <typename ActivationWorkload, armnn::DataType DataType>
64 std::unique_ptr<ActivationWorkload> CreateActivationWorkloadTest(armnn::IWorkloadFactory& factory,
65                                                                  armnn::Graph&            graph)
66 {
67     // Creates the layer we're testing.
68     ActivationDescriptor layerDesc;
69     layerDesc.m_Function = ActivationFunction::Abs;
70     layerDesc.m_A        = 3.5f;
71     layerDesc.m_B        = -10.0f;
72
73     ActivationLayer* const layer = graph.AddLayer<ActivationLayer>(layerDesc, "layer");
74
75     // Creates extra layers.
76     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
77     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
78
79     // Connects up.
80     armnn::TensorInfo tensorInfo({1, 1}, DataType);
81
82     Connect(input, layer, tensorInfo);
83     Connect(layer, output, tensorInfo);
84
85     CreateTensorHandles(graph, factory);
86
87     // Makes the workload and checks it.
88     auto workload = MakeAndCheckWorkload<ActivationWorkload>(*layer, graph, factory);
89
90     ActivationQueueDescriptor queueDescriptor = workload->GetData();
91     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
92     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
93     BOOST_TEST(queueDescriptor.m_Parameters.m_A == 3.5f);
94     BOOST_TEST(queueDescriptor.m_Parameters.m_B == -10.0f);
95     BOOST_TEST((queueDescriptor.m_Parameters.m_Function == ActivationFunction::Abs));
96
97     // Returns so we can do extra, backend-specific tests.
98     return workload;
99 }
100
101 template <typename WorkloadType,
102           typename DescriptorType,
103           typename LayerType,
104           armnn::DataType DataType>
105 std::unique_ptr<WorkloadType> CreateArithmeticWorkloadTest(armnn::IWorkloadFactory& factory,
106                                                            armnn::Graph&            graph)
107 {
108     // Creates the layer we're testing.
109     Layer* const layer = graph.AddLayer<LayerType>("layer");
110
111     // Creates extra layers.
112     Layer* const input1 = graph.AddLayer<InputLayer>(1, "input1");
113     Layer* const input2 = graph.AddLayer<InputLayer>(2, "input2");
114     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
115
116     // Connects up.
117     armnn::TensorInfo tensorInfo({2, 3}, DataType);
118     Connect(input1, layer, tensorInfo, 0, 0);
119     Connect(input2, layer, tensorInfo, 0, 1);
120     Connect(layer, output, tensorInfo);
121     CreateTensorHandles(graph, factory);
122
123     // Makes the workload and checks it.
124     auto workload = MakeAndCheckWorkload<WorkloadType>(*layer, graph, factory);
125
126     DescriptorType queueDescriptor = workload->GetData();
127     BOOST_TEST(queueDescriptor.m_Inputs.size() == 2);
128     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
129
130     // Returns so we can do extra, backend-specific tests.
131     return workload;
132 }
133
134 template <typename BatchNormalizationFloat32Workload, armnn::DataType DataType>
135 std::unique_ptr<BatchNormalizationFloat32Workload> CreateBatchNormalizationWorkloadTest(
136     armnn::IWorkloadFactory& factory, armnn::Graph& graph)
137 {
138     // Creates the layer we're testing.
139     BatchNormalizationDescriptor layerDesc;
140     layerDesc.m_Eps = 0.05f;
141
142     BatchNormalizationLayer* const layer = graph.AddLayer<BatchNormalizationLayer>(layerDesc, "layer");
143
144     armnn::TensorInfo weightInfo({3}, DataType);
145     layer->m_Mean     = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
146     layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
147     layer->m_Beta     = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
148     layer->m_Gamma    = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
149     layer->m_Mean->Allocate();
150     layer->m_Variance->Allocate();
151     layer->m_Beta->Allocate();
152     layer->m_Gamma->Allocate();
153
154     // Creates extra layers.
155     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
156     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
157
158     // Connects up.
159     armnn::TensorInfo tensorInfo({2, 3, 1, 1}, DataType);
160     Connect(input, layer, tensorInfo);
161     Connect(layer, output, tensorInfo);
162     CreateTensorHandles(graph, factory);
163
164     // Makes the workload and checks it.
165     auto workload = MakeAndCheckWorkload<BatchNormalizationFloat32Workload>(*layer, graph, factory);
166     BatchNormalizationQueueDescriptor queueDescriptor = workload->GetData();
167     BOOST_TEST(queueDescriptor.m_Parameters.m_Eps == 0.05f);
168     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
169     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
170     BOOST_TEST((queueDescriptor.m_Mean->GetTensorInfo() == TensorInfo({3}, DataType)));
171     BOOST_TEST((queueDescriptor.m_Variance->GetTensorInfo() == TensorInfo({3}, DataType)));
172     BOOST_TEST((queueDescriptor.m_Gamma->GetTensorInfo() == TensorInfo({3}, DataType)));
173     BOOST_TEST((queueDescriptor.m_Beta->GetTensorInfo() == TensorInfo({3}, DataType)));
174
175     // Returns so we can do extra, backend-specific tests.
176     return workload;
177 }
178
179 template <typename Convolution2dWorkload, armnn::DataType DataType>
180 std::unique_ptr<Convolution2dWorkload> CreateConvolution2dWorkloadTest(armnn::IWorkloadFactory& factory,
181                                                                        armnn::Graph&            graph,
182                                                                        DataLayout dataLayout = DataLayout::NCHW)
183 {
184     // Creates the layer we're testing.
185     Convolution2dDescriptor layerDesc;
186     layerDesc.m_PadLeft = 3;
187     layerDesc.m_PadRight = 3;
188     layerDesc.m_PadTop = 1;
189     layerDesc.m_PadBottom = 1;
190     layerDesc.m_StrideX = 2;
191     layerDesc.m_StrideY = 4;
192     layerDesc.m_BiasEnabled = true;
193     layerDesc.m_DataLayout = dataLayout;
194
195     Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
196
197     TensorShape weightShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 5, 3} : TensorShape{2, 5, 3, 3};
198     TensorShape inputShape  = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 3, 8, 16} : TensorShape{2, 8, 16, 3};
199     TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{2, 2, 2, 10} : TensorShape{2, 2, 10, 2};
200
201     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo(weightShape, DataType));
202     layer->m_Bias   = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({2}, GetBiasDataType(DataType)));
203
204     layer->m_Weight->Allocate();
205     layer->m_Bias->Allocate();
206
207     // Creates extra layers.
208     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
209     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
210
211     // Connects up.
212     Connect(input, layer, TensorInfo(inputShape, DataType));
213     Connect(layer, output, TensorInfo(outputShape, DataType));
214     CreateTensorHandles(graph, factory);
215
216     // Makes the workload and checks it.
217     auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, graph, factory);
218
219     Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
220     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
221     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 4);
222     BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 3);
223     BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 3);
224     BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
225     BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
226     BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled);
227     BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
228
229     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
230     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
231     BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo(weightShape, DataType)));
232     BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() ==
233         TensorInfo({2}, GetBiasDataType(DataType))));
234
235     // Returns so we can do extra, backend-specific tests.
236     return workload;
237 }
238
239 template <typename LstmWorkload>
240 std::unique_ptr<LstmWorkload> CreateLstmWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
241 {
242     // This parameter setting is for withCifgWithPeepholeNoProjection
243     LstmDescriptor layerDesc;
244     layerDesc.m_ActivationFunc = 4;
245     layerDesc.m_ClippingThresCell = 0.0f;
246     layerDesc.m_ClippingThresProj = 0.0f;
247     layerDesc.m_CifgEnabled = true;
248     layerDesc.m_PeepholeEnabled = true;
249     layerDesc.m_ProjectionEnabled = false;
250
251     LstmLayer* const layer = graph.AddLayer<LstmLayer>(layerDesc, "layer");
252     unsigned int batchSize = 2;
253     unsigned int inputSize = 2;
254     unsigned int numUnits = 4;
255     unsigned int outputSize = 4;
256
257     layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
258             (TensorInfo({ numUnits, inputSize }, DataType::Float32));
259     layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedCpuTensorHandle>
260             (TensorInfo({ numUnits, inputSize }, DataType::Float32));
261     layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
262             (TensorInfo({ numUnits, inputSize }, DataType::Float32));
263     layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
264             (TensorInfo({ numUnits, outputSize }, DataType::Float32));
265     layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedCpuTensorHandle>
266             (TensorInfo({ numUnits, outputSize }, DataType::Float32));
267     layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
268             (TensorInfo({ numUnits, outputSize }, DataType::Float32));
269     layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>
270             (TensorInfo({ numUnits }, DataType::Float32));
271     layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>
272             (TensorInfo({ numUnits }, DataType::Float32));
273     layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>
274             (TensorInfo({ numUnits }, DataType::Float32));
275
276     layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
277     layer->m_BasicParameters.m_InputToCellWeights->Allocate();
278     layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
279     layer->m_BasicParameters.m_RecurrentToForgetWeights->Allocate();
280     layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
281     layer->m_BasicParameters.m_RecurrentToOutputWeights->Allocate();
282     layer->m_BasicParameters.m_ForgetGateBias->Allocate();
283     layer->m_BasicParameters.m_CellBias->Allocate();
284     layer->m_BasicParameters.m_OutputGateBias->Allocate();
285
286
287     if (layerDesc.m_PeepholeEnabled)
288     {
289         layer->m_PeepholeParameters.m_CellToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
290                 (TensorInfo({ numUnits }, DataType::Float32));
291         layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
292                 (TensorInfo({ numUnits }, DataType::Float32));
293         layer->m_PeepholeParameters.m_CellToForgetWeights->Allocate();
294         layer->m_PeepholeParameters.m_CellToOutputWeights->Allocate();
295     }
296
297     // create input and output layers
298     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
299     Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
300     Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
301     Layer* const scratchBuffer = graph.AddLayer<OutputLayer>(0, "scratchBuffer");
302     Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
303     Layer* const cellStateOut = graph.AddLayer<OutputLayer>(2, "cellStateOut");
304     Layer* const output = graph.AddLayer<OutputLayer>(3, "output");
305
306     // connect up
307     armnn::TensorInfo lstmTensorInfo1({ batchSize, inputSize }, DataType::Float32);
308     armnn::TensorInfo lstmTensorInfo2({ batchSize, numUnits}, DataType::Float32);
309     armnn::TensorInfo lstmTensorInfo3({ batchSize, outputSize }, DataType::Float32);
310     armnn::TensorInfo lstmTensorInfoScratchBuff({ batchSize, numUnits*3 }, DataType::Float32);
311     if (layerDesc.m_CifgEnabled)
312     {
313         lstmTensorInfoScratchBuff.SetShape({ batchSize, numUnits*4 });
314     }
315
316     Connect(input, layer, lstmTensorInfo1, 0, 0);
317     Connect(cellStateIn, layer, lstmTensorInfo2, 0, 1);
318     Connect(outputStateIn, layer, lstmTensorInfo3, 0, 2);
319     Connect(layer, scratchBuffer, lstmTensorInfoScratchBuff, 0, 0);
320     Connect(layer, outputStateOut, lstmTensorInfo3, 1, 0);
321     Connect(layer, cellStateOut, lstmTensorInfo2, 2, 0);
322     Connect(layer, output, lstmTensorInfo3, 3, 0);
323
324     CreateTensorHandles(graph, factory);
325
326     // make the workload and check it
327     auto workload = MakeAndCheckWorkload<LstmWorkload>(*layer, graph, factory);
328     LstmQueueDescriptor queueDescriptor = workload->GetData();
329     BOOST_TEST(queueDescriptor.m_Parameters.m_ActivationFunc == 4);
330     BOOST_TEST(queueDescriptor.m_Parameters.m_ClippingThresCell == 0.0f);
331     BOOST_TEST(queueDescriptor.m_Parameters.m_ClippingThresProj == 0.0f);
332     BOOST_TEST(queueDescriptor.m_Inputs.size() == 3);
333     BOOST_TEST(queueDescriptor.m_Outputs.size() == 4);
334
335     BOOST_TEST((queueDescriptor.m_InputToForgetWeights->GetTensorInfo() == TensorInfo({ numUnits, inputSize },
336                                                                                      DataType::Float32)));
337     BOOST_TEST((queueDescriptor.m_OutputGateBias->GetTensorInfo() == TensorInfo({ numUnits },
338                                                                                      DataType::Float32)));
339     BOOST_TEST((queueDescriptor.m_CellBias->GetTensorInfo() == TensorInfo({ numUnits }, DataType::Float32)));
340     return workload;
341 }
342
343 template <typename Convolution2dWorkload, armnn::DataType DataType>
344 std::unique_ptr<Convolution2dWorkload> CreateDirectConvolution2dWorkloadTest(armnn::IWorkloadFactory& factory,
345                                                                        armnn::Graph&            graph)
346 {
347     // Creates the layer we're testing.
348     Convolution2dDescriptor layerDesc;
349     layerDesc.m_PadLeft = 1;
350     layerDesc.m_PadRight = 1;
351     layerDesc.m_PadTop = 1;
352     layerDesc.m_PadBottom = 1;
353     layerDesc.m_StrideX = 1;
354     layerDesc.m_StrideY = 1;
355     layerDesc.m_BiasEnabled = true;
356
357     Convolution2dLayer* const layer = graph.AddLayer<Convolution2dLayer>(layerDesc, "layer");
358
359     float inputsQScale = DataType == armnn::DataType::QuantisedAsymm8 ? 1.0f : 0.0;
360     float outputQScale = DataType == armnn::DataType::QuantisedAsymm8 ? 2.0f : 0.0;
361
362     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({ 2, 3, 3, 3 }, DataType, inputsQScale));
363     layer->m_Bias   = std::make_unique<ScopedCpuTensorHandle>
364         (TensorInfo({2},  GetBiasDataType(DataType), inputsQScale));
365     layer->m_Weight->Allocate();
366     layer->m_Bias->Allocate();
367
368     // Creates extra layers.
369     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
370     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
371
372     // Connects up.
373     Connect(input, layer, TensorInfo({2, 3, 6, 6}, DataType, inputsQScale));
374     Connect(layer, output, TensorInfo({2, 2, 6, 6}, DataType, outputQScale));
375     CreateTensorHandles(graph, factory);
376
377     // Makes the workload and checks it.
378     auto workload = MakeAndCheckWorkload<Convolution2dWorkload>(*layer, graph, factory);
379
380     Convolution2dQueueDescriptor queueDescriptor = workload->GetData();
381     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 1);
382     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 1);
383     BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 1);
384     BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 1);
385     BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
386     BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
387     BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
388
389     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
390     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
391     BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({2, 3, 3, 3},
392         DataType, inputsQScale)));
393     BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo()
394                 == TensorInfo({2},  GetBiasDataType(DataType), inputsQScale)));
395
396     // Returns so we can do extra, backend-specific tests.
397     return workload;
398 }
399
400 template <typename DepthwiseConvolution2dFloat32Workload>
401 std::unique_ptr<DepthwiseConvolution2dFloat32Workload> CreateDepthwiseConvolution2dWorkloadTest(
402     armnn::IWorkloadFactory& factory, armnn::Graph& graph)
403 {
404     // Creates the layer we're testing.
405     DepthwiseConvolution2dDescriptor layerDesc;
406     layerDesc.m_PadLeft         = 3;
407     layerDesc.m_PadRight        = 3;
408     layerDesc.m_PadTop          = 1;
409     layerDesc.m_PadBottom       = 1;
410     layerDesc.m_StrideX         = 2;
411     layerDesc.m_StrideY         = 4;
412     layerDesc.m_BiasEnabled     = true;
413
414     DepthwiseConvolution2dLayer* const layer = graph.AddLayer<DepthwiseConvolution2dLayer>(layerDesc, "layer");
415
416     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({3, 3, 5, 3}, DataType::Float32));
417     layer->m_Bias   = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({9}, DataType::Float32));
418     layer->m_Weight->Allocate();
419     layer->m_Bias->Allocate();
420
421     // Creates extra layers.
422     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
423     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
424
425     // Connects up.
426     Connect(input, layer, TensorInfo({2, 3, 8, 16}, armnn::DataType::Float32));
427     Connect(layer, output, TensorInfo({2, 9, 2, 10}, armnn::DataType::Float32));
428     CreateTensorHandles(graph, factory);
429
430     // Makes the workload and checks it.
431     auto workload = MakeAndCheckWorkload<DepthwiseConvolution2dFloat32Workload>(*layer, graph, factory);
432
433     DepthwiseConvolution2dQueueDescriptor queueDescriptor = workload->GetData();
434     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
435     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 4);
436     BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 3);
437     BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 3);
438     BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
439     BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
440     BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
441
442     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
443     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
444     BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({3, 3, 5, 3}, DataType::Float32)));
445     BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() == TensorInfo({9}, DataType::Float32)));
446
447     // Returns so we can do extra, backend-specific tests.
448     return workload;
449 }
450
451 template <typename FullyConnectedWorkload, armnn::DataType DataType>
452 std::unique_ptr<FullyConnectedWorkload> CreateFullyConnectedWorkloadTest(armnn::IWorkloadFactory& factory,
453                                                                          armnn::Graph&            graph)
454 {
455     // Creates the layer we're testing.
456     FullyConnectedDescriptor layerDesc;
457     layerDesc.m_BiasEnabled = true;
458     layerDesc.m_TransposeWeightMatrix = true;
459
460     FullyConnectedLayer* const layer = graph.AddLayer<FullyConnectedLayer>(layerDesc, "layer");
461
462     float inputsQScale = DataType == armnn::DataType::QuantisedAsymm8 ? 1.0f : 0.0;
463     float outputQScale = DataType == armnn::DataType::QuantisedAsymm8 ? 2.0f : 0.0;
464
465     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7, 20}, DataType, inputsQScale, 0));
466     layer->m_Bias   = std::make_unique<ScopedCpuTensorHandle>(TensorInfo({7}, GetBiasDataType(DataType), inputsQScale));
467     layer->m_Weight->Allocate();
468     layer->m_Bias->Allocate();
469
470     // Creates extra layers.
471     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
472     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
473
474     // Connects up.
475     Connect(input, layer, TensorInfo({3, 1, 4, 5}, DataType, inputsQScale));
476     Connect(layer, output, TensorInfo({3, 7}, DataType, outputQScale));
477     CreateTensorHandles(graph, factory);
478
479     // Makes the workload and checks it.
480     auto workload = MakeAndCheckWorkload<FullyConnectedWorkload>(*layer, graph, factory);
481
482     FullyConnectedQueueDescriptor queueDescriptor = workload->GetData();
483     BOOST_TEST(queueDescriptor.m_Parameters.m_BiasEnabled == true);
484     BOOST_TEST(queueDescriptor.m_Parameters.m_TransposeWeightMatrix == true);
485
486     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
487     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
488     BOOST_TEST((queueDescriptor.m_Weight->GetTensorInfo() == TensorInfo({7, 20}, DataType, inputsQScale)));
489     BOOST_TEST((queueDescriptor.m_Bias->GetTensorInfo() == TensorInfo({7}, GetBiasDataType(DataType), inputsQScale)));
490
491     // Returns so we can do extra, backend-specific tests.
492     return workload;
493 }
494
495 template <typename NormalizationWorkload, armnn::DataType DataType>
496 std::unique_ptr<NormalizationWorkload> CreateNormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
497                                                                        armnn::Graph& graph,
498                                                                        DataLayout dataLayout = DataLayout::NCHW)
499 {
500     // Creates the layer we're testing.
501     NormalizationDescriptor layerDesc;
502     layerDesc.m_NormChannelType = NormalizationAlgorithmChannel::Across;
503     layerDesc.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
504     layerDesc.m_NormSize = 3;
505     layerDesc.m_Alpha = 0.5f;
506     layerDesc.m_Beta = -1.0f;
507     layerDesc.m_K = 0.2f;
508     layerDesc.m_DataLayout = dataLayout;
509
510     NormalizationLayer* layer = graph.AddLayer<NormalizationLayer>(layerDesc, "layer");
511
512     // Creates extra layers.
513     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
514     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
515
516     // Connects up.
517     Connect(input, layer, TensorInfo({3, 5, 5, 1}, DataType));
518     Connect(layer, output, TensorInfo({3, 5, 5, 1}, DataType));
519     CreateTensorHandles(graph, factory);
520
521     // Makes the workload and checks it.
522     auto workload = MakeAndCheckWorkload<NormalizationWorkload>(*layer, graph, factory);
523
524     NormalizationQueueDescriptor queueDescriptor = workload->GetData();
525     BOOST_TEST((queueDescriptor.m_Parameters.m_NormChannelType == NormalizationAlgorithmChannel::Across));
526     BOOST_TEST((queueDescriptor.m_Parameters.m_NormMethodType == NormalizationAlgorithmMethod::LocalBrightness));
527     BOOST_TEST(queueDescriptor.m_Parameters.m_NormSize == 3);
528     BOOST_TEST(queueDescriptor.m_Parameters.m_Alpha == 0.5f);
529     BOOST_TEST(queueDescriptor.m_Parameters.m_Beta == -1.0f);
530     BOOST_TEST(queueDescriptor.m_Parameters.m_K == 0.2f);
531     BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
532
533     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
534     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
535
536     // Returns so we can do extra, backend-specific tests.
537     return workload;
538 }
539
540 template <typename Pooling2dWorkload, armnn::DataType DataType>
541 std::unique_ptr<Pooling2dWorkload> CreatePooling2dWorkloadTest(armnn::IWorkloadFactory& factory,
542                                                                armnn::Graph&            graph,
543                                                                DataLayout dataLayout = DataLayout::NCHW)
544 {
545     // Creates the layer we're testing.
546     Pooling2dDescriptor layerDesc;
547     layerDesc.m_PoolType = PoolingAlgorithm::Average;
548     layerDesc.m_PoolWidth = 3;
549     layerDesc.m_PoolHeight = 3;
550     layerDesc.m_PadLeft = 2;
551     layerDesc.m_PadRight = 2;
552     layerDesc.m_PadTop = 1;
553     layerDesc.m_PadBottom = 1;
554     layerDesc.m_StrideX = 2;
555     layerDesc.m_StrideY = 3;
556     layerDesc.m_OutputShapeRounding = OutputShapeRounding::Floor;
557     layerDesc.m_DataLayout = dataLayout;
558
559     Pooling2dLayer* const layer = graph.AddLayer<Pooling2dLayer>(layerDesc, "layer");
560
561     // Create extra layers
562     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
563     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
564
565     TensorShape inputShape  = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 5, 5} : TensorShape{3, 5, 5, 2};
566     TensorShape outputShape = (dataLayout == DataLayout::NCHW) ? TensorShape{3, 2, 2, 4} : TensorShape{3, 2, 4, 2};
567
568     // Connect up
569     Connect(input, layer, TensorInfo(inputShape, DataType));
570     Connect(layer, output, TensorInfo(outputShape, DataType));
571     CreateTensorHandles(graph, factory);
572
573     // Make the workload and checks it
574     auto workload = MakeAndCheckWorkload<Pooling2dWorkload>(*layer, graph, factory);
575
576     Pooling2dQueueDescriptor queueDescriptor = workload->GetData();
577     BOOST_TEST((queueDescriptor.m_Parameters.m_PoolType == PoolingAlgorithm::Average));
578     BOOST_TEST((queueDescriptor.m_Parameters.m_OutputShapeRounding == OutputShapeRounding::Floor));
579     BOOST_TEST(queueDescriptor.m_Parameters.m_PoolWidth == 3);
580     BOOST_TEST(queueDescriptor.m_Parameters.m_PoolHeight == 3);
581     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideX == 2);
582     BOOST_TEST(queueDescriptor.m_Parameters.m_StrideY == 3);
583     BOOST_TEST(queueDescriptor.m_Parameters.m_PadLeft == 2);
584     BOOST_TEST(queueDescriptor.m_Parameters.m_PadRight == 2);
585     BOOST_TEST(queueDescriptor.m_Parameters.m_PadTop == 1);
586     BOOST_TEST(queueDescriptor.m_Parameters.m_PadBottom == 1);
587
588     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
589     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
590
591     // Return so we can do extra, backend-specific tests
592     return workload;
593 }
594
595 template <typename SoftmaxWorkload, armnn::DataType DataType>
596 std::unique_ptr<SoftmaxWorkload> CreateSoftmaxWorkloadTest(armnn::IWorkloadFactory& factory,
597                                                            armnn::Graph&            graph)
598 {
599     // Create the layer we're testing.
600     SoftmaxDescriptor softmaxDescriptor;
601     Layer* const layer = graph.AddLayer<SoftmaxLayer>(softmaxDescriptor, "layer");
602
603     // Create extra layers.
604     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
605     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
606
607     // Connect up
608     armnn::TensorInfo tensorInfo({4, 1}, DataType);
609     Connect(input, layer, tensorInfo);
610     Connect(layer, output, tensorInfo);
611     CreateTensorHandles(graph, factory);
612
613     // Make the workload and checks it.
614     auto workload = MakeAndCheckWorkload<SoftmaxWorkload>(*layer, graph, factory);
615
616     SoftmaxQueueDescriptor queueDescriptor = workload->GetData();
617     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
618     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
619
620     // Return so we can do extra, backend-specific tests.
621     return workload;
622 }
623
624 template<typename SplitterWorkload, armnn::DataType DataType>
625 std::unique_ptr<SplitterWorkload>
626     CreateSplitterWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
627 {
628     // Create the layer we're testing.
629     // NOTE: need three dimensions channels, height/y, width/x because the Compute
630     //       library restricts subtensors to have the same x and y dimensions as
631     //       their parent tensors, and therefore the origin on the x and y dimension
632     //       has to be zero for any view. So we need a third dimension to split...
633     // NOTE: arguments are: number of views, number of dimensions.
634     ViewsDescriptor layerDesc(3, 3);
635     // NOTE: arguments are: view, dimension, value.
636     layerDesc.SetViewOriginCoord(0, 0, 0);
637     layerDesc.SetViewOriginCoord(1, 0, 1);
638     layerDesc.SetViewOriginCoord(2, 0, 3);
639
640     Layer* const layer = graph.AddLayer<SplitterLayer>(layerDesc, "layer");
641
642     // Adds extra layers.
643     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
644     Layer* const output0 = graph.AddLayer<OutputLayer>(0, "output0");
645     Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
646     Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
647
648     // Connects up.
649     armnn::TensorInfo tensorInfo({5, 7, 7}, DataType);
650     Connect(input, layer, tensorInfo);
651
652     armnn::TensorInfo output0Info({1, 7, 7}, DataType);
653     armnn::TensorInfo output1Info({2, 7, 7}, DataType);
654     armnn::TensorInfo output2Info({2, 7, 7}, DataType);
655
656     Connect(layer, output0, output0Info, 0, 0);
657     Connect(layer, output1, output1Info, 1, 0);
658     Connect(layer, output2, output2Info, 2, 0);
659
660     CreateTensorHandles(graph, factory);
661
662     // Makes the workload and checks it.
663     auto workload = MakeAndCheckWorkload<SplitterWorkload>(*layer, graph, factory);
664
665     SplitterQueueDescriptor queueDescriptor = workload->GetData();
666     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
667     BOOST_TEST(queueDescriptor.m_Outputs.size() == 3);
668     BOOST_TEST(queueDescriptor.m_ViewOrigins.size() == 3);
669
670     BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[0] == 0);
671     BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[0] == 1);
672     BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[0] == 3);
673     BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[1] == 0);
674     BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[1] == 0);
675     BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[1] == 0);
676     BOOST_TEST(queueDescriptor.m_ViewOrigins[0].m_Origin[2] == 0);
677     BOOST_TEST(queueDescriptor.m_ViewOrigins[1].m_Origin[2] == 0);
678     BOOST_TEST(queueDescriptor.m_ViewOrigins[2].m_Origin[2] == 0);
679
680     // Returns so we can do extra, backend-specific tests.
681     return workload;
682 }
683
684 /// This function constructs a graph with both a splitter and a merger, and returns a pair of the workloads.
685 template<typename SplitterWorkload, typename MergerWorkload, armnn::DataType DataType>
686 std::pair<std::unique_ptr<SplitterWorkload>, std::unique_ptr<MergerWorkload>>
687     CreateSplitterMergerWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph)
688 {
689     armnn::TensorInfo inputTensorInfo({ 1, 2, 100, 10 }, DataType);
690
691     armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 10 }, DataType);
692     armnn::TensorInfo splitTensorInfo2({ 1, 1, 100, 10 }, DataType);
693
694     //Constructs the graph.
695     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
696
697     armnn::ViewsDescriptor splitterViews(2);
698     splitterViews.SetViewOriginCoord(0, 0, 0);
699     splitterViews.SetViewOriginCoord(0, 1, 0);
700     splitterViews.SetViewOriginCoord(0, 2, 0);
701     splitterViews.SetViewOriginCoord(0, 3, 0);
702
703     splitterViews.SetViewOriginCoord(1, 0, 0);
704     splitterViews.SetViewOriginCoord(1, 1, 1);
705     splitterViews.SetViewOriginCoord(1, 2, 0);
706     splitterViews.SetViewOriginCoord(1, 3, 0);
707
708     Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
709     BOOST_TEST_CHECKPOINT("created splitter layer");
710
711     armnn::OriginsDescriptor mergerViews(2);
712     mergerViews.SetViewOriginCoord(0, 0, 0);
713     mergerViews.SetViewOriginCoord(0, 1, 1);
714     mergerViews.SetViewOriginCoord(0, 2, 0);
715     mergerViews.SetViewOriginCoord(0, 3, 0);
716
717     mergerViews.SetViewOriginCoord(1, 0, 0);
718     mergerViews.SetViewOriginCoord(1, 1, 0);
719     mergerViews.SetViewOriginCoord(1, 2, 0);
720     mergerViews.SetViewOriginCoord(1, 3, 0);
721
722     Layer* const merger = graph.AddLayer<MergerLayer>(mergerViews, "merger");
723     BOOST_TEST_CHECKPOINT("created merger layer");
724
725     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
726
727     // Adds connections.
728     Connect(input, splitter, inputTensorInfo, 0, 0);
729     BOOST_TEST_CHECKPOINT("connect input to splitter");
730     Connect(splitter, merger, splitTensorInfo1, 0, 1); // The splitter & merger are connected up.
731     BOOST_TEST_CHECKPOINT("connect splitter[0] to merger[1]");
732     Connect(splitter, merger, splitTensorInfo2, 1, 0); // So that the outputs are flipped round.
733     BOOST_TEST_CHECKPOINT("connect splitter[1] to merger[0]");
734     Connect(merger, output, inputTensorInfo, 0, 0);
735     BOOST_TEST_CHECKPOINT("connect merger to output");
736
737     CreateTensorHandles(graph, factory);
738     BOOST_TEST_CHECKPOINT("created tensor handles");
739
740     auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, graph, factory);
741     BOOST_TEST_CHECKPOINT("created splitter workload");
742     auto workloadMerger = MakeAndCheckWorkload<MergerWorkload>(*merger, graph, factory);
743     BOOST_TEST_CHECKPOINT("created merger workload");
744
745     return {std::move(workloadSplitter), std::move(workloadMerger)};
746 }
747
748
749 /// This function constructs a graph with a splitter with two outputs. Each of the outputs is then
750 /// connected to two different activation layers
751 template<typename SplitterWorkload, typename ActivationWorkload, armnn::DataType DataType>
752 void CreateSplitterMultipleInputsOneOutputWorkloadTest(armnn::IWorkloadFactory& factory, armnn::Graph& graph,
753                                  std::unique_ptr<SplitterWorkload>& wlSplitter,
754                                  std::unique_ptr<ActivationWorkload>& wlActiv0_0,
755                                  std::unique_ptr<ActivationWorkload>& wlActiv0_1,
756                                  std::unique_ptr<ActivationWorkload>& wlActiv1_0,
757                                  std::unique_ptr<ActivationWorkload>& wlActiv1_1)
758 {
759     armnn::TensorInfo inputTensorInfo ({ 1, 3, 100, 50 }, DataType);
760     armnn::TensorInfo splitTensorInfo1({ 1, 1, 100, 50 }, DataType);
761     armnn::TensorInfo splitTensorInfo2({ 1, 2, 100, 50 }, DataType);
762
763     //Constructs the graph.
764     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
765
766     armnn::ViewsDescriptor splitterViews(2);
767
768     splitterViews.SetViewOriginCoord(0, 0, 0);
769     splitterViews.SetViewOriginCoord(0, 1, 0);
770     splitterViews.SetViewOriginCoord(0, 2, 0);
771     splitterViews.SetViewOriginCoord(0, 3, 0);
772
773     splitterViews.SetViewOriginCoord(1, 0, 0);
774     splitterViews.SetViewOriginCoord(1, 1, 1);
775     splitterViews.SetViewOriginCoord(1, 2, 0);
776     splitterViews.SetViewOriginCoord(1, 3, 0);
777
778     Layer* const splitter = graph.AddLayer<SplitterLayer>(splitterViews, "splitter");
779
780     armnn::ActivationDescriptor activationDesc;
781
782     Layer* const activ0_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_0");
783     Layer* const activ0_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ0_1");
784     Layer* const activ1_0 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_0");
785     Layer* const activ1_1 = graph.AddLayer<ActivationLayer>(activationDesc, "activ1_1");
786
787     Layer* const output1 = graph.AddLayer<OutputLayer>(1, "output1");
788     Layer* const output2 = graph.AddLayer<OutputLayer>(2, "output2");
789     Layer* const output3 = graph.AddLayer<OutputLayer>(3, "output3");
790     Layer* const output4 = graph.AddLayer<OutputLayer>(4, "output4");
791
792     // Adds connections.
793     Connect(input, splitter, inputTensorInfo, 0, 0);
794     Connect(splitter, activ0_0, splitTensorInfo1, 0, 0);
795     Connect(splitter, activ0_1, splitTensorInfo1, 0, 0);
796
797     Connect(splitter, activ1_0, splitTensorInfo2, 1, 0);
798     Connect(splitter, activ1_1, splitTensorInfo2, 1, 0);
799
800     Connect(activ0_0, output1, splitTensorInfo1, 0, 0);
801     Connect(activ0_1, output2, splitTensorInfo1, 0, 0);
802     Connect(activ1_0, output3, splitTensorInfo2, 0, 0);
803     Connect(activ1_1, output4, splitTensorInfo2, 0, 0);
804
805     CreateTensorHandles(graph, factory);
806
807     auto workloadSplitter = MakeAndCheckWorkload<SplitterWorkload>(*splitter, graph, factory);
808     auto workloadActiv0_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_0, graph, factory);
809     auto workloadActiv0_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ0_1, graph, factory);
810     auto workloadActiv1_0 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_0, graph, factory);
811     auto workloadActiv1_1 = MakeAndCheckWorkload<ActivationWorkload>(*activ1_1, graph, factory);
812
813     wlSplitter = std::move(workloadSplitter);
814     wlActiv0_0 = std::move(workloadActiv0_0);
815     wlActiv0_1 = std::move(workloadActiv0_1);
816     wlActiv1_0 = std::move(workloadActiv1_0);
817     wlActiv1_1 = std::move(workloadActiv1_1);
818 }
819
820 template <typename ResizeBilinearWorkload, armnn::DataType DataType>
821 std::unique_ptr<ResizeBilinearWorkload> CreateResizeBilinearWorkloadTest(armnn::IWorkloadFactory& factory,
822                                                                          armnn::Graph& graph,
823                                                                          DataLayout dataLayout = DataLayout::NCHW)
824 {
825     TensorShape inputShape;
826     TensorShape outputShape;
827     unsigned int heightIndex;
828     unsigned int widthIndex;
829
830     switch (dataLayout) {
831         case DataLayout::NHWC:
832             inputShape = { 2, 4, 4, 3 };
833             outputShape = { 2, 2, 2, 3 };
834             heightIndex = 1;
835             widthIndex = 2;
836             break;
837         default: // NCHW
838             inputShape = { 2, 3, 4, 4 };
839             outputShape = { 2, 3, 2, 2 };
840             heightIndex = 2;
841             widthIndex = 3;
842     }
843
844     // Creates the layer we're testing.
845     ResizeBilinearDescriptor resizeDesc;
846     resizeDesc.m_TargetWidth = outputShape[widthIndex];
847     resizeDesc.m_TargetHeight = outputShape[heightIndex];
848     resizeDesc.m_DataLayout = dataLayout;
849     Layer* const layer = graph.AddLayer<ResizeBilinearLayer>(resizeDesc, "layer");
850
851     // Creates extra layers.
852     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
853     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
854
855     // Connects up.
856     armnn::TensorInfo inputTensorInfo(inputShape, DataType);
857     armnn::TensorInfo outputTensorInfo(outputShape, DataType);
858     Connect(input, layer, inputTensorInfo);
859     Connect(layer, output, outputTensorInfo);
860     CreateTensorHandles(graph, factory);
861
862     // Makes the workload and checks it.
863     auto workload = MakeAndCheckWorkload<ResizeBilinearWorkload>(*layer, graph, factory);
864
865     ResizeBilinearQueueDescriptor queueDescriptor = workload->GetData();
866     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
867     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
868     BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
869
870     // Returns so we can do extra, backend-specific tests.
871     return workload;
872 }
873
874 template <typename L2NormalizationWorkload, armnn::DataType DataType>
875 std::unique_ptr<L2NormalizationWorkload> CreateL2NormalizationWorkloadTest(armnn::IWorkloadFactory& factory,
876     armnn::Graph& graph, DataLayout dataLayout = DataLayout::NCHW)
877 {
878     // Creates the layer we're testing.
879     L2NormalizationDescriptor layerDesc;
880     layerDesc.m_DataLayout = dataLayout;
881
882     Layer* const layer = graph.AddLayer<L2NormalizationLayer>(layerDesc, "l2norm");
883
884     // Creates extra layers.
885     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
886     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
887
888     // Connects up.
889     armnn::TensorInfo inputTensorInfo({ 5, 20, 50, 67 }, DataType);
890     armnn::TensorInfo outputTensorInfo({ 5, 20, 50, 67 }, DataType);
891     Connect(input, layer, inputTensorInfo);
892     Connect(layer, output, outputTensorInfo);
893     CreateTensorHandles(graph, factory);
894
895     // Makes the workload and checks it.
896     auto workload = MakeAndCheckWorkload<L2NormalizationWorkload>(*layer, graph, factory);
897
898     L2NormalizationQueueDescriptor queueDescriptor = workload->GetData();
899     BOOST_TEST((queueDescriptor.m_Parameters.m_DataLayout == dataLayout));
900     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
901     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
902
903     // Returns so we can do extra, backend-specific tests.
904     return workload;
905 }
906
907 template <typename ReshapeWorkload, armnn::DataType DataType>
908 std::unique_ptr<ReshapeWorkload> CreateReshapeWorkloadTest(armnn::IWorkloadFactory& factory,
909     armnn::Graph& graph)
910 {
911     // Creates the layer we're testing.
912     TensorShape outputShape({ 1, 4 });
913     ReshapeDescriptor reshapeDesc;
914     reshapeDesc.m_TargetShape = outputShape;
915     Layer* const layer = graph.AddLayer<ReshapeLayer>(reshapeDesc, "layer");
916
917     // Creates extra layers.
918     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
919     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
920
921     // Connects up.
922     armnn::TensorInfo inputTensorInfo({ 4, 1 }, DataType);
923     armnn::TensorInfo outputTensorInfo(outputShape, DataType);
924     Connect(input, layer, inputTensorInfo);
925     Connect(layer, output, outputTensorInfo);
926     CreateTensorHandles(graph, factory);
927
928     // Makes the workload and checks it.
929     auto workload = MakeAndCheckWorkload<ReshapeWorkload>(*layer, graph, factory);
930
931     ReshapeQueueDescriptor queueDescriptor = workload->GetData();
932     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
933     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
934
935     // Returns so we can do extra, backend-specific tests.
936     return workload;
937 }
938
939 template <typename ConvertFp16ToFp32Float32Workload>
940 std::unique_ptr<ConvertFp16ToFp32Float32Workload> CreateConvertFp16ToFp32WorkloadTest(
941     armnn::IWorkloadFactory& factory, armnn::Graph& graph)
942 {
943     // Creates the layer we're testing.
944     ConvertFp16ToFp32Layer* const layer = graph.AddLayer<ConvertFp16ToFp32Layer>("Fp16ToFp32Converter");
945
946     // Creates extra layers.
947     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
948     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
949
950     // Connects up.
951     armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
952     armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
953     Connect(input, layer, inputTensorInfo);
954     Connect(layer, output, outputTensorInfo);
955     CreateTensorHandles(graph, factory);
956
957     // Makes the workload and checks it.
958     auto workload = MakeAndCheckWorkload<ConvertFp16ToFp32Float32Workload>(*layer, graph, factory);
959
960     ConvertFp16ToFp32QueueDescriptor queueDescriptor = workload->GetData();
961     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
962     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
963
964     // Returns so we can do extra, backend-specific tests.
965     return workload;
966 }
967
968 template <typename ConvertFp32ToFp16Float16Workload>
969 std::unique_ptr<ConvertFp32ToFp16Float16Workload> CreateConvertFp32ToFp16WorkloadTest(
970     armnn::IWorkloadFactory& factory, armnn::Graph& graph)
971 {
972     // Creates the layer we're testing.
973     ConvertFp32ToFp16Layer* const layer = graph.AddLayer<ConvertFp32ToFp16Layer>("Fp32ToFp16Converter");
974
975     // Creates extra layers.
976     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
977     Layer* const output = graph.AddLayer<OutputLayer>(0, "output");
978
979     // Connects up.
980     armnn::TensorInfo inputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float32);
981     armnn::TensorInfo outputTensorInfo({1, 3, 2, 3}, armnn::DataType::Float16);
982     Connect(input, layer, inputTensorInfo);
983     Connect(layer, output, outputTensorInfo);
984     CreateTensorHandles(graph, factory);
985
986     // Makes the workload and checks it.
987     auto workload = MakeAndCheckWorkload<ConvertFp32ToFp16Float16Workload>(*layer, graph, factory);
988
989     ConvertFp32ToFp16QueueDescriptor queueDescriptor = workload->GetData();
990     BOOST_TEST(queueDescriptor.m_Inputs.size() == 1);
991     BOOST_TEST(queueDescriptor.m_Outputs.size() == 1);
992
993     // Returns so we can do extra, backend-specific tests.
994     return workload;
995 }
996
997 }