blob: ce193659a1c5b8fd78a214590cc0e28e39135b67 [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
7#include <armnn/ArmNN.hpp>
8#include <armnn/Tensor.hpp>
9#include <armnn/TypesUtils.hpp>
10#include <backends/WorkloadInfo.hpp>
11
David Beckac42efd2018-09-26 17:41:13 +010012#include <test/TensorHelpers.hpp>
telsoa014fcda012018-03-09 14:13:49 +000013#include "QuantizeHelper.hpp"
14
David Beckac42efd2018-09-26 17:41:13 +010015#include <backends/CpuTensorHandle.hpp>
16#include <backends/WorkloadFactory.hpp>
telsoa014fcda012018-03-09 14:13:49 +000017
18// Mapping from input type to bias type for fully connected layers.
19// float => float, uint8_t => int32_t
20template<typename T>
21struct FullyConnectedBiasTypeForInputType;
22
23template<>
24struct FullyConnectedBiasTypeForInputType<float>
25{
26 using Type = float;
27};
28
29template<>
30struct FullyConnectedBiasTypeForInputType<uint8_t>
31{
32 using Type = int32_t;
33};
34
telsoa01c577f2c2018-08-31 09:22:23 +010035// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000036template<typename T, typename B>
37void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
38 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
39{
40 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
41 "Invalid type and parameter combination.");
42 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
43 "Invalid type and parameter combination.");
44
telsoa01c577f2c2018-08-31 09:22:23 +010045 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000046 for (uint32_t i = 0; i < bias.size(); ++i)
47 {
48 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
49 for (uint32_t y = 0; y < h; ++y)
50 {
51 for (uint32_t x = 0; x < w; ++x)
52 {
53 uint32_t offset = (i * h + y) * w + x;
54 BOOST_ASSERT(offset < v.size());
55 T& outRef = v[offset];
56 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
57 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
58 }
59 }
60 }
61}
62
telsoa014fcda012018-03-09 14:13:49 +000063template<typename T, typename B>
64LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
65 const boost::multi_array<T, 4>& input,
66 const boost::multi_array<T, 4>& kernel,
67 const boost::multi_array<B, 1>& bias,
68 const boost::multi_array<T, 4>& outputExpected,
69 float qScale,
70 int32_t qOffset,
71 uint32_t padLeft = 0,
72 uint32_t padTop = 0,
73 uint32_t padRight = 0,
74 uint32_t padBottom = 0)
75{
76 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
77 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
78 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
79 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
80
81 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
82 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
83 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
84 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
85
86 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
87 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +010088 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
89 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000090
91 bool biasEnabled = bias.size() > 0;
92
telsoa01c577f2c2018-08-31 09:22:23 +010093 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +000094 BOOST_ASSERT(inputNum == 1);
95 BOOST_ASSERT(outputNum == 1);
96
telsoa01c577f2c2018-08-31 09:22:23 +010097 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +000098 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
99
100
telsoa01c577f2c2018-08-31 09:22:23 +0100101 // Note these tensors will use two (identical) batches.
telsoa014fcda012018-03-09 14:13:49 +0000102 armnn::TensorInfo inputTensorInfo({2*inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
103 armnn::TensorInfo outputTensorInfo({2*outputNum, outputChannels, outputHeight, outputWidth},
104 armnn::GetDataType<T>());
surmeh013537c2c2018-05-18 16:31:43 +0100105 armnn::TensorInfo kernelDesc({kernelDepthMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
telsoa014fcda012018-03-09 14:13:49 +0000106 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
107
108 // Set quantization parameters if the requested type is a quantized type.
109 if(armnn::IsQuantizedType<T>())
110 {
111 inputTensorInfo.SetQuantizationScale(qScale);
112 inputTensorInfo.SetQuantizationOffset(qOffset);
113 outputTensorInfo.SetQuantizationScale(qScale);
114 outputTensorInfo.SetQuantizationOffset(qOffset);
115 kernelDesc.SetQuantizationScale(qScale);
116 kernelDesc.SetQuantizationOffset(qOffset);
117 biasDesc.SetQuantizationScale(qScale*qScale);
118 biasDesc.SetQuantizationOffset(0);
119 }
120
121 LayerTestResult<T, 4> ret(outputTensorInfo);
122
telsoa01c577f2c2018-08-31 09:22:23 +0100123 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000124 std::vector<T> inputImage;
125 inputImage.assign(input.data(), input.data() + 1*inputChannels*inputHeight*inputWidth);
126 std::vector<T> inputData;
127 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
128 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
129 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
130
131 std::vector<T> outputImage;
132 outputImage.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
133
telsoa01c577f2c2018-08-31 09:22:23 +0100134 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000135 if(biasEnabled)
136 {
137 std::vector<T> biasV;
138 biasV.assign(bias.data(), bias.data() + outputChannels);
139 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
140 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
141 outputWidth, outputHeight);
142 }
143
telsoa01c577f2c2018-08-31 09:22:23 +0100144 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000145 std::vector<T> outputData;
146 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
147 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
148
149 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
150
telsoa01c577f2c2018-08-31 09:22:23 +0100151 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000152 uint32_t strideX = 1;
153 uint32_t strideY = 1;
154
155 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
156 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
157
158 armnn::Convolution2dQueueDescriptor data;
159 armnn::WorkloadInfo info;
160 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
161 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
162
163 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
164
165 if(biasEnabled)
166 {
167 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
168 }
169
170 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
171 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
172
173 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100174 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 +0000175 data.m_Parameters.m_StrideX = strideX;
176 data.m_Parameters.m_StrideY = strideY;
177 data.m_Parameters.m_PadLeft = padLeft;
178 data.m_Parameters.m_PadRight = padRight;
179 data.m_Parameters.m_PadTop = padTop;
180 data.m_Parameters.m_PadBottom = padBottom;
181 data.m_Parameters.m_BiasEnabled = biasEnabled;
182
183 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
184 inputHandle->Allocate();
185 outputHandle->Allocate();
186
187 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
188
surmeh013537c2c2018-05-18 16:31:43 +0100189 workloadFactory.Finalize();
190 workload->Execute();
191
192 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
193
194 return ret;
195}
196
197template<typename T, typename B>
198LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
199 const boost::multi_array<T, 4>& input,
200 const boost::multi_array<T, 4>& kernel,
201 const boost::multi_array<B, 1>& bias,
202 const boost::multi_array<T, 4>& outputExpected,
203 float qScale,
204 int32_t qOffset,
205 uint32_t padLeft = 0,
206 uint32_t padTop = 0,
207 uint32_t padRight = 0,
208 uint32_t padBottom = 0,
209 uint32_t strideX = 1,
210 uint32_t strideY = 1)
211{
212 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
213 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
214 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
215 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
216 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
217 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
218 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
219 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
220 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
221 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
222 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
223 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
224
telsoa01c577f2c2018-08-31 09:22:23 +0100225 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100226 bool biasEnabled = bias.size() > 0;
227 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
228
telsoa01c577f2c2018-08-31 09:22:23 +0100229 // Creates the tensors.
surmeh013537c2c2018-05-18 16:31:43 +0100230 armnn::TensorInfo inputTensorInfo({inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
231 armnn::TensorInfo outputTensorInfo({outputNum, outputChannels, outputHeight, outputWidth},
232 armnn::GetDataType<T>());
233 armnn::TensorInfo kernelDesc({kernelChanMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
234 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
235
236 // Set quantization parameters if the requested type is a quantized type.
237 if (armnn::IsQuantizedType<T>())
238 {
239 inputTensorInfo.SetQuantizationScale(qScale);
240 inputTensorInfo.SetQuantizationOffset(qOffset);
241 outputTensorInfo.SetQuantizationScale(qScale);
242 outputTensorInfo.SetQuantizationOffset(qOffset);
243 kernelDesc.SetQuantizationScale(qScale);
244 kernelDesc.SetQuantizationOffset(qOffset);
245 biasDesc.SetQuantizationScale(qScale*qScale);
246 biasDesc.SetQuantizationOffset(0);
247 }
248
telsoa01c577f2c2018-08-31 09:22:23 +0100249 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100250 std::vector<T> inputData;
251 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
252 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
253
telsoa01c577f2c2018-08-31 09:22:23 +0100254 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100255 std::vector<T> outputData;
256 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
257 if (biasEnabled)
258 {
259 std::vector<T> biasV;
260 biasV.assign(bias.data(), bias.data() + outputChannels);
261 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
262 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
263 outputWidth, outputHeight);
264 }
265
266 LayerTestResult<T, 4> ret(outputTensorInfo);
267 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
268
269 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
270 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
271
272 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
273 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
274
275 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
276 if (biasEnabled)
277 {
278 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
279 }
280
281 armnn::DepthwiseConvolution2dQueueDescriptor data;
282 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100283 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 +0100284 data.m_Parameters.m_StrideX = strideX;
285 data.m_Parameters.m_StrideY = strideY;
286 data.m_Parameters.m_PadLeft = padLeft;
287 data.m_Parameters.m_PadRight = padRight;
288 data.m_Parameters.m_PadTop = padTop;
289 data.m_Parameters.m_PadBottom = padBottom;
290 data.m_Parameters.m_BiasEnabled = biasEnabled;
291
292 armnn::WorkloadInfo info;
293 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
294 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
295
296 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
297 inputHandle->Allocate();
298 outputHandle->Allocate();
299
300 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
301
302 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000303 workload->Execute();
304
305 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
306
307 return ret;
308}
309
310template<typename T, typename B>
311LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
312 float qScale,
313 int32_t qOffset,
314 bool biasEnabled)
315{
316 unsigned int inputHeight = 3;
317 unsigned int inputWidth = 3;
318 unsigned int inputChannels = 2;
319 unsigned int inputNum = 1;
320
321 unsigned int kernelHeight = 3;
322 unsigned int kernelWidth = 3;
323 unsigned int kernelChannels = inputChannels;
324
325 unsigned int outputHeight = 1;
326 unsigned int outputWidth = 1;
327 unsigned int outputChannels = kernelChannels;
328 unsigned int outputNum = inputNum;
329
330 armnn::TensorInfo inputTensorInfo({ inputNum, inputChannels, inputHeight, inputWidth }, armnn::GetDataType<T>());
331 armnn::TensorInfo outputTensorInfo({ outputNum, outputChannels, outputHeight, outputWidth },
332 armnn::GetDataType<T>());
333 armnn::TensorInfo kernelDesc({ 1, outputChannels, kernelHeight, kernelWidth }, armnn::GetDataType<T>());
334 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
335
336 // Set quantization parameters if the requested type is a quantized type.
337 if(armnn::IsQuantizedType<T>())
338 {
339 inputTensorInfo.SetQuantizationScale(qScale);
340 inputTensorInfo.SetQuantizationOffset(qOffset);
341 outputTensorInfo.SetQuantizationScale(qScale);
342 outputTensorInfo.SetQuantizationOffset(qOffset);
343 kernelDesc.SetQuantizationScale(qScale);
344 kernelDesc.SetQuantizationOffset(qOffset);
345 biasDesc.SetQuantizationScale(qScale*qScale);
346 biasDesc.SetQuantizationOffset(0);
347 }
348
349 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
350 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
351 1.f, 2.f, 1.f,
352 2.f, 1.f, 2.f,
353 1.f, 2.f, 1.f,
354
355 1.f, 2.f, 1.f,
356 2.f, 1.f, 2.f,
357 1.f, 2.f, 1.f,
358 })));
359
360 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
361 {0, 2}));
362 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
363
364 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
365 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
366 1.f, 0.f, 1.f,
367 0.f, 0.f, 0.f,
368 -1.f, 0.f, -1.f,
369
370 1.f, 0.f, 1.f,
371 0.f, 0.f, 0.f,
372 -1.f, 0.f, -1.f,
373 })));
374
telsoa01c577f2c2018-08-31 09:22:23 +0100375 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000376 std::vector<T> outputImage(
377 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
378 outputTensorInfo.GetQuantizationOffset(),
379 {0.f, 0.f})
380 );
381
telsoa01c577f2c2018-08-31 09:22:23 +0100382 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000383 if(biasEnabled)
384 {
385 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
386 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
387 outputWidth, outputHeight);
388 }
389
390 LayerTestResult<T, 4> ret(outputTensorInfo);
391 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
392
393 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
394 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
395
396 armnn::DepthwiseConvolution2dQueueDescriptor data;
397 armnn::WorkloadInfo info;
398 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
399 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
400
401 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
402 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
403
404 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
405 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
406
407 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100408 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000409 data.m_Parameters.m_StrideX = 1;
410 data.m_Parameters.m_StrideY = 1;
411 data.m_Parameters.m_PadLeft = 0;
412 data.m_Parameters.m_PadRight = 0;
413 data.m_Parameters.m_PadTop = 0;
414 data.m_Parameters.m_PadBottom = 0;
415 data.m_Parameters.m_BiasEnabled = biasEnabled;
416
417 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
418 inputHandle->Allocate();
419 outputHandle->Allocate();
420
421 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
422
surmeh013537c2c2018-05-18 16:31:43 +0100423 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000424 workload->Execute();
425
426 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
427
428 return ret;
429}
430
431template<typename T, typename B>
432LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
433 float qScale,
434 int32_t qOffset,
435 bool biasEnabled)
436{
437 unsigned int depthMultiplier = 2;
438
439 unsigned int inputHeight = 8;
440 unsigned int inputWidth = 16;
441 unsigned int inputChannels = 2;
442 unsigned int inputBatchSize = 1;
443
444 unsigned int kernelHeight = 5;
445 unsigned int kernelWidth = 3;
446
447 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
448 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
449 unsigned int outputChannels = inputChannels * depthMultiplier;
450 unsigned int outputBatchSize = inputBatchSize;
451
452 armnn::TensorInfo inputTensorInfo({inputBatchSize, inputChannels, inputHeight, inputWidth},
453 armnn::GetDataType<T>());
454 armnn::TensorInfo outputTensorInfo({outputBatchSize, outputChannels, outputHeight, outputWidth},
455 armnn::GetDataType<T>());
456 armnn::TensorInfo kernelDesc({depthMultiplier, inputChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
457 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
458
459 // Set quantization parameters if the requested type is a quantized type.
460 if(armnn::IsQuantizedType<T>())
461 {
462 inputTensorInfo.SetQuantizationScale(qScale);
463 inputTensorInfo.SetQuantizationOffset(qOffset);
464 outputTensorInfo.SetQuantizationScale(qScale);
465 outputTensorInfo.SetQuantizationOffset(qOffset);
466 kernelDesc.SetQuantizationScale(qScale);
467 kernelDesc.SetQuantizationOffset(qOffset);
468 biasDesc.SetQuantizationScale(qScale*qScale);
469 biasDesc.SetQuantizationOffset(0);
470 }
471
472 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
473 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
474 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,
475 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,
476 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,
477 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,
478 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,
479 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,
480 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,
481 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,
482 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
483 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
484 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
485 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
486 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
487 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
488 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
489 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
490 })));
491
492 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
493 {0, 2, 1, -1}));
494 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
495
496 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
497 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
498 1, 1, 1,
499 1, -1, 1,
500 1, 1, 1,
501 1, 1, 1,
502 1, 1, 1,
503
504 2, 2, 2,
505 2, 2, 2,
506 2, 2, 2,
507 2, 2, 2,
508 2, 2, 2,
509
510 0, 0, 0,
511 0, -1, 0,
512 0, 0, 0,
513 0, 0, 0,
514 0, 0, 0,
515
516 0, 0, 0,
517 0, 0, 0,
518 0, 1, 0,
519 0, 0, 0,
520 0, 0, 0
521 })));
522
telsoa01c577f2c2018-08-31 09:22:23 +0100523 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000524 std::vector<T> outputImage = std::vector<T>(
525 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
526 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
527 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
528 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
529 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
530 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
531 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
532
533 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
534 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
535 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
536 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
537 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
538 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
539
540 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
541 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
542 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
543 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
544 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
545 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
546
547 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
548 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
549 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
550 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
551 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
552 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
553 }));
554
telsoa01c577f2c2018-08-31 09:22:23 +0100555 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000556 if(biasEnabled)
557 {
558 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
559 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
560 outputWidth, outputHeight);
561 }
562
563 LayerTestResult<T, 4> ret(outputTensorInfo);
564 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
565
566 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
567 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
568
569 armnn::DepthwiseConvolution2dQueueDescriptor data;
570 armnn::WorkloadInfo info;
571 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
572 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
573
574 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
575 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
576
577 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
578 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
579
580 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100581 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000582 data.m_Parameters.m_StrideX = 2;
583 data.m_Parameters.m_StrideY = 1;
584 data.m_Parameters.m_PadLeft = 0;
585 data.m_Parameters.m_PadRight = 0;
586 data.m_Parameters.m_PadTop = 1;
587 data.m_Parameters.m_PadBottom = 1;
588 data.m_Parameters.m_BiasEnabled = biasEnabled;
589
590 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
591 inputHandle->Allocate();
592 outputHandle->Allocate();
593
594 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
595
surmeh013537c2c2018-05-18 16:31:43 +0100596 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000597 workload->Execute();
598
599 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
600
601 return ret;
602}
603
telsoa014fcda012018-03-09 14:13:49 +0000604template<typename T>
605LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
606 float qScale,
607 int32_t qOffset,
608 bool biasEnabled)
609{
610 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
611
telsoa01c577f2c2018-08-31 09:22:23 +0100612 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000613 // 2D convolution with the final dimension set to 1.
614 // I don't anticipate this being particularly slow, given that convolution is implemented
615 // as a matrix multiplication, at which point dimension doesn't matter.
616
617 unsigned int batchSize = 1;
618 unsigned int inputChannels = 2;
619 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100620 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000621 unsigned int kernelSize = 3;
622 unsigned int padSize = 2;
623 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100624 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000625
626 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
627 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
628 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
629 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
630
631 // Set quantization parameters if the requested type is a quantized type.
632 if(armnn::IsQuantizedType<T>())
633 {
634 inputInfo.SetQuantizationScale(qScale);
635 inputInfo.SetQuantizationOffset(qOffset);
636 outputInfo.SetQuantizationScale(qScale);
637 outputInfo.SetQuantizationOffset(qOffset);
638 kernelInfo.SetQuantizationScale(qScale);
639 kernelInfo.SetQuantizationOffset(qOffset);
640 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
641 biasInfo.SetQuantizationOffset(0);
642 }
643
644 std::vector<T> inputData(
645 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
646 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
647 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
648 }));
649
650 std::vector<T> kernelData(
651 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
652 1.0f, 0.0f, 0.0f,
653 0.0f, 2.0f, -1.5f,
654
655 0.0f, 0.0f, 0.0f,
656 0.2f, 0.2f, 0.2f,
657
658 0.5f, 0.0f, 0.5f,
659 0.0f, -1.0f, 0.0f
660 }));
661
662 std::vector<B> biasData(
663 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
664 1.0f, 0.0f, 0.0f
665 }));
666
667 std::vector<T> outputData(
668 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
669 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,
670 -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,
671 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
672 }));
673
telsoa01c577f2c2018-08-31 09:22:23 +0100674 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000675 if(biasEnabled)
676 {
677 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
678 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
679 1, outputSize);
680 }
681
682 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
683 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
684
685 armnn::Convolution2dQueueDescriptor data;
686 armnn::WorkloadInfo info;
687 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
688 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
689
690 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
691 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
692
693 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
694 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
695
696 data.m_Weight = &weightsTensor;
697 data.m_Bias = &biasTensor;
698 data.m_Parameters.m_StrideX = 1;
699 data.m_Parameters.m_StrideY = stride;
700 data.m_Parameters.m_PadLeft = 0;
701 data.m_Parameters.m_PadRight = 0;
702 data.m_Parameters.m_PadTop = padSize;
703 data.m_Parameters.m_PadBottom = padSize;
704 data.m_Parameters.m_BiasEnabled = biasEnabled;
705
706 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
707 inputHandle->Allocate();
708 outputHandle->Allocate();
709
710 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
711
surmeh013537c2c2018-05-18 16:31:43 +0100712 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000713 workload->Execute();
714
telsoa01c577f2c2018-08-31 09:22:23 +0100715 // Output
telsoa014fcda012018-03-09 14:13:49 +0000716 LayerTestResult<T,4> ret(outputInfo);
717 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
718 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
719 return ret;
720}
721
722
723
724template<typename T>
725LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
726 armnn::IWorkloadFactory& refWorkloadFactory)
727{
728 unsigned int inputHeight = 8;
729 unsigned int inputWidth = 16;
730 unsigned int inputChannels = 3;
731 unsigned int inputNum = 5;
732
733 unsigned int kernelHeight = 3;
734 unsigned int kernelWidth = 3;
735
736 unsigned int strideX = 2;
737 unsigned int strideY = 3;
738 unsigned int padX = 1;
739 unsigned int padY = 1;
740
741 unsigned int outputNum = inputNum;
742 unsigned int outputChannels = 2;
743 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
744 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
745
746 armnn::TensorInfo inputTensorInfo;
747 armnn::TensorInfo outputTensorInfo;
748 armnn::TensorInfo kernelDesc;
749 armnn::TensorInfo biasDesc;
750
751 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
752 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
753 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
754 unsigned int biasShape[] = {outputChannels};
755
756 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
757 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
758 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
759 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
760
761 LayerTestResult<T,4> ret(outputTensorInfo);
762
763 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
764 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
765 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
766
767 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
768 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
769
770 armnn::Convolution2dQueueDescriptor data;
771 armnn::WorkloadInfo info;
772 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
773 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
774
775 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
776 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
777
778 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
779 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
780 data.m_Weight = &weightsTensor;
781 data.m_Bias = &biasTensor;
782 data.m_Parameters.m_StrideX = strideX;
783 data.m_Parameters.m_StrideY = strideY;
784 data.m_Parameters.m_PadLeft = padX;
785 data.m_Parameters.m_PadRight = padX;
786 data.m_Parameters.m_PadTop = padY;
787 data.m_Parameters.m_PadBottom = padY;
788 data.m_Parameters.m_BiasEnabled = true;
789
790 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
791 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
792
793 armnn::Convolution2dQueueDescriptor refData = data;
794 armnn::WorkloadInfo refInfo = info;
795 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
796 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
797
798 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
799 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
800
801 outputHandleRef->Allocate();
802 inputHandleRef->Allocate();
803
804 inputHandle->Allocate();
805 outputHandle->Allocate();
806
807 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
808 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
809
surmeh013537c2c2018-05-18 16:31:43 +0100810 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000811 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +0100812 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000813 workloadRef->Execute();
814
815 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
816 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
817
818 return ret;
819}
820
821template<typename T>
822LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
823 armnn::IWorkloadFactory& refWorkloadFactory)
824{
825 unsigned int inputHeight = 8;
826 unsigned int inputWidth = 16;
827 unsigned int inputChannels = 3;
828 unsigned int inputNum = 5;
829
830 unsigned int kernelHeight = 3;
831 unsigned int kernelWidth = 3;
832 unsigned int channelMultiplier = 1;
833
834 unsigned int strideX = 2;
835 unsigned int strideY = 3;
836 unsigned int padX = 1;
837 unsigned int padY = 1;
838
839 unsigned int outputNum = inputNum;
840 unsigned int outputChannels = inputChannels * channelMultiplier;
841 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
842 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
843
844 armnn::TensorInfo inputTensorInfo;
845 armnn::TensorInfo outputTensorInfo;
846 armnn::TensorInfo kernelDesc;
847 armnn::TensorInfo biasDesc;
848
849 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
850 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
851 unsigned int kernelShape[] = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
852 unsigned int biasShape[] = { outputChannels };
853
854 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
855 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
856 int32_t qOffset = 0;
857
858 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
859 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>(), outputQScale, qOffset);
860 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
861 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
862
863 LayerTestResult<T, 4> ret(outputTensorInfo);
864
865 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
866 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
867 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(biasDesc, 1028, 0.0f, 255.0f);
868
869 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
870 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
871
872 armnn::DepthwiseConvolution2dQueueDescriptor data;
873 armnn::WorkloadInfo info;
874 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
875 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
876
877 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
878 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
879
880 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
881 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
882 data.m_Weight = &weightsTensor;
883 data.m_Bias = &biasTensor;
884 data.m_Parameters.m_StrideX = strideX;
885 data.m_Parameters.m_StrideY = strideY;
886 data.m_Parameters.m_PadLeft = padX;
887 data.m_Parameters.m_PadRight = padX;
888 data.m_Parameters.m_PadTop = padY;
889 data.m_Parameters.m_PadBottom = padY;
890 data.m_Parameters.m_BiasEnabled = true;
891
892 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
893 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
894
895 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
896 armnn::WorkloadInfo refInfo = info;
897 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
898 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
899
900 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
901 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
902
903 outputHandleRef->Allocate();
904 inputHandleRef->Allocate();
905
906 inputHandle->Allocate();
907 outputHandle->Allocate();
908
909 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
910 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
911
surmeh013537c2c2018-05-18 16:31:43 +0100912 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000913 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +0100914 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000915 workloadRef->Execute();
916
917 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
918 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
919
920 return ret;
921}