xref: /aosp_15_r20/external/armnn/src/backends/backendsCommon/test/layerTests/SplitterTestImpl.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2017, 2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "SplitterTestImpl.hpp"
7 
8 #include <armnnUtils/QuantizeHelper.hpp>
9 #include <ResolveType.hpp>
10 
11 
12 #include <armnnTestUtils/TensorCopyUtils.hpp>
13 #include <armnnTestUtils/WorkloadTestUtils.hpp>
14 
15 #include <armnnTestUtils/TensorHelpers.hpp>
16 
17 namespace
18 {
19 
20 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
SplitterTestCommon(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory,float qScale=1.0f,int32_t qOffset=0)21 std::vector<LayerTestResult<T,3>> SplitterTestCommon(
22     armnn::IWorkloadFactory& workloadFactory,
23     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
24     const armnn::ITensorHandleFactory& tensorHandleFactory,
25     float qScale = 1.0f,
26     int32_t qOffset = 0)
27 {
28     IgnoreUnused(memoryManager);
29     unsigned int inputWidth = 5;
30     unsigned int inputHeight = 6;
31     unsigned int inputChannels = 3;
32 
33     // NOTE: Compute Library imposes a restriction that the x and y dimension (input height and width)
34     //       cannot be split.
35     //       For the reasons for this, see first comment on https://jira.arm.com/browse/IVGCVSW-1239
36     //
37     // This test has therefore been recast to split the channels, then split the resulting subtensor.
38 
39     // To take channel 0 of original output
40     // and channel 0 and channel 1 of the split subtensor.
41     unsigned int outputWidth1 = inputWidth;
42     unsigned int outputHeight1 = inputHeight;
43     unsigned int outputChannels1 = 1;
44 
45     // To take channel 1 and 2 of the original output.
46     unsigned int outputWidth2 = inputWidth;
47     unsigned int outputHeight2 = inputHeight;
48     unsigned int outputChannels2 = 2;
49 
50     // Define the tensor descriptors.
51     armnn::TensorInfo inputTensorInfo({ inputChannels, inputHeight, inputWidth }, ArmnnType, qScale, qOffset);
52 
53     // Outputs of the original split.
54     armnn::TensorInfo outputTensorInfo1({ outputChannels1, outputHeight1, outputWidth1 }, ArmnnType, qScale, qOffset);
55     armnn::TensorInfo outputTensorInfo2({ outputChannels2, outputHeight2, outputWidth2 }, ArmnnType, qScale, qOffset);
56 
57     // Outputs of the subsequent subtensor split.
58     armnn::TensorInfo outputTensorInfo3({ outputChannels1, outputHeight1, outputWidth1 }, ArmnnType, qScale, qOffset);
59     armnn::TensorInfo outputTensorInfo4({ outputChannels1, outputHeight1, outputWidth1 }, ArmnnType, qScale, qOffset);
60 
61     // Set quantization parameters if the requested type is a quantized type.
62     // The quantization doesn't really matter as the splitter operator doesn't dequantize/quantize.
63     if(armnn::IsQuantizedType<T>())
64     {
65         inputTensorInfo.SetQuantizationScale(qScale);
66         inputTensorInfo.SetQuantizationOffset(qOffset);
67         outputTensorInfo1.SetQuantizationScale(qScale);
68         outputTensorInfo1.SetQuantizationOffset(qOffset);
69         outputTensorInfo2.SetQuantizationScale(qScale);
70         outputTensorInfo2.SetQuantizationOffset(qOffset);
71         outputTensorInfo3.SetQuantizationScale(qScale);
72         outputTensorInfo3.SetQuantizationOffset(qOffset);
73         outputTensorInfo4.SetQuantizationScale(qScale);
74         outputTensorInfo4.SetQuantizationOffset(qOffset);
75     }
76 
77     auto input = armnnUtils::QuantizedVector<T>(
78         {
79             1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
80             6.0f, 7.0f, 8.0f, 9.0f, 10.0f,
81             11.0f, 12.0f, 13.0f, 14.0f, 15.0f,
82             16.0f, 17.0f, 18.0f, 19.0f, 20.0f,
83             21.0f, 22.0f, 23.0f, 24.0f, 25.0f,
84             26.0f, 27.0f, 28.0f, 29.0f, 30.0f,
85 
86             31.0f, 32.0f, 33.0f, 34.0f, 35.0f,
87             36.0f, 37.0f, 38.0f, 39.0f, 40.0f,
88             41.0f, 42.0f, 43.0f, 44.0f, 45.0f,
89             46.0f, 47.0f, 48.0f, 49.0f, 50.0f,
90             51.0f, 52.0f, 53.0f, 54.0f, 55.0f,
91             56.0f, 57.0f, 58.0f, 59.0f, 60.0f,
92 
93             61.0f, 62.0f, 63.0f, 64.0f, 65.0f,
94             66.0f, 67.0f, 68.0f, 69.0f, 70.0f,
95             71.0f, 72.0f, 73.0f, 74.0f, 75.0f,
96             76.0f, 77.0f, 78.0f, 79.0f, 80.0f,
97             81.0f, 82.0f, 83.0f, 84.0f, 85.0f,
98             86.0f, 87.0f, 88.0f, 89.0f, 90.0f,
99         },
100         qScale, qOffset);
101 
102     // Channel 0 of the original input.
103     auto expectedData1 = armnnUtils::QuantizedVector<T>(
104         {
105             1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
106             6.0f, 7.0f, 8.0f, 9.0f, 10.0f,
107             11.0f, 12.0f, 13.0f, 14.0f, 15.0f,
108             16.0f, 17.0f, 18.0f, 19.0f, 20.0f,
109             21.0f, 22.0f, 23.0f, 24.0f, 25.0f,
110             26.0f, 27.0f, 28.0f, 29.0f, 30.0f,
111         },
112         qScale, qOffset);
113 
114     // Channel 1 & 2 of the original input.
115     auto expectedData2 = armnnUtils::QuantizedVector<T>(
116         {
117             31.0f, 32.0f, 33.0f, 34.0f, 35.0f,
118             36.0f, 37.0f, 38.0f, 39.0f, 40.0f,
119             41.0f, 42.0f, 43.0f, 44.0f, 45.0f,
120             46.0f, 47.0f, 48.0f, 49.0f, 50.0f,
121             51.0f, 52.0f, 53.0f, 54.0f, 55.0f,
122             56.0f, 57.0f, 58.0f, 59.0f, 60.0f,
123 
124             61.0f, 62.0f, 63.0f, 64.0f, 65.0f,
125             66.0f, 67.0f, 68.0f, 69.0f, 70.0f,
126             71.0f, 72.0f, 73.0f, 74.0f, 75.0f,
127             76.0f, 77.0f, 78.0f, 79.0f, 80.0f,
128             81.0f, 82.0f, 83.0f, 84.0f, 85.0f,
129             86.0f, 87.0f, 88.0f, 89.0f, 90.0f,
130         },
131         qScale, qOffset);
132 
133     // Channel 0 of return 2 (i.e. channels 1 and 2 of the original input).
134     auto expectedData3 = armnnUtils::QuantizedVector<T>(
135         {
136             31.0f, 32.0f, 33.0f, 34.0f, 35.0f,
137             36.0f, 37.0f, 38.0f, 39.0f, 40.0f,
138             41.0f, 42.0f, 43.0f, 44.0f, 45.0f,
139             46.0f, 47.0f, 48.0f, 49.0f, 50.0f,
140             51.0f, 52.0f, 53.0f, 54.0f, 55.0f,
141             56.0f, 57.0f, 58.0f, 59.0f, 60.0f,
142         },
143         qScale, qOffset);
144 
145     // Channel 1 of return 2.
146     auto expectedData4 = armnnUtils::QuantizedVector<T>(
147         {
148             61.0f, 62.0f, 63.0f, 64.0f, 65.0f,
149             66.0f, 67.0f, 68.0f, 69.0f, 70.0f,
150             71.0f, 72.0f, 73.0f, 74.0f, 75.0f,
151             76.0f, 77.0f, 78.0f, 79.0f, 80.0f,
152             81.0f, 82.0f, 83.0f, 84.0f, 85.0f,
153             86.0f, 87.0f, 88.0f, 89.0f, 90.0f,
154         },
155         qScale, qOffset);
156 
157     std::vector<T> actualData1(outputTensorInfo1.GetNumElements());
158     std::vector<T> actualData2(outputTensorInfo2.GetNumElements());
159     std::vector<T> actualData3(outputTensorInfo3.GetNumElements());
160     std::vector<T> actualData4(outputTensorInfo4.GetNumElements());
161 
162     // NOTE: as a corollary of the splitting of x and y restriction the x and y values of the view origins
163     //       have to be zero, the co-ordinates are as per the tensor info above channels, height/y, width/x
164     //       note that under the hood the compute engine reverses these i.e. its coordinate system is x, y, channels.
165     std::vector<unsigned int> wOrigin1 = {0, 0, 0}; //Extent of the window is defined by size of output[0].
166     armnn::SplitterQueueDescriptor::ViewOrigin window1(wOrigin1);
167 
168     std::vector<unsigned int> wOrigin2 = {1, 0, 0}; //Extent of the window is defined by size of output[1].
169     armnn::SplitterQueueDescriptor::ViewOrigin window2(wOrigin2);
170 
171     std::vector<unsigned int> wOrigin3 = {0, 0, 0}; //Extent of the window is defined by size of output[2].
172     armnn::SplitterQueueDescriptor::ViewOrigin window3(wOrigin3);
173 
174     std::vector<unsigned int> wOrigin4 = {1, 0, 0}; //Extent of the window is defined by size of output[3].
175     armnn::SplitterQueueDescriptor::ViewOrigin window4(wOrigin4);
176 
177     bool subTensorsSupported = tensorHandleFactory.SupportsSubTensors();
178     std::unique_ptr<armnn::ITensorHandle> inputHandle  = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
179 
180     std::unique_ptr<armnn::ITensorHandle> outputHandle1 =
181         subTensorsSupported ?
182         tensorHandleFactory.CreateSubTensorHandle(*inputHandle, outputTensorInfo1.GetShape(), wOrigin1.data()) :
183         tensorHandleFactory.CreateTensorHandle(outputTensorInfo1);
184 
185     std::unique_ptr<armnn::ITensorHandle> outputHandle2 =
186         subTensorsSupported ?
187         tensorHandleFactory.CreateSubTensorHandle(*inputHandle, outputTensorInfo2.GetShape(), wOrigin2.data()) :
188         tensorHandleFactory.CreateTensorHandle(outputTensorInfo2);
189 
190     std::unique_ptr<armnn::ITensorHandle> outputHandle3 =
191         subTensorsSupported ?
192         tensorHandleFactory.CreateSubTensorHandle(*outputHandle2, outputTensorInfo3.GetShape(), wOrigin3.data()) :
193         tensorHandleFactory.CreateTensorHandle(outputTensorInfo3);
194 
195     std::unique_ptr<armnn::ITensorHandle> outputHandle4 =
196         subTensorsSupported ?
197         tensorHandleFactory.CreateSubTensorHandle(*outputHandle2, outputTensorInfo4.GetShape(), wOrigin4.data()) :
198         tensorHandleFactory.CreateTensorHandle(outputTensorInfo4);
199 
200     // Do the first split
201     armnn::SplitterQueueDescriptor data;
202     armnn::WorkloadInfo info;
203     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
204     AddOutputToWorkload(data, info, outputTensorInfo1, outputHandle1.get());
205     AddOutputToWorkload(data, info, outputTensorInfo2, outputHandle2.get());
206 
207     data.m_ViewOrigins.push_back(window1);
208     data.m_ViewOrigins.push_back(window2);
209 
210     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateWorkload(armnn::LayerType::Splitter,
211                                                                                 data,
212                                                                                 info);
213 
214     inputHandle->Allocate();
215     outputHandle1->Allocate();
216     outputHandle2->Allocate();
217 
218     CopyDataToITensorHandle(inputHandle.get(), input.data());
219 
220     workload->Execute();
221 
222     CopyDataFromITensorHandle(actualData1.data(), outputHandle1.get());
223     CopyDataFromITensorHandle(actualData2.data(), outputHandle2.get());
224 
225     // Do the second split.
226     armnn::SplitterQueueDescriptor data2;
227     armnn::WorkloadInfo info2;
228     AddInputToWorkload(data2, info2, outputTensorInfo2, outputHandle2.get());
229     AddOutputToWorkload(data2, info2, outputTensorInfo3, outputHandle3.get());
230     AddOutputToWorkload(data2, info2, outputTensorInfo4, outputHandle4.get());
231 
232     data2.m_ViewOrigins.push_back(window3);
233     data2.m_ViewOrigins.push_back(window4);
234 
235     std::unique_ptr<armnn::IWorkload> workload2 = workloadFactory.CreateWorkload(armnn::LayerType::Splitter,
236                                                                                  data2,
237                                                                                  info2);
238 
239     outputHandle3->Allocate();
240     outputHandle4->Allocate();
241 
242     ExecuteWorkload(*workload2, memoryManager);
243 
244     CopyDataFromITensorHandle(actualData3.data(), outputHandle3.get());
245     CopyDataFromITensorHandle(actualData4.data(), outputHandle4.get());
246 
247     LayerTestResult<T,3> ret1(actualData1, expectedData1, outputHandle1->GetShape(), outputTensorInfo1.GetShape());
248     LayerTestResult<T,3> ret2(actualData2, expectedData2, outputHandle2->GetShape(), outputTensorInfo2.GetShape());
249     LayerTestResult<T,3> ret3(actualData3, expectedData3, outputHandle3->GetShape(), outputTensorInfo3.GetShape());
250     LayerTestResult<T,3> ret4(actualData4, expectedData4, outputHandle4->GetShape(), outputTensorInfo4.GetShape());
251 
252     std::vector<LayerTestResult<T,3>> ret = {ret1, ret2, ret3, ret4,};
253 
254     return ret;
255 }
256 
257 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
CopyViaSplitterTestImpl(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory,float qScale,int32_t qOffset)258 LayerTestResult<T, 3> CopyViaSplitterTestImpl(
259     armnn::IWorkloadFactory& workloadFactory,
260     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
261     const armnn::ITensorHandleFactory& tensorHandleFactory,
262     float qScale, int32_t qOffset)
263 {
264     IgnoreUnused(memoryManager);
265 
266     const armnn::TensorInfo tensorInfo({ 3, 6, 5 }, ArmnnType, qScale, qOffset);
267     auto input = armnnUtils::QuantizedVector<T>(
268          {
269              1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
270              6.0f, 7.0f, 8.0f, 9.0f, 10.0f,
271             11.0f, 12.0f, 13.0f, 14.0f, 15.0f,
272             16.0f, 17.0f, 18.0f, 19.0f, 20.0f,
273             21.0f, 22.0f, 23.0f, 24.0f, 25.0f,
274             26.0f, 27.0f, 28.0f, 29.0f, 30.0f,
275 
276             31.0f, 32.0f, 33.0f, 34.0f, 35.0f,
277             36.0f, 37.0f, 38.0f, 39.0f, 40.0f,
278             41.0f, 42.0f, 43.0f, 44.0f, 45.0f,
279             46.0f, 47.0f, 48.0f, 49.0f, 50.0f,
280             51.0f, 52.0f, 53.0f, 54.0f, 55.0f,
281             56.0f, 57.0f, 58.0f, 59.0f, 60.0f,
282 
283             61.0f, 62.0f, 63.0f, 64.0f, 65.0f,
284             66.0f, 67.0f, 68.0f, 69.0f, 70.0f,
285             71.0f, 72.0f, 73.0f, 74.0f, 75.0f,
286             76.0f, 77.0f, 78.0f, 79.0f, 80.0f,
287             81.0f, 82.0f, 83.0f, 84.0f, 85.0f,
288             86.0f, 87.0f, 88.0f, 89.0f, 90.0f,
289         },
290         qScale, qOffset);
291 
292     std::vector<T> actualOutput(tensorInfo.GetNumElements());
293 
294     std::vector<unsigned int> origin = { 0, 0, 0 };
295     armnn::SplitterQueueDescriptor::ViewOrigin window(origin);
296 
297     const bool subTensorsSupported = tensorHandleFactory.SupportsSubTensors();
298     std::unique_ptr<armnn::ITensorHandle> inputHandle = tensorHandleFactory.CreateTensorHandle(tensorInfo);
299 
300     std::unique_ptr<armnn::ITensorHandle> outputHandle =
301         subTensorsSupported ?
302         tensorHandleFactory.CreateSubTensorHandle(*inputHandle, tensorInfo.GetShape(), origin.data()) :
303         tensorHandleFactory.CreateTensorHandle(tensorInfo);
304 
305     armnn::SplitterQueueDescriptor data;
306     armnn::WorkloadInfo info;
307     AddInputToWorkload(data, info, tensorInfo, inputHandle.get());
308     AddOutputToWorkload(data, info, tensorInfo, outputHandle.get());
309 
310     data.m_ViewOrigins.push_back(window);
311 
312     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateWorkload(armnn::LayerType::Splitter,
313                                                                                 data,
314                                                                                 info);
315 
316     inputHandle->Allocate();
317     outputHandle->Allocate();
318 
319     CopyDataToITensorHandle(inputHandle.get(), input.data());
320 
321     workload->Execute();
322 
323     CopyDataFromITensorHandle(actualOutput.data(), outputHandle.get());
324 
325     return LayerTestResult<T, 3>(actualOutput,
326                                  input,
327                                  outputHandle->GetShape(),
328                                  tensorInfo.GetShape());
329 }
330 
331 } // anonymous namespace
332 
SplitterFloat32Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)333 std::vector<LayerTestResult<float,3>> SplitterFloat32Test(
334     armnn::IWorkloadFactory& workloadFactory,
335     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
336     const armnn::ITensorHandleFactory& tensorHandleFactory)
337 {
338     return SplitterTestCommon<armnn::DataType::Float32>(workloadFactory, memoryManager, tensorHandleFactory);
339 }
340 
SplitterFloat16Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)341 std::vector<LayerTestResult<armnn::Half,3>> SplitterFloat16Test(
342     armnn::IWorkloadFactory& workloadFactory,
343     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
344     const armnn::ITensorHandleFactory& tensorHandleFactory)
345 {
346     return SplitterTestCommon<armnn::DataType::Float16>(workloadFactory, memoryManager, tensorHandleFactory);
347 }
348 
SplitterUint8Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)349 std::vector<LayerTestResult<uint8_t,3>> SplitterUint8Test(
350     armnn::IWorkloadFactory& workloadFactory,
351     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
352     const armnn::ITensorHandleFactory& tensorHandleFactory)
353 {
354     return SplitterTestCommon<armnn::DataType::QAsymmU8>(workloadFactory, memoryManager, tensorHandleFactory, 1.0f, 0);
355 }
356 
SplitterInt16Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)357 std::vector<LayerTestResult<int16_t,3>> SplitterInt16Test(
358     armnn::IWorkloadFactory& workloadFactory,
359     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
360     const armnn::ITensorHandleFactory& tensorHandleFactory)
361 {
362     return SplitterTestCommon<armnn::DataType::QSymmS16>(workloadFactory, memoryManager, tensorHandleFactory, 1.0f, 0);
363 }
364 
CopyViaSplitterFloat32Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)365 LayerTestResult<float, 3> CopyViaSplitterFloat32Test(
366     armnn::IWorkloadFactory& workloadFactory,
367     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
368     const armnn::ITensorHandleFactory& tensorHandleFactory)
369 {
370     return CopyViaSplitterTestImpl<armnn::DataType::Float32>(workloadFactory,
371                                                              memoryManager,
372                                                              tensorHandleFactory,
373                                                              0.0f,
374                                                              0);
375 }
376 
CopyViaSplitterFloat16Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)377 LayerTestResult<armnn::Half, 3> CopyViaSplitterFloat16Test(
378     armnn::IWorkloadFactory& workloadFactory,
379     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
380     const armnn::ITensorHandleFactory& tensorHandleFactory)
381 {
382     return CopyViaSplitterTestImpl<armnn::DataType::Float16>(workloadFactory,
383                                                              memoryManager,
384                                                              tensorHandleFactory,
385                                                              0.0f,
386                                                              0);
387 }
388 
CopyViaSplitterUint8Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)389 LayerTestResult<uint8_t, 3> CopyViaSplitterUint8Test(
390     armnn::IWorkloadFactory& workloadFactory,
391     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
392     const armnn::ITensorHandleFactory& tensorHandleFactory)
393 {
394     return CopyViaSplitterTestImpl<armnn::DataType::QAsymmU8>(workloadFactory,
395                                                               memoryManager,
396                                                               tensorHandleFactory,
397                                                               1.0f,
398                                                               0);
399 }
400 
CopyViaSplitterInt16Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory)401 LayerTestResult<int16_t, 3> CopyViaSplitterInt16Test(
402         armnn::IWorkloadFactory& workloadFactory,
403         const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
404         const armnn::ITensorHandleFactory& tensorHandleFactory)
405 {
406     return CopyViaSplitterTestImpl<armnn::DataType::QSymmS16>(workloadFactory,
407                                                               memoryManager,
408                                                               tensorHandleFactory,
409                                                               1.0f,
410                                                               0);
411 }
412