blob: 03becab73f4ec44e18008e65475b486dc93166b8 [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
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000015#include <backendsCommon/CpuTensorHandle.hpp>
16#include <backendsCommon/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
telsoa014fcda012018-03-09 14:13:49 +000065template<typename T, typename B>
66LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
jimfly010a088a62018-10-25 17:05:05 +010067 const boost::multi_array<T, 4>& originalInput,
68 const boost::multi_array<T, 4>& originalKernel,
telsoa014fcda012018-03-09 14:13:49 +000069 const boost::multi_array<B, 1>& bias,
jimfly010a088a62018-10-25 17:05:05 +010070 const boost::multi_array<T, 4>& originalOutputExpected,
telsoa014fcda012018-03-09 14:13:49 +000071 float qScale,
72 int32_t qOffset,
jimfly010a088a62018-10-25 17:05:05 +010073 const armnn::DataLayoutIndexed& layout = armnn::DataLayout::NCHW,
telsoa014fcda012018-03-09 14:13:49 +000074 uint32_t padLeft = 0,
75 uint32_t padTop = 0,
76 uint32_t padRight = 0,
77 uint32_t padBottom = 0)
78{
jimfly010a088a62018-10-25 17:05:05 +010079 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
80 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
81 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
82 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000083
jimfly010a088a62018-10-25 17:05:05 +010084 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
85 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
86 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
87 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000088
jimfly010a088a62018-10-25 17:05:05 +010089 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
90 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
91 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
92 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000093
94 bool biasEnabled = bias.size() > 0;
95
telsoa01c577f2c2018-08-31 09:22:23 +010096 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +000097 BOOST_ASSERT(inputNum == 1);
98 BOOST_ASSERT(outputNum == 1);
99
telsoa01c577f2c2018-08-31 09:22:23 +0100100 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +0000101 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
102
103
telsoa01c577f2c2018-08-31 09:22:23 +0100104 // Note these tensors will use two (identical) batches.
jimfly010a088a62018-10-25 17:05:05 +0100105 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(2*inputNum, inputChannels, inputHeight, inputWidth, layout);
106 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
107 2*outputNum, outputChannels, outputHeight, outputWidth, layout);
108 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000109 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
110
111 // Set quantization parameters if the requested type is a quantized type.
112 if(armnn::IsQuantizedType<T>())
113 {
114 inputTensorInfo.SetQuantizationScale(qScale);
115 inputTensorInfo.SetQuantizationOffset(qOffset);
116 outputTensorInfo.SetQuantizationScale(qScale);
117 outputTensorInfo.SetQuantizationOffset(qOffset);
118 kernelDesc.SetQuantizationScale(qScale);
119 kernelDesc.SetQuantizationOffset(qOffset);
120 biasDesc.SetQuantizationScale(qScale*qScale);
121 biasDesc.SetQuantizationOffset(0);
122 }
123
124 LayerTestResult<T, 4> ret(outputTensorInfo);
125
telsoa01c577f2c2018-08-31 09:22:23 +0100126 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000127 std::vector<T> inputImage;
jimfly010a088a62018-10-25 17:05:05 +0100128 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000129 std::vector<T> inputData;
130 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
131 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
jimfly010a088a62018-10-25 17:05:05 +0100132
133 // at this point if we require it permute the input data
134 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
135 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
136 {
137 std::vector<T> tmp(inputData.size());
138 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
139 inputData = tmp;
140 }
141
telsoa014fcda012018-03-09 14:13:49 +0000142 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
143
144 std::vector<T> outputImage;
jimfly010a088a62018-10-25 17:05:05 +0100145 outputImage.assign(originalOutputExpected.data(),
146 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000147
telsoa01c577f2c2018-08-31 09:22:23 +0100148 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000149 if(biasEnabled)
150 {
151 std::vector<T> biasV;
152 biasV.assign(bias.data(), bias.data() + outputChannels);
153 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
154 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
155 outputWidth, outputHeight);
156 }
157
telsoa01c577f2c2018-08-31 09:22:23 +0100158 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000159 std::vector<T> outputData;
160 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
161 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
162
jimfly010a088a62018-10-25 17:05:05 +0100163 // at this point if we require it permute the expected output
164 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
165 {
166 std::vector<T> tmp(outputData.size());
167 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
168 outputData = tmp;
169 }
telsoa014fcda012018-03-09 14:13:49 +0000170 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
171
telsoa01c577f2c2018-08-31 09:22:23 +0100172 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000173 uint32_t strideX = 1;
174 uint32_t strideY = 1;
175
176 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
177 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
178
179 armnn::Convolution2dQueueDescriptor data;
180 armnn::WorkloadInfo info;
181 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
182 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
jimfly010a088a62018-10-25 17:05:05 +0100183 // Permute the kernel if necessary
184 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
185 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
186 {
187 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
188 }
telsoa014fcda012018-03-09 14:13:49 +0000189 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
190
191 if(biasEnabled)
192 {
193 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
194 }
195
196 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
197 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
198
199 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100200 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 +0000201 data.m_Parameters.m_StrideX = strideX;
202 data.m_Parameters.m_StrideY = strideY;
203 data.m_Parameters.m_PadLeft = padLeft;
204 data.m_Parameters.m_PadRight = padRight;
205 data.m_Parameters.m_PadTop = padTop;
206 data.m_Parameters.m_PadBottom = padBottom;
207 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly010a088a62018-10-25 17:05:05 +0100208 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000209
210 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
211 inputHandle->Allocate();
212 outputHandle->Allocate();
213
214 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
215
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000216 workloadFactory.Acquire();
surmeh013537c2c2018-05-18 16:31:43 +0100217 workload->Execute();
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000218 workloadFactory.Release();
surmeh013537c2c2018-05-18 16:31:43 +0100219
220 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
221
222 return ret;
223}
224
225template<typename T, typename B>
Francis Murtaghd59116e2018-10-04 16:03:07 +0100226LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
227 const boost::multi_array<T, 4>& input,
228 const boost::multi_array<T, 4>& kernel,
229 const boost::multi_array<B, 1>& bias,
230 const boost::multi_array<T, 4>& outputExpected,
231 armnn::DataLayout dataLayout,
232 float qScale,
233 int32_t qOffset,
234 uint32_t padLeft = 1,
235 uint32_t padTop = 1,
236 uint32_t padRight = 1,
237 uint32_t padBottom = 1,
238 uint32_t strideX = 1,
239 uint32_t strideY = 1)
240{
241 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
242 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
243 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
244 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
245
246 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
247 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
248 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
249 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
250
251 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
252 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
253 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
254 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
255
256 bool biasEnabled = bias.size() > 0;
257
258 // Creates the tensors.
259 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
260 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
261 armnn::GetDataType<T>());
262 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
263 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
264
265 // Construct the input data.
266 std::vector<T> inputData;
267 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
268 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
269
270 // Construct the output data, with bias applied, as appropriate.
271 std::vector<T> outputData;
272 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
273
274 LayerTestResult<T, 4> ret(outputTensorInfo);
275 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
276
277 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
278 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
279
280 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
281 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
282
283 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
284
285 armnn::Convolution2dQueueDescriptor data;
286
287 data.m_Weight = &weightsTensor;
288 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
289 data.m_Parameters.m_StrideX = strideX;
290 data.m_Parameters.m_StrideY = strideY;
291 data.m_Parameters.m_PadLeft = padLeft;
292 data.m_Parameters.m_PadRight = padRight;
293 data.m_Parameters.m_PadTop = padTop;
294 data.m_Parameters.m_PadBottom = padBottom;
295 data.m_Parameters.m_BiasEnabled = biasEnabled;
296 data.m_Parameters.m_DataLayout = dataLayout;
297
298 armnn::WorkloadInfo info;
299 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
300 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
301
302 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
303 inputHandle->Allocate();
304 outputHandle->Allocate();
305
306 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
307
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000308 workloadFactory.Acquire();
Francis Murtaghd59116e2018-10-04 16:03:07 +0100309 workload->Execute();
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000310 workloadFactory.Release();
Francis Murtaghd59116e2018-10-04 16:03:07 +0100311
312 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
313
314 return ret;
315}
316
317template<typename T, typename B>
surmeh013537c2c2018-05-18 16:31:43 +0100318LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
319 const boost::multi_array<T, 4>& input,
jimfly01382a91d2018-10-26 15:55:50 +0100320 const boost::multi_array<T, 4>& originalKernel,
surmeh013537c2c2018-05-18 16:31:43 +0100321 const boost::multi_array<B, 1>& bias,
322 const boost::multi_array<T, 4>& outputExpected,
323 float qScale,
324 int32_t qOffset,
jimfly01382a91d2018-10-26 15:55:50 +0100325 const armnn::DataLayoutIndexed& layout,
surmeh013537c2c2018-05-18 16:31:43 +0100326 uint32_t padLeft = 0,
327 uint32_t padTop = 0,
328 uint32_t padRight = 0,
329 uint32_t padBottom = 0,
330 uint32_t strideX = 1,
331 uint32_t strideY = 1)
332{
333 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
334 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
335 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
336 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
jimfly01382a91d2018-10-26 15:55:50 +0100337 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
338 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
339 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
340 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +0100341 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
342 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
343 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
344 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
345
telsoa01c577f2c2018-08-31 09:22:23 +0100346 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100347 bool biasEnabled = bias.size() > 0;
348 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
349
telsoa01c577f2c2018-08-31 09:22:23 +0100350 // Creates the tensors.
jimfly01382a91d2018-10-26 15:55:50 +0100351 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
352 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
353 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelChanMul, kernelChannels, kernelHeight, kernelWidth, layout);
surmeh013537c2c2018-05-18 16:31:43 +0100354 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
355
356 // Set quantization parameters if the requested type is a quantized type.
357 if (armnn::IsQuantizedType<T>())
358 {
359 inputTensorInfo.SetQuantizationScale(qScale);
360 inputTensorInfo.SetQuantizationOffset(qOffset);
361 outputTensorInfo.SetQuantizationScale(qScale);
362 outputTensorInfo.SetQuantizationOffset(qOffset);
363 kernelDesc.SetQuantizationScale(qScale);
364 kernelDesc.SetQuantizationOffset(qOffset);
365 biasDesc.SetQuantizationScale(qScale*qScale);
366 biasDesc.SetQuantizationOffset(0);
367 }
368
telsoa01c577f2c2018-08-31 09:22:23 +0100369 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100370 std::vector<T> inputData;
371 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
jimfly01382a91d2018-10-26 15:55:50 +0100372
373 // At this point if we require it permute the input data
374 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
375 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
376 {
377 std::vector<T> tmp(inputData.size());
378 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
379 inputData = tmp;
380 }
381
surmeh013537c2c2018-05-18 16:31:43 +0100382 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
383
telsoa01c577f2c2018-08-31 09:22:23 +0100384 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100385 std::vector<T> outputData;
386 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
387 if (biasEnabled)
388 {
389 std::vector<T> biasV;
390 biasV.assign(bias.data(), bias.data() + outputChannels);
391 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
392 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
393 outputWidth, outputHeight);
394 }
395
396 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01382a91d2018-10-26 15:55:50 +0100397
398 // At this point if we require it permute the expected output
399 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
400 {
401 std::vector<T> tmp(outputData.size());
402 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
403 outputData = tmp;
404 }
405
surmeh013537c2c2018-05-18 16:31:43 +0100406 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
407
408 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
409 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
410
411 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
jimfly01382a91d2018-10-26 15:55:50 +0100412
413 // Permute the kernel if necessary
414 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
415 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
416 {
417 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
418 }
419
surmeh013537c2c2018-05-18 16:31:43 +0100420 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
421
422 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
423 if (biasEnabled)
424 {
425 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
426 }
427
428 armnn::DepthwiseConvolution2dQueueDescriptor data;
429 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100430 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 +0100431 data.m_Parameters.m_StrideX = strideX;
432 data.m_Parameters.m_StrideY = strideY;
433 data.m_Parameters.m_PadLeft = padLeft;
434 data.m_Parameters.m_PadRight = padRight;
435 data.m_Parameters.m_PadTop = padTop;
436 data.m_Parameters.m_PadBottom = padBottom;
437 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01382a91d2018-10-26 15:55:50 +0100438 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
surmeh013537c2c2018-05-18 16:31:43 +0100439
440 armnn::WorkloadInfo info;
441 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
442 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
443
444 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
445 inputHandle->Allocate();
446 outputHandle->Allocate();
447
448 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
449
telsoa014fcda012018-03-09 14:13:49 +0000450 workload->Execute();
451
452 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
453
454 return ret;
455}
456
457template<typename T, typename B>
458LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
459 float qScale,
460 int32_t qOffset,
jimfly01b9c89632018-10-26 16:50:13 +0100461 bool biasEnabled,
462 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +0000463{
464 unsigned int inputHeight = 3;
465 unsigned int inputWidth = 3;
466 unsigned int inputChannels = 2;
467 unsigned int inputNum = 1;
468
469 unsigned int kernelHeight = 3;
470 unsigned int kernelWidth = 3;
471 unsigned int kernelChannels = inputChannels;
472
473 unsigned int outputHeight = 1;
474 unsigned int outputWidth = 1;
475 unsigned int outputChannels = kernelChannels;
476 unsigned int outputNum = inputNum;
477
jimfly01b9c89632018-10-26 16:50:13 +0100478 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
479 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
480 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(1, outputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000481 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
482
483 // Set quantization parameters if the requested type is a quantized type.
484 if(armnn::IsQuantizedType<T>())
485 {
486 inputTensorInfo.SetQuantizationScale(qScale);
487 inputTensorInfo.SetQuantizationOffset(qOffset);
488 outputTensorInfo.SetQuantizationScale(qScale);
489 outputTensorInfo.SetQuantizationOffset(qOffset);
490 kernelDesc.SetQuantizationScale(qScale);
491 kernelDesc.SetQuantizationOffset(qOffset);
492 biasDesc.SetQuantizationScale(qScale*qScale);
493 biasDesc.SetQuantizationOffset(0);
494 }
jimfly01b9c89632018-10-26 16:50:13 +0100495 std::vector<T> inputData = std::vector<T>(
496 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
497 1.f, 2.f, 1.f,
498 2.f, 1.f, 2.f,
499 1.f, 2.f, 1.f,
telsoa014fcda012018-03-09 14:13:49 +0000500
jimfly01b9c89632018-10-26 16:50:13 +0100501 1.f, 2.f, 1.f,
502 2.f, 1.f, 2.f,
503 1.f, 2.f, 1.f,
504 }));
505 // at this point if we require it permute the input data
506 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
507 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
508 {
509 std::vector<T> tmp(inputData.size());
510 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
511 inputData = tmp;
512 }
513 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000514
515 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
516 {0, 2}));
517 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
518
jimfly01b9c89632018-10-26 16:50:13 +0100519 std::vector<T> kernelData = std::vector<T>(
520 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
521 1.f, 0.f, 1.f,
522 0.f, 0.f, 0.f,
523 -1.f, 0.f, -1.f,
telsoa014fcda012018-03-09 14:13:49 +0000524
jimfly01b9c89632018-10-26 16:50:13 +0100525 1.f, 0.f, 1.f,
526 0.f, 0.f, 0.f,
527 -1.f, 0.f, -1.f,
528 }));
529 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
530 {
531 std::vector<T> tmp(kernelData.size());
532 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, kernelData.data(), tmp.data());
533 kernelData = tmp;
534 }
535 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000536
telsoa01c577f2c2018-08-31 09:22:23 +0100537 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000538 std::vector<T> outputImage(
539 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
540 outputTensorInfo.GetQuantizationOffset(),
541 {0.f, 0.f})
542 );
543
telsoa01c577f2c2018-08-31 09:22:23 +0100544 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000545 if(biasEnabled)
546 {
547 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
548 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
549 outputWidth, outputHeight);
550 }
551
552 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01b9c89632018-10-26 16:50:13 +0100553 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
554 {
555 std::vector<T> tmp(outputImage.size());
556 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputImage.data(), tmp.data());
557 outputImage = tmp;
558 }
559
telsoa014fcda012018-03-09 14:13:49 +0000560 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
561
562 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
563 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
564
565 armnn::DepthwiseConvolution2dQueueDescriptor data;
566 armnn::WorkloadInfo info;
567 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
568 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
569
570 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
571 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
572
573 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
574 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
575
576 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100577 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000578 data.m_Parameters.m_StrideX = 1;
579 data.m_Parameters.m_StrideY = 1;
580 data.m_Parameters.m_PadLeft = 0;
581 data.m_Parameters.m_PadRight = 0;
582 data.m_Parameters.m_PadTop = 0;
583 data.m_Parameters.m_PadBottom = 0;
584 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01b9c89632018-10-26 16:50:13 +0100585 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000586
587 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
588 inputHandle->Allocate();
589 outputHandle->Allocate();
590
591 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
592
593 workload->Execute();
594
595 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
596
597 return ret;
598}
599
600template<typename T, typename B>
601LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
602 float qScale,
603 int32_t qOffset,
jimfly01d84216a2018-10-26 12:56:21 +0100604 bool biasEnabled,
605 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +0000606{
607 unsigned int depthMultiplier = 2;
608
609 unsigned int inputHeight = 8;
610 unsigned int inputWidth = 16;
611 unsigned int inputChannels = 2;
612 unsigned int inputBatchSize = 1;
613
614 unsigned int kernelHeight = 5;
615 unsigned int kernelWidth = 3;
616
617 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
618 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
619 unsigned int outputChannels = inputChannels * depthMultiplier;
620 unsigned int outputBatchSize = inputBatchSize;
621
jimfly01d84216a2018-10-26 12:56:21 +0100622 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(
623 inputBatchSize, inputChannels, inputHeight, inputWidth, layout);
624 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
625 outputBatchSize, outputChannels, outputHeight, outputWidth, layout);
626 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(
627 depthMultiplier, inputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000628 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
629
630 // Set quantization parameters if the requested type is a quantized type.
631 if(armnn::IsQuantizedType<T>())
632 {
633 inputTensorInfo.SetQuantizationScale(qScale);
634 inputTensorInfo.SetQuantizationOffset(qOffset);
635 outputTensorInfo.SetQuantizationScale(qScale);
636 outputTensorInfo.SetQuantizationOffset(qOffset);
637 kernelDesc.SetQuantizationScale(qScale);
638 kernelDesc.SetQuantizationOffset(qOffset);
639 biasDesc.SetQuantizationScale(qScale*qScale);
640 biasDesc.SetQuantizationOffset(0);
641 }
642
jimfly01d84216a2018-10-26 12:56:21 +0100643 // NOTE: originalInputData is in NCHW format
644 std::vector<T> originalInputData = std::vector<T>(
645 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
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.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,
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.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,
653 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,
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 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
661 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
662 }));
663 std::vector<T> inputData = originalInputData;
664 // at this point if we require it permute the input data
665 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
666 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
667 {
668 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, originalInputData.data(), inputData.data());
669 }
670 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000671
672 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
673 {0, 2, 1, -1}));
674 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
675
jimfly01d84216a2018-10-26 12:56:21 +0100676 std::vector<T> originalKernelData = std::vector<T>(
677 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
678 1, 1, 1,
679 1, -1, 1,
680 1, 1, 1,
681 1, 1, 1,
682 1, 1, 1,
telsoa014fcda012018-03-09 14:13:49 +0000683
jimfly01d84216a2018-10-26 12:56:21 +0100684 2, 2, 2,
685 2, 2, 2,
686 2, 2, 2,
687 2, 2, 2,
688 2, 2, 2,
telsoa014fcda012018-03-09 14:13:49 +0000689
jimfly01d84216a2018-10-26 12:56:21 +0100690 0, 0, 0,
691 0, -1, 0,
692 0, 0, 0,
693 0, 0, 0,
694 0, 0, 0,
telsoa014fcda012018-03-09 14:13:49 +0000695
jimfly01d84216a2018-10-26 12:56:21 +0100696 0, 0, 0,
697 0, 0, 0,
698 0, 1, 0,
699 0, 0, 0,
700 0, 0, 0
701 }));
702 std::vector<T> kernelData = originalKernelData;
703 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
704 {
705 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernelData.data(), kernelData.data());
706 }
707 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000708
telsoa01c577f2c2018-08-31 09:22:23 +0100709 // Manually calculated.
jimfly01d84216a2018-10-26 12:56:21 +0100710 std::vector<T> originalOutputImage = std::vector<T>(
telsoa014fcda012018-03-09 14:13:49 +0000711 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
712 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
713 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
714 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
715 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
716 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
717 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
718
719 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
720 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
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 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
724 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
725
726 8.0f, 8.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 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
730 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
731 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
732
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 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
738 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
739 }));
740
telsoa01c577f2c2018-08-31 09:22:23 +0100741 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000742 if(biasEnabled)
743 {
jimfly01d84216a2018-10-26 12:56:21 +0100744 ApplyBias(originalOutputImage,
745 outputTensorInfo.GetQuantizationScale(),
746 outputTensorInfo.GetQuantizationOffset(),
747 biasV,
748 biasDesc.GetQuantizationScale(),
749 biasDesc.GetQuantizationOffset(),
750 outputWidth,
751 outputHeight);
telsoa014fcda012018-03-09 14:13:49 +0000752 }
753
754 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01d84216a2018-10-26 12:56:21 +0100755 std::vector<T> outputImage = originalOutputImage;
756 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
757 {
758 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, originalOutputImage.data(), outputImage.data());
759 }
760
telsoa014fcda012018-03-09 14:13:49 +0000761 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
762
763 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
764 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
765
766 armnn::DepthwiseConvolution2dQueueDescriptor data;
767 armnn::WorkloadInfo info;
768 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
769 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
770
771 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
772 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
773
774 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
775 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
776
777 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100778 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000779 data.m_Parameters.m_StrideX = 2;
780 data.m_Parameters.m_StrideY = 1;
781 data.m_Parameters.m_PadLeft = 0;
782 data.m_Parameters.m_PadRight = 0;
783 data.m_Parameters.m_PadTop = 1;
784 data.m_Parameters.m_PadBottom = 1;
785 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01d84216a2018-10-26 12:56:21 +0100786 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000787
788 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
789 inputHandle->Allocate();
790 outputHandle->Allocate();
791
792 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
793
794 workload->Execute();
795
796 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
797
798 return ret;
799}
800
Nikhil Rajcec6b652018-10-12 13:51:57 +0100801template<typename T, typename B>
802LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
803 const boost::multi_array<T, 4>& input,
804 const boost::multi_array<T, 4>& kernel,
805 const boost::multi_array<B, 1>& bias,
806 const boost::multi_array<T, 4>& outputExpected,
807 float qScale,
808 int32_t qOffset,
809 uint32_t padLeft = 0,
810 uint32_t padTop = 0,
811 uint32_t padRight = 0,
812 uint32_t padBottom = 0,
813 uint32_t strideX = 1,
814 uint32_t strideY = 1)
815{
816 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
817 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
818 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
819 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
820
821 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
822 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
823 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
824 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
825
826 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
827 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
828 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
829 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
830
831 // Creates the tensors.
832 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
833 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
834 armnn::GetDataType<T>());
835 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
836 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
837
838 // Set quantization parameters if the requested type is a quantized type.
839 if (armnn::IsQuantizedType<T>())
840 {
841 inputTensorInfo.SetQuantizationScale(qScale);
842 inputTensorInfo.SetQuantizationOffset(qOffset);
843 outputTensorInfo.SetQuantizationScale(qScale);
844 outputTensorInfo.SetQuantizationOffset(qOffset);
845 kernelDesc.SetQuantizationScale(qScale);
846 kernelDesc.SetQuantizationOffset(qOffset);
847 biasDesc.SetQuantizationScale(qScale*qScale);
848 biasDesc.SetQuantizationOffset(0);
849 }
850
851 // Construct the input data.
852 std::vector<T> inputData;
853 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
854 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
855
856 // Construct the output data, with bias applied, as appropriate.
857 std::vector<T> outputData;
858 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
859
860 LayerTestResult<T, 4> ret(outputTensorInfo);
861 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
862
863 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
864 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
865
866 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
867 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
868
869 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
870
871 armnn::DepthwiseConvolution2dQueueDescriptor data;
872 data.m_Weight = &weightsTensor;
873 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
874 data.m_Parameters.m_StrideX = strideX;
875 data.m_Parameters.m_StrideY = strideY;
876 data.m_Parameters.m_PadLeft = padLeft;
877 data.m_Parameters.m_PadRight = padRight;
878 data.m_Parameters.m_PadTop = padTop;
879 data.m_Parameters.m_PadBottom = padBottom;
880 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
881
882 armnn::WorkloadInfo info;
883 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
884 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
885
886 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
887
888 inputHandle->Allocate();
889 outputHandle->Allocate();
890
891 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
892
Nikhil Rajcec6b652018-10-12 13:51:57 +0100893 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
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001008 workloadFactory.Acquire();
telsoa014fcda012018-03-09 14:13:49 +00001009 workload->Execute();
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001010 workloadFactory.Release();
telsoa014fcda012018-03-09 14:13:49 +00001011
telsoa01c577f2c2018-08-31 09:22:23 +01001012 // Output
telsoa014fcda012018-03-09 14:13:49 +00001013 LayerTestResult<T,4> ret(outputInfo);
1014 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1015 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
1016 return ret;
1017}
1018
1019
1020
1021template<typename T>
1022LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
1023 armnn::IWorkloadFactory& refWorkloadFactory)
1024{
1025 unsigned int inputHeight = 8;
1026 unsigned int inputWidth = 16;
1027 unsigned int inputChannels = 3;
1028 unsigned int inputNum = 5;
1029
1030 unsigned int kernelHeight = 3;
1031 unsigned int kernelWidth = 3;
1032
1033 unsigned int strideX = 2;
1034 unsigned int strideY = 3;
1035 unsigned int padX = 1;
1036 unsigned int padY = 1;
1037
1038 unsigned int outputNum = inputNum;
1039 unsigned int outputChannels = 2;
1040 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1041 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1042
1043 armnn::TensorInfo inputTensorInfo;
1044 armnn::TensorInfo outputTensorInfo;
1045 armnn::TensorInfo kernelDesc;
1046 armnn::TensorInfo biasDesc;
1047
1048 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1049 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1050 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1051 unsigned int biasShape[] = {outputChannels};
1052
1053 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
1054 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
1055 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
1056 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
1057
1058 LayerTestResult<T,4> ret(outputTensorInfo);
1059
1060 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1061 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1062 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1063
1064 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1065 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1066
1067 armnn::Convolution2dQueueDescriptor data;
1068 armnn::WorkloadInfo info;
1069 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1070 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1071
1072 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1073 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1074
1075 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1076 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1077 data.m_Weight = &weightsTensor;
1078 data.m_Bias = &biasTensor;
1079 data.m_Parameters.m_StrideX = strideX;
1080 data.m_Parameters.m_StrideY = strideY;
1081 data.m_Parameters.m_PadLeft = padX;
1082 data.m_Parameters.m_PadRight = padX;
1083 data.m_Parameters.m_PadTop = padY;
1084 data.m_Parameters.m_PadBottom = padY;
1085 data.m_Parameters.m_BiasEnabled = true;
1086
1087 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1088 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1089
1090 armnn::Convolution2dQueueDescriptor refData = data;
1091 armnn::WorkloadInfo refInfo = info;
1092 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1093 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1094
1095 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1096 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1097
1098 outputHandleRef->Allocate();
1099 inputHandleRef->Allocate();
1100
1101 inputHandle->Allocate();
1102 outputHandle->Allocate();
1103
1104 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1105 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1106
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001107 workloadFactory.Acquire();
telsoa014fcda012018-03-09 14:13:49 +00001108 workload->Execute();
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001109 workloadFactory.Release();
1110
telsoa014fcda012018-03-09 14:13:49 +00001111 workloadRef->Execute();
1112
1113 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1114 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1115
1116 return ret;
1117}
1118
1119template<typename T>
1120LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
jimfly017af00da2018-10-31 14:43:53 +00001121 armnn::IWorkloadFactory& refWorkloadFactory,
1122 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +00001123{
1124 unsigned int inputHeight = 8;
1125 unsigned int inputWidth = 16;
1126 unsigned int inputChannels = 3;
1127 unsigned int inputNum = 5;
1128
1129 unsigned int kernelHeight = 3;
1130 unsigned int kernelWidth = 3;
1131 unsigned int channelMultiplier = 1;
1132
1133 unsigned int strideX = 2;
1134 unsigned int strideY = 3;
1135 unsigned int padX = 1;
1136 unsigned int padY = 1;
1137
1138 unsigned int outputNum = inputNum;
1139 unsigned int outputChannels = inputChannels * channelMultiplier;
1140 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1141 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1142
1143 armnn::TensorInfo inputTensorInfo;
1144 armnn::TensorInfo outputTensorInfo;
1145 armnn::TensorInfo kernelDesc;
1146 armnn::TensorInfo biasDesc;
1147
jimfly017af00da2018-10-31 14:43:53 +00001148
1149 std::vector<unsigned int> inputShape;
1150 std::vector<unsigned int> outputShape;
1151 std::vector<unsigned int> kernelShape;
1152 std::vector<unsigned int> biasShape= { outputChannels };
1153 switch (layout.GetDataLayout())
1154 {
1155 case armnn::DataLayout::NCHW:
1156 inputShape = { inputNum, inputChannels, inputHeight, inputWidth };
1157 outputShape = { outputNum, outputChannels, outputHeight, outputWidth };
1158 kernelShape = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1159 break;
1160 case armnn::DataLayout ::NHWC:
1161 inputShape = { inputNum, inputHeight, inputWidth, inputChannels };
1162 outputShape = { outputNum, outputHeight, outputWidth, outputChannels };
1163 kernelShape = { channelMultiplier, kernelHeight, kernelWidth, inputChannels };
1164 break;
1165 default:
1166 throw armnn::InvalidArgumentException("unknown data layout ["
1167 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
1168 }
telsoa014fcda012018-03-09 14:13:49 +00001169
1170 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1171 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1172 int32_t qOffset = 0;
1173
jimfly017af00da2018-10-31 14:43:53 +00001174 inputTensorInfo = armnn::TensorInfo(4, inputShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1175 outputTensorInfo = armnn::TensorInfo(4, outputShape.data(), armnn::GetDataType<T>(), outputQScale, qOffset);
1176 kernelDesc = armnn::TensorInfo(4, kernelShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1177 biasDesc = armnn::TensorInfo(
1178 1, biasShape.data(), armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
telsoa014fcda012018-03-09 14:13:49 +00001179
1180 LayerTestResult<T, 4> ret(outputTensorInfo);
1181
1182 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1183 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
jimfly01d84216a2018-10-26 12:56:21 +01001184 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1185 biasDesc, 1028, 0.0f, 255.0f);
telsoa014fcda012018-03-09 14:13:49 +00001186
1187 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1188 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1189
1190 armnn::DepthwiseConvolution2dQueueDescriptor data;
1191 armnn::WorkloadInfo info;
1192 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1193 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1194
1195 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1196 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1197
1198 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1199 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1200 data.m_Weight = &weightsTensor;
1201 data.m_Bias = &biasTensor;
1202 data.m_Parameters.m_StrideX = strideX;
1203 data.m_Parameters.m_StrideY = strideY;
1204 data.m_Parameters.m_PadLeft = padX;
1205 data.m_Parameters.m_PadRight = padX;
1206 data.m_Parameters.m_PadTop = padY;
1207 data.m_Parameters.m_PadBottom = padY;
1208 data.m_Parameters.m_BiasEnabled = true;
jimfly017af00da2018-10-31 14:43:53 +00001209 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +00001210
1211 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1212 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1213
1214 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1215 armnn::WorkloadInfo refInfo = info;
1216 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1217 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1218
1219 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1220 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1221
1222 outputHandleRef->Allocate();
1223 inputHandleRef->Allocate();
1224
1225 inputHandle->Allocate();
1226 outputHandle->Allocate();
1227
1228 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1229 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1230
1231 workload->Execute();
telsoa014fcda012018-03-09 14:13:49 +00001232 workloadRef->Execute();
1233
1234 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1235 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1236
1237 return ret;
1238}