ArmNN
 24.08
ElementwiseBinaryOperator.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
8 
9 TosaSerializationOperator* AddRescaleOp(const string &inputName,
10  const string &outputName,
11  std::vector<TosaSerializationTensor *> &tensors,
12  const std::vector<const TensorInfo *> &inputs,
13  const std::vector<const TensorInfo *> &outputs)
14 {
15  double scale_alpha = inputs[1]->GetQuantizationScale() / outputs[0]->GetQuantizationScale();
16  int32_t input_zp = inputs[1]->GetQuantizationOffset();
17  int32_t output_zp = outputs[0]->GetQuantizationOffset();
18 
19  TosaSerializationOperator* rescaleOp = nullptr;
20  CreateRescaleTosaOperator(inputName,
21  outputName,
22  scale_alpha,
23  input_zp,
24  output_zp,
25  true,
26  true,
27  &rescaleOp);
28 
29  std::vector<int32_t> shape = GetTosaTensorShape(inputs[1]->GetShape());
30  tensors.push_back(new TosaSerializationTensor(outputName,
31  shape,
32  DType_INT32, {}));
33  return rescaleOp;
34 }
35 
36 TosaSerializationBasicBlock* ConvertElementwiseBinaryToTosaOperator(const Layer* layer,
37  const LayerType type,
38  const std::vector<const TensorInfo*>& inputs,
39  const std::vector<const TensorInfo*>& outputs,
40  const ElementwiseBinaryDescriptor* descriptor)
41 {
42  std::string input0Name = std::string("input_0");
43  std::string input1Name = std::string("input_1");
44  std::string outputName = std::string("output0_");
45  std::string input0ElemenwiseBinaryName = std::string("intermediate0_") + GetUniqueTosaMappingID();
46  std::string input1ElemenwiseBinaryName = std::string("intermediate0_") + GetUniqueTosaMappingID();
47  std::string blockName;
48 
49  // If a layer is present then the block will be used for execution, so input and output names need to be determined
50  // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter.
51  if(layer != nullptr)
52  {
53  input0Name = GenerateUniqueInputName(layer->GetInputSlot(0));
54  input1Name = GenerateUniqueInputName(layer->GetInputSlot(1));
55  outputName = GenerateUniqueOutputName(*layer);
56  }
57 
58  TosaSerializationOperator* op = nullptr;
59 
60  std::vector<TosaSerializationTensor*> tensors;
61  std::vector<TosaSerializationOperator*> operators;
62  DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType());
63  DType inputDType1 = ArmNNToDType(inputs[1]->GetDataType());
64  DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType());
65  bool isInputInt8 = (inputDType0 == DType_INT8);
66 
67  // Only add input tensors if connected layer is an input layer.
68  // As intermediate or constant tensors will be created separately.
69  // There also can't be duplicate tensor.
70  if(input0Name.find("input_") != std::string::npos)
71  {
72  std::vector<int32_t> inputShape0 = GetTosaTensorShape(inputs[0]->GetShape());
73  tensors.push_back(new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {}));
74  }
75  if(input1Name.find("input_") != std::string::npos)
76  {
77  std::vector<int32_t> inputShape1 = GetTosaTensorShape(inputs[1]->GetShape());
78  tensors.push_back(new TosaSerializationTensor(input1Name, inputShape1, inputDType1, {}));
79  }
80 
81  std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputs[0]->GetShape());
82 
83  // Assign an output name and add to tensors based on the input type
84  // An int8 input for all ops will require the output to be rescaled from int32 to int8
85  std::string outputElemenwiseBinaryName;
86  if (isInputInt8)
87  {
88  outputElemenwiseBinaryName = std::string("intermediate0_") + GetUniqueTosaMappingID();
89  tensors.push_back(new TosaSerializationTensor(outputElemenwiseBinaryName, outputShape0, DType_INT32, {}));
90  }
91  else
92  {
93  tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}));
94  }
95 
96  std::string& elementwiseInput0Str = isInputInt8 ? input0ElemenwiseBinaryName : input0Name;
97  std::string& elementwiseInput1Str = isInputInt8 ? input1ElemenwiseBinaryName : input1Name;
98  std::string& elementwiseOutputStr = isInputInt8 ? outputElemenwiseBinaryName : outputName;
99  switch(type)
100  {
101  case LayerType::Addition:
102  {
103  op = new TosaSerializationOperator(Op_ADD,
104  Attribute_NONE,
105  nullptr,
106  {input0Name, input1Name},
107  {outputName});
108  blockName = std::string("Op_ADD_block_") + GetUniqueTosaMappingID();
109  break;
110  }
111  case LayerType::ElementwiseBinary:
112  {
113  switch (descriptor->m_Operation)
114  {
116  {
117  // Add supports DType_INT32 input only, so a rescale is required when input is DType_INT8
118  if (inputDType0 == DType_INT8)
119  {
120  operators.push_back(
121  AddRescaleOp(input0Name, input0ElemenwiseBinaryName, tensors, inputs, outputs));
122 
123  operators.push_back(
124  AddRescaleOp(input1Name, input1ElemenwiseBinaryName, tensors, inputs, outputs));
125  }
126  op = new TosaSerializationOperator(Op_ADD,
127  Attribute_NONE,
128  nullptr,
129  {elementwiseInput0Str, elementwiseInput1Str},
130  {elementwiseOutputStr});
131  blockName = std::string("Op_ADD_block_") + GetUniqueTosaMappingID();
132  break;
133  }
135  {
136  // Add supports DType_INT32 input only, so a rescale is required when input is DType_INT8
137  if (inputDType0 == DType_INT8)
138  {
139  operators.push_back(
140  AddRescaleOp(input0Name, input0ElemenwiseBinaryName, tensors, inputs, outputs));
141 
142  operators.push_back(
143  AddRescaleOp(input1Name, input1ElemenwiseBinaryName, tensors, inputs, outputs));
144  }
145  op = new TosaSerializationOperator(Op_MAXIMUM,
146  Attribute_NONE,
147  nullptr,
148  {elementwiseInput0Str, elementwiseInput1Str},
149  {elementwiseOutputStr});
150  blockName = std::string("Op_MAXIMUM_block_") + GetUniqueTosaMappingID();
151  break;
152  }
154  {
155  int8_t shift = 0;
156  TosaMulAttribute mulAttribute(shift);
157 
158  // Mul supports input DType_INT8 so will not require a rescale before the op.
159  // i.e "input0Name" is used for the input and not intermediate "elementwiseInput0Str"
160  op = new TosaSerializationOperator(Op_MUL,
161  Attribute_MulAttribute,
162  &mulAttribute,
163  {input0Name, input1Name},
164  {elementwiseOutputStr});
165  blockName = std::string("Op_MUL_block_") + GetUniqueTosaMappingID();
166  break;
167  }
169  {
170  // Sub supports DType_INT32 input only, so a rescale is required when input is DType_INT8
171  if (inputDType0 == DType_INT8)
172  {
173  operators.push_back(
174  AddRescaleOp(input0Name, input0ElemenwiseBinaryName, tensors, inputs, outputs));
175 
176  operators.push_back(
177  AddRescaleOp(input1Name, input1ElemenwiseBinaryName, tensors, inputs, outputs));
178  }
179 
180  op = new TosaSerializationOperator(Op_SUB,
181  Attribute_NONE,
182  nullptr,
183  {elementwiseInput0Str, elementwiseInput1Str},
184  {elementwiseOutputStr});
185  blockName = std::string("Op_SUB_block_") + GetUniqueTosaMappingID();
186  break;
187  }
188  default:
189  throw armnn::Exception("ConvertElementwiseBinaryToTosaOperator: Unsupported layer type.");
190  }
191  break;
192  }
193  case LayerType::Multiplication:
194  {
195  int32_t shift = 0;
196  TosaMulAttribute mulAttribute(shift);
197  op = new TosaSerializationOperator(Op_MUL,
198  Attribute_MulAttribute,
199  &mulAttribute,
200  {input0Name, input1Name},
201  {outputName});
202  blockName = std::string("Op_MUL_block_") + GetUniqueTosaMappingID();
203  break;
204  }
205  case LayerType::Subtraction:
206  {
207  op = new TosaSerializationOperator(Op_SUB,
208  Attribute_NONE,
209  nullptr,
210  {input0Name, input1Name},
211  {outputName});
212  blockName = std::string("Op_SUB_block_") + GetUniqueTosaMappingID();
213  break;
214  }
215  default:
216  throw armnn::Exception("ConvertElementwiseBinaryToTosaOperator: Unsupported layer type.");
217  }
218 
219  operators.push_back(op);
220 
221  // All operators require a rescale of the output from DType_INT32 to DType_INT8 when the input is DType_INT8
222  if (inputDType0 == DType_INT8)
223  {
224  operators.push_back(
225  AddRescaleOp(outputElemenwiseBinaryName, outputName, tensors, inputs, outputs));
226  }
227 
228  return new TosaSerializationBasicBlock(blockName, // name
229  mainName, // region name
230  {operators}, // operators
231  tensors, // tensors
232  {input0Name, input1Name}, // inputs
233  {outputName}); // outputs
234 }
235 
armnn::BinaryOperation::Mul
@ Mul
armnn::BinaryOperation::Add
@ Add
ConvertElementwiseBinaryToTosaOperator
TosaSerializationBasicBlock * ConvertElementwiseBinaryToTosaOperator(const Layer *layer, const LayerType type, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs, const ElementwiseBinaryDescriptor *descriptor)
Definition: ElementwiseBinaryOperator.cpp:36
armnn::BinaryOperation::Sub
@ Sub
ElementwiseBinaryOperator.hpp
TosaRescaleOperatorUtils.hpp
GenerateUniqueOutputName
std::string GenerateUniqueOutputName(const Layer &layer, uint32_t layerSlot=0)
Definition: TosaOperatorUtils.hpp:120
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
armnn::ElementwiseBinaryDescriptor
A ElementwiseBinaryDescriptor for the ElementwiseBinaryLayer.
Definition: Descriptors.hpp:109
mainName
const std::string mainName
Definition: TosaOperatorUtils.hpp:19
armnn::BinaryOperation::Maximum
@ Maximum
ArmNNToDType
DType ArmNNToDType(const DataType &type)
Definition: TosaOperatorUtils.hpp:22
armnn::Exception
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
GetTosaTensorShape
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
Definition: TosaOperatorUtils.hpp:79
armnn::ElementwiseBinaryDescriptor::m_Operation
BinaryOperation m_Operation
Specifies the elementwiseBinary operation to execute.
Definition: Descriptors.hpp:125
AddRescaleOp
TosaSerializationOperator * AddRescaleOp(const string &inputName, const string &outputName, std::vector< TosaSerializationTensor * > &tensors, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs)
Definition: ElementwiseBinaryOperator.cpp:9
CreateRescaleTosaOperator
void CreateRescaleTosaOperator(const std::string &inputName, const std::string &outputName, const std::vector< int32_t > &multipliers, const std::vector< int32_t > &shifts, int32_t input_zp, int32_t output_zp, bool double_round, bool scale32, bool per_channel, TosaSerializationOperator **op)
Definition: TosaRescaleOperatorUtils.hpp:10
GenerateUniqueInputName
std::string GenerateUniqueInputName(const armnn::InputSlot &slot)
Definition: TosaOperatorUtils.hpp:109
armnn::LayerType
LayerType
When adding a new layer, adapt also the LastLayer enum value in the enum class LayerType below.
Definition: Types.hpp:491
GetUniqueTosaMappingID
std::string GetUniqueTosaMappingID()
Definition: TosaOperatorUtils.hpp:138