ArmNN
 20.08
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 
12 
13 #include <test/GraphUtils.hpp>
14 #include <arm_compute/runtime/Allocator.h>
16 
17 #include <boost/test/unit_test.hpp>
18 #include <armnn/utility/Assert.hpp>
19 
20 BOOST_AUTO_TEST_SUITE(NeonTensorHandleTests)
21 using namespace armnn;
22 
23 BOOST_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,
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 
56 BOOST_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,
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 
90 BOOST_AUTO_TEST_CASE(ConcatOnXorYSubTensorsNoPaddingRequiredTest)
91 {
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 
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 
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
160  ARMNN_ASSERT(numberOfSubTensors > 0);
161  }
162  }
163 }
164 
165 BOOST_AUTO_TEST_CASE(ConcatonXorYPaddingRequiredTest)
166 {
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;
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 
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 
248 BOOST_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.
322 
323  TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32, qScale, qOffset);
324 
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 
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 
452 BOOST_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.
487 
488  TensorInfo inputTensorInfo(inputShape, armnn::DataType::Float32, qScale, qOffset);
489 
490  // Pooling
491  armnn::Pooling2dDescriptor descriptor;
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;
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 
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 
623  }
624  }
625 }
626 
627 BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryMemoryManaged)
628 {
629  std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
630  std::make_unique<arm_compute::Allocator>(),
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_THROW(handle->Import(static_cast<void*>(testPtr), MemorySource::Malloc), MemoryImportException);
665 }
666 
667 BOOST_AUTO_TEST_CASE(NeonTensorHandleFactoryImport)
668 {
669  std::shared_ptr<NeonMemoryManager> memoryManager = std::make_shared<NeonMemoryManager>(
670  std::make_unique<arm_compute::Allocator>(),
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 
BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:32
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:61
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:25
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:324
int NetworkId
Definition: IRuntime.hpp:20
Copyright (c) 2020 ARM Limited.
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:290
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:1014
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:298
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:325
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:593
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
BOOST_AUTO_TEST_CASE(CheckConvolution2dLayer)
ITensorHandle * GetData() const
Gets the allocated tensor memory.
BOOST_AUTO_TEST_SUITE_END()
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:90
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:175
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:101
virtual int Connect(IInputSlot &destination)=0
A Pooling2dDescriptor for the Pooling2dLayer.
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:50
A SoftmaxDescriptor for the SoftmaxLayer.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.