blob: 3bbd5d67700caf5d41de9886684a19d33d413986 [file] [log] [blame]
Aron Virginas-Tar735a4502019-06-26 15:02:47 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5#pragma once
6
7#include "QuantizeHelper.hpp"
8
9#include <armnn/ArmNN.hpp>
10
11#include <ResolveType.hpp>
12
13#include <backendsCommon/CpuTensorHandle.hpp>
14#include <backendsCommon/test/CommonTestUtils.hpp>
15#include <backendsCommon/test/TensorCopyUtils.hpp>
16#include <backendsCommon/test/WorkloadTestUtils.hpp>
17
18#include <reference/RefWorkloadFactory.hpp>
19
20#include <boost/test/unit_test.hpp>
21
22#include <string>
23#include <utility>
24#include <vector>
25
26namespace
27{
28
29template<typename T>
30using TensorData = std::pair<armnn::TensorInfo, std::vector<T>>;
31
32template<typename T>
33void VerifyInputTensorData(const TensorData<T>& data, const std::string& tensorName)
34{
35 if (data.first.GetNumElements() > data.second.size())
36 {
37 throw armnn::InvalidArgumentException("Size of data too small for " + tensorName + ": expected " +
38 std::to_string(data.first.GetNumElements()) + "but got " + std::to_string(data.second.size()));
39 }
40}
41
42template<typename T, typename BT>
43void TransposeConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
44 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
45 const armnn::TransposeConvolution2dDescriptor& descriptor,
46 const TensorData<T>& input,
47 TensorData<T>& output,
48 const TensorData<T>& weights,
49 const armnn::Optional<TensorData<BT>>& biases)
50{
51 using namespace armnn;
52
53 VerifyInputTensorData(input, "input");
54 VerifyInputTensorData(weights, "biases");
55
56 if (descriptor.m_BiasEnabled)
57 {
58 if (!biases.has_value())
59 {
60 throw InvalidArgumentException("Bias enabled but no bias data provided");
61 }
62 VerifyInputTensorData(biases.value(), "biases");
63 }
64
65 // set up weights
66 ScopedCpuTensorHandle weightsTensor(weights.first);
67
68 TransposeConvolution2dQueueDescriptor queueDescriptor;
69 queueDescriptor.m_Parameters = descriptor;
70 queueDescriptor.m_Weight = &weightsTensor;
71
72 AllocateAndCopyDataToITensorHandle(&weightsTensor, weights.second.data());
73
74 std::unique_ptr<ScopedCpuTensorHandle> biasesTensor;
75 if (descriptor.m_BiasEnabled)
76 {
77 // set up biases
78 biasesTensor = std::make_unique<ScopedCpuTensorHandle>(biases.value().first);
79 queueDescriptor.m_Bias = biasesTensor.get();
80
81 AllocateAndCopyDataToITensorHandle(biasesTensor.get(), biases.value().second.data());
82 }
83
84 // set up input and output handles
85 std::unique_ptr<ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(input.first);
86 std::unique_ptr<ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(output.first);
87
88 // set up workload
89 armnn::WorkloadInfo workloadInfo;
90 AddInputToWorkload(queueDescriptor, workloadInfo, input.first, inputHandle.get());
91 AddOutputToWorkload(queueDescriptor, workloadInfo, output.first, outputHandle.get());
92
93 std::unique_ptr<armnn::IWorkload> workload =
94 workloadFactory.CreateTransposeConvolution2d(queueDescriptor, workloadInfo);
95
96 inputHandle->Allocate();
97 outputHandle->Allocate();
98
99 CopyDataToITensorHandle(inputHandle.get(), input.second.data());
100
101 ExecuteWorkload(*workload, nullptr);
102
103 // copy output
104 output.second = std::vector<T>(output.first.GetNumElements(), 0.0f);
105 CopyDataFromITensorHandle(output.second.data(), outputHandle.get());
106}
107
108template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
109LayerTestResult<T, 4> TransposeConvolution2dTestImpl(
110 armnn::IWorkloadFactory& workloadFactory,
111 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
112 const armnn::TransposeConvolution2dDescriptor& descriptor,
113 armnn::TensorInfo& inputInfo,
114 const std::vector<float>& inputData,
115 armnn::TensorInfo& outputInfo,
116 const std::vector<float>& expectedOutputData,
117 armnn::TensorInfo& weightsInfo,
118 const std::vector<float>& weightsData,
119 armnn::TensorInfo& biasesInfo,
120 const std::vector<float>& biasesData)
121{
122 using namespace armnn;
123
124 // set up quantization parameters
125 if (armnn::IsQuantizedType<T>())
126 {
127 constexpr float qScale = 0.25f;
128 constexpr int32_t qOffset = 50;
129
130 inputInfo.SetQuantizationScale(qScale);
131 inputInfo.SetQuantizationOffset(qOffset);
132
133 outputInfo.SetQuantizationScale(qScale);
134 outputInfo.SetQuantizationOffset(qOffset);
135
136 weightsInfo.SetQuantizationScale(qScale);
137 weightsInfo.SetQuantizationOffset(qOffset);
138
139 biasesInfo.SetQuantizationScale(qScale * qScale);
140 biasesInfo.SetQuantizationOffset(0);
141 }
142
143 // set up input
144 TensorData<T> input =
145 {
146 inputInfo,
147 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), inputData)
148 };
149
150 // set up weights
151 TensorData<T> weights =
152 {
153 weightsInfo,
154 QuantizedVector<T>(weightsInfo.GetQuantizationScale(), weightsInfo.GetQuantizationOffset(), weightsData)
155 };
156
157 // set up biases
158 using BT = armnn::ResolveType<ArmnnBType>;
159 Optional<TensorData<BT>> optionalBiases;
160 if (descriptor.m_BiasEnabled)
161 {
162 TensorData<BT> biases =
163 {
164 biasesInfo,
165 QuantizedVector<BT>(biasesInfo.GetQuantizationScale(), biasesInfo.GetQuantizationOffset(), biasesData)
166 };
167
168 optionalBiases = Optional<TensorData<BT>>(biases);
169 }
170
171 // set up output
172 TensorData<T> output = { outputInfo, {} };
173
174 // execute test
175 TransposeConvolution2dTestImpl(workloadFactory,
176 memoryManager,
177 descriptor,
178 input,
179 output,
180 weights,
181 optionalBiases);
182
183 // construct result object
184 LayerTestResult<T, 4> testResult(outputInfo);
185 testResult.output = MakeTensor<T, 4>(outputInfo, output.second);
186 testResult.outputExpected = MakeTensor<T, 4>(outputInfo,
187 QuantizedVector<T>(outputInfo.GetQuantizationScale(),
188 outputInfo.GetQuantizationOffset(),
189 expectedOutputData));
190
191 return testResult;
192}
193
194} // anonymous namespace
195
196template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
197LayerTestResult<T, 4> SimpleTransposeConvolution2dTestImpl(
198 armnn::IWorkloadFactory& workloadFactory,
199 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
200 bool biasEnabled,
201 const armnn::DataLayout layout)
202{
203 using namespace armnn;
204
205 constexpr unsigned int batches = 1u;
206 constexpr unsigned int channels = 1u;
207
208 constexpr unsigned int wInput = 3u;
209 constexpr unsigned int hInput = wInput;
210
211 constexpr unsigned int wOutput = 5u;
212 constexpr unsigned int hOutput = wOutput;
213
214 constexpr unsigned int wWeights = 3u;
215 constexpr unsigned int hWeights = wWeights;
216
217 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
218 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
219 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
220
221 TensorInfo inputInfo(inputShape, ArmnnType);
222 TensorInfo outputInfo(outputShape, ArmnnType);
223 TensorInfo weightsInfo(weightsShape, ArmnnType);
224 TensorInfo biasesInfo({ channels }, ArmnnBType);
225
226 std::vector<float> inputData =
227 {
228 1.f, 1.f, 1.f,
229 1.f, 1.f, 1.f,
230 1.f, 1.f, 1.f
231 };
232
233 std::vector<float> weightsData =
234 {
235 1.f, 2.f, 3.f,
236 4.f, 5.f, 6.f,
237 7.f, 8.f, 9.f
238 };
239
240 std::vector<float> biasesData = { 1.f };
241
242 std::vector<float> expectedOutputData =
243 {
244 1.f, 3.f, 6.f, 5.f, 3.f,
245 5.f, 12.f, 21.f, 16.f, 9.f,
246 12.f, 27.f, 45.f, 33.f, 18.f,
247 11.f, 24.f, 39.f, 28.f, 15.f,
248 7.f, 15.f, 24.f, 17.f, 9.f
249 };
250
251 if (biasEnabled)
252 {
253 // apply bias to expected output data
254 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
255 [&](float f) -> float { return f + biasesData[0]; });
256 }
257
258 TransposeConvolution2dDescriptor descriptor;
259 descriptor.m_StrideX = 1;
260 descriptor.m_StrideY = 1;
261 descriptor.m_BiasEnabled = biasEnabled;
262 descriptor.m_DataLayout = layout;
263
264 // swizzle data if needed
265 if (layout == armnn::DataLayout::NHWC)
266 {
267 constexpr size_t dataTypeSize = sizeof(float);
268 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
269
270 std::vector<float> tmp(inputData.size());
271 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
272 inputData = tmp;
273
274 tmp.resize(weightsData.size());
275 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
276 weightsData = tmp;
277
278 tmp.resize(expectedOutputData.size());
279 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
280 expectedOutputData = tmp;
281 }
282
283 return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
284 memoryManager,
285 descriptor,
286 inputInfo,
287 inputData,
288 outputInfo,
289 expectedOutputData,
290 weightsInfo,
291 weightsData,
292 biasesInfo,
293 biasesData);
294}
295
296template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
297LayerTestResult<T, 4> PaddedTransposeConvolution2dTestImpl(
298 armnn::IWorkloadFactory& workloadFactory,
299 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
300 bool biasEnabled,
301 const armnn::DataLayout layout)
302{
303 using namespace armnn;
304
305 constexpr unsigned int batches = 1u;
306 constexpr unsigned int channels = 1u;
307
308 constexpr unsigned int wInput = 4u;
309 constexpr unsigned int hInput = wInput;
310
311 constexpr unsigned int wOutput = 2u;
312 constexpr unsigned int hOutput = wOutput;
313
314 constexpr unsigned int wWeights = 3u;
315 constexpr unsigned int hWeights = wWeights;
316
317 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
318 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
319 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
320
321 TensorInfo inputInfo(inputShape, ArmnnType);
322 TensorInfo outputInfo(outputShape, ArmnnType);
323 TensorInfo weightsInfo(weightsShape, ArmnnType);
324 TensorInfo biasesInfo({ channels }, ArmnnBType);
325
326 std::vector<float> inputData =
327 {
328 1.f, 3.f, 2.f, 1.f,
329 1.f, 3.f, 3.f, 1.f,
330 2.f, 1.f, 1.f, 3.f,
331 3.f, 2.f, 3.f, 3.f
332 };
333
334 std::vector<float> weightsData =
335 {
336 1.f, 2.f, 3.f,
337 0.f, 1.f, 0.f,
338 2.f, 1.f, 2.f
339 };
340
341 std::vector<float> biasesData = { 1.f };
342
343 std::vector<float> expectedOutputData =
344 {
345 21.f, 21.f,
346 28.f, 27.f
347 };
348
349 if (biasEnabled)
350 {
351 // apply bias to expected output data
352 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
353 [&](float f) -> float { return f + biasesData[0]; });
354 }
355
356 TransposeConvolution2dDescriptor descriptor;
357 descriptor.m_PadLeft = 2;
358 descriptor.m_PadRight = 2;
359 descriptor.m_PadTop = 2;
360 descriptor.m_PadBottom = 2;
361 descriptor.m_StrideX = 1;
362 descriptor.m_StrideY = 1;
363 descriptor.m_BiasEnabled = biasEnabled;
364 descriptor.m_DataLayout = layout;
365
366 // swizzle data if needed
367 if (layout == armnn::DataLayout::NHWC)
368 {
369 constexpr size_t dataTypeSize = sizeof(float);
370 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
371
372 std::vector<float> tmp(inputData.size());
373 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
374 inputData = tmp;
375
376 tmp.resize(weightsData.size());
377 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
378 weightsData = tmp;
379
380 tmp.resize(expectedOutputData.size());
381 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
382 expectedOutputData = tmp;
383 }
384
385 return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
386 memoryManager,
387 descriptor,
388 inputInfo,
389 inputData,
390 outputInfo,
391 expectedOutputData,
392 weightsInfo,
393 weightsData,
394 biasesInfo,
395 biasesData);
396}
397
398 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
399 LayerTestResult<T, 4> StridedTransposeConvolution2dTestImpl(
400 armnn::IWorkloadFactory& workloadFactory,
401 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
402 bool biasEnabled,
403 const armnn::DataLayout layout)
404{
405 using namespace armnn;
406
407 constexpr unsigned int batches = 1u;
408 constexpr unsigned int channels = 1u;
409
410 constexpr unsigned int wInput = 3u;
411 constexpr unsigned int hInput = wInput;
412
413 constexpr unsigned int wOutput = 7u;
414 constexpr unsigned int hOutput = wOutput;
415
416 constexpr unsigned int wWeights = 3u;
417 constexpr unsigned int hWeights = wWeights;
418
419 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
420 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
421 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
422
423 TensorInfo inputInfo(inputShape, ArmnnType);
424 TensorInfo outputInfo(outputShape, ArmnnType);
425 TensorInfo weightsInfo(weightsShape, ArmnnType);
426 TensorInfo biasesInfo({ channels }, ArmnnBType);
427
428 std::vector<float> inputData =
429 {
430 1.f, 1.f, 1.f,
431 1.f, 1.f, 1.f,
432 1.f, 1.f, 1.f
433 };
434
435 std::vector<float> weightsData =
436 {
437 1.f, 2.f, 3.f,
438 4.f, 5.f, 6.f,
439 7.f, 8.f, 9.f
440 };
441
442 std::vector<float> biasesData = { 1.f };
443
444 std::vector<float> expectedOutputData =
445 {
446 1.f, 2.f, 4.f, 2.f, 4.f, 2.f, 3.f,
447 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f,
448 8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f,
449 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f,
450 8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f,
451 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f,
452 7.f, 8.f, 16.f, 8.f, 16.f, 8.f, 9.f
453 };
454
455 if (biasEnabled)
456 {
457 // apply bias to expected output data
458 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
459 [&](float f) -> float { return f + biasesData[0]; });
460 }
461
462 TransposeConvolution2dDescriptor descriptor;
463 descriptor.m_StrideX = 2;
464 descriptor.m_StrideY = 2;
465 descriptor.m_BiasEnabled = biasEnabled;
466 descriptor.m_DataLayout = layout;
467
468 // swizzle data if needed
469 if (layout == armnn::DataLayout::NHWC)
470 {
471 constexpr size_t dataTypeSize = sizeof(float);
472 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
473
474 std::vector<float> tmp(inputData.size());
475 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
476 inputData = tmp;
477
478 tmp.resize(weightsData.size());
479 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
480 weightsData = tmp;
481
482 tmp.resize(expectedOutputData.size());
483 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
484 expectedOutputData = tmp;
485 }
486
487 return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
488 memoryManager,
489 descriptor,
490 inputInfo,
491 inputData,
492 outputInfo,
493 expectedOutputData,
494 weightsInfo,
495 weightsData,
496 biasesInfo,
497 biasesData);
498}