blob: a47cf5c7798ea551a5cc0cf730f463f2122495ae [file] [log] [blame]
Jeremy Johnson015c3552022-02-23 12:15:03 +00001# Copyright (c) 2020-2022, ARM Limited.
2# SPDX-License-Identifier: Apache-2.0
3import numpy as np
4import tensorflow as tf
5from frameworks.tensor_gen import TGen
6
7
8class TBuilder:
9 """The member functions build the tensorflow operators into small networks
10 for our tests"""
11
12 def __init__(self):
13 pass
14
15 def fake_quant(tensor, tensor_scale, name):
16 """Helper function for quantizing with a scaling parameters structure."""
17 return tf.quantization.fake_quant_with_min_max_args(
18 tensor,
19 min=tensor_scale.min,
20 max=tensor_scale.max,
21 num_bits=tensor_scale.num_bits,
22 narrow_range=tensor_scale.narrow_range,
23 name=name,
24 )
25
26 def fake_quant_params(tensor, min, max, scaling, name):
27 """Helper function for quantizing with individual scaling parameters."""
28 return tf.quantization.fake_quant_with_min_max_args(
29 tensor,
30 min=min,
31 max=max,
32 num_bits=scaling.num_bits,
33 narrow_range=scaling.narrow_range,
34 name=name,
35 )
36
37 class Add:
38 def __init__(self, name):
39 self.result_name = name
40
41 def eval(self, a, b):
42 return tf.add(a, b, name=self.result_name)
43
44 class Sub:
45 def __init__(self, name):
46 self.result_name = name
47
48 def eval(self, a, b):
49 return tf.subtract(a, b, name=self.result_name)
50
51 class Mul:
52 def __init__(self, name):
53 self.result_name = name
54
55 def eval(self, a, b):
56 return tf.multiply(a, b, name=self.result_name)
57
58 class Exp:
59 def __init__(self, name):
60 self.result_name = name
61
62 def eval(self, a):
63 return tf.exp(a, name=self.result_name)
64
65 class Rcp:
66 def __init__(self, name):
67 self.result_name = name
68
69 def eval(self, a):
70 return tf.math.reciprocal(a, name=self.result_name)
71
72 class Relu:
73 def __init__(self, name):
74 self.result_name = name
75
76 def eval(self, a):
77 return tf.nn.relu(a, name=self.result_name)
78
79 class Relu6:
80 def __init__(self, name):
81 self.result_name = name
82
83 def eval(self, a):
84 return tf.nn.relu6(a, name=self.result_name)
85
86 class LeakyRelu:
87 def __init__(self, alpha, name):
88 self.alpha = alpha
89 self.result_name = name
90
91 def eval(self, a):
92 return tf.nn.leaky_relu(a, alpha=self.alpha, name=self.result_name)
93
94 class Concat:
95 def __init__(self, axis, name):
96 self.axis = axis
97 self.result_name = name
98
99 def eval(self, a, b):
100 return tf.concat([a, b], self.axis, name=self.result_name)
101
102 class BitwiseAnd:
103 def __init__(self, name):
104 self.result_name = name
105
106 def eval(self, a, b):
107 return tf.bitwise.bitwise_and(a, b, name=self.result_name)
108
109 class BitwiseOr:
110 def __init__(self, name):
111 self.result_name = name
112
113 def eval(self, a, b):
114 return tf.bitwise.bitwise_or(a, b, name=self.result_name)
115
116 class BitwiseNot:
117 def __init__(self, name):
118 self.result_name = name
119
120 def eval(self, a):
121 return tf.bitwise.invert(a, name=self.result_name)
122
123 class BitwiseXor:
124 def __init__(self, name):
125 self.result_name = name
126
127 def eval(self, a, b):
128 return tf.bitwise.bitwise_xor(a, b, name=self.result_name)
129
130 class LogicalAnd:
131 def __init__(self, name):
132 self.result_name = name
133
134 def eval(self, a, b):
135 return tf.math.logical_and(a, b, name=self.result_name)
136
137 class LogicalOr:
138 def __init__(self, name):
139 self.result_name = name
140
141 def eval(self, a, b):
142 return tf.math.logical_or(a, b, name=self.result_name)
143
144 class LogicalNot:
145 def __init__(self, name):
146 self.result_name = name
147
148 def eval(self, a):
149 return tf.math.logical_not(a, name=self.result_name)
150
151 class ReduceAny:
152 def __init__(self, axis_list, keepdims, name):
153 self.axis_list = axis_list
154 self.keepdims = keepdims
155 self.result_name = name
156
157 def eval(self, a):
158 return tf.math.reduce_any(
159 a, self.axis_list, keepdims=self.keepdims, name=self.result_name
160 )
161
162 class ReduceAll:
163 def __init__(self, axis_list, keepdims, name):
164 self.axis_list = axis_list
165 self.keepdims = keepdims
166 self.result_name = name
167
168 def eval(self, a):
169 return tf.math.reduce_all(
170 a, self.axis_list, keepdims=self.keepdims, name=self.result_name
171 )
172
173 class ReduceMin:
174 def __init__(self, axis_list, keepdims, name):
175 self.axis_list = axis_list
176 self.keepdims = keepdims
177 self.result_name = name
178
179 def eval(self, a):
180 return tf.math.reduce_min(
181 a, self.axis_list, keepdims=self.keepdims, name=self.result_name
182 )
183
184 class ReduceMax:
185 def __init__(self, axis_list, keepdims, name):
186 self.axis_list = axis_list
187 self.keepdims = keepdims
188 self.result_name = name
189
190 def eval(self, a):
191 return tf.math.reduce_max(
192 a, self.axis_list, keepdims=self.keepdims, name=self.result_name
193 )
194
195 class ReduceSum:
196 def __init__(self, axis_list, keepdims, name):
197 self.axis_list = axis_list
198 self.keepdims = keepdims
199 self.result_name = name
200
201 def eval(self, a):
202 return tf.math.reduce_sum(
203 a, self.axis_list, keepdims=self.keepdims, name=self.result_name
204 )
205
206 class ReduceMean:
207 def __init__(self, axis_list, keepdims, name):
208 self.axis_list = axis_list
209 self.keepdims = keepdims
210 self.result_name = name
211
212 def eval(self, a):
213 return tf.math.reduce_mean(
214 a, self.axis_list, keepdims=self.keepdims, name=self.result_name
215 )
216
217 class ReduceProduct:
218 def __init__(self, axis_list, keepdims, name):
219 self.axis_list = axis_list
220 self.keepdims = keepdims
221 self.result_name = name
222
223 def eval(self, a):
224 return tf.math.reduce_prod(
225 a, self.axis_list, keepdims=self.keepdims, name=self.result_name
226 )
227
228 class Min:
229 def __init__(self, name):
230 self.result_name = name
231
232 def eval(self, a, b):
233 return tf.math.minimum(a, b, name=self.result_name)
234
235 class Max:
236 def __init__(self, name):
237 self.result_name = name
238
239 def eval(self, a, b):
240 return tf.math.maximum(a, b, name=self.result_name)
241
242 class Pow:
243 def __init__(self, name):
244 self.result_name = name
245
246 def eval(self, a, b):
247 return tf.math.pow(a, b, name=self.result_name)
248
249 class Abs:
250 def __init__(self, name):
251 self.result_name = name
252
253 def eval(self, a):
254 return tf.math.abs(a, name=self.result_name)
255
256 class Ceil:
257 def __init__(self, name):
258 self.result_name = name
259
260 def eval(self, a):
261 return tf.math.ceil(a, name=self.result_name)
262
263 class Floor:
264 def __init__(self, name):
265 self.result_name = name
266
267 def eval(self, a):
268 return tf.math.floor(a, name=self.result_name)
269
270 class Log:
271 def __init__(self, name):
272 self.result_name = name
273
274 def eval(self, a):
275 return tf.math.log(a, name=self.result_name)
276
277 class Negate:
278 def __init__(self, name):
279 self.result_name = name
280
281 def eval(self, a):
282 return tf.math.negative(a, name=self.result_name)
283
284 class Rsqrt:
285 def __init__(self, name):
286 self.result_name = name
287
288 def eval(self, a):
289 return tf.math.rsqrt(a, name=self.result_name)
290
291 class Sigmoid:
292 def __init__(self, name):
293 self.result_name = name
294
295 def eval(self, a):
296 return tf.math.sigmoid(a, name=self.result_name)
297
298 class Tanh:
299 def __init__(self, name):
300 self.result_name = name
301
302 def eval(self, a):
303 return tf.math.tanh(a, name=self.result_name)
304
305 class Square:
306 def __init__(self, name):
307 self.result_name = name
308
309 def eval(self, a):
310 return tf.math.square(a, name=self.result_name)
311
312 class SquaredDifference:
313 def __init__(self, name):
314 self.result_name = name
315
316 def eval(self, a, b):
317 return tf.math.squared_difference(a, b, name=self.result_name)
318
319 class Equal:
320 def __init__(self, name):
321 self.result_name = name
322
323 def eval(self, a, b):
324 return tf.math.equal(a, b, name=self.result_name)
325
326 class GreaterEqual:
327 def __init__(self, name):
328 self.result_name = name
329
330 def eval(self, a, b):
331 return tf.math.greater_equal(a, b, name=self.result_name)
332
333 class Greater:
334 def __init__(self, name):
335 self.result_name = name
336
337 def eval(self, a, b):
338 return tf.math.greater(a, b, name=self.result_name)
339
340 class Less:
341 def __init__(self, name):
342 self.result_name = name
343
344 def eval(self, a, b):
345 return tf.math.less(a, b, name=self.result_name)
346
347 class LessEqual:
348 def __init__(self, name):
349 self.result_name = name
350
351 def eval(self, a, b):
352 return tf.math.less_equal(a, b, name=self.result_name)
353
354 class Conv2d:
355 def __init__(self, weight, strides, padding, dilations, name):
356 self.weight = weight
357 self.strides = strides
358 self.padding = padding
359 self.dilations = dilations
360 self.result_name = name
361
362 def eval(self, input):
363 return tf.nn.conv2d(
364 input,
365 self.weight,
366 self.strides,
367 self.padding,
368 data_format="NHWC",
369 dilations=self.dilations,
370 name=self.result_name,
371 )
372
373 class Conv2dRelu:
374 def __init__(self, weight, name):
375 self.weight = weight
376 self.result_name = name
377
378 def eval(self, input):
379 conv2d = tf.nn.conv2d(
380 input,
381 self.weight,
382 [1, 1, 1, 1],
383 "SAME",
384 data_format="NHWC",
385 dilations=[1, 1, 1, 1],
386 name="conv2d",
387 )
388 return tf.nn.relu(conv2d, name=self.result_name)
389
390 class Conv2dRelu6:
391 def __init__(self, weight, name):
392 self.weight = weight
393 self.result_name = name
394
395 def eval(self, input):
396 conv2d = tf.nn.conv2d(
397 input,
398 self.weight,
399 [1, 1, 1, 1],
400 "SAME",
401 data_format="NHWC",
402 dilations=[1, 1, 1, 1],
403 name="conv2d",
404 )
405 return tf.nn.relu6(conv2d, name=self.result_name)
406
407 class Conv2dReluN1To1:
408 def __init__(self, weight, name):
409 self.weight = weight
410 self.result_name = name
411
412 def eval(self, input):
413 conv2d = tf.nn.conv2d(
414 input,
415 self.weight,
416 [1, 1, 1, 1],
417 "SAME",
418 data_format="NHWC",
419 dilations=[1, 1, 1, 1],
420 name="conv2d",
421 )
422 return tf.clip_by_value(conv2d, -1.0, 1.0, name=self.result_name)
423
424 class Conv2dTanh:
425 def __init__(self, weight, name):
426 self.weight = weight
427 self.result_name = name
428
429 def eval(self, input):
430 conv2d = tf.nn.conv2d(
431 input,
432 self.weight,
433 [1, 1, 1, 1],
434 "SAME",
435 data_format="NHWC",
436 dilations=[1, 1, 1, 1],
437 name="conv2d",
438 )
439 return tf.math.tanh(conv2d, name=self.result_name)
440
441 class Conv2dWithBias:
442 def __init__(self, weight, bias, strides, padding, dilations, name):
443 self.weight = weight
444 self.bias = bias
445 self.strides = strides
446 self.padding = padding
447 self.dilations = dilations
448 self.result_name = name
449
450 def eval(self, input):
451 conv2d_op = tf.nn.conv2d(
452 input,
453 self.weight,
454 self.strides,
455 self.padding,
456 data_format="NHWC",
457 dilations=self.dilations,
458 name="conv2d",
459 )
460 bias_add_op = tf.nn.bias_add(
461 conv2d_op, self.bias, data_format="NHWC", name=self.result_name
462 )
463 return bias_add_op
464
465 class DepthwiseConv2d:
466 def __init__(self, weight, strides, padding, dilations, name):
467 self.weight = weight
468 self.strides = strides
469 self.padding = padding
470 self.dilations = dilations
471 self.result_name = name
472
473 def eval(self, input):
474 dws_conv2d = tf.nn.depthwise_conv2d(
475 input,
476 self.weight,
477 self.strides,
478 self.padding,
479 data_format="NHWC",
480 dilations=self.dilations,
481 name="dws_conv2d",
482 )
483 return tf.identity(dws_conv2d, name=self.result_name)
484
485 class DepthwiseConv2dWithBias:
486 def __init__(self, weight, bias, strides, padding, dilations, name):
487 self.weight = weight
488 self.bias = bias
489 self.strides = strides
490 self.padding = padding
491 self.dilations = dilations
492 self.result_name = name
493
494 def eval(self, input):
495 dws_conv2d = tf.nn.depthwise_conv2d(
496 input,
497 self.weight,
498 self.strides,
499 self.padding,
500 data_format="NHWC",
501 dilations=self.dilations,
502 name="dws_conv2d",
503 )
504 bias_add_op = tf.nn.bias_add(
505 dws_conv2d, self.bias, data_format="NHWC", name=self.result_name
506 )
507 return bias_add_op
508
509 class TransposeConv2d:
510 def __init__(self, weight, output_shape, strides, padding, name):
511 self.weight = weight
512 self.output_shape = output_shape
513 self.strides = strides
514 self.padding = padding
515 self.result_name = name
516
517 def eval(self, input):
518 return tf.nn.conv2d_transpose(
519 input,
520 self.weight,
521 self.output_shape,
522 self.strides,
523 self.padding,
524 data_format="NHWC",
525 name=self.result_name,
526 )
527
528 class Argmax:
529 def __init__(self, axis, name):
530 self.axis = axis
531 self.result_name = name
532
533 def eval(self, a):
534 return tf.argmax(a, self.axis, output_type=tf.int32, name=self.result_name)
535
536 class AvgPool2d:
537 def __init__(self, strides, kernel_size, padding, name):
538 self.strides = strides
539 self.kernel_size = kernel_size
540 self.padding = padding
541 self.result_name = name
542
543 def eval(self, input):
544 return tf.nn.avg_pool2d(
545 input,
546 strides=self.strides,
547 ksize=self.kernel_size,
548 padding=self.padding,
549 data_format="NHWC",
550 name=self.result_name,
551 )
552
553 class MaxPool2d:
554 def __init__(self, strides, kernel_size, padding, name):
555 self.strides = strides
556 self.kernel_size = kernel_size
557 self.padding = padding
558 self.result_name = name
559
560 def eval(self, input):
561 return tf.nn.max_pool2d(
562 input,
563 strides=self.strides,
564 ksize=self.kernel_size,
565 padding=self.padding,
566 data_format="NHWC",
567 name=self.result_name,
568 )
569
570 class Reshape:
571 def __init__(self, shape, name):
572 self.shape = shape
573 self.result_name = name
574
575 def eval(self, a):
576 reshape_op = tf.reshape(a, self.shape)
577 return tf.identity(reshape_op, name=self.result_name)
578
579 class Transpose:
580 def __init__(self, perm, name):
581 self.perm = perm
582 self.result_name = name
583
584 def eval(self, a):
585 return tf.transpose(a, self.perm, name=self.result_name)
586
587 class Slice:
588 def __init__(self, begin, size, name):
589 self.begin = begin
590 self.size = size
591 self.result_name = name
592
593 def eval(self, a):
594 return tf.slice(a, begin=self.begin, size=self.size, name=self.result_name)
595
596 class StridedSlice:
597 def __init__(
598 self,
599 begin,
600 end,
601 strides,
602 begin_mask,
603 end_mask,
604 ellipsis_mask,
605 new_axis_mask,
606 shrink_axis_mask,
607 name,
608 ):
609 self.begin = begin
610 self.end = end
611 self.strides = strides
612 self.begin_mask = begin_mask
613 self.end_mask = end_mask
614 self.ellipsis_mask = ellipsis_mask
615 self.new_axis_mask = new_axis_mask
616 self.shrink_axis_mask = shrink_axis_mask
617 self.result_name = name
618
619 def eval(self, a):
620 return tf.strided_slice(
621 a,
622 begin=self.begin,
623 end=self.end,
624 strides=self.strides,
625 begin_mask=self.begin_mask,
626 end_mask=self.end_mask,
627 ellipsis_mask=self.ellipsis_mask,
628 new_axis_mask=self.new_axis_mask,
629 shrink_axis_mask=self.shrink_axis_mask,
630 name=self.result_name,
631 )
632
633 class Select:
634 def __init__(self, name):
635 self.result_name = name
636
637 def eval(self, selector, a, b):
638 return tf.where(condition=selector, x=a, y=b, name=self.result_name)
639
640 class Addn:
641 def __init__(self, name):
642 self.result_name = name
643
644 def eval(self, a, b, c, d):
645 return tf.add_n([a, b, c, d], name=self.result_name)
646
647 class Concatv2:
648 def __init__(self, axis, name):
649 self.axis = axis
650 self.result_name = name
651
652 def eval(self, a, b, c, d):
653 return tf.concat([a, b, c, d], axis=self.axis, name=self.result_name)
654
655 class Stack:
656 def __init__(self, axis, name):
657 self.axis = axis
658 self.result_name = name
659
660 def eval(self, a, b, c, d):
661 return tf.stack([a, b, c, d], axis=self.axis, name=self.result_name)
662
663 class Unstack:
664 def __init__(self, axis, name):
665 self.axis = axis
666 self.result_name = name
667
668 def eval(self, a):
669 unstack_op = tf.unstack(a, axis=self.axis, name="unstack_op")
670 result_count = a.shape[self.axis]
671
672 if result_count == 1:
673 return tf.identity(unstack_op[0], name=self.result_name)
674
675 sums = []
676 for i in range(result_count):
677 sums.append(
678 tf.math.reduce_sum(unstack_op[i], name="reduce_{}".format(i))
679 )
680 return tf.stack(sums, 0, name=self.result_name)
681
682 class Pad:
683 def __init__(self, padding, name):
684 self.padding = padding
685 self.result_name = name
686
687 def eval(self, a):
688 return tf.pad(
689 a,
690 self.padding,
691 mode="CONSTANT",
692 constant_values=0,
693 name=self.result_name,
694 )
695
696 class ExpandDims:
697 def __init__(self, axis, name):
698 self.axis = axis
699 self.result_name = name
700
701 def eval(self, a):
702 return tf.expand_dims(a, self.axis, name=self.result_name)
703
704 class Shape:
705 def __init__(self, name):
706 self.result_name = name
707
708 def eval(self, a):
709 return tf.shape(a, name=self.result_name)
710
711 class Rank:
712 def __init__(self, name):
713 self.result_name = name
714
715 def eval(self, a):
716 return tf.rank(a, name=self.result_name)
717
718 class Fill:
719 def __init__(self, shape, value, name):
720 self.shape = shape
721 self.value = value
722 self.result_name = name
723
724 def eval(self, a):
725 return tf.fill(self.shape, self.value, name=self.result_name)
726
727 class Elu:
728 def __init__(self, name):
729 self.result_name = name
730
731 def eval(self, a):
732 return tf.nn.elu(a, name=self.result_name)
733
734 class Softmax:
735 def __init__(self, name):
736 self.result_name = name
737
738 def eval(self, a):
739 return tf.nn.softmax(a, name=self.result_name)
740
741 class LogSoftmax:
742 def __init__(self, name):
743 self.result_name = name
744
745 def eval(self, a):
746 return tf.nn.log_softmax(a, name=self.result_name)
747
748 class MatMul:
749 def __init__(self, name):
750 self.result_name = name
751
752 def eval(self, a, b):
753 return tf.linalg.matmul(a, b, name=self.result_name)
754
755 class AddScalar:
756 def __init__(self, name):
757 self.result_name = name
758
759 def eval(self, a):
760 return tf.add(a, 1, name=self.result_name)
761
762 class Add1d:
763 def __init__(self, name):
764 self.result_name = name
765
766 def eval(self, a, b):
767 if len(b.shape) > 1:
768 b_1d = tf.reduce_sum(b, axis=list(range(0, len(b.shape) - 1, 1)))
769 else:
770 b_1d = b
771 return tf.add(a, b_1d, name=self.result_name)
772
773 class Split:
774 def __init__(self, num_splits, axis, name):
775 self.num_splits = num_splits
776 self.axis = axis
777 self.result_name = name
778
779 def eval(self, a):
780 # The split op generates a list of outputs. Since we have difficulty
781 # serializing a list or array of Numpy arrays, we will reduce each of
782 # the results
783
784 if not isinstance(self.num_splits, list):
785 split_op = tf.split(
786 a, num_or_size_splits=self.num_splits, axis=self.axis, name="split"
787 )
788 result_count = self.num_splits
789 else:
790 num_split = np.asarray(self.num_splits, dtype=np.int32)
791 split_vec_op = tf.compat.v1.constant(
792 num_split,
793 shape=num_split.shape,
794 dtype=tf.int32,
795 name="const_split_vec",
796 )
797 split_op = tf.split(
798 a, num_or_size_splits=split_vec_op, axis=self.axis, name="split"
799 )
800 result_count = num_split.shape[0]
801
802 sums = []
803 for i in range(result_count):
804 sums.append(tf.math.reduce_sum(split_op[i], name="reduce_{}".format(i)))
805 return tf.stack(sums, 0, name=self.result_name)
806
807 class Tile:
808 def __init__(self, multiples, name):
809 self.multiples = multiples
810 self.result_name = name
811
812 def eval(self, a):
813 t = tf.tile(a, self.multiples, name="tile")
814 return tf.identity(t, name=self.result_name)
815
816 class Reverse:
817 def __init__(self, axis, name):
818 self.axis = axis
819 self.result_name = name
820
821 def eval(self, a):
822 return tf.reverse(a, [self.axis], name=self.result_name)
823
824 class Gather:
825 def __init__(self, indices, batch_dims, axis, name):
826 self.indices = indices
827 self.batch_dims = batch_dims
828 self.axis = axis
829 self.result_name = name
830
831 def eval(self, a):
832 return tf.gather(
833 a,
834 self.indices,
835 batch_dims=self.batch_dims,
836 axis=self.axis,
837 name=self.result_name,
838 )
839
840 class GatherNd:
841 def __init__(self, indices, name):
842 self.indices = indices
843 self.result_name = name
844
845 def eval(self, a):
846 return tf.gather_nd(a, self.indices, name=self.result_name)
847
848 class ScatterNd:
849 def __init__(self, shape, indices_shape, N, rng, name):
850 self.shape = shape
851 self.indices_shape = indices_shape
852 self.N = N
853 self.rng = rng
854 self.result_name = name
855
856 def eval(self, a):
857
858 # This operator is special. The indices and updates tensors really need
859 # to be created together, but in the current structure of this tool there
860 # is no way to do that before now. The number of updates is determined by
861 # the indices, so we can really only create that after indices; but we
862 # don't know the type at that time.
863 #
864 # Shapes are guaranteed deterministic, but we'll use our rng
865 # copied from the arggen stage. It's possible that index and
866 # update *values* will be non-deterministic.
867 #
868 # We take the tensor_tensor simply to get the dtype.
869
870 shape_const = tf.constant(self.shape, tf.int32)
871
872 updates_shape = list(self.indices_shape[:-1])
873 updates_shape.extend(self.shape[self.indices_shape[-1] :])
874
875 updates_const = tf.constant(TGen.getRand(updates_shape, a.dtype, self.rng))
876
877 indices = np.zeros(self.indices_shape, dtype=np.int32)
878
879 # We need to generate the random indices tensor based on the
880 # limits of 'shape' for each dimension. Surely, there is a faster
881 # vectorized way to do this, but the tensors are fairly small so we
882 # will do this one element at a time. Each element needs to be sized based
883 # on the size of the last dimension.
884 for idx in np.ndindex(indices.shape):
885 indices[idx] = self.rng.integers(0, self.shape[idx[-1]], size=1)[0]
886 # print('{} {}'.format(idx, indices[idx]))
887
888 indices_const = tf.constant(indices, dtype=tf.int32)
889
890 return tf.scatter_nd(
891 indices=indices_const,
892 updates=updates_const,
893 shape=shape_const,
894 name=self.result_name,
895 )
896
897 class SpaceToBatch:
898 def __init__(self, block_shape, padding, name):
899 self.block_shape = block_shape
900 self.padding = padding
901 self.result_name = name
902
903 def eval(self, a):
904 return tf.space_to_batch(
905 a, self.block_shape, self.padding, name=self.result_name
906 )
907
908 class BatchToSpace:
909 def __init__(self, block_shape, cropping, name):
910 self.block_shape = block_shape
911 self.cropping = cropping
912 self.result_name = name
913
914 def eval(self, a):
915 # transpose to swap depth and batch first. this could avoid adding new shape
916 block_rank = len(self.block_shape)
917 perm = [len(a.shape) - 1]
918 for i in range(block_rank):
919 perm.append(i + 1)
920 perm.append(0)
921 transpose_op = tf.transpose(a, perm)
922 return tf.batch_to_space(
923 transpose_op, self.block_shape, self.cropping, name=self.result_name
924 )
925
926 class SpaceToDepth:
927 def __init__(self, block_shape, name):
928 self.block_shape = block_shape
929 self.result_name = name
930
931 def eval(self, a):
932 return tf.nn.space_to_depth(a, self.block_shape, name=self.result_name)
933
934 class DepthToSpace:
935 def __init__(self, block_shape, name):
936 self.block_shape = block_shape
937 self.result_name = name
938
939 def eval(self, a):
940 return tf.nn.depth_to_space(a, self.block_shape, name=self.result_name)
941
942 class OneHot:
943 def __init__(self, depth, axis, name):
944 self.depth = depth
945 self.axis = axis
946 self.result_name = name
947
948 def eval(self, indices, on_value, off_value):
949 return tf.one_hot(
950 indices,
951 self.depth,
952 on_value,
953 off_value,
954 self.axis,
955 on_value.dtype,
956 self.result_name,
957 )
958
959 class Fakequant:
960 def __init__(self, num_bits, narrow_range, name):
961 self.num_bits = num_bits
962 self.narrow_range = narrow_range
963 self.result_name = name
964
965 def eval(self, a):
966 return tf.quantization.fake_quant_with_min_max_args(
967 a,
968 min=-2.0,
969 max=2.0,
970 num_bits=self.num_bits,
971 narrow_range=self.narrow_range,
972 name=self.result_name,
973 )
974
975 class ResizeNearest:
976 def __init__(self, name):
977 self.result_name = name
978
979 def eval(self, a):
980 out_shape = []
981 out_shape.append(a.shape[1] * 2)
982 out_shape.append(a.shape[2] * 2)
983
984 # tf.image.resize() will overwrite the node name with result_name +
985 # '/BILINEAR' need to add extra identity to force output tensor name to
986 # result_name return tf.image.resize(a, out_shape,
987 # method=tf.image.ResizeMethod.NEAREST_NEIGHBOR, name=result_name)
988 resize = tf.image.resize(
989 a,
990 out_shape,
991 method=tf.image.ResizeMethod.NEAREST_NEIGHBOR,
992 name="resize",
993 )
994 return tf.identity(resize, name=self.result_name)
995
996 class ResizeBilinear:
997 def __init__(self, name):
998 self.result_name = name
999
1000 def eval(self, a):
1001 out_shape = []
1002 out_shape.append(a.shape[1] * 2)
1003 out_shape.append(a.shape[2] * 2)
1004
1005 # tf.image.resize() will overwrite the node name with result_name +
1006 # '/BILINEAR' need to add extra identity to force output tensor name to
1007 # result_name return tf.image.resize(a, out_shape,
1008 # method=tf.image.ResizeMethod.NEAREST_NEIGHBOR, name=result_name)
1009 resize = tf.image.resize(
1010 a, out_shape, method=tf.image.ResizeMethod.BILINEAR, name="resize"
1011 )
1012 return tf.identity(resize, name=self.result_name)
1013
1014 class LeftShift:
1015 def __init__(self, shift, name):
1016 self.shift = shift
1017 self.result_name = name
1018
1019 def eval(self, a):
1020 return tf.bitwise.left_shift(a, self.shift, name=self.result_name)
1021
1022 class RightShift:
1023 def __init__(self, shift, name):
1024 self.shift = shift
1025 self.result_name = name
1026
1027 def eval(self, a):
1028 return tf.bitwise.right_shift(a, self.shift, name=self.result_name)