blob: c17c4c70fee095472fc29058bfcf6f422d0d14c9 [file] [log] [blame]
Tracy Narine10403ec2023-11-28 11:55:08 +00001//
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"
11#include "TosaRescaleOperatorUtils.hpp"
12
13#include <layers/ActivationLayer.hpp>
14
15// This function is paraphrased from:
16// tensorflow/compiler/mlir/tosa/transforms/legalize_tfl.cc from function ConvertTFLLeakyReluOp
17TosaSerializationBasicBlock* 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
Tracy Narine91ffe3d2024-01-23 09:40:00 +000068#if TOSA_COMPAT_VERSION(0, 60, 0)
Tracy Narine10403ec2023-11-28 11:55:08 +000069 std::string outputNameMAXMIN= std::string("intermediate3_") + GetUniqueTosaMappingID();
70
Teresa Charlina42e0062024-04-23 13:03:40 +010071 if (inputDType0 == DType::DType_FP32 ||
72 inputDType0 == DType::DType_FP16)
Tracy Narine10403ec2023-11-28 11:55:08 +000073 {
74 // const_alpha
75 TosaSerializationOperator* alphaOp = nullptr;
76 TosaSerializationTensor* alphaTensor = nullptr;
77 CreateConstTosaOperator<float>(outputNameAlpha,
78 activationDescriptor->m_A,
79 inputDType0,
80 inputShape0,
81 alphaOp,
82 alphaTensor);
83 tensors.push_back(alphaTensor);
84
85 // mul
86 int32_t shift = 0;
87 TosaMulAttribute mulAttribute(shift);
88 TosaSerializationOperator* mulOp = new TosaSerializationOperator(Op_MUL,
89 Attribute_MulAttribute,
90 &mulAttribute,
91 {inputName, outputNameAlpha},
92 {outputNameMul});
93 tensors.push_back(new TosaSerializationTensor(outputNameMul, inputShape0, inputDType0, {}));
94
95 TosaSerializationOperator* op = nullptr;
96 if (activationDescriptor->m_A <= 1.0)
97 {
98 op = new TosaSerializationOperator(Op_MAXIMUM,
99 Attribute_NONE,
100 nullptr,
101 {inputName, outputNameMul},
102 {outputName});
103 }
104 else
105 {
106 op = new TosaSerializationOperator(Op_MINIMUM,
107 Attribute_NONE,
108 nullptr,
109 {inputName, outputNameMul},
110 {outputName});
111
112 }
113
114 // operatorInputNames/operatorOutputNames ends up being the same as
115 // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
116 return new TosaSerializationBasicBlock(blockName, // name
117 mainName, // region name
118 {alphaOp, mulOp, op}, // operators
119 tensors, // tensors
120 {inputName}, // inputs
121 {outputName}); // outputs
122 }
123 else
124 {
125 std::string outputNameRescaleAlpha = std::string("intermediate3_") + GetUniqueTosaMappingID();
126 std::string outputNameRescaleIdentity = std::string("intermediate4_") + GetUniqueTosaMappingID();
127 std::string outputNameRescaleMaxMin = std::string("intermediate5_") + GetUniqueTosaMappingID();
128
129 DType rescale_type = DType::DType_INT32;
130 float alpha = activationDescriptor->m_A;
131 double scale_alpha = inputs[0]->GetQuantizationScale() * alpha / outputs[0]->GetQuantizationScale();
132 double scale_identity = inputs[0]->GetQuantizationScale() / outputs[0]->GetQuantizationScale();
133 int32_t input_zp = inputs[0]->GetQuantizationOffset();
134 int32_t output_zp = outputs[0]->GetQuantizationOffset();
135
136 // Value op_rescale_alpha_in =
137 // buildRescale(rewriter, op, rescale_type, input, scale_alpha,
138 // input_qtype.getZeroPoint(), 0, true, true);
139 TosaSerializationOperator* rescaleAlphaOp = nullptr;
140 TosaSerializationTensor* rescaleAlphaTensor = nullptr;
141 CreateRescaleTosaOperator(inputName,
142 outputNameRescaleAlpha,
143 rescale_type,
144 inputShape0,
145 scale_alpha,
146 input_zp,
147 0,
148 true,
149 true,
150 &rescaleAlphaOp,
151 &rescaleAlphaTensor);
152 tensors.push_back(rescaleAlphaTensor);
153
154 // Value op_rescale_identity_in =
155 // buildRescale(rewriter, op, rescale_type, input, scale_identity,
156 // input_qtype.getZeroPoint(), 0, true, true);
157 TosaSerializationOperator* rescaleIdentityOp = nullptr;
158 TosaSerializationTensor* rescaleIdentityTensor = nullptr;
159 CreateRescaleTosaOperator(inputName,
160 outputNameRescaleIdentity,
161 rescale_type,
162 inputShape0,
163 scale_identity,
164 input_zp,
165 0,
166 true,
167 true,
168 &rescaleIdentityOp,
169 &rescaleIdentityTensor);
170 tensors.push_back(rescaleIdentityTensor);
171
172 // Value result_int32;
173 // if (alpha <= 1.0) {
174 // auto max_op = CreateOpAndInfer<tosa::MaximumOp>(
175 // rewriter, op->getLoc(), rescale_type, op_rescale_identity_in,
176 // op_rescale_alpha_in);
177 // result_int32 = max_op.getResult();
178 // } else {
179 // auto min_op = CreateOpAndInfer<tosa::MinimumOp>(
180 // rewriter, op->getLoc(), rescale_type, op_rescale_identity_in,
181 // op_rescale_alpha_in);
182 // result_int32 = min_op.getResult();
183 // }
184 TosaSerializationOperator* op = nullptr;
185 if (alpha <= 1.0)
186 {
187 op = new TosaSerializationOperator(Op_MAXIMUM,
188 Attribute_NONE,
189 nullptr,
190 {outputNameRescaleAlpha, outputNameRescaleIdentity},
191 {outputNameRescaleMaxMin});
192 }
193 else
194 {
195 op = new TosaSerializationOperator(Op_MINIMUM,
196 Attribute_NONE,
197 nullptr,
198 {outputNameRescaleAlpha, outputNameRescaleIdentity},
199 {outputNameRescaleMaxMin});
200
201 }
202 tensors.push_back(new TosaSerializationTensor(outputNameRescaleMaxMin, inputShape0, rescale_type, {}));
203
204 // Value output = buildRescaleFromInt32(rewriter, op, output_type, result_int32,
205 // 1.0, output_qtype.getZeroPoint());
206 TosaSerializationOperator* rescaleOutputOp = nullptr;
207 CreateFromInt32RescaleTosaOperator(outputNameRescaleMaxMin,
208 outputName,
209 outputDType0,
210 outputShape0,
211 1.0,
212 output_zp,
213 &rescaleOutputOp,
214 nullptr);
215
216 // operatorInputNames/operatorOutputNames ends up being the same as
217 // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
218 return new TosaSerializationBasicBlock(blockName, // name
219 mainName, // region name
220 {rescaleAlphaOp, rescaleIdentityOp, op, rescaleOutputOp}, // operators
221 tensors, // tensors
222 {inputName}, // inputs
223 {outputName}); // outputs
224 }
225#else
226 std::string outputNameZero = std::string("intermediate3_") + GetUniqueTosaMappingID();
227 std::string outputNameGE = std::string("intermediate4_") + GetUniqueTosaMappingID();
228
229 // const_zero
230 TosaSerializationOperator* zeroOp = nullptr;
231 TosaSerializationTensor* zeroTensor = nullptr;
232 CreateConstTosaOperator<float>(outputNameZero,
233 0.0f,
234 inputDType0,
235 inputShape0,
236 zeroOp,
237 zeroTensor);
238 tensors.push_back(zeroTensor);
239
240 // const_alpha
241 TosaSerializationOperator* alphaOp = nullptr;
242 TosaSerializationTensor* alphaTensor = nullptr;
243 CreateConstTosaOperator<float>(outputNameAlpha,
244 activationDescriptor->m_A,
245 inputDType0,
246 inputShape0,
247 alphaOp,
248 alphaTensor);
249 tensors.push_back(alphaTensor);
250
251 // mul
252 int32_t shift = 0;
253 TosaMulAttribute mulAttribute(shift);
254 TosaSerializationOperator* mulOp = new TosaSerializationOperator(Op_MUL,
255 Attribute_MulAttribute,
256 &mulAttribute,
257 {inputName, outputNameAlpha},
258 {outputNameMul});
259 tensors.push_back(new TosaSerializationTensor(outputNameMul, inputShape0, inputDType0, {}));
260
261 // greater_equal
262 TosaSerializationOperator* geOp = new TosaSerializationOperator(Op_GREATER_EQUAL,
263 Attribute_NONE,
264 nullptr,
265 {inputName, outputNameZero},
266 {outputNameGE});
267 tensors.push_back(new TosaSerializationTensor(outputNameGE, outputShape0, DType::DType_BOOL, {}));
268
269 // select
270 TosaSerializationOperator* selOp = new TosaSerializationOperator(Op_SELECT,
271 Attribute_NONE,
272 nullptr,
273 {outputNameGE, inputName, outputNameMul},
274 {outputName});
275
276 // operatorInputNames/operatorOutputNames ends up being the same as
277 // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
278 return new TosaSerializationBasicBlock(blockName, // name
279 mainName, // region name
280 {zeroOp, alphaOp, mulOp, geOp, selOp}, // operators
281 tensors, // tensors
282 {inputName}, // inputs
283 {outputName}); // outputs
284#endif
285}