blob: 0ba8d7552c419dd9c516ac9b81c29b8c5d39fbaa [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-Tar5caf9072018-11-14 18:35:18 +00009
Matteo Martincigh21350152018-11-28 16:22:22 +000010#include <Permute.hpp>
11#include <DataLayoutIndexed.hpp>
12
13#include <test/TensorHelpers.hpp>
14
telsoa014fcda012018-03-09 14:13:49 +000015#include <armnn/ArmNN.hpp>
16#include <armnn/Tensor.hpp>
17#include <armnn/TypesUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +000018
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000019#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000020#include <backendsCommon/IBackendInternal.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000021#include <backendsCommon/WorkloadFactory.hpp>
Matteo Martincigh21350152018-11-28 16:22:22 +000022#include <backendsCommon/test/QuantizeHelper.hpp>
23
jimfly010a088a62018-10-25 17:05:05 +010024#include <boost/numeric/conversion/cast.hpp>
telsoa014fcda012018-03-09 14:13:49 +000025
Matteo Martincigh21350152018-11-28 16:22:22 +000026#include <string>
27
telsoa014fcda012018-03-09 14:13:49 +000028// Mapping from input type to bias type for fully connected layers.
29// float => float, uint8_t => int32_t
30template<typename T>
31struct FullyConnectedBiasTypeForInputType;
32
33template<>
34struct FullyConnectedBiasTypeForInputType<float>
35{
36 using Type = float;
37};
38
39template<>
40struct FullyConnectedBiasTypeForInputType<uint8_t>
41{
42 using Type = int32_t;
43};
44
telsoa01c577f2c2018-08-31 09:22:23 +010045// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000046template<typename T, typename B>
47void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
48 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
49{
50 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
51 "Invalid type and parameter combination.");
52 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
53 "Invalid type and parameter combination.");
54
telsoa01c577f2c2018-08-31 09:22:23 +010055 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000056 for (uint32_t i = 0; i < bias.size(); ++i)
57 {
58 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
59 for (uint32_t y = 0; y < h; ++y)
60 {
61 for (uint32_t x = 0; x < w; ++x)
62 {
63 uint32_t offset = (i * h + y) * w + x;
64 BOOST_ASSERT(offset < v.size());
65 T& outRef = v[offset];
66 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
67 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
68 }
69 }
70 }
71}
72
telsoa014fcda012018-03-09 14:13:49 +000073template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000074LayerTestResult<T, 4> SimpleConvolution2dTestImpl(
75 armnn::IWorkloadFactory& workloadFactory,
76 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
77 const boost::multi_array<T, 4>& originalInput,
78 const boost::multi_array<T, 4>& originalKernel,
79 const boost::multi_array<B, 1>& bias,
80 const boost::multi_array<T, 4>& originalOutputExpected,
81 float qScale,
82 int32_t qOffset,
Matthew Bentham8800c002018-11-19 13:19:28 +000083 const armnn::DataLayout layout = armnn::DataLayout::NCHW,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000084 uint32_t padLeft = 0,
85 uint32_t padTop = 0,
86 uint32_t padRight = 0,
87 uint32_t padBottom = 0)
telsoa014fcda012018-03-09 14:13:49 +000088{
jimfly010a088a62018-10-25 17:05:05 +010089 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
90 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
91 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
92 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000093
jimfly010a088a62018-10-25 17:05:05 +010094 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
95 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
96 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
97 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000098
jimfly010a088a62018-10-25 17:05:05 +010099 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
100 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
101 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
102 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000103
104 bool biasEnabled = bias.size() > 0;
105
telsoa01c577f2c2018-08-31 09:22:23 +0100106 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +0000107 BOOST_ASSERT(inputNum == 1);
108 BOOST_ASSERT(outputNum == 1);
109
telsoa01c577f2c2018-08-31 09:22:23 +0100110 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +0000111 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
112
113
telsoa01c577f2c2018-08-31 09:22:23 +0100114 // Note these tensors will use two (identical) batches.
Nina Drozdd41b2592018-11-19 13:03:36 +0000115 armnn::TensorInfo inputTensorInfo =
116 armnnUtils::GetTensorInfo<T>(2*inputNum, inputChannels, inputHeight, inputWidth, layout);
117 armnn::TensorInfo outputTensorInfo =
118 armnnUtils::GetTensorInfo<T>(2*outputNum, outputChannels, outputHeight, outputWidth, layout);
119 armnn::TensorInfo kernelDesc =
120 armnnUtils::GetTensorInfo<T>(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000121 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
122
123 // Set quantization parameters if the requested type is a quantized type.
124 if(armnn::IsQuantizedType<T>())
125 {
126 inputTensorInfo.SetQuantizationScale(qScale);
127 inputTensorInfo.SetQuantizationOffset(qOffset);
128 outputTensorInfo.SetQuantizationScale(qScale);
129 outputTensorInfo.SetQuantizationOffset(qOffset);
130 kernelDesc.SetQuantizationScale(qScale);
131 kernelDesc.SetQuantizationOffset(qOffset);
132 biasDesc.SetQuantizationScale(qScale*qScale);
133 biasDesc.SetQuantizationOffset(0);
134 }
135
136 LayerTestResult<T, 4> ret(outputTensorInfo);
137
telsoa01c577f2c2018-08-31 09:22:23 +0100138 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000139 std::vector<T> inputImage;
jimfly010a088a62018-10-25 17:05:05 +0100140 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000141 std::vector<T> inputData;
142 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
143 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
jimfly010a088a62018-10-25 17:05:05 +0100144
145 // at this point if we require it permute the input data
146 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000147 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100148 {
149 std::vector<T> tmp(inputData.size());
150 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
151 inputData = tmp;
152 }
153
telsoa014fcda012018-03-09 14:13:49 +0000154 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
155
156 std::vector<T> outputImage;
jimfly010a088a62018-10-25 17:05:05 +0100157 outputImage.assign(originalOutputExpected.data(),
158 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000159
telsoa01c577f2c2018-08-31 09:22:23 +0100160 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000161 if(biasEnabled)
162 {
163 std::vector<T> biasV;
164 biasV.assign(bias.data(), bias.data() + outputChannels);
165 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
166 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
167 outputWidth, outputHeight);
168 }
169
telsoa01c577f2c2018-08-31 09:22:23 +0100170 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000171 std::vector<T> outputData;
172 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
173 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
174
jimfly010a088a62018-10-25 17:05:05 +0100175 // at this point if we require it permute the expected output
Matthew Bentham8800c002018-11-19 13:19:28 +0000176 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100177 {
178 std::vector<T> tmp(outputData.size());
179 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
180 outputData = tmp;
181 }
telsoa014fcda012018-03-09 14:13:49 +0000182 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
183
telsoa01c577f2c2018-08-31 09:22:23 +0100184 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000185 uint32_t strideX = 1;
186 uint32_t strideY = 1;
187
188 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
189 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
190
191 armnn::Convolution2dQueueDescriptor data;
192 armnn::WorkloadInfo info;
193 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
194 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
jimfly010a088a62018-10-25 17:05:05 +0100195 // Permute the kernel if necessary
196 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
Matthew Bentham8800c002018-11-19 13:19:28 +0000197 if (layout == armnn::DataLayout::NHWC)
jimfly010a088a62018-10-25 17:05:05 +0100198 {
199 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
200 }
telsoa014fcda012018-03-09 14:13:49 +0000201 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
202
203 if(biasEnabled)
204 {
205 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
206 }
207
208 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
209 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
210
211 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100212 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 +0000213 data.m_Parameters.m_StrideX = strideX;
214 data.m_Parameters.m_StrideY = strideY;
215 data.m_Parameters.m_PadLeft = padLeft;
216 data.m_Parameters.m_PadRight = padRight;
217 data.m_Parameters.m_PadTop = padTop;
218 data.m_Parameters.m_PadBottom = padBottom;
219 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000220 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000221
222 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
223 inputHandle->Allocate();
224 outputHandle->Allocate();
225
226 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
227
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000228 ExecuteWorkload(*workload, memoryManager);
surmeh013537c2c2018-05-18 16:31:43 +0100229
230 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
231
232 return ret;
233}
234
235template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000236LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(
237 armnn::IWorkloadFactory& workloadFactory,
238 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
239 const boost::multi_array<T, 4>& input,
240 const boost::multi_array<T, 4>& kernel,
241 const boost::multi_array<B, 1>& bias,
242 const boost::multi_array<T, 4>& outputExpected,
243 armnn::DataLayout dataLayout,
244 float qScale,
245 int32_t qOffset,
246 uint32_t padLeft = 1,
247 uint32_t padTop = 1,
248 uint32_t padRight = 1,
249 uint32_t padBottom = 1,
250 uint32_t strideX = 1,
251 uint32_t strideY = 1)
Francis Murtaghd59116e2018-10-04 16:03:07 +0100252{
253 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
254 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
255 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
256 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
257
258 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
259 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
260 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
261 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
262
263 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
264 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
265 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
266 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
267
268 bool biasEnabled = bias.size() > 0;
269
270 // Creates the tensors.
271 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
272 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
273 armnn::GetDataType<T>());
274 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
275 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
276
277 // Construct the input data.
278 std::vector<T> inputData;
279 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
280 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
281
282 // Construct the output data, with bias applied, as appropriate.
283 std::vector<T> outputData;
284 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
285
286 LayerTestResult<T, 4> ret(outputTensorInfo);
287 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
288
289 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
290 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
291
292 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
293 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
294
295 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
296
297 armnn::Convolution2dQueueDescriptor data;
298
299 data.m_Weight = &weightsTensor;
300 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
301 data.m_Parameters.m_StrideX = strideX;
302 data.m_Parameters.m_StrideY = strideY;
303 data.m_Parameters.m_PadLeft = padLeft;
304 data.m_Parameters.m_PadRight = padRight;
305 data.m_Parameters.m_PadTop = padTop;
306 data.m_Parameters.m_PadBottom = padBottom;
307 data.m_Parameters.m_BiasEnabled = biasEnabled;
308 data.m_Parameters.m_DataLayout = dataLayout;
309
310 armnn::WorkloadInfo info;
311 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
312 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
313
314 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
315 inputHandle->Allocate();
316 outputHandle->Allocate();
317
318 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
319
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000320 ExecuteWorkload(*workload, memoryManager);
Francis Murtaghd59116e2018-10-04 16:03:07 +0100321
322 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
323
324 return ret;
325}
326
327template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000328LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(
329 armnn::IWorkloadFactory& workloadFactory,
330 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
331 const boost::multi_array<T, 4>& input,
332 const boost::multi_array<T, 4>& originalKernel,
333 const boost::multi_array<B, 1>& bias,
334 const boost::multi_array<T, 4>& outputExpected,
335 float qScale,
336 int32_t qOffset,
Matthew Bentham8800c002018-11-19 13:19:28 +0000337 const armnn::DataLayout layout,
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000338 uint32_t padLeft = 0,
339 uint32_t padTop = 0,
340 uint32_t padRight = 0,
341 uint32_t padBottom = 0,
342 uint32_t strideX = 1,
343 uint32_t strideY = 1)
surmeh013537c2c2018-05-18 16:31:43 +0100344{
345 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
346 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
347 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
348 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
jimfly01382a91d2018-10-26 15:55:50 +0100349 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
350 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
351 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
352 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +0100353 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
354 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
355 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
356 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
357
telsoa01c577f2c2018-08-31 09:22:23 +0100358 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100359 bool biasEnabled = bias.size() > 0;
360 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
361
telsoa01c577f2c2018-08-31 09:22:23 +0100362 // Creates the tensors.
Nina Drozdd41b2592018-11-19 13:03:36 +0000363 armnn::TensorInfo inputTensorInfo =
364 armnnUtils::GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
365 armnn::TensorInfo outputTensorInfo =
366 armnnUtils::GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
367 armnn::TensorInfo kernelDesc =
368 armnnUtils::GetTensorInfo<T>(kernelChanMul, kernelChannels, kernelHeight, kernelWidth, layout);
surmeh013537c2c2018-05-18 16:31:43 +0100369 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
370
371 // Set quantization parameters if the requested type is a quantized type.
372 if (armnn::IsQuantizedType<T>())
373 {
374 inputTensorInfo.SetQuantizationScale(qScale);
375 inputTensorInfo.SetQuantizationOffset(qOffset);
376 outputTensorInfo.SetQuantizationScale(qScale);
377 outputTensorInfo.SetQuantizationOffset(qOffset);
378 kernelDesc.SetQuantizationScale(qScale);
379 kernelDesc.SetQuantizationOffset(qOffset);
380 biasDesc.SetQuantizationScale(qScale*qScale);
381 biasDesc.SetQuantizationOffset(0);
382 }
383
telsoa01c577f2c2018-08-31 09:22:23 +0100384 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100385 std::vector<T> inputData;
386 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
jimfly01382a91d2018-10-26 15:55:50 +0100387
388 // At this point if we require it permute the input data
389 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000390 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100391 {
392 std::vector<T> tmp(inputData.size());
393 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
394 inputData = tmp;
395 }
396
surmeh013537c2c2018-05-18 16:31:43 +0100397 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
398
telsoa01c577f2c2018-08-31 09:22:23 +0100399 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100400 std::vector<T> outputData;
401 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
402 if (biasEnabled)
403 {
404 std::vector<T> biasV;
405 biasV.assign(bias.data(), bias.data() + outputChannels);
406 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
407 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
408 outputWidth, outputHeight);
409 }
410
411 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01382a91d2018-10-26 15:55:50 +0100412
413 // At this point if we require it permute the expected output
Matthew Bentham8800c002018-11-19 13:19:28 +0000414 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100415 {
416 std::vector<T> tmp(outputData.size());
417 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
418 outputData = tmp;
419 }
420
surmeh013537c2c2018-05-18 16:31:43 +0100421 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
422
423 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
424 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
425
426 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
jimfly01382a91d2018-10-26 15:55:50 +0100427
428 // Permute the kernel if necessary
429 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
Matthew Bentham8800c002018-11-19 13:19:28 +0000430 if (layout == armnn::DataLayout::NHWC)
jimfly01382a91d2018-10-26 15:55:50 +0100431 {
432 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
433 }
434
surmeh013537c2c2018-05-18 16:31:43 +0100435 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
436
437 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
438 if (biasEnabled)
439 {
440 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
441 }
442
443 armnn::DepthwiseConvolution2dQueueDescriptor data;
444 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100445 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 +0100446 data.m_Parameters.m_StrideX = strideX;
447 data.m_Parameters.m_StrideY = strideY;
448 data.m_Parameters.m_PadLeft = padLeft;
449 data.m_Parameters.m_PadRight = padRight;
450 data.m_Parameters.m_PadTop = padTop;
451 data.m_Parameters.m_PadBottom = padBottom;
452 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000453 data.m_Parameters.m_DataLayout = layout;
surmeh013537c2c2018-05-18 16:31:43 +0100454
455 armnn::WorkloadInfo info;
456 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
457 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
458
459 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
460 inputHandle->Allocate();
461 outputHandle->Allocate();
462
463 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
464
telsoa014fcda012018-03-09 14:13:49 +0000465 workload->Execute();
466
467 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
468
469 return ret;
470}
471
472template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000473LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(
474 armnn::IWorkloadFactory& workloadFactory,
475 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
476 float qScale,
477 int32_t qOffset,
478 bool biasEnabled,
Matthew Bentham8800c002018-11-19 13:19:28 +0000479 const armnn::DataLayout layout)
telsoa014fcda012018-03-09 14:13:49 +0000480{
481 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;
489
490 unsigned int outputHeight = 1;
491 unsigned int outputWidth = 1;
492 unsigned int outputChannels = kernelChannels;
493 unsigned int outputNum = inputNum;
494
Nina Drozdd41b2592018-11-19 13:03:36 +0000495 armnn::TensorInfo inputTensorInfo =
496 armnnUtils::GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
497 armnn::TensorInfo outputTensorInfo =
498 armnnUtils::GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
499 armnn::TensorInfo kernelDesc = armnnUtils::GetTensorInfo<T>(1, outputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000500 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
501
502 // Set quantization parameters if the requested type is a quantized type.
503 if(armnn::IsQuantizedType<T>())
504 {
505 inputTensorInfo.SetQuantizationScale(qScale);
506 inputTensorInfo.SetQuantizationOffset(qOffset);
507 outputTensorInfo.SetQuantizationScale(qScale);
508 outputTensorInfo.SetQuantizationOffset(qOffset);
509 kernelDesc.SetQuantizationScale(qScale);
510 kernelDesc.SetQuantizationOffset(qOffset);
511 biasDesc.SetQuantizationScale(qScale*qScale);
512 biasDesc.SetQuantizationOffset(0);
513 }
jimfly01b9c89632018-10-26 16:50:13 +0100514 std::vector<T> inputData = std::vector<T>(
515 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
516 1.f, 2.f, 1.f,
517 2.f, 1.f, 2.f,
518 1.f, 2.f, 1.f,
telsoa014fcda012018-03-09 14:13:49 +0000519
jimfly01b9c89632018-10-26 16:50:13 +0100520 1.f, 2.f, 1.f,
521 2.f, 1.f, 2.f,
522 1.f, 2.f, 1.f,
523 }));
524 // at this point if we require it permute the input data
525 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000526 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100527 {
528 std::vector<T> tmp(inputData.size());
529 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
530 inputData = tmp;
531 }
532 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000533
534 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
535 {0, 2}));
536 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
537
jimfly01b9c89632018-10-26 16:50:13 +0100538 std::vector<T> kernelData = std::vector<T>(
539 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
540 1.f, 0.f, 1.f,
541 0.f, 0.f, 0.f,
542 -1.f, 0.f, -1.f,
telsoa014fcda012018-03-09 14:13:49 +0000543
jimfly01b9c89632018-10-26 16:50:13 +0100544 1.f, 0.f, 1.f,
545 0.f, 0.f, 0.f,
546 -1.f, 0.f, -1.f,
547 }));
Matthew Bentham8800c002018-11-19 13:19:28 +0000548 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100549 {
550 std::vector<T> tmp(kernelData.size());
551 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, kernelData.data(), tmp.data());
552 kernelData = tmp;
553 }
554 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000555
telsoa01c577f2c2018-08-31 09:22:23 +0100556 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000557 std::vector<T> outputImage(
558 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
559 outputTensorInfo.GetQuantizationOffset(),
560 {0.f, 0.f})
561 );
562
telsoa01c577f2c2018-08-31 09:22:23 +0100563 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000564 if(biasEnabled)
565 {
566 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
567 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
568 outputWidth, outputHeight);
569 }
570
571 LayerTestResult<T, 4> ret(outputTensorInfo);
Matthew Bentham8800c002018-11-19 13:19:28 +0000572 if (layout == armnn::DataLayout::NHWC)
jimfly01b9c89632018-10-26 16:50:13 +0100573 {
574 std::vector<T> tmp(outputImage.size());
575 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputImage.data(), tmp.data());
576 outputImage = tmp;
577 }
578
telsoa014fcda012018-03-09 14:13:49 +0000579 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
580
581 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
582 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
583
584 armnn::DepthwiseConvolution2dQueueDescriptor data;
585 armnn::WorkloadInfo info;
586 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
587 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
588
589 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
590 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
591
592 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
593 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
594
595 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100596 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000597 data.m_Parameters.m_StrideX = 1;
598 data.m_Parameters.m_StrideY = 1;
599 data.m_Parameters.m_PadLeft = 0;
600 data.m_Parameters.m_PadRight = 0;
601 data.m_Parameters.m_PadTop = 0;
602 data.m_Parameters.m_PadBottom = 0;
603 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000604 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000605
606 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
607 inputHandle->Allocate();
608 outputHandle->Allocate();
609
610 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
611
612 workload->Execute();
613
614 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
615
616 return ret;
617}
618
619template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000620LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(
621 armnn::IWorkloadFactory& workloadFactory,
622 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
623 float qScale,
624 int32_t qOffset,
625 bool biasEnabled,
Matthew Bentham8800c002018-11-19 13:19:28 +0000626 const armnn::DataLayout layout)
telsoa014fcda012018-03-09 14:13:49 +0000627{
628 unsigned int depthMultiplier = 2;
629
630 unsigned int inputHeight = 8;
631 unsigned int inputWidth = 16;
632 unsigned int inputChannels = 2;
633 unsigned int inputBatchSize = 1;
634
635 unsigned int kernelHeight = 5;
636 unsigned int kernelWidth = 3;
637
638 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
639 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
640 unsigned int outputChannels = inputChannels * depthMultiplier;
641 unsigned int outputBatchSize = inputBatchSize;
642
Nina Drozdd41b2592018-11-19 13:03:36 +0000643 armnn::TensorInfo inputTensorInfo = armnnUtils::GetTensorInfo<T>(
jimfly01d84216a2018-10-26 12:56:21 +0100644 inputBatchSize, inputChannels, inputHeight, inputWidth, layout);
Nina Drozdd41b2592018-11-19 13:03:36 +0000645 armnn::TensorInfo outputTensorInfo = armnnUtils::GetTensorInfo<T>(
jimfly01d84216a2018-10-26 12:56:21 +0100646 outputBatchSize, outputChannels, outputHeight, outputWidth, layout);
Nina Drozdd41b2592018-11-19 13:03:36 +0000647 armnn::TensorInfo kernelDesc = armnnUtils::GetTensorInfo<T>(
jimfly01d84216a2018-10-26 12:56:21 +0100648 depthMultiplier, inputChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000649 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
650
651 // Set quantization parameters if the requested type is a quantized type.
652 if(armnn::IsQuantizedType<T>())
653 {
654 inputTensorInfo.SetQuantizationScale(qScale);
655 inputTensorInfo.SetQuantizationOffset(qOffset);
656 outputTensorInfo.SetQuantizationScale(qScale);
657 outputTensorInfo.SetQuantizationOffset(qOffset);
658 kernelDesc.SetQuantizationScale(qScale);
659 kernelDesc.SetQuantizationOffset(qOffset);
660 biasDesc.SetQuantizationScale(qScale*qScale);
661 biasDesc.SetQuantizationOffset(0);
662 }
663
jimfly01d84216a2018-10-26 12:56:21 +0100664 // NOTE: originalInputData is in NCHW format
665 std::vector<T> originalInputData = std::vector<T>(
666 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
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.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,
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.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,
674 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,
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 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
682 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
683 }));
684 std::vector<T> inputData = originalInputData;
685 // at this point if we require it permute the input data
686 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
Matthew Bentham8800c002018-11-19 13:19:28 +0000687 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100688 {
689 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, originalInputData.data(), inputData.data());
690 }
691 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
telsoa014fcda012018-03-09 14:13:49 +0000692
693 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
694 {0, 2, 1, -1}));
695 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
696
jimfly01d84216a2018-10-26 12:56:21 +0100697 std::vector<T> originalKernelData = std::vector<T>(
698 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
699 1, 1, 1,
700 1, -1, 1,
701 1, 1, 1,
702 1, 1, 1,
703 1, 1, 1,
telsoa014fcda012018-03-09 14:13:49 +0000704
jimfly01d84216a2018-10-26 12:56:21 +0100705 2, 2, 2,
706 2, 2, 2,
707 2, 2, 2,
708 2, 2, 2,
709 2, 2, 2,
telsoa014fcda012018-03-09 14:13:49 +0000710
jimfly01d84216a2018-10-26 12:56:21 +0100711 0, 0, 0,
712 0, -1, 0,
713 0, 0, 0,
714 0, 0, 0,
715 0, 0, 0,
telsoa014fcda012018-03-09 14:13:49 +0000716
jimfly01d84216a2018-10-26 12:56:21 +0100717 0, 0, 0,
718 0, 0, 0,
719 0, 1, 0,
720 0, 0, 0,
721 0, 0, 0
722 }));
723 std::vector<T> kernelData = originalKernelData;
Matthew Bentham8800c002018-11-19 13:19:28 +0000724 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100725 {
726 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernelData.data(), kernelData.data());
727 }
728 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
telsoa014fcda012018-03-09 14:13:49 +0000729
telsoa01c577f2c2018-08-31 09:22:23 +0100730 // Manually calculated.
jimfly01d84216a2018-10-26 12:56:21 +0100731 std::vector<T> originalOutputImage = std::vector<T>(
telsoa014fcda012018-03-09 14:13:49 +0000732 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
733 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
734 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
735 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
736 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
737 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
738 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
739
740 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
741 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
742 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
743 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
744 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
745 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
746
747 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
748 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
749 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
750 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
751 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
752 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
753
754 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
755 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
756 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
757 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
758 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
759 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
760 }));
761
telsoa01c577f2c2018-08-31 09:22:23 +0100762 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000763 if(biasEnabled)
764 {
jimfly01d84216a2018-10-26 12:56:21 +0100765 ApplyBias(originalOutputImage,
766 outputTensorInfo.GetQuantizationScale(),
767 outputTensorInfo.GetQuantizationOffset(),
768 biasV,
769 biasDesc.GetQuantizationScale(),
770 biasDesc.GetQuantizationOffset(),
771 outputWidth,
772 outputHeight);
telsoa014fcda012018-03-09 14:13:49 +0000773 }
774
775 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01d84216a2018-10-26 12:56:21 +0100776 std::vector<T> outputImage = originalOutputImage;
Matthew Bentham8800c002018-11-19 13:19:28 +0000777 if (layout == armnn::DataLayout::NHWC)
jimfly01d84216a2018-10-26 12:56:21 +0100778 {
779 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, originalOutputImage.data(), outputImage.data());
780 }
781
telsoa014fcda012018-03-09 14:13:49 +0000782 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
783
784 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
785 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
786
787 armnn::DepthwiseConvolution2dQueueDescriptor data;
788 armnn::WorkloadInfo info;
789 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
790 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
791
792 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
793 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
794
795 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
796 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
797
798 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100799 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000800 data.m_Parameters.m_StrideX = 2;
801 data.m_Parameters.m_StrideY = 1;
802 data.m_Parameters.m_PadLeft = 0;
803 data.m_Parameters.m_PadRight = 0;
804 data.m_Parameters.m_PadTop = 1;
805 data.m_Parameters.m_PadBottom = 1;
806 data.m_Parameters.m_BiasEnabled = biasEnabled;
Matthew Bentham8800c002018-11-19 13:19:28 +0000807 data.m_Parameters.m_DataLayout = layout;
telsoa014fcda012018-03-09 14:13:49 +0000808
809 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
810 inputHandle->Allocate();
811 outputHandle->Allocate();
812
813 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
814
815 workload->Execute();
816
817 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
818
819 return ret;
820}
821
Nikhil Rajcec6b652018-10-12 13:51:57 +0100822template<typename T, typename B>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000823LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(
824 armnn::IWorkloadFactory& workloadFactory,
825 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
826 const boost::multi_array<T, 4>& input,
827 const boost::multi_array<T, 4>& kernel,
828 const boost::multi_array<B, 1>& bias,
829 const boost::multi_array<T, 4>& outputExpected,
830 float qScale,
831 int32_t qOffset,
832 uint32_t padLeft = 0,
833 uint32_t padTop = 0,
834 uint32_t padRight = 0,
835 uint32_t padBottom = 0,
836 uint32_t strideX = 1,
837 uint32_t strideY = 1)
Nikhil Rajcec6b652018-10-12 13:51:57 +0100838{
839 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
840 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
841 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
842 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
843
844 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
845 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
846 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
847 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
848
849 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
850 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
851 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
852 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
853
854 // Creates the tensors.
855 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
856 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
857 armnn::GetDataType<T>());
858 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
859 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
860
861 // Set quantization parameters if the requested type is a quantized type.
862 if (armnn::IsQuantizedType<T>())
863 {
864 inputTensorInfo.SetQuantizationScale(qScale);
865 inputTensorInfo.SetQuantizationOffset(qOffset);
866 outputTensorInfo.SetQuantizationScale(qScale);
867 outputTensorInfo.SetQuantizationOffset(qOffset);
868 kernelDesc.SetQuantizationScale(qScale);
869 kernelDesc.SetQuantizationOffset(qOffset);
870 biasDesc.SetQuantizationScale(qScale*qScale);
871 biasDesc.SetQuantizationOffset(0);
872 }
873
874 // Construct the input data.
875 std::vector<T> inputData;
876 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
877 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
878
879 // Construct the output data, with bias applied, as appropriate.
880 std::vector<T> outputData;
881 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
882
883 LayerTestResult<T, 4> ret(outputTensorInfo);
884 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
885
886 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
887 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
888
889 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
890 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
891
892 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
893
894 armnn::DepthwiseConvolution2dQueueDescriptor data;
895 data.m_Weight = &weightsTensor;
896 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
897 data.m_Parameters.m_StrideX = strideX;
898 data.m_Parameters.m_StrideY = strideY;
899 data.m_Parameters.m_PadLeft = padLeft;
900 data.m_Parameters.m_PadRight = padRight;
901 data.m_Parameters.m_PadTop = padTop;
902 data.m_Parameters.m_PadBottom = padBottom;
903 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
904
905 armnn::WorkloadInfo info;
906 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
907 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
908
909 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
910
911 inputHandle->Allocate();
912 outputHandle->Allocate();
913
914 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
915
Nikhil Rajcec6b652018-10-12 13:51:57 +0100916 workload->Execute();
917
918 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
919
920 return ret;
921}
922
telsoa014fcda012018-03-09 14:13:49 +0000923template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000924LayerTestResult<T,4> Convolution1dTestImpl(
925 armnn::IWorkloadFactory& workloadFactory,
926 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
927 float qScale,
928 int32_t qOffset,
929 bool biasEnabled)
telsoa014fcda012018-03-09 14:13:49 +0000930{
931 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
932
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
947 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
948 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
949 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
950 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
951
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
1042
1043
1044template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001045LayerTestResult<T,4> CompareConvolution2dTestImpl(
1046 armnn::IWorkloadFactory& workloadFactory,
1047 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1048 armnn::IWorkloadFactory& refWorkloadFactory)
telsoa014fcda012018-03-09 14:13:49 +00001049{
1050 unsigned int inputHeight = 8;
1051 unsigned int inputWidth = 16;
1052 unsigned int inputChannels = 3;
1053 unsigned int inputNum = 5;
1054
1055 unsigned int kernelHeight = 3;
1056 unsigned int kernelWidth = 3;
1057
1058 unsigned int strideX = 2;
1059 unsigned int strideY = 3;
1060 unsigned int padX = 1;
1061 unsigned int padY = 1;
1062
1063 unsigned int outputNum = inputNum;
1064 unsigned int outputChannels = 2;
1065 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1066 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1067
1068 armnn::TensorInfo inputTensorInfo;
1069 armnn::TensorInfo outputTensorInfo;
1070 armnn::TensorInfo kernelDesc;
1071 armnn::TensorInfo biasDesc;
1072
1073 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1074 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1075 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1076 unsigned int biasShape[] = {outputChannels};
1077
1078 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
1079 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
1080 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
1081 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
1082
1083 LayerTestResult<T,4> ret(outputTensorInfo);
1084
1085 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1086 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1087 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1088
1089 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1090 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1091
1092 armnn::Convolution2dQueueDescriptor data;
1093 armnn::WorkloadInfo info;
1094 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1095 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1096
1097 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1098 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1099
1100 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1101 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1102 data.m_Weight = &weightsTensor;
1103 data.m_Bias = &biasTensor;
1104 data.m_Parameters.m_StrideX = strideX;
1105 data.m_Parameters.m_StrideY = strideY;
1106 data.m_Parameters.m_PadLeft = padX;
1107 data.m_Parameters.m_PadRight = padX;
1108 data.m_Parameters.m_PadTop = padY;
1109 data.m_Parameters.m_PadBottom = padY;
1110 data.m_Parameters.m_BiasEnabled = true;
1111
1112 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1113 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1114
1115 armnn::Convolution2dQueueDescriptor refData = data;
1116 armnn::WorkloadInfo refInfo = info;
1117 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1118 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1119
1120 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1121 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1122
1123 outputHandleRef->Allocate();
1124 inputHandleRef->Allocate();
1125
1126 inputHandle->Allocate();
1127 outputHandle->Allocate();
1128
1129 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1130 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1131
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001132 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +00001133
telsoa014fcda012018-03-09 14:13:49 +00001134 workloadRef->Execute();
1135
1136 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1137 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1138
1139 return ret;
1140}
1141
1142template<typename T>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001143LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(
1144 armnn::IWorkloadFactory& workloadFactory,
1145 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
1146 armnn::IWorkloadFactory& refWorkloadFactory,
Matteo Martincigh21350152018-11-28 16:22:22 +00001147 const armnnUtils::DataLayoutIndexed& layout)
telsoa014fcda012018-03-09 14:13:49 +00001148{
1149 unsigned int inputHeight = 8;
1150 unsigned int inputWidth = 16;
1151 unsigned int inputChannels = 3;
1152 unsigned int inputNum = 5;
1153
1154 unsigned int kernelHeight = 3;
1155 unsigned int kernelWidth = 3;
1156 unsigned int channelMultiplier = 1;
1157
1158 unsigned int strideX = 2;
1159 unsigned int strideY = 3;
1160 unsigned int padX = 1;
1161 unsigned int padY = 1;
1162
1163 unsigned int outputNum = inputNum;
1164 unsigned int outputChannels = inputChannels * channelMultiplier;
1165 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1166 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1167
1168 armnn::TensorInfo inputTensorInfo;
1169 armnn::TensorInfo outputTensorInfo;
1170 armnn::TensorInfo kernelDesc;
1171 armnn::TensorInfo biasDesc;
1172
jimfly017af00da2018-10-31 14:43:53 +00001173
1174 std::vector<unsigned int> inputShape;
1175 std::vector<unsigned int> outputShape;
1176 std::vector<unsigned int> kernelShape;
1177 std::vector<unsigned int> biasShape= { outputChannels };
1178 switch (layout.GetDataLayout())
1179 {
1180 case armnn::DataLayout::NCHW:
1181 inputShape = { inputNum, inputChannels, inputHeight, inputWidth };
1182 outputShape = { outputNum, outputChannels, outputHeight, outputWidth };
1183 kernelShape = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1184 break;
1185 case armnn::DataLayout ::NHWC:
1186 inputShape = { inputNum, inputHeight, inputWidth, inputChannels };
1187 outputShape = { outputNum, outputHeight, outputWidth, outputChannels };
1188 kernelShape = { channelMultiplier, kernelHeight, kernelWidth, inputChannels };
1189 break;
1190 default:
1191 throw armnn::InvalidArgumentException("unknown data layout ["
1192 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
1193 }
telsoa014fcda012018-03-09 14:13:49 +00001194
1195 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1196 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1197 int32_t qOffset = 0;
1198
jimfly017af00da2018-10-31 14:43:53 +00001199 inputTensorInfo = armnn::TensorInfo(4, inputShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1200 outputTensorInfo = armnn::TensorInfo(4, outputShape.data(), armnn::GetDataType<T>(), outputQScale, qOffset);
1201 kernelDesc = armnn::TensorInfo(4, kernelShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1202 biasDesc = armnn::TensorInfo(
1203 1, biasShape.data(), armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
telsoa014fcda012018-03-09 14:13:49 +00001204
1205 LayerTestResult<T, 4> ret(outputTensorInfo);
1206
1207 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1208 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
jimfly01d84216a2018-10-26 12:56:21 +01001209 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1210 biasDesc, 1028, 0.0f, 255.0f);
telsoa014fcda012018-03-09 14:13:49 +00001211
1212 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1213 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1214
1215 armnn::DepthwiseConvolution2dQueueDescriptor data;
1216 armnn::WorkloadInfo info;
1217 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1218 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1219
1220 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1221 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1222
1223 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1224 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1225 data.m_Weight = &weightsTensor;
1226 data.m_Bias = &biasTensor;
1227 data.m_Parameters.m_StrideX = strideX;
1228 data.m_Parameters.m_StrideY = strideY;
1229 data.m_Parameters.m_PadLeft = padX;
1230 data.m_Parameters.m_PadRight = padX;
1231 data.m_Parameters.m_PadTop = padY;
1232 data.m_Parameters.m_PadBottom = padY;
1233 data.m_Parameters.m_BiasEnabled = true;
jimfly017af00da2018-10-31 14:43:53 +00001234 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +00001235
1236 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1237 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1238
1239 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1240 armnn::WorkloadInfo refInfo = info;
1241 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1242 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1243
1244 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1245 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1246
1247 outputHandleRef->Allocate();
1248 inputHandleRef->Allocate();
1249
1250 inputHandle->Allocate();
1251 outputHandle->Allocate();
1252
1253 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1254 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1255
1256 workload->Execute();
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00001257
telsoa014fcda012018-03-09 14:13:49 +00001258 workloadRef->Execute();
1259
1260 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1261 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1262
1263 return ret;
1264}