blob: 9b889141be4df1c2120af70c3d0f7d8e4de1f823 [file] [log] [blame]
//
// Copyright © 2020-2024 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//
#pragma once
#include <armnn/backends/OptimizationViews.hpp>
#include <aclCommon/ArmComputeUtils.hpp>
#include <backendsCommon/SubgraphUtils.hpp>
namespace armnn
{
namespace
{
//
// this helper only works if all layers where the inputs connect to are not selected
//
bool checkDataTypeInputandOutput(const Layer& layer)
{
auto inputInfo = layer.GetInputSlot(0).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
template<typename LayerType>
LayerType* FuseLayer(OptimizationViews& optimizationViews,
LayerType* baseLayer,
LayerType* replacementLayer,
ActivationLayer* activationLayer,
ActivationDescriptor& activationDesc)
{
replacementLayer->SetAdditionalInfoForObject(
std::make_shared<ActivationDescriptor>(activationDesc));
SubgraphView substitutionSubgraph({baseLayer, activationLayer},
CreateIInputsFrom({baseLayer}),
CreateIOutputsFrom({activationLayer}));
SubgraphView replacementSubgraph(replacementLayer);
optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph});
return replacementLayer;
}
template<typename LayerType>
LayerType* FuseAdditionLayer(OptimizationViews& optimizationViews,
LayerType* baseLayer,
ActivationLayer* activationLayer,
ActivationDescriptor& activationDesc,
std::string name)
{
ARMNN_NO_DEPRECATE_WARN_BEGIN
IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddAdditionLayer(name.c_str());
ARMNN_NO_DEPRECATE_WARN_END
LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
return replacementLayer;
}
template<typename LayerType>
LayerType* FuseSubtractionLayer(OptimizationViews& optimizationViews,
LayerType* baseLayer,
ActivationLayer* activationLayer,
ActivationDescriptor& activationDesc,
std::string name)
{
ARMNN_NO_DEPRECATE_WARN_BEGIN
IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddSubtractionLayer(name.c_str());
ARMNN_NO_DEPRECATE_WARN_END
LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
return replacementLayer;
}
template<typename LayerType>
LayerType* FuseDivisionLayer(OptimizationViews& optimizationViews,
LayerType* baseLayer,
ActivationLayer* activationLayer,
ActivationDescriptor& activationDesc,
std::string name)
{
ARMNN_NO_DEPRECATE_WARN_BEGIN
IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddDivisionLayer(name.c_str());
ARMNN_NO_DEPRECATE_WARN_END
LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
return replacementLayer;
}
template<typename LayerType>
LayerType* FuseMultiplicationLayer(OptimizationViews& optimizationViews,
LayerType* baseLayer,
ActivationLayer* activationLayer,
ActivationDescriptor& activationDesc,
std::string name)
{
ARMNN_NO_DEPRECATE_WARN_BEGIN
IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddMultiplicationLayer(name.c_str());
ARMNN_NO_DEPRECATE_WARN_END
LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
return replacementLayer;
}
template<typename LayerType>
LayerType* FuseElementwiseBinaryLayer(OptimizationViews& optimizationViews,
LayerType* baseLayer,
ActivationLayer* activationLayer,
ActivationDescriptor& activationDesc,
BinaryOperation operation,
std::string name)
{
IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddElementwiseBinaryLayer(operation,
name.c_str());
LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
return replacementLayer;
}
template<typename LayerType>
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<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
SubgraphView substitutionSubgraph({baseLayer, activationLayer},
CreateIInputsFrom({baseLayer}),
CreateIOutputsFrom({activationLayer}));
SubgraphView replacementSubgraph(replacementLayer);
return replacementLayer;
}
template<typename LayerType>
LayerType* FuseConvolution2dLayer(OptimizationViews& optimizationViews,
LayerType* baseLayer,
ActivationLayer* activationLayer,
ActivationDescriptor& activationDesc,
std::string name)
{
IConnectableLayer* replacement = optimizationViews.GetINetwork()
->AddConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
return replacementLayer;
}
template<typename LayerType>
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<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
return replacementLayer;
}
template<typename LayerType>
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<LayerType*>(replacement);
FuseLayer(optimizationViews,
baseLayer,
replacementLayer,
activationLayer,
activationDesc);
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<typename LayerType>
std::vector<IConnectableLayer*> ChainReduceLayers(OptimizationViews& optimizationViews,
LayerType* baseLayer,
ReduceDescriptor& desc)
{
// Vector of new chained layers, used for substitution.
std::vector<IConnectableLayer*> layers;
// Vector of axes so each layer is reshaped correctly.
std::vector<uint32_t> 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<uint32_t> 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<Layer*>(
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);
}
return layers;
}
//
// Substitute baseLayer with new subgraph
//
template<typename LayerType>
void ReplaceLayers(OptimizationViews& optimizationViews,
LayerType* baseLayer,
std::vector<IConnectableLayer*>& layers)
{
std::list<IConnectableLayer*> replacementLayers(layers.begin(), layers.end());
SubgraphView substitutionSubgraph(baseLayer);
SubgraphView replacementSubgraph(std::move(replacementLayers),
CreateIInputsFrom({replacementLayers.front()}),
CreateIOutputsFrom({replacementLayers.back()}));
optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph});
}
//
// Substitute a multi-layer subgraph with one new layer
//
template<typename LayerType>
void ReplaceMultipleLayers(OptimizationViews& optimizationViews,
std::vector<IConnectableLayer*>& originalLayers,
LayerType* baseLayer,
const std::vector<SlotList> inputLayersSlotLists,
const std::vector<SlotList> outputLayersSlotLists)
{
std::list<IConnectableLayer*> originalLayerList(originalLayers.begin(), originalLayers.end());
SubgraphView substitutionSubgraph(
std::move(originalLayerList),
CreateIInputsFromSlotLists<armnn::IConnectableLayer>(originalLayers, inputLayersSlotLists),
CreateIOutputsFromSlotLists<armnn::IConnectableLayer>(originalLayers, outputLayersSlotLists));
SubgraphView replacementSubgraph(baseLayer);
optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph});
}
} // namespace armnn