blob: 98e5090e274c8215e9f3d16d5f34cef57904b2ab [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,
Teresa Charlin20b1f882019-06-19 09:34:37 +0100819 typename T = armnn::ResolveType<ArmnnType>, typename B = armnn::ResolveType<ArmnnBType>>
820LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000821 armnn::IWorkloadFactory& workloadFactory,
822 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
Teresa Charlin20b1f882019-06-19 09:34:37 +0100823 const boost::multi_array<T, 4>& originalInput,
824 const boost::multi_array<T, 4>& originalKernel,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000825 const boost::multi_array<B, 1>& bias,
Teresa Charlin20b1f882019-06-19 09:34:37 +0100826 const boost::multi_array<T, 4>& originalOutputExpected,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000827 float qScale,
828 int32_t qOffset,
Teresa Charlin20b1f882019-06-19 09:34:37 +0100829 const armnn::DataLayout layout = armnn::DataLayout::NCHW,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000830 uint32_t padLeft = 0,
831 uint32_t padTop = 0,
832 uint32_t padRight = 0,
833 uint32_t padBottom = 0,
834 uint32_t strideX = 1,
Bruno Goncalves22972f02019-04-26 21:03:24 -0300835 uint32_t strideY = 1,
836 uint32_t dilationX = 1,
837 uint32_t dilationY = 1)
Nikhil Rajcec6b652018-10-12 13:51:57 +0100838{
Teresa Charlin20b1f882019-06-19 09:34:37 +0100839 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
840 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
841 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
842 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100843
Teresa Charlin20b1f882019-06-19 09:34:37 +0100844 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
845 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
846 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
847 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100848
Teresa Charlin20b1f882019-06-19 09:34:37 +0100849 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
850 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
851 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
852 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100853
Teresa Charlin20b1f882019-06-19 09:34:37 +0100854 bool biasEnabled = bias.size() > 0;
855
856 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
857 BOOST_ASSERT(inputNum == 1);
858 BOOST_ASSERT(outputNum == 1);
859
860 // If a bias is used, its size must equal the number of output channels.
861 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
862
863
864 // Note these tensors will use two (identical) batches.
865 armnn::TensorInfo inputTensorInfo =
866 armnnUtils::GetTensorInfo(2*inputNum, inputChannels, inputHeight, inputWidth, layout, ArmnnType);
867 armnn::TensorInfo outputTensorInfo =
868 armnnUtils::GetTensorInfo(2*outputNum, outputChannels, outputHeight, outputWidth, layout, ArmnnType);
869
870 // Kernel must be NCHW layout always, independently of the layout of the input and output for depthwise convolution.
871 armnn::TensorInfo kernelDesc({kernelDepthMul, kernelChannels, kernelHeight, kernelWidth}, ArmnnType);
872
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000873 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, ArmnnBType);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100874
875 // Set quantization parameters if the requested type is a quantized type.
Teresa Charlin20b1f882019-06-19 09:34:37 +0100876 if(armnn::IsQuantizedType<T>())
Nikhil Rajcec6b652018-10-12 13:51:57 +0100877 {
878 inputTensorInfo.SetQuantizationScale(qScale);
879 inputTensorInfo.SetQuantizationOffset(qOffset);
880 outputTensorInfo.SetQuantizationScale(qScale);
881 outputTensorInfo.SetQuantizationOffset(qOffset);
882 kernelDesc.SetQuantizationScale(qScale);
883 kernelDesc.SetQuantizationOffset(qOffset);
884 biasDesc.SetQuantizationScale(qScale*qScale);
885 biasDesc.SetQuantizationOffset(0);
886 }
887
Teresa Charlin20b1f882019-06-19 09:34:37 +0100888 LayerTestResult<T, 4> ret(outputTensorInfo);
889
890 // Construct input data
891 std::vector<T> input;
892 input.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100893 std::vector<T> inputData;
Teresa Charlin20b1f882019-06-19 09:34:37 +0100894 inputData.insert(inputData.end(), input.begin(), input.end());
895 inputData.insert(inputData.end(), input.begin(), input.end());
896
897 // at this point if we require it permute the input data
898 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
899 if (layout == armnn::DataLayout::NHWC)
900 {
901 std::vector<T> tmp(inputData.size());
902 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data(), sizeof(T));
903 inputData = tmp;
904 }
905
Nikhil Rajcec6b652018-10-12 13:51:57 +0100906 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
907
Teresa Charlin20b1f882019-06-19 09:34:37 +0100908 std::vector<T> output;
909 output.assign(originalOutputExpected.data(),
910 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100911
Teresa Charlin20b1f882019-06-19 09:34:37 +0100912 // Apply bias to output data if it is enabled.
913 if(biasEnabled)
914 {
915 std::vector<T> biasV;
916 biasV.assign(bias.data(), bias.data() + outputChannels);
917 ApplyBias(output, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
918 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
919 outputWidth, outputHeight);
920 }
921
922 // Construct expected output data
923 std::vector<T> outputData;
924 outputData.insert(outputData.end(), output.begin(), output.end());
925 outputData.insert(outputData.end(), output.begin(), output.end());
926
927 // at this point if we require it permute the expected output
928 if (layout == armnn::DataLayout::NHWC)
929 {
930 std::vector<T> tmp(outputData.size());
931 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data(), sizeof(T));
932 outputData = tmp;
933 }
Nikhil Rajcec6b652018-10-12 13:51:57 +0100934 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
935
936 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
937 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
938
Teresa Charlin20b1f882019-06-19 09:34:37 +0100939 armnn::DepthwiseConvolution2dQueueDescriptor data;
940 armnn::WorkloadInfo info;
Nikhil Rajcec6b652018-10-12 13:51:57 +0100941 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100942 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
943
Teresa Charlin20b1f882019-06-19 09:34:37 +0100944 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
945 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
946
947 if(biasEnabled)
948 {
949 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
950 }
951
952 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
953 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
954
Nikhil Rajcec6b652018-10-12 13:51:57 +0100955 data.m_Weight = &weightsTensor;
Teresa Charlin20b1f882019-06-19 09:34:37 +0100956 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
Nikhil Rajcec6b652018-10-12 13:51:57 +0100957 data.m_Parameters.m_StrideX = strideX;
958 data.m_Parameters.m_StrideY = strideY;
959 data.m_Parameters.m_PadLeft = padLeft;
960 data.m_Parameters.m_PadRight = padRight;
961 data.m_Parameters.m_PadTop = padTop;
962 data.m_Parameters.m_PadBottom = padBottom;
Teresa Charlin20b1f882019-06-19 09:34:37 +0100963 data.m_Parameters.m_BiasEnabled = biasEnabled;
964 data.m_Parameters.m_DataLayout = layout;
Bruno Goncalves22972f02019-04-26 21:03:24 -0300965 data.m_Parameters.m_DilationX = dilationX;
966 data.m_Parameters.m_DilationY = dilationY;
Nikhil Rajcec6b652018-10-12 13:51:57 +0100967
Nikhil Rajcec6b652018-10-12 13:51:57 +0100968 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100969 inputHandle->Allocate();
970 outputHandle->Allocate();
971
972 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
973
Ruomei Yan495852f2019-05-23 11:37:33 +0100974 ExecuteWorkload(*workload, memoryManager);
Nikhil Rajcec6b652018-10-12 13:51:57 +0100975
976 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
977
978 return ret;
979}
980
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000981template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000982LayerTestResult<T,4> Convolution1dTestImpl(
983 armnn::IWorkloadFactory& workloadFactory,
984 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
985 float qScale,
986 int32_t qOffset,
987 bool biasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000988{
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +0000989 using B = armnn::ResolveType<ArmnnBType>;
telsoa01c577f2c2018-08-31 09:22:23 +0100990 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000991 // 2D convolution with the final dimension set to 1.
992 // I don't anticipate this being particularly slow, given that convolution is implemented
993 // as a matrix multiplication, at which point dimension doesn't matter.
994
995 unsigned int batchSize = 1;
996 unsigned int inputChannels = 2;
997 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100998 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000999 unsigned int kernelSize = 3;
1000 unsigned int padSize = 2;
1001 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +01001002 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +00001003
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001004 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, ArmnnType);
1005 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, ArmnnType);
1006 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, ArmnnType);
1007 armnn::TensorInfo biasInfo({outputChannels}, ArmnnBType);
telsoa014fcda012018-03-09 14:13:49 +00001008
1009 // Set quantization parameters if the requested type is a quantized type.
1010 if(armnn::IsQuantizedType<T>())
1011 {
1012 inputInfo.SetQuantizationScale(qScale);
1013 inputInfo.SetQuantizationOffset(qOffset);
1014 outputInfo.SetQuantizationScale(qScale);
1015 outputInfo.SetQuantizationOffset(qOffset);
1016 kernelInfo.SetQuantizationScale(qScale);
1017 kernelInfo.SetQuantizationOffset(qOffset);
1018 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
1019 biasInfo.SetQuantizationOffset(0);
1020 }
1021
1022 std::vector<T> inputData(
1023 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
1024 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
1025 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
1026 }));
1027
1028 std::vector<T> kernelData(
1029 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
1030 1.0f, 0.0f, 0.0f,
1031 0.0f, 2.0f, -1.5f,
1032
1033 0.0f, 0.0f, 0.0f,
1034 0.2f, 0.2f, 0.2f,
1035
1036 0.5f, 0.0f, 0.5f,
1037 0.0f, -1.0f, 0.0f
1038 }));
1039
1040 std::vector<B> biasData(
1041 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
1042 1.0f, 0.0f, 0.0f
1043 }));
1044
1045 std::vector<T> outputData(
1046 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
1047 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,
1048 -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,
1049 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
1050 }));
1051
telsoa01c577f2c2018-08-31 09:22:23 +01001052 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +00001053 if(biasEnabled)
1054 {
1055 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
1056 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
1057 1, outputSize);
1058 }
1059
1060 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
1061 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
1062
1063 armnn::Convolution2dQueueDescriptor data;
1064 armnn::WorkloadInfo info;
1065 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
1066 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
1067
1068 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
1069 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
1070
1071 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
1072 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
1073
1074 data.m_Weight = &weightsTensor;
1075 data.m_Bias = &biasTensor;
1076 data.m_Parameters.m_StrideX = 1;
1077 data.m_Parameters.m_StrideY = stride;
1078 data.m_Parameters.m_PadLeft = 0;
1079 data.m_Parameters.m_PadRight = 0;
1080 data.m_Parameters.m_PadTop = padSize;
1081 data.m_Parameters.m_PadBottom = padSize;
1082 data.m_Parameters.m_BiasEnabled = biasEnabled;
1083
1084 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1085 inputHandle->Allocate();
1086 outputHandle->Allocate();
1087
1088 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
1089
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001090 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +00001091
telsoa01c577f2c2018-08-31 09:22:23 +01001092 // Output
telsoa014fcda012018-03-09 14:13:49 +00001093 LayerTestResult<T,4> ret(outputInfo);
1094 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1095 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
1096 return ret;
1097}
1098
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001099template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001100LayerTestResult<T,4> CompareConvolution2dTestImpl(
1101 armnn::IWorkloadFactory& workloadFactory,
1102 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1103 armnn::IWorkloadFactory& refWorkloadFactory)
telsoa014fcda012018-03-09 14:13:49 +00001104{
1105 unsigned int inputHeight = 8;
1106 unsigned int inputWidth = 16;
1107 unsigned int inputChannels = 3;
1108 unsigned int inputNum = 5;
1109
1110 unsigned int kernelHeight = 3;
1111 unsigned int kernelWidth = 3;
1112
1113 unsigned int strideX = 2;
1114 unsigned int strideY = 3;
1115 unsigned int padX = 1;
1116 unsigned int padY = 1;
1117
1118 unsigned int outputNum = inputNum;
1119 unsigned int outputChannels = 2;
1120 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1121 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1122
1123 armnn::TensorInfo inputTensorInfo;
1124 armnn::TensorInfo outputTensorInfo;
1125 armnn::TensorInfo kernelDesc;
1126 armnn::TensorInfo biasDesc;
1127
Matteo Martincigh747ef822018-12-18 09:26:39 +00001128 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1129 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1130 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1131 unsigned int biasShape[] = {outputChannels};
telsoa014fcda012018-03-09 14:13:49 +00001132
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001133 inputTensorInfo = armnn::TensorInfo(4, inputShape, ArmnnType);
1134 outputTensorInfo = armnn::TensorInfo(4, outputShape, ArmnnType);
1135 kernelDesc = armnn::TensorInfo(4, kernelShape, ArmnnType);
1136 biasDesc = armnn::TensorInfo(1, biasShape, ArmnnType);
telsoa014fcda012018-03-09 14:13:49 +00001137
1138 LayerTestResult<T,4> ret(outputTensorInfo);
1139
1140 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1141 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1142 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1143
1144 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1145 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1146
1147 armnn::Convolution2dQueueDescriptor data;
1148 armnn::WorkloadInfo info;
1149 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1150 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1151
1152 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1153 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1154
1155 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1156 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1157 data.m_Weight = &weightsTensor;
1158 data.m_Bias = &biasTensor;
1159 data.m_Parameters.m_StrideX = strideX;
1160 data.m_Parameters.m_StrideY = strideY;
1161 data.m_Parameters.m_PadLeft = padX;
1162 data.m_Parameters.m_PadRight = padX;
1163 data.m_Parameters.m_PadTop = padY;
1164 data.m_Parameters.m_PadBottom = padY;
1165 data.m_Parameters.m_BiasEnabled = true;
1166
1167 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1168 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1169
1170 armnn::Convolution2dQueueDescriptor refData = data;
1171 armnn::WorkloadInfo refInfo = info;
1172 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1173 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1174
1175 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1176 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1177
1178 outputHandleRef->Allocate();
1179 inputHandleRef->Allocate();
1180
1181 inputHandle->Allocate();
1182 outputHandle->Allocate();
1183
1184 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1185 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1186
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001187 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001188
Mike Kelly9b398322019-05-22 17:21:49 +01001189 workloadRef->PostAllocationConfigure();
telsoa014fcda012018-03-09 14:13:49 +00001190 workloadRef->Execute();
1191
1192 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1193 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1194
1195 return ret;
1196}
1197
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001198template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001199LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(
1200 armnn::IWorkloadFactory& workloadFactory,
1201 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1202 armnn::IWorkloadFactory& refWorkloadFactory,
Matteo Martincigh21350152018-11-28 16:22:22 +00001203 const armnnUtils::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +00001204{
1205 unsigned int inputHeight = 8;
1206 unsigned int inputWidth = 16;
1207 unsigned int inputChannels = 3;
1208 unsigned int inputNum = 5;
1209
1210 unsigned int kernelHeight = 3;
1211 unsigned int kernelWidth = 3;
1212 unsigned int channelMultiplier = 1;
1213
1214 unsigned int strideX = 2;
1215 unsigned int strideY = 3;
1216 unsigned int padX = 1;
1217 unsigned int padY = 1;
1218
1219 unsigned int outputNum = inputNum;
1220 unsigned int outputChannels = inputChannels * channelMultiplier;
1221 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1222 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1223
1224 armnn::TensorInfo inputTensorInfo;
1225 armnn::TensorInfo outputTensorInfo;
1226 armnn::TensorInfo kernelDesc;
1227 armnn::TensorInfo biasDesc;
1228
jimfly017af00da2018-10-31 14:43:53 +00001229
1230 std::vector<unsigned int> inputShape;
1231 std::vector<unsigned int> outputShape;
Matteo Martincigh747ef822018-12-18 09:26:39 +00001232 std::vector<unsigned int> kernelShape{ channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1233 std::vector<unsigned int> biasShape{ outputChannels };
jimfly017af00da2018-10-31 14:43:53 +00001234 switch (layout.GetDataLayout())
1235 {
1236 case armnn::DataLayout::NCHW:
1237 inputShape = { inputNum, inputChannels, inputHeight, inputWidth };
1238 outputShape = { outputNum, outputChannels, outputHeight, outputWidth };
jimfly017af00da2018-10-31 14:43:53 +00001239 break;
1240 case armnn::DataLayout ::NHWC:
1241 inputShape = { inputNum, inputHeight, inputWidth, inputChannels };
1242 outputShape = { outputNum, outputHeight, outputWidth, outputChannels };
jimfly017af00da2018-10-31 14:43:53 +00001243 break;
1244 default:
1245 throw armnn::InvalidArgumentException("unknown data layout ["
1246 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
1247 }
telsoa014fcda012018-03-09 14:13:49 +00001248
1249 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1250 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1251 int32_t qOffset = 0;
1252
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001253 inputTensorInfo = armnn::TensorInfo(4, inputShape.data(), ArmnnType, inputsQScale, qOffset);
1254 outputTensorInfo = armnn::TensorInfo(4, outputShape.data(), ArmnnType, outputQScale, qOffset);
1255 kernelDesc = armnn::TensorInfo(4, kernelShape.data(), ArmnnType, inputsQScale, qOffset);
jimfly017af00da2018-10-31 14:43:53 +00001256 biasDesc = armnn::TensorInfo(
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +00001257 1, biasShape.data(), armnn::GetBiasDataType(ArmnnType), inputsQScale, qOffset);
telsoa014fcda012018-03-09 14:13:49 +00001258
1259 LayerTestResult<T, 4> ret(outputTensorInfo);
1260
1261 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1262 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
jimfly01d84216a2018-10-26 12:56:21 +01001263 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1264 biasDesc, 1028, 0.0f, 255.0f);
telsoa014fcda012018-03-09 14:13:49 +00001265
1266 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1267 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1268
1269 armnn::DepthwiseConvolution2dQueueDescriptor data;
1270 armnn::WorkloadInfo info;
1271 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1272 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1273
1274 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1275 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1276
1277 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1278 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1279 data.m_Weight = &weightsTensor;
1280 data.m_Bias = &biasTensor;
1281 data.m_Parameters.m_StrideX = strideX;
1282 data.m_Parameters.m_StrideY = strideY;
1283 data.m_Parameters.m_PadLeft = padX;
1284 data.m_Parameters.m_PadRight = padX;
1285 data.m_Parameters.m_PadTop = padY;
1286 data.m_Parameters.m_PadBottom = padY;
1287 data.m_Parameters.m_BiasEnabled = true;
jimfly017af00da2018-10-31 14:43:53 +00001288 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +00001289
1290 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1291 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1292
1293 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1294 armnn::WorkloadInfo refInfo = info;
1295 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1296 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1297
1298 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1299 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1300
1301 outputHandleRef->Allocate();
1302 inputHandleRef->Allocate();
1303
1304 inputHandle->Allocate();
1305 outputHandle->Allocate();
1306
1307 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1308 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1309
Ruomei Yan495852f2019-05-23 11:37:33 +01001310 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001311
Ruomei Yan495852f2019-05-23 11:37:33 +01001312 workloadRef->PostAllocationConfigure();
telsoa014fcda012018-03-09 14:13:49 +00001313 workloadRef->Execute();
1314
1315 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1316 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1317
1318 return ret;
1319}