2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "OperationUtils.h"
32 uint32_t getNumberOfDimensions(const IPortableTensor *tensor)
35 return tensor->num_dimensions();
38 uint32_t getNumberOfElements(const IPortableTensor *tensor)
42 for (size_t i = 0; i < tensor->num_dimensions(); i++)
44 count *= tensor->dimension(i);
49 uint32_t getSizeOfDimension(const IPortableTensor *tensor, uint32_t dimensionIdx)
52 if (dimensionIdx >= tensor->num_dimensions())
54 // TODO, log the error
57 return tensor->dimension(dimensionIdx);
60 void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
62 if (double_multiplier == 0.)
64 *quantized_multiplier = 0;
68 const double q = std::frexp(double_multiplier, shift);
69 auto q_fixed = static_cast<int64_t>(std::round(q * (1ll << 31)));
71 assert(q_fixed <= (1ll << 31));
72 if (q_fixed == (1ll << 31))
77 assert(q_fixed <= std::numeric_limits<int32_t>::max());
78 *quantized_multiplier = static_cast<int32_t>(q_fixed);
81 void GetQuantizedConvolutionMultiplier(const IPortableTensor *input, const IPortableTensor *filter,
82 const IPortableTensor *bias, const IPortableTensor *output,
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;
97 void QuantizeMultiplierGreaterThanOne(double double_multiplier, int32_t *quantized_multiplier,
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))
109 assert(*left_shift >= 0);
110 assert(q_fixed <= std::numeric_limits<int32_t>::max());
111 *quantized_multiplier = static_cast<int32_t>(q_fixed);
114 void CalculateActivationRangeUint8(ir::Activation activation, const IPortableTensor *output,
115 int32_t *act_min, int32_t *act_max)
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));
124 if (activation == ir::Activation::RELU)
126 *act_min = std::max(qmin, quantize(0.0));
129 else if (activation == ir::Activation::RELU6)
131 *act_min = std::max(qmin, quantize(0.0));
132 *act_max = std::min(qmax, quantize(6.0));
134 else if (activation == ir::Activation::RELU1)
136 *act_min = std::max(qmin, quantize(-1.0));
137 *act_max = std::min(qmax, quantize(1.0));
139 else if (activation == ir::Activation::SIGMOID)
141 *act_min = std::max(qmin, quantize(0.0));
142 *act_max = std::min(qmax, quantize(1.0));
144 else if (activation == ir::Activation::NONE)
151 std::cout << "Unsupported fused activation function." << std::endl;
155 bool HaveSameShapes(const IPortableTensor *input1, const IPortableTensor *input2)
157 if (input1 == input2)
159 if (input2 == NULL || input2 == NULL)
164 return (getNumberOfDimensions(input2) == 0);
167 if (getNumberOfDimensions(input1) != getNumberOfDimensions(input2))
170 for (uint32_t i = 0; i < getNumberOfDimensions(input1); i++)
171 if (input1->dimension(i) != input2->dimension(i))
177 int32_t CalculateInputRadius(int input_integer_bits, int input_left_shift)
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));
187 uint32_t sizeOfData(OperandType type, const std::vector<int32_t> &dimensions)
193 case OperandType::FLOAT32:
194 case OperandType::INT32:
195 case OperandType::UINT32:
198 case OperandType::BOOL8:
199 case OperandType::QUANT_UINT8_ASYMM:
200 case OperandType::QUANT_INT8_SYMM:
203 case OperandType::INT64:
207 throw std::runtime_error("Not supported operand type.");
211 for (auto d : dimensions)
214 size *= static_cast<uint32_t>(d);
220 nnfw::cker::PaddingType getPaddingType(ir::PaddingType ir_padding_type)
222 switch (ir_padding_type)
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;
231 throw std::runtime_error("Wrong padding type.");
236 std::vector<int32_t> getReducerAxes(const IPortableTensor *axes)
238 std::vector<int32_t> ret;
240 assert(axes->layout() == ir::Layout::NHWC);
241 assert(axes->dimension(0) == axes->getShape().num_elements());
242 switch (axes->data_type())
244 case ir::DataType::INT32:
246 for (size_t i = 0; i < axes->dimension(0); ++i)
247 ret.emplace_back(*(reinterpret_cast<const int32_t *>(axes->buffer()) + i));
250 case ir::DataType::INT64:
252 for (size_t i = 0; i < axes->dimension(0); ++i)
253 ret.emplace_back(*(reinterpret_cast<const int64_t *>(axes->buffer()) + i));
257 throw std::runtime_error("getReducerAxes: Not supported data type");
265 } // namespace backend