Imported Upstream version 1.25.0
[platform/core/ml/nnfw.git] / compiler / luci / pass / src / QuantizeActivation.cpp
1 /*
2  * Copyright (c) 2022 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 "QuantizeActivation.h"
18 #include "QuantizationUtils.h"
19
20 #include <luci/Service/Nodes/CircleConst.h>
21 #include <luci/Log.h>
22
23 #include <algorithm>
24 #include <cmath>
25
26 using namespace luci;
27
28 namespace
29 {
30
31 bool has_min_max(const CircleNode *node)
32 {
33   return node->quantparam() && !node->quantparam()->min.empty() && !node->quantparam()->max.empty();
34 }
35
36 } // namespace
37
38 // QuantizeActivation
39 namespace luci
40 {
41
42 void QuantizeActivation::visit(luci::CircleNode *node)
43 {
44   LOGGER(l);
45   INFO(l) << "QuantizeActivation visit node: " << node->name() << std::endl;
46
47   // Check if node is fp32
48   if (not is_fp32(node))
49     return;
50
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)
55     return;
56
57   // Check if this is activation
58   // We assume min/max are recorded only for activations
59   if (has_min_max(node))
60   {
61     // Quantize using recorded min/max
62     auto quantparam = node->quantparam();
63     assert(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];
68
69     float scaling_factor{0};
70     int64_t zp{0};
71     float nudged_min{0};
72     float nudged_max{0};
73
74     if (output_type == loco::DataType::U8)
75     {
76       compute_asym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
77       node->dtype(loco::DataType::U8);
78     }
79     else
80     {
81       compute_sym_scale(min, max, scaling_factor, nudged_min, nudged_max);
82       node->dtype(loco::DataType::S16);
83     }
84
85     node->quantparam()->scale.push_back(scaling_factor);
86     node->quantparam()->zerop.push_back(zp);
87   }
88   // Fix special attributes
89   if (node->opcode() == luci::CircleOpcode::CAST)
90   {
91     auto *cast = loco::must_cast<luci::CircleCast *>(node);
92     auto *cast_input = loco::must_cast<luci::CircleNode *>(cast->x());
93
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());
98   }
99 }
100
101 } // namespace luci
102
103 // QuantizeSpecialActivation
104 namespace luci
105 {
106
107 void QuantizeSpecialActivation::visit(luci::CircleNode *node)
108 {
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)
112   {
113     auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedTanh, output_type);
114     node->quantparam(std::move(qparam));
115   }
116 }
117
118 void QuantizeSpecialActivation::visit(luci::CircleLogistic *node)
119 {
120   auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedLogistic, output_type);
121   node->quantparam(std::move(qparam));
122 }
123
124 void QuantizeSpecialActivation::visit(luci::CircleTanh *node)
125 {
126   auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedTanh, output_type);
127   node->quantparam(std::move(qparam));
128 }
129
130 void QuantizeSpecialActivation::visit(luci::CircleSoftmax *node)
131 {
132   auto qparam = make_predefined_qparam(luci::ActivationQType::PreDefinedSoftmax, output_type);
133   node->quantparam(std::move(qparam));
134 }
135
136 void QuantizeSpecialActivation::visit(luci::CircleFloor *node)
137 {
138   assert(activation_qtype(node) == luci::ActivationQType::IntScale);
139   set_int_scale(node);
140 }
141
142 void QuantizeSpecialActivation::visit(luci::CircleFloorDiv *node)
143 {
144   assert(activation_qtype(node) == luci::ActivationQType::IntScale);
145   set_int_scale(node);
146 }
147
148 void QuantizeSpecialActivation::visit(luci::CircleFloorMod *node)
149 {
150   assert(activation_qtype(node) == luci::ActivationQType::IntScale);
151   set_int_scale(node);
152 }
153
154 void QuantizeSpecialActivation::visit(luci::CircleCeil *node)
155 {
156   assert(activation_qtype(node) == luci::ActivationQType::IntScale);
157   set_int_scale(node);
158 }
159
160 } // namespace luci
161
162 // QuantizeConstInputActivation
163 namespace luci
164 {
165
166 // Default behavior (NYI)
167 void QuantizeConstInputActivation::visit(luci::CircleNode *node)
168 {
169   for (uint32_t i = 0; i < node->arity(); i++)
170   {
171     auto input_node = node->arg(i);
172     auto const_node = dynamic_cast<luci::CircleConst *>(input_node);
173     if (const_node != nullptr)
174     {
175       std::string msg = "Unsupported Op for const inputs: " + node->name();
176       throw std::runtime_error(msg);
177     }
178   }
179 }
180
181 // INPUT_NAME is the only activation of NODE
182 #define QUANTIZE_SINGLE_CONST_INPUT(NODE, INPUT_NAME)           \
183   void QuantizeConstInputActivation::visit(NODE *node)          \
184   {                                                             \
185     auto input = node->INPUT_NAME();                            \
186     auto const_node = dynamic_cast<luci::CircleConst *>(input); \
187     if (const_node && is_fp32(const_node))                      \
188     {                                                           \
189       auto new_const = luci::clone(const_node);                 \
190       quant_const(new_const, _output_type);                     \
191       node->INPUT_NAME(new_const);                              \
192     }                                                           \
193   }
194
195 // INPUT_NAME1 and INPUT_NAME2 are the only activations of NODE
196 #define QUANTIZE_TWO_CONST_INPUTS(NODE, INPUT_NAME1, INPUT_NAME2) \
197   void QuantizeConstInputActivation::visit(NODE *node)            \
198   {                                                               \
199     auto input1 = node->INPUT_NAME1();                            \
200     auto const_node1 = dynamic_cast<luci::CircleConst *>(input1); \
201     if (const_node1 && is_fp32(const_node1))                      \
202     {                                                             \
203       auto new_const1 = luci::clone(const_node1);                 \
204       quant_const(new_const1, _output_type);                      \
205       node->INPUT_NAME1(new_const1);                              \
206     }                                                             \
207     auto input2 = node->INPUT_NAME2();                            \
208     auto const_node2 = dynamic_cast<luci::CircleConst *>(input2); \
209     if (const_node2 && is_fp32(const_node2))                      \
210     {                                                             \
211       auto new_const2 = luci::clone(const_node2);                 \
212       quant_const(new_const2, _output_type);                      \
213       node->INPUT_NAME2(new_const2);                              \
214     }                                                             \
215   }
216
217 // Ops that receive a single activation as an input
218 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleAbs, x)
219 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleArgMax, input)
220 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleArgMin, input)
221 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleBatchToSpaceND, input)
222 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleDepthToSpace, input)
223 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleElu, features)
224 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleExp, x)
225 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleFloor, x)
226 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleGather, params)
227 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleGelu, features)
228 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleLocalResponseNormalization, input)
229 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleLogistic, x)
230 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleMean, input)
231 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleMirrorPad, input)
232 QUANTIZE_SINGLE_CONST_INPUT(luci::CirclePad, input)
233 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceAny, input)
234 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceProd, input)
235 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceMax, input)
236 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReduceMin, input)
237 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReshape, tensor)
238 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleResizeBilinear, input)
239 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleResizeNearestNeighbor, input)
240 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleReverseSequence, input)
241 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleRsqrt, x)
242 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSlice, input)
243 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSoftmax, logits)
244 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSpaceToBatchND, input)
245 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSpaceToDepth, input)
246 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSplit, input)
247 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSplitV, input)
248 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSqrt, x)
249 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSqueeze, input)
250 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleStridedSlice, input)
251 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleSum, input)
252 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTanh, x)
253 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTile, input)
254 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTopKV2, input)
255 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleTranspose, a)
256 QUANTIZE_SINGLE_CONST_INPUT(luci::CircleUnpack, value)
257
258 // Ops that receive two activations as inputs
259 QUANTIZE_TWO_CONST_INPUTS(luci::CircleAdd, x, y)
260 QUANTIZE_TWO_CONST_INPUTS(luci::CircleBatchMatMul, x, y)
261 QUANTIZE_TWO_CONST_INPUTS(luci::CircleDiv, x, y)
262 QUANTIZE_TWO_CONST_INPUTS(luci::CircleEqual, x, y)
263 QUANTIZE_TWO_CONST_INPUTS(luci::CircleFloorDiv, x, y)
264 QUANTIZE_TWO_CONST_INPUTS(luci::CircleFloorMod, x, y)
265 QUANTIZE_TWO_CONST_INPUTS(luci::CircleGreater, x, y)
266 QUANTIZE_TWO_CONST_INPUTS(luci::CircleGreaterEqual, x, y)
267 QUANTIZE_TWO_CONST_INPUTS(luci::CircleLess, x, y)
268 QUANTIZE_TWO_CONST_INPUTS(luci::CircleLessEqual, x, y)
269 QUANTIZE_TWO_CONST_INPUTS(luci::CircleMaximum, x, y)
270 QUANTIZE_TWO_CONST_INPUTS(luci::CircleMinimum, x, y)
271 QUANTIZE_TWO_CONST_INPUTS(luci::CircleMul, x, y)
272 QUANTIZE_TWO_CONST_INPUTS(luci::CircleNotEqual, x, y)
273 QUANTIZE_TWO_CONST_INPUTS(luci::CirclePow, x, y)
274 QUANTIZE_TWO_CONST_INPUTS(luci::CircleSub, x, y)
275
276 // AddN has arbitrary number of inputs
277 void QuantizeConstInputActivation::visit(luci::CircleAddN *node)
278 {
279   auto arity = node->arity();
280   for (uint32_t i = 0; i < arity; i++)
281   {
282     auto input_node = node->inputs(i);
283     auto const_node = dynamic_cast<luci::CircleConst *>(input_node);
284     if (const_node && is_fp32(const_node))
285     {
286       auto new_const = luci::clone(const_node);
287       quant_const(new_const, _output_type);
288       node->inputs(i, new_const);
289     }
290   }
291 }
292
293 #undef QUANTIZE_SINGLE_CONST_INPUT
294 #undef QUANTIZE_TWO_CONST_INPUTS
295
296 } // namespace luci