blob: 6685a8edd20d958c911441e1e8a49ac46694df37 [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
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00007#include "WorkloadTestUtils.hpp"
8
jimfly010a088a62018-10-25 17:05:05 +01009#include <string>
telsoa014fcda012018-03-09 14:13:49 +000010#include <armnn/ArmNN.hpp>
11#include <armnn/Tensor.hpp>
12#include <armnn/TypesUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +000013
David Beckac42efd2018-09-26 17:41:13 +010014#include <test/TensorHelpers.hpp>
telsoa014fcda012018-03-09 14:13:49 +000015#include "QuantizeHelper.hpp"
16
Matthew Bentham8800c002018-11-19 13:19:28 +000017#include <backendsCommon/DataLayoutIndexed.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000018#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000019#include <backendsCommon/IBackendInternal.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000020#include <backendsCommon/WorkloadFactory.hpp>
jimfly010a088a62018-10-25 17:05:05 +010021#include "Permute.hpp"
22#include <boost/numeric/conversion/cast.hpp>
telsoa014fcda012018-03-09 14:13:49 +000023
24// Mapping from input type to bias type for fully connected layers.
25// float => float, uint8_t => int32_t
26template<typename T>
27struct FullyConnectedBiasTypeForInputType;
28
29template<>
30struct FullyConnectedBiasTypeForInputType<float>
31{
32 using Type = float;
33};
34
35template<>
36struct FullyConnectedBiasTypeForInputType<uint8_t>
37{
38 using Type = int32_t;
39};
40
telsoa01c577f2c2018-08-31 09:22:23 +010041// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000042template<typename T, typename B>
43void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
44 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
45{
46 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
47 "Invalid type and parameter combination.");
48 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
49 "Invalid type and parameter combination.");
50
telsoa01c577f2c2018-08-31 09:22:23 +010051 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000052 for (uint32_t i = 0; i < bias.size(); ++i)
53 {
54 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
55 for (uint32_t y = 0; y < h; ++y)
56 {
57 for (uint32_t x = 0; x < w; ++x)
58 {
59 uint32_t offset = (i * h + y) * w + x;
60 BOOST_ASSERT(offset < v.size());
61 T& outRef = v[offset];
62 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
63 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
64 }
65 }
66 }
67}
68
telsoa014fcda012018-03-09 14:13:49 +000069template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000070LayerTestResult<T, 4> SimpleConvolution2dTestImpl(
71 armnn::IWorkloadFactory& workloadFactory,
72 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
73 const boost::multi_array<T, 4>& originalInput,
74 const boost::multi_array<T, 4>& originalKernel,
75 const boost::multi_array<B, 1>& bias,
76 const boost::multi_array<T, 4>& originalOutputExpected,
77 float qScale,
78 int32_t qOffset,
Matthew Bentham8800c002018-11-19 13:19:28 +000079 const armnn::DataLayout layout = armnn::DataLayout::NCHW,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000080 uint32_t padLeft = 0,
81 uint32_t padTop = 0,
82 uint32_t padRight = 0,
83 uint32_t padBottom = 0)
telsoa014fcda012018-03-09 14:13:49 +000084{
jimfly010a088a62018-10-25 17:05:05 +010085 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
86 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
87 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
88 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000089
jimfly010a088a62018-10-25 17:05:05 +010090 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
91 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
92 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
93 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000094
jimfly010a088a62018-10-25 17:05:05 +010095 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
96 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
97 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
98 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000099
100 bool biasEnabled = bias.size() > 0;
101
telsoa01c577f2c2018-08-31 09:22:23 +0100102 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +0000103 BOOST_ASSERT(inputNum == 1);
104 BOOST_ASSERT(outputNum == 1);
105
telsoa01c577f2c2018-08-31 09:22:23 +0100106 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +0000107 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
108
109
telsoa01c577f2c2018-08-31 09:22:23 +0100110 // Note these tensors will use two (identical) batches.
jimfly010a088a62018-10-25 17:05:05 +0100111 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(2*inputNum, inputChannels, inputHeight, inputWidth, layout);
112 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
113 2*outputNum, outputChannels, outputHeight, outputWidth, layout);
114 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000115 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
116
117 // Set quantization parameters if the requested type is a quantized type.
118 if(armnn::IsQuantizedType<T>())
119 {
120 inputTensorInfo.SetQuantizationScale(qScale);
121 inputTensorInfo.SetQuantizationOffset(qOffset);
122 outputTensorInfo.SetQuantizationScale(qScale);
123 outputTensorInfo.SetQuantizationOffset(qOffset);
124 kernelDesc.SetQuantizationScale(qScale);
125 kernelDesc.SetQuantizationOffset(qOffset);
126 biasDesc.SetQuantizationScale(qScale*qScale);
127 biasDesc.SetQuantizationOffset(0);
128 }
129
130 LayerTestResult<T, 4> ret(outputTensorInfo);
131
telsoa01c577f2c2018-08-31 09:22:23 +0100132 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000133 std::vector<T> inputImage;
jimfly010a088a62018-10-25 17:05:05 +0100134 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000135 std::vector<T> inputData;
136 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
137 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
jimfly010a088a62018-10-25 17:05:05 +0100138
139 // at this point if we require it permute the input data
140 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000141 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100142 {
143 std::vector<T> tmp(inputData.size());
144 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
145 inputData = tmp;
146 }
147
telsoa014fcda012018-03-09 14:13:49 +0000148 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
149
150 std::vector<T> outputImage;
jimfly010a088a62018-10-25 17:05:05 +0100151 outputImage.assign(originalOutputExpected.data(),
152 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000153
telsoa01c577f2c2018-08-31 09:22:23 +0100154 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000155 if(biasEnabled)
156 {
157 std::vector<T> biasV;
158 biasV.assign(bias.data(), bias.data() + outputChannels);
159 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
160 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
161 outputWidth, outputHeight);
162 }
163
telsoa01c577f2c2018-08-31 09:22:23 +0100164 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000165 std::vector<T> outputData;
166 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
167 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
168
jimfly010a088a62018-10-25 17:05:05 +0100169 // at this point if we require it permute the expected output
Matthew Bentham8800c002018-11-19 13:19:28 +0000170 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100171 {
172 std::vector<T> tmp(outputData.size());
173 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
174 outputData = tmp;
175 }
telsoa014fcda012018-03-09 14:13:49 +0000176 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
177
telsoa01c577f2c2018-08-31 09:22:23 +0100178 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000179 uint32_t strideX = 1;
180 uint32_t strideY = 1;
181
182 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
183 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
184
185 armnn::Convolution2dQueueDescriptor data;
186 armnn::WorkloadInfo info;
187 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
188 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
jimfly010a088a62018-10-25 17:05:05 +0100189 // Permute the kernel if necessary
190 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
Matthew Bentham8800c002018-11-19 13:19:28 +0000191 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100192 {
193 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
194 }
telsoa014fcda012018-03-09 14:13:49 +0000195 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
196
197 if(biasEnabled)
198 {
199 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
200 }
201
202 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
203 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
204
205 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100206 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 +0000207 data.m_Parameters.m_StrideX = strideX;
208 data.m_Parameters.m_StrideY = strideY;
209 data.m_Parameters.m_PadLeft = padLeft;
210 data.m_Parameters.m_PadRight = padRight;
211 data.m_Parameters.m_PadTop = padTop;
212 data.m_Parameters.m_PadBottom = padBottom;
213 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000214 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000215
216 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
217 inputHandle->Allocate();
218 outputHandle->Allocate();
219
220 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
221
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000222 ExecuteWorkload(*workload, memoryManager);
surmeh013537c2c2018-05-18 16:31:43 +0100223
224 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
225
226 return ret;
227}
228
229template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000230LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(
231 armnn::IWorkloadFactory& workloadFactory,
232 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
233 const boost::multi_array<T, 4>& input,
234 const boost::multi_array<T, 4>& kernel,
235 const boost::multi_array<B, 1>& bias,
236 const boost::multi_array<T, 4>& outputExpected,
237 armnn::DataLayout dataLayout,
238 float qScale,
239 int32_t qOffset,
240 uint32_t padLeft = 1,
241 uint32_t padTop = 1,
242 uint32_t padRight = 1,
243 uint32_t padBottom = 1,
244 uint32_t strideX = 1,
245 uint32_t strideY = 1)
Francis Murtaghd59116e2018-10-04 16:03:07 +0100246{
247 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
248 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
249 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
250 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
251
252 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
253 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
254 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
255 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
256
257 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
258 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
259 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
260 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
261
262 bool biasEnabled = bias.size() > 0;
263
264 // Creates the tensors.
265 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
266 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
267 armnn::GetDataType<T>());
268 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
269 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
270
271 // Construct the input data.
272 std::vector<T> inputData;
273 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
274 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
275
276 // Construct the output data, with bias applied, as appropriate.
277 std::vector<T> outputData;
278 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
279
280 LayerTestResult<T, 4> ret(outputTensorInfo);
281 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
282
283 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
284 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
285
286 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
287 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
288
289 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
290
291 armnn::Convolution2dQueueDescriptor data;
292
293 data.m_Weight = &weightsTensor;
294 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
295 data.m_Parameters.m_StrideX = strideX;
296 data.m_Parameters.m_StrideY = strideY;
297 data.m_Parameters.m_PadLeft = padLeft;
298 data.m_Parameters.m_PadRight = padRight;
299 data.m_Parameters.m_PadTop = padTop;
300 data.m_Parameters.m_PadBottom = padBottom;
301 data.m_Parameters.m_BiasEnabled = biasEnabled;
302 data.m_Parameters.m_DataLayout = dataLayout;
303
304 armnn::WorkloadInfo info;
305 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
306 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
307
308 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
309 inputHandle->Allocate();
310 outputHandle->Allocate();
311
312 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
313
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000314 ExecuteWorkload(*workload, memoryManager);
Francis Murtaghd59116e2018-10-04 16:03:07 +0100315
316 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
317
318 return ret;
319}
320
321template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000322LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(
323 armnn::IWorkloadFactory& workloadFactory,
324 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
325 const boost::multi_array<T, 4>& input,
326 const boost::multi_array<T, 4>& originalKernel,
327 const boost::multi_array<B, 1>& bias,
328 const boost::multi_array<T, 4>& outputExpected,
329 float qScale,
330 int32_t qOffset,
Matthew Bentham8800c002018-11-19 13:19:28 +0000331 const armnn::DataLayout layout,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000332 uint32_t padLeft = 0,
333 uint32_t padTop = 0,
334 uint32_t padRight = 0,
335 uint32_t padBottom = 0,
336 uint32_t strideX = 1,
337 uint32_t strideY = 1)
surmeh013537c2c2018-05-18 16:31:43 +0100338{
339 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
340 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
341 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
342 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
jimfly01382a91d2018-10-26 15:55:50 +0100343 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
344 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
345 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
346 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +0100347 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
348 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
349 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
350 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
351
telsoa01c577f2c2018-08-31 09:22:23 +0100352 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100353 bool biasEnabled = bias.size() > 0;
354 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
355
telsoa01c577f2c2018-08-31 09:22:23 +0100356 // Creates the tensors.
jimfly01382a91d2018-10-26 15:55:50 +0100357 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
358 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
359 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelChanMul, kernelChannels, kernelHeight, kernelWidth, layout);
surmeh013537c2c2018-05-18 16:31:43 +0100360 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
361
362 // Set quantization parameters if the requested type is a quantized type.
363 if (armnn::IsQuantizedType<T>())
364 {
365 inputTensorInfo.SetQuantizationScale(qScale);
366 inputTensorInfo.SetQuantizationOffset(qOffset);
367 outputTensorInfo.SetQuantizationScale(qScale);
368 outputTensorInfo.SetQuantizationOffset(qOffset);
369 kernelDesc.SetQuantizationScale(qScale);
370 kernelDesc.SetQuantizationOffset(qOffset);
371 biasDesc.SetQuantizationScale(qScale*qScale);
372 biasDesc.SetQuantizationOffset(0);
373 }
374
telsoa01c577f2c2018-08-31 09:22:23 +0100375 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100376 std::vector<T> inputData;
377 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
jimfly01382a91d2018-10-26 15:55:50 +0100378
379 // At this point if we require it permute the input data
380 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000381 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100382 {
383 std::vector<T> tmp(inputData.size());
384 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
385 inputData = tmp;
386 }
387
surmeh013537c2c2018-05-18 16:31:43 +0100388 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
389
telsoa01c577f2c2018-08-31 09:22:23 +0100390 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100391 std::vector<T> outputData;
392 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
393 if (biasEnabled)
394 {
395 std::vector<T> biasV;
396 biasV.assign(bias.data(), bias.data() + outputChannels);
397 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
398 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
399 outputWidth, outputHeight);
400 }
401
402 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01382a91d2018-10-26 15:55:50 +0100403
404 // At this point if we require it permute the expected output
Matthew Bentham8800c002018-11-19 13:19:28 +0000405 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100406 {
407 std::vector<T> tmp(outputData.size());
408 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
409 outputData = tmp;
410 }
411
surmeh013537c2c2018-05-18 16:31:43 +0100412 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
413
414 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
415 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
416
417 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
jimfly01382a91d2018-10-26 15:55:50 +0100418
419 // Permute the kernel if necessary
420 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
Matthew Bentham8800c002018-11-19 13:19:28 +0000421 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100422 {
423 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
424 }
425
surmeh013537c2c2018-05-18 16:31:43 +0100426 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
427
428 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
429 if (biasEnabled)
430 {
431 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
432 }
433
434 armnn::DepthwiseConvolution2dQueueDescriptor data;
435 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100436 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 +0100437 data.m_Parameters.m_StrideX = strideX;
438 data.m_Parameters.m_StrideY = strideY;
439 data.m_Parameters.m_PadLeft = padLeft;
440 data.m_Parameters.m_PadRight = padRight;
441 data.m_Parameters.m_PadTop = padTop;
442 data.m_Parameters.m_PadBottom = padBottom;
443 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000444 data.m_Parameters.m_DataLayout = layout;
surmeh013537c2c2018-05-18 16:31:43 +0100445
446 armnn::WorkloadInfo info;
447 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
448 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
449
450 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
451 inputHandle->Allocate();
452 outputHandle->Allocate();
453
454 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
455
telsoa014fcda012018-03-09 14:13:49 +0000456 workload->Execute();
457
458 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
459
460 return ret;
461}
462
463template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000464LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(
465 armnn::IWorkloadFactory& workloadFactory,
466 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
467 float qScale,
468 int32_t qOffset,
469 bool biasEnabled,
Matthew Bentham8800c002018-11-19 13:19:28 +0000470 const armnn::DataLayout layout)
telsoa014fcda012018-03-09 14:13:49 +0000471{
472 unsigned int inputHeight = 3;
473 unsigned int inputWidth = 3;
474 unsigned int inputChannels = 2;
475 unsigned int inputNum = 1;
476
477 unsigned int kernelHeight = 3;
478 unsigned int kernelWidth = 3;
479 unsigned int kernelChannels = inputChannels;
480
481 unsigned int outputHeight = 1;
482 unsigned int outputWidth = 1;
483 unsigned int outputChannels = kernelChannels;
484 unsigned int outputNum = inputNum;
485
jimfly01b9c89632018-10-26 16:50:13 +0100486 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
487 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
488 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(1, outputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000489 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
490
491 // Set quantization parameters if the requested type is a quantized type.
492 if(armnn::IsQuantizedType<T>())
493 {
494 inputTensorInfo.SetQuantizationScale(qScale);
495 inputTensorInfo.SetQuantizationOffset(qOffset);
496 outputTensorInfo.SetQuantizationScale(qScale);
497 outputTensorInfo.SetQuantizationOffset(qOffset);
498 kernelDesc.SetQuantizationScale(qScale);
499 kernelDesc.SetQuantizationOffset(qOffset);
500 biasDesc.SetQuantizationScale(qScale*qScale);
501 biasDesc.SetQuantizationOffset(0);
502 }
jimfly01b9c89632018-10-26 16:50:13 +0100503 std::vector<T> inputData = std::vector<T>(
504 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
505 1.f, 2.f, 1.f,
506 2.f, 1.f, 2.f,
507 1.f, 2.f, 1.f,
telsoa014fcda012018-03-09 14:13:49 +0000508
jimfly01b9c89632018-10-26 16:50:13 +0100509 1.f, 2.f, 1.f,
510 2.f, 1.f, 2.f,
511 1.f, 2.f, 1.f,
512 }));
513 // at this point if we require it permute the input data
514 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000515 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100516 {
517 std::vector<T> tmp(inputData.size());
518 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
519 inputData = tmp;
520 }
521 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000522
523 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
524 {0, 2}));
525 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
526
jimfly01b9c89632018-10-26 16:50:13 +0100527 std::vector<T> kernelData = std::vector<T>(
528 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
529 1.f, 0.f, 1.f,
530 0.f, 0.f, 0.f,
531 -1.f, 0.f, -1.f,
telsoa014fcda012018-03-09 14:13:49 +0000532
jimfly01b9c89632018-10-26 16:50:13 +0100533 1.f, 0.f, 1.f,
534 0.f, 0.f, 0.f,
535 -1.f, 0.f, -1.f,
536 }));
Matthew Bentham8800c002018-11-19 13:19:28 +0000537 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100538 {
539 std::vector<T> tmp(kernelData.size());
540 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, kernelData.data(), tmp.data());
541 kernelData = tmp;
542 }
543 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000544
telsoa01c577f2c2018-08-31 09:22:23 +0100545 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000546 std::vector<T> outputImage(
547 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
548 outputTensorInfo.GetQuantizationOffset(),
549 {0.f, 0.f})
550 );
551
telsoa01c577f2c2018-08-31 09:22:23 +0100552 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000553 if(biasEnabled)
554 {
555 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
556 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
557 outputWidth, outputHeight);
558 }
559
560 LayerTestResult<T, 4> ret(outputTensorInfo);
Matthew Bentham8800c002018-11-19 13:19:28 +0000561 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100562 {
563 std::vector<T> tmp(outputImage.size());
564 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputImage.data(), tmp.data());
565 outputImage = tmp;
566 }
567
telsoa014fcda012018-03-09 14:13:49 +0000568 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
569
570 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
571 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
572
573 armnn::DepthwiseConvolution2dQueueDescriptor data;
574 armnn::WorkloadInfo info;
575 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
576 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
577
578 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
579 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
580
581 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
582 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
583
584 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100585 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000586 data.m_Parameters.m_StrideX = 1;
587 data.m_Parameters.m_StrideY = 1;
588 data.m_Parameters.m_PadLeft = 0;
589 data.m_Parameters.m_PadRight = 0;
590 data.m_Parameters.m_PadTop = 0;
591 data.m_Parameters.m_PadBottom = 0;
592 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000593 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000594
595 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
596 inputHandle->Allocate();
597 outputHandle->Allocate();
598
599 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
600
601 workload->Execute();
602
603 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
604
605 return ret;
606}
607
608template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000609LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(
610 armnn::IWorkloadFactory& workloadFactory,
611 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
612 float qScale,
613 int32_t qOffset,
614 bool biasEnabled,
Matthew Bentham8800c002018-11-19 13:19:28 +0000615 const armnn::DataLayout layout)
telsoa014fcda012018-03-09 14:13:49 +0000616{
617 unsigned int depthMultiplier = 2;
618
619 unsigned int inputHeight = 8;
620 unsigned int inputWidth = 16;
621 unsigned int inputChannels = 2;
622 unsigned int inputBatchSize = 1;
623
624 unsigned int kernelHeight = 5;
625 unsigned int kernelWidth = 3;
626
627 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
628 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
629 unsigned int outputChannels = inputChannels * depthMultiplier;
630 unsigned int outputBatchSize = inputBatchSize;
631
jimfly01d84216a2018-10-26 12:56:21 +0100632 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(
633 inputBatchSize, inputChannels, inputHeight, inputWidth, layout);
634 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
635 outputBatchSize, outputChannels, outputHeight, outputWidth, layout);
636 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(
637 depthMultiplier, inputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000638 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
639
640 // Set quantization parameters if the requested type is a quantized type.
641 if(armnn::IsQuantizedType<T>())
642 {
643 inputTensorInfo.SetQuantizationScale(qScale);
644 inputTensorInfo.SetQuantizationOffset(qOffset);
645 outputTensorInfo.SetQuantizationScale(qScale);
646 outputTensorInfo.SetQuantizationOffset(qOffset);
647 kernelDesc.SetQuantizationScale(qScale);
648 kernelDesc.SetQuantizationOffset(qOffset);
649 biasDesc.SetQuantizationScale(qScale*qScale);
650 biasDesc.SetQuantizationOffset(0);
651 }
652
jimfly01d84216a2018-10-26 12:56:21 +0100653 // NOTE: originalInputData is in NCHW format
654 std::vector<T> originalInputData = std::vector<T>(
655 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
656 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,
657 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,
658 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,
659 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,
660 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,
661 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,
662 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,
663 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,
664 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
665 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
666 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
667 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
668 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
669 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
670 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
671 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
672 }));
673 std::vector<T> inputData = originalInputData;
674 // at this point if we require it permute the input data
675 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000676 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100677 {
678 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, originalInputData.data(), inputData.data());
679 }
680 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000681
682 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
683 {0, 2, 1, -1}));
684 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
685
jimfly01d84216a2018-10-26 12:56:21 +0100686 std::vector<T> originalKernelData = std::vector<T>(
687 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
688 1, 1, 1,
689 1, -1, 1,
690 1, 1, 1,
691 1, 1, 1,
692 1, 1, 1,
telsoa014fcda012018-03-09 14:13:49 +0000693
jimfly01d84216a2018-10-26 12:56:21 +0100694 2, 2, 2,
695 2, 2, 2,
696 2, 2, 2,
697 2, 2, 2,
698 2, 2, 2,
telsoa014fcda012018-03-09 14:13:49 +0000699
jimfly01d84216a2018-10-26 12:56:21 +0100700 0, 0, 0,
701 0, -1, 0,
702 0, 0, 0,
703 0, 0, 0,
704 0, 0, 0,
telsoa014fcda012018-03-09 14:13:49 +0000705
jimfly01d84216a2018-10-26 12:56:21 +0100706 0, 0, 0,
707 0, 0, 0,
708 0, 1, 0,
709 0, 0, 0,
710 0, 0, 0
711 }));
712 std::vector<T> kernelData = originalKernelData;
Matthew Bentham8800c002018-11-19 13:19:28 +0000713 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100714 {
715 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernelData.data(), kernelData.data());
716 }
717 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000718
telsoa01c577f2c2018-08-31 09:22:23 +0100719 // Manually calculated.
jimfly01d84216a2018-10-26 12:56:21 +0100720 std::vector<T> originalOutputImage = std::vector<T>(
telsoa014fcda012018-03-09 14:13:49 +0000721 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
722 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
723 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
724 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
725 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
726 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
727 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
728
729 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
730 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
731 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
732 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
733 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
734 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
735
736 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
737 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
738 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
739 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
740 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
741 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
742
743 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
744 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
745 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
746 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
747 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
748 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
749 }));
750
telsoa01c577f2c2018-08-31 09:22:23 +0100751 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000752 if(biasEnabled)
753 {
jimfly01d84216a2018-10-26 12:56:21 +0100754 ApplyBias(originalOutputImage,
755 outputTensorInfo.GetQuantizationScale(),
756 outputTensorInfo.GetQuantizationOffset(),
757 biasV,
758 biasDesc.GetQuantizationScale(),
759 biasDesc.GetQuantizationOffset(),
760 outputWidth,
761 outputHeight);
telsoa014fcda012018-03-09 14:13:49 +0000762 }
763
764 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01d84216a2018-10-26 12:56:21 +0100765 std::vector<T> outputImage = originalOutputImage;
Matthew Bentham8800c002018-11-19 13:19:28 +0000766 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100767 {
768 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, originalOutputImage.data(), outputImage.data());
769 }
770
telsoa014fcda012018-03-09 14:13:49 +0000771 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
772
773 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
774 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
775
776 armnn::DepthwiseConvolution2dQueueDescriptor data;
777 armnn::WorkloadInfo info;
778 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
779 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
780
781 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
782 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
783
784 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
785 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
786
787 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100788 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000789 data.m_Parameters.m_StrideX = 2;
790 data.m_Parameters.m_StrideY = 1;
791 data.m_Parameters.m_PadLeft = 0;
792 data.m_Parameters.m_PadRight = 0;
793 data.m_Parameters.m_PadTop = 1;
794 data.m_Parameters.m_PadBottom = 1;
795 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000796 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000797
798 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
799 inputHandle->Allocate();
800 outputHandle->Allocate();
801
802 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
803
804 workload->Execute();
805
806 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
807
808 return ret;
809}
810
Nikhil Rajcec6b652018-10-12 13:51:57 +0100811template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000812LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(
813 armnn::IWorkloadFactory& workloadFactory,
814 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
815 const boost::multi_array<T, 4>& input,
816 const boost::multi_array<T, 4>& kernel,
817 const boost::multi_array<B, 1>& bias,
818 const boost::multi_array<T, 4>& outputExpected,
819 float qScale,
820 int32_t qOffset,
821 uint32_t padLeft = 0,
822 uint32_t padTop = 0,
823 uint32_t padRight = 0,
824 uint32_t padBottom = 0,
825 uint32_t strideX = 1,
826 uint32_t strideY = 1)
Nikhil Rajcec6b652018-10-12 13:51:57 +0100827{
828 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
829 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
830 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
831 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
832
833 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
834 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
835 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
836 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
837
838 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
839 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
840 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
841 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
842
843 // Creates the tensors.
844 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
845 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
846 armnn::GetDataType<T>());
847 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
848 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
849
850 // Set quantization parameters if the requested type is a quantized type.
851 if (armnn::IsQuantizedType<T>())
852 {
853 inputTensorInfo.SetQuantizationScale(qScale);
854 inputTensorInfo.SetQuantizationOffset(qOffset);
855 outputTensorInfo.SetQuantizationScale(qScale);
856 outputTensorInfo.SetQuantizationOffset(qOffset);
857 kernelDesc.SetQuantizationScale(qScale);
858 kernelDesc.SetQuantizationOffset(qOffset);
859 biasDesc.SetQuantizationScale(qScale*qScale);
860 biasDesc.SetQuantizationOffset(0);
861 }
862
863 // Construct the input data.
864 std::vector<T> inputData;
865 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
866 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
867
868 // Construct the output data, with bias applied, as appropriate.
869 std::vector<T> outputData;
870 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
871
872 LayerTestResult<T, 4> ret(outputTensorInfo);
873 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
874
875 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
876 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
877
878 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
879 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
880
881 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
882
883 armnn::DepthwiseConvolution2dQueueDescriptor data;
884 data.m_Weight = &weightsTensor;
885 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
886 data.m_Parameters.m_StrideX = strideX;
887 data.m_Parameters.m_StrideY = strideY;
888 data.m_Parameters.m_PadLeft = padLeft;
889 data.m_Parameters.m_PadRight = padRight;
890 data.m_Parameters.m_PadTop = padTop;
891 data.m_Parameters.m_PadBottom = padBottom;
892 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
893
894 armnn::WorkloadInfo info;
895 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
896 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
897
898 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
899
900 inputHandle->Allocate();
901 outputHandle->Allocate();
902
903 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
904
Nikhil Rajcec6b652018-10-12 13:51:57 +0100905 workload->Execute();
906
907 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
908
909 return ret;
910}
911
telsoa014fcda012018-03-09 14:13:49 +0000912template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000913LayerTestResult<T,4> Convolution1dTestImpl(
914 armnn::IWorkloadFactory& workloadFactory,
915 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
916 float qScale,
917 int32_t qOffset,
918 bool biasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000919{
920 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
921
telsoa01c577f2c2018-08-31 09:22:23 +0100922 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000923 // 2D convolution with the final dimension set to 1.
924 // I don't anticipate this being particularly slow, given that convolution is implemented
925 // as a matrix multiplication, at which point dimension doesn't matter.
926
927 unsigned int batchSize = 1;
928 unsigned int inputChannels = 2;
929 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100930 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000931 unsigned int kernelSize = 3;
932 unsigned int padSize = 2;
933 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100934 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000935
936 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
937 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
938 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
939 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
940
941 // Set quantization parameters if the requested type is a quantized type.
942 if(armnn::IsQuantizedType<T>())
943 {
944 inputInfo.SetQuantizationScale(qScale);
945 inputInfo.SetQuantizationOffset(qOffset);
946 outputInfo.SetQuantizationScale(qScale);
947 outputInfo.SetQuantizationOffset(qOffset);
948 kernelInfo.SetQuantizationScale(qScale);
949 kernelInfo.SetQuantizationOffset(qOffset);
950 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
951 biasInfo.SetQuantizationOffset(0);
952 }
953
954 std::vector<T> inputData(
955 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
956 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
957 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
958 }));
959
960 std::vector<T> kernelData(
961 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
962 1.0f, 0.0f, 0.0f,
963 0.0f, 2.0f, -1.5f,
964
965 0.0f, 0.0f, 0.0f,
966 0.2f, 0.2f, 0.2f,
967
968 0.5f, 0.0f, 0.5f,
969 0.0f, -1.0f, 0.0f
970 }));
971
972 std::vector<B> biasData(
973 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
974 1.0f, 0.0f, 0.0f
975 }));
976
977 std::vector<T> outputData(
978 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
979 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,
980 -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,
981 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
982 }));
983
telsoa01c577f2c2018-08-31 09:22:23 +0100984 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000985 if(biasEnabled)
986 {
987 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
988 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
989 1, outputSize);
990 }
991
992 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
993 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
994
995 armnn::Convolution2dQueueDescriptor data;
996 armnn::WorkloadInfo info;
997 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
998 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
999
1000 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
1001 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
1002
1003 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
1004 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
1005
1006 data.m_Weight = &weightsTensor;
1007 data.m_Bias = &biasTensor;
1008 data.m_Parameters.m_StrideX = 1;
1009 data.m_Parameters.m_StrideY = stride;
1010 data.m_Parameters.m_PadLeft = 0;
1011 data.m_Parameters.m_PadRight = 0;
1012 data.m_Parameters.m_PadTop = padSize;
1013 data.m_Parameters.m_PadBottom = padSize;
1014 data.m_Parameters.m_BiasEnabled = biasEnabled;
1015
1016 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1017 inputHandle->Allocate();
1018 outputHandle->Allocate();
1019
1020 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
1021
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001022 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +00001023
telsoa01c577f2c2018-08-31 09:22:23 +01001024 // Output
telsoa014fcda012018-03-09 14:13:49 +00001025 LayerTestResult<T,4> ret(outputInfo);
1026 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1027 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
1028 return ret;
1029}
1030
1031
1032
1033template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001034LayerTestResult<T,4> CompareConvolution2dTestImpl(
1035 armnn::IWorkloadFactory& workloadFactory,
1036 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1037 armnn::IWorkloadFactory& refWorkloadFactory)
telsoa014fcda012018-03-09 14:13:49 +00001038{
1039 unsigned int inputHeight = 8;
1040 unsigned int inputWidth = 16;
1041 unsigned int inputChannels = 3;
1042 unsigned int inputNum = 5;
1043
1044 unsigned int kernelHeight = 3;
1045 unsigned int kernelWidth = 3;
1046
1047 unsigned int strideX = 2;
1048 unsigned int strideY = 3;
1049 unsigned int padX = 1;
1050 unsigned int padY = 1;
1051
1052 unsigned int outputNum = inputNum;
1053 unsigned int outputChannels = 2;
1054 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1055 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1056
1057 armnn::TensorInfo inputTensorInfo;
1058 armnn::TensorInfo outputTensorInfo;
1059 armnn::TensorInfo kernelDesc;
1060 armnn::TensorInfo biasDesc;
1061
1062 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1063 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1064 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1065 unsigned int biasShape[] = {outputChannels};
1066
1067 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
1068 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
1069 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
1070 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
1071
1072 LayerTestResult<T,4> ret(outputTensorInfo);
1073
1074 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1075 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1076 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1077
1078 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1079 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1080
1081 armnn::Convolution2dQueueDescriptor data;
1082 armnn::WorkloadInfo info;
1083 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1084 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1085
1086 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1087 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1088
1089 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1090 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1091 data.m_Weight = &weightsTensor;
1092 data.m_Bias = &biasTensor;
1093 data.m_Parameters.m_StrideX = strideX;
1094 data.m_Parameters.m_StrideY = strideY;
1095 data.m_Parameters.m_PadLeft = padX;
1096 data.m_Parameters.m_PadRight = padX;
1097 data.m_Parameters.m_PadTop = padY;
1098 data.m_Parameters.m_PadBottom = padY;
1099 data.m_Parameters.m_BiasEnabled = true;
1100
1101 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1102 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1103
1104 armnn::Convolution2dQueueDescriptor refData = data;
1105 armnn::WorkloadInfo refInfo = info;
1106 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1107 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1108
1109 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1110 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1111
1112 outputHandleRef->Allocate();
1113 inputHandleRef->Allocate();
1114
1115 inputHandle->Allocate();
1116 outputHandle->Allocate();
1117
1118 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1119 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1120
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001121 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001122
telsoa014fcda012018-03-09 14:13:49 +00001123 workloadRef->Execute();
1124
1125 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1126 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1127
1128 return ret;
1129}
1130
1131template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001132LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(
1133 armnn::IWorkloadFactory& workloadFactory,
1134 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1135 armnn::IWorkloadFactory& refWorkloadFactory,
1136 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +00001137{
1138 unsigned int inputHeight = 8;
1139 unsigned int inputWidth = 16;
1140 unsigned int inputChannels = 3;
1141 unsigned int inputNum = 5;
1142
1143 unsigned int kernelHeight = 3;
1144 unsigned int kernelWidth = 3;
1145 unsigned int channelMultiplier = 1;
1146
1147 unsigned int strideX = 2;
1148 unsigned int strideY = 3;
1149 unsigned int padX = 1;
1150 unsigned int padY = 1;
1151
1152 unsigned int outputNum = inputNum;
1153 unsigned int outputChannels = inputChannels * channelMultiplier;
1154 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1155 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1156
1157 armnn::TensorInfo inputTensorInfo;
1158 armnn::TensorInfo outputTensorInfo;
1159 armnn::TensorInfo kernelDesc;
1160 armnn::TensorInfo biasDesc;
1161
jimfly017af00da2018-10-31 14:43:53 +00001162
1163 std::vector<unsigned int> inputShape;
1164 std::vector<unsigned int> outputShape;
1165 std::vector<unsigned int> kernelShape;
1166 std::vector<unsigned int> biasShape= { outputChannels };
1167 switch (layout.GetDataLayout())
1168 {
1169 case armnn::DataLayout::NCHW:
1170 inputShape = { inputNum, inputChannels, inputHeight, inputWidth };
1171 outputShape = { outputNum, outputChannels, outputHeight, outputWidth };
1172 kernelShape = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1173 break;
1174 case armnn::DataLayout ::NHWC:
1175 inputShape = { inputNum, inputHeight, inputWidth, inputChannels };
1176 outputShape = { outputNum, outputHeight, outputWidth, outputChannels };
1177 kernelShape = { channelMultiplier, kernelHeight, kernelWidth, inputChannels };
1178 break;
1179 default:
1180 throw armnn::InvalidArgumentException("unknown data layout ["
1181 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
1182 }
telsoa014fcda012018-03-09 14:13:49 +00001183
1184 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1185 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1186 int32_t qOffset = 0;
1187
jimfly017af00da2018-10-31 14:43:53 +00001188 inputTensorInfo = armnn::TensorInfo(4, inputShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1189 outputTensorInfo = armnn::TensorInfo(4, outputShape.data(), armnn::GetDataType<T>(), outputQScale, qOffset);
1190 kernelDesc = armnn::TensorInfo(4, kernelShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1191 biasDesc = armnn::TensorInfo(
1192 1, biasShape.data(), armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
telsoa014fcda012018-03-09 14:13:49 +00001193
1194 LayerTestResult<T, 4> ret(outputTensorInfo);
1195
1196 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1197 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
jimfly01d84216a2018-10-26 12:56:21 +01001198 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1199 biasDesc, 1028, 0.0f, 255.0f);
telsoa014fcda012018-03-09 14:13:49 +00001200
1201 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1202 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1203
1204 armnn::DepthwiseConvolution2dQueueDescriptor data;
1205 armnn::WorkloadInfo info;
1206 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1207 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1208
1209 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1210 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1211
1212 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1213 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1214 data.m_Weight = &weightsTensor;
1215 data.m_Bias = &biasTensor;
1216 data.m_Parameters.m_StrideX = strideX;
1217 data.m_Parameters.m_StrideY = strideY;
1218 data.m_Parameters.m_PadLeft = padX;
1219 data.m_Parameters.m_PadRight = padX;
1220 data.m_Parameters.m_PadTop = padY;
1221 data.m_Parameters.m_PadBottom = padY;
1222 data.m_Parameters.m_BiasEnabled = true;
jimfly017af00da2018-10-31 14:43:53 +00001223 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +00001224
1225 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1226 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1227
1228 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1229 armnn::WorkloadInfo refInfo = info;
1230 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1231 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1232
1233 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1234 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1235
1236 outputHandleRef->Allocate();
1237 inputHandleRef->Allocate();
1238
1239 inputHandle->Allocate();
1240 outputHandle->Allocate();
1241
1242 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1243 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1244
1245 workload->Execute();
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001246
telsoa014fcda012018-03-09 14:13:49 +00001247 workloadRef->Execute();
1248
1249 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1250 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1251
1252 return ret;
1253}