ArmNN
 21.02
NeonTensorHandleTests.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include <Graph.hpp>
6 #include <Network.hpp>
7 
10 
13 
14 #include <test/GraphUtils.hpp>
15 #include <arm_compute/runtime/Allocator.h>
17 
18 #include <boost/test/unit_test.hpp>
19 #include <armnn/utility/Assert.hpp>
20 
21 BOOST_AUTO_TEST_SUITE(NeonTensorHandleTests)
22 using namespace armnn;
23 
24 BOOST_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,
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 
57 BOOST_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,
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 
91 BOOST_AUTO_TEST_CASE(ConcatOnXorYSubTensorsNoPaddingRequiredTest)
92 {
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 
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 
127 
128  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
129  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
130 
131  const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
132 
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
161  ARMNN_ASSERT(numberOfSubTensors > 0);
162  }
163  }
164 }
165 
166 BOOST_AUTO_TEST_CASE(ConcatonXorYPaddingRequiredTest)
167 {
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;
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 
210 
211  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
212  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
213 
214  const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
215 
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 
249 BOOST_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.
323 
324  TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32, qScale, qOffset);
325 
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);
370  IConnectableLayer* output = net->AddOutputLayer(armnn::numeric_cast<LayerBindingId>(i));
371  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 
379 
380  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
381  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
382 
383  const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
384 
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 
453 BOOST_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.
488 
489  TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32, qScale, qOffset);
490 
491  // Pooling
492  armnn::Pooling2dDescriptor descriptor;
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;
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);
545  IConnectableLayer* output = net->AddOutputLayer(armnn::numeric_cast<LayerBindingId>(i));
546  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 
554 
555  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
556  armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
557 
558  const armnn::Graph& theGraph = GetGraphForTesting(optimizedNet.get());
559 
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 
624  }
625  }
626 }
627 
628 BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryMemoryManaged)
629 {
630  std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
631  std::make_unique<arm_compute::Allocator>(),
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
665  BOOST_CHECK_THROW(handle->Import(static_cast<void*>(testPtr), MemorySource::Malloc), MemoryImportException);
666 }
667 
668 BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryImport)
669 {
670  std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
671  std::make_unique<arm_compute::Allocator>(),
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 
701 BOOST_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 
BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:37
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:62
uint32_t m_PadBottom
Padding bottom value in the height dimension.
const TensorShape & GetShape() const
Definition: Tensor.hpp:187
uint32_t m_PadLeft
Padding left value in the width dimension.
std::unique_ptr< ITensorHandle > CreateTensorHandle(const TensorInfo &tensorInfo) const override
uint32_t m_PoolWidth
Pooling width value.
float m_Beta
Exponentiation value.
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:26
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
uint32_t m_PadTop
Padding top value in the height dimension.
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:340
int NetworkId
Definition: IRuntime.hpp:20
Copyright (c) 2021 ARM Limited and Contributors.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:306
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadRight
Padding right value in the width dimension.
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1502
Status SetViewSize(uint32_t view, uint32_t coord, uint32_t value)
Set the size of the views.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:314
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:341
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:174
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
BOOST_AUTO_TEST_CASE(CheckConvolution2dLayer)
ITensorHandle * GetData() const
Gets the allocated tensor memory.
Graph & GetGraphForTesting(IOptimizedNetwork *optNet)
Definition: TestUtils.cpp:25
BOOST_AUTO_TEST_SUITE_END()
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:98
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
The padding fields count, but are ignored.
CPU Execution: NEON: ArmCompute.
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
const OutputHandler & GetOutputHandler() const
Definition: Layer.hpp:119
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
void Connect(armnn::IConnectableLayer *from, armnn::IConnectableLayer *to, const armnn::TensorInfo &tensorInfo, unsigned int fromIndex, unsigned int toIndex)
Definition: TestUtils.cpp:12
OriginsDescriptor CreateDescriptorForConcatenation(TensorShapeIt first, TensorShapeIt last, unsigned int concatenationDimension)
Convenience template to create an OriginsDescriptor to use when creating a ConcatLayer for performing...
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:173
virtual int Connect(IInputSlot &destination)=0
A Pooling2dDescriptor for the Pooling2dLayer.
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:510
A SoftmaxDescriptor for the SoftmaxLayer.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.