2 * Copyright (c) 2022 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 "QuantizeActivation.h"
18 #include "QuantizationUtils.h"
20 #include <luci/Service/Nodes/CircleConst.h>
31 bool has_min_max(const CircleNode *node)
33 return node->quantparam() && !node->quantparam()->min.empty() && !node->quantparam()->max.empty();
42 void QuantizeActivation::visit(luci::CircleNode *node)
45 INFO(l) << "QuantizeActivation visit node: " << node->name() << std::endl;
47 // Check if node is fp32
48 if (not is_fp32(node))
51 // Check if this is const (const activation is handled by QuantizeConstInputActivation)
52 // NOTE QuantizePreChecker guarantees weights/bias are const.
53 // Update this code when we accept non-const weights/bias.
54 if (node->opcode() == luci::CircleOpcode::CIRCLECONST)
57 // Check if this is activation
58 // We assume min/max are recorded only for activations
59 if (has_min_max(node))
61 // Quantize using recorded min/max
62 auto quantparam = node->quantparam();
64 assert(quantparam->min.size() == 1); // only support layer-wise quant
65 assert(quantparam->max.size() == 1); // only support layer-wise quant
66 auto min = quantparam->min[0];
67 auto max = quantparam->max[0];
69 float scaling_factor{0};
74 if (output_type == loco::DataType::U8)
76 compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
77 node->dtype(loco::DataType::U8);
81 compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
82 node->dtype(loco::DataType::S16);
85 node->quantparam()->scale.push_back(scaling_factor);
86 node->quantparam()->zerop.push_back(zp);
88 // Fix special attributes
89 if (node->opcode() == luci::CircleOpcode::CAST)
91 auto *cast = loco::must_cast<luci::CircleCast *>(node);
92 auto *cast_input = loco::must_cast<luci::CircleNode *>(cast->x());
94 // make sure that cast_input is already quantized
95 assert(cast_input->dtype() != loco::DataType::FLOAT32);
96 cast->in_data_type(cast_input->dtype());
97 cast->out_data_type(cast->dtype());
103 // QuantizeSpecialActivation
107 void QuantizeSpecialActivation::visit(luci::CircleNode *node)
109 // Nodes fused with activation functions which need special quantization
110 auto fused_act_node = dynamic_cast<CircleNodeMixin<CircleNodeTrait::FusedActFunc> *>(node);
111 if (fused_act_node != nullptr && fused_act_node->fusedActivationFunction() == FusedActFunc::TANH)
113 auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedTanh, output_type);
114 node->quantparam(std::move(qparam));
118 void QuantizeSpecialActivation::visit(luci::CircleLogistic *node)
120 auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedLogistic, output_type);
121 node->quantparam(std::move(qparam));
124 void QuantizeSpecialActivation::visit(luci::CircleTanh *node)
126 auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedTanh, output_type);
127 node->quantparam(std::move(qparam));
130 void QuantizeSpecialActivation::visit(luci::CircleSoftmax *node)
132 auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedSoftmax, output_type);
133 node->quantparam(std::move(qparam));
136 void QuantizeSpecialActivation::visit(luci::CircleFloor *node)
138 assert(activation_qtype(node) == luci::ActivationQType::IntScale);
142 void QuantizeSpecialActivation::visit(luci::CircleFloorDiv *node)
144 assert(activation_qtype(node) == luci::ActivationQType::IntScale);
148 void QuantizeSpecialActivation::visit(luci::CircleFloorMod *node)
150 assert(activation_qtype(node) == luci::ActivationQType::IntScale);
154 void QuantizeSpecialActivation::visit(luci::CircleCeil *node)
156 assert(activation_qtype(node) == luci::ActivationQType::IntScale);
162 // QuantizeConstInputActivation
166 // Default behavior (NYI)
167 void QuantizeConstInputActivation::visit(luci::CircleNode *node)
169 for (uint32_t i = 0; i < node->arity(); i++)
171 auto input_node = node->arg(i);
172 auto const_node = dynamic_cast<luci::CircleConst *>(input_node);
173 if (const_node != nullptr)
174 throw std::runtime_error("Unsupported Op for const inputs");
178 // INPUT_NAME is the only activation of NODE
179 #define QUANTIZE_SINGLE_CONST_INPUT(NODE, INPUT_NAME) \
180 void QuantizeConstInputActivation::visit(NODE *node) \
182 auto input = node->INPUT_NAME(); \
183 auto const_node = dynamic_cast<luci::CircleConst *>(input); \
184 if (const_node && is_fp32(const_node)) \
186 auto new_const = luci::clone(const_node); \
187 quant_const(new_const, _output_type); \
188 node->INPUT_NAME(new_const); \
192 // INPUT_NAME1 and INPUT_NAME2 are the only activations of NODE
193 #define QUANTIZE_TWO_CONST_INPUTS(NODE, INPUT_NAME1, INPUT_NAME2) \
194 void QuantizeConstInputActivation::visit(NODE *node) \
196 auto input1 = node->INPUT_NAME1(); \
197 auto const_node1 = dynamic_cast<luci::CircleConst *>(input1); \
198 if (const_node1 && is_fp32(const_node1)) \
200 auto new_const1 = luci::clone(const_node1); \
201 quant_const(new_const1, _output_type); \
202 node->INPUT_NAME1(new_const1); \
204 auto input2 = node->INPUT_NAME2(); \
205 auto const_node2 = dynamic_cast<luci::CircleConst *>(input2); \
206 if (const_node2 && is_fp32(const_node2)) \
208 auto new_const2 = luci::clone(const_node2); \
209 quant_const(new_const2, _output_type); \
210 node->INPUT_NAME2(new_const2); \
214 // Ops that receive a single activation as an input
215 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleAbs, x)
216 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleArgMax, input)
217 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleArgMin, input)
218 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleBatchToSpaceND, input)
219 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleDepthToSpace, input)
220 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleElu, features)
221 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleExp, x)
222 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleFloor, x)
223 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleGather, params)
224 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleLocalResponseNormalization, input)
225 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleLogistic, x)
226 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleMean, input)
227 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleMirrorPad, input)
228 QUANTIZE_SINGLE_CONST_INPUT(luci::CirclePad, input)
229 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceAny, input)
230 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceProd, input)
231 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceMax, input)
232 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceMin, input)
233 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReshape, tensor)
234 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleResizeBilinear, input)
235 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleResizeNearestNeighbor, input)
236 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReverseSequence, input)
237 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleRsqrt, x)
238 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSlice, input)
239 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSoftmax, logits)
240 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSpaceToBatchND, input)
241 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSpaceToDepth, input)
242 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSplit, input)
243 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSplitV, input)
244 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSqrt, x)
245 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleStridedSlice, input)
246 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSum, input)
247 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTanh, x)
248 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTile, input)
249 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTopKV2, input)
250 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTranspose, a)
251 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleUnpack, value)
253 // Ops that receive two activations as inputs
254 QUANTIZE_TWO_CONST_INPUTS(luci::CircleAdd, x, y)
255 QUANTIZE_TWO_CONST_INPUTS(luci::CircleBatchMatMul, x, y)
256 QUANTIZE_TWO_CONST_INPUTS(luci::CircleDiv, x, y)
257 QUANTIZE_TWO_CONST_INPUTS(luci::CircleEqual, x, y)
258 QUANTIZE_TWO_CONST_INPUTS(luci::CircleFloorDiv, x, y)
259 QUANTIZE_TWO_CONST_INPUTS(luci::CircleGreater, x, y)
260 QUANTIZE_TWO_CONST_INPUTS(luci::CircleGreaterEqual, x, y)
261 QUANTIZE_TWO_CONST_INPUTS(luci::CircleLess, x, y)
262 QUANTIZE_TWO_CONST_INPUTS(luci::CircleLessEqual, x, y)
263 QUANTIZE_TWO_CONST_INPUTS(luci::CircleMaximum, x, y)
264 QUANTIZE_TWO_CONST_INPUTS(luci::CircleMinimum, x, y)
265 QUANTIZE_TWO_CONST_INPUTS(luci::CircleMul, x, y)
266 QUANTIZE_TWO_CONST_INPUTS(luci::CircleNotEqual, x, y)
267 QUANTIZE_TWO_CONST_INPUTS(luci::CirclePow, x, y)
268 QUANTIZE_TWO_CONST_INPUTS(luci::CircleSub, x, y)
270 // AddN has arbitrary number of inputs
271 void QuantizeConstInputActivation::visit(luci::CircleAddN *node)
273 auto arity = node->arity();
274 for (uint32_t i = 0; i < arity; i++)
276 auto input_node = node->inputs(i);
277 auto const_node = dynamic_cast<luci::CircleConst *>(input_node);
278 if (const_node && is_fp32(const_node))
280 auto new_const = luci::clone(const_node);
281 quant_const(new_const, _output_type);
282 node->inputs(i, new_const);
287 #undef QUANTIZE_SINGLE_CONST_INPUT
288 #undef QUANTIZE_TWO_CONST_INPUTS