// // Copyright © 2020 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once #include #include #include namespace armnn { namespace { // // this helper only works if all layers where the inputs connect to are not selected // SubgraphView::IInputSlots CreateIInputsFrom(const std::vector& layers) { SubgraphView::IInputSlots result; for (auto&& layer : layers) { for (unsigned int i = 0 ; i < layer->GetNumInputSlots(); ++i) { result.push_back(&(layer->GetInputSlot(i))); } } return result; } // // this helper only works if all layers where the outputs connect to are not selected // SubgraphView::IOutputSlots CreateIOutputsFrom(const std::vector& layers) { SubgraphView::IOutputSlots result; for (auto &&layer: layers) { for (unsigned int i = 0; i < layer->GetNumOutputSlots(); ++i) { result.push_back(&(layer->GetOutputSlot(i))); } } return result; } bool checkDataTypeInputandOutput(const Layer& layer) { auto inputInfo = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); auto outputInfo = layer.GetOutputSlot(0).GetTensorInfo(); bool sameDataType = (inputInfo.GetDataType() == outputInfo.GetDataType()); // Check is same quantization info (same scale and offset) if (sameDataType) { if (IsQuantizedType(inputInfo.GetDataType())) { bool sameScale = (inputInfo.GetQuantizationScale() == outputInfo.GetQuantizationScale()); bool sameOffset = (inputInfo.GetQuantizationOffset() == outputInfo.GetQuantizationOffset()); return (sameScale && sameOffset); } else { return true; } } else { return false; } } } // namespace inline void ReportUntouchedLayers(OptimizationViews& optimizationViews, std::map untouched) { std::vector untouchedVector; for (const auto& pair : untouched) { Layer* layer = pair.second; SubgraphView subgraphView({layer}, CreateIInputsFrom({layer}), CreateIOutputsFrom({layer})); optimizationViews.AddUntouchedSubgraph(std::move(subgraphView)); } } template LayerType* FuseLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, LayerType* replacementLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc) { replacementLayer->SetAdditionalInfoForObject( std::make_shared(activationDesc)); SubgraphView substitutionSubgraph({baseLayer, activationLayer}, CreateIInputsFrom({baseLayer}), CreateIOutputsFrom({activationLayer})); SubgraphView replacementSubgraph(replacementLayer); optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph}); return replacementLayer; } template LayerType* FuseAdditionLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddAdditionLayer(name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); return replacementLayer; } template LayerType* FuseSubtractionLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddSubtractionLayer(name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); return replacementLayer; } template LayerType* FuseDivisionLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddDivisionLayer(name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); return replacementLayer; } template LayerType* FuseMultiplicationLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddMultiplicationLayer(name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); return replacementLayer; } template LayerType* FuseBatchNormalizationLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddBatchNormalizationLayer(baseLayer->GetParameters(), ConstTensor(), ConstTensor(), ConstTensor(), ConstTensor(), name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); SubgraphView substitutionSubgraph({baseLayer, activationLayer}, CreateIInputsFrom({baseLayer}), CreateIOutputsFrom({activationLayer})); SubgraphView replacementSubgraph(replacementLayer); return replacementLayer; } template LayerType* FuseConvolution2dLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { std::shared_ptr weightHandle = baseLayer->m_Weight; TensorInfo weightInfo = weightHandle->GetTensorInfo(); std::shared_ptr biasHandle = baseLayer->m_Bias; ConstTensor biasTensor; if (!biasHandle) { biasTensor = ConstTensor(); } else { biasTensor = ConstTensor(biasHandle->GetTensorInfo(), biasHandle->Map(true)); } IConnectableLayer* replacement = optimizationViews.GetINetwork()-> AddConvolution2dLayer(baseLayer->GetParameters(), ConstTensor(weightInfo, weightHandle->Map(true)), Optional(biasTensor), name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); return replacementLayer; } template LayerType* FuseDepthwiseConvolution2dLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { IConnectableLayer* replacement = optimizationViews.GetINetwork()-> AddDepthwiseConvolution2dLayer(baseLayer->GetParameters(), name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); replacementLayer->m_Weight = std::move(baseLayer->m_Weight); replacementLayer->m_Bias = std::move(baseLayer->m_Bias); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); return replacementLayer; } template LayerType* FuseFullyConnectedLayer(OptimizationViews& optimizationViews, LayerType* baseLayer, ActivationLayer* activationLayer, ActivationDescriptor& activationDesc, std::string name) { IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddFullyConnectedLayer(baseLayer->GetParameters(), name.c_str()); LayerType* replacementLayer = PolymorphicDowncast(replacement); FuseLayer(optimizationViews, baseLayer, replacementLayer, activationLayer, activationDesc); replacementLayer->m_Weight = std::move(baseLayer->m_Weight); replacementLayer->m_Bias = std::move(baseLayer->m_Bias); return replacementLayer; } // // If reduce layer has multiple axes, add new layer for each axis to simulate the same behaviour // as currently only one axis is supported. // template std::vector ChainReduceLayers(OptimizationViews& optimizationViews, LayerType* baseLayer, ReduceDescriptor& desc) { // Vector of new chained layers, used for substitution. std::vector layers; // Vector of axes so each layer is reshaped correctly. std::vector axes; unsigned int recalulatedAxis = 0; for (unsigned int i = 0; i != desc.m_vAxis.size(); ++i) { // Get TensorInfo from base layer and reduce shape using axis. TensorInfo layerInfo = baseLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo(); axes.emplace_back(desc.m_vAxis[i]); const TensorInfo& reducedTensorInfo = ComputeReductionTensorShape(layerInfo, axes, desc.m_KeepDims); // Create a vector for the single axis to be assigned to the descriptor. // Update axis if keepDims is set reduce layers correctly. std::vector singleAxis(1, desc.m_vAxis[i] - recalulatedAxis); // Create a descriptor and assign single axis. ReduceDescriptor newReduceDescriptor = baseLayer->GetParameters(); newReduceDescriptor.m_vAxis.assign(singleAxis.begin(), singleAxis.end()); // Add new layer to graph. std::string layerName = "reduce_layer_" + std::to_string(i); Layer* replacementLayer = PolymorphicDowncast( optimizationViews.GetINetwork()->AddReduceLayer(newReduceDescriptor, layerName.c_str())); // Connect previous layer with new layer. // The first and last layer will be connected when the subgraph is replaced. if (!layers.empty()) { layers[i - 1]->GetOutputSlot(0).Connect(replacementLayer->GetInputSlot(0)); } // Set updated tensorInfo for new layer. replacementLayer->GetOutputSlot(0).SetTensorInfo(reducedTensorInfo); if (!desc.m_KeepDims) { recalulatedAxis++; } layers.emplace_back(replacementLayer); } // Check if the TensorInfo from the last layer equals the inferred output from the original layer. ARMNN_ASSERT(baseLayer->GetOutputSlot(0).GetTensorInfo() == PolymorphicDowncast(layers.back())->GetOutputSlot().GetTensorInfo()); return layers; } // // Substitute baseLayer with new subgraph // template void ReplaceLayers(OptimizationViews& optimizationViews, LayerType* baseLayer, std::vector& layers) { std::list replacementLayers(layers.begin(), layers.end()); SubgraphView substitutionSubgraph(baseLayer); SubgraphView replacementSubgraph(std::move(replacementLayers), CreateIInputsFrom({replacementLayers.front()}), CreateIOutputsFrom({replacementLayers.back()})); optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph}); } } // namespace armnn