3ce32555be952e67b04768123603b80ec9f1f23a
[platform/core/ml/nnfw.git] / compiler / luci / pass / src / VerifyQuantizedNodeType.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 "VerifyQuantizedNodeType.h"
18
19 #include <cmath>
20 #include <memory>
21
22 // This macro is undef at the end of the file
23 #define RETURN_FALSE_UNLESS(ARG) \
24   if (not(ARG))                  \
25   {                              \
26     return false;                \
27   }
28
29 namespace luci
30 {
31
32 std::shared_ptr<VerifyQuantizedNodeType> VerifyQuantizedNodeType::create(loco::DataType dtype)
33 {
34   if (dtype == loco::DataType::U8)
35     return std::make_shared<VerifyQuantizedNodeU8Type>();
36   else if (dtype == loco::DataType::S16)
37     return std::make_shared<VerifyQuantizedNodeS16Type>();
38   else
39     throw std::domain_error("Not supported Quantized type");
40 }
41
42 } // namespace luci
43
44 namespace luci
45 {
46
47 template <loco::DataType Qtype, loco::DataType Btype>
48 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleAdd *node)
49 {
50   // Allow add of indices
51   if (group_has_type(node, loco::DataType::S32) or group_has_type(node, loco::DataType::S64))
52     return true;
53
54   return group_has_type(node, Qtype);
55 }
56
57 template <loco::DataType Qtype, loco::DataType Btype>
58 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleArgMax *node)
59 {
60   RETURN_FALSE_UNLESS(has_type(node, node->output_type()))
61   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
62   RETURN_FALSE_UNLESS(has_type(node->dimension(), loco::DataType::S32) ||
63                       has_type(node->dimension(), loco::DataType::S64))
64   return true;
65 }
66
67 template <loco::DataType Qtype, loco::DataType Btype>
68 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleAveragePool2D *node)
69 {
70   return group_has_type(node, Qtype);
71 }
72
73 template <loco::DataType Qtype, loco::DataType Btype>
74 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleBatchToSpaceND *node)
75 {
76   RETURN_FALSE_UNLESS(has_type(node, Qtype))
77   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
78   return true;
79 }
80
81 template <loco::DataType Qtype, loco::DataType Btype>
82 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleCast *node)
83 {
84   auto *input = loco::must_cast<luci::CircleNode *>(node->x());
85   bool input_quantized = input->quantparam() != nullptr;
86   if (input_quantized)
87   {
88     RETURN_FALSE_UNLESS(has_type(input, node->in_data_type()))
89     RETURN_FALSE_UNLESS(has_type(input, Qtype))
90   }
91
92   bool node_quantized = node->quantparam() != nullptr;
93   if (node_quantized)
94   {
95     RETURN_FALSE_UNLESS(has_type(node, node->out_data_type()))
96     RETURN_FALSE_UNLESS(has_type(node, Qtype))
97   }
98   return true;
99 }
100
101 template <loco::DataType Qtype, loco::DataType Btype>
102 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleConv2D *node)
103 {
104   RETURN_FALSE_UNLESS(has_type(node, Qtype))
105   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
106   RETURN_FALSE_UNLESS(has_type(node->filter(), Qtype))
107   RETURN_FALSE_UNLESS(has_type(node->bias(), Btype))
108   return true;
109 }
110
111 template <loco::DataType Qtype, loco::DataType Btype>
112 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleConcatenation *node)
113 {
114   // Allow concatenation of indices
115   if (group_has_type(node, loco::DataType::S32) or group_has_type(node, loco::DataType::S64))
116     return true;
117
118   return group_has_type(node, Qtype);
119 }
120
121 template <loco::DataType Qtype, loco::DataType Btype>
122 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleDepthToSpace *node)
123 {
124   return group_has_type(node, Qtype);
125 }
126
127 template <loco::DataType Qtype, loco::DataType Btype>
128 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleDepthwiseConv2D *node)
129 {
130   RETURN_FALSE_UNLESS(has_type(node, Qtype))
131   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
132   RETURN_FALSE_UNLESS(has_type(node->filter(), Qtype))
133   RETURN_FALSE_UNLESS(has_type(node->bias(), Btype))
134   return true;
135 }
136
137 template <loco::DataType Qtype, loco::DataType Btype>
138 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleDiv *node)
139 {
140   return group_has_type(node, Qtype);
141 }
142
143 template <loco::DataType Qtype, loco::DataType Btype>
144 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleElu *node)
145 {
146   return group_has_type(node, Qtype);
147 }
148
149 template <loco::DataType Qtype, loco::DataType Btype>
150 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleFloor *node)
151 {
152   RETURN_FALSE_UNLESS(group_has_type(node, Qtype));
153
154   // This checks the value of scale is an integer
155   RETURN_FALSE_UNLESS(node->quantparam());
156   RETURN_FALSE_UNLESS(std::roundf(node->quantparam()->scale[0]) == node->quantparam()->scale[0]);
157   return true;
158 }
159
160 template <loco::DataType Qtype, loco::DataType Btype>
161 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleFloorDiv *node)
162 {
163   RETURN_FALSE_UNLESS(group_has_type(node, Qtype));
164
165   // This checks the value of scale is an integer
166   RETURN_FALSE_UNLESS(node->quantparam());
167   RETURN_FALSE_UNLESS(std::roundf(node->quantparam()->scale[0]) == node->quantparam()->scale[0]);
168   return true;
169 }
170
171 template <loco::DataType Qtype, loco::DataType Btype>
172 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleFullyConnected *node)
173 {
174   RETURN_FALSE_UNLESS(has_type(node, Qtype))
175   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
176   RETURN_FALSE_UNLESS(has_type(node->weights(), Qtype))
177   luci::CircleConst *bias = dynamic_cast<luci::CircleConst *>(node->bias());
178   if (bias != nullptr)
179     RETURN_FALSE_UNLESS(has_type(bias, Btype))
180   return true;
181 }
182
183 template <loco::DataType Qtype, loco::DataType Btype>
184 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleGreater *node)
185 {
186   RETURN_FALSE_UNLESS(has_type(node, loco::DataType::BOOL))
187   RETURN_FALSE_UNLESS(has_type(node->x(), Qtype))
188   RETURN_FALSE_UNLESS(has_type(node->y(), Qtype))
189   return true;
190 }
191
192 template <loco::DataType Qtype, loco::DataType Btype>
193 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleGreaterEqual *node)
194 {
195   RETURN_FALSE_UNLESS(has_type(node, loco::DataType::BOOL))
196   RETURN_FALSE_UNLESS(has_type(node->x(), Qtype))
197   RETURN_FALSE_UNLESS(has_type(node->y(), Qtype))
198   return true;
199 }
200
201 template <loco::DataType Qtype, loco::DataType Btype>
202 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleInstanceNorm *node)
203 {
204   return group_has_type(node, Qtype);
205 }
206
207 template <loco::DataType Qtype, loco::DataType Btype>
208 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(
209   const luci::CircleLocalResponseNormalization *node)
210 {
211   return group_has_type(node, Qtype);
212 }
213
214 template <loco::DataType Qtype, loco::DataType Btype>
215 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleLogicalOr *node)
216 {
217   return group_has_type(node, loco::DataType::BOOL);
218 }
219
220 template <loco::DataType Qtype, loco::DataType Btype>
221 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleMaxPool2D *node)
222 {
223   return group_has_type(node, Qtype);
224 }
225
226 template <loco::DataType Qtype, loco::DataType Btype>
227 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleMean *node)
228 {
229   RETURN_FALSE_UNLESS(has_type(node, Qtype))
230   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
231   RETURN_FALSE_UNLESS(has_type(node->reduction_indices(), loco::DataType::S32))
232   return true;
233 }
234
235 template <loco::DataType Qtype, loco::DataType Btype>
236 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleMirrorPad *node)
237 {
238   RETURN_FALSE_UNLESS(has_type(node, Qtype))
239   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
240   RETURN_FALSE_UNLESS(has_type(node->paddings(), loco::DataType::S32))
241   return true;
242 }
243
244 template <loco::DataType Qtype, loco::DataType Btype>
245 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleMul *node)
246 {
247   // Allow mul of indices
248   if (group_has_type(node, loco::DataType::S32) or group_has_type(node, loco::DataType::S64))
249     return true;
250
251   return group_has_type(node, Qtype);
252 }
253
254 template <loco::DataType Qtype, loco::DataType Btype>
255 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleNotEqual *node)
256 {
257   RETURN_FALSE_UNLESS(has_type(node, loco::DataType::BOOL))
258   RETURN_FALSE_UNLESS(has_type(node->x(), Qtype))
259   RETURN_FALSE_UNLESS(has_type(node->y(), Qtype))
260   return true;
261 }
262
263 template <loco::DataType Qtype, loco::DataType Btype>
264 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleOneHot *node)
265 {
266   RETURN_FALSE_UNLESS(has_type(node, Qtype));
267   RETURN_FALSE_UNLESS(has_type(node->indices(), loco::DataType::S32) ||
268                       has_type(node->indices(), loco::DataType::S64));
269   RETURN_FALSE_UNLESS(has_type(node->depth(), loco::DataType::S32));
270   RETURN_FALSE_UNLESS(has_type(node->on_value(), Qtype));
271   RETURN_FALSE_UNLESS(has_type(node->off_value(), Qtype));
272   return true;
273 }
274
275 template <loco::DataType Qtype, loco::DataType Btype>
276 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CirclePack *node)
277 {
278   return group_has_type(node, Qtype);
279 }
280
281 template <loco::DataType Qtype, loco::DataType Btype>
282 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CirclePad *node)
283 {
284   RETURN_FALSE_UNLESS(has_type(node, Qtype))
285   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
286   RETURN_FALSE_UNLESS(has_type(node->paddings(), loco::DataType::S32))
287   return true;
288 }
289
290 template <loco::DataType Qtype, loco::DataType Btype>
291 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CirclePadV2 *node)
292 {
293   RETURN_FALSE_UNLESS(has_type(node, Qtype))
294   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
295   RETURN_FALSE_UNLESS(has_type(node->paddings(), loco::DataType::S32))
296   RETURN_FALSE_UNLESS(has_type(node->constant_values(), Qtype))
297   return true;
298 }
299
300 template <loco::DataType Qtype, loco::DataType Btype>
301 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CirclePRelu *node)
302 {
303   return group_has_type(node, Qtype);
304 }
305
306 template <loco::DataType Qtype, loco::DataType Btype>
307 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CirclePow *node)
308 {
309   return group_has_type(node, Qtype);
310 }
311
312 template <loco::DataType Qtype, loco::DataType Btype>
313 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleReduceMax *node)
314 {
315   RETURN_FALSE_UNLESS(has_type(node, Qtype))
316   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
317   RETURN_FALSE_UNLESS(has_type(node->reduction_indices(), loco::DataType::S32))
318   return true;
319 }
320
321 template <loco::DataType Qtype, loco::DataType Btype>
322 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleRelu *node)
323 {
324   return group_has_type(node, Qtype);
325 }
326
327 template <loco::DataType Qtype, loco::DataType Btype>
328 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleReshape *node)
329 {
330   if (node->quantparam())
331   {
332     RETURN_FALSE_UNLESS(has_type(node, Qtype))
333     RETURN_FALSE_UNLESS(has_type(node->tensor(), Qtype))
334   }
335   else
336   {
337     RETURN_FALSE_UNLESS(has_type(node->tensor(), node->dtype()))
338   }
339   luci::CircleConst *shape = dynamic_cast<luci::CircleConst *>(node->shape());
340   if (shape != nullptr)
341     RETURN_FALSE_UNLESS(has_type(shape, loco::DataType::S32))
342   return true;
343 }
344
345 template <loco::DataType Qtype, loco::DataType Btype>
346 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleResizeBilinear *node)
347 {
348   RETURN_FALSE_UNLESS(has_type(node, Qtype))
349   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
350   return true;
351 }
352
353 template <loco::DataType Qtype, loco::DataType Btype>
354 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleResizeNearestNeighbor *node)
355 {
356   RETURN_FALSE_UNLESS(has_type(node, Qtype))
357   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
358   return true;
359 }
360
361 template <loco::DataType Qtype, loco::DataType Btype>
362 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleRsqrt *node)
363 {
364   return group_has_type(node, Qtype);
365 }
366
367 template <loco::DataType Qtype, loco::DataType Btype>
368 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSlice *node)
369 {
370   RETURN_FALSE_UNLESS(has_type(node, Qtype))
371   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
372   RETURN_FALSE_UNLESS(has_type(node->begin(), loco::DataType::S32) ||
373                       has_type(node->begin(), loco::DataType::S64))
374   RETURN_FALSE_UNLESS(has_type(node->size(), loco::DataType::S32) ||
375                       has_type(node->size(), loco::DataType::S64))
376   return true;
377 }
378
379 template <loco::DataType Qtype, loco::DataType Btype>
380 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSpaceToBatchND *node)
381 {
382   RETURN_FALSE_UNLESS(has_type(node, Qtype))
383   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
384   return true;
385 }
386
387 template <loco::DataType Qtype, loco::DataType Btype>
388 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSpaceToDepth *node)
389 {
390   return group_has_type(node, Qtype);
391 }
392
393 template <loco::DataType Qtype, loco::DataType Btype>
394 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSplit *node)
395 {
396   // node's output is the input of CircleSplitOut, thus not quantized
397   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
398   return true;
399 }
400
401 template <loco::DataType Qtype, loco::DataType Btype>
402 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSplitOut *node)
403 {
404   RETURN_FALSE_UNLESS(has_type(node, Qtype))
405
406   // SplitOut has the same qparam with the input of Split
407   auto split = loco::must_cast<luci::CircleSplit *>(node->input());
408   auto input = loco::must_cast<luci::CircleNode *>(split->input());
409   RETURN_FALSE_UNLESS(node->quantparam());
410   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == input->quantparam()->scale[0]);
411   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == input->quantparam()->zerop[0]);
412   return true;
413 }
414
415 template <loco::DataType Qtype, loco::DataType Btype>
416 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSplitV *node)
417 {
418   // node's output is the input of CircleSplitVOut, thus not quantized
419   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
420   return true;
421 }
422
423 template <loco::DataType Qtype, loco::DataType Btype>
424 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSplitVOut *node)
425 {
426   RETURN_FALSE_UNLESS(has_type(node, Qtype))
427
428   // SplitVOut has the same qparam with the input of SplitV
429   auto splitv = loco::must_cast<luci::CircleSplitV *>(node->input());
430   auto input = loco::must_cast<luci::CircleNode *>(splitv->input());
431   RETURN_FALSE_UNLESS(node->quantparam());
432   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == input->quantparam()->scale[0]);
433   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == input->quantparam()->zerop[0]);
434   return true;
435 }
436
437 template <loco::DataType Qtype, loco::DataType Btype>
438 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleSqrt *node)
439 {
440   return group_has_type(node, Qtype);
441 }
442
443 template <loco::DataType Qtype, loco::DataType Btype>
444 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleStridedSlice *node)
445 {
446   RETURN_FALSE_UNLESS(has_type(node, Qtype))
447   RETURN_FALSE_UNLESS(has_type(node->input(), Qtype))
448
449   auto input = loco::must_cast<luci::CircleNode *>(node->input());
450   RETURN_FALSE_UNLESS(node->quantparam());
451   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == input->quantparam()->scale[0]);
452   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == input->quantparam()->zerop[0]);
453   return true;
454 }
455
456 template <loco::DataType Qtype, loco::DataType Btype>
457 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleTranspose *node)
458 {
459   RETURN_FALSE_UNLESS(has_type(node, Qtype))
460   RETURN_FALSE_UNLESS(has_type(node->a(), Qtype))
461   RETURN_FALSE_UNLESS(has_type(node->perm(), loco::DataType::S32))
462   return true;
463 }
464
465 template <loco::DataType Qtype, loco::DataType Btype>
466 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleTransposeConv *node)
467 {
468   RETURN_FALSE_UNLESS(has_type(node, Qtype))
469   RETURN_FALSE_UNLESS(has_type(node->outBackprop(), Qtype))
470   RETURN_FALSE_UNLESS(has_type(node->filter(), Qtype))
471   luci::CircleConst *bias = dynamic_cast<luci::CircleConst *>(node->bias());
472   if (bias != nullptr)
473     RETURN_FALSE_UNLESS(has_type(bias, Btype))
474   return true;
475 }
476
477 template <loco::DataType Qtype, loco::DataType Btype>
478 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleUnpack *node)
479 {
480   // node's output is the input of CircleUnpackOut, thus not quantized
481   RETURN_FALSE_UNLESS(has_type(node->value(), Qtype))
482   return true;
483 }
484
485 template <loco::DataType Qtype, loco::DataType Btype>
486 bool VerifyQuantizedNodeTypeBase<Qtype, Btype>::visit(const luci::CircleUnpackOut *node)
487 {
488   RETURN_FALSE_UNLESS(has_type(node, Qtype))
489
490   // UnpackOut has the same qparam with the input of Unpack
491   auto Unpack = loco::must_cast<luci::CircleUnpack *>(node->input());
492   auto input = loco::must_cast<luci::CircleNode *>(Unpack->value());
493   RETURN_FALSE_UNLESS(node->quantparam() && input->quantparam());
494   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == input->quantparam()->scale[0]);
495   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == input->quantparam()->zerop[0]);
496   return true;
497 }
498
499 } // namespace luci
500
501 namespace luci
502 {
503
504 bool VerifyQuantizedNodeU8Type::visit(const luci::CircleTanh *node)
505 {
506   RETURN_FALSE_UNLESS(group_has_type(node, loco::DataType::U8));
507
508   RETURN_FALSE_UNLESS(node->quantparam());
509   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == 2.0f / 256.0f);
510   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == 128);
511   return true;
512 }
513
514 bool VerifyQuantizedNodeU8Type::visit(const luci::CircleLogistic *node)
515 {
516   RETURN_FALSE_UNLESS(group_has_type(node, loco::DataType::U8));
517
518   RETURN_FALSE_UNLESS(node->quantparam());
519   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == 1.0f / 256.0f);
520   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == 0);
521   return true;
522 }
523
524 bool VerifyQuantizedNodeU8Type::visit(const luci::CircleSoftmax *node)
525 {
526   RETURN_FALSE_UNLESS(group_has_type(node, loco::DataType::U8));
527
528   RETURN_FALSE_UNLESS(node->quantparam());
529   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == 1.0f / 255.0f);
530   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == 0);
531   return true;
532 }
533
534 } // namespace luci
535
536 namespace luci
537 {
538
539 bool VerifyQuantizedNodeS16Type::visit(const luci::CircleTanh *node)
540 {
541   RETURN_FALSE_UNLESS(group_has_type(node, loco::DataType::S16));
542
543   RETURN_FALSE_UNLESS(node->quantparam());
544   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == 1.0f / 32768.0f);
545   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == 0);
546   return true;
547 }
548
549 bool VerifyQuantizedNodeS16Type::visit(const luci::CircleLogistic *node)
550 {
551   RETURN_FALSE_UNLESS(group_has_type(node, loco::DataType::S16));
552
553   RETURN_FALSE_UNLESS(node->quantparam());
554   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == 1.0f / 32768.0f);
555   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == 0);
556   return true;
557 }
558
559 bool VerifyQuantizedNodeS16Type::visit(const luci::CircleSoftmax *node)
560 {
561   RETURN_FALSE_UNLESS(group_has_type(node, loco::DataType::S16));
562
563   RETURN_FALSE_UNLESS(node->quantparam());
564   RETURN_FALSE_UNLESS(node->quantparam()->scale[0] == 1.0f / 32767.0f);
565   RETURN_FALSE_UNLESS(node->quantparam()->zerop[0] == 0);
566   return true;
567 }
568
569 } // namespace luci
570
571 #undef RETURN_FALSE_UNLESS