1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6
7 #include <ResolveType.hpp>
8
9 #include <armnn/INetwork.hpp>
10
11 #include <armnn/utility/NumericCast.hpp>
12
13 #include <CommonTestUtils.hpp>
14
15 #include <doctest/doctest.h>
16
17 #include <vector>
18
19 namespace
20 {
21
22 template<typename armnn::DataType DataType>
CreateSplitterNetwork(const TensorShape & inputShape,const std::vector<TensorShape> & outputShapes,unsigned int splitAxis,unsigned int numSplit,const float qScale=1.0f,const int32_t qOffset=0)23 INetworkPtr CreateSplitterNetwork(const TensorShape& inputShape,
24 const std::vector<TensorShape>& outputShapes,
25 unsigned int splitAxis,
26 unsigned int numSplit,
27 const float qScale = 1.0f,
28 const int32_t qOffset = 0)
29 {
30 using namespace armnn;
31 // Builds up the structure of the network.
32 INetworkPtr net(INetwork::Create());
33
34 TensorInfo inputTensorInfo(inputShape, DataType, qScale, qOffset, true);
35
36 std::vector<unsigned int> splitterDimSizes(inputShape.GetNumDimensions());
37
38 // Add current input shape to splitterDimSizes
39 for (unsigned int i = 0; i < inputShape.GetNumDimensions(); ++i)
40 {
41 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
42 }
43
44 if (splitterDimSizes[splitAxis] % numSplit != 0)
45 {
46 throw ParseException("Number of splits must evenly divide the dimension");
47 }
48 splitterDimSizes[splitAxis] /= numSplit;
49
50 SplitterDescriptor splitDesc(numSplit, inputShape.GetNumDimensions());
51 for (unsigned int g = 0; g < numSplit; ++g)
52 {
53 // Set the size of the views.
54 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
55 {
56 splitDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
57 }
58 splitDesc.SetViewOriginCoord(g, splitAxis, splitterDimSizes[splitAxis] * g);
59 }
60
61 IConnectableLayer* splitter = net->AddSplitterLayer(splitDesc, "splitter");
62 IConnectableLayer* input = net->AddInputLayer(0, "input");
63 Connect(input, splitter, inputTensorInfo, 0, 0);
64
65 for (unsigned int i = 0; i < outputShapes.size(); ++i)
66 {
67 TensorInfo outputTensorInfo(outputShapes[i], DataType, qScale, qOffset);
68 IConnectableLayer* output = net->AddOutputLayer(armnn::numeric_cast<LayerBindingId>(i));
69 Connect(splitter, output, outputTensorInfo, i, 0);
70 }
71
72 return net;
73 }
74
75 template<armnn::DataType ArmnnType>
Splitter1dEndToEnd(const std::vector<BackendId> & backends)76 void Splitter1dEndToEnd(const std::vector<BackendId>& backends)
77 {
78 using namespace armnn;
79 using T = ResolveType<ArmnnType>;
80
81 unsigned int splitAxis = 0;
82 unsigned int numSplit = 2;
83 const TensorShape& inputShape = { 4 };
84 const std::vector<TensorShape> outputShapes{{ 2 }, { 2 }};
85
86 // Builds up the structure of the network
87 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
88
89 CHECK(net);
90
91 // Creates structures for input & output.
92 std::vector<T> inputData{ 1, 2, 3, 4 };
93
94 std::vector<T> expectedOutput0{ 1, 2 };
95 std::vector<T> expectedOutput1{ 3, 4 };
96
97 std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
98 std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput0 }, {1, expectedOutput1} };
99
100 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
101 }
102
103 template<armnn::DataType ArmnnType>
Splitter2dDim0EndToEnd(const std::vector<BackendId> & backends)104 void Splitter2dDim0EndToEnd(const std::vector<BackendId>& backends)
105 {
106 using namespace armnn;
107 using T = ResolveType<ArmnnType>;
108
109 unsigned int splitAxis = 0;
110 unsigned int numSplit = 2;
111 const TensorShape& inputShape = { 4, 3 };
112 const std::vector<TensorShape> outputShapes{{ 2, 3 }, { 2, 3 }};
113
114 // Builds up the structure of the network
115 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
116
117 CHECK(net);
118
119 // Creates structures for input & output.
120 std::vector<T> inputData{
121 1, 2,
122 3, 4,
123 5, 6,
124 7, 8,
125 9, 10,
126 11, 12
127 };
128
129 std::vector<T> expectedOutput0{ 1, 2, 3, 4, 5, 6 };
130 std::vector<T> expectedOutput1{ 7, 8, 9, 10, 11, 12 };
131
132 std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
133 std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput0 }, {1, expectedOutput1} };
134
135 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
136 }
137
138 template<armnn::DataType ArmnnType>
Splitter2dDim1EndToEnd(const std::vector<BackendId> & backends)139 void Splitter2dDim1EndToEnd(const std::vector<BackendId>& backends)
140 {
141 using namespace armnn;
142 using T = ResolveType<ArmnnType>;
143
144 unsigned int splitAxis = 1;
145 unsigned int numSplit = 3;
146 const TensorShape& inputShape = { 4, 3 };
147 const std::vector<TensorShape> outputShapes{{ 4, 1 }, { 4, 1 }, { 4, 1 }};
148
149 // Builds up the structure of the network
150 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
151
152 CHECK(net);
153
154 // Creates structures for input & output.
155 std::vector<T> inputData{
156 1, 2,
157 3, 4,
158 5, 6,
159 7, 8,
160 9, 10,
161 11, 12
162 };
163
164 std::vector<T> expectedOutput0{ 1, 4, 7, 10 };
165 std::vector<T> expectedOutput1{ 2, 5, 8, 11 };
166 std::vector<T> expectedOutput2{ 3, 6, 9, 12 };
167
168 std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
169 std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput0 },
170 { 1, expectedOutput1 },
171 { 2, expectedOutput2 } };
172
173 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
174 }
175
176 template<armnn::DataType ArmnnType>
Splitter3dDim0EndToEnd(const std::vector<BackendId> & backends)177 void Splitter3dDim0EndToEnd(const std::vector<BackendId>& backends)
178 {
179 using namespace armnn;
180 using T = ResolveType<ArmnnType>;
181
182 unsigned int splitAxis = 0;
183 unsigned int numSplit = 2;
184 const TensorShape& inputShape = { 2, 4, 3 };
185 const std::vector<TensorShape> outputShapes{{ 1, 4, 3 }, { 1, 4, 3 }};
186
187 // Builds up the structure of the network
188 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
189
190 CHECK(net);
191
192 // Creates structures for input & output.
193 std::vector<T> inputData{
194 1, 2, 3,
195 4, 5, 6,
196 7, 8, 9,
197 10, 11, 12,
198 13, 14, 15,
199 16, 17, 18,
200 19, 20, 21,
201 22, 23, 24
202 };
203
204 std::vector<T> expectedOutput0{
205 1, 2, 3,
206 4, 5, 6,
207 7, 8, 9,
208 10, 11, 12
209 };
210 std::vector<T> expectedOutput1{
211 13, 14, 15,
212 16, 17, 18,
213 19, 20, 21,
214 22, 23, 24
215 };
216
217 std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
218 std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput0 },
219 { 1, expectedOutput1 } };
220
221 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
222 }
223
224 template<armnn::DataType ArmnnType>
Splitter3dDim1EndToEnd(const std::vector<BackendId> & backends)225 void Splitter3dDim1EndToEnd(const std::vector<BackendId>& backends)
226 {
227 using namespace armnn;
228 using T = ResolveType<ArmnnType>;
229
230 unsigned int splitAxis = 1;
231 unsigned int numSplit = 2;
232 const TensorShape& inputShape = { 2, 4, 3 };
233 const std::vector<TensorShape> outputShapes{{ 2, 2, 3 }, { 2, 2, 3 }};
234
235 // Builds up the structure of the network
236 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
237
238 CHECK(net);
239
240 // Creates structures for input & output.
241 std::vector<T> inputData{
242 1, 2, 3,
243 4, 5, 6,
244 7, 8, 9,
245 10, 11, 12,
246 13, 14, 15,
247 16, 17, 18,
248 19, 20, 21,
249 22, 23, 24
250 };
251
252 std::vector<T> expectedOutput0{
253 1, 2, 3,
254 4, 5, 6,
255 13, 14, 15,
256 16, 17, 18
257 };
258 std::vector<T> expectedOutput1{
259 7, 8, 9,
260 10, 11, 12,
261 19, 20, 21,
262 22, 23, 24
263 };
264
265 std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
266 std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput0 },
267 { 1, expectedOutput1 } };
268
269 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
270 }
271
272 template<armnn::DataType ArmnnType>
Splitter3dDim2EndToEnd(const std::vector<BackendId> & backends)273 void Splitter3dDim2EndToEnd(const std::vector<BackendId>& backends)
274 {
275 using namespace armnn;
276 using T = ResolveType<ArmnnType>;
277
278 unsigned int splitAxis = 2;
279 unsigned int numSplit = 3;
280 const TensorShape& inputShape = { 2, 4, 3 };
281 const std::vector<TensorShape> outputShapes{{ 2, 4, 1 }, { 2, 4, 1 }, { 2, 4, 1 }};
282
283 // Builds up the structure of the network
284 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
285
286 CHECK(net);
287
288 // Creates structures for input & output.
289 std::vector<T> inputData{
290 1, 2, 3,
291 4, 5, 6,
292 7, 8, 9,
293 10, 11, 12,
294 13, 14, 15,
295 16, 17, 18,
296 19, 20, 21,
297 22, 23, 24
298 };
299
300 std::vector<T> expectedOutput0{ 1, 4, 7, 10, 13, 16, 19, 22 };
301 std::vector<T> expectedOutput1{ 2, 5, 8, 11, 14, 17, 20, 23 };
302 std::vector<T> expectedOutput2{ 3, 6, 9, 12, 15, 18, 21, 24 };
303
304 std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
305 std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput0 },
306 { 1, expectedOutput1 },
307 { 2, expectedOutput2 } };
308
309 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
310 }
311
312 template<armnn::DataType ArmnnType>
Splitter4dDim0EndToEnd(const std::vector<BackendId> & backends)313 void Splitter4dDim0EndToEnd(const std::vector<BackendId>& backends)
314 {
315 using namespace armnn;
316 using T = ResolveType<ArmnnType>;
317
318 unsigned int splitAxis = 0;
319 unsigned int numSplit = 2;
320 const TensorShape& inputShape = { 4, 3, 2, 2 };
321 const std::vector<TensorShape> outputShapes{{ 2, 3, 2, 2 }, { 2, 3, 2, 2 }};
322
323 // Builds up the structure of the network
324 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
325
326 CHECK(net);
327
328 // Creates structures for input & output.
329 std::vector<T> inputData{
330 1, 2,
331 3, 4,
332 5, 6,
333 7, 8,
334 9, 10,
335 11, 12,
336 13, 14,
337 15, 16,
338 17, 18,
339 19, 20,
340 21, 22,
341 23, 24,
342 25, 26,
343 27, 28,
344 29, 30,
345 31, 32,
346 33, 34,
347 35, 36,
348 37, 38,
349 39, 40,
350 41, 42,
351 43, 44,
352 45, 46,
353 47, 48
354 };
355
356 std::vector<T> expectedOutput0{
357 1, 2,
358 3, 4,
359 5, 6,
360 7, 8,
361 9, 10,
362 11, 12,
363 13, 14,
364 15, 16,
365 17, 18,
366 19, 20,
367 21, 22,
368 23, 24
369 };
370
371 std::vector<T> expectedOutput1{
372 25, 26,
373 27, 28,
374 29, 30,
375 31, 32,
376 33, 34,
377 35, 36,
378 37, 38,
379 39, 40,
380 41, 42,
381 43, 44,
382 45, 46,
383 47, 48
384 };
385
386 std::map<int, std::vector<T>> inputTensorData = {{ 0,inputData }};
387 std::map<int, std::vector<T>> expectedOutputData = {{ 0, expectedOutput0 }, { 1, expectedOutput1 }};
388
389 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
390 }
391
392 template<armnn::DataType ArmnnType>
Splitter4dDim1EndToEnd(const std::vector<BackendId> & backends)393 void Splitter4dDim1EndToEnd(const std::vector<BackendId>& backends)
394 {
395 using namespace armnn;
396 using T = ResolveType<ArmnnType>;
397
398 unsigned int splitAxis = 1;
399 unsigned int numSplit = 2;
400 const TensorShape& inputShape = { 2, 6, 2, 2 };
401 const std::vector<TensorShape> outputShapes{{ 2, 3, 2, 2 }, { 2, 3, 2, 2 }};
402
403 // Builds up the structure of the network
404 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
405
406 CHECK(net);
407
408 // Creates structures for input & output.
409 std::vector<T> inputData{
410 1, 2,
411 3, 4,
412 5, 6,
413 7, 8,
414 9, 10,
415 11, 12,
416 13, 14,
417 15, 16,
418 17, 18,
419 19, 20,
420 21, 22,
421 23, 24,
422 25, 26,
423 27, 28,
424 29, 30,
425 31, 32,
426 33, 34,
427 35, 36,
428 37, 38,
429 39, 40,
430 41, 42,
431 43, 44,
432 45, 46,
433 47, 48
434 };
435
436 std::vector<T> expectedOutput0{
437 1, 2,
438 3, 4,
439 5, 6,
440 7, 8,
441 9, 10,
442 11, 12,
443 25, 26,
444 27, 28,
445 29, 30,
446 31, 32,
447 33, 34,
448 35, 36
449 };
450
451 std::vector<T> expectedOutput1{
452 13, 14,
453 15, 16,
454 17, 18,
455 19, 20,
456 21, 22,
457 23, 24,
458 37, 38,
459 39, 40,
460 41, 42,
461 43, 44,
462 45, 46,
463 47, 48
464 };
465
466 std::map<int, std::vector<T>> inputTensorData = {{ 0,inputData }};
467 std::map<int, std::vector<T>> expectedOutputData = {{ 0, expectedOutput0 }, { 1, expectedOutput1 }};
468
469 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
470 }
471
472 template<armnn::DataType ArmnnType>
Splitter4dDim2EndToEnd(const std::vector<BackendId> & backends)473 void Splitter4dDim2EndToEnd(const std::vector<BackendId>& backends)
474 {
475 using namespace armnn;
476 using T = ResolveType<ArmnnType>;
477
478 unsigned int splitAxis = 2;
479 unsigned int numSplit = 2;
480 const TensorShape& inputShape = { 2, 3, 4, 2 };
481 const std::vector<TensorShape> outputShapes{{ 2, 3, 2, 2 }, { 2, 3, 2, 2 }};
482
483 // Builds up the structure of the network
484 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
485
486 CHECK(net);
487
488 // Creates structures for input & output.
489 std::vector<T> inputData{
490 1, 2,
491 3, 4,
492 5, 6,
493 7, 8,
494 9, 10,
495 11, 12,
496 13, 14,
497 15, 16,
498 17, 18,
499 19, 20,
500 21, 22,
501 23, 24,
502 25, 26,
503 27, 28,
504 29, 30,
505 31, 32,
506 33, 34,
507 35, 36,
508 37, 38,
509 39, 40,
510 41, 42,
511 43, 44,
512 45, 46,
513 47, 48
514 };
515
516 std::vector<T> expectedOutput0{
517 1, 2,
518 3, 4,
519 9, 10,
520 11, 12,
521 17, 18,
522 19, 20,
523 25, 26,
524 27, 28,
525 33, 34,
526 35, 36,
527 41, 42,
528 43, 44
529 };
530
531 std::vector<T> expectedOutput1{
532 5, 6,
533 7, 8,
534 13, 14,
535 15, 16,
536 21, 22,
537 23, 24,
538 29, 30,
539 31, 32,
540 37, 38,
541 39, 40,
542 45, 46,
543 47, 48
544 };
545
546 std::map<int, std::vector<T>> inputTensorData = {{ 0,inputData }};
547 std::map<int, std::vector<T>> expectedOutputData = {{ 0, expectedOutput0 }, { 1, expectedOutput1 }};
548
549 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
550 }
551
552 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
Splitter4dDim3EndToEnd(const std::vector<BackendId> & backends)553 void Splitter4dDim3EndToEnd(const std::vector<BackendId>& backends)
554 {
555 using namespace armnn;
556
557 unsigned int splitAxis = 3;
558 unsigned int numSplit = 2;
559 const TensorShape& inputShape = { 2, 3, 4, 2 };
560 const std::vector<TensorShape> outputShapes{{ 2, 3, 4, 1 }, { 2, 3, 4, 1 }};
561
562 // Builds up the structure of the network
563 INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit);
564
565 CHECK(net);
566
567 // Creates structures for input & output.
568 std::vector<T> inputData{
569 1, 2,
570 3, 4,
571 5, 6,
572 7, 8,
573 9, 10,
574 11, 12,
575 13, 14,
576 15, 16,
577 17, 18,
578 19, 20,
579 21, 22,
580 23, 24,
581 25, 26,
582 27, 28,
583 29, 30,
584 31, 32,
585 33, 34,
586 35, 36,
587 37, 38,
588 39, 40,
589 41, 42,
590 43, 44,
591 45, 46,
592 47, 48
593 };
594
595 std::vector<T> expectedOutput0{
596 1, 3, 5, 7,
597 9, 11, 13, 15,
598 17, 19, 21, 23,
599 25, 27, 29, 31,
600 33, 35, 37, 39,
601 41, 43, 45, 47
602 };
603
604 std::vector<T> expectedOutput1{
605 2, 4, 6, 8,
606 10, 12, 14, 16,
607 18, 20, 22, 24,
608 26, 28, 30, 32,
609 34, 36, 38, 40,
610 42, 44, 46, 48
611 };
612
613 std::map<int, std::vector<T>> inputTensorData = {{ 0,inputData }};
614 std::map<int, std::vector<T>> expectedOutputData = {{ 0, expectedOutput0 }, { 1, expectedOutput1 }};
615
616 EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(net), inputTensorData, expectedOutputData, backends);
617 }
618
619 } // anonymous namespace
620