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