blob: 5f66d2ec85bbd9cc6cefe3a0ecd12f7fec73c285 [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"
Nina Drozdd41b2592018-11-19 13:03:36 +00008#include "TensorUtils.hpp"
Aron Virginas-Tard4f0fea2019-04-09 14:08:06 +01009#include <ResolveType.hpp>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000010
Matteo Martincigh21350152018-11-28 16:22:22 +000011#include <Permute.hpp>
12#include <DataLayoutIndexed.hpp>
13
14#include <test/TensorHelpers.hpp>
15
telsoa014fcda012018-03-09 14:13:49 +000016#include <armnn/ArmNN.hpp>
17#include <armnn/Tensor.hpp>
18#include <armnn/TypesUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +000019
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000020#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000021#include <backendsCommon/IBackendInternal.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000022#include <backendsCommon/WorkloadFactory.hpp>
Matteo Martincigh21350152018-11-28 16:22:22 +000023#include <backendsCommon/test/QuantizeHelper.hpp>
24
jimfly010a088a62018-10-25 17:05:05 +010025#include <boost/numeric/conversion/cast.hpp>
telsoa014fcda012018-03-09 14:13:49 +000026
Matteo Martincigh21350152018-11-28 16:22:22 +000027#include <string>
28
telsoa014fcda012018-03-09 14:13:49 +000029// Mapping from input type to bias type for fully connected layers.
30// float => float, uint8_t => int32_t
31template<typename T>
32struct FullyConnectedBiasTypeForInputType;
33
34template<>
35struct FullyConnectedBiasTypeForInputType<float>
36{
37 using Type = float;
38};
39
40template<>
41struct FullyConnectedBiasTypeForInputType<uint8_t>
42{
43 using Type = int32_t;
44};
45
telsoa01c577f2c2018-08-31 09:22:23 +010046// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000047template<typename T, typename B>
48void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
49 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
50{
51 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
52 "Invalid type and parameter combination.");
53 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
54 "Invalid type and parameter combination.");
55
telsoa01c577f2c2018-08-31 09:22:23 +010056 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000057 for (uint32_t i = 0; i < bias.size(); ++i)
58 {
59 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
60 for (uint32_t y = 0; y < h; ++y)
61 {
62 for (uint32_t x = 0; x < w; ++x)
63 {
64 uint32_t offset = (i * h + y) * w + x;
65 BOOST_ASSERT(offset < v.size());
66 T& outRef = v[offset];
67 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
68 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
69 }
70 }
71 }
72}
73
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +000074template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType,
75 typename T = armnn::ResolveType<ArmnnType>, typename B = armnn::ResolveType<ArmnnBType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000076LayerTestResult<T, 4> SimpleConvolution2dTestImpl(
77 armnn::IWorkloadFactory& workloadFactory,
78 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
79 const boost::multi_array<T, 4>& originalInput,
80 const boost::multi_array<T, 4>& originalKernel,
81 const boost::multi_array<B, 1>& bias,
82 const boost::multi_array<T, 4>& originalOutputExpected,
83 float qScale,
84 int32_t qOffset,
Matthew Bentham8800c002018-11-19 13:19:28 +000085 const armnn::DataLayout layout = armnn::DataLayout::NCHW,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000086 uint32_t padLeft = 0,
87 uint32_t padTop = 0,
88 uint32_t padRight = 0,
Mike Kelly7332ed82018-12-20 17:03:06 +000089 uint32_t padBottom = 0,
90 uint32_t strideX = 1,
Teresa Charlinedeeb162019-06-14 11:09:19 +010091 uint32_t strideY = 1,
92 uint32_t dilationX = 1,
93 uint32_t dilationY = 1)
telsoa014fcda012018-03-09 14:13:49 +000094{
jimfly010a088a62018-10-25 17:05:05 +010095 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
96 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
97 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
98 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000099
jimfly010a088a62018-10-25 17:05:05 +0100100 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
101 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
102 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
103 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000104
jimfly010a088a62018-10-25 17:05:05 +0100105 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
106 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
107 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
108 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000109
110 bool biasEnabled = bias.size() > 0;
111
telsoa01c577f2c2018-08-31 09:22:23 +0100112 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +0000113 BOOST_ASSERT(inputNum == 1);
114 BOOST_ASSERT(outputNum == 1);
115
telsoa01c577f2c2018-08-31 09:22:23 +0100116 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +0000117 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
118
119
telsoa01c577f2c2018-08-31 09:22:23 +0100120 // Note these tensors will use two (identical) batches.
Nina Drozdd41b2592018-11-19 13:03:36 +0000121 armnn::TensorInfo inputTensorInfo =
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000122 armnnUtils::GetTensorInfo(2*inputNum, inputChannels, inputHeight, inputWidth, layout, ArmnnType);
Nina Drozdd41b2592018-11-19 13:03:36 +0000123 armnn::TensorInfo outputTensorInfo =
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000124 armnnUtils::GetTensorInfo(2*outputNum, outputChannels, outputHeight, outputWidth, layout, ArmnnType);
Nina Drozdd41b2592018-11-19 13:03:36 +0000125 armnn::TensorInfo kernelDesc =
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000126 armnnUtils::GetTensorInfo(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout, ArmnnType);
127 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, ArmnnBType);
telsoa014fcda012018-03-09 14:13:49 +0000128
129 // Set quantization parameters if the requested type is a quantized type.
130 if(armnn::IsQuantizedType<T>())
131 {
132 inputTensorInfo.SetQuantizationScale(qScale);
133 inputTensorInfo.SetQuantizationOffset(qOffset);
134 outputTensorInfo.SetQuantizationScale(qScale);
135 outputTensorInfo.SetQuantizationOffset(qOffset);
136 kernelDesc.SetQuantizationScale(qScale);
137 kernelDesc.SetQuantizationOffset(qOffset);
138 biasDesc.SetQuantizationScale(qScale*qScale);
139 biasDesc.SetQuantizationOffset(0);
140 }
141
142 LayerTestResult<T, 4> ret(outputTensorInfo);
143
telsoa01c577f2c2018-08-31 09:22:23 +0100144 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000145 std::vector<T> inputImage;
jimfly010a088a62018-10-25 17:05:05 +0100146 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000147 std::vector<T> inputData;
148 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
149 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
jimfly010a088a62018-10-25 17:05:05 +0100150
151 // at this point if we require it permute the input data
152 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000153 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100154 {
155 std::vector<T> tmp(inputData.size());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000156 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data(), sizeof(T));
jimfly010a088a62018-10-25 17:05:05 +0100157 inputData = tmp;
158 }
159
telsoa014fcda012018-03-09 14:13:49 +0000160 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
161
162 std::vector<T> outputImage;
jimfly010a088a62018-10-25 17:05:05 +0100163 outputImage.assign(originalOutputExpected.data(),
164 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000165
telsoa01c577f2c2018-08-31 09:22:23 +0100166 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000167 if(biasEnabled)
168 {
169 std::vector<T> biasV;
170 biasV.assign(bias.data(), bias.data() + outputChannels);
171 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
172 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
173 outputWidth, outputHeight);
174 }
175
telsoa01c577f2c2018-08-31 09:22:23 +0100176 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000177 std::vector<T> outputData;
178 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
179 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
180
jimfly010a088a62018-10-25 17:05:05 +0100181 // at this point if we require it permute the expected output
Matthew Bentham8800c002018-11-19 13:19:28 +0000182 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100183 {
184 std::vector<T> tmp(outputData.size());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000185 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data(), sizeof(T));
jimfly010a088a62018-10-25 17:05:05 +0100186 outputData = tmp;
187 }
telsoa014fcda012018-03-09 14:13:49 +0000188 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
189
telsoa014fcda012018-03-09 14:13:49 +0000190 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
191 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
192
193 armnn::Convolution2dQueueDescriptor data;
194 armnn::WorkloadInfo info;
195 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
196 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
jimfly010a088a62018-10-25 17:05:05 +0100197 // Permute the kernel if necessary
198 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
Matthew Bentham8800c002018-11-19 13:19:28 +0000199 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100200 {
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000201 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data(), sizeof(T));
jimfly010a088a62018-10-25 17:05:05 +0100202 }
telsoa014fcda012018-03-09 14:13:49 +0000203 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
204
205 if(biasEnabled)
206 {
207 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
208 }
209
210 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
211 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
212
213 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100214 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 +0000215 data.m_Parameters.m_StrideX = strideX;
216 data.m_Parameters.m_StrideY = strideY;
217 data.m_Parameters.m_PadLeft = padLeft;
218 data.m_Parameters.m_PadRight = padRight;
219 data.m_Parameters.m_PadTop = padTop;
220 data.m_Parameters.m_PadBottom = padBottom;
221 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000222 data.m_Parameters.m_DataLayout = layout;
Teresa Charlinedeeb162019-06-14 11:09:19 +0100223 data.m_Parameters.m_DilationX = dilationX;
224 data.m_Parameters.m_DilationY = dilationY;
telsoa014fcda012018-03-09 14:13:49 +0000225
226 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
227 inputHandle->Allocate();
228 outputHandle->Allocate();
229
230 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
231
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000232 ExecuteWorkload(*workload, memoryManager);
surmeh013537c2c2018-05-18 16:31:43 +0100233
234 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
235
236 return ret;
237}
238
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000239template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType,
240 typename T = armnn::ResolveType<ArmnnType>, typename B = armnn::ResolveType<ArmnnBType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000241LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(
242 armnn::IWorkloadFactory& workloadFactory,
243 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
244 const boost::multi_array<T, 4>& input,
245 const boost::multi_array<T, 4>& kernel,
246 const boost::multi_array<B, 1>& bias,
247 const boost::multi_array<T, 4>& outputExpected,
Mike Kelly7332ed82018-12-20 17:03:06 +0000248 const armnn::DataLayout dataLayout,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000249 float qScale,
250 int32_t qOffset,
251 uint32_t padLeft = 1,
252 uint32_t padTop = 1,
253 uint32_t padRight = 1,
254 uint32_t padBottom = 1,
255 uint32_t strideX = 1,
256 uint32_t strideY = 1)
Francis Murtaghd59116e2018-10-04 16:03:07 +0100257{
258 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
259 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
260 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
261 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
262
263 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
264 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
265 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
266 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
267
268 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
269 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
270 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
271 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
272
273 bool biasEnabled = bias.size() > 0;
274
275 // Creates the tensors.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000276 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, ArmnnType);
Francis Murtaghd59116e2018-10-04 16:03:07 +0100277 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000278 ArmnnType);
279 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, ArmnnType);
280 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, ArmnnBType);
Francis Murtaghd59116e2018-10-04 16:03:07 +0100281
282 // Construct the input data.
283 std::vector<T> inputData;
284 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
285 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
286
287 // Construct the output data, with bias applied, as appropriate.
288 std::vector<T> outputData;
289 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
290
291 LayerTestResult<T, 4> ret(outputTensorInfo);
292 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
293
294 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
295 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
296
297 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
298 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
299
300 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
301
302 armnn::Convolution2dQueueDescriptor data;
303
304 data.m_Weight = &weightsTensor;
305 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
306 data.m_Parameters.m_StrideX = strideX;
307 data.m_Parameters.m_StrideY = strideY;
308 data.m_Parameters.m_PadLeft = padLeft;
309 data.m_Parameters.m_PadRight = padRight;
310 data.m_Parameters.m_PadTop = padTop;
311 data.m_Parameters.m_PadBottom = padBottom;
312 data.m_Parameters.m_BiasEnabled = biasEnabled;
313 data.m_Parameters.m_DataLayout = dataLayout;
314
315 armnn::WorkloadInfo info;
316 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
317 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
318
319 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
320 inputHandle->Allocate();
321 outputHandle->Allocate();
322
323 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
324
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000325 ExecuteWorkload(*workload, memoryManager);
Francis Murtaghd59116e2018-10-04 16:03:07 +0100326
327 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
328
329 return ret;
330}
331
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000332template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType,
333 typename T = armnn::ResolveType<ArmnnType>, typename B = armnn::ResolveType<ArmnnBType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000334LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(
335 armnn::IWorkloadFactory& workloadFactory,
336 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
337 const boost::multi_array<T, 4>& input,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000338 const boost::multi_array<T, 4>& kernel,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000339 const boost::multi_array<B, 1>& bias,
340 const boost::multi_array<T, 4>& outputExpected,
341 float qScale,
342 int32_t qOffset,
Matthew Bentham8800c002018-11-19 13:19:28 +0000343 const armnn::DataLayout layout,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000344 uint32_t padLeft = 0,
345 uint32_t padTop = 0,
346 uint32_t padRight = 0,
347 uint32_t padBottom = 0,
348 uint32_t strideX = 1,
349 uint32_t strideY = 1)
surmeh013537c2c2018-05-18 16:31:43 +0100350{
351 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
352 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
353 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
354 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000355 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
356 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
357 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
358 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +0100359 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
360 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
361 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
362 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
363
telsoa01c577f2c2018-08-31 09:22:23 +0100364 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100365 bool biasEnabled = bias.size() > 0;
366 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
367
telsoa01c577f2c2018-08-31 09:22:23 +0100368 // Creates the tensors.
Nina Drozdd41b2592018-11-19 13:03:36 +0000369 armnn::TensorInfo inputTensorInfo =
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000370 armnnUtils::GetTensorInfo(inputNum, inputChannels, inputHeight, inputWidth, layout, ArmnnType);
Nina Drozdd41b2592018-11-19 13:03:36 +0000371 armnn::TensorInfo outputTensorInfo =
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000372 armnnUtils::GetTensorInfo(outputNum, outputChannels, outputHeight, outputWidth, layout, ArmnnType);
373 armnn::TensorInfo kernelDesc({kernelChanMul, kernelChannels, kernelHeight, kernelWidth}, ArmnnType);
374 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, ArmnnBType);
surmeh013537c2c2018-05-18 16:31:43 +0100375
376 // Set quantization parameters if the requested type is a quantized type.
377 if (armnn::IsQuantizedType<T>())
378 {
379 inputTensorInfo.SetQuantizationScale(qScale);
380 inputTensorInfo.SetQuantizationOffset(qOffset);
381 outputTensorInfo.SetQuantizationScale(qScale);
382 outputTensorInfo.SetQuantizationOffset(qOffset);
383 kernelDesc.SetQuantizationScale(qScale);
384 kernelDesc.SetQuantizationOffset(qOffset);
385 biasDesc.SetQuantizationScale(qScale*qScale);
386 biasDesc.SetQuantizationOffset(0);
387 }
388
telsoa01c577f2c2018-08-31 09:22:23 +0100389 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100390 std::vector<T> inputData;
391 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
jimfly01382a91d2018-10-26 15:55:50 +0100392
393 // At this point if we require it permute the input data
394 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000395 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100396 {
397 std::vector<T> tmp(inputData.size());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000398 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data(), sizeof(T));
jimfly01382a91d2018-10-26 15:55:50 +0100399 inputData = tmp;
400 }
401
surmeh013537c2c2018-05-18 16:31:43 +0100402 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
403
telsoa01c577f2c2018-08-31 09:22:23 +0100404 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100405 std::vector<T> outputData;
406 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
407 if (biasEnabled)
408 {
409 std::vector<T> biasV;
410 biasV.assign(bias.data(), bias.data() + outputChannels);
411 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
412 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
413 outputWidth, outputHeight);
414 }
415
416 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01382a91d2018-10-26 15:55:50 +0100417
418 // At this point if we require it permute the expected output
Matthew Bentham8800c002018-11-19 13:19:28 +0000419 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100420 {
421 std::vector<T> tmp(outputData.size());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000422 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data(), sizeof(T));
jimfly01382a91d2018-10-26 15:55:50 +0100423 outputData = tmp;
424 }
425
surmeh013537c2c2018-05-18 16:31:43 +0100426 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
427
428 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
429 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
430
431 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
jimfly01382a91d2018-10-26 15:55:50 +0100432
surmeh013537c2c2018-05-18 16:31:43 +0100433 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
434
435 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
436 if (biasEnabled)
437 {
438 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
439 }
440
441 armnn::DepthwiseConvolution2dQueueDescriptor data;
442 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100443 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 +0100444 data.m_Parameters.m_StrideX = strideX;
445 data.m_Parameters.m_StrideY = strideY;
446 data.m_Parameters.m_PadLeft = padLeft;
447 data.m_Parameters.m_PadRight = padRight;
448 data.m_Parameters.m_PadTop = padTop;
449 data.m_Parameters.m_PadBottom = padBottom;
450 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000451 data.m_Parameters.m_DataLayout = layout;
surmeh013537c2c2018-05-18 16:31:43 +0100452
453 armnn::WorkloadInfo info;
454 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
455 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
456
457 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
458 inputHandle->Allocate();
459 outputHandle->Allocate();
460
461 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
462
Ruomei Yan495852f2019-05-23 11:37:33 +0100463 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +0000464
465 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
466
467 return ret;
468}
469
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000470template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000471LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(
472 armnn::IWorkloadFactory& workloadFactory,
473 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
474 float qScale,
475 int32_t qOffset,
476 bool biasEnabled,
Matthew Bentham8800c002018-11-19 13:19:28 +0000477 const armnn::DataLayout layout)
telsoa014fcda012018-03-09 14:13:49 +0000478{
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000479 using B = armnn::ResolveType<ArmnnBType>;
480
telsoa014fcda012018-03-09 14:13:49 +0000481 unsigned int inputHeight = 3;
482 unsigned int inputWidth = 3;
483 unsigned int inputChannels = 2;
484 unsigned int inputNum = 1;
485
486 unsigned int kernelHeight = 3;
487 unsigned int kernelWidth = 3;
488 unsigned int kernelChannels = inputChannels;
Matteo Martincigh747ef822018-12-18 09:26:39 +0000489 unsigned int kernelDepthMultiplier = 1;
telsoa014fcda012018-03-09 14:13:49 +0000490
491 unsigned int outputHeight = 1;
492 unsigned int outputWidth = 1;
493 unsigned int outputChannels = kernelChannels;
494 unsigned int outputNum = inputNum;
495
Nina Drozdd41b2592018-11-19 13:03:36 +0000496 armnn::TensorInfo inputTensorInfo =
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000497 armnnUtils::GetTensorInfo(inputNum, inputChannels, inputHeight, inputWidth, layout, ArmnnType);
Nina Drozdd41b2592018-11-19 13:03:36 +0000498 armnn::TensorInfo outputTensorInfo =
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000499 armnnUtils::GetTensorInfo(outputNum, outputChannels, outputHeight, outputWidth, layout, ArmnnType);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000500 armnn::TensorInfo kernelDesc({kernelDepthMultiplier, kernelChannels, kernelHeight, kernelWidth},
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000501 ArmnnType);
502 armnn::TensorInfo biasDesc({ outputChannels }, ArmnnBType);
telsoa014fcda012018-03-09 14:13:49 +0000503
504 // Set quantization parameters if the requested type is a quantized type.
505 if(armnn::IsQuantizedType<T>())
506 {
507 inputTensorInfo.SetQuantizationScale(qScale);
508 inputTensorInfo.SetQuantizationOffset(qOffset);
509 outputTensorInfo.SetQuantizationScale(qScale);
510 outputTensorInfo.SetQuantizationOffset(qOffset);
511 kernelDesc.SetQuantizationScale(qScale);
512 kernelDesc.SetQuantizationOffset(qOffset);
513 biasDesc.SetQuantizationScale(qScale*qScale);
514 biasDesc.SetQuantizationOffset(0);
515 }
jimfly01b9c89632018-10-26 16:50:13 +0100516 std::vector<T> inputData = std::vector<T>(
517 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
518 1.f, 2.f, 1.f,
519 2.f, 1.f, 2.f,
520 1.f, 2.f, 1.f,
telsoa014fcda012018-03-09 14:13:49 +0000521
jimfly01b9c89632018-10-26 16:50:13 +0100522 1.f, 2.f, 1.f,
523 2.f, 1.f, 2.f,
524 1.f, 2.f, 1.f,
525 }));
526 // at this point if we require it permute the input data
527 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000528 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100529 {
530 std::vector<T> tmp(inputData.size());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000531 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data(), sizeof(T));
jimfly01b9c89632018-10-26 16:50:13 +0100532 inputData = tmp;
533 }
534 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000535
536 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
537 {0, 2}));
538 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
539
jimfly01b9c89632018-10-26 16:50:13 +0100540 std::vector<T> kernelData = std::vector<T>(
541 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
542 1.f, 0.f, 1.f,
543 0.f, 0.f, 0.f,
544 -1.f, 0.f, -1.f,
telsoa014fcda012018-03-09 14:13:49 +0000545
jimfly01b9c89632018-10-26 16:50:13 +0100546 1.f, 0.f, 1.f,
547 0.f, 0.f, 0.f,
548 -1.f, 0.f, -1.f,
549 }));
jimfly01b9c89632018-10-26 16:50:13 +0100550 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000551
telsoa01c577f2c2018-08-31 09:22:23 +0100552 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000553 std::vector<T> outputImage(
554 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
555 outputTensorInfo.GetQuantizationOffset(),
556 {0.f, 0.f})
557 );
558
telsoa01c577f2c2018-08-31 09:22:23 +0100559 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000560 if(biasEnabled)
561 {
562 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
563 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
564 outputWidth, outputHeight);
565 }
566
567 LayerTestResult<T, 4> ret(outputTensorInfo);
Matthew Bentham8800c002018-11-19 13:19:28 +0000568 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100569 {
570 std::vector<T> tmp(outputImage.size());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000571 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputImage.data(), tmp.data(), sizeof(T));
jimfly01b9c89632018-10-26 16:50:13 +0100572 outputImage = tmp;
573 }
574
telsoa014fcda012018-03-09 14:13:49 +0000575 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
576
577 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
578 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
579
580 armnn::DepthwiseConvolution2dQueueDescriptor data;
581 armnn::WorkloadInfo info;
582 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
583 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
584
585 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
586 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
587
588 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
589 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
590
591 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100592 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000593 data.m_Parameters.m_StrideX = 1;
594 data.m_Parameters.m_StrideY = 1;
595 data.m_Parameters.m_PadLeft = 0;
596 data.m_Parameters.m_PadRight = 0;
597 data.m_Parameters.m_PadTop = 0;
598 data.m_Parameters.m_PadBottom = 0;
599 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000600 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000601
602 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
603 inputHandle->Allocate();
604 outputHandle->Allocate();
605
606 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
607
Ruomei Yan495852f2019-05-23 11:37:33 +0100608 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +0000609
610 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
611
612 return ret;
613}
614
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000615template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000616LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(
617 armnn::IWorkloadFactory& workloadFactory,
618 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
619 float qScale,
620 int32_t qOffset,
621 bool biasEnabled,
Matthew Bentham8800c002018-11-19 13:19:28 +0000622 const armnn::DataLayout layout)
telsoa014fcda012018-03-09 14:13:49 +0000623{
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000624 using B = armnn::ResolveType<ArmnnBType>;
625
telsoa014fcda012018-03-09 14:13:49 +0000626 unsigned int depthMultiplier = 2;
627
628 unsigned int inputHeight = 8;
629 unsigned int inputWidth = 16;
630 unsigned int inputChannels = 2;
631 unsigned int inputBatchSize = 1;
632
633 unsigned int kernelHeight = 5;
634 unsigned int kernelWidth = 3;
635
636 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
637 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
638 unsigned int outputChannels = inputChannels * depthMultiplier;
639 unsigned int outputBatchSize = inputBatchSize;
640
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000641 armnn::TensorInfo inputTensorInfo = armnnUtils::GetTensorInfo(
642 inputBatchSize, inputChannels, inputHeight, inputWidth, layout, ArmnnType);
643 armnn::TensorInfo outputTensorInfo = armnnUtils::GetTensorInfo(
644 outputBatchSize, outputChannels, outputHeight, outputWidth, layout, ArmnnType);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000645 armnn::TensorInfo kernelDesc({depthMultiplier, inputChannels, kernelHeight, kernelWidth},
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000646 ArmnnType);
647 armnn::TensorInfo biasDesc({outputChannels}, ArmnnBType);
telsoa014fcda012018-03-09 14:13:49 +0000648
649 // Set quantization parameters if the requested type is a quantized type.
650 if(armnn::IsQuantizedType<T>())
651 {
652 inputTensorInfo.SetQuantizationScale(qScale);
653 inputTensorInfo.SetQuantizationOffset(qOffset);
654 outputTensorInfo.SetQuantizationScale(qScale);
655 outputTensorInfo.SetQuantizationOffset(qOffset);
656 kernelDesc.SetQuantizationScale(qScale);
657 kernelDesc.SetQuantizationOffset(qOffset);
658 biasDesc.SetQuantizationScale(qScale*qScale);
659 biasDesc.SetQuantizationOffset(0);
660 }
661
jimfly01d84216a2018-10-26 12:56:21 +0100662 // NOTE: originalInputData is in NCHW format
663 std::vector<T> originalInputData = std::vector<T>(
664 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
665 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,
666 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,
667 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,
668 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,
669 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,
670 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,
671 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,
672 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,
673 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
674 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
675 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
676 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
677 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
678 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
679 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
680 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
681 }));
682 std::vector<T> inputData = originalInputData;
683 // at this point if we require it permute the input data
684 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000685 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100686 {
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000687 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC,
688 originalInputData.data(), inputData.data(), sizeof(T));
jimfly01d84216a2018-10-26 12:56:21 +0100689 }
690 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000691
692 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
693 {0, 2, 1, -1}));
694 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
695
Matteo Martincigh747ef822018-12-18 09:26:39 +0000696 std::vector<T> kernelData = std::vector<T>(
jimfly01d84216a2018-10-26 12:56:21 +0100697 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
698 1, 1, 1,
699 1, -1, 1,
700 1, 1, 1,
701 1, 1, 1,
702 1, 1, 1,
telsoa014fcda012018-03-09 14:13:49 +0000703
jimfly01d84216a2018-10-26 12:56:21 +0100704 2, 2, 2,
705 2, 2, 2,
706 2, 2, 2,
707 2, 2, 2,
708 2, 2, 2,
telsoa014fcda012018-03-09 14:13:49 +0000709
jimfly01d84216a2018-10-26 12:56:21 +0100710 0, 0, 0,
711 0, -1, 0,
712 0, 0, 0,
713 0, 0, 0,
714 0, 0, 0,
telsoa014fcda012018-03-09 14:13:49 +0000715
jimfly01d84216a2018-10-26 12:56:21 +0100716 0, 0, 0,
717 0, 0, 0,
718 0, 1, 0,
719 0, 0, 0,
720 0, 0, 0
Matteo Martincigh747ef822018-12-18 09:26:39 +0000721
jimfly01d84216a2018-10-26 12:56:21 +0100722 }));
jimfly01d84216a2018-10-26 12:56:21 +0100723 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000724
telsoa01c577f2c2018-08-31 09:22:23 +0100725 // Manually calculated.
jimfly01d84216a2018-10-26 12:56:21 +0100726 std::vector<T> originalOutputImage = std::vector<T>(
telsoa014fcda012018-03-09 14:13:49 +0000727 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
728 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
729 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
730 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
731 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
732 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
733 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
734
735 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
736 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
737 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
738 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
739 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
740 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
741
742 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
743 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
744 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
745 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
746 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
747 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
748
749 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
750 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
751 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
752 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
753 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
754 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
755 }));
756
telsoa01c577f2c2018-08-31 09:22:23 +0100757 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000758 if(biasEnabled)
759 {
jimfly01d84216a2018-10-26 12:56:21 +0100760 ApplyBias(originalOutputImage,
761 outputTensorInfo.GetQuantizationScale(),
762 outputTensorInfo.GetQuantizationOffset(),
763 biasV,
764 biasDesc.GetQuantizationScale(),
765 biasDesc.GetQuantizationOffset(),
766 outputWidth,
767 outputHeight);
telsoa014fcda012018-03-09 14:13:49 +0000768 }
769
770 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01d84216a2018-10-26 12:56:21 +0100771 std::vector<T> outputImage = originalOutputImage;
Matthew Bentham8800c002018-11-19 13:19:28 +0000772 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100773 {
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000774 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC,
775 originalOutputImage.data(), outputImage.data(), sizeof(T));
jimfly01d84216a2018-10-26 12:56:21 +0100776 }
777
telsoa014fcda012018-03-09 14:13:49 +0000778 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
779
780 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
781 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
782
783 armnn::DepthwiseConvolution2dQueueDescriptor data;
784 armnn::WorkloadInfo info;
785 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
786 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
787
788 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
789 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
790
791 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
792 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
793
794 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100795 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000796 data.m_Parameters.m_StrideX = 2;
797 data.m_Parameters.m_StrideY = 1;
798 data.m_Parameters.m_PadLeft = 0;
799 data.m_Parameters.m_PadRight = 0;
800 data.m_Parameters.m_PadTop = 1;
801 data.m_Parameters.m_PadBottom = 1;
802 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000803 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000804
805 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
806 inputHandle->Allocate();
807 outputHandle->Allocate();
808
809 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
810
Ruomei Yan495852f2019-05-23 11:37:33 +0100811 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +0000812
813 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
814
815 return ret;
816}
817
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000818template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType,
819 typename T = armnn::ResolveType<ArmnnType>, typename B = armnn::ResolveType<ArmnnBType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000820LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(
821 armnn::IWorkloadFactory& workloadFactory,
822 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
823 const boost::multi_array<T, 4>& input,
824 const boost::multi_array<T, 4>& kernel,
825 const boost::multi_array<B, 1>& bias,
826 const boost::multi_array<T, 4>& outputExpected,
827 float qScale,
828 int32_t qOffset,
829 uint32_t padLeft = 0,
830 uint32_t padTop = 0,
831 uint32_t padRight = 0,
832 uint32_t padBottom = 0,
833 uint32_t strideX = 1,
Bruno Goncalves22972f02019-04-26 21:03:24 -0300834 uint32_t strideY = 1,
835 uint32_t dilationX = 1,
836 uint32_t dilationY = 1)
Nikhil Rajcec6b652018-10-12 13:51:57 +0100837{
838 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
839 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
840 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
841 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
842
843 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000844 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
845 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
846 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100847
848 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
849 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
850 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
851 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
852
853 // Creates the tensors.
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000854 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, ArmnnType);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100855 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000856 ArmnnType);
857 armnn::TensorInfo kernelDesc({kernelChanMul, kernelChannels, kernelHeight, kernelWidth}, ArmnnType);
858 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, ArmnnBType);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100859
860 // Set quantization parameters if the requested type is a quantized type.
861 if (armnn::IsQuantizedType<T>())
862 {
863 inputTensorInfo.SetQuantizationScale(qScale);
864 inputTensorInfo.SetQuantizationOffset(qOffset);
865 outputTensorInfo.SetQuantizationScale(qScale);
866 outputTensorInfo.SetQuantizationOffset(qOffset);
867 kernelDesc.SetQuantizationScale(qScale);
868 kernelDesc.SetQuantizationOffset(qOffset);
869 biasDesc.SetQuantizationScale(qScale*qScale);
870 biasDesc.SetQuantizationOffset(0);
871 }
872
873 // Construct the input data.
874 std::vector<T> inputData;
875 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
876 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
877
878 // Construct the output data, with bias applied, as appropriate.
879 std::vector<T> outputData;
880 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
881
882 LayerTestResult<T, 4> ret(outputTensorInfo);
883 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
884
885 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
886 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
887
888 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
889 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
890
891 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
892
893 armnn::DepthwiseConvolution2dQueueDescriptor data;
894 data.m_Weight = &weightsTensor;
895 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
896 data.m_Parameters.m_StrideX = strideX;
897 data.m_Parameters.m_StrideY = strideY;
898 data.m_Parameters.m_PadLeft = padLeft;
899 data.m_Parameters.m_PadRight = padRight;
900 data.m_Parameters.m_PadTop = padTop;
901 data.m_Parameters.m_PadBottom = padBottom;
902 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
Bruno Goncalves22972f02019-04-26 21:03:24 -0300903 data.m_Parameters.m_DilationX = dilationX;
904 data.m_Parameters.m_DilationY = dilationY;
Nikhil Rajcec6b652018-10-12 13:51:57 +0100905
906 armnn::WorkloadInfo info;
907 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
908 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
909
910 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
911
912 inputHandle->Allocate();
913 outputHandle->Allocate();
914
915 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
916
Ruomei Yan495852f2019-05-23 11:37:33 +0100917 ExecuteWorkload(*workload, memoryManager);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100918
919 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
920
921 return ret;
922}
923
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000924template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000925LayerTestResult<T,4> Convolution1dTestImpl(
926 armnn::IWorkloadFactory& workloadFactory,
927 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
928 float qScale,
929 int32_t qOffset,
930 bool biasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000931{
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000932 using B = armnn::ResolveType<ArmnnBType>;
telsoa01c577f2c2018-08-31 09:22:23 +0100933 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000934 // 2D convolution with the final dimension set to 1.
935 // I don't anticipate this being particularly slow, given that convolution is implemented
936 // as a matrix multiplication, at which point dimension doesn't matter.
937
938 unsigned int batchSize = 1;
939 unsigned int inputChannels = 2;
940 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100941 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000942 unsigned int kernelSize = 3;
943 unsigned int padSize = 2;
944 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100945 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000946
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000947 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, ArmnnType);
948 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, ArmnnType);
949 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, ArmnnType);
950 armnn::TensorInfo biasInfo({outputChannels}, ArmnnBType);
telsoa014fcda012018-03-09 14:13:49 +0000951
952 // Set quantization parameters if the requested type is a quantized type.
953 if(armnn::IsQuantizedType<T>())
954 {
955 inputInfo.SetQuantizationScale(qScale);
956 inputInfo.SetQuantizationOffset(qOffset);
957 outputInfo.SetQuantizationScale(qScale);
958 outputInfo.SetQuantizationOffset(qOffset);
959 kernelInfo.SetQuantizationScale(qScale);
960 kernelInfo.SetQuantizationOffset(qOffset);
961 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
962 biasInfo.SetQuantizationOffset(0);
963 }
964
965 std::vector<T> inputData(
966 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
967 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
968 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
969 }));
970
971 std::vector<T> kernelData(
972 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
973 1.0f, 0.0f, 0.0f,
974 0.0f, 2.0f, -1.5f,
975
976 0.0f, 0.0f, 0.0f,
977 0.2f, 0.2f, 0.2f,
978
979 0.5f, 0.0f, 0.5f,
980 0.0f, -1.0f, 0.0f
981 }));
982
983 std::vector<B> biasData(
984 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
985 1.0f, 0.0f, 0.0f
986 }));
987
988 std::vector<T> outputData(
989 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
990 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,
991 -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,
992 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
993 }));
994
telsoa01c577f2c2018-08-31 09:22:23 +0100995 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000996 if(biasEnabled)
997 {
998 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
999 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
1000 1, outputSize);
1001 }
1002
1003 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
1004 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
1005
1006 armnn::Convolution2dQueueDescriptor data;
1007 armnn::WorkloadInfo info;
1008 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
1009 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
1010
1011 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
1012 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
1013
1014 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
1015 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
1016
1017 data.m_Weight = &weightsTensor;
1018 data.m_Bias = &biasTensor;
1019 data.m_Parameters.m_StrideX = 1;
1020 data.m_Parameters.m_StrideY = stride;
1021 data.m_Parameters.m_PadLeft = 0;
1022 data.m_Parameters.m_PadRight = 0;
1023 data.m_Parameters.m_PadTop = padSize;
1024 data.m_Parameters.m_PadBottom = padSize;
1025 data.m_Parameters.m_BiasEnabled = biasEnabled;
1026
1027 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1028 inputHandle->Allocate();
1029 outputHandle->Allocate();
1030
1031 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
1032
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001033 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +00001034
telsoa01c577f2c2018-08-31 09:22:23 +01001035 // Output
telsoa014fcda012018-03-09 14:13:49 +00001036 LayerTestResult<T,4> ret(outputInfo);
1037 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1038 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
1039 return ret;
1040}
1041
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001042template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001043LayerTestResult<T,4> CompareConvolution2dTestImpl(
1044 armnn::IWorkloadFactory& workloadFactory,
1045 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1046 armnn::IWorkloadFactory& refWorkloadFactory)
telsoa014fcda012018-03-09 14:13:49 +00001047{
1048 unsigned int inputHeight = 8;
1049 unsigned int inputWidth = 16;
1050 unsigned int inputChannels = 3;
1051 unsigned int inputNum = 5;
1052
1053 unsigned int kernelHeight = 3;
1054 unsigned int kernelWidth = 3;
1055
1056 unsigned int strideX = 2;
1057 unsigned int strideY = 3;
1058 unsigned int padX = 1;
1059 unsigned int padY = 1;
1060
1061 unsigned int outputNum = inputNum;
1062 unsigned int outputChannels = 2;
1063 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1064 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1065
1066 armnn::TensorInfo inputTensorInfo;
1067 armnn::TensorInfo outputTensorInfo;
1068 armnn::TensorInfo kernelDesc;
1069 armnn::TensorInfo biasDesc;
1070
Matteo Martincigh747ef822018-12-18 09:26:39 +00001071 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1072 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1073 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1074 unsigned int biasShape[] = {outputChannels};
telsoa014fcda012018-03-09 14:13:49 +00001075
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001076 inputTensorInfo = armnn::TensorInfo(4, inputShape, ArmnnType);
1077 outputTensorInfo = armnn::TensorInfo(4, outputShape, ArmnnType);
1078 kernelDesc = armnn::TensorInfo(4, kernelShape, ArmnnType);
1079 biasDesc = armnn::TensorInfo(1, biasShape, ArmnnType);
telsoa014fcda012018-03-09 14:13:49 +00001080
1081 LayerTestResult<T,4> ret(outputTensorInfo);
1082
1083 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1084 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1085 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1086
1087 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1088 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1089
1090 armnn::Convolution2dQueueDescriptor data;
1091 armnn::WorkloadInfo info;
1092 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1093 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1094
1095 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1096 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1097
1098 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1099 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1100 data.m_Weight = &weightsTensor;
1101 data.m_Bias = &biasTensor;
1102 data.m_Parameters.m_StrideX = strideX;
1103 data.m_Parameters.m_StrideY = strideY;
1104 data.m_Parameters.m_PadLeft = padX;
1105 data.m_Parameters.m_PadRight = padX;
1106 data.m_Parameters.m_PadTop = padY;
1107 data.m_Parameters.m_PadBottom = padY;
1108 data.m_Parameters.m_BiasEnabled = true;
1109
1110 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1111 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1112
1113 armnn::Convolution2dQueueDescriptor refData = data;
1114 armnn::WorkloadInfo refInfo = info;
1115 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1116 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1117
1118 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1119 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1120
1121 outputHandleRef->Allocate();
1122 inputHandleRef->Allocate();
1123
1124 inputHandle->Allocate();
1125 outputHandle->Allocate();
1126
1127 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1128 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1129
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001130 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001131
Mike Kelly9b398322019-05-22 17:21:49 +01001132 workloadRef->PostAllocationConfigure();
telsoa014fcda012018-03-09 14:13:49 +00001133 workloadRef->Execute();
1134
1135 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1136 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1137
1138 return ret;
1139}
1140
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001141template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001142LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(
1143 armnn::IWorkloadFactory& workloadFactory,
1144 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1145 armnn::IWorkloadFactory& refWorkloadFactory,
Matteo Martincigh21350152018-11-28 16:22:22 +00001146 const armnnUtils::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +00001147{
1148 unsigned int inputHeight = 8;
1149 unsigned int inputWidth = 16;
1150 unsigned int inputChannels = 3;
1151 unsigned int inputNum = 5;
1152
1153 unsigned int kernelHeight = 3;
1154 unsigned int kernelWidth = 3;
1155 unsigned int channelMultiplier = 1;
1156
1157 unsigned int strideX = 2;
1158 unsigned int strideY = 3;
1159 unsigned int padX = 1;
1160 unsigned int padY = 1;
1161
1162 unsigned int outputNum = inputNum;
1163 unsigned int outputChannels = inputChannels * channelMultiplier;
1164 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1165 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1166
1167 armnn::TensorInfo inputTensorInfo;
1168 armnn::TensorInfo outputTensorInfo;
1169 armnn::TensorInfo kernelDesc;
1170 armnn::TensorInfo biasDesc;
1171
jimfly017af00da2018-10-31 14:43:53 +00001172
1173 std::vector<unsigned int> inputShape;
1174 std::vector<unsigned int> outputShape;
Matteo Martincigh747ef822018-12-18 09:26:39 +00001175 std::vector<unsigned int> kernelShape{ channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1176 std::vector<unsigned int> biasShape{ outputChannels };
jimfly017af00da2018-10-31 14:43:53 +00001177 switch (layout.GetDataLayout())
1178 {
1179 case armnn::DataLayout::NCHW:
1180 inputShape = { inputNum, inputChannels, inputHeight, inputWidth };
1181 outputShape = { outputNum, outputChannels, outputHeight, outputWidth };
jimfly017af00da2018-10-31 14:43:53 +00001182 break;
1183 case armnn::DataLayout ::NHWC:
1184 inputShape = { inputNum, inputHeight, inputWidth, inputChannels };
1185 outputShape = { outputNum, outputHeight, outputWidth, outputChannels };
jimfly017af00da2018-10-31 14:43:53 +00001186 break;
1187 default:
1188 throw armnn::InvalidArgumentException("unknown data layout ["
1189 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
1190 }
telsoa014fcda012018-03-09 14:13:49 +00001191
1192 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1193 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1194 int32_t qOffset = 0;
1195
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001196 inputTensorInfo = armnn::TensorInfo(4, inputShape.data(), ArmnnType, inputsQScale, qOffset);
1197 outputTensorInfo = armnn::TensorInfo(4, outputShape.data(), ArmnnType, outputQScale, qOffset);
1198 kernelDesc = armnn::TensorInfo(4, kernelShape.data(), ArmnnType, inputsQScale, qOffset);
jimfly017af00da2018-10-31 14:43:53 +00001199 biasDesc = armnn::TensorInfo(
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001200 1, biasShape.data(), armnn::GetBiasDataType(ArmnnType), inputsQScale, qOffset);
telsoa014fcda012018-03-09 14:13:49 +00001201
1202 LayerTestResult<T, 4> ret(outputTensorInfo);
1203
1204 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1205 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
jimfly01d84216a2018-10-26 12:56:21 +01001206 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1207 biasDesc, 1028, 0.0f, 255.0f);
telsoa014fcda012018-03-09 14:13:49 +00001208
1209 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1210 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1211
1212 armnn::DepthwiseConvolution2dQueueDescriptor data;
1213 armnn::WorkloadInfo info;
1214 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1215 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1216
1217 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1218 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1219
1220 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1221 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1222 data.m_Weight = &weightsTensor;
1223 data.m_Bias = &biasTensor;
1224 data.m_Parameters.m_StrideX = strideX;
1225 data.m_Parameters.m_StrideY = strideY;
1226 data.m_Parameters.m_PadLeft = padX;
1227 data.m_Parameters.m_PadRight = padX;
1228 data.m_Parameters.m_PadTop = padY;
1229 data.m_Parameters.m_PadBottom = padY;
1230 data.m_Parameters.m_BiasEnabled = true;
jimfly017af00da2018-10-31 14:43:53 +00001231 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +00001232
1233 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1234 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1235
1236 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1237 armnn::WorkloadInfo refInfo = info;
1238 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1239 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1240
1241 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1242 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1243
1244 outputHandleRef->Allocate();
1245 inputHandleRef->Allocate();
1246
1247 inputHandle->Allocate();
1248 outputHandle->Allocate();
1249
1250 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1251 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1252
Ruomei Yan495852f2019-05-23 11:37:33 +01001253 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001254
Ruomei Yan495852f2019-05-23 11:37:33 +01001255 workloadRef->PostAllocationConfigure();
telsoa014fcda012018-03-09 14:13:49 +00001256 workloadRef->Execute();
1257
1258 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1259 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1260
1261 return ret;
1262}