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