ONE - On-device Neural Engine
Loading...
Searching...
No Matches
Utils.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "kernels/Utils.h"
19
20#include <cassert>
21#include <cmath>
22#include <limits>
23#include <stdexcept>
24
25namespace luci_interpreter
26{
27namespace kernels
28{
29
30TfLiteFusedActivation getTfLiteActivation(Activation activation)
31{
32 switch (activation)
33 {
35 return kTfLiteActRelu;
37 return kTfLiteActRelu6;
39 return kTfLiteActReluN1To1;
41 return kTfLiteActTanh;
43 return kTfLiteActSignBit;
45 return kTfLiteActNone;
46 default:
47 throw std::runtime_error("Unsupported activation type");
48 }
49}
50
51template <typename T>
52void calculateActivationRange(Activation activation, T *activation_min, T *activation_max)
53{
54 switch (activation)
55 {
56 case Activation::NONE:
57 *activation_min = std::numeric_limits<T>::lowest();
58 *activation_max = std::numeric_limits<T>::max();
59 break;
60 case Activation::RELU:
61 *activation_min = 0;
62 *activation_max = std::numeric_limits<T>::max();
63 break;
64 case Activation::RELU_N1_TO_1:
65 *activation_min = -1;
66 *activation_max = 1;
67 break;
68 case Activation::RELU6:
69 *activation_min = 0;
70 *activation_max = 6;
71 break;
72 default:
73 throw std::runtime_error("Unsupported activation.");
74 }
75}
76
77template void calculateActivationRange(Activation activation, float *activation_min,
78 float *activation_max);
79template void calculateActivationRange(Activation activation, int32_t *activation_min,
80 int32_t *activation_max);
81template void calculateActivationRange(Activation activation, int64_t *activation_min,
82 int64_t *activation_max);
83
84static void calculateActivationRangeQuantizedImpl(Activation activation, int32_t qmin, int32_t qmax,
85 const Tensor *output, int32_t *activation_min,
86 int32_t *activation_max)
87{
88 const float scale = output->scale();
89 const int32_t zero_point = output->zero_point();
90
91 auto quantize = [scale, zero_point](float x) {
92 return zero_point + static_cast<int32_t>(std::round(x / scale));
93 };
94
95 switch (activation)
96 {
97 case Activation::NONE:
98 case Activation::TANH:
99 *activation_min = qmin;
100 *activation_max = qmax;
101 break;
102 case Activation::RELU:
103 *activation_min = std::max(qmin, quantize(0.0f));
104 *activation_max = qmax;
105 break;
106 case Activation::RELU_N1_TO_1:
107 *activation_min = std::max(qmin, quantize(-1.0f));
108 *activation_max = std::min(qmax, quantize(1.0f));
109 break;
110 case Activation::RELU6:
111 *activation_min = std::max(qmin, quantize(0.0f));
112 *activation_max = std::min(qmax, quantize(6.0f));
113 break;
114 default:
115 throw std::runtime_error("Unsupported activation.");
116 }
117}
118
120 int32_t *activation_min, int32_t *activation_max)
121{
122 assert(output->zero_points().size() == 1);
123 int32_t qmin{};
124 int32_t qmax{};
125 switch (output->element_type())
126 {
127 case DataType::U4:
128 qmin = 0;
129 qmax = 15;
130 break;
131 case DataType::U8:
132 qmin = 0;
133 qmax = std::numeric_limits<uint8_t>::max();
134 break;
135 case DataType::S4:
136 qmin = -8;
137 qmax = 7;
138 break;
139 case DataType::S8:
140 qmin = -std::numeric_limits<int8_t>::max();
141 qmax = std::numeric_limits<int8_t>::max();
142 break;
143 case DataType::S16:
144 // For now, assume that signed int16 type implies signed symmetric quantization.
145 assert(output->zero_point() == 0);
146 qmin = -std::numeric_limits<int16_t>::max();
147 qmax = std::numeric_limits<int16_t>::max();
148 break;
149 default:
150 throw std::runtime_error("luci-intp (calculateActivationRangeQuantized) Unsupported type.");
151 }
152
153 calculateActivationRangeQuantizedImpl(activation, qmin, qmax, output, activation_min,
154 activation_max);
155}
156
157void quantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
158{
159 if (double_multiplier == 0.0)
160 {
161 *quantized_multiplier = 0;
162 *shift = 0;
163 return;
164 }
165
166 const double q = std::frexp(double_multiplier, shift);
167 auto q_fixed = static_cast<int64_t>(std::round(q * (INT64_C(1) << 31)));
168
169 if (q_fixed == (INT64_C(1) << 31))
170 {
171 q_fixed /= 2;
172 ++*shift;
173 }
174 assert(q_fixed <= std::numeric_limits<int32_t>::max());
175 // A shift amount smaller than -31 would cause all bits to be shifted out
176 // and thus all results would be zero. We implement that instead with
177 // q_fixed==0, so as to avoid hitting issues with right-shift
178 // operations with shift amounts greater than 31. Note that this happens
179 // roughly when abs(double_multiplier) < 2^-31 and the present handling means
180 // that we're effectively flushing tiny double_multiplier's to zero.
181 // We could conceivably handle values in the range (roughly) [32, 63]
182 // as 'denormals' i.e. (shift==0, q_fixed < 2^30). In that point of view
183 // the present handling is just doing 'flush denormals to zero'. We could
184 // reconsider and actually generate nonzero denormals if a need arises.
185 if (*shift < -31)
186 {
187 *shift = 0;
188 q_fixed = 0;
189 }
190 *quantized_multiplier = static_cast<int32_t>(q_fixed);
191}
192
193void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quantized_multiplier,
194 int *left_shift)
195{
196 assert(double_multiplier < 1.0);
197 assert(double_multiplier > 0.0);
198 int shift;
199 quantizeMultiplier(double_multiplier, quantized_multiplier, &shift);
200 assert(shift <= 0);
201 *left_shift = shift;
202}
203
204Shape calculateShapeForBroadcast(const Shape &input1_shape, const Shape &input2_shape)
205{
206 const int num_input1_dims = input1_shape.num_dims();
207 const int num_input2_dims = input2_shape.num_dims();
208 const int num_out_dims = std::max(num_input1_dims, num_input2_dims);
209 Shape output_shape(num_out_dims);
210
211 for (int i = 0; i < num_out_dims; ++i)
212 {
213 const int32_t input1_dim = i < num_input1_dims ? input1_shape.dim(num_input1_dims - i - 1) : 1;
214 const int32_t input2_dim = i < num_input2_dims ? input2_shape.dim(num_input2_dims - i - 1) : 1;
215
216 bool need_broadcast = input1_dim != input2_dim;
217 bool can_broadcast = input1_dim == 1 || input2_dim == 1;
218 LUCI_INTERPRETER_CHECK(!need_broadcast || can_broadcast);
219
220 output_shape.dim(num_out_dims - i - 1) = std::max(input1_dim, input2_dim);
221 }
222
223 return output_shape;
224}
225
226} // namespace kernels
227} // namespace luci_interpreter
int32_t dim(int i) const
Definition Tensor.h:41
int num_dims() const
Definition Tensor.h:39
#define LUCI_INTERPRETER_CHECK(cond)
Definition Utils.h:36
const luci_interpreter::RuntimeShape output_shape
Shape calculateShapeForBroadcast(const Shape &input1_shape, const Shape &input2_shape)
Definition Utils.cpp:204
TfLiteFusedActivation getTfLiteActivation(Activation activation)
Definition Utils.cpp:30
void calculateActivationRange(Activation activation, T *activation_min, T *activation_max)
Definition Utils.cpp:52
void quantizeMultiplierSmallerThanOneExp(double double_multiplier, int32_t *quantized_multiplier, int *left_shift)
Definition Utils.cpp:193
void calculateActivationRangeQuantized(Activation activation, const Tensor *output, int32_t *activation_min, int32_t *activation_max)
Definition Utils.cpp:119
void quantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
Definition Utils.cpp:157