ArmNN
 22.02
OptimizerTests.cpp File Reference

Go to the source code of this file.

Functions

 TEST_SUITE ("Optimizer")
 

Function Documentation

◆ TEST_SUITE()

TEST_SUITE ( "Optimizer"  )

Definition at line 306 of file OptimizerTests.cpp.

References armnn::Activation, armnn::Addition, Graph::AddLayer(), ILayerSupport::anchors, ARMNN_ASSERT, armnn::AssignBackends(), armnn::Average, armnn::BackendRegistryInstance(), Layer::BackendSelectionHint(), Graph::begin(), ILayerSupport::beta, armnn::Bilinear, Graph::cbegin(), Graph::cend(), CheckSequence(), OutputSlot::Connect(), armnn::ConvertFp16ToFp32, armnn::ConvertFp32ToFp16, INetwork::Create(), ILayerSupport::descriptor, Graph::end(), armnn::Exclude, armnn::Float16, armnn::Float32, armnn::Floor, ILayerSupport::gamma, armnn::GetCapability(), TensorInfo::GetDataType(), Layer::GetDataType(), OptimizedNetworkImpl::GetGraph(), Layer::GetInputSlot(), armnn::GetLayerTypeAsCString(), Layer::GetNameStr(), Graph::GetNumLayers(), Layer::GetOutputHandler(), Layer::GetOutputSlot(), OutputSlot::GetTensorInfo(), IConnectableLayer::GetType(), Layer::GetType(), armnn::HasCapability(), armnn::IgnoreUnused(), Graph::InferTensorInfos(), armnn::info, armnn::Input, ILayerSupport::input1, armnn::InsertConvertFp16ToFp32LayersBefore(), armnn::InsertConvertFp32ToFp16LayersAfter(), OptimizationResult::IsOk(), armnn::Linear, DetectionPostProcessLayer::m_Anchors, Convolution2dDescriptor::m_BiasEnabled, DepthwiseConvolution2dDescriptor::m_BiasEnabled, Pooling2dDescriptor::m_DataLayout, Convolution2dDescriptor::m_DataLayout, DepthwiseConvolution2dDescriptor::m_DataLayout, BatchNormalizationDescriptor::m_DataLayout, ResizeDescriptor::m_DataLayout, ActivationDescriptor::m_Function, DetectionPostProcessDescriptor::m_MaxDetections, ResizeDescriptor::m_Method, Pooling2dDescriptor::m_PadBottom, Pooling2dDescriptor::m_PaddingMethod, Pooling2dDescriptor::m_PadLeft, Pooling2dDescriptor::m_PadRight, Pooling2dDescriptor::m_PadTop, Pooling2dDescriptor::m_PoolHeight, Pooling2dDescriptor::m_PoolType, Pooling2dDescriptor::m_PoolWidth, Pooling2dDescriptor::m_StrideX, Convolution2dDescriptor::m_StrideX, DepthwiseConvolution2dDescriptor::m_StrideX, Pooling2dDescriptor::m_StrideY, Convolution2dDescriptor::m_StrideY, DepthwiseConvolution2dDescriptor::m_StrideY, ResizeDescriptor::m_TargetHeight, ResizeDescriptor::m_TargetWidth, DepthwiseConvolution2dLayer::m_Weight, Convolution2dLayer::m_Weight, armnn::MakeOptimizations(), ILayerSupport::mean, armnn::NCHW, armnn::NHWC, armnn::Output, ILayerSupport::paramsInfo, Optimizer::Pass(), armnn::QAsymmU8, OutputHandler::SetTensorInfo(), OutputSlot::SetTensorInfo(), armnn::Signed32, and ILayerSupport::weights.

307 {
308 using namespace armnn::optimizations;
309 
310 TEST_CASE("LSTMValidateTensorShapesFromInputsCIFGDisabledTest")
311 {
312  Graph graph;
313 
314  //Helper function creates graph containing LSTM layer with required input and output layers
315  CreateLSTMLayerHelper(graph, false);
316 
317  //This function used to call ValidateShapesFromInputs();
318  CHECK_NOTHROW(graph.InferTensorInfos());
319 }
320 
321 TEST_CASE("LSTMValidateTensorShapesFromInputsCIFGEnabledTest")
322 {
323  Graph graph;
324 
325  //Helper function creates graph containing LSTM layer with required input and output layers
326  CreateLSTMLayerHelper(graph, true);
327 
328  //This function used to call ValidateShapesFromInputs();
329  CHECK_NOTHROW(graph.InferTensorInfos());
330 }
331 
332 TEST_CASE("InsertConvertersTest")
333 {
334  const armnn::TensorInfo info({ 1, 5, 2, 3 }, armnn::DataType::Float16);
335 
336  armnn::Graph graph;
337 
338  armnn::LayerBindingId inputId = 0;
339 
340  armnn::Layer* head = graph.AddLayer<armnn::OutputLayer>(0, "output");
341 
342  head = graph.InsertNewLayer<armnn::AdditionLayer>(head->GetInputSlot(0), "");
343  head->GetOutputHandler().SetTensorInfo(info);
344 
345  graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
346  ->GetOutputHandler().SetTensorInfo(info);
347 
348  head = graph.InsertNewLayer<armnn::FloorLayer>(head->GetInputSlot(0), "");
349  head->GetOutputHandler().SetTensorInfo(info);
350 
351  head = graph.InsertNewLayer<armnn::MemCopyLayer>(head->GetInputSlot(0), "");
352  head->GetOutputHandler().SetTensorInfo(info);
353 
354  graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(0), inputId++, "")
355  ->GetOutputHandler().SetTensorInfo(info);
356 
357  // Check graph layer sequence before inserting convert layers
358  CHECK(CheckSequence(graph.cbegin(),
359  graph.cend(),
360  &IsLayerOfType<armnn::InputLayer>,
361  &IsLayerOfType<armnn::InputLayer>,
362  &IsLayerOfType<armnn::MemCopyLayer>,
363  &IsLayerOfType<armnn::FloorLayer>,
364  &IsLayerOfType<armnn::AdditionLayer>,
365  &IsLayerOfType<armnn::OutputLayer>));
366 
367  // Check layers have Float16 DataType
368  for (auto& layer : graph)
369  {
370  if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
371  {
372  ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float16);
373  ARMNN_ASSERT(layer->GetDataType() == DataType::Float16);
374  }
375  }
376 
377  // Insert convert layers either side of unsupported layer
378  for (auto& layer : graph)
379  {
380  if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
381  {
383  InsertConvertFp32ToFp16LayersAfter(graph, *layer);
384  }
385  }
386 
387  // Check layers have correct DataType after inserting convert layers
388  for (auto& layer : graph)
389  {
390  if (layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
391  {
392  ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float32);
393  ARMNN_ASSERT(layer->GetDataType() == DataType::Float32);
394  }
395  else if (layer->GetType() == LayerType::ConvertFp16ToFp32)
396  {
397  ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float32);
398  ARMNN_ASSERT(layer->GetDataType() == DataType::Float16);
399  }
400  else if (layer->GetType() == LayerType::ConvertFp32ToFp16)
401  {
402  ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float16);
403  ARMNN_ASSERT(layer->GetDataType() == DataType::Float32);
404  }
405  }
406 
407  // Check sequence of layers after inserting convert layers
408  CHECK(CheckSequence(graph.cbegin(),
409  graph.cend(),
410  &IsLayerOfType<armnn::InputLayer>,
411  &IsLayerOfType<armnn::InputLayer>,
412  &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
413  &IsLayerOfType<armnn::MemCopyLayer>,
414  &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
415  &IsLayerOfType<armnn::FloorLayer>,
416  &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
417  &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
418  &IsLayerOfType<armnn::AdditionLayer>,
419  &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
420  &IsLayerOfType<armnn::OutputLayer>));
421 }
422 
423 void CreateConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
424  const unsigned int* weightsShape, const unsigned int* outputShape,
425  DataLayout dataLayout = DataLayout::NCHW)
426 {
427  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
428  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
429 
430  std::vector<float> weightsVector(90);
431  armnn::ConstTensor weights(
432  armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32, 0.0f, 0, true),
433  weightsVector);
434 
436  desc.m_BiasEnabled = false;
437  desc.m_StrideX = 1;
438  desc.m_StrideY = 1;
439  desc.m_DataLayout = dataLayout;
440 
441  Layer* input = graph.AddLayer<InputLayer>(0, "input");
442  input->GetOutputSlot().SetTensorInfo(inputInfo);
443 
444  Convolution2dLayer* layer = graph.AddLayer<Convolution2dLayer>(desc, "conv2d");
445  layer->m_Weight = std::make_unique<armnn::ScopedTensorHandle>(weights);
446  layer->GetOutputSlot().SetTensorInfo(outputInfo);
447 
448  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
449  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
450  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
451 }
452 
453 TEST_CASE("Conv2dValidateTensorShapesFromInputs")
454 {
455  Graph graph;
456  const unsigned int inputShape[] = { 1, 3, 8, 16 };
457  const unsigned int weightsShape[] = { 2, 3, 5, 3 };
458  const unsigned int outputShape[] = { 1, 2, 4, 14 };
459  CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
460 
461  CHECK_NOTHROW(graph.InferTensorInfos());
462 }
463 
464 TEST_CASE("Conv2dValidateTensorShapesFromInputsNhwc")
465 {
466  Graph graph;
467  const unsigned int inputShape[] = { 1, 8, 16, 3 };
468  const unsigned int weightsShape[] = { 2, 5, 3, 3 };
469  const unsigned int outputShape[] = { 1, 4, 14, 2 };
470  CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
471 
472  CHECK_NOTHROW(graph.InferTensorInfos());
473 }
474 
475 void CreateDepthwiseConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
476  const unsigned int* weightsShape, const unsigned int* outputShape,
477  DataLayout dataLayout = DataLayout::NCHW)
478 {
479  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
480  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
481 
482  std::vector<float> weightsVector(18);
483  armnn::ConstTensor weights(
484  armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32, 0.0f, 0, true),
485  weightsVector);
486 
488  desc.m_BiasEnabled = false;
489  desc.m_StrideX = 1;
490  desc.m_StrideY = 1;
491  desc.m_DataLayout = dataLayout;
492 
493  Layer* input = graph.AddLayer<InputLayer>(0, "input");
494  input->GetOutputSlot().SetTensorInfo(inputInfo);
495 
496  DepthwiseConvolution2dLayer* layer = graph.AddLayer<DepthwiseConvolution2dLayer>(desc, "depthwiseConv2d");
497  layer->m_Weight = std::make_unique<armnn::ScopedTensorHandle>(weights);
498  layer->GetOutputSlot().SetTensorInfo(outputInfo);
499 
500  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
501  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
502  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
503 }
504 
505 TEST_CASE("DepthwiseConv2dValidateTensorShapesFromInputs")
506 {
507  Graph graph;
508  const unsigned int inputShape[] = { 1, 2, 3, 3 };
509  const unsigned int weightsShape[] = { 1, 3, 3, 2 };
510  const unsigned int outputShape[] = { 1, 2, 1, 1 };
511  CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
512 
513  CHECK_NOTHROW(graph.InferTensorInfos());
514 }
515 
516 TEST_CASE("DepthwiseConv2dValidateTensorShapesFromInputsNhwc")
517 {
518  Graph graph;
519  const unsigned int inputShape[] = { 1, 3, 3, 2 };
520  const unsigned int weightsShape[] = { 1, 3, 3, 2 };
521  const unsigned int outputShape[] = { 1, 1, 1, 2 };
522  CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
523 
524  CHECK_NOTHROW(graph.InferTensorInfos());
525 }
526 
527 void CreatePooling2dGraph(Graph& graph, const unsigned int* inputShape, const unsigned int* outputShape,
528  DataLayout dataLayout = DataLayout::NCHW)
529 {
530  armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
531  armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
532 
533  Pooling2dDescriptor desc;
535  desc.m_PoolWidth = desc.m_PoolHeight = 100;
536  desc.m_StrideX = desc.m_StrideY = 5;
537  desc.m_PadLeft = 50;
538  desc.m_PadRight = 50;
539  desc.m_PadTop = 50;
540  desc.m_PadBottom = 50;
542  desc.m_DataLayout = dataLayout;
543 
544  Layer* input = graph.AddLayer<InputLayer>(0, "input");
545  input->GetOutputSlot().SetTensorInfo(inputInfo);
546 
547  Pooling2dLayer* layer = graph.AddLayer<Pooling2dLayer>(desc, "pooling2d");
548  layer->GetOutputSlot().SetTensorInfo(outputInfo);
549 
550  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
551  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
552  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
553 }
554 
555 TEST_CASE("Pooling2dValidateTensorShapesFromInputs")
556 {
557  Graph graph;
558  const unsigned int inputShape[] = { 5, 3, 52, 60 };
559  const unsigned int outputShape[] = { 5, 3, 11, 13 };
560  CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NCHW);
561 
562  CHECK_NOTHROW(graph.InferTensorInfos());
563 }
564 
565 TEST_CASE("Pooling2dValidateTensorShapesFromInputsNhwc")
566 {
567  Graph graph;
568  const unsigned int inputShape[] = { 5, 52, 60, 3 };
569  const unsigned int outputShape[] = { 5, 11, 13, 3 };
570  CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NHWC);
571 
572  CHECK_NOTHROW(graph.InferTensorInfos());
573 }
574 
575 void CreateResizeBilinearGraph(Graph& graph,
576  const unsigned int* inputShape,
577  const unsigned int* outputShape,
578  DataLayout dataLayout = DataLayout::NCHW)
579 {
580  TensorInfo inputInfo(4, inputShape, DataType::Float32);
581  TensorInfo outputInfo(4, outputShape, DataType::Float32);
582 
583  ResizeDescriptor desc;
585  desc.m_TargetHeight = 3;
586  desc.m_TargetWidth = 4;
587  desc.m_DataLayout = dataLayout;
588 
589  Layer* input = graph.AddLayer<InputLayer>(0, "input");
590  input->GetOutputSlot().SetTensorInfo(inputInfo);
591 
592  ResizeLayer* layer = graph.AddLayer<ResizeLayer>(desc, "resizeBilinear");
593  layer->GetOutputSlot().SetTensorInfo(outputInfo);
594 
595  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
596  input->GetOutputSlot().Connect(layer->GetInputSlot(0));
597  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
598 }
599 
600 TEST_CASE("ResizeBilinearValidateTensorShapesFromInputs")
601 {
602  Graph graph;
603  const unsigned int inputShape[] = { 1, 2, 4, 5 };
604  const unsigned int outputShape[] = { 1, 2, 3, 4 };
605  CreateResizeBilinearGraph(graph, inputShape, outputShape);
606 
607  CHECK_NOTHROW(graph.InferTensorInfos());
608 }
609 
610 TEST_CASE("ResizeBilinearValidateTensorShapesFromInputsNhwc")
611 {
612  Graph graph;
613  const unsigned int inputShape[] = { 1, 4, 5, 2 };
614  const unsigned int outputShape[] = { 1, 3, 4, 2 };
615  CreateResizeBilinearGraph(graph, inputShape, outputShape, DataLayout::NHWC);
616 
617  CHECK_NOTHROW(graph.InferTensorInfos());
618 }
619 
620 void CreateGatherGraph(Graph& graph,
621  const armnn::TensorInfo& paramsInfo,
622  const armnn::TensorInfo& indicesInfo,
623  const armnn::TensorInfo& outputInfo)
624 {
625  Layer* input0 = graph.AddLayer<InputLayer>(0, "params");
626  input0->GetOutputSlot().SetTensorInfo(paramsInfo);
627 
628  Layer* input1 = graph.AddLayer<InputLayer>(1, "indices");
629  input1->GetOutputSlot().SetTensorInfo(indicesInfo);
630 
631  GatherDescriptor descriptor;
632  GatherLayer* layer = graph.AddLayer<GatherLayer>(descriptor, "gather");
633  layer->GetOutputSlot().SetTensorInfo(outputInfo);
634 
635  Layer* output = graph.AddLayer<OutputLayer>(0, "output");
636  input0->GetOutputSlot().Connect(layer->GetInputSlot(0));
637  input1->GetOutputSlot().Connect(layer->GetInputSlot(1));
638  layer->GetOutputSlot().Connect(output->GetInputSlot(0));
639 }
640 
641 TEST_CASE("GatherValidateTensorShapesFromInputs")
642 {
643  Graph graph;
644  armnn::TensorInfo paramsInfo({10, 5}, DataType::Float32);
645  armnn::TensorInfo indicesInfo({3}, DataType::Signed32);
646  armnn::TensorInfo outputInfo({3, 5}, DataType::Float32);
647 
648  CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
649 
650  CHECK_NOTHROW(graph.InferTensorInfos());
651 }
652 
653 TEST_CASE("GatherValidateTensorShapesFromInputs1DParams")
654 {
655  Graph graph;
656  armnn::TensorInfo paramsInfo({8}, DataType::Float32);
657  armnn::TensorInfo indicesInfo({5}, DataType::Signed32);
658  armnn::TensorInfo outputInfo( {5}, DataType::Float32);
659 
660  CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
661 
662  CHECK_NOTHROW(graph.InferTensorInfos());
663 }
664 
665 TEST_CASE("GatherValidateTensorShapesFromInputsMultiDimIndices")
666 {
667  Graph graph;
668  armnn::TensorInfo paramsInfo({3, 2, 5}, DataType::Float32);
669  armnn::TensorInfo indicesInfo({2, 2}, DataType::Signed32);
670  armnn::TensorInfo outputInfo({2, 2, 2, 5}, DataType::Float32);
671 
672  CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
673 
674  CHECK_NOTHROW(graph.InferTensorInfos());
675 }
676 
677 TEST_CASE("DetectionPostProcessValidateTensorShapes")
678 {
679  Graph graph;
680  armnn::TensorInfo boxEncodingsInfo({1, 10, 4}, DataType::QAsymmU8);
681  armnn::TensorInfo scoresInfo({1, 10, 4}, DataType::QAsymmU8);
682  std::vector<uint8_t> anchorsVector(40);
683  armnn::ConstTensor anchors(armnn::TensorInfo({10, 4}, armnn::DataType::QAsymmU8, 0.0f, 0, true), anchorsVector);
684 
685  armnn::TensorInfo detectionBoxesInfo({1, 3, 4}, DataType::QAsymmU8);
686  armnn::TensorInfo detectionScoresInfo({1, 3}, DataType::QAsymmU8);
687  armnn::TensorInfo detectionClassesInfo({1, 3}, DataType::QAsymmU8);
688  armnn::TensorInfo numDetectionInfo({1}, DataType::QAsymmU8);
689 
690  Layer* input0 = graph.AddLayer<InputLayer>(0, "boxEncodings");
691  input0->GetOutputSlot().SetTensorInfo(boxEncodingsInfo);
692 
693  Layer* input1 = graph.AddLayer<InputLayer>(1, "score");
694  input1->GetOutputSlot().SetTensorInfo(scoresInfo);
695 
697  descriptor.m_MaxDetections = 3;
698 
699  DetectionPostProcessLayer* layer = graph.AddLayer<DetectionPostProcessLayer>(descriptor, "detectionPostProcess");
700  layer->m_Anchors = std::make_unique<armnn::ScopedTensorHandle>(anchors);
701  layer->GetOutputSlot(0).SetTensorInfo(detectionBoxesInfo);
702  layer->GetOutputSlot(1).SetTensorInfo(detectionScoresInfo);
703  layer->GetOutputSlot(2).SetTensorInfo(detectionClassesInfo);
704  layer->GetOutputSlot(3).SetTensorInfo(numDetectionInfo);
705 
706  input0->GetOutputSlot().Connect(layer->GetInputSlot(0));
707  input1->GetOutputSlot().Connect(layer->GetInputSlot(1));
708 
709  CHECK_NOTHROW(graph.InferTensorInfos());
710 }
711 
712 TEST_CASE("BackendCapabilityTest")
713 {
714  BackendId backendId = "MockBackend";
715 
716  armnn::BackendOptions::BackendOption nonConstWeights{"NonConstWeights", true};
717 
718  // MockBackend does not support the NonConstWeights capability
719  CHECK(!armnn::HasCapability(nonConstWeights, backendId));
720  CHECK(!armnn::HasCapability("NonConstWeights", backendId));
721 
722  // MockBackend does not support the AsyncExecution capability
723  CHECK(!armnn::GetCapability("AsyncExecution", backendId).has_value());
724 }
725 
726 TEST_CASE("BackendHintTest")
727 {
728  class TestBackendAssignment : public StrategyBase<NoThrowStrategy>
729  {
730  public:
731 
732  void ExecuteStrategy(const armnn::IConnectableLayer* layer,
733  const armnn::BaseDescriptor& descriptor,
734  const std::vector<armnn::ConstTensor>& constants,
735  const char* name,
736  const armnn::LayerBindingId id = 0) override
737  {
738  armnn::IgnoreUnused(descriptor, constants, id, name);
739  switch (layer->GetType())
740  {
742  {
743  auto inputLayer = PolymorphicDowncast<const InputLayer*>(layer);
744  const auto connectedLayerBackendId = inputLayer->GetOutputSlot(0).GetOwningLayer().GetBackendId();
745  CHECK((inputLayer->GetBackendId() == connectedLayerBackendId));
746  break;
747  }
749  {
750  auto outputLayer = PolymorphicDowncast<const OutputLayer*>(layer);
751  CHECK((outputLayer->GetBackendId() == "MockBackend"));
752  break;
753  }
755  {
756  auto activation = PolymorphicDowncast<const ActivationLayer*>(layer);
757  CHECK((activation->GetBackendId() == "CustomBackend"));
758  break;
759  }
760  default:
761  {
762  m_DefaultStrategy.Apply(GetLayerTypeAsCString(layer->GetType()));
763  }
764  }
765  }
766  };
767 
768  struct CustomPolicy
769  {
770  static const BackendId& GetIdStatic()
771  {
772  static BackendId id = "CustomBackend";
773  return id;
774  }
775  };
776 
777  struct MockPolicy
778  {
779  static const BackendId& GetIdStatic()
780  {
781  static BackendId id = "MockBackend";
782  return id;
783  }
784  };
785 
786  auto& backendRegistry = BackendRegistryInstance();
787 
788  backendRegistry.Register("MockBackend", []() { return std::make_unique<CustomAllocatorBackend<MockPolicy>>(); });
789 
790  backendRegistry.Register("CustomBackend",
791  []() { return std::make_unique<CustomAllocatorBackend<CustomPolicy>>(); });
792 
793  // Define the network
794  auto network = INetwork::Create();
797 
798  std::unique_ptr<Graph> graph = std::make_unique<Graph>();
799  auto input = graph->AddLayer<InputLayer>(0, "input");
800  auto act = graph->AddLayer<ActivationLayer>(desc, "activation");
801  auto output = graph->AddLayer<OutputLayer>(0, "output");
802 
803  BackendId customBackendId("CustomBackend");
804  act->BackendSelectionHint(customBackendId);
805 
806  input->GetOutputSlot(0).Connect(act->GetInputSlot(0));
807  act->GetOutputSlot(0).Connect(output->GetInputSlot(0));
808 
809  OptimizedNetworkImpl optNet(std::move(graph));
810 
811  // Get the optimized graph
812  Graph& optGraph = optNet.GetGraph();
813 
814  std::vector<BackendId> prefs{ "MockBackend", "CustomBackend" };
815 
816  BackendIdSet availableBackends = { "CustomBackend", "MockBackend" };
817  DeviceSpec spec(availableBackends);
818 
819  BackendSettings backendSettings(prefs, spec);
820 
821  // Assign an available backend to each layer
822  Graph::Iterator firstLayer = optGraph.begin();
823  Graph::Iterator lastLayer = optGraph.end();
824 
825  OptimizedNetworkImpl* optNetObjPtr = &optNet;
826  OptimizationResult res = AssignBackends(optNetObjPtr,
827  backendSettings,
828  firstLayer,
829  lastLayer,
830  EmptyOptional());
831 
832  CHECK(res.IsOk());
833 
834  TestBackendAssignment visitor;
835  for (auto it = firstLayer; it != lastLayer; ++it)
836  {
837  (*it)->ExecuteStrategy(visitor);
838  }
839  // Clean up the registry for the next test.
840  backendRegistry.Deregister("MockBackend");
841  backendRegistry.Deregister("CustomBackend");
842 }
843 
844 // Tests that OptimizeForExclusiveConnections works, fusing when needed, using BatchNorm fusing as example
845 TEST_CASE("OptimizeForExclusiveConnectionsFuseTest")
846 {
847  using namespace armnn;
848  // Define layers information
849  Convolution2dDescriptor convolution2dDescriptor;
850  convolution2dDescriptor.m_BiasEnabled = false;
851  convolution2dDescriptor.m_DataLayout = DataLayout::NHWC;
852  BatchNormalizationDescriptor batchNormDescriptor;
853  batchNormDescriptor.m_DataLayout = DataLayout::NHWC;
854 
855  const unsigned int inputDimensionSizes[] = { 1, 4, 4, 3 }; // NHWCin
856  const unsigned int weightsDimensionSizes[] = { 1, 2, 2, 3 }; // CoutHWCin
857  const unsigned int outputDimensionSizes[] = { 1, 3, 3, 1 }; // NHWCout
858  const unsigned int outputChannelSize[] = { outputDimensionSizes[3] }; // Cout
859 
860  TensorInfo inputInfo(4, inputDimensionSizes, DataType::Float32);
861  TensorInfo outputInfo(4, outputDimensionSizes, DataType::Float32);
862 
863  std::vector<float> weightsVector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
864  ConstTensor weights(TensorInfo(4, weightsDimensionSizes, DataType::Float32, 0.0f, 0, true), weightsVector);
865 
866  std::vector<float> betaVector = { 0.1f };
867  std::vector<float> gammaVector = { 0.5f };
868  std::vector<float> meanVector = { 0 };
869  std::vector<float> varianceVector = { 1 };
870  ConstTensor beta(TensorInfo(1, outputChannelSize, DataType::Float32, 0.0f, 0, true), betaVector);
871  ConstTensor gamma(TensorInfo(1, outputChannelSize, DataType::Float32, 0.0f, 0, true), gammaVector);
872  ConstTensor mean(TensorInfo(1, outputChannelSize, DataType::Float32, 0.0f, 0, true), meanVector);
873  ConstTensor variance(TensorInfo(1, outputChannelSize, DataType::Float32, 0.0f, 0, true), varianceVector);
874 
875  // Define the network
876  Graph graph;
877  auto input = graph.AddLayer<InputLayer>(0, "input");
878  auto conv = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "convolution");
879  auto batchNorm = graph.AddLayer<BatchNormalizationLayer>(batchNormDescriptor, "batchNorm");
880  auto output = graph.AddLayer<OutputLayer>(0, "output");
881 
882  // Set layer information
883  input->GetOutputSlot().SetTensorInfo(inputInfo);
884  conv->GetOutputSlot().SetTensorInfo(outputInfo);
885  batchNorm->GetOutputSlot().SetTensorInfo(outputInfo);
886  conv->m_Weight = std::make_unique<ScopedTensorHandle>(weights);
887  batchNorm->m_Beta = std::make_unique<ScopedTensorHandle>(beta);
888  batchNorm->m_Gamma = std::make_unique<ScopedTensorHandle>(gamma);
889  batchNorm->m_Mean = std::make_unique<ScopedTensorHandle>(mean);
890  batchNorm->m_Variance = std::make_unique<ScopedTensorHandle>(variance);
891  if (convolution2dDescriptor.m_BiasEnabled)
892  {
893  std::vector<float> biasVector = { 11 };
894  ConstTensor bias(TensorInfo(1, outputChannelSize, DataType::Float32, 0.0f, 0, true), biasVector);
895  conv->m_Bias = std::make_unique<ScopedTensorHandle>(bias);
896  }
897 
898  // Connect layers
899  input->GetOutputSlot(0).Connect(conv->GetInputSlot(0));
900  conv->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0));
901  batchNorm->GetOutputSlot(0).Connect(output->GetInputSlot(0));
902 
903  CHECK(4 == graph.GetNumLayers());
904  CHECK(CheckSequence(graph.cbegin(), graph.cend(),
905  &IsLayerOfType<InputLayer>,
906  &IsLayerOfType<Convolution2dLayer>,
907  &IsLayerOfType<BatchNormalizationLayer>,
908  &IsLayerOfType<OutputLayer>));
909 
910  // Optimize graph
912 
913  auto checkFusedConv2d = [](const armnn::Layer* const layer) -> bool {
914  return IsLayerOfType<armnn::Convolution2dLayer>(layer) &&
915  (layer->GetNameStr() == "fused-batchNorm-into-convolution");
916  };
917 
918  CHECK(3 == graph.GetNumLayers());
919  CHECK(CheckSequence(graph.cbegin(), graph.cend(),
920  &IsLayerOfType<InputLayer>,
921  checkFusedConv2d,
922  &IsLayerOfType<OutputLayer>));
923 }
924 
925 // Tests that OptimizeForExclusiveConnections works, not fusing when not needed, using BatchNorm fusing as example
926 TEST_CASE("OptimizeForExclusiveConnectionsWithoutFuseTest")
927 {
928  // Define the network
929  Graph graph;
930  Convolution2dDescriptor convolution2dDescriptor;
931  BatchNormalizationDescriptor batchNormDescriptor;
932 
933  auto input = graph.AddLayer<InputLayer>(0, "input");
934  auto conv = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "convolution");
935  auto batchNorm = graph.AddLayer<BatchNormalizationLayer>(batchNormDescriptor, "batchNorm");
936  auto output = graph.AddLayer<OutputLayer>(0, "output");
937  auto output2 = graph.AddLayer<OutputLayer>(1, "output2");
938 
939  // Connect layers
940  input->GetOutputSlot(0).Connect(conv->GetInputSlot(0));
941  conv->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0));
942  batchNorm->GetOutputSlot(0).Connect(output->GetInputSlot(0));
943  conv->GetOutputSlot(0).Connect(output2->GetInputSlot(0));
944 
945  CHECK(5 == graph.GetNumLayers());
946  CHECK(CheckSequence(graph.cbegin(), graph.cend(),
947  &IsLayerOfType<armnn::InputLayer>,
948  &IsLayerOfType<armnn::Convolution2dLayer>,
949  &IsLayerOfType<armnn::BatchNormalizationLayer>,
950  &IsLayerOfType<armnn::OutputLayer>,
951  &IsLayerOfType<armnn::OutputLayer>));
952  // Optimize graph
954 
955  CHECK(5 == graph.GetNumLayers());
956  CHECK(CheckSequence(graph.cbegin(), graph.cend(),
957  &IsLayerOfType<armnn::InputLayer>,
958  &IsLayerOfType<armnn::Convolution2dLayer>,
959  &IsLayerOfType<armnn::BatchNormalizationLayer>,
960  &IsLayerOfType<armnn::OutputLayer>,
961  &IsLayerOfType<armnn::OutputLayer>));
962 }
963 } // Optimizer TestSuite
Iterator begin()
Returns iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:167
bool m_BiasEnabled
Enable/disable bias.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
OptimizationResult AssignBackends(OptimizedNetworkImpl *optNetObjPtr, BackendSettings &backendSettings, Graph::Iterator &firstLayer, Graph::Iterator &lastLayer, Optional< std::vector< std::string > &> errMessages)
Definition: Network.cpp:1034
bool HasCapability(const std::string &name, const BackendCapabilities &capabilities)
Convenience function to check if a capability exists in a BackendCapabilites struct.
This layer represents a batch normalization operation.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:66
uint32_t m_PadBottom
Padding bottom value in the height dimension.
std::vector< ConvertFp32ToFp16Layer * > InsertConvertFp32ToFp16LayersAfter(Graph &graph, Layer &layer)
bool m_BiasEnabled
Enable/disable bias.
DataLayout
Definition: Types.hpp:49
std::vector< ConvertFp16ToFp32Layer * > InsertConvertFp16ToFp32LayersBefore(Graph &graph, Layer &layer, bool expectCorrectInputType)
uint32_t m_PadLeft
Padding left value in the width dimension.
Optimizer::Optimizations MakeOptimizations(Args &&... args)
Definition: Optimizer.hpp:43
bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
Definition: TestUtils.hpp:21
std::unordered_set< BackendId > BackendIdSet
Definition: BackendId.hpp:193
void BackendSelectionHint(Optional< BackendId > backend) final
Provide a hint for the optimizer as to which backend to prefer for this layer.
Definition: Layer.hpp:335
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
Optional< const BackendOptions::BackendOption > GetCapability(const std::string &backendCapabilityName, const BackendCapabilities &capabilities)
Returns a BackendCapability if the backend lists the capability The BackendCapability must then be in...
This layer represents a depthwise convolution 2d operation.
LayerT * AddLayer(Args &&... args)
Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
Definition: Graph.hpp:420
uint32_t m_PoolWidth
Pooling width value.
ConstIterator cbegin() const
Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:177
A Convolution2dDescriptor for the Convolution2dLayer.
int Connect(InputSlot &destination)
Definition: Layer.cpp:86
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
static void Pass(Graph &graph, const Optimizations &optimizations)
Definition: Optimizer.cpp:16
The padding fields don&#39;t count and are ignored.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
OptimizeForExclusiveConnection< Convolution2dLayer, BatchNormalizationLayer, FuseBatchNorm< Convolution2dLayer, armnn::DataType::Float32 > > FuseBatchNormIntoConvolution2DFloat32
std::shared_ptr< ConstTensorHandle > m_Weight
A unique pointer to store Weight values.
This layer represents an activation operation with the specified activation function.
BackendRegistry & BackendRegistryInstance()
uint32_t m_PadTop
Padding top value in the height dimension.
This layer represents a detection postprocess operator.
Copyright (c) 2021 ARM Limited and Contributors.
void IgnoreUnused(Ts &&...)
LayerList::const_iterator Iterator
Definition: Graph.hpp:53
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:277
A ResizeBilinearDescriptor for the ResizeBilinearLayer.
Base class for all descriptors.
Definition: Descriptors.hpp:22
Strategy base class with empty implementations.
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_MaxDetections
Maximum numbers of detections.
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:321
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: OutputLayer.hpp:13
This layer represents a Gather operator.
Definition: GatherLayer.hpp:14
uint32_t m_PadRight
Padding right value in the width dimension.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
uint32_t m_TargetWidth
Target width value.
A GatherDescriptor for the GatherLayer.
This layer represents a memory copy operation.
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:36
This layer represents a floor operation.
Definition: FloorLayer.hpp:13
uint32_t m_TargetHeight
Target height value.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
virtual LayerType GetType() const =0
Returns the armnn::LayerType of this layer.
This layer represents a pooling 2d operation.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
This layer represents an addition operation.
void SetTensorInfo(const TensorInfo &tensorInfo)
Sets the TensorInfo used by this output handler.
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
void InferTensorInfos()
Definition: Graph.cpp:560
std::shared_ptr< ConstTensorHandle > m_Weight
A unique pointer to store Weight values.
const OutputHandler & GetOutputHandler(unsigned int i=0) const
Definition: Layer.hpp:230
A layer user-provided data can be bound to (e.g. inputs, outputs).
Definition: InputLayer.hpp:13
Iterator end()
Returns iterator pointing to the end of the list. Lowercase for range-based for loops.
Definition: Graph.hpp:169
void SetTensorInfo(const TensorInfo &tensorInfo) override
Definition: Layer.cpp:61
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
Definition: Layer.hpp:323
ConstIterator cend() const
Returns const iterator pointing to the end of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:179
This layer represents a convolution 2d operation.
A Pooling2dDescriptor for the Pooling2dLayer.
size_t GetNumLayers() const
Definition: Graph.hpp:196
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::shared_ptr< ConstTensorHandle > m_Anchors
A unique pointer to store Anchor values.
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:492
const char * GetLayerTypeAsCString(LayerType type)
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square, Elu).
Definition: Descriptors.hpp:59
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
A BatchNormalizationDescriptor for the BatchNormalizationLayer.
This layer represents a resize operation.
Definition: ResizeLayer.hpp:13