blob: d99b7f7fa3657e873be7cb00ac00f871fdf0f8ca [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
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000017#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000018#include <backendsCommon/IBackendInternal.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000019#include <backendsCommon/WorkloadFactory.hpp>
jimfly010a088a62018-10-25 17:05:05 +010020#include "Permute.hpp"
21#include <boost/numeric/conversion/cast.hpp>
telsoa014fcda012018-03-09 14:13:49 +000022
23// Mapping from input type to bias type for fully connected layers.
24// float => float, uint8_t => int32_t
25template<typename T>
26struct FullyConnectedBiasTypeForInputType;
27
28template<>
29struct FullyConnectedBiasTypeForInputType<float>
30{
31 using Type = float;
32};
33
34template<>
35struct FullyConnectedBiasTypeForInputType<uint8_t>
36{
37 using Type = int32_t;
38};
39
telsoa01c577f2c2018-08-31 09:22:23 +010040// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000041template<typename T, typename B>
42void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
43 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
44{
45 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
46 "Invalid type and parameter combination.");
47 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
48 "Invalid type and parameter combination.");
49
telsoa01c577f2c2018-08-31 09:22:23 +010050 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000051 for (uint32_t i = 0; i < bias.size(); ++i)
52 {
53 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
54 for (uint32_t y = 0; y < h; ++y)
55 {
56 for (uint32_t x = 0; x < w; ++x)
57 {
58 uint32_t offset = (i * h + y) * w + x;
59 BOOST_ASSERT(offset < v.size());
60 T& outRef = v[offset];
61 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
62 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
63 }
64 }
65 }
66}
67
telsoa014fcda012018-03-09 14:13:49 +000068template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000069LayerTestResult<T, 4> SimpleConvolution2dTestImpl(
70 armnn::IWorkloadFactory& workloadFactory,
71 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
72 const boost::multi_array<T, 4>& originalInput,
73 const boost::multi_array<T, 4>& originalKernel,
74 const boost::multi_array<B, 1>& bias,
75 const boost::multi_array<T, 4>& originalOutputExpected,
76 float qScale,
77 int32_t qOffset,
78 const armnn::DataLayoutIndexed& layout = armnn::DataLayout::NCHW,
79 uint32_t padLeft = 0,
80 uint32_t padTop = 0,
81 uint32_t padRight = 0,
82 uint32_t padBottom = 0)
telsoa014fcda012018-03-09 14:13:49 +000083{
jimfly010a088a62018-10-25 17:05:05 +010084 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
85 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
86 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
87 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000088
jimfly010a088a62018-10-25 17:05:05 +010089 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
90 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
91 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
92 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000093
jimfly010a088a62018-10-25 17:05:05 +010094 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
95 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
96 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
97 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000098
99 bool biasEnabled = bias.size() > 0;
100
telsoa01c577f2c2018-08-31 09:22:23 +0100101 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +0000102 BOOST_ASSERT(inputNum == 1);
103 BOOST_ASSERT(outputNum == 1);
104
telsoa01c577f2c2018-08-31 09:22:23 +0100105 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +0000106 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
107
108
telsoa01c577f2c2018-08-31 09:22:23 +0100109 // Note these tensors will use two (identical) batches.
jimfly010a088a62018-10-25 17:05:05 +0100110 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(2*inputNum, inputChannels, inputHeight, inputWidth, layout);
111 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
112 2*outputNum, outputChannels, outputHeight, outputWidth, layout);
113 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000114 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
115
116 // Set quantization parameters if the requested type is a quantized type.
117 if(armnn::IsQuantizedType<T>())
118 {
119 inputTensorInfo.SetQuantizationScale(qScale);
120 inputTensorInfo.SetQuantizationOffset(qOffset);
121 outputTensorInfo.SetQuantizationScale(qScale);
122 outputTensorInfo.SetQuantizationOffset(qOffset);
123 kernelDesc.SetQuantizationScale(qScale);
124 kernelDesc.SetQuantizationOffset(qOffset);
125 biasDesc.SetQuantizationScale(qScale*qScale);
126 biasDesc.SetQuantizationOffset(0);
127 }
128
129 LayerTestResult<T, 4> ret(outputTensorInfo);
130
telsoa01c577f2c2018-08-31 09:22:23 +0100131 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000132 std::vector<T> inputImage;
jimfly010a088a62018-10-25 17:05:05 +0100133 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000134 std::vector<T> inputData;
135 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
136 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
jimfly010a088a62018-10-25 17:05:05 +0100137
138 // at this point if we require it permute the input data
139 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
140 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
141 {
142 std::vector<T> tmp(inputData.size());
143 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
144 inputData = tmp;
145 }
146
telsoa014fcda012018-03-09 14:13:49 +0000147 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
148
149 std::vector<T> outputImage;
jimfly010a088a62018-10-25 17:05:05 +0100150 outputImage.assign(originalOutputExpected.data(),
151 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000152
telsoa01c577f2c2018-08-31 09:22:23 +0100153 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000154 if(biasEnabled)
155 {
156 std::vector<T> biasV;
157 biasV.assign(bias.data(), bias.data() + outputChannels);
158 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
159 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
160 outputWidth, outputHeight);
161 }
162
telsoa01c577f2c2018-08-31 09:22:23 +0100163 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000164 std::vector<T> outputData;
165 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
166 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
167
jimfly010a088a62018-10-25 17:05:05 +0100168 // at this point if we require it permute the expected output
169 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
170 {
171 std::vector<T> tmp(outputData.size());
172 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
173 outputData = tmp;
174 }
telsoa014fcda012018-03-09 14:13:49 +0000175 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
176
telsoa01c577f2c2018-08-31 09:22:23 +0100177 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000178 uint32_t strideX = 1;
179 uint32_t strideY = 1;
180
181 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
182 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
183
184 armnn::Convolution2dQueueDescriptor data;
185 armnn::WorkloadInfo info;
186 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
187 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
jimfly010a088a62018-10-25 17:05:05 +0100188 // Permute the kernel if necessary
189 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
190 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
191 {
192 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
193 }
telsoa014fcda012018-03-09 14:13:49 +0000194 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
195
196 if(biasEnabled)
197 {
198 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
199 }
200
201 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
202 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
203
204 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100205 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 +0000206 data.m_Parameters.m_StrideX = strideX;
207 data.m_Parameters.m_StrideY = strideY;
208 data.m_Parameters.m_PadLeft = padLeft;
209 data.m_Parameters.m_PadRight = padRight;
210 data.m_Parameters.m_PadTop = padTop;
211 data.m_Parameters.m_PadBottom = padBottom;
212 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly010a088a62018-10-25 17:05:05 +0100213 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000214
215 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
216 inputHandle->Allocate();
217 outputHandle->Allocate();
218
219 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
220
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000221 ExecuteWorkload(*workload, memoryManager);
surmeh013537c2c2018-05-18 16:31:43 +0100222
223 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
224
225 return ret;
226}
227
228template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000229LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(
230 armnn::IWorkloadFactory& workloadFactory,
231 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
232 const boost::multi_array<T, 4>& input,
233 const boost::multi_array<T, 4>& kernel,
234 const boost::multi_array<B, 1>& bias,
235 const boost::multi_array<T, 4>& outputExpected,
236 armnn::DataLayout dataLayout,
237 float qScale,
238 int32_t qOffset,
239 uint32_t padLeft = 1,
240 uint32_t padTop = 1,
241 uint32_t padRight = 1,
242 uint32_t padBottom = 1,
243 uint32_t strideX = 1,
244 uint32_t strideY = 1)
Francis Murtaghd59116e2018-10-04 16:03:07 +0100245{
246 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
247 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
248 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
249 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
250
251 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
252 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
253 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
254 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
255
256 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
257 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
258 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
259 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
260
261 bool biasEnabled = bias.size() > 0;
262
263 // Creates the tensors.
264 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
265 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
266 armnn::GetDataType<T>());
267 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
268 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
269
270 // Construct the input data.
271 std::vector<T> inputData;
272 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
273 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
274
275 // Construct the output data, with bias applied, as appropriate.
276 std::vector<T> outputData;
277 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
278
279 LayerTestResult<T, 4> ret(outputTensorInfo);
280 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
281
282 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
283 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
284
285 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
286 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
287
288 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
289
290 armnn::Convolution2dQueueDescriptor data;
291
292 data.m_Weight = &weightsTensor;
293 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
294 data.m_Parameters.m_StrideX = strideX;
295 data.m_Parameters.m_StrideY = strideY;
296 data.m_Parameters.m_PadLeft = padLeft;
297 data.m_Parameters.m_PadRight = padRight;
298 data.m_Parameters.m_PadTop = padTop;
299 data.m_Parameters.m_PadBottom = padBottom;
300 data.m_Parameters.m_BiasEnabled = biasEnabled;
301 data.m_Parameters.m_DataLayout = dataLayout;
302
303 armnn::WorkloadInfo info;
304 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
305 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
306
307 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
308 inputHandle->Allocate();
309 outputHandle->Allocate();
310
311 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
312
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000313 ExecuteWorkload(*workload, memoryManager);
Francis Murtaghd59116e2018-10-04 16:03:07 +0100314
315 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
316
317 return ret;
318}
319
320template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000321LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(
322 armnn::IWorkloadFactory& workloadFactory,
323 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
324 const boost::multi_array<T, 4>& input,
325 const boost::multi_array<T, 4>& originalKernel,
326 const boost::multi_array<B, 1>& bias,
327 const boost::multi_array<T, 4>& outputExpected,
328 float qScale,
329 int32_t qOffset,
330 const armnn::DataLayoutIndexed& layout,
331 uint32_t padLeft = 0,
332 uint32_t padTop = 0,
333 uint32_t padRight = 0,
334 uint32_t padBottom = 0,
335 uint32_t strideX = 1,
336 uint32_t strideY = 1)
surmeh013537c2c2018-05-18 16:31:43 +0100337{
338 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
339 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
340 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
341 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
jimfly01382a91d2018-10-26 15:55:50 +0100342 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
343 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
344 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
345 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +0100346 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
347 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
348 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
349 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
350
telsoa01c577f2c2018-08-31 09:22:23 +0100351 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100352 bool biasEnabled = bias.size() > 0;
353 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
354
telsoa01c577f2c2018-08-31 09:22:23 +0100355 // Creates the tensors.
jimfly01382a91d2018-10-26 15:55:50 +0100356 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
357 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
358 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelChanMul, kernelChannels, kernelHeight, kernelWidth, layout);
surmeh013537c2c2018-05-18 16:31:43 +0100359 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
360
361 // Set quantization parameters if the requested type is a quantized type.
362 if (armnn::IsQuantizedType<T>())
363 {
364 inputTensorInfo.SetQuantizationScale(qScale);
365 inputTensorInfo.SetQuantizationOffset(qOffset);
366 outputTensorInfo.SetQuantizationScale(qScale);
367 outputTensorInfo.SetQuantizationOffset(qOffset);
368 kernelDesc.SetQuantizationScale(qScale);
369 kernelDesc.SetQuantizationOffset(qOffset);
370 biasDesc.SetQuantizationScale(qScale*qScale);
371 biasDesc.SetQuantizationOffset(0);
372 }
373
telsoa01c577f2c2018-08-31 09:22:23 +0100374 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100375 std::vector<T> inputData;
376 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
jimfly01382a91d2018-10-26 15:55:50 +0100377
378 // At this point if we require it permute the input data
379 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
380 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
381 {
382 std::vector<T> tmp(inputData.size());
383 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
384 inputData = tmp;
385 }
386
surmeh013537c2c2018-05-18 16:31:43 +0100387 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
388
telsoa01c577f2c2018-08-31 09:22:23 +0100389 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100390 std::vector<T> outputData;
391 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
392 if (biasEnabled)
393 {
394 std::vector<T> biasV;
395 biasV.assign(bias.data(), bias.data() + outputChannels);
396 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
397 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
398 outputWidth, outputHeight);
399 }
400
401 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01382a91d2018-10-26 15:55:50 +0100402
403 // At this point if we require it permute the expected output
404 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
405 {
406 std::vector<T> tmp(outputData.size());
407 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
408 outputData = tmp;
409 }
410
surmeh013537c2c2018-05-18 16:31:43 +0100411 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
412
413 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
414 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
415
416 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
jimfly01382a91d2018-10-26 15:55:50 +0100417
418 // Permute the kernel if necessary
419 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
420 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
421 {
422 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
423 }
424
surmeh013537c2c2018-05-18 16:31:43 +0100425 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
426
427 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
428 if (biasEnabled)
429 {
430 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
431 }
432
433 armnn::DepthwiseConvolution2dQueueDescriptor data;
434 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100435 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 +0100436 data.m_Parameters.m_StrideX = strideX;
437 data.m_Parameters.m_StrideY = strideY;
438 data.m_Parameters.m_PadLeft = padLeft;
439 data.m_Parameters.m_PadRight = padRight;
440 data.m_Parameters.m_PadTop = padTop;
441 data.m_Parameters.m_PadBottom = padBottom;
442 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01382a91d2018-10-26 15:55:50 +0100443 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
surmeh013537c2c2018-05-18 16:31:43 +0100444
445 armnn::WorkloadInfo info;
446 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
447 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
448
449 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
450 inputHandle->Allocate();
451 outputHandle->Allocate();
452
453 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
454
telsoa014fcda012018-03-09 14:13:49 +0000455 workload->Execute();
456
457 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
458
459 return ret;
460}
461
462template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000463LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(
464 armnn::IWorkloadFactory& workloadFactory,
465 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
466 float qScale,
467 int32_t qOffset,
468 bool biasEnabled,
469 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +0000470{
471 unsigned int inputHeight = 3;
472 unsigned int inputWidth = 3;
473 unsigned int inputChannels = 2;
474 unsigned int inputNum = 1;
475
476 unsigned int kernelHeight = 3;
477 unsigned int kernelWidth = 3;
478 unsigned int kernelChannels = inputChannels;
479
480 unsigned int outputHeight = 1;
481 unsigned int outputWidth = 1;
482 unsigned int outputChannels = kernelChannels;
483 unsigned int outputNum = inputNum;
484
jimfly01b9c89632018-10-26 16:50:13 +0100485 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
486 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
487 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(1, outputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000488 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
489
490 // Set quantization parameters if the requested type is a quantized type.
491 if(armnn::IsQuantizedType<T>())
492 {
493 inputTensorInfo.SetQuantizationScale(qScale);
494 inputTensorInfo.SetQuantizationOffset(qOffset);
495 outputTensorInfo.SetQuantizationScale(qScale);
496 outputTensorInfo.SetQuantizationOffset(qOffset);
497 kernelDesc.SetQuantizationScale(qScale);
498 kernelDesc.SetQuantizationOffset(qOffset);
499 biasDesc.SetQuantizationScale(qScale*qScale);
500 biasDesc.SetQuantizationOffset(0);
501 }
jimfly01b9c89632018-10-26 16:50:13 +0100502 std::vector<T> inputData = std::vector<T>(
503 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
504 1.f, 2.f, 1.f,
505 2.f, 1.f, 2.f,
506 1.f, 2.f, 1.f,
telsoa014fcda012018-03-09 14:13:49 +0000507
jimfly01b9c89632018-10-26 16:50:13 +0100508 1.f, 2.f, 1.f,
509 2.f, 1.f, 2.f,
510 1.f, 2.f, 1.f,
511 }));
512 // at this point if we require it permute the input data
513 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
514 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
515 {
516 std::vector<T> tmp(inputData.size());
517 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
518 inputData = tmp;
519 }
520 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000521
522 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
523 {0, 2}));
524 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
525
jimfly01b9c89632018-10-26 16:50:13 +0100526 std::vector<T> kernelData = std::vector<T>(
527 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
528 1.f, 0.f, 1.f,
529 0.f, 0.f, 0.f,
530 -1.f, 0.f, -1.f,
telsoa014fcda012018-03-09 14:13:49 +0000531
jimfly01b9c89632018-10-26 16:50:13 +0100532 1.f, 0.f, 1.f,
533 0.f, 0.f, 0.f,
534 -1.f, 0.f, -1.f,
535 }));
536 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
537 {
538 std::vector<T> tmp(kernelData.size());
539 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, kernelData.data(), tmp.data());
540 kernelData = tmp;
541 }
542 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000543
telsoa01c577f2c2018-08-31 09:22:23 +0100544 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000545 std::vector<T> outputImage(
546 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
547 outputTensorInfo.GetQuantizationOffset(),
548 {0.f, 0.f})
549 );
550
telsoa01c577f2c2018-08-31 09:22:23 +0100551 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000552 if(biasEnabled)
553 {
554 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
555 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
556 outputWidth, outputHeight);
557 }
558
559 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01b9c89632018-10-26 16:50:13 +0100560 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
561 {
562 std::vector<T> tmp(outputImage.size());
563 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputImage.data(), tmp.data());
564 outputImage = tmp;
565 }
566
telsoa014fcda012018-03-09 14:13:49 +0000567 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
568
569 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
570 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
571
572 armnn::DepthwiseConvolution2dQueueDescriptor data;
573 armnn::WorkloadInfo info;
574 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
575 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
576
577 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
578 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
579
580 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
581 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
582
583 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100584 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000585 data.m_Parameters.m_StrideX = 1;
586 data.m_Parameters.m_StrideY = 1;
587 data.m_Parameters.m_PadLeft = 0;
588 data.m_Parameters.m_PadRight = 0;
589 data.m_Parameters.m_PadTop = 0;
590 data.m_Parameters.m_PadBottom = 0;
591 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01b9c89632018-10-26 16:50:13 +0100592 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000593
594 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
595 inputHandle->Allocate();
596 outputHandle->Allocate();
597
598 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
599
600 workload->Execute();
601
602 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
603
604 return ret;
605}
606
607template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000608LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(
609 armnn::IWorkloadFactory& workloadFactory,
610 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
611 float qScale,
612 int32_t qOffset,
613 bool biasEnabled,
614 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +0000615{
616 unsigned int depthMultiplier = 2;
617
618 unsigned int inputHeight = 8;
619 unsigned int inputWidth = 16;
620 unsigned int inputChannels = 2;
621 unsigned int inputBatchSize = 1;
622
623 unsigned int kernelHeight = 5;
624 unsigned int kernelWidth = 3;
625
626 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
627 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
628 unsigned int outputChannels = inputChannels * depthMultiplier;
629 unsigned int outputBatchSize = inputBatchSize;
630
jimfly01d84216a2018-10-26 12:56:21 +0100631 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(
632 inputBatchSize, inputChannels, inputHeight, inputWidth, layout);
633 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
634 outputBatchSize, outputChannels, outputHeight, outputWidth, layout);
635 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(
636 depthMultiplier, inputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000637 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
638
639 // Set quantization parameters if the requested type is a quantized type.
640 if(armnn::IsQuantizedType<T>())
641 {
642 inputTensorInfo.SetQuantizationScale(qScale);
643 inputTensorInfo.SetQuantizationOffset(qOffset);
644 outputTensorInfo.SetQuantizationScale(qScale);
645 outputTensorInfo.SetQuantizationOffset(qOffset);
646 kernelDesc.SetQuantizationScale(qScale);
647 kernelDesc.SetQuantizationOffset(qOffset);
648 biasDesc.SetQuantizationScale(qScale*qScale);
649 biasDesc.SetQuantizationOffset(0);
650 }
651
jimfly01d84216a2018-10-26 12:56:21 +0100652 // NOTE: originalInputData is in NCHW format
653 std::vector<T> originalInputData = std::vector<T>(
654 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
655 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,
656 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,
657 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,
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, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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 }));
672 std::vector<T> inputData = originalInputData;
673 // at this point if we require it permute the input data
674 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
675 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
676 {
677 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, originalInputData.data(), inputData.data());
678 }
679 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000680
681 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
682 {0, 2, 1, -1}));
683 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
684
jimfly01d84216a2018-10-26 12:56:21 +0100685 std::vector<T> originalKernelData = std::vector<T>(
686 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
687 1, 1, 1,
688 1, -1, 1,
689 1, 1, 1,
690 1, 1, 1,
691 1, 1, 1,
telsoa014fcda012018-03-09 14:13:49 +0000692
jimfly01d84216a2018-10-26 12:56:21 +0100693 2, 2, 2,
694 2, 2, 2,
695 2, 2, 2,
696 2, 2, 2,
697 2, 2, 2,
telsoa014fcda012018-03-09 14:13:49 +0000698
jimfly01d84216a2018-10-26 12:56:21 +0100699 0, 0, 0,
700 0, -1, 0,
701 0, 0, 0,
702 0, 0, 0,
703 0, 0, 0,
telsoa014fcda012018-03-09 14:13:49 +0000704
jimfly01d84216a2018-10-26 12:56:21 +0100705 0, 0, 0,
706 0, 0, 0,
707 0, 1, 0,
708 0, 0, 0,
709 0, 0, 0
710 }));
711 std::vector<T> kernelData = originalKernelData;
712 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
713 {
714 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernelData.data(), kernelData.data());
715 }
716 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000717
telsoa01c577f2c2018-08-31 09:22:23 +0100718 // Manually calculated.
jimfly01d84216a2018-10-26 12:56:21 +0100719 std::vector<T> originalOutputImage = std::vector<T>(
telsoa014fcda012018-03-09 14:13:49 +0000720 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
721 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
722 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
723 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
724 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
725 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
726 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
727
728 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
729 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
730 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
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
735 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
736 10.0f, 10.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 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
741
742 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
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 }));
749
telsoa01c577f2c2018-08-31 09:22:23 +0100750 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000751 if(biasEnabled)
752 {
jimfly01d84216a2018-10-26 12:56:21 +0100753 ApplyBias(originalOutputImage,
754 outputTensorInfo.GetQuantizationScale(),
755 outputTensorInfo.GetQuantizationOffset(),
756 biasV,
757 biasDesc.GetQuantizationScale(),
758 biasDesc.GetQuantizationOffset(),
759 outputWidth,
760 outputHeight);
telsoa014fcda012018-03-09 14:13:49 +0000761 }
762
763 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01d84216a2018-10-26 12:56:21 +0100764 std::vector<T> outputImage = originalOutputImage;
765 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
766 {
767 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, originalOutputImage.data(), outputImage.data());
768 }
769
telsoa014fcda012018-03-09 14:13:49 +0000770 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
771
772 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
773 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
774
775 armnn::DepthwiseConvolution2dQueueDescriptor data;
776 armnn::WorkloadInfo info;
777 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
778 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
779
780 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
781 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
782
783 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
784 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
785
786 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100787 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000788 data.m_Parameters.m_StrideX = 2;
789 data.m_Parameters.m_StrideY = 1;
790 data.m_Parameters.m_PadLeft = 0;
791 data.m_Parameters.m_PadRight = 0;
792 data.m_Parameters.m_PadTop = 1;
793 data.m_Parameters.m_PadBottom = 1;
794 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01d84216a2018-10-26 12:56:21 +0100795 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000796
797 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
798 inputHandle->Allocate();
799 outputHandle->Allocate();
800
801 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
802
803 workload->Execute();
804
805 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
806
807 return ret;
808}
809
Nikhil Rajcec6b652018-10-12 13:51:57 +0100810template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000811LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(
812 armnn::IWorkloadFactory& workloadFactory,
813 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
814 const boost::multi_array<T, 4>& input,
815 const boost::multi_array<T, 4>& kernel,
816 const boost::multi_array<B, 1>& bias,
817 const boost::multi_array<T, 4>& outputExpected,
818 float qScale,
819 int32_t qOffset,
820 uint32_t padLeft = 0,
821 uint32_t padTop = 0,
822 uint32_t padRight = 0,
823 uint32_t padBottom = 0,
824 uint32_t strideX = 1,
825 uint32_t strideY = 1)
Nikhil Rajcec6b652018-10-12 13:51:57 +0100826{
827 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
828 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
829 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
830 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
831
832 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
833 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
834 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
835 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
836
837 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
838 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
839 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
840 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
841
842 // Creates the tensors.
843 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
844 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
845 armnn::GetDataType<T>());
846 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
847 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
848
849 // Set quantization parameters if the requested type is a quantized type.
850 if (armnn::IsQuantizedType<T>())
851 {
852 inputTensorInfo.SetQuantizationScale(qScale);
853 inputTensorInfo.SetQuantizationOffset(qOffset);
854 outputTensorInfo.SetQuantizationScale(qScale);
855 outputTensorInfo.SetQuantizationOffset(qOffset);
856 kernelDesc.SetQuantizationScale(qScale);
857 kernelDesc.SetQuantizationOffset(qOffset);
858 biasDesc.SetQuantizationScale(qScale*qScale);
859 biasDesc.SetQuantizationOffset(0);
860 }
861
862 // Construct the input data.
863 std::vector<T> inputData;
864 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
865 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
866
867 // Construct the output data, with bias applied, as appropriate.
868 std::vector<T> outputData;
869 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
870
871 LayerTestResult<T, 4> ret(outputTensorInfo);
872 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
873
874 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
875 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
876
877 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
878 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
879
880 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
881
882 armnn::DepthwiseConvolution2dQueueDescriptor data;
883 data.m_Weight = &weightsTensor;
884 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
885 data.m_Parameters.m_StrideX = strideX;
886 data.m_Parameters.m_StrideY = strideY;
887 data.m_Parameters.m_PadLeft = padLeft;
888 data.m_Parameters.m_PadRight = padRight;
889 data.m_Parameters.m_PadTop = padTop;
890 data.m_Parameters.m_PadBottom = padBottom;
891 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
892
893 armnn::WorkloadInfo info;
894 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
895 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
896
897 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
898
899 inputHandle->Allocate();
900 outputHandle->Allocate();
901
902 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
903
Nikhil Rajcec6b652018-10-12 13:51:57 +0100904 workload->Execute();
905
906 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
907
908 return ret;
909}
910
telsoa014fcda012018-03-09 14:13:49 +0000911template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000912LayerTestResult<T,4> Convolution1dTestImpl(
913 armnn::IWorkloadFactory& workloadFactory,
914 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
915 float qScale,
916 int32_t qOffset,
917 bool biasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000918{
919 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
920
telsoa01c577f2c2018-08-31 09:22:23 +0100921 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000922 // 2D convolution with the final dimension set to 1.
923 // I don't anticipate this being particularly slow, given that convolution is implemented
924 // as a matrix multiplication, at which point dimension doesn't matter.
925
926 unsigned int batchSize = 1;
927 unsigned int inputChannels = 2;
928 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100929 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000930 unsigned int kernelSize = 3;
931 unsigned int padSize = 2;
932 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100933 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000934
935 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
936 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
937 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
938 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
939
940 // Set quantization parameters if the requested type is a quantized type.
941 if(armnn::IsQuantizedType<T>())
942 {
943 inputInfo.SetQuantizationScale(qScale);
944 inputInfo.SetQuantizationOffset(qOffset);
945 outputInfo.SetQuantizationScale(qScale);
946 outputInfo.SetQuantizationOffset(qOffset);
947 kernelInfo.SetQuantizationScale(qScale);
948 kernelInfo.SetQuantizationOffset(qOffset);
949 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
950 biasInfo.SetQuantizationOffset(0);
951 }
952
953 std::vector<T> inputData(
954 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
955 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
956 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
957 }));
958
959 std::vector<T> kernelData(
960 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
961 1.0f, 0.0f, 0.0f,
962 0.0f, 2.0f, -1.5f,
963
964 0.0f, 0.0f, 0.0f,
965 0.2f, 0.2f, 0.2f,
966
967 0.5f, 0.0f, 0.5f,
968 0.0f, -1.0f, 0.0f
969 }));
970
971 std::vector<B> biasData(
972 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
973 1.0f, 0.0f, 0.0f
974 }));
975
976 std::vector<T> outputData(
977 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
978 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,
979 -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,
980 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
981 }));
982
telsoa01c577f2c2018-08-31 09:22:23 +0100983 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000984 if(biasEnabled)
985 {
986 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
987 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
988 1, outputSize);
989 }
990
991 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
992 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
993
994 armnn::Convolution2dQueueDescriptor data;
995 armnn::WorkloadInfo info;
996 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
997 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
998
999 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
1000 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
1001
1002 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
1003 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
1004
1005 data.m_Weight = &weightsTensor;
1006 data.m_Bias = &biasTensor;
1007 data.m_Parameters.m_StrideX = 1;
1008 data.m_Parameters.m_StrideY = stride;
1009 data.m_Parameters.m_PadLeft = 0;
1010 data.m_Parameters.m_PadRight = 0;
1011 data.m_Parameters.m_PadTop = padSize;
1012 data.m_Parameters.m_PadBottom = padSize;
1013 data.m_Parameters.m_BiasEnabled = biasEnabled;
1014
1015 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1016 inputHandle->Allocate();
1017 outputHandle->Allocate();
1018
1019 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
1020
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001021 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +00001022
telsoa01c577f2c2018-08-31 09:22:23 +01001023 // Output
telsoa014fcda012018-03-09 14:13:49 +00001024 LayerTestResult<T,4> ret(outputInfo);
1025 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1026 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
1027 return ret;
1028}
1029
1030
1031
1032template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001033LayerTestResult<T,4> CompareConvolution2dTestImpl(
1034 armnn::IWorkloadFactory& workloadFactory,
1035 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1036 armnn::IWorkloadFactory& refWorkloadFactory)
telsoa014fcda012018-03-09 14:13:49 +00001037{
1038 unsigned int inputHeight = 8;
1039 unsigned int inputWidth = 16;
1040 unsigned int inputChannels = 3;
1041 unsigned int inputNum = 5;
1042
1043 unsigned int kernelHeight = 3;
1044 unsigned int kernelWidth = 3;
1045
1046 unsigned int strideX = 2;
1047 unsigned int strideY = 3;
1048 unsigned int padX = 1;
1049 unsigned int padY = 1;
1050
1051 unsigned int outputNum = inputNum;
1052 unsigned int outputChannels = 2;
1053 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1054 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1055
1056 armnn::TensorInfo inputTensorInfo;
1057 armnn::TensorInfo outputTensorInfo;
1058 armnn::TensorInfo kernelDesc;
1059 armnn::TensorInfo biasDesc;
1060
1061 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1062 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1063 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1064 unsigned int biasShape[] = {outputChannels};
1065
1066 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
1067 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
1068 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
1069 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
1070
1071 LayerTestResult<T,4> ret(outputTensorInfo);
1072
1073 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1074 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1075 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1076
1077 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1078 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1079
1080 armnn::Convolution2dQueueDescriptor data;
1081 armnn::WorkloadInfo info;
1082 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1083 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1084
1085 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1086 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1087
1088 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1089 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1090 data.m_Weight = &weightsTensor;
1091 data.m_Bias = &biasTensor;
1092 data.m_Parameters.m_StrideX = strideX;
1093 data.m_Parameters.m_StrideY = strideY;
1094 data.m_Parameters.m_PadLeft = padX;
1095 data.m_Parameters.m_PadRight = padX;
1096 data.m_Parameters.m_PadTop = padY;
1097 data.m_Parameters.m_PadBottom = padY;
1098 data.m_Parameters.m_BiasEnabled = true;
1099
1100 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1101 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1102
1103 armnn::Convolution2dQueueDescriptor refData = data;
1104 armnn::WorkloadInfo refInfo = info;
1105 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1106 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1107
1108 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1109 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1110
1111 outputHandleRef->Allocate();
1112 inputHandleRef->Allocate();
1113
1114 inputHandle->Allocate();
1115 outputHandle->Allocate();
1116
1117 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1118 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1119
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001120 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001121
telsoa014fcda012018-03-09 14:13:49 +00001122 workloadRef->Execute();
1123
1124 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1125 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1126
1127 return ret;
1128}
1129
1130template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001131LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(
1132 armnn::IWorkloadFactory& workloadFactory,
1133 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1134 armnn::IWorkloadFactory& refWorkloadFactory,
1135 const armnn::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +00001136{
1137 unsigned int inputHeight = 8;
1138 unsigned int inputWidth = 16;
1139 unsigned int inputChannels = 3;
1140 unsigned int inputNum = 5;
1141
1142 unsigned int kernelHeight = 3;
1143 unsigned int kernelWidth = 3;
1144 unsigned int channelMultiplier = 1;
1145
1146 unsigned int strideX = 2;
1147 unsigned int strideY = 3;
1148 unsigned int padX = 1;
1149 unsigned int padY = 1;
1150
1151 unsigned int outputNum = inputNum;
1152 unsigned int outputChannels = inputChannels * channelMultiplier;
1153 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1154 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1155
1156 armnn::TensorInfo inputTensorInfo;
1157 armnn::TensorInfo outputTensorInfo;
1158 armnn::TensorInfo kernelDesc;
1159 armnn::TensorInfo biasDesc;
1160
jimfly017af00da2018-10-31 14:43:53 +00001161
1162 std::vector<unsigned int> inputShape;
1163 std::vector<unsigned int> outputShape;
1164 std::vector<unsigned int> kernelShape;
1165 std::vector<unsigned int> biasShape= { outputChannels };
1166 switch (layout.GetDataLayout())
1167 {
1168 case armnn::DataLayout::NCHW:
1169 inputShape = { inputNum, inputChannels, inputHeight, inputWidth };
1170 outputShape = { outputNum, outputChannels, outputHeight, outputWidth };
1171 kernelShape = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1172 break;
1173 case armnn::DataLayout ::NHWC:
1174 inputShape = { inputNum, inputHeight, inputWidth, inputChannels };
1175 outputShape = { outputNum, outputHeight, outputWidth, outputChannels };
1176 kernelShape = { channelMultiplier, kernelHeight, kernelWidth, inputChannels };
1177 break;
1178 default:
1179 throw armnn::InvalidArgumentException("unknown data layout ["
1180 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
1181 }
telsoa014fcda012018-03-09 14:13:49 +00001182
1183 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1184 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1185 int32_t qOffset = 0;
1186
jimfly017af00da2018-10-31 14:43:53 +00001187 inputTensorInfo = armnn::TensorInfo(4, inputShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1188 outputTensorInfo = armnn::TensorInfo(4, outputShape.data(), armnn::GetDataType<T>(), outputQScale, qOffset);
1189 kernelDesc = armnn::TensorInfo(4, kernelShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1190 biasDesc = armnn::TensorInfo(
1191 1, biasShape.data(), armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
telsoa014fcda012018-03-09 14:13:49 +00001192
1193 LayerTestResult<T, 4> ret(outputTensorInfo);
1194
1195 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1196 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
jimfly01d84216a2018-10-26 12:56:21 +01001197 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1198 biasDesc, 1028, 0.0f, 255.0f);
telsoa014fcda012018-03-09 14:13:49 +00001199
1200 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1201 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1202
1203 armnn::DepthwiseConvolution2dQueueDescriptor data;
1204 armnn::WorkloadInfo info;
1205 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1206 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1207
1208 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1209 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1210
1211 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1212 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1213 data.m_Weight = &weightsTensor;
1214 data.m_Bias = &biasTensor;
1215 data.m_Parameters.m_StrideX = strideX;
1216 data.m_Parameters.m_StrideY = strideY;
1217 data.m_Parameters.m_PadLeft = padX;
1218 data.m_Parameters.m_PadRight = padX;
1219 data.m_Parameters.m_PadTop = padY;
1220 data.m_Parameters.m_PadBottom = padY;
1221 data.m_Parameters.m_BiasEnabled = true;
jimfly017af00da2018-10-31 14:43:53 +00001222 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +00001223
1224 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1225 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1226
1227 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1228 armnn::WorkloadInfo refInfo = info;
1229 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1230 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1231
1232 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1233 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1234
1235 outputHandleRef->Allocate();
1236 inputHandleRef->Allocate();
1237
1238 inputHandle->Allocate();
1239 outputHandle->Allocate();
1240
1241 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1242 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1243
1244 workload->Execute();
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001245
telsoa014fcda012018-03-09 14:13:49 +00001246 workloadRef->Execute();
1247
1248 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1249 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1250
1251 return ret;
1252}