1 //
2 // Copyright © 2020-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #pragma once
7
8 #include <armnn/backends/OptimizationViews.hpp>
9 #include <armnn/utility/Assert.hpp>
10
11 #include <aclCommon/ArmComputeUtils.hpp>
12 #include <backendsCommon/SubgraphUtils.hpp>
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
checkDataTypeInputandOutput(const Layer & layer)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>
FuseLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,LayerType * replacementLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc)54 LayerType* FuseLayer(OptimizationViews& optimizationViews,
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>
FuseAdditionLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)74 LayerType* FuseAdditionLayer(OptimizationViews& optimizationViews,
75 LayerType* baseLayer,
76 ActivationLayer* activationLayer,
77 ActivationDescriptor& activationDesc,
78 std::string name)
79 {
80 ARMNN_NO_DEPRECATE_WARN_BEGIN
81 IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddAdditionLayer(name.c_str());
82 ARMNN_NO_DEPRECATE_WARN_END
83 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
84
85 FuseLayer(optimizationViews,
86 baseLayer,
87 replacementLayer,
88 activationLayer,
89 activationDesc);
90
91 return replacementLayer;
92 }
93
94 template<typename LayerType>
FuseSubtractionLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)95 LayerType* FuseSubtractionLayer(OptimizationViews& optimizationViews,
96 LayerType* baseLayer,
97 ActivationLayer* activationLayer,
98 ActivationDescriptor& activationDesc,
99 std::string name)
100 {
101 ARMNN_NO_DEPRECATE_WARN_BEGIN
102 IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddSubtractionLayer(name.c_str());
103 ARMNN_NO_DEPRECATE_WARN_END
104 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
105
106 FuseLayer(optimizationViews,
107 baseLayer,
108 replacementLayer,
109 activationLayer,
110 activationDesc);
111
112 return replacementLayer;
113 }
114
115 template<typename LayerType>
FuseDivisionLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)116 LayerType* FuseDivisionLayer(OptimizationViews& optimizationViews,
117 LayerType* baseLayer,
118 ActivationLayer* activationLayer,
119 ActivationDescriptor& activationDesc,
120 std::string name)
121 {
122 ARMNN_NO_DEPRECATE_WARN_BEGIN
123 IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddDivisionLayer(name.c_str());
124 ARMNN_NO_DEPRECATE_WARN_END
125 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
126
127 FuseLayer(optimizationViews,
128 baseLayer,
129 replacementLayer,
130 activationLayer,
131 activationDesc);
132
133 return replacementLayer;
134 }
135
136 template<typename LayerType>
FuseMultiplicationLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)137 LayerType* FuseMultiplicationLayer(OptimizationViews& optimizationViews,
138 LayerType* baseLayer,
139 ActivationLayer* activationLayer,
140 ActivationDescriptor& activationDesc,
141 std::string name)
142 {
143 ARMNN_NO_DEPRECATE_WARN_BEGIN
144 IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddMultiplicationLayer(name.c_str());
145 ARMNN_NO_DEPRECATE_WARN_END
146 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
147
148 FuseLayer(optimizationViews,
149 baseLayer,
150 replacementLayer,
151 activationLayer,
152 activationDesc);
153
154 return replacementLayer;
155 }
156
157 template<typename LayerType>
FuseElementwiseBinaryLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,BinaryOperation operation,std::string name)158 LayerType* FuseElementwiseBinaryLayer(OptimizationViews& optimizationViews,
159 LayerType* baseLayer,
160 ActivationLayer* activationLayer,
161 ActivationDescriptor& activationDesc,
162 BinaryOperation operation,
163 std::string name)
164 {
165 IConnectableLayer* replacement = optimizationViews.GetINetwork()->AddElementwiseBinaryLayer(operation,
166 name.c_str());
167 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
168
169 FuseLayer(optimizationViews,
170 baseLayer,
171 replacementLayer,
172 activationLayer,
173 activationDesc);
174
175 return replacementLayer;
176 }
177
178 template<typename LayerType>
FuseBatchNormalizationLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)179 LayerType* FuseBatchNormalizationLayer(OptimizationViews& optimizationViews,
180 LayerType* baseLayer,
181 ActivationLayer* activationLayer,
182 ActivationDescriptor& activationDesc,
183 std::string name)
184 {
185 IConnectableLayer* replacement =
186 optimizationViews.GetINetwork()->AddBatchNormalizationLayer(baseLayer->GetParameters(),
187 ConstTensor(),
188 ConstTensor(),
189 ConstTensor(),
190 ConstTensor(),
191 name.c_str());
192 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
193
194 FuseLayer(optimizationViews,
195 baseLayer,
196 replacementLayer,
197 activationLayer,
198 activationDesc);
199
200 SubgraphView substitutionSubgraph({baseLayer, activationLayer},
201 CreateIInputsFrom({baseLayer}),
202 CreateIOutputsFrom({activationLayer}));
203 SubgraphView replacementSubgraph(replacementLayer);
204
205 return replacementLayer;
206 }
207
208 template<typename LayerType>
FuseConvolution2dLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)209 LayerType* FuseConvolution2dLayer(OptimizationViews& optimizationViews,
210 LayerType* baseLayer,
211 ActivationLayer* activationLayer,
212 ActivationDescriptor& activationDesc,
213 std::string name)
214 {
215 IConnectableLayer* replacement = optimizationViews.GetINetwork()
216 ->AddConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
217
218 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
219
220
221 FuseLayer(optimizationViews,
222 baseLayer,
223 replacementLayer,
224 activationLayer,
225 activationDesc);
226
227 return replacementLayer;
228 }
229
230 template<typename LayerType>
FuseDepthwiseConvolution2dLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)231 LayerType* FuseDepthwiseConvolution2dLayer(OptimizationViews& optimizationViews,
232 LayerType* baseLayer,
233 ActivationLayer* activationLayer,
234 ActivationDescriptor& activationDesc,
235 std::string name)
236 {
237 IConnectableLayer* replacement =
238 optimizationViews.GetINetwork()->AddDepthwiseConvolution2dLayer(baseLayer->GetParameters(), name.c_str());
239
240 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
241
242
243 FuseLayer(optimizationViews,
244 baseLayer,
245 replacementLayer,
246 activationLayer,
247 activationDesc);
248
249 return replacementLayer;
250 }
251
252 template<typename LayerType>
FuseFullyConnectedLayer(OptimizationViews & optimizationViews,LayerType * baseLayer,ActivationLayer * activationLayer,ActivationDescriptor & activationDesc,std::string name)253 LayerType* FuseFullyConnectedLayer(OptimizationViews& optimizationViews,
254 LayerType* baseLayer,
255 ActivationLayer* activationLayer,
256 ActivationDescriptor& activationDesc,
257 std::string name)
258 {
259 IConnectableLayer* replacement =
260 optimizationViews.GetINetwork()->AddFullyConnectedLayer(baseLayer->GetParameters(),
261 name.c_str());
262 LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
263
264 FuseLayer(optimizationViews,
265 baseLayer,
266 replacementLayer,
267 activationLayer,
268 activationDesc);
269
270
271 return replacementLayer;
272 }
273
274 //
275 // If reduce layer has multiple axes, add new layer for each axis to simulate the same behaviour
276 // as currently only one axis is supported.
277 //
278 template<typename LayerType>
ChainReduceLayers(OptimizationViews & optimizationViews,LayerType * baseLayer,ReduceDescriptor & desc)279 std::vector<IConnectableLayer*> ChainReduceLayers(OptimizationViews& optimizationViews,
280 LayerType* baseLayer,
281 ReduceDescriptor& desc)
282 {
283 // Vector of new chained layers, used for substitution.
284 std::vector<IConnectableLayer*> layers;
285
286 // Vector of axes so each layer is reshaped correctly.
287 std::vector<uint32_t> axes;
288 unsigned int recalulatedAxis = 0;
289
290 for (unsigned int i = 0; i != desc.m_vAxis.size(); ++i)
291 {
292 // Get TensorInfo from base layer and reduce shape using axis.
293 TensorInfo layerInfo = baseLayer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo();
294
295 axes.emplace_back(desc.m_vAxis[i]);
296
297 const TensorInfo& reducedTensorInfo = ComputeReductionTensorShape(layerInfo,
298 axes,
299 desc.m_KeepDims);
300
301 // Create a vector for the single axis to be assigned to the descriptor.
302 // Update axis if keepDims is set reduce layers correctly.
303 std::vector<uint32_t> singleAxis(1, desc.m_vAxis[i] - recalulatedAxis);
304
305 // Create a descriptor and assign single axis.
306 ReduceDescriptor newReduceDescriptor = baseLayer->GetParameters();
307 newReduceDescriptor.m_vAxis.assign(singleAxis.begin(), singleAxis.end());
308
309 // Add new layer to graph.
310 std::string layerName = "reduce_layer_" + std::to_string(i);
311
312 Layer* replacementLayer = PolymorphicDowncast<Layer*>(
313 optimizationViews.GetINetwork()->AddReduceLayer(newReduceDescriptor,
314 layerName.c_str()));
315
316 // Connect previous layer with new layer.
317 // The first and last layer will be connected when the subgraph is replaced.
318 if (!layers.empty())
319 {
320 layers[i - 1]->GetOutputSlot(0).Connect(replacementLayer->GetInputSlot(0));
321 }
322
323 // Set updated tensorInfo for new layer.
324 replacementLayer->GetOutputSlot(0).SetTensorInfo(reducedTensorInfo);
325
326 if (!desc.m_KeepDims)
327 {
328 recalulatedAxis++;
329 }
330
331 layers.emplace_back(replacementLayer);
332 }
333
334 // Check if the TensorInfo from the last layer equals the inferred output from the original layer.
335 ARMNN_ASSERT(baseLayer->GetOutputSlot(0).GetTensorInfo() ==
336 PolymorphicDowncast<Layer*>(layers.back())->GetOutputSlot().GetTensorInfo());
337
338 return layers;
339 }
340
341 //
342 // Substitute baseLayer with new subgraph
343 //
344 template<typename LayerType>
ReplaceLayers(OptimizationViews & optimizationViews,LayerType * baseLayer,std::vector<IConnectableLayer * > & layers)345 void ReplaceLayers(OptimizationViews& optimizationViews,
346 LayerType* baseLayer,
347 std::vector<IConnectableLayer*>& layers)
348 {
349 std::list<IConnectableLayer*> replacementLayers(layers.begin(), layers.end());
350
351 SubgraphView substitutionSubgraph(baseLayer);
352 SubgraphView replacementSubgraph(std::move(replacementLayers),
353 CreateIInputsFrom({replacementLayers.front()}),
354 CreateIOutputsFrom({replacementLayers.back()}));
355
356 optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph});
357 }
358
359 } // namespace armnn
360