Release 18.08
[platform/upstream/armnn.git] / src / armnn / backends / test / Conv2dTestImpl.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 <armnn/ArmNN.hpp>
8 #include <armnn/Tensor.hpp>
9 #include <armnn/TypesUtils.hpp>
10 #include <backends/WorkloadInfo.hpp>
11
12 #include "test/TensorHelpers.hpp"
13 #include "QuantizeHelper.hpp"
14
15 #include "backends/CpuTensorHandle.hpp"
16 #include "backends/WorkloadFactory.hpp"
17
18 // Mapping from input type to bias type for fully connected layers.
19 // float => float, uint8_t => int32_t
20 template<typename T>
21 struct FullyConnectedBiasTypeForInputType;
22
23 template<>
24 struct FullyConnectedBiasTypeForInputType<float>
25 {
26     using Type = float;
27 };
28
29 template<>
30 struct FullyConnectedBiasTypeForInputType<uint8_t>
31 {
32     using Type = int32_t;
33 };
34
35 // Modifies a std::vector in-place using a specified bias.
36 template<typename T, typename B>
37 void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
38     const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
39 {
40     BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
41                      "Invalid type and parameter combination.");
42     BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
43                      "Invalid type and parameter combination.");
44
45     // Note we need to dequantize and re-quantize the image value and the bias.
46     for (uint32_t i = 0; i < bias.size(); ++i)
47     {
48         float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
49         for (uint32_t y = 0; y < h; ++y)
50         {
51             for (uint32_t x = 0; x < w; ++x)
52             {
53                 uint32_t offset = (i * h + y) * w + x;
54                 BOOST_ASSERT(offset < v.size());
55                 T& outRef = v[offset];
56                 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
57                 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
58             }
59         }
60     }
61 }
62
63 template<typename T, typename B>
64 LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
65                                                   const boost::multi_array<T, 4>& input,
66                                                   const boost::multi_array<T, 4>& kernel,
67                                                   const boost::multi_array<B, 1>& bias,
68                                                   const boost::multi_array<T, 4>& outputExpected,
69                                                   float qScale,
70                                                   int32_t qOffset,
71                                                   uint32_t padLeft = 0,
72                                                   uint32_t padTop = 0,
73                                                   uint32_t padRight = 0,
74                                                   uint32_t padBottom = 0)
75 {
76     unsigned int inputHeight   = boost::numeric_cast<unsigned int>(input.shape()[2]);
77     unsigned int inputWidth    = boost::numeric_cast<unsigned int>(input.shape()[3]);
78     unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
79     unsigned int inputNum      = boost::numeric_cast<unsigned int>(input.shape()[0]);
80
81     unsigned int outputHeight   = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
82     unsigned int outputWidth    = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
83     unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
84     unsigned int outputNum      = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
85
86     unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
87     unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
88     unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
89     unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
90
91     bool biasEnabled = bias.size() > 0;
92
93     // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
94     BOOST_ASSERT(inputNum == 1);
95     BOOST_ASSERT(outputNum == 1);
96
97     // If a bias is used, its size must equal the number of output channels.
98     BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
99
100
101     // Note these tensors will use two (identical) batches.
102     armnn::TensorInfo inputTensorInfo({2*inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
103     armnn::TensorInfo outputTensorInfo({2*outputNum, outputChannels, outputHeight, outputWidth},
104         armnn::GetDataType<T>());
105     armnn::TensorInfo kernelDesc({kernelDepthMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
106     armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
107
108     // Set quantization parameters if the requested type is a quantized type.
109     if(armnn::IsQuantizedType<T>())
110     {
111         inputTensorInfo.SetQuantizationScale(qScale);
112         inputTensorInfo.SetQuantizationOffset(qOffset);
113         outputTensorInfo.SetQuantizationScale(qScale);
114         outputTensorInfo.SetQuantizationOffset(qOffset);
115         kernelDesc.SetQuantizationScale(qScale);
116         kernelDesc.SetQuantizationOffset(qOffset);
117         biasDesc.SetQuantizationScale(qScale*qScale);
118         biasDesc.SetQuantizationOffset(0);
119     }
120
121     LayerTestResult<T, 4> ret(outputTensorInfo);
122
123     // Construct input data - two batches of the same input image.
124     std::vector<T> inputImage;
125     inputImage.assign(input.data(), input.data() + 1*inputChannels*inputHeight*inputWidth);
126     std::vector<T> inputData;
127     inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
128     inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
129     auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
130
131     std::vector<T> outputImage;
132     outputImage.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
133
134     // Apply bias to output image if it is enabled.
135     if(biasEnabled)
136     {
137         std::vector<T> biasV;
138         biasV.assign(bias.data(), bias.data() + outputChannels);
139         ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
140             biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
141             outputWidth, outputHeight);
142     }
143
144     // Construct expected output data - two identical images.
145     std::vector<T> outputData;
146     outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
147     outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
148
149     ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
150
151     // Todo: nontrivial padding and strides.
152     uint32_t                    strideX  = 1;
153     uint32_t                    strideY  = 1;
154
155     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
156     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
157
158     armnn::Convolution2dQueueDescriptor data;
159     armnn::WorkloadInfo info;
160     armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
161     armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
162
163     AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
164
165     if(biasEnabled)
166     {
167         AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
168     }
169
170     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
171     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
172
173     data.m_Weight = &weightsTensor;
174     data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
175     data.m_Parameters.m_StrideX = strideX;
176     data.m_Parameters.m_StrideY = strideY;
177     data.m_Parameters.m_PadLeft = padLeft;
178     data.m_Parameters.m_PadRight = padRight;
179     data.m_Parameters.m_PadTop = padTop;
180     data.m_Parameters.m_PadBottom = padBottom;
181     data.m_Parameters.m_BiasEnabled = biasEnabled;
182
183     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
184     inputHandle->Allocate();
185     outputHandle->Allocate();
186
187     CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
188
189     workloadFactory.Finalize();
190     workload->Execute();
191
192     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
193
194     return ret;
195 }
196
197 template<typename T, typename B>
198 LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
199                                                                const boost::multi_array<T, 4>& input,
200                                                                const boost::multi_array<T, 4>& kernel,
201                                                                const boost::multi_array<B, 1>& bias,
202                                                                const boost::multi_array<T, 4>& outputExpected,
203                                                                float qScale,
204                                                                int32_t qOffset,
205                                                                uint32_t padLeft = 0,
206                                                                uint32_t padTop = 0,
207                                                                uint32_t padRight = 0,
208                                                                uint32_t padBottom = 0,
209                                                                uint32_t strideX = 1,
210                                                                uint32_t strideY = 1)
211 {
212     unsigned int inputNum       = boost::numeric_cast<unsigned int>(input.shape()[0]);
213     unsigned int inputChannels  = boost::numeric_cast<unsigned int>(input.shape()[1]);
214     unsigned int inputHeight    = boost::numeric_cast<unsigned int>(input.shape()[2]);
215     unsigned int inputWidth     = boost::numeric_cast<unsigned int>(input.shape()[3]);
216     unsigned int kernelChanMul  = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
217     unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
218     unsigned int kernelHeight   = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
219     unsigned int kernelWidth    = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
220     unsigned int outputNum      = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
221     unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
222     unsigned int outputHeight   = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
223     unsigned int outputWidth    = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
224
225     // If a bias is used, its size must equal the number of output channels.
226     bool biasEnabled = bias.size() > 0;
227     BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
228
229     // Creates the tensors.
230     armnn::TensorInfo inputTensorInfo({inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
231     armnn::TensorInfo outputTensorInfo({outputNum, outputChannels, outputHeight, outputWidth},
232                                        armnn::GetDataType<T>());
233     armnn::TensorInfo kernelDesc({kernelChanMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
234     armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
235
236     // Set quantization parameters if the requested type is a quantized type.
237     if (armnn::IsQuantizedType<T>())
238     {
239         inputTensorInfo.SetQuantizationScale(qScale);
240         inputTensorInfo.SetQuantizationOffset(qOffset);
241         outputTensorInfo.SetQuantizationScale(qScale);
242         outputTensorInfo.SetQuantizationOffset(qOffset);
243         kernelDesc.SetQuantizationScale(qScale);
244         kernelDesc.SetQuantizationOffset(qOffset);
245         biasDesc.SetQuantizationScale(qScale*qScale);
246         biasDesc.SetQuantizationOffset(0);
247     }
248
249     // Construct the input data.
250     std::vector<T> inputData;
251     inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
252     auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
253
254     // Construct the output data, with bias applied, as appropriate.
255     std::vector<T> outputData;
256     outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
257     if (biasEnabled)
258     {
259         std::vector<T> biasV;
260         biasV.assign(bias.data(), bias.data() + outputChannels);
261         ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
262             biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
263             outputWidth, outputHeight);
264     }
265
266     LayerTestResult<T, 4> ret(outputTensorInfo);
267     ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
268
269     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
270     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
271
272     armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
273     AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
274
275     armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
276     if (biasEnabled)
277     {
278         AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
279     }
280
281     armnn::DepthwiseConvolution2dQueueDescriptor data;
282     data.m_Weight = &weightsTensor;
283     data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
284     data.m_Parameters.m_StrideX = strideX;
285     data.m_Parameters.m_StrideY = strideY;
286     data.m_Parameters.m_PadLeft = padLeft;
287     data.m_Parameters.m_PadRight = padRight;
288     data.m_Parameters.m_PadTop = padTop;
289     data.m_Parameters.m_PadBottom = padBottom;
290     data.m_Parameters.m_BiasEnabled = biasEnabled;
291
292     armnn::WorkloadInfo info;
293     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
294     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
295
296     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
297     inputHandle->Allocate();
298     outputHandle->Allocate();
299
300     CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
301
302     workloadFactory.Finalize();
303     workload->Execute();
304
305     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
306
307     return ret;
308 }
309
310 template<typename T, typename B>
311 LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
312                                                               float qScale,
313                                                               int32_t qOffset,
314                                                               bool biasEnabled)
315 {
316     unsigned int inputHeight = 3;
317     unsigned int inputWidth = 3;
318     unsigned int inputChannels = 2;
319     unsigned int inputNum = 1;
320
321     unsigned int kernelHeight = 3;
322     unsigned int kernelWidth = 3;
323     unsigned int kernelChannels = inputChannels;
324
325     unsigned int outputHeight = 1;
326     unsigned int outputWidth = 1;
327     unsigned int outputChannels = kernelChannels;
328     unsigned int outputNum = inputNum;
329
330     armnn::TensorInfo inputTensorInfo({ inputNum, inputChannels, inputHeight, inputWidth }, armnn::GetDataType<T>());
331     armnn::TensorInfo outputTensorInfo({ outputNum, outputChannels, outputHeight, outputWidth },
332         armnn::GetDataType<T>());
333     armnn::TensorInfo kernelDesc({ 1, outputChannels, kernelHeight, kernelWidth }, armnn::GetDataType<T>());
334     armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
335
336     // Set quantization parameters if the requested type is a quantized type.
337     if(armnn::IsQuantizedType<T>())
338     {
339         inputTensorInfo.SetQuantizationScale(qScale);
340         inputTensorInfo.SetQuantizationOffset(qOffset);
341         outputTensorInfo.SetQuantizationScale(qScale);
342         outputTensorInfo.SetQuantizationOffset(qOffset);
343         kernelDesc.SetQuantizationScale(qScale);
344         kernelDesc.SetQuantizationOffset(qOffset);
345         biasDesc.SetQuantizationScale(qScale*qScale);
346         biasDesc.SetQuantizationOffset(0);
347     }
348
349     auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
350         QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
351             1.f, 2.f, 1.f,
352             2.f, 1.f, 2.f,
353             1.f, 2.f, 1.f,
354
355             1.f, 2.f, 1.f,
356             2.f, 1.f, 2.f,
357             1.f, 2.f, 1.f,
358         })));
359
360     std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
361                                             {0, 2}));
362     auto bias = MakeTensor<B, 1>(biasDesc, biasV);
363
364     auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
365         QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
366             1.f, 0.f,  1.f,
367             0.f, 0.f,  0.f,
368            -1.f, 0.f, -1.f,
369
370             1.f, 0.f,  1.f,
371             0.f, 0.f,  0.f,
372            -1.f, 0.f, -1.f,
373         })));
374
375     // Manually calculated.
376     std::vector<T> outputImage(
377         QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
378                            outputTensorInfo.GetQuantizationOffset(),
379                            {0.f, 0.f})
380     );
381
382     // Optionally apply bias to output image.
383     if(biasEnabled)
384     {
385         ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
386                   biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
387                   outputWidth, outputHeight);
388     }
389
390     LayerTestResult<T, 4> ret(outputTensorInfo);
391     ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
392
393     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
394     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
395
396     armnn::DepthwiseConvolution2dQueueDescriptor data;
397     armnn::WorkloadInfo info;
398     armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
399     armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
400
401     AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
402     AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
403
404     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
405     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
406
407     data.m_Weight = &weightsTensor;
408     data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
409     data.m_Parameters.m_StrideX = 1;
410     data.m_Parameters.m_StrideY = 1;
411     data.m_Parameters.m_PadLeft = 0;
412     data.m_Parameters.m_PadRight = 0;
413     data.m_Parameters.m_PadTop = 0;
414     data.m_Parameters.m_PadBottom = 0;
415     data.m_Parameters.m_BiasEnabled = biasEnabled;
416
417     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
418     inputHandle->Allocate();
419     outputHandle->Allocate();
420
421     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
422
423     workloadFactory.Finalize();
424     workload->Execute();
425
426     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
427
428     return ret;
429 }
430
431 template<typename T, typename B>
432 LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
433                                                      float qScale,
434                                                      int32_t qOffset,
435                                                      bool biasEnabled)
436 {
437     unsigned int depthMultiplier = 2;
438
439     unsigned int inputHeight    = 8;
440     unsigned int inputWidth     = 16;
441     unsigned int inputChannels  = 2;
442     unsigned int inputBatchSize = 1;
443
444     unsigned int kernelHeight = 5;
445     unsigned int kernelWidth  = 3;
446
447     unsigned int outputHeight    = inputHeight - kernelHeight + 1 + 2;
448     unsigned int outputWidth     = (inputWidth - kernelWidth + 1)/2;
449     unsigned int outputChannels  = inputChannels * depthMultiplier;
450     unsigned int outputBatchSize = inputBatchSize;
451
452     armnn::TensorInfo inputTensorInfo({inputBatchSize, inputChannels, inputHeight, inputWidth},
453                                       armnn::GetDataType<T>());
454     armnn::TensorInfo outputTensorInfo({outputBatchSize, outputChannels, outputHeight, outputWidth},
455         armnn::GetDataType<T>());
456     armnn::TensorInfo kernelDesc({depthMultiplier, inputChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
457     armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
458
459     // Set quantization parameters if the requested type is a quantized type.
460     if(armnn::IsQuantizedType<T>())
461     {
462         inputTensorInfo.SetQuantizationScale(qScale);
463         inputTensorInfo.SetQuantizationOffset(qOffset);
464         outputTensorInfo.SetQuantizationScale(qScale);
465         outputTensorInfo.SetQuantizationOffset(qOffset);
466         kernelDesc.SetQuantizationScale(qScale);
467         kernelDesc.SetQuantizationOffset(qOffset);
468         biasDesc.SetQuantizationScale(qScale*qScale);
469         biasDesc.SetQuantizationOffset(0);
470     }
471
472     auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
473         QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
474             0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
475             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
476             0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
477             0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
478             0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
479             0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
480             0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
481             0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
482             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
483             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
484             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
485             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
486             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
487             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
488             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
489             0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
490         })));
491
492     std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
493         {0, 2, 1, -1}));
494     auto bias = MakeTensor<B, 1>(biasDesc, biasV);
495
496     auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
497         QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
498             1, 1, 1,
499             1, -1, 1,
500             1, 1, 1,
501             1, 1, 1,
502             1, 1, 1,
503
504             2, 2, 2,
505             2, 2, 2,
506             2, 2, 2,
507             2, 2, 2,
508             2, 2, 2,
509
510             0, 0, 0,
511             0, -1, 0,
512             0, 0, 0,
513             0, 0, 0,
514             0, 0, 0,
515
516             0, 0, 0,
517             0, 0, 0,
518             0, 1, 0,
519             0, 0, 0,
520             0, 0, 0
521         })));
522
523     // Manually calculated.
524     std::vector<T> outputImage = std::vector<T>(
525         QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
526             3.5f,  3.5f,  3.5f,  3.5f,  3.5f,  3.5f,  3.5f,
527             6.0f,  6.0f,  6.0f,  6.0f,  6.0f,  6.0f,  6.0f,
528             5.0f,  5.0f,  5.0f,  5.0f,  5.0f,  5.0f,  5.0f,
529             6.5f,  6.5f,  6.5f,  6.5f,  6.5f,  6.5f,  6.5f,
530             6.5f,  6.5f,  6.5f,  6.5f,  6.5f,  6.5f,  6.5f,
531             5.0f,  5.0f,  5.0f,  5.0f,  5.0f,  5.0f,  5.0f,
532
533             -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
534             0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
535             -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
536             -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
537             -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
538             -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
539
540             8.0f,  8.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
541             10.0f, 10.0f, 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
542             10.0f, 10.0f, 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
543             10.0f, 10.0f, 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
544             10.0f, 10.0f, 0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
545             8.0f,  8.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
546
547             0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
548             0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
549             0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
550             0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
551             0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,
552             0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f,  0.0f
553         }));
554
555     // Optionally apply bias to output image.
556     if(biasEnabled)
557     {
558         ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
559             biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
560             outputWidth, outputHeight);
561     }
562
563     LayerTestResult<T, 4> ret(outputTensorInfo);
564     ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
565
566     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
567     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
568
569     armnn::DepthwiseConvolution2dQueueDescriptor data;
570     armnn::WorkloadInfo info;
571     armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
572     armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
573
574     AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
575     AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
576
577     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
578     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
579
580     data.m_Weight = &weightsTensor;
581     data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
582     data.m_Parameters.m_StrideX = 2;
583     data.m_Parameters.m_StrideY = 1;
584     data.m_Parameters.m_PadLeft = 0;
585     data.m_Parameters.m_PadRight = 0;
586     data.m_Parameters.m_PadTop = 1;
587     data.m_Parameters.m_PadBottom = 1;
588     data.m_Parameters.m_BiasEnabled = biasEnabled;
589
590     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
591     inputHandle->Allocate();
592     outputHandle->Allocate();
593
594     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
595
596     workloadFactory.Finalize();
597     workload->Execute();
598
599     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
600
601     return ret;
602 }
603
604 template<typename T>
605 LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
606                                            float qScale,
607                                            int32_t qOffset,
608                                            bool biasEnabled)
609 {
610     using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
611
612     // Until we have a specialist 1D convolution layer, we can fake one using
613     // 2D convolution with the final dimension set to 1.
614     // I don't anticipate this being particularly slow, given that convolution is implemented
615     // as a matrix multiplication, at which point dimension doesn't matter.
616
617     unsigned int batchSize      = 1;
618     unsigned int inputChannels  = 2;
619     unsigned int outputChannels = 3;
620     unsigned int inputSize      = 5; // The 1D size (could view as 'width' or 'height').
621     unsigned int kernelSize     = 3;
622     unsigned int padSize        = 2;
623     unsigned int stride         = 1;
624     unsigned int outputSize     = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
625
626     armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
627     armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
628     armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
629     armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
630
631     // Set quantization parameters if the requested type is a quantized type.
632     if(armnn::IsQuantizedType<T>())
633     {
634         inputInfo.SetQuantizationScale(qScale);
635         inputInfo.SetQuantizationOffset(qOffset);
636         outputInfo.SetQuantizationScale(qScale);
637         outputInfo.SetQuantizationOffset(qOffset);
638         kernelInfo.SetQuantizationScale(qScale);
639         kernelInfo.SetQuantizationOffset(qOffset);
640         biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
641         biasInfo.SetQuantizationOffset(0);
642     }
643
644     std::vector<T> inputData(
645         QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
646             5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
647             -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
648         }));
649
650     std::vector<T> kernelData(
651         QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
652             1.0f, 0.0f, 0.0f,
653             0.0f, 2.0f, -1.5f,
654
655             0.0f, 0.0f, 0.0f,
656             0.2f, 0.2f, 0.2f,
657
658             0.5f, 0.0f, 0.5f,
659             0.0f, -1.0f, 0.0f
660         }));
661
662     std::vector<B> biasData(
663         QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
664             1.0f, 0.0f, 0.0f
665         }));
666
667     std::vector<T> outputData(
668         QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
669             4.5f, -10.8f, 5.0f + 6.4f - 7.5f, -2.0f + 10.0f -3.0f, 2.5f + 4.0f - 4.5f, 6.0f, 1.0f,
670             -0.6f, -0.6f + 0.64f, -0.6f + 0.64f + 1.0f, 0.64f + 1.0f + 0.4f, 1.0f + 0.4f + 0.6f, 0.4f + 0.6f, 0.6f,
671             2.5f, -1.0f + 3.0f, 1.25f - 3.2f + 2.5f, -1.0f - 5.0f, 1.25f + 0.5f - 2.0f, -3.0f, 0.5f
672         }));
673
674     // Optionally apply bias to output image.
675     if(biasEnabled)
676     {
677         ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
678             biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
679             1, outputSize);
680     }
681
682     std::unique_ptr<armnn::ITensorHandle> inputHandle  = workloadFactory.CreateTensorHandle(inputInfo);
683     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
684
685     armnn::Convolution2dQueueDescriptor data;
686     armnn::WorkloadInfo info;
687     armnn::ScopedCpuTensorHandle         weightsTensor(kernelInfo);
688     armnn::ScopedCpuTensorHandle         biasTensor(biasInfo);
689
690     AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
691     AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
692
693     AddInputToWorkload(data, info, inputInfo, inputHandle.get());
694     AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
695
696     data.m_Weight         = &weightsTensor;
697     data.m_Bias           = &biasTensor;
698     data.m_Parameters.m_StrideX        = 1;
699     data.m_Parameters.m_StrideY        = stride;
700     data.m_Parameters.m_PadLeft        = 0;
701     data.m_Parameters.m_PadRight       = 0;
702     data.m_Parameters.m_PadTop         = padSize;
703     data.m_Parameters.m_PadBottom      = padSize;
704     data.m_Parameters.m_BiasEnabled    = biasEnabled;
705
706     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
707     inputHandle->Allocate();
708     outputHandle->Allocate();
709
710     CopyDataToITensorHandle(inputHandle.get(), inputData.data());
711
712     workloadFactory.Finalize();
713     workload->Execute();
714
715     // Output
716     LayerTestResult<T,4> ret(outputInfo);
717     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
718     ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
719     return ret;
720 }
721
722
723
724 template<typename T>
725 LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
726                                                 armnn::IWorkloadFactory& refWorkloadFactory)
727 {
728     unsigned int inputHeight   = 8;
729     unsigned int inputWidth    = 16;
730     unsigned int inputChannels = 3;
731     unsigned int inputNum      = 5;
732
733     unsigned int kernelHeight = 3;
734     unsigned int kernelWidth  = 3;
735
736     unsigned int strideX = 2;
737     unsigned int strideY = 3;
738     unsigned int padX    = 1;
739     unsigned int padY    = 1;
740
741     unsigned int outputNum      = inputNum;
742     unsigned int outputChannels = 2;
743     unsigned int outputHeight   = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
744     unsigned int outputWidth    = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
745
746     armnn::TensorInfo inputTensorInfo;
747     armnn::TensorInfo outputTensorInfo;
748     armnn::TensorInfo kernelDesc;
749     armnn::TensorInfo biasDesc;
750
751     unsigned int inputShape[]    = {inputNum, inputChannels, inputHeight, inputWidth};
752     unsigned int outputShape[]   = {outputNum, outputChannels, outputHeight, outputWidth};
753     unsigned int kernelShape[]   = {outputChannels, inputChannels, kernelHeight, kernelWidth};
754     unsigned int biasShape[]     = {outputChannels};
755
756     inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
757     outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
758     kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
759     biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
760
761     LayerTestResult<T,4> ret(outputTensorInfo);
762
763     auto input  = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
764     auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
765     auto bias   = MakeRandomTensor<T, 1>(biasDesc, 1028);
766
767     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
768     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
769
770     armnn::Convolution2dQueueDescriptor data;
771     armnn::WorkloadInfo info;
772     armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
773     armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
774
775     AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
776     AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
777
778     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
779     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
780     data.m_Weight = &weightsTensor;
781     data.m_Bias = &biasTensor;
782     data.m_Parameters.m_StrideX = strideX;
783     data.m_Parameters.m_StrideY = strideY;
784     data.m_Parameters.m_PadLeft = padX;
785     data.m_Parameters.m_PadRight = padX;
786     data.m_Parameters.m_PadTop = padY;
787     data.m_Parameters.m_PadBottom = padY;
788     data.m_Parameters.m_BiasEnabled = true;
789
790     std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
791     std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
792
793     armnn::Convolution2dQueueDescriptor refData = data;
794     armnn::WorkloadInfo               refInfo = info;
795     SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
796     SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
797
798     std::unique_ptr<armnn::IWorkload> workload  = workloadFactory.CreateConvolution2d(data, info);
799     std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
800
801     outputHandleRef->Allocate();
802     inputHandleRef->Allocate();
803
804     inputHandle->Allocate();
805     outputHandle->Allocate();
806
807     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
808     CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
809
810     workloadFactory.Finalize();
811     workload->Execute();
812     refWorkloadFactory.Finalize();
813     workloadRef->Execute();
814
815     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
816     CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
817
818     return ret;
819 }
820
821 template<typename T>
822 LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
823     armnn::IWorkloadFactory& refWorkloadFactory)
824 {
825     unsigned int inputHeight = 8;
826     unsigned int inputWidth = 16;
827     unsigned int inputChannels = 3;
828     unsigned int inputNum = 5;
829
830     unsigned int kernelHeight = 3;
831     unsigned int kernelWidth = 3;
832     unsigned int channelMultiplier = 1;
833
834     unsigned int strideX = 2;
835     unsigned int strideY = 3;
836     unsigned int padX = 1;
837     unsigned int padY = 1;
838
839     unsigned int outputNum = inputNum;
840     unsigned int outputChannels = inputChannels * channelMultiplier;
841     unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
842     unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
843
844     armnn::TensorInfo inputTensorInfo;
845     armnn::TensorInfo outputTensorInfo;
846     armnn::TensorInfo kernelDesc;
847     armnn::TensorInfo biasDesc;
848
849     unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
850     unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
851     unsigned int kernelShape[] = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
852     unsigned int biasShape[] = { outputChannels };
853
854     float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
855     float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
856     int32_t qOffset = 0;
857
858     inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
859     outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>(), outputQScale, qOffset);
860     kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
861     biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
862
863     LayerTestResult<T, 4> ret(outputTensorInfo);
864
865     auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
866     auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
867     auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(biasDesc, 1028, 0.0f, 255.0f);
868
869     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
870     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
871
872     armnn::DepthwiseConvolution2dQueueDescriptor data;
873     armnn::WorkloadInfo info;
874     armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
875     armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
876
877     AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
878     AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
879
880     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
881     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
882     data.m_Weight = &weightsTensor;
883     data.m_Bias = &biasTensor;
884     data.m_Parameters.m_StrideX = strideX;
885     data.m_Parameters.m_StrideY = strideY;
886     data.m_Parameters.m_PadLeft = padX;
887     data.m_Parameters.m_PadRight = padX;
888     data.m_Parameters.m_PadTop = padY;
889     data.m_Parameters.m_PadBottom = padY;
890     data.m_Parameters.m_BiasEnabled = true;
891
892     std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
893     std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
894
895     armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
896     armnn::WorkloadInfo refInfo = info;
897     SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
898     SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
899
900     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
901     std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
902
903     outputHandleRef->Allocate();
904     inputHandleRef->Allocate();
905
906     inputHandle->Allocate();
907     outputHandle->Allocate();
908
909     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
910     CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
911
912     workloadFactory.Finalize();
913     workload->Execute();
914     refWorkloadFactory.Finalize();
915     workloadRef->Execute();
916
917     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
918     CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
919
920     return ret;
921 }