ArmNN
 24.08
ReduceOperator.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2024 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 // Copyright © 2020 The TensorFlow Authors. All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7 //
8 
9 #include "ReduceOperator.hpp"
10 
11 #include <armnn/TypesUtils.hpp>
13 
14 // This function is paraphrased from:
15 // tensorflow/compiler/mlir/tosa/transforms/legalize_common.cc from functions convertReduceMeanOp, convertReduceSumOp,
16 // convertReduceOpCommon
17 TosaSerializationBasicBlock* ConvertReduceToTosaOperator(const Layer* layer,
18  const std::vector<const TensorInfo*>& inputs,
19  const std::vector<const TensorInfo*>& outputs,
20  const ReduceDescriptor* reduceDescriptor)
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.size() > 1)
35  {
36  throw armnn::Exception("ConvertReduceOperator: Reduce Operation with multiple axes not implemented.");
37  }
38 
39  if (reduceDescriptor->m_vAxis.empty())
40  {
41  throw armnn::Exception("ConvertReduceOperator: Reduce Operation with empty axis not implemented.");
42  }
43 
44  auto axis = static_cast<int32_t>(reduceDescriptor->m_vAxis[0]);
45  auto rank = static_cast<int32_t>(inputs[0]->GetNumDimensions());
46 
47  if (axis < 0 || axis >= rank)
48  {
49  throw armnn::Exception("Axis value not within range of input shape.");
50  }
51 
52  // Tensor names
53  std::string inputName = "input_";
54 
55  std::string outputNameRescale1 = "intermediate0_" + GetUniqueTosaMappingID();
56  std::string outputNameReduce = "intermediate1_" + GetUniqueTosaMappingID();
57  std::string outputNameRescale2 = "intermediate2_" + GetUniqueTosaMappingID();
58  std::string outputNameMul = "intermediate3_" + GetUniqueTosaMappingID();
59 
60  std::string outputName = "output0_";
61 
62  std::string reduceOpName = GetReduceOperationAsCString(reduceDescriptor->m_ReduceOperation);
63  std::string blockName = "Op_REDUCE_" + reduceOpName + "_block_" + GetUniqueTosaMappingID();
64 
65  std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape());
66  std::vector<int32_t> outputShape = GetTosaTensorShape(outputs[0]->GetShape());
67 
68  if (layer)
69  {
70  inputName = GenerateUniqueInputName(layer->GetInputSlot(0));
71  outputName = GenerateUniqueOutputName(*layer);
72  }
73 
74  std::vector<TosaSerializationTensor*> tensors;
75  std::vector<std::string> inputNames{inputName};
76 
77  DType inputType = ArmNNToDType(inputs[0]->GetDataType());
78 
79  if (inputName.substr(0, 6) == "input_")
80  {
81  tensors.emplace_back(new TosaSerializationTensor(inputName,
82  inputShape,
83  inputType,
84  {}));
85  }
86 
87  int32_t input_shift = 20;
88 
89  double input_scale = static_cast<double>(1 << input_shift) * inputs[0]->GetQuantizationScale();
90  double output_scale = 1.0 / (outputs[0]->GetQuantizationScale() * static_cast<double>(1 << input_shift));
91 
92  int32_t input_zp = 0;
93  int32_t output_zp = 0;
94 
95  std::vector<TosaSerializationOperator*> operators;
96 
97  // Conditional RESCALE
98  if (inputs[0]->IsQuantized())
99  {
100  TosaSerializationOperator* rescaleOp1 = nullptr;
101 
102  CreateRescaleTosaOperator(inputName,
103  outputNameRescale1,
104  input_scale,
105  input_zp,
106  output_zp,
107  true,
108  true,
109  &rescaleOp1);
110 
111  operators.emplace_back(rescaleOp1);
112 
113  tensors.emplace_back(new TosaSerializationTensor(outputNameRescale1,
114  inputShape,
115  DType_INT32,
116  {}));
117  }
118 
119  // REDUCE
120  TosaAxisAttribute reduceAttribute(axis);
121 
122  switch(reduceDescriptor->m_ReduceOperation)
123  {
124  case ReduceOperation::Sum:
125  case ReduceOperation::Mean:
126  operators.emplace_back(new TosaSerializationOperator(Op_REDUCE_SUM,
127  Attribute_AxisAttribute,
128  &reduceAttribute,
129  { tensors.back()->GetName() },
130  { outputNameReduce }));
131  break;
132  default:
133  throw armnn::Exception("ConvertReduceOperator: Reduce Operation not implemented.");
134  }
135 
136  std::vector<int32_t> outputShapeReduce = inputShape;
137  outputShapeReduce[reduceDescriptor->m_vAxis[0]] = 1;
138 
139  tensors.emplace_back(new TosaSerializationTensor(outputNameReduce,
140  outputShapeReduce,
141  tensors.back()->GetDtype(),
142  {}));
143 
144  // Conditional RESCALE
145  auto numElemsOnReducedAxis = inputShape[static_cast<unsigned long>(axis)];
146  float divScale = 1.0f / static_cast<float>(numElemsOnReducedAxis);
147  if (inputs[0]->IsQuantized())
148  {
149 
150  // if Mean, modify output_scale to account for 1/number of elements.
151  if (reduceDescriptor->m_ReduceOperation == ReduceOperation::Mean)
152  {
153  output_scale *= divScale;
154  }
155 
156  TosaSerializationOperator* rescaleOp2 = nullptr;
157 
158  CreateRescaleTosaOperator(outputNameReduce,
159  outputNameRescale2,
160  output_scale,
161  output_zp,
162  input_zp,
163  true,
164  true,
165  &rescaleOp2);
166 
167  operators.push_back(rescaleOp2);
168 
169  tensors.emplace_back(new TosaSerializationTensor(outputNameRescale2,
170  outputShapeReduce,
171  inputType,
172  {}));
173  }
174  else
175  {
176  // if Mean, add Mul to account for 1/number of elements.
177  if (reduceDescriptor->m_ReduceOperation == ReduceOperation::Mean)
178  {
179  // CONSTANT operator, value to multiply by
180  std::string divConstantName = std::string("constant_") + GetUniqueTosaMappingID();
181  inputNames.emplace_back(divConstantName);
182 
183  operators.push_back(new TosaSerializationOperator(Op_CONST,
184  Attribute_NONE,
185  nullptr,
186  {},
187  {divConstantName}));
188 
189  std::vector<uint8_t> uint8DivScale;
190  switch (inputType)
191  {
192  case DType_FP16:
193  TosaSerializationHandler::ConvertF16toU8({divScale}, uint8DivScale);
194  break;
195  case DType_FP32:
196  TosaSerializationHandler::ConvertF32toU8({divScale}, uint8DivScale);
197  break;
198  default:
199  throw armnn::Exception("ConvertReduceOperator: Data type not supported");
200  }
201 
202  std::vector<int32_t> divConstantShape (outputShapeReduce.size(), 1);
203  tensors.push_back(new TosaSerializationTensor(divConstantName,
204  divConstantShape,
205  inputType,
206  uint8DivScale));
207 
208  // MUL operator
209  int8_t shift = 0;
210  TosaMulAttribute mulAttribute(shift);
211 
212  operators.emplace_back(new TosaSerializationOperator(Op_MUL,
213  Attribute_MulAttribute,
214  &mulAttribute,
215  {divConstantName, outputNameReduce},
216  {outputNameMul}));
217  tensors.push_back(new TosaSerializationTensor(outputNameMul,
218  outputShapeReduce,
219  inputType,
220  {}));
221  }
222  }
223 
224  // RESHAPE
225  TosaReshapeAttribute reshapeAttribute(GetTosaTensorShape(outputs[0]->GetShape()));
226 
227  operators.emplace_back(new TosaSerializationOperator(Op_RESHAPE,
228  Attribute_ReshapeAttribute,
229  &reshapeAttribute,
230  { tensors.back()->GetName() },
231  { outputName }));
232 
233  tensors.emplace_back(new TosaSerializationTensor(outputName,
234  outputShape,
235  inputType,
236  {}));
237 
238  return new TosaSerializationBasicBlock(blockName, // name
239  mainName, // region name
240  operators, // operators
241  tensors, // tensors
242  inputNames, // inputs
243  { outputName }); // outputs
244 }
TypesUtils.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::ReduceDescriptor::m_ReduceOperation
ReduceOperation m_ReduceOperation
Specifies the reduction operation to execute.
Definition: Descriptors.hpp:1558
armnn::Layer
Definition: Layer.hpp:230
mainName
const std::string mainName
Definition: TosaOperatorUtils.hpp:19
ArmNNToDType
DType ArmNNToDType(const DataType &type)
Definition: TosaOperatorUtils.hpp:22
ReduceOperator.hpp
armnn::Exception
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
ConvertReduceToTosaOperator
TosaSerializationBasicBlock * ConvertReduceToTosaOperator(const Layer *layer, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs, const ReduceDescriptor *reduceDescriptor)
Definition: ReduceOperator.cpp:17
armnn::GetReduceOperationAsCString
constexpr char const * GetReduceOperationAsCString(ReduceOperation reduce_operation)
Definition: TypesUtils.hpp:170
armnn::ReduceDescriptor::m_vAxis
std::vector< uint32_t > m_vAxis
The indices of the dimensions to reduce.
Definition: Descriptors.hpp:1556
GetTosaTensorShape
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
Definition: TosaOperatorUtils.hpp:79
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::ReduceDescriptor
A ReduceDescriptor for the REDUCE operators.
Definition: Descriptors.hpp:1538
GetUniqueTosaMappingID
std::string GetUniqueTosaMappingID()
Definition: TosaOperatorUtils.hpp:138