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