Imported Upstream version 1.7.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->num_dimensions();
36 }
37
38 uint32_t getNumberOfElements(const IPortableTensor *tensor)
39 {
40   assert(tensor);
41   uint32_t count = 1;
42   for (size_t i = 0; i < tensor->num_dimensions(); i++)
43   {
44     count *= tensor->dimension(i);
45   }
46   return count;
47 }
48
49 uint32_t getSizeOfDimension(const IPortableTensor *tensor, uint32_t dimensionIdx)
50 {
51   assert(tensor);
52   if (dimensionIdx >= tensor->num_dimensions())
53   {
54     // TODO, log the error
55     return 0;
56   }
57   return tensor->dimension(dimensionIdx);
58 }
59
60 void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
61 {
62   if (double_multiplier == 0.)
63   {
64     *quantized_multiplier = 0;
65     *shift = 0;
66     return;
67   }
68   const double q = std::frexp(double_multiplier, shift);
69   auto q_fixed = static_cast<int64_t>(std::round(q * (1ll << 31)));
70
71   assert(q_fixed <= (1ll << 31));
72   if (q_fixed == (1ll << 31))
73   {
74     q_fixed /= 2;
75     ++*shift;
76   }
77   assert(q_fixed <= std::numeric_limits<int32_t>::max());
78   *quantized_multiplier = static_cast<int32_t>(q_fixed);
79 }
80
81 void GetQuantizedConvolutionMultiplier(const IPortableTensor *input, const IPortableTensor *filter,
82                                        const IPortableTensor *bias, const IPortableTensor *output,
83                                        double *multiplier)
84 {
85   const double input_product_scale = input->data_scale() * filter->data_scale();
86   const double bias_scale = (bias != nullptr) ? bias->data_scale() : input_product_scale;
87   const double output_scale = output->data_scale();
88   // The following conditions must be guaranteed by the training pipeline.
89   UNUSED_RELEASE(bias_scale);
90   assert(std::abs(input_product_scale - bias_scale) <=
91          1e-6 * std::min(input_product_scale, bias_scale));
92   assert(input_product_scale >= 0);
93   assert(input_product_scale < output_scale);
94   *multiplier = input_product_scale / output_scale;
95 }
96
97 void QuantizeMultiplierGreaterThanOne(double double_multiplier, int32_t *quantized_multiplier,
98                                       int *left_shift)
99 {
100   assert(double_multiplier > 1.);
101   const double q = std::frexp(double_multiplier, left_shift);
102   int64_t q_fixed = static_cast<int64_t>(std::round(q * (1ll << 31)));
103   assert(q_fixed <= (1ll << 31));
104   if (q_fixed == (1ll << 31))
105   {
106     q_fixed /= 2;
107     ++*left_shift;
108   }
109   assert(*left_shift >= 0);
110   assert(q_fixed <= std::numeric_limits<int32_t>::max());
111   *quantized_multiplier = static_cast<int32_t>(q_fixed);
112 }
113
114 void CalculateActivationRangeUint8(ir::Activation activation, const IPortableTensor *output,
115                                    int32_t *act_min, int32_t *act_max)
116 {
117   const int32_t qmin = std::numeric_limits<uint8_t>::min();
118   const int32_t qmax = std::numeric_limits<uint8_t>::max();
119   const auto scale = output->data_scale();
120   const auto zero_point = output->data_offset();
121   auto quantize = [scale, zero_point](float f) {
122     return zero_point + static_cast<int32_t>(std::round(f / scale));
123   };
124   if (activation == ir::Activation::RELU)
125   {
126     *act_min = std::max(qmin, quantize(0.0));
127     *act_max = qmax;
128   }
129   else if (activation == ir::Activation::RELU6)
130   {
131     *act_min = std::max(qmin, quantize(0.0));
132     *act_max = std::min(qmax, quantize(6.0));
133   }
134   else if (activation == ir::Activation::RELU1)
135   {
136     *act_min = std::max(qmin, quantize(-1.0));
137     *act_max = std::min(qmax, quantize(1.0));
138   }
139   else if (activation == ir::Activation::SIGMOID)
140   {
141     *act_min = std::max(qmin, quantize(0.0));
142     *act_max = std::min(qmax, quantize(1.0));
143   }
144   else if (activation == ir::Activation::NONE)
145   {
146     *act_min = qmin;
147     *act_max = qmax;
148   }
149   else
150   {
151     std::cout << "Unsupported fused activation function." << std::endl;
152   }
153 }
154
155 bool HaveSameShapes(const IPortableTensor *input1, const IPortableTensor *input2)
156 {
157   if (input1 == input2)
158     return true;
159   if (input2 == NULL || input2 == NULL)
160     return false;
161
162   if (input1 == NULL)
163   {
164     return (getNumberOfDimensions(input2) == 0);
165   }
166
167   if (getNumberOfDimensions(input1) != getNumberOfDimensions(input2))
168     return false;
169
170   for (uint32_t i = 0; i < getNumberOfDimensions(input1); i++)
171     if (input1->dimension(i) != input2->dimension(i))
172       return false;
173
174   return true;
175 }
176
177 int32_t CalculateInputRadius(int input_integer_bits, int input_left_shift)
178 {
179   const double max_input_rescaled = 1.0 * ((1 << input_integer_bits) - 1) *
180                                     (1ll << (31 - input_integer_bits)) / (1ll << input_left_shift);
181   // Tighten bound using floor.  Suppose that we could use the exact value.
182   // After scaling the difference, the result would be at the maximum.  Thus we
183   // must ensure that our value has lower magnitude.
184   return static_cast<int32_t>(std::floor(max_input_rescaled));
185 }
186
187 uint32_t sizeOfData(OperandType type, const std::vector<int32_t> &dimensions)
188 {
189   uint32_t size = 4;
190
191   switch (type)
192   {
193     case OperandType::FLOAT32:
194     case OperandType::INT32:
195     case OperandType::UINT32:
196       size = 4;
197       break;
198     case OperandType::BOOL8:
199     case OperandType::QUANT_UINT8_ASYMM:
200     case OperandType::QUANT_INT8_SYMM:
201       size = 1;
202       break;
203     case OperandType::INT64:
204       size = 8;
205       break;
206     default:
207       throw std::runtime_error("Not supported operand type.");
208       break;
209   }
210
211   for (auto d : dimensions)
212   {
213     assert(d >= 0);
214     size *= static_cast<uint32_t>(d);
215   }
216
217   return size;
218 }
219
220 nnfw::cker::PaddingType getPaddingType(ir::PaddingType ir_padding_type)
221 {
222   switch (ir_padding_type)
223   {
224     case ir::PaddingType::EXPLICIT:
225       return nnfw::cker::PaddingType::kNone;
226     case ir::PaddingType::SAME:
227       return nnfw::cker::PaddingType::kSame;
228     case ir::PaddingType::VALID:
229       return nnfw::cker::PaddingType::kValid;
230     default:
231       throw std::runtime_error("Wrong padding type.");
232       break;
233   }
234 }
235
236 std::vector<int32_t> getReducerAxes(const IPortableTensor *axes)
237 {
238   std::vector<int32_t> ret;
239
240   assert(axes->layout() == ir::Layout::NHWC);
241   assert(axes->dimension(0) == axes->getShape().num_elements());
242   switch (axes->data_type())
243   {
244     case ir::DataType::INT32:
245     {
246       for (size_t i = 0; i < axes->dimension(0); ++i)
247         ret.emplace_back(*(reinterpret_cast<const int32_t *>(axes->buffer()) + i));
248       break;
249     }
250     case ir::DataType::INT64:
251     {
252       for (size_t i = 0; i < axes->dimension(0); ++i)
253         ret.emplace_back(*(reinterpret_cast<const int64_t *>(axes->buffer()) + i));
254       break;
255     }
256     default:
257       throw std::runtime_error("getReducerAxes: Not supported data type");
258       break;
259   }
260   return ret;
261 }
262
263 } // namespace ops
264 } // namespace cpu
265 } // namespace backend
266 } // namespace onert