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