2 * Copyright (c) 2020 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 "QuantizationUtils.h"
27 uint8_t fp32_to_uint8_cast(float f)
29 assert(std::numeric_limits<uint8_t>::min() <= f);
30 assert(f <= std::numeric_limits<uint8_t>::max());
31 return static_cast<uint8_t>(f);
34 void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
35 float &nudged_min, float &nudged_max)
39 const int32_t kMaxScale = std::numeric_limits<int16_t>::max();
40 const int32_t kMinScale = -kMaxScale;
41 const double qmin_double = kMinScale;
42 const double qmax_double = kMaxScale;
43 const double rmin = std::fmin(0, min);
44 const double rmax = std::fmax(0, max);
45 double scale_factor_from_min_side{0};
46 double scale_factor_from_max_side{0};
48 if ((qmin_double * rmin) > 0)
49 scale_factor_from_min_side = rmin / qmin_double;
51 if ((qmax_double * rmax) > 0)
52 scale_factor_from_max_side = rmax / qmax_double;
54 scaling_factor = scale_factor_from_min_side > scale_factor_from_max_side
55 ? scale_factor_from_min_side
56 : scale_factor_from_max_side;
58 nudged_min = static_cast<float>(qmin_double * scaling_factor);
59 nudged_max = static_cast<float>(qmax_double * scaling_factor);
62 void compute_asym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp,
63 float &nudged_min, float &nudged_max)
68 const int32_t kMinScale = 0;
69 const int32_t kMaxScale = 255;
70 const double qmin_double = kMinScale;
71 const double qmax_double = kMaxScale;
72 const double rmin = std::fmin(0, min);
73 const double rmax = std::fmax(0, max);
75 double scale = (rmax - rmin) / (qmax_double - qmin_double);
76 double zero_point_double = 0;
77 uint8_t nudged_zero_point = 0;
80 WARN(l) << "The minimum and maximum values are the same." << std::endl;
81 if (min >= 0 && max >= 0)
82 zero_point_double = kMinScale;
84 zero_point_double = kMaxScale;
87 zero_point_double = qmin_double - rmin / scale;
90 assert(min >= 0 && max >= 0);
91 nudged_zero_point = kMinScale;
92 scale = max / (qmax_double - qmin_double);
93 if (min > 0 && max > 0)
94 WARN(l) << "The minimum and maximum values are all positive." << std::endl;
98 assert(min < 0 && max < 0);
99 nudged_zero_point = kMaxScale;
100 scale = -min / (qmax_double - qmin_double);
101 WARN(l) << "The minimum and maximum values are all negative." << std::endl;
105 assert(min < 0 && max >= 0);
106 nudged_zero_point = fp32_to_uint8_cast(std::round(zero_point_double));
109 // protect scale from being very low due to overflow
113 nudged_zero_point = fp32_to_uint8_cast(std::round(qmin_double - rmin / scale));
116 nudged_min = static_cast<float>((qmin_double - nudged_zero_point) * scale);
117 nudged_max = static_cast<float>((qmax_double - nudged_zero_point) * scale);
119 scaling_factor = scale;
120 zp = nudged_zero_point;
123 bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, int &channel_dim_index)
125 auto succs = loco::succs(node);
126 if (succs.size() != 1) // assume weights is used by only one node
129 for (auto out : succs)
131 auto conv = dynamic_cast<CircleConv2D *>(out);
132 auto dw_conv = dynamic_cast<CircleDepthwiseConv2D *>(out);
133 auto tw_conv = dynamic_cast<CircleTransposeConv *>(out);
134 auto fc = dynamic_cast<CircleFullyConnected *>(out);
136 // Refer to https://github.com/Samsung/ONE/pull/2448.
137 if ((conv != nullptr && conv->filter() == node) ||
138 (tw_conv != nullptr && tw_conv->filter() == node)) // OHWI
140 assert(node->rank() == 4);
141 dimension.dim(0).set(node->dim(0).value());
142 dimension.dim(1).set(node->dim(1).value());
143 dimension.dim(2).set(node->dim(2).value());
144 dimension.dim(3).set(node->dim(3).value());
145 channel_dim_index = 0; // Set channel_dim_index based on "O"
148 else if (dw_conv != nullptr && dw_conv->filter() == node) // IHWC
150 assert(node->rank() == 4);
151 dimension.dim(0).set(node->dim(0).value());
152 dimension.dim(1).set(node->dim(1).value());
153 dimension.dim(2).set(node->dim(2).value());
154 dimension.dim(3).set(node->dim(3).value());
155 channel_dim_index = 3; // Set channel_dim_index based on "C"
158 else if (fc != nullptr && fc->weights() == node) // OI
160 assert(node->rank() == 2);
161 dimension.dim(0).set(node->dim(0).value());
162 dimension.dim(1).set(1); // Set FC layer like CONV
163 dimension.dim(2).set(1);
164 dimension.dim(3).set(node->dim(1).value());
165 channel_dim_index = 0; // Set channel_dim_index based on "O"
170 // node does not support channle-wise quantization
178 uint32_t cal_offset(loco::TensorShape &dimension, uint32_t *indices)
180 return indices[0] * dimension.dim(1).value() * dimension.dim(2).value() *
181 dimension.dim(3).value() +
182 indices[1] * dimension.dim(2).value() * dimension.dim(3).value() +
183 indices[2] * dimension.dim(3).value() + indices[3];