214e61c1e86ad69007b892d258b70a9c3dfb524e
[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_zp(min, max, scaling_factor, zp, 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       throw std::runtime_error("Unsupported Op for const inputs");
175   }
176 }
177
178 // INPUT_NAME is the only activation of NODE
179 #define QUANTIZE_SINGLE_CONST_INPUT(NODE, INPUT_NAME)           \
180   void QuantizeConstInputActivation::visit(NODE *node)          \
181   {                                                             \
182     auto input = node->INPUT_NAME();                            \
183     auto const_node = dynamic_cast<luci::CircleConst *>(input); \
184     if (const_node && is_fp32(const_node))                      \
185     {                                                           \
186       auto new_const = luci::clone(const_node);                 \
187       quant_const(new_const, _output_type);                     \
188       node->INPUT_NAME(new_const);                              \
189     }                                                           \
190   }
191
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)            \
195   {                                                               \
196     auto input1 = node->INPUT_NAME1();                            \
197     auto const_node1 = dynamic_cast<luci::CircleConst *>(input1); \
198     if (const_node1 && is_fp32(const_node1))                      \
199     {                                                             \
200       auto new_const1 = luci::clone(const_node1);                 \
201       quant_const(new_const1, _output_type);                      \
202       node->INPUT_NAME1(new_const1);                              \
203     }                                                             \
204     auto input2 = node->INPUT_NAME2();                            \
205     auto const_node2 = dynamic_cast<luci::CircleConst *>(input2); \
206     if (const_node2 && is_fp32(const_node2))                      \
207     {                                                             \
208       auto new_const2 = luci::clone(const_node2);                 \
209       quant_const(new_const2, _output_type);                      \
210       node->INPUT_NAME2(new_const2);                              \
211     }                                                             \
212   }
213
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)
252
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)
269
270 // AddN has arbitrary number of inputs
271 void QuantizeConstInputActivation::visit(luci::CircleAddN *node)
272 {
273   auto arity = node->arity();
274   for (uint32_t i = 0; i < arity; i++)
275   {
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))
279     {
280       auto new_const = luci::clone(const_node);
281       quant_const(new_const, _output_type);
282       node->inputs(i, new_const);
283     }
284   }
285 }
286
287 #undef QUANTIZE_SINGLE_CONST_INPUT
288 #undef QUANTIZE_TWO_CONST_INPUTS
289
290 } // namespace luci