ArmNN
 25.02
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ReduceOperator.cpp File Reference
Include dependency graph for ReduceOperator.cpp:

Go to the source code of this file.

Functions

TosaSerializationBasicBlock * ConvertReduceToTosaOperator (const Layer *layer, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs, const ReduceDescriptor *reduceDescriptor)
 

Function Documentation

◆ ConvertReduceToTosaOperator()

TosaSerializationBasicBlock* ConvertReduceToTosaOperator ( const Layer layer,
const std::vector< const TensorInfo * > &  inputs,
const std::vector< const TensorInfo * > &  outputs,
const ReduceDescriptor reduceDescriptor 
)

Definition at line 17 of file ReduceOperator.cpp.

21 {
22  // Early exits
23  if (!inputs[0])
24  {
25  throw armnn::Exception("ConvertReduceOperator: Must provide a valid input tensor.");
26  }
27 
28  if (inputs[0]->IsQuantized() ^ outputs[0]->IsQuantized())
29  {
30  throw armnn::Exception("ConvertReduceOperator: "
31  "Both input and output tensors must be either quantised or non-quantised data types.");
32  }
33 
34  if (reduceDescriptor->m_vAxis.empty())
35  {
36  throw armnn::Exception("ConvertReduceOperator: Reduce Operation with empty axis not implemented.");
37  }
38 
39  // Tensor names
40  std::string inputName = "input_";
41 
42  std::size_t intermediateCounter = 0;
43 
44  std::string outputName = "output0_";
45 
46  std::string reduceOpName = GetReduceOperationAsCString(reduceDescriptor->m_ReduceOperation);
47  std::string blockName = "Op_REDUCE_" + reduceOpName + "_block_" + GetUniqueTosaMappingID();
48 
49  std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape());
50  std::vector<int32_t> outputShape = GetTosaTensorShape(outputs[0]->GetShape());
51 
52  if (layer)
53  {
54  inputName = GenerateUniqueInputName(layer->GetInputSlot(0));
55  outputName = GenerateUniqueOutputName(*layer);
56  }
57 
58  std::vector<TosaSerializationTensor*> tensors;
59  std::vector<std::string> inputNames{inputName};
60 
61  DType inputType = ArmNNToDType(inputs[0]->GetDataType());
62 
63  if (inputName.substr(0, 6) == "input_")
64  {
65  tensors.emplace_back(new TosaSerializationTensor(inputName,
66  inputShape,
67  inputType,
68  {}));
69  }
70 
71  int64_t input_zp = 0;
72  int64_t output_zp = 0;
73 
74  double input_scale = 1.0;
75  double output_scale = 1.0;
76 
77  int32_t input_multiplier = 1;
78  int32_t output_multiplier = 1;
79 
80  int32_t input_shift = 0;
81  int32_t output_shift = 0;
82 
83  int64_t numElemsOnReducedAxis = 1;
84 
85  std::vector<int32_t> axes(reduceDescriptor->m_vAxis.begin(), reduceDescriptor->m_vAxis.end());
86 
87  for (int64_t axis : axes)
88  {
89  numElemsOnReducedAxis *= inputShape[static_cast<uint64_t>(axis)];
90  }
91 
92  std::vector<TosaSerializationOperator*> operators;
93 
94  bool inputQuantised = inputs[0]->IsQuantized();
95 
96  // Conditional RESCALE
97  if (inputQuantised)
98  {
99  input_zp = inputs[0]->GetQuantizationOffset();
100  output_zp = outputs[0]->GetQuantizationOffset();
101 
102  std::string outputNameRescale =
103  "intermediate" + std::to_string(intermediateCounter++) + "_" + GetUniqueTosaMappingID();
104 
105  TosaSerializationOperator* rescaleOp1 = nullptr;
106 
107  switch(reduceDescriptor->m_ReduceOperation)
108  {
109  case ReduceOperation::Sum:
110  input_shift = 20;
111 
112  input_scale = static_cast<double>(1 << input_shift) * inputs[0]->GetQuantizationScale();
113  output_scale = 1.0 / (outputs[0]->GetQuantizationScale() * static_cast<double>(1 << input_shift));
114 
115  CreateRescaleTosaOperator(inputName,
116  outputNameRescale,
117  input_scale,
118  static_cast<int32_t>(input_zp),
119  0,
120  false,
121  false,
122  true,
123  true,
124  &rescaleOp1);
125 
126  break;
127  case ReduceOperation::Mean:
128  {
129  // calculate shifts and multipliers
130  ComputeMultiplierAndShiftTosaScale32(1.0, input_multiplier, input_shift);
132  (
133  static_cast<double>(inputs[0]->GetQuantizationScale()) /
134  static_cast<double>(outputs[0]->GetQuantizationScale()),
135  output_multiplier,
136  output_shift
137  );
138 
139  int shift = 63 - __builtin_clzl(static_cast<uint64_t>(numElemsOnReducedAxis));
140  shift = std::min(shift, 32);
141  shift = std::min(shift, 62 - output_shift);
142 
143  output_multiplier = static_cast<int32_t>(
144  (static_cast<int64_t>(output_multiplier) << shift) / numElemsOnReducedAxis);
145 
146  output_shift += shift;
147 
149  outputNameRescale,
150  {input_multiplier},
151  {input_shift},
152  static_cast<int32_t>(input_zp),
153  0,
154  false,
155  false,
156  true,
157  true,
158  false,
159  &rescaleOp1);
160  break;
161  }
162  default:
163  throw armnn::Exception("ConvertReduceOperator: Reduce Operation not implemented.");
164  }
165 
166  operators.emplace_back(rescaleOp1);
167 
168  tensors.emplace_back(new TosaSerializationTensor(outputNameRescale,
169  inputShape,
170  DType_INT32,
171  {}));
172  }
173 
174  // REDUCE_SUM
175  for (const auto axis : axes)
176  {
177  auto rank = static_cast<int64_t>(inputs[0]->GetNumDimensions());
178 
179  if (axis < 0 || axis >= rank)
180  {
181  throw armnn::Exception("Axis value not within range of input shape.");
182  }
183 
184  TosaAxisAttribute reduceAttribute(axis);
185 
186  std::string outputNameReduce =
187  "intermediate" + std::to_string(intermediateCounter++) + "_" + GetUniqueTosaMappingID();
188 
189  switch(reduceDescriptor->m_ReduceOperation)
190  {
191  case ReduceOperation::Sum:
192  case ReduceOperation::Mean:
193  operators.emplace_back(new TosaSerializationOperator(Op_REDUCE_SUM,
194  Attribute_AxisAttribute,
195  &reduceAttribute,
196  { tensors.back()->GetName() },
197  { outputNameReduce }));
198  break;
199  default:
200  throw armnn::Exception("ConvertReduceOperator: Reduce Operation not implemented.");
201  }
202 
203  std::vector<int32_t> outputShapeReduce = tensors.back()->GetShape();
204  outputShapeReduce[static_cast<std::size_t>(axis)] = 1;
205 
206  tensors.emplace_back(new TosaSerializationTensor(outputNameReduce,
207  outputShapeReduce,
208  tensors.back()->GetDtype(),
209  {}));
210  }
211 
212  // Conditional RESCALE
213  if (inputQuantised)
214  {
215  std::string outputNameRescale =
216  "intermediate" + std::to_string(intermediateCounter++) + "_" + GetUniqueTosaMappingID();
217 
218  TosaSerializationOperator* rescaleOp2 = nullptr;
219 
220  switch(reduceDescriptor->m_ReduceOperation)
221  {
222  case ReduceOperation::Sum:
223  CreateRescaleTosaOperator(tensors.back()->GetName(),
224  outputNameRescale,
225  output_scale,
226  0,
227  static_cast<int32_t>(output_zp),
228  false,
229  false,
230  true,
231  true,
232  &rescaleOp2);
233  break;
234  case ReduceOperation::Mean:
235  CreateRawRescaleTosaOperator(tensors.back()->GetName(),
236  outputNameRescale,
237  {output_multiplier},
238  {output_shift},
239  0,
240  static_cast<int32_t>(output_zp),
241  false,
242  false,
243  true,
244  true,
245  false,
246  &rescaleOp2);
247  break;
248  default:
249  throw armnn::Exception("ConvertReduceOperator: Reduce Operation not implemented.");
250  }
251 
252  operators.push_back(rescaleOp2);
253 
254  tensors.emplace_back(new TosaSerializationTensor(outputNameRescale,
255  tensors.back()->GetShape(),
256  inputType,
257  {}));
258  }
259 
260  // RESHAPE
261  TosaReshapeAttribute reshapeAttribute(GetTosaTensorShape(outputs[0]->GetShape()));
262 
263  std::string outputNameReshape = !inputQuantised && reduceDescriptor->m_ReduceOperation == ReduceOperation::Mean
264  ? "intermediate" + std::to_string(intermediateCounter++) + "_" + GetUniqueTosaMappingID()
265  : outputName;
266 
267  operators.emplace_back(new TosaSerializationOperator(Op_RESHAPE,
268  Attribute_ReshapeAttribute,
269  &reshapeAttribute,
270  { tensors.back()->GetName() },
271  { outputNameReshape }));
272 
273  tensors.emplace_back(new TosaSerializationTensor(outputNameReshape,
274  outputShape,
275  inputType,
276  {}));
277 
278  // Conditional MUL
279  // Multiply previous tensor by constant of 1 / number of elements
280  if (!inputQuantised && reduceDescriptor->m_ReduceOperation == ReduceOperation::Mean)
281  {
282  // Constant
283  std::string constNameDivScale = "constant_" + GetUniqueTosaMappingID();
284  inputNames.emplace_back(constNameDivScale);
285 
286  operators.push_back(new TosaSerializationOperator(Op_CONST,
287  Attribute_NONE,
288  nullptr,
289  {},
290  { constNameDivScale }));
291 
292  float divScale = 1.0f / static_cast<float>(numElemsOnReducedAxis);
293 
294  std::vector<uint8_t> uint8DivScale;
295  switch (inputType)
296  {
297  case DType_FP32:
298  TosaSerializationHandler::ConvertF32toU8({divScale}, uint8DivScale);
299  break;
300  case DType_FP16:
301  TosaSerializationHandler::ConvertF16toU8({divScale}, uint8DivScale);
302  break;
303  default:
304  throw armnn::Exception("ConvertReduceOperator: Data type not supported");
305  }
306 
307  // Broadcast to match shapes
308  std::vector<int32_t> divConstantShape(outputShape.size(), 1);
309 
310  tensors.push_back(new TosaSerializationTensor(constNameDivScale,
311  divConstantShape,
312  inputType,
313  uint8DivScale));
314 
315  // MUL
316  TosaMulAttribute mulAttribute(0);
317 
318  operators.emplace_back(new TosaSerializationOperator(Op_MUL,
319  Attribute_MulAttribute,
320  &mulAttribute,
321  { constNameDivScale, outputNameReshape },
322  { outputName }));
323 
324  tensors.push_back(new TosaSerializationTensor(outputName,
325  outputShape,
326  inputType,
327  {}));
328  }
329 
330  return new TosaSerializationBasicBlock(blockName, // name
331  mainName, // region name
332  operators, // operators
333  tensors, // tensors
334  inputNames, // inputs
335  { outputName }); // outputs
336 }
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 CreateRawRescaleTosaOperator(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 input_unsigned, bool output_unsigned, bool double_round, bool scale32, bool per_channel, TosaSerializationOperator **op)
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)
void ComputeMultiplierAndShiftTosaScale32(double scale, int32_t &multiplier, int32_t &shift)
The following is taken from mlir/lib/Dialect/Tosa/Utils/QuantUtils.cpp in the LLVM project From a sca...
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 char const * GetReduceOperationAsCString(ReduceOperation reduce_operation)
Definition: TypesUtils.hpp:171
std::vector< uint32_t > m_vAxis
The indices of the dimensions to reduce.
ReduceOperation m_ReduceOperation
Specifies the reduction operation to execute.

References armnn::GetReduceOperationAsCString(), GetTosaTensorShape(), GetUniqueTosaMappingID(), ReduceDescriptor::m_ReduceOperation, and ReduceDescriptor::m_vAxis.

Referenced by GetTosaMapping().