ArmNN
 24.02
ActivationOperator.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2024 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 //
6 // Copyright © 2020 The TensorFlow Authors. All Rights Reserved.
7 // SPDX-License-Identifier: Apache-2.0
8 //
9 
10 #include "ActivationOperator.hpp"
12 
14 
15 // This function is paraphrased from:
16 // tensorflow/compiler/mlir/tosa/transforms/legalize_tfl.cc from function ConvertTFLLeakyReluOp
17 TosaSerializationBasicBlock* ConvertActivationToTosaOperator(const Layer* layer,
18  const std::vector<const TensorInfo*>& inputs,
19  const std::vector<const TensorInfo*>& outputs,
20  const ActivationDescriptor* activationDescriptor)
21 {
22  if (inputs.size() != 1)
23  {
24  throw armnn::Exception("ConvertActivationToTosaOperator: 1 input tensors required.");
25  }
26 
27  if (outputs.size() != 1)
28  {
29  throw armnn::Exception("ConvertActivationToTosaOperator: 1 output tensor required.");
30  }
31 
32  std::string inputName = std::string("input0_");
33  std::string outputNameAlpha = std::string("intermediate1_") + GetUniqueTosaMappingID();
34  std::string outputNameMul = std::string("intermediate2_") + GetUniqueTosaMappingID();
35  std::string outputName = std::string("output0_");
36  std::string blockName = std::string("Op_ACTIVATION_block_") + GetUniqueTosaMappingID();
37 
38  // If a layer is present then the block will be used for execution, so input and output names need to be determined
39  // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter.
40  if (layer != nullptr)
41  {
42  // Get the layers connected to the input slots and determine unique tensors names.
43  Layer& connectedInputLayer = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer();
44  inputName = GenerateUniqueName(connectedInputLayer, 0);
45 
46  // Determine unique output tensor name.
47  outputName = GenerateUniqueOutputName(*layer, 0);
48  }
49 
50  std::vector<TosaSerializationTensor*> tensors;
51 
52  // Only add input tensors if connected layer is an input layer.
53  // As intermediate or constant tensors will be created separately.
54  // There also can't be duplicate tensor.
55  std::vector<int32_t> inputShape0;
56  DType inputDType0 = DType::DType_UNKNOWN;
57  if(inputName.find("input0_") != std::string::npos)
58  {
59  inputShape0 = GetTosaTensorShape(inputs[0]->GetShape());
60  inputDType0 = ArmNNToDType(inputs[0]->GetDataType());
61  tensors.push_back(new TosaSerializationTensor(inputName, inputShape0, inputDType0, {}));
62  }
63 
64  std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputs[0]->GetShape());
65  DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType());
66  tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}));
67 
68 #if TOSA_COMPAT_VERSION(0, 60, 0)
69  std::string outputNameMAXMIN= std::string("intermediate3_") + GetUniqueTosaMappingID();
70 
71  if (inputDType0 == DType::DType_FP32)
72  {
73  // const_alpha
74  TosaSerializationOperator* alphaOp = nullptr;
75  TosaSerializationTensor* alphaTensor = nullptr;
76  CreateConstTosaOperator<float>(outputNameAlpha,
77  activationDescriptor->m_A,
78  inputDType0,
79  inputShape0,
80  alphaOp,
81  alphaTensor);
82  tensors.push_back(alphaTensor);
83 
84  // mul
85  int32_t shift = 0;
86  TosaMulAttribute mulAttribute(shift);
87  TosaSerializationOperator* mulOp = new TosaSerializationOperator(Op_MUL,
88  Attribute_MulAttribute,
89  &mulAttribute,
90  {inputName, outputNameAlpha},
91  {outputNameMul});
92  tensors.push_back(new TosaSerializationTensor(outputNameMul, inputShape0, inputDType0, {}));
93 
94  TosaSerializationOperator* op = nullptr;
95  if (activationDescriptor->m_A <= 1.0)
96  {
97  op = new TosaSerializationOperator(Op_MAXIMUM,
98  Attribute_NONE,
99  nullptr,
100  {inputName, outputNameMul},
101  {outputName});
102  }
103  else
104  {
105  op = new TosaSerializationOperator(Op_MINIMUM,
106  Attribute_NONE,
107  nullptr,
108  {inputName, outputNameMul},
109  {outputName});
110 
111  }
112 
113  // operatorInputNames/operatorOutputNames ends up being the same as
114  // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
115  return new TosaSerializationBasicBlock(blockName, // name
116  mainName, // region name
117  {alphaOp, mulOp, op}, // operators
118  tensors, // tensors
119  {inputName}, // inputs
120  {outputName}); // outputs
121  }
122  else
123  {
124  std::string outputNameRescaleAlpha = std::string("intermediate3_") + GetUniqueTosaMappingID();
125  std::string outputNameRescaleIdentity = std::string("intermediate4_") + GetUniqueTosaMappingID();
126  std::string outputNameRescaleMaxMin = std::string("intermediate5_") + GetUniqueTosaMappingID();
127 
128  DType rescale_type = DType::DType_INT32;
129  float alpha = activationDescriptor->m_A;
130  double scale_alpha = inputs[0]->GetQuantizationScale() * alpha / outputs[0]->GetQuantizationScale();
131  double scale_identity = inputs[0]->GetQuantizationScale() / outputs[0]->GetQuantizationScale();
132  int32_t input_zp = inputs[0]->GetQuantizationOffset();
133  int32_t output_zp = outputs[0]->GetQuantizationOffset();
134 
135  // Value op_rescale_alpha_in =
136  // buildRescale(rewriter, op, rescale_type, input, scale_alpha,
137  // input_qtype.getZeroPoint(), 0, true, true);
138  TosaSerializationOperator* rescaleAlphaOp = nullptr;
139  TosaSerializationTensor* rescaleAlphaTensor = nullptr;
140  CreateRescaleTosaOperator(inputName,
141  outputNameRescaleAlpha,
142  rescale_type,
143  inputShape0,
144  scale_alpha,
145  input_zp,
146  0,
147  true,
148  true,
149  &rescaleAlphaOp,
150  &rescaleAlphaTensor);
151  tensors.push_back(rescaleAlphaTensor);
152 
153  // Value op_rescale_identity_in =
154  // buildRescale(rewriter, op, rescale_type, input, scale_identity,
155  // input_qtype.getZeroPoint(), 0, true, true);
156  TosaSerializationOperator* rescaleIdentityOp = nullptr;
157  TosaSerializationTensor* rescaleIdentityTensor = nullptr;
158  CreateRescaleTosaOperator(inputName,
159  outputNameRescaleIdentity,
160  rescale_type,
161  inputShape0,
162  scale_identity,
163  input_zp,
164  0,
165  true,
166  true,
167  &rescaleIdentityOp,
168  &rescaleIdentityTensor);
169  tensors.push_back(rescaleIdentityTensor);
170 
171  // Value result_int32;
172  // if (alpha <= 1.0) {
173  // auto max_op = CreateOpAndInfer<tosa::MaximumOp>(
174  // rewriter, op->getLoc(), rescale_type, op_rescale_identity_in,
175  // op_rescale_alpha_in);
176  // result_int32 = max_op.getResult();
177  // } else {
178  // auto min_op = CreateOpAndInfer<tosa::MinimumOp>(
179  // rewriter, op->getLoc(), rescale_type, op_rescale_identity_in,
180  // op_rescale_alpha_in);
181  // result_int32 = min_op.getResult();
182  // }
183  TosaSerializationOperator* op = nullptr;
184  if (alpha <= 1.0)
185  {
186  op = new TosaSerializationOperator(Op_MAXIMUM,
187  Attribute_NONE,
188  nullptr,
189  {outputNameRescaleAlpha, outputNameRescaleIdentity},
190  {outputNameRescaleMaxMin});
191  }
192  else
193  {
194  op = new TosaSerializationOperator(Op_MINIMUM,
195  Attribute_NONE,
196  nullptr,
197  {outputNameRescaleAlpha, outputNameRescaleIdentity},
198  {outputNameRescaleMaxMin});
199 
200  }
201  tensors.push_back(new TosaSerializationTensor(outputNameRescaleMaxMin, inputShape0, rescale_type, {}));
202 
203  // Value output = buildRescaleFromInt32(rewriter, op, output_type, result_int32,
204  // 1.0, output_qtype.getZeroPoint());
205  TosaSerializationOperator* rescaleOutputOp = nullptr;
206  CreateFromInt32RescaleTosaOperator(outputNameRescaleMaxMin,
207  outputName,
208  outputDType0,
209  outputShape0,
210  1.0,
211  output_zp,
212  &rescaleOutputOp,
213  nullptr);
214 
215  // operatorInputNames/operatorOutputNames ends up being the same as
216  // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
217  return new TosaSerializationBasicBlock(blockName, // name
218  mainName, // region name
219  {rescaleAlphaOp, rescaleIdentityOp, op, rescaleOutputOp}, // operators
220  tensors, // tensors
221  {inputName}, // inputs
222  {outputName}); // outputs
223  }
224 #else
225  std::string outputNameZero = std::string("intermediate3_") + GetUniqueTosaMappingID();
226  std::string outputNameGE = std::string("intermediate4_") + GetUniqueTosaMappingID();
227 
228  // const_zero
229  TosaSerializationOperator* zeroOp = nullptr;
230  TosaSerializationTensor* zeroTensor = nullptr;
231  CreateConstTosaOperator<float>(outputNameZero,
232  0.0f,
233  inputDType0,
234  inputShape0,
235  zeroOp,
236  zeroTensor);
237  tensors.push_back(zeroTensor);
238 
239  // const_alpha
240  TosaSerializationOperator* alphaOp = nullptr;
241  TosaSerializationTensor* alphaTensor = nullptr;
242  CreateConstTosaOperator<float>(outputNameAlpha,
243  activationDescriptor->m_A,
244  inputDType0,
245  inputShape0,
246  alphaOp,
247  alphaTensor);
248  tensors.push_back(alphaTensor);
249 
250  // mul
251  int32_t shift = 0;
252  TosaMulAttribute mulAttribute(shift);
253  TosaSerializationOperator* mulOp = new TosaSerializationOperator(Op_MUL,
254  Attribute_MulAttribute,
255  &mulAttribute,
256  {inputName, outputNameAlpha},
257  {outputNameMul});
258  tensors.push_back(new TosaSerializationTensor(outputNameMul, inputShape0, inputDType0, {}));
259 
260  // greater_equal
261  TosaSerializationOperator* geOp = new TosaSerializationOperator(Op_GREATER_EQUAL,
262  Attribute_NONE,
263  nullptr,
264  {inputName, outputNameZero},
265  {outputNameGE});
266  tensors.push_back(new TosaSerializationTensor(outputNameGE, outputShape0, DType::DType_BOOL, {}));
267 
268  // select
269  TosaSerializationOperator* selOp = new TosaSerializationOperator(Op_SELECT,
270  Attribute_NONE,
271  nullptr,
272  {outputNameGE, inputName, outputNameMul},
273  {outputName});
274 
275  // operatorInputNames/operatorOutputNames ends up being the same as
276  // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
277  return new TosaSerializationBasicBlock(blockName, // name
278  mainName, // region name
279  {zeroOp, alphaOp, mulOp, geOp, selOp}, // operators
280  tensors, // tensors
281  {inputName}, // inputs
282  {outputName}); // outputs
283 #endif
284 }
armnn::ActivationDescriptor
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:36
CreateRescaleTosaOperator
void CreateRescaleTosaOperator(const std::string &inputName, const std::string &outputName, DType output_type, const std::vector< int32_t > &shape, int32_t scale_multiplier, int32_t scale_shift, int32_t input_zp, int32_t output_zp, bool double_round, bool scale32, TosaSerializationOperator **op, TosaSerializationTensor **tensor)
Definition: TosaRescaleOperatorUtils.hpp:10
armnn::ActivationDescriptor::m_A
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH,...
Definition: Descriptors.hpp:61
ActivationLayer.hpp
TosaRescaleOperatorUtils.hpp
armnn::Layer::GetInputSlot
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:337
armnn::Layer
Definition: Layer.hpp:230
mainName
const std::string mainName
Definition: TosaOperatorUtils.hpp:19
armnn::OutputSlot::GetOwningLayer
Layer & GetOwningLayer() const
Definition: Layer.hpp:132
ArmNNToDType
DType ArmNNToDType(const DataType &type)
Definition: TosaOperatorUtils.hpp:22
GenerateUniqueOutputName
std::string GenerateUniqueOutputName(const Layer &layer, uint32_t layerSlot)
Definition: TosaOperatorUtils.hpp:82
CreateFromInt32RescaleTosaOperator
void CreateFromInt32RescaleTosaOperator(const std::string &inputName, const std::string &outputName, DType output_type, const std::vector< int32_t > &shape, double output_scale, int32_t output_zp, TosaSerializationOperator **op, TosaSerializationTensor **tensor)
Definition: TosaRescaleOperatorUtils.hpp:119
armnn::Exception
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
GenerateUniqueName
std::string GenerateUniqueName(const Layer &layer, uint32_t layerSlot)
Definition: TosaOperatorUtils.hpp:63
GetTosaTensorShape
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
Definition: TosaOperatorUtils.hpp:52
armnn::InputSlot::GetConnectedOutputSlot
const OutputSlot * GetConnectedOutputSlot() const
Definition: Layer.hpp:56
ConvertActivationToTosaOperator
TosaSerializationBasicBlock * ConvertActivationToTosaOperator(const Layer *layer, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs, const ActivationDescriptor *activationDescriptor)
Definition: ActivationOperator.cpp:17
ActivationOperator.hpp
GetUniqueTosaMappingID
std::string GetUniqueTosaMappingID()
Definition: TosaOperatorUtils.hpp:100