Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / runtime / onert / backend / cpu / ops / OperationUtils.cc
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
23 namespace onert
24 {
25 namespace backend
26 {
27 namespace cpu
28 {
29 namespace ops
30 {
31
32 uint32_t getNumberOfDimensions(const IPortableTensor *tensor)
33 {
34   assert(tensor);
35   return tensor->getShape().rank();
36 }
37
38 uint32_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
50 uint32_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
62 void 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
83 void GetQuantizedConvolutionMultiplier(const IPortableTensor *input, const IPortableTensor *filter,
84                                        const IPortableTensor *bias, const IPortableTensor *output,
85                                        double *multiplier)
86 {
87   const double input_product_scale = input->data_scale() * filter->data_scale();
88   const double bias_scale = (bias != nullptr) ? bias->data_scale() : input_product_scale;
89   const double output_scale = output->data_scale();
90   // The following conditions must be guaranteed by the training pipeline.
91   UNUSED_RELEASE(bias_scale);
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
99 void GetQuantizedConvolutionMultipliersAndShifts(
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
127 void 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
144 void CalculateActivationRangeQuantized(ir::Activation activation, const IPortableTensor *output,
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
201 bool 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
225 int32_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
235 uint32_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
268 nnfw::cker::PaddingType getPaddingType(ir::PaddingType ir_padding_type)
269 {
270   switch (ir_padding_type)
271   {
272     case ir::PaddingType::EXPLICIT:
273       return nnfw::cker::PaddingType::kNone;
274     case ir::PaddingType::SAME:
275       return nnfw::cker::PaddingType::kSame;
276     case ir::PaddingType::VALID:
277       return nnfw::cker::PaddingType::kValid;
278     default:
279       throw std::runtime_error("Wrong padding type.");
280       break;
281   }
282 }
283
284 std::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(axes->layout() == ir::Layout::NHWC);
290   assert(static_cast<size_t>(axes_vals) == axes->getShape().num_elements());
291   switch (axes->data_type())
292   {
293     case ir::DataType::INT32:
294     {
295       for (int i = 0; i < axes_vals; ++i)
296         ret.emplace_back(*(getBuffer<int32_t>(axes) + i));
297       break;
298     }
299     case ir::DataType::INT64:
300     {
301       for (int i = 0; i < axes_vals; ++i)
302         ret.emplace_back(*(getBuffer<int64_t>(axes) + i));
303       break;
304     }
305     default:
306       throw std::runtime_error("getReducerAxes: Not supported data type");
307       break;
308   }
309   return ret;
310 }
311
312 } // namespace ops
313 } // namespace cpu
314 } // namespace backend
315 } // namespace onert