blob: 0e24e9505b82395986c129e8ef586949a6580194 [file] [log] [blame]
Narumol Prangnawarat1a268962020-07-27 15:52:13 +01001//
2// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Sadik Armagan76615a52020-08-04 14:01:05 +01005#include <Graph.hpp>
6#include <Network.hpp>
7
Narumol Prangnawarat1a268962020-07-27 15:52:13 +01008#include <neon/NeonTensorHandle.hpp>
9#include <neon/NeonTensorHandleFactory.hpp>
10
Matthew Sloyan171214c2020-09-09 09:07:37 +010011#include <armnn/utility/NumericCast.hpp>
Sadik Armagan76615a52020-08-04 14:01:05 +010012#include <armnn/utility/PolymorphicDowncast.hpp>
13
14#include <test/GraphUtils.hpp>
Narumol Prangnawaratb8d771a2020-08-14 11:51:12 +010015#include <arm_compute/runtime/Allocator.h>
Keith Davis3674f142020-08-16 23:44:15 +010016#include <backendsCommon/test/CommonTestUtils.hpp>
Sadik Armagan76615a52020-08-04 14:01:05 +010017
Narumol Prangnawarat1a268962020-07-27 15:52:13 +010018#include <boost/test/unit_test.hpp>
Keith Davis3674f142020-08-16 23:44:15 +010019#include <armnn/utility/Assert.hpp>
Narumol Prangnawarat1a268962020-07-27 15:52:13 +010020
21BOOST_AUTO_TEST_SUITE(NeonTensorHandleTests)
22using namespace armnn;
23
24BOOST_AUTO_TEST_CASE(NeonTensorHandleGetCapabilitiesNoPadding)
25{
26 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>();
27 NeonTensorHandleFactory handleFactory(memoryManager);
28
29 INetworkPtr network(INetwork::Create());
30
31 // Add the layers
32 IConnectableLayer* input = network->AddInputLayer(0);
33 SoftmaxDescriptor descriptor;
34 descriptor.m_Beta = 1.0f;
35 IConnectableLayer* softmax = network->AddSoftmaxLayer(descriptor);
36 IConnectableLayer* output = network->AddOutputLayer(2);
37
38 // Establish connections
39 input->GetOutputSlot(0).Connect(softmax->GetInputSlot(0));
40 softmax->GetOutputSlot(0).Connect(output->GetInputSlot(0));
41
42 // No padding required for input
43 std::vector<Capability> capabilities = handleFactory.GetCapabilities(input,
44 softmax,
45 CapabilityClass::PaddingRequired);
46 BOOST_TEST(capabilities.empty());
47
48 // No padding required for Softmax
49 capabilities = handleFactory.GetCapabilities(softmax, output, CapabilityClass::PaddingRequired);
50 BOOST_TEST(capabilities.empty());
51
52 // No padding required for output
53 capabilities = handleFactory.GetCapabilities(output, nullptr, CapabilityClass::PaddingRequired);
54 BOOST_TEST(capabilities.empty());
55}
56
57BOOST_AUTO_TEST_CASE(NeonTensorHandleGetCapabilitiesPadding)
58{
59 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>();
60 NeonTensorHandleFactory handleFactory(memoryManager);
61
62 INetworkPtr network(INetwork::Create());
63
64 // Add the layers
65 IConnectableLayer* input = network->AddInputLayer(0);
66 Pooling2dDescriptor descriptor;
67 IConnectableLayer* pooling = network->AddPooling2dLayer(descriptor);
68 IConnectableLayer* output = network->AddOutputLayer(2);
69
70 // Establish connections
71 input->GetOutputSlot(0).Connect(pooling->GetInputSlot(0));
72 pooling->GetOutputSlot(0).Connect(output->GetInputSlot(0));
73
74 // No padding required for input
75 std::vector<Capability> capabilities = handleFactory.GetCapabilities(input,
76 pooling,
77 CapabilityClass::PaddingRequired);
78 BOOST_TEST(capabilities.empty());
79
80 // No padding required for output
81 capabilities = handleFactory.GetCapabilities(output, nullptr, CapabilityClass::PaddingRequired);
82 BOOST_TEST(capabilities.empty());
83
84 // Padding required for Pooling2d
85 capabilities = handleFactory.GetCapabilities(pooling, output, CapabilityClass::PaddingRequired);
86 BOOST_TEST(capabilities.size() == 1);
87 BOOST_TEST((capabilities[0].m_CapabilityClass == CapabilityClass::PaddingRequired));
88 BOOST_TEST(capabilities[0].m_Value);
89}
90
Keith Davis3674f142020-08-16 23:44:15 +010091BOOST_AUTO_TEST_CASE(ConcatOnXorYSubTensorsNoPaddingRequiredTest)
Sadik Armagan76615a52020-08-04 14:01:05 +010092{
93 armnn::INetworkPtr net(armnn::INetwork::Create());
94
95 // Set up tensor infos
96 const armnn::TensorInfo inputInfo = armnn::TensorInfo({2, 3, 2, 2}, armnn::DataType::Float32);
97 const armnn::TensorInfo intermediateInfo = armnn::TensorInfo({2, 3, 2, 2}, armnn::DataType::Float32);
98 const armnn::TensorInfo outputInfo = armnn::TensorInfo({2, 3, 4, 2}, armnn::DataType::Float32);
99
100 armnn::ElementwiseUnaryDescriptor descriptor(armnn::UnaryOperation::Abs);
101
102 // Create the network
103 armnn::IConnectableLayer* const input0Layer = net->AddInputLayer(0, "input_0");
104 input0Layer->GetOutputSlot(0).SetTensorInfo(inputInfo);
105 armnn::IConnectableLayer* elementwiseUnaryLayer0 = net->AddElementwiseUnaryLayer(descriptor, "elementwiseUnary_0");
106 elementwiseUnaryLayer0->GetOutputSlot(0).SetTensorInfo(intermediateInfo);
107 input0Layer->GetOutputSlot(0).Connect(elementwiseUnaryLayer0->GetInputSlot(0));
108
109 armnn::IConnectableLayer* const input1Layer = net->AddInputLayer(1, "input_1");
110 input1Layer->GetOutputSlot(0).SetTensorInfo(inputInfo);
111 armnn::IConnectableLayer* elementwiseUnaryLayer1 = net->AddElementwiseUnaryLayer(descriptor, "elementwiseUnary_1");
112 elementwiseUnaryLayer1->GetOutputSlot(0).SetTensorInfo(intermediateInfo);
113 input1Layer->GetOutputSlot(0).Connect(elementwiseUnaryLayer1->GetInputSlot(0));
114
115 std::array<armnn::TensorShape, 2> concatInputShapes = { intermediateInfo.GetShape(), intermediateInfo.GetShape() };
116 armnn::IConnectableLayer* const concatLayer = net->AddConcatLayer(armnn::CreateDescriptorForConcatenation(
117 concatInputShapes.begin(), concatInputShapes.end(), 2), "concatenation");
118 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
119 elementwiseUnaryLayer0->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(0));
120 elementwiseUnaryLayer1->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(1));
121
122 armnn::IConnectableLayer* const outputLayer = net->AddOutputLayer(0, "output");
123 concatLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
124
125 armnn::IRuntime::CreationOptions options;
126 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
127
128 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
129 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
130
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000131 const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
Sadik Armagan76615a52020-08-04 14:01:05 +0100132
133 // Load graph into runtime
134 armnn::NetworkId networkIdentifier;
135 runtime->LoadNetwork(networkIdentifier, std::move(optimizedNet));
136
137 // now check the concat how many sub-tensors it is using..
138 auto TraceSubTensorHandleAncestry = [](armnn::ITensorHandle* const subTensorHandle)
139 {
140 if (subTensorHandle && subTensorHandle->GetParent())
141 {
142 return true;
143 }
144 return false;
145 };
146
147 for (auto&& layer : theGraph)
148 {
149 if(layer->GetType() == armnn::LayerType::Concat)
150 {
151 unsigned int numberOfSubTensors = 0;
152 for (unsigned int i = 0; i < layer->GetNumInputSlots(); ++i)
153 {
154 const armnn::OutputSlot* slot = layer->GetInputSlot(i).GetConnectedOutputSlot();
155 if (TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData()))
156 {
157 ++numberOfSubTensors;
158 }
159 }
160 // sub-tensors should be supported in this configuration
Keith Davis3674f142020-08-16 23:44:15 +0100161 ARMNN_ASSERT(numberOfSubTensors > 0);
162 }
163 }
164}
165
166BOOST_AUTO_TEST_CASE(ConcatonXorYPaddingRequiredTest)
167{
168 armnn::INetworkPtr net(armnn::INetwork::Create());
169
170 // Set up tensor infos
171 const armnn::TensorInfo inputInfo = armnn::TensorInfo({2, 3, 2, 2}, armnn::DataType::Float32);
172 const armnn::TensorInfo intermediateInfo = armnn::TensorInfo({2, 3, 2, 2}, armnn::DataType::Float32);
173 const armnn::TensorInfo outputInfo = armnn::TensorInfo({2, 3, 4, 2}, armnn::DataType::Float32);
174
175 armnn::Pooling2dDescriptor descriptor;
176 descriptor.m_PoolType = armnn::PoolingAlgorithm::Average;
177 descriptor.m_PoolWidth = descriptor.m_PoolHeight = 3;
178 descriptor.m_StrideX = descriptor.m_StrideY = 1;
179 descriptor.m_PadLeft = 1;
180 descriptor.m_PadRight = 1;
181 descriptor.m_PadTop = 1;
182 descriptor.m_PadBottom = 1;
183 descriptor.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
184
185 // Create the network
186 armnn::IConnectableLayer* const input0Layer = net->AddInputLayer(0, "input_0");
187 input0Layer->GetOutputSlot(0).SetTensorInfo(inputInfo);
188 armnn::IConnectableLayer* pooling2dLayer0 = net->AddPooling2dLayer(descriptor, "pooling2d_0");
189 pooling2dLayer0->GetOutputSlot(0).SetTensorInfo(intermediateInfo);
190 input0Layer->GetOutputSlot(0).Connect(pooling2dLayer0->GetInputSlot(0));
191
192 armnn::IConnectableLayer* const input1Layer = net->AddInputLayer(1, "input_1");
193 input1Layer->GetOutputSlot(0).SetTensorInfo(inputInfo);
194 armnn::IConnectableLayer* pooling2dLayer1 = net->AddPooling2dLayer(descriptor, "pooling2d_1");
195 pooling2dLayer1->GetOutputSlot(0).SetTensorInfo(intermediateInfo);
196 input1Layer->GetOutputSlot(0).Connect(pooling2dLayer1->GetInputSlot(0));
197
198 std::array<armnn::TensorShape, 2> concatInputShapes = { intermediateInfo.GetShape(), intermediateInfo.GetShape() };
199 armnn::IConnectableLayer* const concatLayer = net->AddConcatLayer(armnn::CreateDescriptorForConcatenation(
200 concatInputShapes.begin(), concatInputShapes.end(), 2), "concatenation");
201 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
202 pooling2dLayer0->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(0));
203 pooling2dLayer1->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(1));
204
205 armnn::IConnectableLayer* const outputLayer = net->AddOutputLayer(0, "output");
206 concatLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
207
208 armnn::IRuntime::CreationOptions options;
209 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
210
211 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
212 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
213
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000214 const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
Keith Davis3674f142020-08-16 23:44:15 +0100215
216 // Load graph into runtime
217 armnn::NetworkId networkIdentifier;
218 runtime->LoadNetwork(networkIdentifier, std::move(optimizedNet));
219
220 // now check the concat how many sub-tensors it is using..
221 auto TraceSubTensorHandleAncestry = [](armnn::ITensorHandle* const subTensorHandle)
222 {
223 if (subTensorHandle && subTensorHandle->GetParent())
224 {
225 return true;
226 }
227 return false;
228 };
229
230 unsigned int numberOfSubTensors = 0;
231 for (auto&& layer : theGraph)
232 {
233 if(layer->GetType() == armnn::LayerType::Concat)
234 {
235 for (unsigned int i = 0; i < layer->GetNumInputSlots(); ++i)
236 {
237 const armnn::OutputSlot* slot = layer->GetInputSlot(i).GetConnectedOutputSlot();
238 if (TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData()))
239 {
240 ++numberOfSubTensors;
241 }
242 }
243 }
244 }
245 // sub-tensors should not be supported in this configuration
246 ARMNN_ASSERT(numberOfSubTensors == 0);
247}
248
249BOOST_AUTO_TEST_CASE(SplitteronXorYNoPaddingRequiredTest)
250{
251 using namespace armnn;
252
253 unsigned int splitAxis = 2;
254 unsigned int numSplit = 2;
255
256 const TensorShape& inputShape = { 2, 3, 4, 2 };
257 const armnn::TensorInfo intermediateInfo = armnn::TensorInfo({ 2, 3, 2, 2 }, armnn::DataType::Float32);
258 const std::vector<TensorShape> outputShapes{{ 2, 3, 2, 2 },
259 { 2, 3, 2, 2 }};
260 const float qScale = 1.0f;
261 const int32_t qOffset = 0;
262
263 // Creates structures for input & output.
264 std::vector<float> inputData{
265 1, 2,
266 3, 4,
267 5, 6,
268 7, 8,
269 9, 10,
270 11, 12,
271 13, 14,
272 15, 16,
273 17, 18,
274 19, 20,
275 21, 22,
276 23, 24,
277 25, 26,
278 27, 28,
279 29, 30,
280 31, 32,
281 33, 34,
282 35, 36,
283 37, 38,
284 39, 40,
285 41, 42,
286 43, 44,
287 45, 46,
288 47, 48
289 };
290
291 std::vector<float> expectedOutput0{
292 1, 2,
293 3, 4,
294 9, 10,
295 11, 12,
296 17, 18,
297 19, 20,
298 25, 26,
299 27, 28,
300 33, 34,
301 35, 36,
302 41, 42,
303 43, 44
304 };
305
306 std::vector<float> expectedOutput1{
307 5, 6,
308 7, 8,
309 13, 14,
310 15, 16,
311 21, 22,
312 23, 24,
313 29, 30,
314 31, 32,
315 37, 38,
316 39, 40,
317 45, 46,
318 47, 48
319 };
320
321 // Builds up the structure of the network.
322 INetworkPtr net(INetwork::Create());
323
324 TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32, qScale, qOffset);
325
326 armnn::ElementwiseUnaryDescriptor descriptor(armnn::UnaryOperation::Abs);
327
328 // Splitter
329 std::vector<unsigned int> splitterDimSizes(inputShape.GetNumDimensions());
330
331 // Add current input shape to splitterDimSizes
332 for (unsigned int i = 0; i < inputShape.GetNumDimensions(); ++i)
333 {
334 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
335 }
336
337 if (splitterDimSizes[splitAxis] % numSplit != 0)
338 {
339 throw ParseException("Number of splits must evenly divide the dimension");
340 }
341
342 splitterDimSizes[splitAxis] /= numSplit;
343
344 SplitterDescriptor splitDesc(numSplit, inputShape.GetNumDimensions());
345
346 for (unsigned int g = 0; g < numSplit; ++g)
347 {
348 // Set the size of the views.
349 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
350 {
351 splitDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
352 }
353 splitDesc.SetViewOriginCoord(g, splitAxis, splitterDimSizes[splitAxis] * g);
354 }
355 IConnectableLayer* input = net->AddInputLayer(0, "input");
356 IConnectableLayer* elementWiseUnary0 = net->AddElementwiseUnaryLayer(descriptor, "elementwiseunary_0");
357 IConnectableLayer* elementWiseUnary1 = net->AddElementwiseUnaryLayer(descriptor, "elementwiseunary_0");
358 IConnectableLayer* splitter = net->AddSplitterLayer(splitDesc, "splitter");
359
360 // Connections
361 Connect(input, splitter, inputTensorInfo, 0, 0);
362 Connect(splitter, elementWiseUnary0, intermediateInfo, 0, 0);
363 Connect(splitter, elementWiseUnary1, intermediateInfo, 1, 0);
364
365 std::vector<IConnectableLayer*> pooling2dLayers{elementWiseUnary0, elementWiseUnary1};
366
367 for (unsigned int i = 0; i < outputShapes.size(); ++i)
368 {
369 TensorInfo outputTensorInfo(outputShapes[i], armnn::DataType::Float32, qScale, qOffset);
Matthew Sloyan171214c2020-09-09 09:07:37 +0100370 IConnectableLayer* output = net->AddOutputLayer(armnn::numeric_cast<LayerBindingId>(i));
Keith Davis3674f142020-08-16 23:44:15 +0100371 Connect(pooling2dLayers[i], output, outputTensorInfo, 0, 0);
372 }
373
374 std::map<int, std::vector<float>> inputTensorData = {{ 0,inputData }};
375 std::map<int, std::vector<float>> expectedOutputData = {{ 0, expectedOutput0 }, { 1, expectedOutput1 }};
376
377 armnn::IRuntime::CreationOptions options;
378 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
379
380 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
381 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
382
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000383 const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
Keith Davis3674f142020-08-16 23:44:15 +0100384
385 // Load graph into runtime
386 armnn::NetworkId networkIdentifier;
387 runtime->LoadNetwork(networkIdentifier, std::move(optimizedNet));
388
389 // now check the concat how many sub-tensors it is using..
390 auto TraceSubTensorHandleAncestry = [](armnn::ITensorHandle* const subTensorHandle)
391 {
392 if (subTensorHandle && subTensorHandle->GetParent())
393 {
394 return true;
395 }
396 return false;
397 };
398
399 for (auto&& layer : theGraph)
400 {
401 if(layer->GetType() == armnn::LayerType::ElementwiseUnary)
402 {
403 unsigned int numberOfSubTensors = 0;
404 for (unsigned int i = 0; i < layer->GetNumInputSlots(); ++i)
405 {
406 const armnn::OutputSlot* slot = layer->GetInputSlot(i).GetConnectedOutputSlot();
407 if (TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData()))
408 {
409 ++numberOfSubTensors;
410 }
411 }
412 // sub-tensors should be supported in this configuration
413 ARMNN_ASSERT(numberOfSubTensors > 0);
414 }
415 }
416
417 InputTensors inputTensors;
418 inputTensors.reserve(inputTensorData.size());
419 for (auto&& it : inputTensorData)
420 {
421 inputTensors.push_back({it.first,
422 ConstTensor(runtime->GetInputTensorInfo(networkIdentifier, it.first), it.second.data())});
423 }
424 OutputTensors outputTensors;
425 outputTensors.reserve(expectedOutputData.size());
426 std::map<int, std::vector<float>> outputStorage;
427 for (auto&& it : expectedOutputData)
428 {
429 std::vector<float> out(it.second.size());
430 outputStorage.emplace(it.first, out);
431 outputTensors.push_back({it.first,
432 Tensor(runtime->GetOutputTensorInfo(networkIdentifier, it.first),
433 outputStorage.at(it.first).data())});
434 }
435
436 // Does the inference.
437 runtime->EnqueueWorkload(networkIdentifier, inputTensors, outputTensors);
438
439 // Checks the results.
440 float tolerance = 0.000001f;
441 for (auto&& it : expectedOutputData)
442 {
443 std::vector<float> out = outputStorage.at(it.first);
444 for (unsigned int i = 0; i < out.size(); ++i)
445 {
446 BOOST_CHECK_MESSAGE(Compare<armnn::DataType::Float32>(it.second[i], out[i], tolerance) == true,
447 "Actual output: " << out[i] << ". Expected output:" << it.second[i]);
448
449 }
450 }
451}
452
453BOOST_AUTO_TEST_CASE(SplitteronXorYPaddingRequiredTest)
454{
455 using namespace armnn;
456
457 unsigned int splitAxis = 2;
458 unsigned int numSplit = 2;
459
460 const TensorShape& inputShape = { 1, 1, 4, 4 };
461 const armnn::TensorInfo intermediateInfo = armnn::TensorInfo({ 1, 1, 2, 4 }, armnn::DataType::Float32);
462 const std::vector<TensorShape> outputShapes{{ 1, 1, 2, 4 },
463 { 1, 1, 2, 4 }};
464
465 const float qScale = 1.0f;
466 const int32_t qOffset = 0;
467
468 // Creates structures for input & output.
469 std::vector<float> inputData{
470 9.0f, 27.0f, 18.0f, 36.0f,
471 18.0f, 9.0f, 18.0f, 9.0f,
472 27.0f, 18.0f, 9.0f, 27.0f,
473 9.0f, 27.0f, 9.0f, 18.0f,
474 };
475
476 std::vector<float> expectedOutput0{
477 7.0f, 11.0f, 13.0f, 9.0f,
478 7.0f, 11.0f, 13.0f, 9.0f
479 };
480
481 std::vector<float> expectedOutput1{
482 9.0f, 11.0f, 12.0f, 7.0f,
483 9.0f, 11.0f, 12.0f, 7.0f
484 };
485
486 // Builds up the structure of the network.
487 INetworkPtr net(INetwork::Create());
488
489 TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32, qScale, qOffset);
490
491 // Pooling
492 armnn::Pooling2dDescriptor descriptor;
493 descriptor.m_PoolType = armnn::PoolingAlgorithm::Average;
494 descriptor.m_PoolWidth = descriptor.m_PoolHeight = 3;
495 descriptor.m_StrideX = descriptor.m_StrideY = 1;
496 descriptor.m_PadLeft = 1;
497 descriptor.m_PadRight = 1;
498 descriptor.m_PadTop = 1;
499 descriptor.m_PadBottom = 1;
500 descriptor.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue;
501
502 // Splitter
503 std::vector<unsigned int> splitterDimSizes(inputShape.GetNumDimensions());
504
505 // Add current input shape to splitterDimSizes
506 for (unsigned int i = 0; i < inputShape.GetNumDimensions(); ++i)
507 {
508 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
509 }
510
511 if (splitterDimSizes[splitAxis] % numSplit != 0)
512 {
513 throw ParseException("Number of splits must evenly divide the dimension");
514 }
515
516 splitterDimSizes[splitAxis] /= numSplit;
517
518 SplitterDescriptor splitDesc(numSplit, inputShape.GetNumDimensions());
519
520 for (unsigned int g = 0; g < numSplit; ++g)
521 {
522 // Set the size of the views.
523 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
524 {
525 splitDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
526 }
527 splitDesc.SetViewOriginCoord(g, splitAxis, splitterDimSizes[splitAxis] * g);
528 }
529
530 IConnectableLayer* input = net->AddInputLayer(0, "input");
531 IConnectableLayer* pooling2d0 = net->AddPooling2dLayer(descriptor, "pooling2d_0");
532 IConnectableLayer* pooling2d1 = net->AddPooling2dLayer(descriptor, "pooling2d_1");
533 IConnectableLayer* splitter = net->AddSplitterLayer(splitDesc, "splitter");
534
535 // Connections
536 Connect(input, splitter, inputTensorInfo, 0, 0);
537 Connect(splitter, pooling2d0, intermediateInfo, 0, 0);
538 Connect(splitter, pooling2d1, intermediateInfo, 1, 0);
539
540 std::vector<IConnectableLayer*> pooling2dLayers{pooling2d0, pooling2d1};
541
542 for (unsigned int i = 0; i < outputShapes.size(); ++i)
543 {
544 TensorInfo outputTensorInfo(outputShapes[i], armnn::DataType::Float32, qScale, qOffset);
Matthew Sloyan171214c2020-09-09 09:07:37 +0100545 IConnectableLayer* output = net->AddOutputLayer(armnn::numeric_cast<LayerBindingId>(i));
Keith Davis3674f142020-08-16 23:44:15 +0100546 Connect(pooling2dLayers[i], output, outputTensorInfo, 0, 0);
547 }
548
549 std::map<int, std::vector<float>> inputTensorData = {{ 0,inputData }};
550 std::map<int, std::vector<float>> expectedOutputData = {{ 0, expectedOutput0 }, { 1, expectedOutput1 }};
551
552 armnn::IRuntime::CreationOptions options;
553 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
554
555 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
556 armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
557
Francis Murtagh3d2b4b22021-02-15 18:23:17 +0000558 const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
Keith Davis3674f142020-08-16 23:44:15 +0100559
560 // Load graph into runtime
561 armnn::NetworkId networkIdentifier;
562 runtime->LoadNetwork(networkIdentifier, std::move(optimizedNet));
563
564 // now check the concat how many sub-tensors it is using..
565 auto TraceSubTensorHandleAncestry = [](armnn::ITensorHandle* const subTensorHandle)
566 {
567 if (subTensorHandle && subTensorHandle->GetParent())
568 {
569 return true;
570 }
571 return false;
572 };
573
574 for (auto&& layer : theGraph)
575 {
576 if(layer->GetType() == armnn::LayerType::Pooling2d)
577 {
578 unsigned int numberOfSubTensors = 0;
579 for (unsigned int i = 0; i < layer->GetNumInputSlots(); ++i)
580 {
581 const armnn::OutputSlot* slot = layer->GetInputSlot(i).GetConnectedOutputSlot();
582 if (TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData()))
583 {
584 ++numberOfSubTensors;
585 }
586 }
587 // sub-tensors should be supported in this configuration
588 ARMNN_ASSERT(numberOfSubTensors == 0);
589 }
590 }
591
592 InputTensors inputTensors;
593 inputTensors.reserve(inputTensorData.size());
594 for (auto&& it : inputTensorData)
595 {
596 inputTensors.push_back({it.first,
597 ConstTensor(runtime->GetInputTensorInfo(networkIdentifier, it.first), it.second.data())});
598 }
599 OutputTensors outputTensors;
600 outputTensors.reserve(expectedOutputData.size());
601 std::map<int, std::vector<float>> outputStorage;
602 for (auto&& it : expectedOutputData)
603 {
604 std::vector<float> out(it.second.size());
605 outputStorage.emplace(it.first, out);
606 outputTensors.push_back({it.first,
607 Tensor(runtime->GetOutputTensorInfo(networkIdentifier, it.first),
608 outputStorage.at(it.first).data())});
609 }
610
611 // Does the inference.
612 runtime->EnqueueWorkload(networkIdentifier, inputTensors, outputTensors);
613
614 // Checks the results.
615 float tolerance = 0.000001f;
616 for (auto&& it : expectedOutputData)
617 {
618 std::vector<float> out = outputStorage.at(it.first);
619 for (unsigned int i = 0; i < out.size(); ++i)
620 {
621 BOOST_CHECK_MESSAGE(Compare<armnn::DataType::Float32>(it.second[i], out[i], tolerance) == true,
622 "Actual output: " << out[i] << ". Expected output:" << it.second[i]);
623
Sadik Armagan76615a52020-08-04 14:01:05 +0100624 }
625 }
626}
627
Narumol Prangnawaratb8d771a2020-08-14 11:51:12 +0100628BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryMemoryManaged)
629{
630 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
631 std::make_unique<arm_compute::Allocator>(),
632 BaseMemoryManager::MemoryAffinity::Offset);
633 NeonTensorHandleFactory handleFactory(memoryManager);
634 TensorInfo info({ 1, 1, 2, 1 }, DataType::Float32);
635
636 // create TensorHandle with memory managed
637 auto handle = handleFactory.CreateTensorHandle(info, true);
638 handle->Manage();
639 handle->Allocate();
640
641 memoryManager->Acquire();
642 {
643 float* buffer = reinterpret_cast<float*>(handle->Map());
644 BOOST_CHECK(buffer != nullptr); // Yields a valid pointer
645 buffer[0] = 1.5f;
646 buffer[1] = 2.5f;
647 BOOST_CHECK(buffer[0] == 1.5f); // Memory is writable and readable
648 BOOST_CHECK(buffer[1] == 2.5f); // Memory is writable and readable
649 }
650 memoryManager->Release();
651
652 memoryManager->Acquire();
653 {
654 float* buffer = reinterpret_cast<float*>(handle->Map());
655 BOOST_CHECK(buffer != nullptr); // Yields a valid pointer
656 buffer[0] = 3.5f;
657 buffer[1] = 4.5f;
658 BOOST_CHECK(buffer[0] == 3.5f); // Memory is writable and readable
659 BOOST_CHECK(buffer[1] == 4.5f); // Memory is writable and readable
660 }
661 memoryManager->Release();
662
663 float testPtr[2] = { 2.5f, 5.5f };
664 // Cannot import as import is disabled
Narumol Prangnawarata2493a02020-08-19 14:39:07 +0100665 BOOST_CHECK_THROW(handle->Import(static_cast<void*>(testPtr), MemorySource::Malloc), MemoryImportException);
Narumol Prangnawaratb8d771a2020-08-14 11:51:12 +0100666}
667
668BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryImport)
669{
670 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
671 std::make_unique<arm_compute::Allocator>(),
672 BaseMemoryManager::MemoryAffinity::Offset);
673 NeonTensorHandleFactory handleFactory(memoryManager);
674 TensorInfo info({ 1, 1, 2, 1 }, DataType::Float32);
675
676 // create TensorHandle without memory managed
677 auto handle = handleFactory.CreateTensorHandle(info, false);
678 handle->Manage();
679 handle->Allocate();
680 memoryManager->Acquire();
681
682 // No buffer allocated when import is enabled
683 BOOST_CHECK((PolymorphicDowncast<NeonTensorHandle*>(handle.get()))->GetTensor().buffer() == nullptr);
684
685 float testPtr[2] = { 2.5f, 5.5f };
686 // Correctly import
687 BOOST_CHECK(handle->Import(static_cast<void*>(testPtr), MemorySource::Malloc));
688 float* buffer = reinterpret_cast<float*>(handle->Map());
689 BOOST_CHECK(buffer != nullptr); // Yields a valid pointer after import
690 BOOST_CHECK(buffer == testPtr); // buffer is pointing to testPtr
691 // Memory is writable and readable with correct value
692 BOOST_CHECK(buffer[0] == 2.5f);
693 BOOST_CHECK(buffer[1] == 5.5f);
694 buffer[0] = 3.5f;
695 buffer[1] = 10.0f;
696 BOOST_CHECK(buffer[0] == 3.5f);
697 BOOST_CHECK(buffer[1] == 10.0f);
698 memoryManager->Release();
699}
700
Sadik Armaganab3bd4d2020-08-25 11:48:00 +0100701BOOST_AUTO_TEST_CASE(NeonTensorHandleSupportsInPlaceComputation)
702{
703 std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>();
704 NeonTensorHandleFactory handleFactory(memoryManager);
705
706 // NeonTensorHandleFactory supports InPlaceComputation
707 ARMNN_ASSERT(handleFactory.SupportsInPlaceComputation());
708}
709
Narumol Prangnawarat1a268962020-07-27 15:52:13 +0100710BOOST_AUTO_TEST_SUITE_END()