blob: d0c0a0b1edfeedd22c5db5ad70075b037800c1fc [file] [log] [blame]
Jerry Ge9e94af82022-10-27 09:57:00 -07001# Copyright (c) 2020-2023, ARM Limited.
Jeremy Johnson015c3552022-02-23 12:15:03 +00002# SPDX-License-Identifier: Apache-2.0
Jerry Geb1f25012023-03-03 11:33:51 -08003import enum
4
Jeremy Johnson015c3552022-02-23 12:15:03 +00005import numpy as np
6import tensorflow as tf
7
8# FIXME: replace hardcoded '* 2' with random integers, where possible
9
10# The scaling factor for random numbers generated in input tensors. The
11# random numbers are calculated as:
12# (np.random.rand() - RAND_SHIFT_FACTOR) * RAND_SCALE_FACTOR
13# FIXME: improve range here
14RAND_SCALE_FACTOR = 4.0
15# Amount to add to random numbers
16RAND_SHIFT_FACTOR = 0.5
17
18RAND_INT_MIN = -128
19RAND_INT_MAX = 128
20
21
Jerry Geb1f25012023-03-03 11:33:51 -080022class ElemSignedness(enum.Enum):
23 ALL_RANGE = 1
24 POSITIVE = 2
25 NEGATIVE = 3
26
27
Jeremy Johnson015c3552022-02-23 12:15:03 +000028class TGen:
29 """A collection of functions to build tensor value arguments for an operator"""
30
31 def __init__(self):
32 pass
33
34 @staticmethod
Jerry Geb1f25012023-03-03 11:33:51 -080035 def getRand(shape, dtype, rng, elem_signedness=ElemSignedness.ALL_RANGE):
36 if elem_signedness == ElemSignedness.POSITIVE:
37 RAND_SHIFT_FACTOR = 0
38 elif elem_signedness == ElemSignedness.NEGATIVE:
39 RAND_SHIFT_FACTOR = 1
40 else:
41 RAND_SHIFT_FACTOR = 0.5
42
Jeremy Johnson015c3552022-02-23 12:15:03 +000043 if dtype == tf.float32:
44 return np.float32(
45 (rng.random(size=shape) - RAND_SHIFT_FACTOR) * RAND_SCALE_FACTOR
46 )
47 if dtype == tf.float16:
48 return np.float16(
49 (rng.random(size=shape) - RAND_SHIFT_FACTOR) * RAND_SCALE_FACTOR
50 )
51 if dtype == tf.int32:
52 return np.int32(
53 rng.integers(low=RAND_INT_MIN, high=RAND_INT_MAX, size=shape)
54 )
55 if dtype == tf.uint32:
56 return np.uint32(rng.integers(low=0, high=RAND_INT_MAX, size=shape))
57 if dtype == tf.bool:
58 return np.bool_(rng.choice(a=[False, True], size=shape))
Luke Hutton714aa602023-02-08 19:45:26 +000059 if dtype == tf.complex64:
60 return TGen.getRand(shape, np.float32, rng) + 1j * TGen.getRand(
61 shape, np.float32, rng
62 )
Jeremy Johnson015c3552022-02-23 12:15:03 +000063
64 raise Exception("Unsupported type: {}".format(dtype))
65
66 @staticmethod
Jerry Geb1f25012023-03-03 11:33:51 -080067 def tgBasicPositive(op, shape, dtype, rng, elem_signedness=ElemSignedness.POSITIVE):
68 return TGen.tgBasic(op, shape, dtype, rng, elem_signedness)
69
70 @staticmethod
71 def tgBasic(op, shape, dtype, rng, elem_signedness=ElemSignedness.ALL_RANGE):
Jeremy Johnson015c3552022-02-23 12:15:03 +000072 # Build random tensor placeholder node args of a given shape
73 pl, const = op["operands"]
74
75 tf_placeholders = []
76 tf_consts = []
77
78 for i in range(pl):
79 tf_placeholders.append(
Jerry Geb1f25012023-03-03 11:33:51 -080080 (
81 "placeholder_{}".format(i),
82 TGen.getRand(shape, dtype, rng, elem_signedness),
83 )
Jeremy Johnson015c3552022-02-23 12:15:03 +000084 )
85
86 for i in range(const):
Jerry Geb1f25012023-03-03 11:33:51 -080087 tf_consts.append(
88 ("const_{}".format(i), TGen.getRand(shape, dtype, rng, elem_signedness))
89 )
Jeremy Johnson015c3552022-02-23 12:15:03 +000090
91 return tf_placeholders, tf_consts
92
93 @staticmethod
Won Jeon6c93f412023-07-08 07:04:08 +000094 def tgBFuzz(op, shape, dtype, rng, for_tflite_converter=True):
Jeremy Johnson015c3552022-02-23 12:15:03 +000095 # Build random tensor placeholder node args of a given shape, optionally
96 # fuzzing the arguments with random 1's to force broadcasting
97
98 pl, const = op["operands"]
99
100 assert const == 0
101
Won Jeon6c93f412023-07-08 07:04:08 +0000102 if not for_tflite_converter:
103 fuzz_arg = rng.integers(0, pl + const)
104 fuzz_idx = rng.integers(0, len(shape))
Jeremy Johnson015c3552022-02-23 12:15:03 +0000105
106 tf_placeholders = []
107 tf_consts = []
Won Jeon6c93f412023-07-08 07:04:08 +0000108
Jeremy Johnson015c3552022-02-23 12:15:03 +0000109 for i in range(pl):
Won Jeon6c93f412023-07-08 07:04:08 +0000110 if not for_tflite_converter and i == fuzz_arg:
Jeremy Johnson015c3552022-02-23 12:15:03 +0000111 # Insert the broadcast in one dimension index
112 s_fuzz = list(shape)
113 s_fuzz[fuzz_idx] = 1
114 s_fuzz = tuple(s_fuzz)
115 i_shape = s_fuzz
116 else:
117 i_shape = shape
Won Jeon6c93f412023-07-08 07:04:08 +0000118
Jeremy Johnson015c3552022-02-23 12:15:03 +0000119 tf_placeholders.append(
120 ("placeholder_{}".format(i), TGen.getRand(i_shape, dtype, rng))
121 )
122
123 return tf_placeholders, tf_consts
124
125 @staticmethod
TatWai Chongfd629052022-07-25 04:01:58 +0000126 def tgConvCommon(op, ifm_shape, filter_shape, out_channels, dtype, rng):
Jeremy Johnson015c3552022-02-23 12:15:03 +0000127
128 # Take the shape and generate an input and filter
129 tf_placeholders = []
130 tf_consts = []
TatWai Chongfd629052022-07-25 04:01:58 +0000131 tf_placeholders.append(("placeholder_0", TGen.getRand(ifm_shape, dtype, rng)))
132 tf_consts.append(("const_0", TGen.getRand(filter_shape, dtype, rng)))
133
134 try:
135 bias = op["bias"]
136 except KeyError:
137 bias = False
138
139 if bias:
140 # bias is 1D and size == output channels
141 bias_shape = (out_channels,)
142 tf_consts.append(("const_1", TGen.getRand(bias_shape, dtype, rng)))
143
144 return tf_placeholders, tf_consts
145
146 @staticmethod
147 def tgConv2d(op, ifm_shape, dtype, rng):
Jeremy Johnson015c3552022-02-23 12:15:03 +0000148
149 # Require rank 4 shape
150 if len(ifm_shape) != 4:
151 return [], []
152
153 filter_h, filter_w = op["filter"]
154
155 # TODO: Hard-code the test by making the OFM depth 2x the IFM depth.
156 # Could randomize this in the future.
TatWai Chongfd629052022-07-25 04:01:58 +0000157 out_channels = ifm_shape[3] * 2
158 filter_shape = (filter_h, filter_w, ifm_shape[3], out_channels)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000159
TatWai Chongfd629052022-07-25 04:01:58 +0000160 return TGen.tgConvCommon(op, ifm_shape, filter_shape, out_channels, dtype, rng)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000161
162 @staticmethod
163 def tgDepthwiseConv2d(op, ifm_shape, dtype, rng):
164
Jeremy Johnson015c3552022-02-23 12:15:03 +0000165 # Require rank 4 shape
166 if len(ifm_shape) != 4:
167 return [], []
168
169 filter_h, filter_w = op["filter"]
170
TatWai Chongfd629052022-07-25 04:01:58 +0000171 # TODO: Hard-code the test by making the channel_multiplier=2.
172 # Could randomize this in the future.
Jeremy Johnson015c3552022-02-23 12:15:03 +0000173 filter_shape = (filter_h, filter_w, ifm_shape[3], 2)
TatWai Chongfd629052022-07-25 04:01:58 +0000174 out_channels = ifm_shape[3] * 2
Jeremy Johnson015c3552022-02-23 12:15:03 +0000175
TatWai Chongfd629052022-07-25 04:01:58 +0000176 return TGen.tgConvCommon(op, ifm_shape, filter_shape, out_channels, dtype, rng)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000177
178 @staticmethod
179 def tgTransposeConv2d(op, ifm_shape, dtype, rng):
180
Jeremy Johnson015c3552022-02-23 12:15:03 +0000181 # Require rank 4 shape
182 if len(ifm_shape) != 4:
183 return [], []
184
185 filter_h, filter_w = op["filter"]
186
187 # TODO: Hard-code the test by making the IFM depth 2x the OFM depth.
188 # Could randomize this in the future.
TatWai Chongfd629052022-07-25 04:01:58 +0000189 out_channels = ifm_shape[3] * 2
190 filter_shape = (filter_h, filter_w, out_channels, ifm_shape[3])
Jeremy Johnson015c3552022-02-23 12:15:03 +0000191
TatWai Chongfd629052022-07-25 04:01:58 +0000192 return TGen.tgConvCommon(op, ifm_shape, filter_shape, out_channels, dtype, rng)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000193
TatWai Chongfd629052022-07-25 04:01:58 +0000194 @staticmethod
195 def tgConv3d(op, ifm_shape, dtype, rng):
Jeremy Johnson015c3552022-02-23 12:15:03 +0000196
TatWai Chongfd629052022-07-25 04:01:58 +0000197 # Require rank 5 shape
198 if len(ifm_shape) != 5:
199 return [], []
Jeremy Johnson015c3552022-02-23 12:15:03 +0000200
TatWai Chongfd629052022-07-25 04:01:58 +0000201 filter_d, filter_h, filter_w = op["filter"]
202
203 # TODO: Hard-code the test by making the OFM depth 2x the IFM depth.
204 # Could randomize this in the future.
TatWai Chong5a76b2a2022-08-29 14:50:48 -0700205 in_channels = ifm_shape[4]
206 out_channels = in_channels * 2
207 filter_shape = (filter_d, filter_h, filter_w, in_channels, out_channels)
TatWai Chongfd629052022-07-25 04:01:58 +0000208
209 return TGen.tgConvCommon(op, ifm_shape, filter_shape, out_channels, dtype, rng)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000210
211 @staticmethod
212 def tgPooling(op, shapes, dtype, rng):
213 # Pooling does nothing special except filter out non-rank-4 tensors
214 if len(shapes) != 4:
215 return [], []
216
217 return TGen.tgBasic(op, shapes, dtype, rng)
218
219 @staticmethod
220 def tgMatmul(op, ifm_shape, dtype, rng):
221 # Take the shape and generate an input and filter
222 tf_placeholders = []
223 tf_consts = []
224
225 if len(ifm_shape) < 2:
226 return [], []
227
228 # For ifm_shape = [..., N, K]
229 # Generate rhs tensor with shape [..., K x (2 * N)]
230 tf_placeholders.append(("placeholder_0", TGen.getRand(ifm_shape, dtype, rng)))
231
232 shape_rhs = list(ifm_shape)
233 shape_rhs[-2] = ifm_shape[-1]
234 shape_rhs[-1] = ifm_shape[-2] * 2
235 tf_placeholders.append(
236 (
237 "placeholder_1",
238 TGen.getRand(shape_rhs, dtype, rng),
239 )
240 )
241
242 return tf_placeholders, tf_consts
243
244 @staticmethod
245 def tgOneHot(op, shape, dtype, rng):
246 # Build random tensor placeholder node args of a given shape
247 pl, const = op["operands"]
248
249 assert pl == 3 and const == 1
250
251 tf_placeholders = []
252 tf_consts = []
253
254 # depth
255 depth = np.int32(rng.integers(low=1, high=32, size=None))
256 tf_consts.append(("const_0", depth))
257
258 # indices
259 indices = np.int32(rng.integers(low=0, high=depth, size=shape))
260 tf_placeholders.append(("placeholder_0", indices))
261
262 # on_value
263 tf_placeholders.append(("placeholder_1", TGen.getRand(None, dtype, rng)))
264
265 # off_value
266 tf_placeholders.append(("placeholder_2", TGen.getRand(None, dtype, rng)))
267
268 return tf_placeholders, tf_consts
269
270 @staticmethod
271 def tgSelect(op, shape, dtype, rng):
272 # Build random tensor placeholder node args of a given shape
273 pl, const = op["operands"]
274 assert pl == 3 and const == 0
275
276 tf_placeholders = []
277 tf_consts = []
278
279 # selector
280 tf_placeholders.append(("placeholder_0", TGen.getRand(None, tf.bool, rng)))
281 # inputs
282 tf_placeholders.append(("placeholder_1", TGen.getRand(shape, dtype, rng)))
283 tf_placeholders.append(("placeholder_2", TGen.getRand(shape, dtype, rng)))
284
285 return tf_placeholders, tf_consts
Jerry Ge9e94af82022-10-27 09:57:00 -0700286
287 @staticmethod
288 def tgRecurrent(op, ifm_shape, dtype, rng):
289 # Require rank 3 shape for recurrent networks
290 if len(ifm_shape) != 3:
291 return [], []
292 pl, const = op["operands"]
293
294 tf_placeholders = []
295 tf_consts = []
296
297 for i in range(pl):
298 tf_placeholders.append(
299 ("placeholder_{}".format(i), TGen.getRand(ifm_shape, dtype, rng))
300 )
301
302 for i in range(const):
303 tf_consts.append(
304 ("const_{}".format(i), TGen.getRand(ifm_shape, dtype, rng))
305 )
306
307 return tf_placeholders, tf_consts
Luke Hutton261b7b62023-01-10 14:50:31 +0000308
309 @staticmethod
310 def tgRFFT2d(op, shape, dtype, rng):
311 # Require rank 3 shape
312 if len(shape) != 3:
313 return [], []
314
Luke Hutton714aa602023-02-08 19:45:26 +0000315 return TGen.tgBasic(op, shape, dtype, rng)
316
317 @staticmethod
318 def tgComplexComponents(op, shape, dtype, rng):
319 # Temporarily require up to rank 3 shape, due to
320 # slice maximum rank limitiation.
321 if len(shape) > 3:
322 return [], []
323
324 return TGen.tgBasic(op, shape, dtype, rng)
Tai Lyfe36fa92023-06-01 21:45:12 +0000325
326 @staticmethod
327 def tgBroadcastTo(op, shape, dtype, rng):
328
329 pl, const = op["operands"]
330
331 assert pl == 1
332 assert const == 1
333
334 tf_placeholders = []
335 tf_consts = []
336
337 shape_list = list(shape)
338 t_shape_list = []
339 s_shape_list = []
340 for i in range(len(shape)):
341 dim = shape_list[i]
342 if rng.integers(0, 1) == 0:
343 # append dim in s_shape_list, and 1 in t_shape_list unless it is still empty
344 s_shape_list.append(dim)
345 if len(t_shape_list) > 0:
346 t_shape_list.append(1)
347 else:
348 # append 1 in s_shape_list, and dim in t_shape_list
349 s_shape_list.append(1)
350 t_shape_list.append(dim)
351
352 # if t_shape_list is empty, then insert 1
353 if len(t_shape_list) == 0:
354 t_shape_list.append(1)
355
356 tf_placeholders.append(
357 ("placeholder_0", TGen.getRand(tuple(t_shape_list), dtype, rng))
358 )
359
360 tf_consts.append(("shape", tuple(s_shape_list)))
361
362 return tf_placeholders, tf_consts