blob: 4009ae834a2c6187f3ce1ea4311e5e01b033acda [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
68#if TOSA_FWD_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}