ArmNN
 22.11
ArmComputeSubgraphUtils.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #pragma once
7 
10 
13 
14 namespace armnn
15 {
16 
17 namespace
18 {
19 
20 //
21 // this helper only works if all layers where the inputs connect to are not selected
22 //
23 
24 bool checkDataTypeInputandOutput(const Layer& layer)
25 {
26  auto inputInfo = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
27  auto outputInfo = layer.GetOutputSlot(0).GetTensorInfo();
28  bool sameDataType = (inputInfo.GetDataType() == outputInfo.GetDataType());
29 
30  // Check is same quantization info (same scale and offset)
31  if (sameDataType)
32  {
33  if (IsQuantizedType(inputInfo.GetDataType()))
34  {
35  bool sameScale = (inputInfo.GetQuantizationScale() == outputInfo.GetQuantizationScale());
36  bool sameOffset = (inputInfo.GetQuantizationOffset() == outputInfo.GetQuantizationOffset());
37 
38  return (sameScale && sameOffset);
39  }
40  else
41  {
42  return true;
43  }
44  }
45  else
46  {
47  return false;
48  }
49 }
50 
51 } // namespace
52 
53 template<typename LayerType>
55  LayerType* baseLayer,
56  LayerType* replacementLayer,
57  ActivationLayer* activationLayer,
58  ActivationDescriptor& activationDesc)
59 {
60  replacementLayer->SetAdditionalInfoForObject(
61  std::make_shared<ActivationDescriptor>(activationDesc));
62 
63  SubgraphView substitutionSubgraph({baseLayer, activationLayer},
64  CreateIInputsFrom({baseLayer}),
65  CreateIOutputsFrom({activationLayer}));
66  SubgraphView replacementSubgraph(replacementLayer);
67 
68  optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph});
69 
70  return replacementLayer;
71 }
72 
73 template<typename LayerType>
75  LayerType* baseLayer,
76  ActivationLayer* activationLayer,
77  ActivationDescriptor& activationDesc,
78  std::string name)
79 {
80  IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddAdditionLayer(name.c_str());
81  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
82 
83  FuseLayer(optimizationViews,
84  baseLayer,
85  replacementLayer,
86  activationLayer,
87  activationDesc);
88 
89  return replacementLayer;
90 }
91 
92 template<typename LayerType>
94  LayerType* baseLayer,
95  ActivationLayer* activationLayer,
96  ActivationDescriptor& activationDesc,
97  std::string name)
98 {
99  IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddSubtractionLayer(name.c_str());
100  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
101 
102  FuseLayer(optimizationViews,
103  baseLayer,
104  replacementLayer,
105  activationLayer,
106  activationDesc);
107 
108  return replacementLayer;
109 }
110 
111 template<typename LayerType>
113  LayerType* baseLayer,
114  ActivationLayer* activationLayer,
115  ActivationDescriptor& activationDesc,
116  std::string name)
117 {
118  IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddDivisionLayer(name.c_str());
119  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
120 
121  FuseLayer(optimizationViews,
122  baseLayer,
123  replacementLayer,
124  activationLayer,
125  activationDesc);
126 
127  return replacementLayer;
128 }
129 
130 template<typename LayerType>
132  LayerType* baseLayer,
133  ActivationLayer* activationLayer,
134  ActivationDescriptor& activationDesc,
135  std::string name)
136 {
137  IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddMultiplicationLayer(name.c_str());
138  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
139 
140  FuseLayer(optimizationViews,
141  baseLayer,
142  replacementLayer,
143  activationLayer,
144  activationDesc);
145 
146  return replacementLayer;
147 }
148 
149 template<typename LayerType>
151  LayerType* baseLayer,
152  ActivationLayer* activationLayer,
153  ActivationDescriptor& activationDesc,
154  std::string name)
155 {
156  IConnectableLayer* replacement =
157  optimizationViews.GetINetwork()->AddBatchNormalizationLayer(baseLayer->GetParameters(),
158  ConstTensor(),
159  ConstTensor(),
160  ConstTensor(),
161  ConstTensor(),
162  name.c_str());
163  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
164 
165  FuseLayer(optimizationViews,
166  baseLayer,
167  replacementLayer,
168  activationLayer,
169  activationDesc);
170 
171  SubgraphView substitutionSubgraph({baseLayer, activationLayer},
172  CreateIInputsFrom({baseLayer}),
173  CreateIOutputsFrom({activationLayer}));
174  SubgraphView replacementSubgraph(replacementLayer);
175 
176  return replacementLayer;
177 }
178 
179 template<typename LayerType>
181  LayerType* baseLayer,
182  ActivationLayer* activationLayer,
183  ActivationDescriptor& activationDesc,
184  std::string name)
185 {
186  IConnectableLayer* replacement = optimizationViews.GetINetwork()
187  ->AddConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
188 
189  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
190 
191  replacementLayer->m_Weight = std::move(baseLayer->m_Weight);
192  replacementLayer->m_Bias = std::move(baseLayer->m_Bias);
193 
194  FuseLayer(optimizationViews,
195  baseLayer,
196  replacementLayer,
197  activationLayer,
198  activationDesc);
199 
200  return replacementLayer;
201 }
202 
203 template<typename LayerType>
205  LayerType* baseLayer,
206  ActivationLayer* activationLayer,
207  ActivationDescriptor& activationDesc,
208  std::string name)
209 {
210  IConnectableLayer* replacement =
211  optimizationViews.GetINetwork()->AddDepthwiseConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
212 
213  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
214 
215  replacementLayer->m_Weight = std::move(baseLayer->m_Weight);
216  replacementLayer->m_Bias = std::move(baseLayer->m_Bias);
217 
218  FuseLayer(optimizationViews,
219  baseLayer,
220  replacementLayer,
221  activationLayer,
222  activationDesc);
223 
224  return replacementLayer;
225 }
226 
227 template<typename LayerType>
229  LayerType* baseLayer,
230  ActivationLayer* activationLayer,
231  ActivationDescriptor& activationDesc,
232  std::string name)
233 {
234  IConnectableLayer* replacement =
235  optimizationViews.GetINetwork()->AddFullyConnectedLayer(baseLayer->GetParameters(),
236  name.c_str());
237  LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
238 
239  FuseLayer(optimizationViews,
240  baseLayer,
241  replacementLayer,
242  activationLayer,
243  activationDesc);
244 
245  replacementLayer->m_Weight = std::move(baseLayer->m_Weight);
246  replacementLayer->m_Bias = std::move(baseLayer->m_Bias);
247 
248  return replacementLayer;
249 }
250 
251 //
252 // If reduce layer has multiple axes, add new layer for each axis to simulate the same behaviour
253 // as currently only one axis is supported.
254 //
255 template<typename LayerType>
256 std::vector<IConnectableLayer*> ChainReduceLayers(OptimizationViews& optimizationViews,
257  LayerType* baseLayer,
258  ReduceDescriptor& desc)
259 {
260  // Vector of new chained layers, used for substitution.
261  std::vector<IConnectableLayer*> layers;
262 
263  // Vector of axes so each layer is reshaped correctly.
264  std::vector<uint32_t> axes;
265  unsigned int recalulatedAxis = 0;
266 
267  for (unsigned int i = 0; i != desc.m_vAxis.size(); ++i)
268  {
269  // Get TensorInfo from base layer and reduce shape using axis.
270  TensorInfo layerInfo = baseLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo();
271 
272  axes.emplace_back(desc.m_vAxis[i]);
273 
274  const TensorInfo& reducedTensorInfo = ComputeReductionTensorShape(layerInfo,
275  axes,
276  desc.m_KeepDims);
277 
278  // Create a vector for the single axis to be assigned to the descriptor.
279  // Update axis if keepDims is set reduce layers correctly.
280  std::vector<uint32_t> singleAxis(1, desc.m_vAxis[i] - recalulatedAxis);
281 
282  // Create a descriptor and assign single axis.
283  ReduceDescriptor newReduceDescriptor = baseLayer->GetParameters();
284  newReduceDescriptor.m_vAxis.assign(singleAxis.begin(), singleAxis.end());
285 
286  // Add new layer to graph.
287  std::string layerName = "reduce_layer_" + std::to_string(i);
288 
289  Layer* replacementLayer = PolymorphicDowncast<Layer*>(
290  optimizationViews.GetINetwork()->AddReduceLayer(newReduceDescriptor,
291  layerName.c_str()));
292 
293  // Connect previous layer with new layer.
294  // The first and last layer will be connected when the subgraph is replaced.
295  if (!layers.empty())
296  {
297  layers[i - 1]->GetOutputSlot(0).Connect(replacementLayer->GetInputSlot(0));
298  }
299 
300  // Set updated tensorInfo for new layer.
301  replacementLayer->GetOutputSlot(0).SetTensorInfo(reducedTensorInfo);
302 
303  if (!desc.m_KeepDims)
304  {
305  recalulatedAxis++;
306  }
307 
308  layers.emplace_back(replacementLayer);
309  }
310 
311  // Check if the TensorInfo from the last layer equals the inferred output from the original layer.
312  ARMNN_ASSERT(baseLayer->GetOutputSlot(0).GetTensorInfo() ==
313  PolymorphicDowncast<Layer*>(layers.back())->GetOutputSlot().GetTensorInfo());
314 
315  return layers;
316 }
317 
318 //
319 // Substitute baseLayer with new subgraph
320 //
321 template<typename LayerType>
322 void ReplaceLayers(OptimizationViews& optimizationViews,
323  LayerType* baseLayer,
324  std::vector<IConnectableLayer*>& layers)
325 {
326  std::list<IConnectableLayer*> replacementLayers(layers.begin(), layers.end());
327 
328  SubgraphView substitutionSubgraph(baseLayer);
329  SubgraphView replacementSubgraph(std::move(replacementLayers),
330  CreateIInputsFrom({replacementLayers.front()}),
331  CreateIOutputsFrom({replacementLayers.back()}));
332 
333  optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph});
334 }
335 
336 } // namespace armnn
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:68
constexpr bool IsQuantizedType()
Definition: TypesUtils.hpp:280
LayerType * FuseConvolution2dLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
bool m_KeepDims
if true then output shape has no change.
void AddSubstitution(SubstitutionPair &&substitution)
This layer represents an activation operation with the specified activation function.
Copyright (c) 2021 ARM Limited and Contributors.
The SubgraphView class represents a subgraph of a Graph.
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:324
LayerType * FuseDivisionLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
A ReduceDescriptor for the REDUCE operators.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
LayerType * FuseLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, LayerType *replacementLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc)
LayerType * FuseBatchNormalizationLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:36
std::vector< uint32_t > m_vAxis
The indices of the dimensions to reduce.
std::vector< IConnectableLayer * > ChainReduceLayers(OptimizationViews &optimizationViews, LayerType *baseLayer, ReduceDescriptor &desc)
LayerType * FuseSubtractionLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
void SetTensorInfo(const TensorInfo &tensorInfo) override
Definition: Layer.cpp:87
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
Definition: Layer.hpp:326
LayerType * FuseAdditionLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
LayerType * FuseDepthwiseConvolution2dLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
void ReplaceLayers(OptimizationViews &optimizationViews, LayerType *baseLayer, std::vector< IConnectableLayer *> &layers)
const TensorInfo ComputeReductionTensorShape(const armnn::TensorInfo &input, const std::vector< uint32_t > &vAxis, const bool keepDims)
Function to compute the output tensor shape based on the axes and if keepDims is set.
LayerType * FuseMultiplicationLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
LayerType * FuseFullyConnectedLayer(OptimizationViews &optimizationViews, LayerType *baseLayer, ActivationLayer *activationLayer, ActivationDescriptor &activationDesc, std::string name)
LayerType
When adding a new layer, adapt also the LastLayer enum value in the enum class LayerType below...
Definition: Types.hpp:468