ArmNN
 26.07
PReluOperator.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2025 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 "PReluOperator.hpp"
12 
14 
15 // This function is paraphrased from:
16 // tensorflow/compiler/mlir/tosa/transforms/legalize_tfl.cc from function ConvertTFLPReluOp
17 TosaSerializationBasicBlock* ConvertPReluToTosaOperator(const Layer* layer,
18  const std::vector<const TensorInfo*>& inputs,
19  const std::vector<const TensorInfo*>& outputs)
20 {
21  ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(IsQuantized8BitType(inputs[0]->GetDataType()) &&
22  IsQuantized8BitType(outputs[0]->GetDataType()),
23  "ConvertPReluToTosaOperator: Prelu currently only supports Int8 Quantized inputs");
24  if (inputs.size() != 2) {
25  throw armnn::Exception("ConvertPReluToTosaOperator: 2 input tensors required.");
26  }
27 
28  if (outputs.size() != 1) {
29  throw armnn::Exception("ConvertPReluToTosaOperator: 1 output tensor required.");
30  }
31 
32  std::string inputName0 = std::string("input_0");
33  std::string inputName1 = std::string("input_1");
34  std::string inputNameZero = std::string("constant1_") + GetUniqueTosaMappingID();
35  std::string outputNameMul = std::string("layer_intermediate2_") + GetUniqueTosaMappingID();
36  std::string outputNameClampEq = std::string("layer_intermediate3_") + GetUniqueTosaMappingID();
37  std::string outputNameRescaleTo32 = std::string("layer_intermediate4_") + GetUniqueTosaMappingID();
38  std::string outputNameRescaleSlope = std::string("layer_intermediate5_") + GetUniqueTosaMappingID();
39  std::string outputNameRescaleSlope2 = std::string("layer_intermediate6_") + GetUniqueTosaMappingID();
40  std::string outputNameRescaleIdentity = std::string("layer_intermediate7_") + GetUniqueTosaMappingID();
41  std::string outputNameClamp = std::string("layer_intermediate8_") + GetUniqueTosaMappingID();
42  std::string outputNameClampTo32 = std::string("layer_intermediate9_") + GetUniqueTosaMappingID();
43  std::string outputName = std::string("output0_");
44  std::string blockName = std::string("Op_PRELU_block_") + GetUniqueTosaMappingID();
45 
46  double scale_alpha = inputs[0]->GetQuantizationScale() * inputs[1]->GetQuantizationScale() /
47  outputs[0]->GetQuantizationScale();
48  double scale_identity = inputs[0]->GetQuantizationScale() / outputs[0]->GetQuantizationScale();
49  int32_t input0_zp = inputs[0]->GetQuantizationOffset();
50  int32_t input1_zp = inputs[1]->GetQuantizationOffset();
51  int32_t output_zp = outputs[0]->GetQuantizationOffset();
52 
53  // If a layer is present then the block will be used for execution, so input and output names need to be determined
54  // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter.
55  if (layer != nullptr) {
56  inputName0 = GenerateUniqueInputName(layer->GetInputSlot(0));
57  inputName1 = GenerateUniqueInputName(layer->GetInputSlot(1));
58  outputName = GenerateUniqueOutputName(*layer);
59  }
60 
61  std::vector<TosaSerializationTensor *> tensors;
62  std::vector<TosaSerializationOperator *> operators;
63 
64  // Only add input tensors if connected layer is an input layer.
65  // As intermediate or constant tensors will be created separately.
66  // There also can't be duplicate tensor.
67  std::vector<int32_t> inputShape0;
68  inputShape0 = GetTosaTensorShape(inputs[0]->GetShape());
69  DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType());
70  if (inputName0.find("input_") != std::string::npos) {
71  tensors.push_back(new TosaSerializationTensor(inputName0, inputShape0, inputDType0, {}));
72  }
73 
74  std::vector<int32_t> inputShape1;
75  inputShape1 = GetTosaTensorShape(inputs[1]->GetShape());
76  if (inputName1.find("input_") != std::string::npos) {
77  tensors.push_back(new TosaSerializationTensor(inputName1, inputShape1, inputDType0, {}));
78  }
79 
80  std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputs[0]->GetShape());
81  DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType());
82 
83  // Implement PReLU as:
84  // rescaled_in = rescale(in)
85  // rescaled_alpha = rescale(alpha)
86  // rescaled_identity_in = rescale(in, scale_identity)
87  // slope_in = mul(rescaled_in, rescaled_alpha)
88  // rescaled_slope_in = rescale(slope_in, scale_alpha)
89  // cond_result = greater_equal(rescaled_in, 0)
90  // output = select(cond_result, rescaled_identity_in, rescaled_slope_in)
91 
92  TosaSerializationOperator* op_rescale_in = nullptr;
93  CreateRescaleTosaOperator(inputName0,
94  outputNameRescaleTo32,
95  1.0f,
96  input0_zp,
97  0,
98  false,
99  false,
100  true,
101  true,
102  &op_rescale_in);
103  operators.emplace_back(op_rescale_in);
104  tensors.emplace_back(new TosaSerializationTensor(outputNameRescaleTo32,
105  inputShape0,
106  DType_INT32,
107  {}));
108 
109  int32_t clamp_min = 0;
110  int32_t clamp_max = std::numeric_limits<int8_t>::max();
111  float float_max = std::numeric_limits<float>::max();
112 
113  // If CLAMP result matches original then no negative values
114  // CLAMP does not support INT32
115  TosaClampAttribute attribute(clamp_min, clamp_max, 0, float_max);
116  auto* clamp_op = new TosaSerializationOperator(Op_CLAMP,
117  Attribute_ClampAttribute,
118  &attribute,
119  {inputName0},
120  {outputNameClamp});
121  operators.push_back(clamp_op);
122  tensors.push_back(new TosaSerializationTensor(outputNameClamp, inputShape0, inputDType0, {}));
123 
124  // EQUAL does not support INT8 so RESCALE CLAMP output required
125  TosaSerializationOperator* op_clamp = nullptr;
126  CreateRescaleTosaOperator(outputNameClamp,
127  outputNameClampTo32,
128  1.0f,
129  input0_zp,
130  0,
131  false,
132  false,
133  true,
134  true,
135  &op_clamp);
136  operators.emplace_back(op_clamp);
137  tensors.emplace_back(new TosaSerializationTensor(outputNameClampTo32,
138  inputShape0,
139  DType_INT32,
140  {}));
141 
142  TosaSerializationOperator *op_ge = new TosaSerializationOperator(Op_EQUAL,
143  Attribute_NONE,
144  nullptr,
145  {outputNameRescaleTo32, outputNameClampTo32},
146  {outputNameClampEq});
147  operators.push_back(op_ge);
148  tensors.push_back(new TosaSerializationTensor(outputNameClampEq, inputShape0, DType_BOOL, {}));
149 
150  // RESHAPE for outputNameRescaleSlope not required as already done on input2 by AddBroadCastReshapeLayer
151  TosaSerializationOperator* op_rescale_slope_in = nullptr;
152  CreateRescaleTosaOperator(inputName1,
153  outputNameRescaleSlope,
154  1.0f,
155  input1_zp,
156  0,
157  false,
158  false,
159  true,
160  true,
161  &op_rescale_slope_in);
162  operators.emplace_back(op_rescale_slope_in);
163  tensors.emplace_back(new TosaSerializationTensor(outputNameRescaleSlope,
164  inputShape1,
165  DType_INT32,
166  {}));
167  // mul shift
168  int32_t shift = 0;
169  TosaMulAttribute mulAttribute(shift);
170  TosaSerializationOperator *mulOp = new TosaSerializationOperator(Op_MUL,
171  Attribute_MulAttribute,
172  &mulAttribute,
173  {outputNameRescaleTo32,
174  outputNameRescaleSlope},
175  {outputNameMul});
176  operators.push_back(mulOp);
177  tensors.push_back(new TosaSerializationTensor(outputNameMul, outputShape0, DType_INT32, {}));
178 
179  TosaSerializationOperator *op_rescale_slope_in2 = nullptr;
180  CreateRescaleTosaOperator(outputNameMul,
181  outputNameRescaleSlope2,
182  scale_alpha,
183  0,
184  output_zp,
185  false,
186  false,
187  true,
188  true,
189  &op_rescale_slope_in2);
190  operators.push_back(op_rescale_slope_in2);
191  tensors.push_back(new TosaSerializationTensor(outputNameRescaleSlope2,
192  outputShape0,
193  outputDType0, {}));
194 
195  TosaSerializationOperator *op_rescale_identity_in = nullptr;
196  CreateRescaleTosaOperator(inputName0,
197  outputNameRescaleIdentity,
198  scale_identity,
199  input0_zp,
200  output_zp,
201  false,
202  false,
203  true,
204  true,
205  &op_rescale_identity_in);
206  operators.push_back(op_rescale_identity_in);
207  tensors.push_back(new TosaSerializationTensor(outputNameRescaleIdentity,
208  outputShape0,
209  outputDType0, {}));
210 
211  TosaSerializationOperator *selectOp = new TosaSerializationOperator(Op_SELECT,
212  Attribute_NONE,
213  nullptr,
214  {outputNameClampEq,
215  outputNameRescaleIdentity,
216  outputNameRescaleSlope2},
217  {outputName});
218  operators.push_back(selectOp);
219  tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}));
220 
221  // operatorInputNames/operatorOutputNames ends up being the same as
222  // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings
223  return new TosaSerializationBasicBlock(blockName, // name
224  mainName, // region name
225  operators, // operators
226  tensors, // tensors
227  {inputName0,inputName1},// inputs
228  {outputName}); // outputs
229 
230 }
#define ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(_cond, _str)
Definition: Exceptions.hpp:210
TosaSerializationBasicBlock * ConvertPReluToTosaOperator(const Layer *layer, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs)
std::string GenerateUniqueOutputName(const Layer &layer, uint32_t layerSlot=0)
const std::string mainName
DType ArmNNToDType(const DataType &type)
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
std::string GenerateUniqueInputName(const armnn::InputSlot &slot)
std::string GetUniqueTosaMappingID()
void CreateRescaleTosaOperator(const std::string &inputName, const std::string &outputName, double scale, int32_t input_zp, int32_t output_zp, bool input_unsigned, bool output_unsigned, bool double_round, bool scale32, TosaSerializationOperator **op)
Creates a Tosa rescale operator.
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:47
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition: Layer.hpp:337
constexpr bool IsQuantized8BitType(DataType dataType)
Definition: TypesUtils.hpp:317