3bbd5d67700caf5d41de9886684a19d33d413986
[platform/upstream/armnn.git] / src / backends / backendsCommon / test / TransposeConvolution2dTestImpl.hpp
1 //
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
26 namespace
27 {
28
29 template<typename T>
30 using TensorData = std::pair<armnn::TensorInfo, std::vector<T>>;
31
32 template<typename T>
33 void 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
42 template<typename T, typename BT>
43 void 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
108 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
109 LayerTestResult<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
196 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
197 LayerTestResult<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
296 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
297 LayerTestResult<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 }