ONE - On-device Neural Engine
Loading...
Searching...
No Matches
OperationUtils.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "OperationUtils.h"
18
19#include <algorithm>
20#include <cassert>
21#include <cmath>
22
23namespace onert
24{
25namespace backend
26{
27namespace cpu
28{
29namespace ops
30{
31
33{
34 assert(tensor);
35 return tensor->getShape().rank();
36}
37
38uint32_t getNumberOfElements(const IPortableTensor *tensor)
39{
40 assert(tensor);
41 uint32_t count = 1;
42 auto shape = tensor->getShape();
43 for (int i = 0; i < shape.rank(); i++)
44 {
45 count *= shape.dim(i);
46 }
47 return count;
48}
49
50uint32_t getSizeOfDimension(const IPortableTensor *tensor, uint32_t dimensionIdx)
51{
52 assert(tensor);
53 auto shape = tensor->getShape();
54 if (dimensionIdx >= static_cast<uint32_t>(shape.rank()))
55 {
56 // TODO, log the error
57 return 0;
58 }
59 return shape.dim(dimensionIdx);
60}
61
62void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
63{
64 if (double_multiplier == 0.)
65 {
66 *quantized_multiplier = 0;
67 *shift = 0;
68 return;
69 }
70 const double q = std::frexp(double_multiplier, shift);
71 auto q_fixed = static_cast<int64_t>(std::round(q * (1ll << 31)));
72
73 assert(q_fixed <= (1ll << 31));
74 if (q_fixed == (1ll << 31))
75 {
76 q_fixed /= 2;
77 ++*shift;
78 }
79 assert(q_fixed <= std::numeric_limits<int32_t>::max());
80 *quantized_multiplier = static_cast<int32_t>(q_fixed);
81}
82
84 const IPortableTensor *bias, const IPortableTensor *output,
85 double *multiplier)
86{
87 const double input_product_scale = input->data_scale() * filter->data_scale();
88 [[maybe_unused]] const double bias_scale =
89 (bias != nullptr) ? bias->data_scale() : input_product_scale;
90 const double output_scale = output->data_scale();
91 // The following conditions must be guaranteed by the training pipeline.
92 assert(std::abs(input_product_scale - bias_scale) <=
93 1e-6 * std::min(input_product_scale, bias_scale));
94 assert(input_product_scale >= 0);
95 assert(input_product_scale < output_scale);
96 *multiplier = input_product_scale / output_scale;
97}
98
100 float input_scale, float output_scale, const float *filter_scales, size_t filter_scales_size,
101 int num_channels, std::vector<int32_t> &per_channel_output_multiplier,
102 std::vector<int> &per_channel_output_shift)
103{
104 // Originates from tflite's PopulateConvolutionQuantizationParams()
105 per_channel_output_multiplier.resize(num_channels);
106 per_channel_output_shift.resize(num_channels);
107
108 const bool is_per_channel = filter_scales_size > 1;
109 auto per_channel_multiplier = per_channel_output_multiplier.data();
110 auto per_channel_shift = per_channel_output_shift.data();
111 for (int i = 0; i < num_channels; ++i)
112 {
113 // If per-tensor quantization parameter is specified, broadcast it along the
114 // quantization dimension (channels_out).
115 const float scale = is_per_channel ? filter_scales[i] : filter_scales[0];
116 const double filter_scale = static_cast<double>(scale);
117 const double effective_output_scale =
118 static_cast<double>(input_scale) * filter_scale / static_cast<double>(output_scale);
119 int32_t significand;
120 int channel_shift;
121 QuantizeMultiplier(effective_output_scale, &significand, &channel_shift);
122 per_channel_multiplier[i] = significand;
123 per_channel_shift[i] = channel_shift;
124 }
125}
126
127void QuantizeMultiplierGreaterThanOne(double double_multiplier, int32_t *quantized_multiplier,
128 int *left_shift)
129{
130 assert(double_multiplier > 1.);
131 const double q = std::frexp(double_multiplier, left_shift);
132 int64_t q_fixed = static_cast<int64_t>(std::round(q * (1ll << 31)));
133 assert(q_fixed <= (1ll << 31));
134 if (q_fixed == (1ll << 31))
135 {
136 q_fixed /= 2;
137 ++*left_shift;
138 }
139 assert(*left_shift >= 0);
140 assert(q_fixed <= std::numeric_limits<int32_t>::max());
141 *quantized_multiplier = static_cast<int32_t>(q_fixed);
142}
143
145 int32_t *act_min, int32_t *act_max)
146{
147 int32_t qmin = 0;
148 int32_t qmax = 0;
149
150 switch (output->data_type())
151 {
152 case OperandType::QUANT_UINT8_ASYMM:
153 qmin = std::numeric_limits<uint8_t>::min();
154 qmax = std::numeric_limits<uint8_t>::max();
155 break;
156 case OperandType::QUANT_INT8_ASYMM:
157 case OperandType::QUANT_INT8_SYMM:
158 qmin = std::numeric_limits<int8_t>::min();
159 qmax = std::numeric_limits<int8_t>::max();
160 break;
161 default:
162 throw std::runtime_error("CalculateActivationRangeQuantized: Not supported operand type.");
163 }
164
165 const auto scale = output->data_scale();
166 const auto zero_point = output->data_zero_point();
167 auto quantize = [scale, zero_point](float f) {
168 return zero_point + static_cast<int32_t>(std::round(f / scale));
169 };
170 if (activation == ir::Activation::RELU)
171 {
172 *act_min = std::max(qmin, quantize(0.0));
173 *act_max = qmax;
174 }
175 else if (activation == ir::Activation::RELU6)
176 {
177 *act_min = std::max(qmin, quantize(0.0));
178 *act_max = std::min(qmax, quantize(6.0));
179 }
180 else if (activation == ir::Activation::RELU1)
181 {
182 *act_min = std::max(qmin, quantize(-1.0));
183 *act_max = std::min(qmax, quantize(1.0));
184 }
185 else if (activation == ir::Activation::SIGMOID)
186 {
187 *act_min = std::max(qmin, quantize(0.0));
188 *act_max = std::min(qmax, quantize(1.0));
189 }
190 else if (activation == ir::Activation::NONE)
191 {
192 *act_min = qmin;
193 *act_max = qmax;
194 }
195 else
196 {
197 throw std::runtime_error{"Unsupported fused activation function."};
198 }
199}
200
201bool HaveSameShapes(const IPortableTensor *input1, const IPortableTensor *input2)
202{
203 if (input1 == input2)
204 return true;
205 if (input2 == NULL || input2 == NULL)
206 return false;
207
208 if (input1 == NULL)
209 {
210 return (getNumberOfDimensions(input2) == 0);
211 }
212
213 if (getNumberOfDimensions(input1) != getNumberOfDimensions(input2))
214 return false;
215
216 auto shape1 = input1->getShape();
217 auto shape2 = input2->getShape();
218 for (uint32_t i = 0; i < getNumberOfDimensions(input1); i++)
219 if (shape1.dim(i) != shape2.dim(i))
220 return false;
221
222 return true;
223}
224
225int32_t CalculateInputRadius(int input_integer_bits, int input_left_shift)
226{
227 const double max_input_rescaled = 1.0 * ((1 << input_integer_bits) - 1) *
228 (1ll << (31 - input_integer_bits)) / (1ll << input_left_shift);
229 // Tighten bound using floor. Suppose that we could use the exact value.
230 // After scaling the difference, the result would be at the maximum. Thus we
231 // must ensure that our value has lower magnitude.
232 return static_cast<int32_t>(std::floor(max_input_rescaled));
233}
234
235uint32_t sizeOfData(OperandType type, const std::vector<int32_t> &dimensions)
236{
237 uint32_t size = 4;
238
239 switch (type)
240 {
241 case OperandType::FLOAT32:
242 case OperandType::INT32:
243 case OperandType::UINT32:
244 size = 4;
245 break;
246 case OperandType::BOOL8:
247 case OperandType::QUANT_UINT8_ASYMM:
248 case OperandType::QUANT_INT8_SYMM:
249 size = 1;
250 break;
251 case OperandType::INT64:
252 size = 8;
253 break;
254 default:
255 throw std::runtime_error("Not supported operand type.");
256 break;
257 }
258
259 for (auto &&d : dimensions)
260 {
261 assert(d >= 0);
262 size *= static_cast<uint32_t>(d);
263 }
264
265 return size;
266}
267
269{
270 switch (ir_padding_type)
271 {
278 default:
279 throw std::runtime_error("Wrong padding type.");
280 break;
281 }
282}
283
284std::vector<int32_t> getReducerAxes(const IPortableTensor *axes)
285{
286 std::vector<int32_t> ret;
287
288 auto axes_vals = (axes->getShape().rank() == 0) ? 1 : axes->getShape().dim(0);
289 assert(static_cast<size_t>(axes_vals) == axes->getShape().num_elements());
290 switch (axes->data_type())
291 {
292 case ir::DataType::INT32:
293 {
294 for (int i = 0; i < axes_vals; ++i)
295 ret.emplace_back(*(getBuffer<int32_t>(axes) + i));
296 break;
297 }
298 case ir::DataType::INT64:
299 {
300 for (int i = 0; i < axes_vals; ++i)
301 ret.emplace_back(*(getBuffer<int64_t>(axes) + i));
302 break;
303 }
304 default:
305 throw std::runtime_error("getReducerAxes: Not supported data type");
306 break;
307 }
308 return ret;
309}
310
312{
313 switch (rope_mode)
314 {
319 default:
320 throw std::runtime_error("Wrong rope mode.");
321 break;
322 }
323}
324
325} // namespace ops
326} // namespace cpu
327} // namespace backend
328} // namespace onert
OperandType
Definition OperandType.h:24
A tensor class that is portable for other backends.
ir::DataType data_type() const override final
ir::Shape getShape() const override final
Get ir::Shape of tensor.
uint32_t getNumberOfElements(const Shape &shape)
Definition Shape.cpp:48
uint32_t getSizeOfDimension(const Shape &shape, uint32_t dimensionIdx)
Definition Shape.cpp:60
uint32_t getNumberOfDimensions(const Shape &shape)
Definition Shape.cpp:58
PaddingType
Definition Types.h:41
int32_t CalculateInputRadius(int input_integer_bits, int input_left_shift)
nnfw::cker::RoPEMode getRoPEMode(ir::operation::RoPE::RoPEMode rope_mode)
void GetQuantizedConvolutionMultipliersAndShifts(float input_scale, float output_scale, const float *filter_scales, size_t filter_scales_size, int num_channels, std::vector< int32_t > &per_channel_output_multiplier, std::vector< int > &per_channel_output_shift)
void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
nnfw::cker::PaddingType getPaddingType(ir::PaddingType ir_padding_type)
uint32_t sizeOfData(OperandType type, const std::vector< int32_t > &dimensions)
void QuantizeMultiplierGreaterThanOne(double double_multiplier, int32_t *quantized_multiplier, int *left_shift)
std::vector< int32_t > getReducerAxes(const IPortableTensor *axes)
void CalculateActivationRangeQuantized(ir::Activation activation, const IPortableTensor *output, int32_t *act_min, int32_t *act_max)
void GetQuantizedConvolutionMultiplier(const IPortableTensor *input, const IPortableTensor *filter, const IPortableTensor *bias, const IPortableTensor *output, double *multiplier)
bool HaveSameShapes(const IPortableTensor *input1, const IPortableTensor *input2)
int32_t size[5]
Definition Slice.cpp:35