blob: 7a3f4525159b48bba90fc8cc6f05674e015ffe3d [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5#pragma once
6
jimfly010a088a62018-10-25 17:05:05 +01007#include <string>
telsoa014fcda012018-03-09 14:13:49 +00008#include <armnn/ArmNN.hpp>
9#include <armnn/Tensor.hpp>
10#include <armnn/TypesUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +000011
David Beckac42efd2018-09-26 17:41:13 +010012#include <test/TensorHelpers.hpp>
telsoa014fcda012018-03-09 14:13:49 +000013#include "QuantizeHelper.hpp"
14
David Beckac42efd2018-09-26 17:41:13 +010015#include <backends/CpuTensorHandle.hpp>
16#include <backends/WorkloadFactory.hpp>
jimfly010a088a62018-10-25 17:05:05 +010017#include "Permute.hpp"
18#include <boost/numeric/conversion/cast.hpp>
telsoa014fcda012018-03-09 14:13:49 +000019
20// Mapping from input type to bias type for fully connected layers.
21// float => float, uint8_t => int32_t
22template<typename T>
23struct FullyConnectedBiasTypeForInputType;
24
25template<>
26struct FullyConnectedBiasTypeForInputType<float>
27{
28 using Type = float;
29};
30
31template<>
32struct FullyConnectedBiasTypeForInputType<uint8_t>
33{
34 using Type = int32_t;
35};
36
telsoa01c577f2c2018-08-31 09:22:23 +010037// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000038template<typename T, typename B>
39void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
40 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
41{
42 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
43 "Invalid type and parameter combination.");
44 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
45 "Invalid type and parameter combination.");
46
telsoa01c577f2c2018-08-31 09:22:23 +010047 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000048 for (uint32_t i = 0; i < bias.size(); ++i)
49 {
50 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
51 for (uint32_t y = 0; y < h; ++y)
52 {
53 for (uint32_t x = 0; x < w; ++x)
54 {
55 uint32_t offset = (i * h + y) * w + x;
56 BOOST_ASSERT(offset < v.size());
57 T& outRef = v[offset];
58 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
59 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
60 }
61 }
62 }
63}
64
jimfly010a088a62018-10-25 17:05:05 +010065template<typename T>
66armnn::TensorInfo GetTensorInfo(unsigned int numberOfBatches,
67 unsigned int numberOfChannels,
68 unsigned int height,
69 unsigned int width,
70 const armnn::DataLayoutIndexed& layout)
71{
72 switch (layout.GetDataLayout())
73 {
74 case armnn::DataLayout::NCHW:
75 return armnn::TensorInfo({numberOfBatches, numberOfChannels, height, width}, armnn::GetDataType<T>());
76 case armnn::DataLayout ::NHWC:
77 return armnn::TensorInfo({numberOfBatches, height, width, numberOfChannels}, armnn::GetDataType<T>());
78 default:
79 throw armnn::InvalidArgumentException("unknown data layout ["
80 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
81 }
82}
83
84
85
telsoa014fcda012018-03-09 14:13:49 +000086template<typename T, typename B>
87LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
jimfly010a088a62018-10-25 17:05:05 +010088 const boost::multi_array<T, 4>& originalInput,
89 const boost::multi_array<T, 4>& originalKernel,
telsoa014fcda012018-03-09 14:13:49 +000090 const boost::multi_array<B, 1>& bias,
jimfly010a088a62018-10-25 17:05:05 +010091 const boost::multi_array<T, 4>& originalOutputExpected,
telsoa014fcda012018-03-09 14:13:49 +000092 float qScale,
93 int32_t qOffset,
jimfly010a088a62018-10-25 17:05:05 +010094 const armnn::DataLayoutIndexed& layout = armnn::DataLayout::NCHW,
telsoa014fcda012018-03-09 14:13:49 +000095 uint32_t padLeft = 0,
96 uint32_t padTop = 0,
97 uint32_t padRight = 0,
98 uint32_t padBottom = 0)
99{
jimfly010a088a62018-10-25 17:05:05 +0100100 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
101 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
102 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
103 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000104
jimfly010a088a62018-10-25 17:05:05 +0100105 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
106 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
107 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
108 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000109
jimfly010a088a62018-10-25 17:05:05 +0100110 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
111 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
112 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
113 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000114
115 bool biasEnabled = bias.size() > 0;
116
telsoa01c577f2c2018-08-31 09:22:23 +0100117 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +0000118 BOOST_ASSERT(inputNum == 1);
119 BOOST_ASSERT(outputNum == 1);
120
telsoa01c577f2c2018-08-31 09:22:23 +0100121 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +0000122 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
123
124
telsoa01c577f2c2018-08-31 09:22:23 +0100125 // Note these tensors will use two (identical) batches.
jimfly010a088a62018-10-25 17:05:05 +0100126 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(2*inputNum, inputChannels, inputHeight, inputWidth, layout);
127 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
128 2*outputNum, outputChannels, outputHeight, outputWidth, layout);
129 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000130 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
131
132 // Set quantization parameters if the requested type is a quantized type.
133 if(armnn::IsQuantizedType<T>())
134 {
135 inputTensorInfo.SetQuantizationScale(qScale);
136 inputTensorInfo.SetQuantizationOffset(qOffset);
137 outputTensorInfo.SetQuantizationScale(qScale);
138 outputTensorInfo.SetQuantizationOffset(qOffset);
139 kernelDesc.SetQuantizationScale(qScale);
140 kernelDesc.SetQuantizationOffset(qOffset);
141 biasDesc.SetQuantizationScale(qScale*qScale);
142 biasDesc.SetQuantizationOffset(0);
143 }
144
145 LayerTestResult<T, 4> ret(outputTensorInfo);
146
telsoa01c577f2c2018-08-31 09:22:23 +0100147 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000148 std::vector<T> inputImage;
jimfly010a088a62018-10-25 17:05:05 +0100149 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000150 std::vector<T> inputData;
151 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
152 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
jimfly010a088a62018-10-25 17:05:05 +0100153
154 // at this point if we require it permute the input data
155 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
156 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
157 {
158 std::vector<T> tmp(inputData.size());
159 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
160 inputData = tmp;
161 }
162
telsoa014fcda012018-03-09 14:13:49 +0000163 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
164
165 std::vector<T> outputImage;
jimfly010a088a62018-10-25 17:05:05 +0100166 outputImage.assign(originalOutputExpected.data(),
167 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000168
telsoa01c577f2c2018-08-31 09:22:23 +0100169 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000170 if(biasEnabled)
171 {
172 std::vector<T> biasV;
173 biasV.assign(bias.data(), bias.data() + outputChannels);
174 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
175 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
176 outputWidth, outputHeight);
177 }
178
telsoa01c577f2c2018-08-31 09:22:23 +0100179 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000180 std::vector<T> outputData;
181 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
182 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
183
jimfly010a088a62018-10-25 17:05:05 +0100184 // at this point if we require it permute the expected output
185 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
186 {
187 std::vector<T> tmp(outputData.size());
188 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
189 outputData = tmp;
190 }
telsoa014fcda012018-03-09 14:13:49 +0000191 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
192
telsoa01c577f2c2018-08-31 09:22:23 +0100193 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000194 uint32_t strideX = 1;
195 uint32_t strideY = 1;
196
197 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
198 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
199
200 armnn::Convolution2dQueueDescriptor data;
201 armnn::WorkloadInfo info;
202 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
203 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
jimfly010a088a62018-10-25 17:05:05 +0100204 // Permute the kernel if necessary
205 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
206 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
207 {
208 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
209 }
telsoa014fcda012018-03-09 14:13:49 +0000210 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
211
212 if(biasEnabled)
213 {
214 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
215 }
216
217 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
218 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
219
220 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100221 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
telsoa014fcda012018-03-09 14:13:49 +0000222 data.m_Parameters.m_StrideX = strideX;
223 data.m_Parameters.m_StrideY = strideY;
224 data.m_Parameters.m_PadLeft = padLeft;
225 data.m_Parameters.m_PadRight = padRight;
226 data.m_Parameters.m_PadTop = padTop;
227 data.m_Parameters.m_PadBottom = padBottom;
228 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly010a088a62018-10-25 17:05:05 +0100229 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000230
231 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
232 inputHandle->Allocate();
233 outputHandle->Allocate();
234
235 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
236
surmeh013537c2c2018-05-18 16:31:43 +0100237 workloadFactory.Finalize();
238 workload->Execute();
239
240 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
241
242 return ret;
243}
244
245template<typename T, typename B>
Francis Murtaghd59116e2018-10-04 16:03:07 +0100246LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
247 const boost::multi_array<T, 4>& input,
248 const boost::multi_array<T, 4>& kernel,
249 const boost::multi_array<B, 1>& bias,
250 const boost::multi_array<T, 4>& outputExpected,
251 armnn::DataLayout dataLayout,
252 float qScale,
253 int32_t qOffset,
254 uint32_t padLeft = 1,
255 uint32_t padTop = 1,
256 uint32_t padRight = 1,
257 uint32_t padBottom = 1,
258 uint32_t strideX = 1,
259 uint32_t strideY = 1)
260{
261 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
262 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
263 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
264 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
265
266 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
267 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
268 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
269 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
270
271 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
272 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
273 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
274 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
275
276 bool biasEnabled = bias.size() > 0;
277
278 // Creates the tensors.
279 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
280 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
281 armnn::GetDataType<T>());
282 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
283 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
284
285 // Construct the input data.
286 std::vector<T> inputData;
287 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
288 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
289
290 // Construct the output data, with bias applied, as appropriate.
291 std::vector<T> outputData;
292 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
293
294 LayerTestResult<T, 4> ret(outputTensorInfo);
295 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
296
297 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
298 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
299
300 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
301 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
302
303 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
304
305 armnn::Convolution2dQueueDescriptor data;
306
307 data.m_Weight = &weightsTensor;
308 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
309 data.m_Parameters.m_StrideX = strideX;
310 data.m_Parameters.m_StrideY = strideY;
311 data.m_Parameters.m_PadLeft = padLeft;
312 data.m_Parameters.m_PadRight = padRight;
313 data.m_Parameters.m_PadTop = padTop;
314 data.m_Parameters.m_PadBottom = padBottom;
315 data.m_Parameters.m_BiasEnabled = biasEnabled;
316 data.m_Parameters.m_DataLayout = dataLayout;
317
318 armnn::WorkloadInfo info;
319 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
320 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
321
322 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
323 inputHandle->Allocate();
324 outputHandle->Allocate();
325
326 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
327
328 workloadFactory.Finalize();
329 workload->Execute();
330
331 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
332
333 return ret;
334}
335
336template<typename T, typename B>
surmeh013537c2c2018-05-18 16:31:43 +0100337LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
338 const boost::multi_array<T, 4>& input,
jimfly01382a91d2018-10-26 15:55:50 +0100339 const boost::multi_array<T, 4>& originalKernel,
surmeh013537c2c2018-05-18 16:31:43 +0100340 const boost::multi_array<B, 1>& bias,
341 const boost::multi_array<T, 4>& outputExpected,
342 float qScale,
343 int32_t qOffset,
jimfly01382a91d2018-10-26 15:55:50 +0100344 const armnn::DataLayoutIndexed& layout,
surmeh013537c2c2018-05-18 16:31:43 +0100345 uint32_t padLeft = 0,
346 uint32_t padTop = 0,
347 uint32_t padRight = 0,
348 uint32_t padBottom = 0,
349 uint32_t strideX = 1,
350 uint32_t strideY = 1)
351{
352 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
353 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
354 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
355 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
jimfly01382a91d2018-10-26 15:55:50 +0100356 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
357 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
358 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
359 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +0100360 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
361 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
362 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
363 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
364
telsoa01c577f2c2018-08-31 09:22:23 +0100365 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100366 bool biasEnabled = bias.size() > 0;
367 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
368
telsoa01c577f2c2018-08-31 09:22:23 +0100369 // Creates the tensors.
jimfly01382a91d2018-10-26 15:55:50 +0100370 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
371 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
372 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelChanMul, kernelChannels, kernelHeight, kernelWidth, layout);
surmeh013537c2c2018-05-18 16:31:43 +0100373 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
374
375 // Set quantization parameters if the requested type is a quantized type.
376 if (armnn::IsQuantizedType<T>())
377 {
378 inputTensorInfo.SetQuantizationScale(qScale);
379 inputTensorInfo.SetQuantizationOffset(qOffset);
380 outputTensorInfo.SetQuantizationScale(qScale);
381 outputTensorInfo.SetQuantizationOffset(qOffset);
382 kernelDesc.SetQuantizationScale(qScale);
383 kernelDesc.SetQuantizationOffset(qOffset);
384 biasDesc.SetQuantizationScale(qScale*qScale);
385 biasDesc.SetQuantizationOffset(0);
386 }
387
telsoa01c577f2c2018-08-31 09:22:23 +0100388 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100389 std::vector<T> inputData;
390 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
jimfly01382a91d2018-10-26 15:55:50 +0100391
392 // At this point if we require it permute the input data
393 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
394 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
395 {
396 std::vector<T> tmp(inputData.size());
397 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
398 inputData = tmp;
399 }
400
surmeh013537c2c2018-05-18 16:31:43 +0100401 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
402
telsoa01c577f2c2018-08-31 09:22:23 +0100403 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100404 std::vector<T> outputData;
405 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
406 if (biasEnabled)
407 {
408 std::vector<T> biasV;
409 biasV.assign(bias.data(), bias.data() + outputChannels);
410 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
411 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
412 outputWidth, outputHeight);
413 }
414
415 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01382a91d2018-10-26 15:55:50 +0100416
417 // At this point if we require it permute the expected output
418 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
419 {
420 std::vector<T> tmp(outputData.size());
421 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
422 outputData = tmp;
423 }
424
surmeh013537c2c2018-05-18 16:31:43 +0100425 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
426
427 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
428 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
429
430 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
jimfly01382a91d2018-10-26 15:55:50 +0100431
432 // Permute the kernel if necessary
433 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
434 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
435 {
436 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
437 }
438
surmeh013537c2c2018-05-18 16:31:43 +0100439 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
440
441 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
442 if (biasEnabled)
443 {
444 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
445 }
446
447 armnn::DepthwiseConvolution2dQueueDescriptor data;
448 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100449 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
surmeh013537c2c2018-05-18 16:31:43 +0100450 data.m_Parameters.m_StrideX = strideX;
451 data.m_Parameters.m_StrideY = strideY;
452 data.m_Parameters.m_PadLeft = padLeft;
453 data.m_Parameters.m_PadRight = padRight;
454 data.m_Parameters.m_PadTop = padTop;
455 data.m_Parameters.m_PadBottom = padBottom;
456 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01382a91d2018-10-26 15:55:50 +0100457 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
surmeh013537c2c2018-05-18 16:31:43 +0100458
459 armnn::WorkloadInfo info;
460 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
461 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
462
463 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
464 inputHandle->Allocate();
465 outputHandle->Allocate();
466
467 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
468
469 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000470 workload->Execute();
471
472 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
473
474 return ret;
475}
476
477template<typename T, typename B>
478LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
479 float qScale,
480 int32_t qOffset,
481 bool biasEnabled)
482{
483 unsigned int inputHeight = 3;
484 unsigned int inputWidth = 3;
485 unsigned int inputChannels = 2;
486 unsigned int inputNum = 1;
487
488 unsigned int kernelHeight = 3;
489 unsigned int kernelWidth = 3;
490 unsigned int kernelChannels = inputChannels;
491
492 unsigned int outputHeight = 1;
493 unsigned int outputWidth = 1;
494 unsigned int outputChannels = kernelChannels;
495 unsigned int outputNum = inputNum;
496
497 armnn::TensorInfo inputTensorInfo({ inputNum, inputChannels, inputHeight, inputWidth }, armnn::GetDataType<T>());
498 armnn::TensorInfo outputTensorInfo({ outputNum, outputChannels, outputHeight, outputWidth },
499 armnn::GetDataType<T>());
500 armnn::TensorInfo kernelDesc({ 1, outputChannels, kernelHeight, kernelWidth }, armnn::GetDataType<T>());
501 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
502
503 // Set quantization parameters if the requested type is a quantized type.
504 if(armnn::IsQuantizedType<T>())
505 {
506 inputTensorInfo.SetQuantizationScale(qScale);
507 inputTensorInfo.SetQuantizationOffset(qOffset);
508 outputTensorInfo.SetQuantizationScale(qScale);
509 outputTensorInfo.SetQuantizationOffset(qOffset);
510 kernelDesc.SetQuantizationScale(qScale);
511 kernelDesc.SetQuantizationOffset(qOffset);
512 biasDesc.SetQuantizationScale(qScale*qScale);
513 biasDesc.SetQuantizationOffset(0);
514 }
515
516 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
517 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
518 1.f, 2.f, 1.f,
519 2.f, 1.f, 2.f,
520 1.f, 2.f, 1.f,
521
522 1.f, 2.f, 1.f,
523 2.f, 1.f, 2.f,
524 1.f, 2.f, 1.f,
525 })));
526
527 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
528 {0, 2}));
529 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
530
531 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
532 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
533 1.f, 0.f, 1.f,
534 0.f, 0.f, 0.f,
535 -1.f, 0.f, -1.f,
536
537 1.f, 0.f, 1.f,
538 0.f, 0.f, 0.f,
539 -1.f, 0.f, -1.f,
540 })));
541
telsoa01c577f2c2018-08-31 09:22:23 +0100542 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000543 std::vector<T> outputImage(
544 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
545 outputTensorInfo.GetQuantizationOffset(),
546 {0.f, 0.f})
547 );
548
telsoa01c577f2c2018-08-31 09:22:23 +0100549 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000550 if(biasEnabled)
551 {
552 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
553 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
554 outputWidth, outputHeight);
555 }
556
557 LayerTestResult<T, 4> ret(outputTensorInfo);
558 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
559
560 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
561 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
562
563 armnn::DepthwiseConvolution2dQueueDescriptor data;
564 armnn::WorkloadInfo info;
565 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
566 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
567
568 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
569 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
570
571 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
572 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
573
574 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100575 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000576 data.m_Parameters.m_StrideX = 1;
577 data.m_Parameters.m_StrideY = 1;
578 data.m_Parameters.m_PadLeft = 0;
579 data.m_Parameters.m_PadRight = 0;
580 data.m_Parameters.m_PadTop = 0;
581 data.m_Parameters.m_PadBottom = 0;
582 data.m_Parameters.m_BiasEnabled = biasEnabled;
583
584 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
585 inputHandle->Allocate();
586 outputHandle->Allocate();
587
588 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
589
surmeh013537c2c2018-05-18 16:31:43 +0100590 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000591 workload->Execute();
592
593 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
594
595 return ret;
596}
597
598template<typename T, typename B>
599LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
600 float qScale,
601 int32_t qOffset,
jimfly01d84216a2018-10-26 12:56:21 +0100602 bool biasEnabled,
603 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +0000604{
605 unsigned int depthMultiplier = 2;
606
607 unsigned int inputHeight = 8;
608 unsigned int inputWidth = 16;
609 unsigned int inputChannels = 2;
610 unsigned int inputBatchSize = 1;
611
612 unsigned int kernelHeight = 5;
613 unsigned int kernelWidth = 3;
614
615 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
616 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
617 unsigned int outputChannels = inputChannels * depthMultiplier;
618 unsigned int outputBatchSize = inputBatchSize;
619
jimfly01d84216a2018-10-26 12:56:21 +0100620 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(
621 inputBatchSize, inputChannels, inputHeight, inputWidth, layout);
622 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
623 outputBatchSize, outputChannels, outputHeight, outputWidth, layout);
624 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(
625 depthMultiplier, inputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000626 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
627
628 // Set quantization parameters if the requested type is a quantized type.
629 if(armnn::IsQuantizedType<T>())
630 {
631 inputTensorInfo.SetQuantizationScale(qScale);
632 inputTensorInfo.SetQuantizationOffset(qOffset);
633 outputTensorInfo.SetQuantizationScale(qScale);
634 outputTensorInfo.SetQuantizationOffset(qOffset);
635 kernelDesc.SetQuantizationScale(qScale);
636 kernelDesc.SetQuantizationOffset(qOffset);
637 biasDesc.SetQuantizationScale(qScale*qScale);
638 biasDesc.SetQuantizationOffset(0);
639 }
640
jimfly01d84216a2018-10-26 12:56:21 +0100641 // NOTE: originalInputData is in NCHW format
642 std::vector<T> originalInputData = std::vector<T>(
643 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
644 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,
645 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,
646 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,
647 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,
648 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,
649 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,
650 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,
651 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,
652 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
653 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
654 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
655 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
656 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
657 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
658 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
659 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
660 }));
661 std::vector<T> inputData = originalInputData;
662 // at this point if we require it permute the input data
663 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
664 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
665 {
666 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, originalInputData.data(), inputData.data());
667 }
668 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000669
670 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
671 {0, 2, 1, -1}));
672 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
673
jimfly01d84216a2018-10-26 12:56:21 +0100674 std::vector<T> originalKernelData = std::vector<T>(
675 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
676 1, 1, 1,
677 1, -1, 1,
678 1, 1, 1,
679 1, 1, 1,
680 1, 1, 1,
telsoa014fcda012018-03-09 14:13:49 +0000681
jimfly01d84216a2018-10-26 12:56:21 +0100682 2, 2, 2,
683 2, 2, 2,
684 2, 2, 2,
685 2, 2, 2,
686 2, 2, 2,
telsoa014fcda012018-03-09 14:13:49 +0000687
jimfly01d84216a2018-10-26 12:56:21 +0100688 0, 0, 0,
689 0, -1, 0,
690 0, 0, 0,
691 0, 0, 0,
692 0, 0, 0,
telsoa014fcda012018-03-09 14:13:49 +0000693
jimfly01d84216a2018-10-26 12:56:21 +0100694 0, 0, 0,
695 0, 0, 0,
696 0, 1, 0,
697 0, 0, 0,
698 0, 0, 0
699 }));
700 std::vector<T> kernelData = originalKernelData;
701 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
702 {
703 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernelData.data(), kernelData.data());
704 }
705 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000706
telsoa01c577f2c2018-08-31 09:22:23 +0100707 // Manually calculated.
jimfly01d84216a2018-10-26 12:56:21 +0100708 std::vector<T> originalOutputImage = std::vector<T>(
telsoa014fcda012018-03-09 14:13:49 +0000709 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
710 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
711 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
712 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
713 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
714 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
715 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
716
717 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
718 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
719 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
720 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
721 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
722 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
723
724 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
725 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
726 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
727 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
728 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
729 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
730
731 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
732 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
733 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
734 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
735 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
736 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
737 }));
738
telsoa01c577f2c2018-08-31 09:22:23 +0100739 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000740 if(biasEnabled)
741 {
jimfly01d84216a2018-10-26 12:56:21 +0100742 ApplyBias(originalOutputImage,
743 outputTensorInfo.GetQuantizationScale(),
744 outputTensorInfo.GetQuantizationOffset(),
745 biasV,
746 biasDesc.GetQuantizationScale(),
747 biasDesc.GetQuantizationOffset(),
748 outputWidth,
749 outputHeight);
telsoa014fcda012018-03-09 14:13:49 +0000750 }
751
752 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01d84216a2018-10-26 12:56:21 +0100753 std::vector<T> outputImage = originalOutputImage;
754 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
755 {
756 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, originalOutputImage.data(), outputImage.data());
757 }
758
telsoa014fcda012018-03-09 14:13:49 +0000759 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
760
761 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
762 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
763
764 armnn::DepthwiseConvolution2dQueueDescriptor data;
765 armnn::WorkloadInfo info;
766 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
767 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
768
769 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
770 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
771
772 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
773 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
774
775 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100776 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000777 data.m_Parameters.m_StrideX = 2;
778 data.m_Parameters.m_StrideY = 1;
779 data.m_Parameters.m_PadLeft = 0;
780 data.m_Parameters.m_PadRight = 0;
781 data.m_Parameters.m_PadTop = 1;
782 data.m_Parameters.m_PadBottom = 1;
783 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01d84216a2018-10-26 12:56:21 +0100784 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000785
786 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
787 inputHandle->Allocate();
788 outputHandle->Allocate();
789
790 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
791
surmeh013537c2c2018-05-18 16:31:43 +0100792 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000793 workload->Execute();
794
795 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
796
797 return ret;
798}
799
Nikhil Rajcec6b652018-10-12 13:51:57 +0100800template<typename T, typename B>
801LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
802 const boost::multi_array<T, 4>& input,
803 const boost::multi_array<T, 4>& kernel,
804 const boost::multi_array<B, 1>& bias,
805 const boost::multi_array<T, 4>& outputExpected,
806 float qScale,
807 int32_t qOffset,
808 uint32_t padLeft = 0,
809 uint32_t padTop = 0,
810 uint32_t padRight = 0,
811 uint32_t padBottom = 0,
812 uint32_t strideX = 1,
813 uint32_t strideY = 1)
814{
815 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
816 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
817 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
818 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
819
820 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
821 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
822 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
823 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
824
825 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
826 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
827 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
828 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
829
830 // Creates the tensors.
831 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
832 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
833 armnn::GetDataType<T>());
834 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
835 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
836
837 // Set quantization parameters if the requested type is a quantized type.
838 if (armnn::IsQuantizedType<T>())
839 {
840 inputTensorInfo.SetQuantizationScale(qScale);
841 inputTensorInfo.SetQuantizationOffset(qOffset);
842 outputTensorInfo.SetQuantizationScale(qScale);
843 outputTensorInfo.SetQuantizationOffset(qOffset);
844 kernelDesc.SetQuantizationScale(qScale);
845 kernelDesc.SetQuantizationOffset(qOffset);
846 biasDesc.SetQuantizationScale(qScale*qScale);
847 biasDesc.SetQuantizationOffset(0);
848 }
849
850 // Construct the input data.
851 std::vector<T> inputData;
852 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
853 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
854
855 // Construct the output data, with bias applied, as appropriate.
856 std::vector<T> outputData;
857 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
858
859 LayerTestResult<T, 4> ret(outputTensorInfo);
860 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
861
862 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
863 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
864
865 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
866 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
867
868 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
869
870 armnn::DepthwiseConvolution2dQueueDescriptor data;
871 data.m_Weight = &weightsTensor;
872 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
873 data.m_Parameters.m_StrideX = strideX;
874 data.m_Parameters.m_StrideY = strideY;
875 data.m_Parameters.m_PadLeft = padLeft;
876 data.m_Parameters.m_PadRight = padRight;
877 data.m_Parameters.m_PadTop = padTop;
878 data.m_Parameters.m_PadBottom = padBottom;
879 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
880
881 armnn::WorkloadInfo info;
882 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
883 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
884
885 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
886
887 inputHandle->Allocate();
888 outputHandle->Allocate();
889
890 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
891
892 workloadFactory.Finalize();
893 workload->Execute();
894
895 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
896
897 return ret;
898}
899
telsoa014fcda012018-03-09 14:13:49 +0000900template<typename T>
901LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
902 float qScale,
903 int32_t qOffset,
904 bool biasEnabled)
905{
906 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
907
telsoa01c577f2c2018-08-31 09:22:23 +0100908 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000909 // 2D convolution with the final dimension set to 1.
910 // I don't anticipate this being particularly slow, given that convolution is implemented
911 // as a matrix multiplication, at which point dimension doesn't matter.
912
913 unsigned int batchSize = 1;
914 unsigned int inputChannels = 2;
915 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100916 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000917 unsigned int kernelSize = 3;
918 unsigned int padSize = 2;
919 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100920 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000921
922 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
923 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
924 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
925 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
926
927 // Set quantization parameters if the requested type is a quantized type.
928 if(armnn::IsQuantizedType<T>())
929 {
930 inputInfo.SetQuantizationScale(qScale);
931 inputInfo.SetQuantizationOffset(qOffset);
932 outputInfo.SetQuantizationScale(qScale);
933 outputInfo.SetQuantizationOffset(qOffset);
934 kernelInfo.SetQuantizationScale(qScale);
935 kernelInfo.SetQuantizationOffset(qOffset);
936 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
937 biasInfo.SetQuantizationOffset(0);
938 }
939
940 std::vector<T> inputData(
941 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
942 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
943 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
944 }));
945
946 std::vector<T> kernelData(
947 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
948 1.0f, 0.0f, 0.0f,
949 0.0f, 2.0f, -1.5f,
950
951 0.0f, 0.0f, 0.0f,
952 0.2f, 0.2f, 0.2f,
953
954 0.5f, 0.0f, 0.5f,
955 0.0f, -1.0f, 0.0f
956 }));
957
958 std::vector<B> biasData(
959 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
960 1.0f, 0.0f, 0.0f
961 }));
962
963 std::vector<T> outputData(
964 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
965 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,
966 -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,
967 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
968 }));
969
telsoa01c577f2c2018-08-31 09:22:23 +0100970 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000971 if(biasEnabled)
972 {
973 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
974 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
975 1, outputSize);
976 }
977
978 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
979 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
980
981 armnn::Convolution2dQueueDescriptor data;
982 armnn::WorkloadInfo info;
983 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
984 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
985
986 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
987 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
988
989 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
990 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
991
992 data.m_Weight = &weightsTensor;
993 data.m_Bias = &biasTensor;
994 data.m_Parameters.m_StrideX = 1;
995 data.m_Parameters.m_StrideY = stride;
996 data.m_Parameters.m_PadLeft = 0;
997 data.m_Parameters.m_PadRight = 0;
998 data.m_Parameters.m_PadTop = padSize;
999 data.m_Parameters.m_PadBottom = padSize;
1000 data.m_Parameters.m_BiasEnabled = biasEnabled;
1001
1002 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1003 inputHandle->Allocate();
1004 outputHandle->Allocate();
1005
1006 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
1007
surmeh013537c2c2018-05-18 16:31:43 +01001008 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001009 workload->Execute();
1010
telsoa01c577f2c2018-08-31 09:22:23 +01001011 // Output
telsoa014fcda012018-03-09 14:13:49 +00001012 LayerTestResult<T,4> ret(outputInfo);
1013 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1014 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
1015 return ret;
1016}
1017
1018
1019
1020template<typename T>
1021LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
1022 armnn::IWorkloadFactory& refWorkloadFactory)
1023{
1024 unsigned int inputHeight = 8;
1025 unsigned int inputWidth = 16;
1026 unsigned int inputChannels = 3;
1027 unsigned int inputNum = 5;
1028
1029 unsigned int kernelHeight = 3;
1030 unsigned int kernelWidth = 3;
1031
1032 unsigned int strideX = 2;
1033 unsigned int strideY = 3;
1034 unsigned int padX = 1;
1035 unsigned int padY = 1;
1036
1037 unsigned int outputNum = inputNum;
1038 unsigned int outputChannels = 2;
1039 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1040 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1041
1042 armnn::TensorInfo inputTensorInfo;
1043 armnn::TensorInfo outputTensorInfo;
1044 armnn::TensorInfo kernelDesc;
1045 armnn::TensorInfo biasDesc;
1046
1047 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1048 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1049 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1050 unsigned int biasShape[] = {outputChannels};
1051
1052 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
1053 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
1054 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
1055 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
1056
1057 LayerTestResult<T,4> ret(outputTensorInfo);
1058
1059 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1060 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1061 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1062
1063 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1064 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1065
1066 armnn::Convolution2dQueueDescriptor data;
1067 armnn::WorkloadInfo info;
1068 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1069 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1070
1071 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1072 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1073
1074 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1075 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1076 data.m_Weight = &weightsTensor;
1077 data.m_Bias = &biasTensor;
1078 data.m_Parameters.m_StrideX = strideX;
1079 data.m_Parameters.m_StrideY = strideY;
1080 data.m_Parameters.m_PadLeft = padX;
1081 data.m_Parameters.m_PadRight = padX;
1082 data.m_Parameters.m_PadTop = padY;
1083 data.m_Parameters.m_PadBottom = padY;
1084 data.m_Parameters.m_BiasEnabled = true;
1085
1086 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1087 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1088
1089 armnn::Convolution2dQueueDescriptor refData = data;
1090 armnn::WorkloadInfo refInfo = info;
1091 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1092 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1093
1094 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1095 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1096
1097 outputHandleRef->Allocate();
1098 inputHandleRef->Allocate();
1099
1100 inputHandle->Allocate();
1101 outputHandle->Allocate();
1102
1103 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1104 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1105
surmeh013537c2c2018-05-18 16:31:43 +01001106 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001107 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +01001108 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001109 workloadRef->Execute();
1110
1111 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1112 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1113
1114 return ret;
1115}
1116
1117template<typename T>
1118LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
1119 armnn::IWorkloadFactory& refWorkloadFactory)
1120{
1121 unsigned int inputHeight = 8;
1122 unsigned int inputWidth = 16;
1123 unsigned int inputChannels = 3;
1124 unsigned int inputNum = 5;
1125
1126 unsigned int kernelHeight = 3;
1127 unsigned int kernelWidth = 3;
1128 unsigned int channelMultiplier = 1;
1129
1130 unsigned int strideX = 2;
1131 unsigned int strideY = 3;
1132 unsigned int padX = 1;
1133 unsigned int padY = 1;
1134
1135 unsigned int outputNum = inputNum;
1136 unsigned int outputChannels = inputChannels * channelMultiplier;
1137 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1138 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1139
1140 armnn::TensorInfo inputTensorInfo;
1141 armnn::TensorInfo outputTensorInfo;
1142 armnn::TensorInfo kernelDesc;
1143 armnn::TensorInfo biasDesc;
1144
1145 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
1146 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
1147 unsigned int kernelShape[] = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1148 unsigned int biasShape[] = { outputChannels };
1149
1150 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1151 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1152 int32_t qOffset = 0;
1153
1154 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
1155 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>(), outputQScale, qOffset);
1156 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
1157 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
1158
1159 LayerTestResult<T, 4> ret(outputTensorInfo);
1160
1161 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1162 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
jimfly01d84216a2018-10-26 12:56:21 +01001163 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1164 biasDesc, 1028, 0.0f, 255.0f);
telsoa014fcda012018-03-09 14:13:49 +00001165
1166 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1167 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1168
1169 armnn::DepthwiseConvolution2dQueueDescriptor data;
1170 armnn::WorkloadInfo info;
1171 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1172 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1173
1174 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1175 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1176
1177 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1178 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1179 data.m_Weight = &weightsTensor;
1180 data.m_Bias = &biasTensor;
1181 data.m_Parameters.m_StrideX = strideX;
1182 data.m_Parameters.m_StrideY = strideY;
1183 data.m_Parameters.m_PadLeft = padX;
1184 data.m_Parameters.m_PadRight = padX;
1185 data.m_Parameters.m_PadTop = padY;
1186 data.m_Parameters.m_PadBottom = padY;
1187 data.m_Parameters.m_BiasEnabled = true;
1188
1189 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1190 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1191
1192 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1193 armnn::WorkloadInfo refInfo = info;
1194 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1195 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1196
1197 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1198 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1199
1200 outputHandleRef->Allocate();
1201 inputHandleRef->Allocate();
1202
1203 inputHandle->Allocate();
1204 outputHandle->Allocate();
1205
1206 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1207 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1208
surmeh013537c2c2018-05-18 16:31:43 +01001209 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001210 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +01001211 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001212 workloadRef->Execute();
1213
1214 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1215 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1216
1217 return ret;
1218}