blob: b059ef56c4bc3d70e3fc5c0919b9598f211ce428 [file] [log] [blame]
Eric Kunzee5e26762020-10-13 16:11:07 -07001#!/usr/bin/env python3
2
Kevin Cheng3a478572021-01-22 17:21:02 -08003# Copyright (c) 2020-2021, ARM Limited.
Eric Kunzee5e26762020-10-13 16:11:07 -07004#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17
18import numpy as np
19import argparse
20import sys
21import re
22import os
23import subprocess
24import shlex
25import json
26import glob
27import math
28import queue
29import threading
30import traceback
31import math
32
33from enum import IntEnum, Enum, unique
34
Kevin Cheng550ccc52021-03-03 11:21:43 -080035# Include the ../thirdparty/serialization_lib/python directory in PYTHONPATH
36parent_dir = os.path.dirname(os.path.realpath(__file__))
37sys.path.append(
38 os.path.join(parent_dir, "..", "thirdparty", "serialization_lib", "python")
39)
Eric Kunzee5e26762020-10-13 16:11:07 -070040import tosa_serializer as ts
41from tosa_serializer import *
42import tosa
43
44# Convenience variables to the flatc-generated types that should be enums, but aren't
45DType = tosa.DType.DType()
Kevin Cheng550ccc52021-03-03 11:21:43 -080046Op = tosa.Op.Op()
Eric Kunzee5e26762020-10-13 16:11:07 -070047ResizeMode = tosa.ResizeMode.ResizeMode()
48
Kevin Cheng550ccc52021-03-03 11:21:43 -080049
Eric Kunzee5e26762020-10-13 16:11:07 -070050class TosaQuantGen:
Kevin Cheng550ccc52021-03-03 11:21:43 -080051 """QuantizedInfo random generator helper functions. Specify with 'qgen': in the operator defintion"""
52
Eric Kunzee5e26762020-10-13 16:11:07 -070053 def __init__(self):
54 pass
55
56 @staticmethod
57 def needsQinfo(op, dtype):
Jared Smolens2a76ad22021-03-04 11:18:54 -080058 if dtype == DType.INT8 or dtype == DType.INT16:
Eric Kunzee5e26762020-10-13 16:11:07 -070059 return True
60 return False
61
62 @staticmethod
63 def qgUnary(testGen, op, dtype):
64 qinfo = ts.TosaSerializerQuantInfo()
65 if TosaQuantGen.needsQinfo(op, dtype):
66 qinfo.UnaryQuantInfo(testGen.randInt(), testGen.randInt())
67 else:
68 qinfo.UnaryQuantInfo(0, 0)
69 return qinfo
70
71 @staticmethod
72 def qgConv(testGen, op, dtype):
73 qinfo = ts.TosaSerializerQuantInfo()
74 if TosaQuantGen.needsQinfo(op, dtype):
75 qinfo.ConvQuantInfo(testGen.randInt(), testGen.randInt())
76 else:
77 qinfo.ConvQuantInfo(0, 0)
78 return qinfo
79
80 @staticmethod
81 def qgMatmul(testGen, op, dtype):
82 qinfo = ts.TosaSerializerQuantInfo()
83 if TosaQuantGen.needsQinfo(op, dtype):
84 qinfo.MatMulQuantInfo(testGen.randInt(), testGen.randInt())
85 else:
86 qinfo.MatMulQuantInfo(0, 0)
87 return qinfo
88
89 @staticmethod
90 def qgPad(testGen, op, dtype):
91 qinfo = ts.TosaSerializerQuantInfo()
92 if TosaQuantGen.needsQinfo(op, dtype):
93 qinfo.PadQuantInfo(testGen.randInt())
94 else:
95 qinfo.PadQuantInfo(0)
96 return qinfo
97
98 @staticmethod
99 def computeMultiplierAndShift(scaleFp, scale32):
100 # Derived from computeMultiplierAndShiftTosaScale32
101 # Provide a floating-point scaling factor and the scale32 parameter
102 # to compute the multiplier and shift
103
104 if scale32:
105 scaleBits = 31
106 else:
107 scaleBits = 15
108
109 m, shift = math.frexp(scaleFp)
110
111 if scaleFp < 0.0:
112 m = -m
113
114 multiplier = round(m * (1 << scaleBits))
Kevin Cheng550ccc52021-03-03 11:21:43 -0800115 assert multiplier <= (1 << scaleBits)
Eric Kunzee5e26762020-10-13 16:11:07 -0700116
117 if multiplier == (1 << scaleBits):
118 multiplier = multiplier // 2
119 shift = shift + 1
120
121 shift = (-shift) + scaleBits
Kevin Cheng550ccc52021-03-03 11:21:43 -0800122 # print('scalefp {} scaleBits {} m {} mult {} shift {}'.format(scaleFp, scaleBits, m, multiplier, shift))
Eric Kunzee5e26762020-10-13 16:11:07 -0700123
Kevin Cheng550ccc52021-03-03 11:21:43 -0800124 assert multiplier <= (1 << scaleBits)
125 assert shift >= 0 and shift <= 63
Eric Kunzee5e26762020-10-13 16:11:07 -0700126
127 return multiplier, shift
128
129
Kevin Cheng550ccc52021-03-03 11:21:43 -0800130class TosaTensorGen:
131 """Tensor generators create a shape list for the placeholder and const tensor
132 data operands for the operator. The actual random data is generated separately for each test."""
133
Eric Kunzee5e26762020-10-13 16:11:07 -0700134 def __init__(self):
135 pass
136
137 @staticmethod
138 def tgBasic(testGen, opName, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800139 pl, const = opName["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700140 shape = testGen.makeShape(rank)
141
142 shape_list = []
143 for i in range(pl + const):
144 shape_list.append(shape.copy())
145
146 return shape_list
147
148 @staticmethod
149 def tgNHWC(testGen, opName, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800150 pl, const = opName["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700151
Kevin Cheng550ccc52021-03-03 11:21:43 -0800152 assert rank == 4
Eric Kunzee5e26762020-10-13 16:11:07 -0700153
154 shape = testGen.makeShape(rank)
155
156 # Constrict the batch size?
157 if testGen.args.max_batch_size:
158 shape[0] = (shape[0] % testGen.args.max_batch_size) + 1
159
160 shape_list = []
161 for i in range(pl + const):
162 shape_list.append(shape.copy())
163
164 return shape_list
165
166 @staticmethod
Kevin Cheng77d0f762020-11-24 10:26:32 -0800167 def tgScatter(testGen, opName, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800168 pl, const = opName["operands"]
Kevin Cheng77d0f762020-11-24 10:26:32 -0800169
Kevin Cheng550ccc52021-03-03 11:21:43 -0800170 assert pl == 2
171 assert const == 0
172 assert rank == 3
Kevin Cheng77d0f762020-11-24 10:26:32 -0800173
174 values_in_shape = testGen.makeShape(rank)
175
176 # Constrict the batch size?
177 if testGen.args.max_batch_size:
178 values_in_shape[0] = (values_in_shape[0] % testGen.args.max_batch_size) + 1
179
Kevin Cheng550ccc52021-03-03 11:21:43 -0800180 W = testGen.randInt(
181 testGen.args.tensor_shape_range[0], testGen.args.tensor_shape_range[1]
182 )
Kevin Cheng77d0f762020-11-24 10:26:32 -0800183 input_shape = [values_in_shape[0], W, values_in_shape[2]]
184
185 shape_list = []
186 shape_list.append(values_in_shape.copy())
187 shape_list.append(input_shape.copy())
188
189 return shape_list
190
191 @staticmethod
Eric Kunzee5e26762020-10-13 16:11:07 -0700192 def tgBroadcastFuzz(testGen, op, rank):
193 shape = testGen.makeShape(rank)
194
Kevin Cheng550ccc52021-03-03 11:21:43 -0800195 pl, const = op["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700196
197 shape_list = []
198
199 # Choose one of the inputs to broadcast
200 bcast_idx = testGen.randInt(0, pl + const)
201 for i in range(pl + const):
202 shape_bcast = shape.copy()
203
204 # If the chosen input, pick a random index to broadcast
205 if i == bcast_idx:
206 fuzz_idx = testGen.randInt(0, rank)
207 shape_bcast[fuzz_idx] = 1
208
209 shape_list.append(shape_bcast)
210
211 return shape_list
212
213 @staticmethod
214 def tgConv2D(testGen, op, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800215 pl, const = op["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700216
Kevin Cheng550ccc52021-03-03 11:21:43 -0800217 assert rank == 4
Eric Kunzee5e26762020-10-13 16:11:07 -0700218
219 # IFM dimensions are NHWC
220 ifm_shape = testGen.makeShape(rank)
221
222 # Constrict the batch size?
223 if testGen.args.max_batch_size:
224 ifm_shape[0] = (ifm_shape[0] % testGen.args.max_batch_size) + 1
225
226 # Get the filter height/width from the operator parameters
Kevin Cheng550ccc52021-03-03 11:21:43 -0800227 filter_hw = op["filter"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700228
229 # Generate a random OFM depth
230 ofm_depth = testGen.makeShape(1)[0]
231
232 # The filter dimensions are OHWI
233 filter_shape = np.asarray([ofm_depth, filter_hw[0], filter_hw[1], ifm_shape[3]])
234
235 # The bias is OC
236 bias_shape = np.asarray([ofm_depth])
237
238 return [ifm_shape, filter_shape, bias_shape]
239
240 @staticmethod
241 def tgTransposeConv2D(testGen, op, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800242 pl, const = op["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700243
Kevin Cheng550ccc52021-03-03 11:21:43 -0800244 assert rank == 4
Eric Kunzee5e26762020-10-13 16:11:07 -0700245
246 # IFM dimensions are NHWC
247 ifm_shape = testGen.makeShape(rank)
248
249 # Constrict the batch size?
250 if testGen.args.max_batch_size:
251 ifm_shape[0] = (ifm_shape[0] % testGen.args.max_batch_size) + 1
252
253 # Get the filter height/width from the operator parameters
Kevin Cheng550ccc52021-03-03 11:21:43 -0800254 filter_hw = op["filter"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700255
256 # Generate a random OFM depth
257 ofm_depth = testGen.makeShape(1)[0]
258
259 # The filter dimensions are OHWI
260 filter_shape = np.asarray([ofm_depth, filter_hw[0], filter_hw[1], ifm_shape[3]])
261
262 return [ifm_shape, filter_shape]
263
264 @staticmethod
265 def tgDepthwiseConv2D(testGen, op, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800266 pl, const = op["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700267
Kevin Cheng550ccc52021-03-03 11:21:43 -0800268 assert rank == 4
269 assert pl == 1 and const == 2
Eric Kunzee5e26762020-10-13 16:11:07 -0700270
271 # IFM dimensions are NHWC
272 ifm_shape = testGen.makeShape(rank)
273
274 # Constrict the batch size?
275 if testGen.args.max_batch_size:
276 ifm_shape[0] = (ifm_shape[0] % testGen.args.max_batch_size) + 1
277
278 # Get the filter height/width from the operator parameters
279 # Filter is KH, HW, C, M
Kevin Cheng550ccc52021-03-03 11:21:43 -0800280 filter_hw = op["filter"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700281
282 # Generate a random OFM depth, but don't let it get too big because
283 # the output depth is M * C
Kevin Cheng550ccc52021-03-03 11:21:43 -0800284 filter_m = (
285 testGen.makeShape(1)[0] % (testGen.args.tensor_shape_range[1] // 4)
286 ) + 1
Eric Kunzee5e26762020-10-13 16:11:07 -0700287
288 # The filter dimensions are HWCM
289 filter_shape = np.asarray([filter_hw[0], filter_hw[1], ifm_shape[3], filter_m])
290
291 # The bias is M * C
292 bias_shape = np.asarray([ifm_shape[3] * filter_m])
293
294 return [ifm_shape, filter_shape, bias_shape]
295
296 @staticmethod
297 def tgFullyConnected(testGen, op, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800298 pl, const = op["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700299
Kevin Cheng550ccc52021-03-03 11:21:43 -0800300 assert rank == 2
301 assert pl == 2 and const == 0
Eric Kunzee5e26762020-10-13 16:11:07 -0700302
303 input_shape = testGen.makeShape(rank)
304 filter_oc = testGen.makeShape(1)[0]
305 filter_shape = np.asarray([filter_oc, input_shape[1]])
306
307 bias_shape = np.asarray([filter_oc])
308
309 return [input_shape, filter_shape, bias_shape]
310
311 @staticmethod
312 def tgMatmul(testGen, op, rank):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800313 pl, const = op["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -0700314
Kevin Cheng550ccc52021-03-03 11:21:43 -0800315 assert rank == 2
316 assert pl == 2 and const == 0
Eric Kunzee5e26762020-10-13 16:11:07 -0700317
318 a_shape = testGen.makeShape(rank)
319 b_oc = testGen.makeShape(1)[0]
320 b_shape = np.asarray([a_shape[1], b_oc])
321
322 return [a_shape, b_shape]
323
Kevin Cheng550ccc52021-03-03 11:21:43 -0800324
Eric Kunzee5e26762020-10-13 16:11:07 -0700325class TosaArgGen:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800326 """Argument generators create exhaustive or random lists of attributes for operators that take
327 attributes or other parameters. The return value is a list of (descriptive_name, [arglist])
328 tuples where the descriptive_name is appended to the test name and the arglist is expanded
329 as arguments to the operator build function."""
330
Eric Kunzee5e26762020-10-13 16:11:07 -0700331 def __init__(self):
332 pass
333
334 @staticmethod
335 def agNone(testGen, opName, shapeList, dtype):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800336 """A trivial argument generator for operators that don't take any
337 non-tensor arguments"""
338 return [("", [])]
Eric Kunzee5e26762020-10-13 16:11:07 -0700339
340 @staticmethod
341 def agAxis(testGen, opName, shapeList, dtype):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800342 """Build the axis argument for operators that take a single axis"""
Eric Kunzee5e26762020-10-13 16:11:07 -0700343 axes = []
344
345 shape = shapeList[0]
346
347 for a in range(0, len(shape)):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800348 axes.append(("axis_{}".format(a), [a]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700349 return axes
350
351 @staticmethod
352 def agConv2D(testGen, opName, shapeList, dtype):
353 arg_list = []
354
355 ifm_shape = shapeList[0]
356 filter_shape = shapeList[1]
357
358 # Must be rank 4
Kevin Cheng550ccc52021-03-03 11:21:43 -0800359 assert len(ifm_shape) == 4
360 assert len(filter_shape) == 4
Eric Kunzee5e26762020-10-13 16:11:07 -0700361
362 maxStride = testGen.args.max_conv_stride
363 maxPadding = testGen.args.max_conv_padding + 1
364 maxDilation = testGen.args.max_conv_dilation
365
366 # Strides, padding, dilations
367 for stride in range(0, maxStride ** 2):
368 for padding in range(0, (maxPadding) ** 4):
369 for dilation in range(0, maxDilation ** 2):
370
Kevin Cheng550ccc52021-03-03 11:21:43 -0800371 s = [stride // maxStride + 1, stride % maxStride + 1]
372 p = [
373 (padding // (maxPadding * 4)) % maxPadding,
374 (padding // (maxPadding * 2)) % maxPadding,
375 (padding // (maxPadding * 1)) % maxPadding,
376 padding % maxPadding,
377 ]
378 d = [dilation // maxDilation + 1, dilation % maxDilation + 1]
Eric Kunzee5e26762020-10-13 16:11:07 -0700379
380 # 4 padding parameters for regular conv2d
Kevin Cheng550ccc52021-03-03 11:21:43 -0800381 arg_list.append(
382 (
383 "st{}{}_pad{}{}{}{}_dilat{}{}".format(
384 s[0], s[1], p[0], p[1], p[2], p[3], d[0], d[1]
385 ),
386 [s, p, d],
387 )
388 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700389 return arg_list
390
391 @staticmethod
392 def agTransposeConv2D(testGen, opName, shapeList, dtype):
393 arg_list = []
394
395 ifm_shape = shapeList[0]
396 filter_shape = shapeList[1]
397
398 # Must be rank 4
Kevin Cheng550ccc52021-03-03 11:21:43 -0800399 assert len(ifm_shape) == 4
400 assert len(filter_shape) == 4
Eric Kunzee5e26762020-10-13 16:11:07 -0700401
402 maxStride = testGen.args.max_conv_stride
403 maxPadding = testGen.args.max_conv_padding + 1
404 maxDilation = testGen.args.max_conv_dilation
405
406 # Strides, padding, dilations
407 for stride in range(0, maxStride ** 2):
408 for out_padding in range(0, (maxPadding) ** 2):
409 for dilation in range(0, maxDilation ** 2):
410
Kevin Cheng550ccc52021-03-03 11:21:43 -0800411 s = [stride // maxStride + 1, stride % maxStride + 1]
412 p = [
413 (out_padding // (maxPadding * 1)) % maxPadding,
414 out_padding % maxPadding,
415 ]
416 d = [dilation // maxDilation + 1, dilation % maxDilation + 1]
Eric Kunzee5e26762020-10-13 16:11:07 -0700417
Kevin Cheng550ccc52021-03-03 11:21:43 -0800418 oh = (
419 ifm_shape[1]
420 - filter_shape[1]
421 - (filter_shape[1] - 1) * (d[0] - 1)
422 + 2 * p[0]
423 ) // s[0] + 1
Eric Kunzee5e26762020-10-13 16:11:07 -0700424
Kevin Cheng550ccc52021-03-03 11:21:43 -0800425 ow = (
426 ifm_shape[2]
427 - filter_shape[2]
428 - (filter_shape[2] - 1) * (d[1] - 1)
429 + 2 * p[1]
430 ) // s[1] + 1
Eric Kunzee5e26762020-10-13 16:11:07 -0700431
432 # Output shape
Kevin Cheng550ccc52021-03-03 11:21:43 -0800433 os = [ifm_shape[0], oh, ow, filter_shape[0]]
Eric Kunzee5e26762020-10-13 16:11:07 -0700434
Kevin Cheng550ccc52021-03-03 11:21:43 -0800435 arg_list.append(
436 (
437 "st{}{}_outpad{}{}_dilat{}{}_os{}x{}x{}x{}".format(
438 s[0],
439 s[1],
440 p[0],
441 p[1],
442 d[0],
443 d[1],
444 os[0],
445 os[1],
446 os[2],
447 os[3],
448 ),
449 [s, p, d, os],
450 )
451 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700452
453 return arg_list
454
455 @staticmethod
456 def agPad(testGen, opName, shapeList, dtype):
457 arg_list = []
458 rank = len(shapeList[0])
459
460 # Exhaustively test combinations of 0/1 padding on each side of each dimension
461 # This process might need some revision for >1 padding, but use rank**2 as a bitmask
462 # for now
463 for v in range(rank ** 2):
464
465 # Create a flat arraypadding4D
466 paddings = np.zeros((rank * 2), dtype=np.int32)
467
468 # Fill in the 1's
Kevin Cheng550ccc52021-03-03 11:21:43 -0800469 for r in range(rank * 2):
Eric Kunzee5e26762020-10-13 16:11:07 -0700470 if (v >> r) & 1:
471 paddings[r] = 1
472
473 # Reshape back to a 2D array
474 paddings = paddings.reshape((rank, 2))
475
Kevin Cheng550ccc52021-03-03 11:21:43 -0800476 arg_list.append(("pad{0:b}".format(v), [paddings]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700477
478 return arg_list
479
480 @staticmethod
481 def agPooling(testGen, opName, shapeList, dtype):
482 arg_list = []
483
484 shape = shapeList[0]
Kevin Cheng550ccc52021-03-03 11:21:43 -0800485 assert len(shape) == 4
Eric Kunzee5e26762020-10-13 16:11:07 -0700486
487 maxStride = testGen.args.max_pooling_stride
488 maxKernel = testGen.args.max_pooling_kernel
489 maxPadding = testGen.args.max_pooling_padding + 1
490
491 for kernel in range(0, maxKernel ** 2):
492 for stride in range(0, maxStride ** 2):
493 for padding in range(0, maxPadding ** 4):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800494 s = [stride // maxStride + 1, stride % maxStride + 1]
495 k = [(kernel // maxKernel) + 2, (kernel % maxKernel) + 2]
496 p = [
497 (padding // (maxPadding * 4)) % maxPadding,
498 (padding // (maxPadding * 2)) % maxPadding,
499 (padding // (maxPadding * 1)) % maxPadding,
500 padding % maxPadding,
501 ]
Eric Kunzee5e26762020-10-13 16:11:07 -0700502
Kevin Cheng550ccc52021-03-03 11:21:43 -0800503 arg_list.append(
504 (
505 "st{}{}_kern{}{}_pad{}{}{}{}".format(
506 s[0], s[1], k[0], k[1], p[0], p[1], p[2], p[3]
507 ),
508 [k, s, p],
509 )
510 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700511 return arg_list
512
513 @staticmethod
514 def agCast(testGen, opName, shapeList, inDtype):
515 arg_list = []
516
517 # Enumerate the output types here
518 if inDtype == DType.INT8:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800519 dtypeList = [DType.BOOL, DType.INT16, DType.INT32, DType.FLOAT]
Eric Kunzee5e26762020-10-13 16:11:07 -0700520 elif inDtype == DType.INT16:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800521 dtypeList = [DType.BOOL, DType.INT8, DType.INT32, DType.FLOAT]
Eric Kunzee5e26762020-10-13 16:11:07 -0700522 elif inDtype == DType.INT32:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800523 dtypeList = [DType.BOOL, DType.INT8, DType.INT16, DType.FLOAT]
Eric Kunzee5e26762020-10-13 16:11:07 -0700524 elif inDtype == DType.BOOL:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800525 dtypeList = [DType.INT8, DType.INT16, DType.INT32]
Eric Kunzee5e26762020-10-13 16:11:07 -0700526 elif inDtype == DType.FLOAT:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800527 dtypeList = [DType.INT8, DType.INT16, DType.INT32]
Eric Kunzee5e26762020-10-13 16:11:07 -0700528 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800529 raise Exception("Unexpected input dtype: {}".format(inDtype))
Eric Kunzee5e26762020-10-13 16:11:07 -0700530
531 for dtype in dtypeList:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800532 arg_list.append(("out{}".format(DTypeNames[dtype]), [dtype]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700533
534 return arg_list
535
536 @staticmethod
537 def agRescale(testGen, opName, shapeList, inDtype):
538 arg_list = []
539
540 # Enumerate the output types here
Kevin Cheng550ccc52021-03-03 11:21:43 -0800541 for dtype in [DType.INT8, DType.INT16, DType.INT32]:
542 for scale32 in [False, True]:
543 for double_round in [False, True]:
544 for per_channel in [False, True]:
Eric Kunzee5e26762020-10-13 16:11:07 -0700545
546 if inDtype == DType.INT48 and scale32:
547 # Illegal condition. Must be scale32=False
548 continue
549
Kevin Cheng550ccc52021-03-03 11:21:43 -0800550 arg_list.append(
551 (
552 "out{}_sc{}_dr{}_pc{}".format(
553 DTypeNames[dtype],
554 int(scale32),
555 int(double_round),
556 int(per_channel),
557 ),
558 [dtype, scale32, double_round, per_channel],
559 )
560 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700561
562 return arg_list
563
Kevin Chengaee1fac2020-11-11 13:54:06 -0800564 @staticmethod
565 def agMul(testGen, opName, shapeList, dtype):
566 arg_list = []
567
568 if dtype is DType.INT32:
569 for p in range(testGen.args.num_rand_permutations):
570
571 shift = testGen.randInt(0, 32)
572
Kevin Cheng550ccc52021-03-03 11:21:43 -0800573 arg_list.append(("perm{}_shift{}".format(p, shift), [shift]))
Kevin Chengaee1fac2020-11-11 13:54:06 -0800574 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800575 arg_list.append(("shift0", [0]))
Kevin Chengaee1fac2020-11-11 13:54:06 -0800576
577 return arg_list
578
579 @staticmethod
580 def agArithmeticRightShift(testGen, opName, shapeList, dtype):
581 arg_list = []
582
Kevin Cheng550ccc52021-03-03 11:21:43 -0800583 arg_list.append(("roundTrue", [True]))
584 arg_list.append(("roundFalse", [False]))
Kevin Chengaee1fac2020-11-11 13:54:06 -0800585
586 return arg_list
587
Eric Kunzee5e26762020-10-13 16:11:07 -0700588 # Helper function for reshape. Gets some factors of a larger number.
589 @staticmethod
590 def getFactors(val, start=1):
591 factors = []
592
593 for i in range(start, int(np.sqrt(val))):
594 if (val % i) == 0:
595 factors.append(i)
596
597 return factors
598
599 @staticmethod
600 def agReshape(testGen, opName, shapeList, dtype):
601 arg_list = []
602
603 origShape = shapeList[0]
604
605 totalElements = 1
606 for s in origShape:
607 totalElements *= s
608
609 # This code is NOT fast. Fortunately, the numbers are fairly small.
610 factors = TosaArgGen.getFactors(totalElements)
611
612 for p in range(testGen.args.num_rand_permutations):
613 newRank = testGen.randInt(1, 6)
614 newShape = []
Kevin Cheng550ccc52021-03-03 11:21:43 -0800615 if len(factors) < newRank:
Eric Kunzee5e26762020-10-13 16:11:07 -0700616 continue
617
618 remainingElements = totalElements
619 shuffledFactors = testGen.rng.permutation(factors)
620 for i in range(newRank):
621 # pick rank-1 factors
622 newShape.append(shuffledFactors[0])
623 remainingElements = remainingElements // shuffledFactors[0]
Kevin Cheng550ccc52021-03-03 11:21:43 -0800624 shuffledFactors = testGen.rng.permutation(
625 TosaArgGen.getFactors(remainingElements)
626 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700627 newShape.append(remainingElements)
628
629 # Toss in a -1 sometimes
630 minusOne = testGen.randInt(0, newRank * 4)
631 if minusOne < newRank:
632 newShape[minusOne] = -1
633
Kevin Cheng550ccc52021-03-03 11:21:43 -0800634 arg_list.append(("perm{}_rank{}".format(p, newRank), [newShape]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700635
636 return arg_list
637
Eric Kunzee5e26762020-10-13 16:11:07 -0700638 @staticmethod
639 def agTranspose(testGen, opName, shapeList, dtype):
640 arg_list = []
641
642 ifm_shape = shapeList[0]
643
644 perms = range(len(ifm_shape))
645 for p in range(testGen.args.num_rand_permutations):
646 perms = np.int32(testGen.rng.permutation(perms)).tolist()
647
648 # Avoid duplicates
649 found = False
650 for name, other_perm in arg_list:
651 if other_perm[0] == perms:
652 found = True
653 break
654
655 if not found:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800656 arg_list.append(("perm{}".format(p), [perms]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700657
658 return arg_list
659
660 @staticmethod
661 def agSlice(testGen, opName, shapeList, dtype):
662 arg_list = []
663
664 ifm_shape = shapeList[0]
665 rank = len(ifm_shape)
666
667 for p in range(testGen.args.num_rand_permutations):
668 begin = []
669 size = []
670
Kevin Cheng550ccc52021-03-03 11:21:43 -0800671 valid = True
Eric Kunzee5e26762020-10-13 16:11:07 -0700672
673 for i in range(rank):
674 if ifm_shape[i] > 1:
675 begin.append(testGen.randInt(0, ifm_shape[i]))
676 size.append(testGen.randInt(0, ifm_shape[i] - begin[i]))
677
678 # Invalid slice size?
679 if size[i] == 0:
680 valid = False
681 else:
682 begin.append(0)
683 size.append(1)
684
685 if valid:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800686 arg_list.append(("perm{}".format(p), [begin, size]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700687 return arg_list
688
689 @staticmethod
690 def agTile(testGen, opName, shapeList, dtype):
691 arg_list = []
692
693 ifm_shape = shapeList[0]
694 rank = len(ifm_shape)
695
696 for p in range(testGen.args.num_rand_permutations):
697
698 # Pick a few random, but small multiple values
699 # because otherwise this has a tendency to generate
700 # enormous tensors
701 multiples = []
702 for i in range(rank):
703 multiples.append(testGen.randInt(1, 4))
704
Kevin Cheng550ccc52021-03-03 11:21:43 -0800705 arg_list.append(("perm{}".format(p), [multiples]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700706
707 return arg_list
708
709 @staticmethod
710 def agResize(testGen, opName, shapeList, dtype):
711 arg_list = []
712
713 ifm_shape = shapeList[0]
714
715 for m in [ResizeMode.NEAREST, ResizeMode.BILINEAR]:
716
717 # Exclude illegal {mode, type} configurations. Pick legal output types
718 if m == ResizeMode.NEAREST and dtype == DType.INT8:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800719 outputDTypeList = [DType.INT32]
Eric Kunzee5e26762020-10-13 16:11:07 -0700720 elif m == ResizeMode.NEAREST and dtype == DType.INT16:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800721 outputDTypeList = [DType.INT16]
Eric Kunzee5e26762020-10-13 16:11:07 -0700722 elif m == ResizeMode.BILINEAR and dtype == DType.INT8:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800723 outputDTypeList = [DType.INT8]
Eric Kunzee5e26762020-10-13 16:11:07 -0700724 elif m == ResizeMode.BILINEAR and dtype == DType.INT16:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800725 outputDTypeList = [DType.INT48]
Kevin Cheng77d0f762020-11-24 10:26:32 -0800726 elif dtype == DType.FLOAT:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800727 outputDTypeList = [DType.FLOAT]
Eric Kunzee5e26762020-10-13 16:11:07 -0700728 else:
729 continue
730
731 for outputDType in outputDTypeList:
732 for perm in range(testGen.args.num_rand_permutations):
733
734 # Randomly generate legal output dimensions and shift
735 # and then compute the stride and offset based on them
Kevin Cheng550ccc52021-03-03 11:21:43 -0800736 output_dims = [testGen.randInt(1), testGen.randInt(1)]
Kevin Cheng77d0f762020-11-24 10:26:32 -0800737 in_center_h = (ifm_shape[1] - 1) / 2.0
738 in_center_w = (ifm_shape[2] - 1) / 2.0
739 out_center_h = (output_dims[0] - 1) / 2.0
740 out_center_w = (output_dims[1] - 1) / 2.0
Eric Kunzee5e26762020-10-13 16:11:07 -0700741
Kevin Cheng77d0f762020-11-24 10:26:32 -0800742 fp_stride_y = float(ifm_shape[1]) / float(output_dims[0])
743 fp_stride_x = float(ifm_shape[2]) / float(output_dims[1])
744 fp_offset_y = in_center_h - fp_stride_y * out_center_h
745 fp_offset_x = in_center_w - fp_stride_x * out_center_w
Eric Kunzee5e26762020-10-13 16:11:07 -0700746
Kevin Cheng77d0f762020-11-24 10:26:32 -0800747 if outputDType == DType.FLOAT:
748 shift = 0
749 stride = [0, 0]
750 offset = [0, 0]
Kevin Cheng550ccc52021-03-03 11:21:43 -0800751 stride_fp = [fp_stride_y, fp_stride_x]
752 offset_fp = [fp_offset_y, fp_offset_x]
753 arg_list.append(
754 (
755 "mode{}_odim{}x{}_out{}_st{:.2f}x{:.2f}_off{:.2f}x{:.2f}".format(
756 m,
757 output_dims[0],
758 output_dims[1],
759 testGen.typeStr(outputDType),
760 stride_fp[0],
761 stride_fp[1],
762 offset_fp[0],
763 offset_fp[1],
764 ),
765 [
766 m,
767 stride,
768 offset,
769 shift,
770 stride_fp,
771 offset_fp,
772 output_dims,
773 dtype,
774 outputDType,
775 ],
776 )
777 )
Kevin Cheng77d0f762020-11-24 10:26:32 -0800778 else:
779 shift = 11
780 unit = float(1 << shift)
781 stride_y = int(round(fp_stride_y * unit))
782 stride_x = int(round(fp_stride_x * unit))
783 offset_y = int(round(fp_offset_y * unit))
784 offset_x = int(round(fp_offset_x * unit))
Eric Kunzee5e26762020-10-13 16:11:07 -0700785
Kevin Cheng550ccc52021-03-03 11:21:43 -0800786 while (
787 stride_y >= 32768
788 or stride_x >= 32768
789 or offset_y >= 32768
790 or offset_x >= 32768
791 or offset_y < -32768
792 or offset_x < -32768
793 ):
Kevin Cheng77d0f762020-11-24 10:26:32 -0800794 shift = shift - 1
795 unit = float(1 << shift)
796 stride_y = int(round(fp_stride_y * unit))
797 stride_x = int(round(fp_stride_x * unit))
798 offset_y = int(round(fp_offset_y * unit))
799 offset_x = int(round(fp_offset_x * unit))
Eric Kunzee5e26762020-10-13 16:11:07 -0700800
Kevin Cheng550ccc52021-03-03 11:21:43 -0800801 stride = [stride_y, stride_x]
802 offset = [offset_y, offset_x]
Kevin Cheng77d0f762020-11-24 10:26:32 -0800803
804 stride_fp = [0.0, 0.0]
805 offset_fp = [0.0, 0.0]
806
Kevin Cheng550ccc52021-03-03 11:21:43 -0800807 arg_list.append(
808 (
809 "mode{}_shift{}_odim{}x{}_out{}_st{}x{}_off{}x{}".format(
810 m,
811 shift,
812 output_dims[0],
813 output_dims[1],
814 testGen.typeStr(outputDType),
815 stride[0],
816 stride[1],
817 offset[0],
818 offset[1],
819 ),
820 [
821 m,
822 stride,
823 offset,
824 shift,
825 stride_fp,
826 offset_fp,
827 output_dims,
828 dtype,
829 outputDType,
830 ],
831 )
832 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700833
834 return arg_list
835
836 def agCondIf(testGen, opName, shapeList, dtype):
837 # CondIf generates the condition values here.
838 # Convert to tensors in the build function, along with the
839 # then and else blocks
840 arg_list = []
841
842 for c in [False, True]:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800843 arg_list.append(("cond{}".format(int(c)), [c]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700844
845 return arg_list
846
847 def agWhileLoop(testGen, opName, shapeList, dtype):
848 # While loop: 0 iterations, 1, more than 1
849 arg_list = []
850
851 for iter in [0, 1, 4]:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800852 arg_list.append(("iter{}".format(iter), [iter]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700853
854 return arg_list
855
Kevin Cheng550ccc52021-03-03 11:21:43 -0800856
Eric Kunzee5e26762020-10-13 16:11:07 -0700857class TosaTestGen:
858 def __init__(self, args):
859 self.args = args
860 self.basePath = args.output_dir
861 self.random_seed = args.random_seed
862 self.ser = None
863 self.rng = np.random.default_rng(self.random_seed)
864 self.createDynamicOpLists()
865 self.initOpListDefaults()
866 self.quantGen = TosaQuantGen()
867 # Force makeShape to do a specific starting shape
868 self.targetted_shape = None
869
870 def createSerializer(self, opName, testPath):
871 self.testPath = os.path.join(opName, testPath)
872
873 fullPath = os.path.join(self.basePath, self.testPath)
874 os.makedirs(fullPath, exist_ok=True)
875 self.ser = ts.TosaSerializer(fullPath)
876
877 def getSerializer(self):
878 return self.ser
879
880 def serialize(self, testName):
Kevin Cheng550ccc52021-03-03 11:21:43 -0800881 with open(
882 os.path.join(self.basePath, self.testPath, "{}.tosa".format(testName)), "wb"
883 ) as fd:
Eric Kunzee5e26762020-10-13 16:11:07 -0700884 fd.write(self.ser.serialize())
885
Kevin Cheng550ccc52021-03-03 11:21:43 -0800886 with open(os.path.join(self.basePath, self.testPath, "desc.json"), "w") as fd:
887 fd.write(self.ser.writeJson("{}.tosa".format(testName)))
Eric Kunzee5e26762020-10-13 16:11:07 -0700888
889 def getRandTensor(self, shape, dtype):
890 RAND_SHIFT_FACTOR = 0.5
891 RAND_SCALE_FACTOR = 4.0
892
893 if dtype == DType.BOOL:
894 np_dt = np.bool
895 return np.bool_(self.rng.choice(a=[False, True], size=shape))
Eric Kunzee5e26762020-10-13 16:11:07 -0700896 elif dtype == DType.INT4:
897 return np.int32(self.rng.integers(low=-7, high=8, size=shape))
898 elif dtype == DType.INT8:
899 return np.int32(self.rng.integers(low=-127, high=128, size=shape))
900 elif dtype == DType.INT16:
901 return np.int32(self.rng.integers(low=-32768, high=32768, size=shape))
902 elif dtype == DType.INT32:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800903 return np.int32(
904 self.rng.integers(low=-(1 << 31), high=(1 << 31), size=shape)
905 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700906 elif dtype == DType.INT48:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800907 return np.int64(
908 self.rng.integers(low=-(1 << 47), high=(1 << 47), size=shape)
909 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700910 elif dtype == DType.FLOAT:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800911 return np.float32(
912 self.rng.random(size=shape) - RAND_SHIFT_FACTOR * RAND_SCALE_FACTOR
913 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700914 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800915 raise Exception("Unrecognized Dtype: {}".format(dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -0700916
917 def buildPlaceholderTensors(self, shape_list, dtype):
918 placeholders = []
919
920 for shape in shape_list:
921 arr = self.getRandTensor(shape, dtype)
Kevin Cheng550ccc52021-03-03 11:21:43 -0800922 placeholders.append(self.ser.addPlaceholder(shape, dtype, arr))
Eric Kunzee5e26762020-10-13 16:11:07 -0700923
924 return placeholders
925
926 def buildConstTensors(self, shape_list, dtype):
927 consts = []
928
929 for shape in shape_list:
930 arr = self.getRandTensor(shape, dtype)
Kevin Cheng550ccc52021-03-03 11:21:43 -0800931 consts.append(self.ser.addConst(shape, dtype, arr))
Eric Kunzee5e26762020-10-13 16:11:07 -0700932
933 return consts
934
935 def makeShape(self, rank):
936 if self.targetted_shape:
937 return np.int32(self.targetted_shape)
Kevin Cheng550ccc52021-03-03 11:21:43 -0800938 return np.int32(
939 self.rng.integers(
940 low=self.args.tensor_shape_range[0],
941 high=self.args.tensor_shape_range[1],
942 size=rank,
943 )
944 )
Eric Kunzee5e26762020-10-13 16:11:07 -0700945
946 def setTargetShape(self, shape):
947 self.targetted_shape = shape
948
949 def randInt(self, low=0, high=256):
950 return np.int32(self.rng.integers(low=low, high=high, size=1))[0]
951
952 def getRandNumberDType(self, dtype):
953 if dtype == DType.FLOAT:
954 return self.rng.random()
955 elif dtype == DType.BOOL:
956 return self.rng.choice([False, True])
957 elif dtype == DType.INT4:
958 low, high = (-7, 8)
Eric Kunzee5e26762020-10-13 16:11:07 -0700959 elif dtype == DType.INT8:
960 low, high = (-127, 128)
961 elif dtype == DType.INT16:
962 low, high = (-32768, 32768)
963 elif dtype == DType.INT32:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800964 low, high = (-(1 << 31), (1 << 31))
Eric Kunzee5e26762020-10-13 16:11:07 -0700965 elif dtype == DType.INT48:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800966 low, high = (-(1 << 47), (1 << 47))
Eric Kunzee5e26762020-10-13 16:11:07 -0700967 # Special size
968 return np.int64(self.rng.integers(low, high, size=1))[0]
969 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800970 raise Exception("Unknown dtype: {}".format(dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -0700971
972 return np.int32(self.rng.integers(low, high, size=1))[0]
973
974 def shapeStr(self, shape):
975
976 sStr = []
977 # Convert to strings
978 for i in shape:
979 sStr.append(str(i))
980
Kevin Cheng550ccc52021-03-03 11:21:43 -0800981 return "x".join(sStr)
Eric Kunzee5e26762020-10-13 16:11:07 -0700982
983 def typeStr(self, t):
984 if t == DType.BOOL:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800985 return "b"
Eric Kunzee5e26762020-10-13 16:11:07 -0700986 elif t == DType.INT4:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800987 return "i4"
Eric Kunzee5e26762020-10-13 16:11:07 -0700988 elif t == DType.INT8:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800989 return "i8"
Kevin Cheng3a478572021-01-22 17:21:02 -0800990 elif t == DType.UINT8:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800991 return "u8"
Eric Kunzee5e26762020-10-13 16:11:07 -0700992 elif t == DType.INT16:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800993 return "i16"
Eric Kunzee5e26762020-10-13 16:11:07 -0700994 elif t == DType.INT32:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800995 return "i32"
Eric Kunzee5e26762020-10-13 16:11:07 -0700996 elif t == DType.INT48:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800997 return "i48"
Eric Kunzee5e26762020-10-13 16:11:07 -0700998 elif t == DType.FLOAT:
Kevin Cheng550ccc52021-03-03 11:21:43 -0800999 return "float"
Eric Kunzee5e26762020-10-13 16:11:07 -07001000 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001001 raise Exception("Unknown dtype, cannot convert to string: {}".format(t))
Eric Kunzee5e26762020-10-13 16:11:07 -07001002
1003 def typeWidth(self, t):
Kevin Cheng550ccc52021-03-03 11:21:43 -08001004 """ Get the datatype width for integer types"""
Kevin Cheng3a478572021-01-22 17:21:02 -08001005 if t == DType.INT4:
Eric Kunzee5e26762020-10-13 16:11:07 -07001006 return 4
1007 elif t == DType.INT8:
1008 return 8
Kevin Cheng3a478572021-01-22 17:21:02 -08001009 elif t == DType.UINT8:
1010 return 8
Eric Kunzee5e26762020-10-13 16:11:07 -07001011 elif t == DType.INT16:
1012 return 16
1013 elif t == DType.INT32:
1014 return 32
1015 elif t == DType.INT48:
1016 return 48
1017 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001018 raise Exception("Unknown dtype, cannot convert to string: {}".format(t))
Eric Kunzee5e26762020-10-13 16:11:07 -07001019
1020 # Argument generators
1021 # Returns a list of tuples (stringDescriptor, [build_fcn_arg_list])
1022 # Where the string descriptor is used to generate the test name and
1023 # The build_fcn_arg_list is expanded and passed to the operator test
1024 # build function
1025
Kevin Cheng550ccc52021-03-03 11:21:43 -08001026 def build_unary(self, op, a, qinfo=None):
Eric Kunzee5e26762020-10-13 16:11:07 -07001027 result_tens = OutputShaper.unaryOp(self.ser, a)
1028 self.ser.addOperator(op, [a.name], [result_tens.name], None, qinfo)
1029 return result_tens
1030
1031 def build_binary_broadcast(self, op, a, b):
1032 result_tens = OutputShaper.binaryBroadcastOp(self.ser, a, b)
1033 self.ser.addOperator(op, [a.name, b.name], [result_tens.name])
1034 return result_tens
1035
1036 def build_binary_nonbroadcast(self, op, a, b):
1037 result_tens = OutputShaper.binaryNonBroadcastOp(self.ser, a, b)
1038 self.ser.addOperator(op, [a.name, b.name], [result_tens.name])
1039 return result_tens
1040
Kevin Chengaee1fac2020-11-11 13:54:06 -08001041 def build_arithmetic_right_shift(self, op, a, b, round):
1042 result_tens = OutputShaper.binaryBroadcastOp(self.ser, a, b)
1043
1044 attr = ts.TosaSerializerAttribute()
1045 attr.ArithmeticRightShiftAttribute(round)
1046
1047 self.ser.addOperator(op, [a.name, b.name], [result_tens.name], attr)
1048 return result_tens
1049
1050 def build_mul(self, op, a, b, shift):
Eric Kunzee5e26762020-10-13 16:11:07 -07001051 result_tens = OutputShaper.binaryBroadcastOp(self.ser, a, b)
1052
1053 # Special for multiply:
1054 # Force the result to INT32 for INT types
1055 if a.dtype != DType.FLOAT:
1056 result_tens.setDtype(DType.INT32)
1057
Kevin Chengaee1fac2020-11-11 13:54:06 -08001058 attr = ts.TosaSerializerAttribute()
1059 attr.MulAttribute(shift)
1060
1061 self.ser.addOperator(op, [a.name, b.name], [result_tens.name], attr)
Eric Kunzee5e26762020-10-13 16:11:07 -07001062 return result_tens
1063
1064 def build_table(self, op, a):
1065 # Constant size, random values
1066 table_arr = self.getRandTensor([513], DType.INT16)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001067 table_tens = self.ser.addConst(table_arr.shape, DType.INT16, table_arr)
Eric Kunzee5e26762020-10-13 16:11:07 -07001068
1069 result_tens = OutputShaper.tableOp(self.ser, a, table_tens)
1070 self.ser.addOperator(op, [a.name, table_tens.name], [result_tens.name], None)
1071
1072 return result_tens
1073
1074 def build_select(self, op, cond, a, b):
1075
1076 # Replace the cond tensor with a boolean tensor since it probably
1077 # has the wrong dtype
1078 t = self.buildPlaceholderTensors([cond.shape], DType.BOOL)
1079 cond = t[0]
1080
1081 result_tens = OutputShaper.selectOp(self.ser, cond, a, b)
1082 self.ser.addOperator(op, [cond.name, a.name, b.name], [result_tens.name])
1083
1084 return result_tens
1085
1086 def build_comparison(self, op, a, b):
1087 result_tens = OutputShaper.binaryComparisonOp(self.ser, a, b)
1088 self.ser.addOperator(op, [a.name, b.name], [result_tens.name])
1089 return result_tens
1090
1091 def build_argmax(self, op, a, axis):
1092 result_tens = OutputShaper.argmaxOp(self.ser, a, axis)
1093
1094 attr = ts.TosaSerializerAttribute()
1095 attr.AxisAttribute(axis)
1096
1097 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1098 return result_tens
1099
Kevin Cheng550ccc52021-03-03 11:21:43 -08001100 def build_pool2d(self, op, input, kernel, stride, pad, qinfo=None):
Eric Kunzee5e26762020-10-13 16:11:07 -07001101 result_tens = OutputShaper.pool2dOp(self.ser, input, kernel, stride, pad)
1102
1103 attr = ts.TosaSerializerAttribute()
1104 attr.Pool2dAttribute(kernel, stride, pad)
Eric Kunzee5e26762020-10-13 16:11:07 -07001105
1106 self.ser.addOperator(op, [input.name], [result_tens.name], attr, qinfo)
1107 return result_tens
1108
1109 def build_conv2d(self, op, ifm, filter, bias, strides, padding, dilations, qinfo):
Kevin Cheng550ccc52021-03-03 11:21:43 -08001110 assert len(padding) == 4
1111 result_tens = OutputShaper.conv2dOp(
1112 self.ser, ifm, filter, strides, padding, dilations
1113 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001114
1115 attr = ts.TosaSerializerAttribute()
1116 attr.Conv2dAttribute(padding, strides, dilations)
1117
Kevin Cheng550ccc52021-03-03 11:21:43 -08001118 self.ser.addOperator(
1119 op, [ifm.name, filter.name, bias.name], [result_tens.name], attr, qinfo
1120 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001121 return result_tens
1122
Kevin Cheng550ccc52021-03-03 11:21:43 -08001123 def build_transpose_conv2d(
1124 self, op, ifm, filter, stride, outpad, dilation, output_shape, qinfo
1125 ):
1126 assert len(outpad) == 2
Eric Kunzee5e26762020-10-13 16:11:07 -07001127 result_tens = OutputShaper.transposeConv2DOp(self.ser, ifm, output_shape)
1128
1129 attr = ts.TosaSerializerAttribute()
1130 attr.TransposeConv2DAttribute(outpad, stride, dilation, output_shape)
1131
Eric Kunzee5e26762020-10-13 16:11:07 -07001132 # Create bias here since the acc_t depends on (but isn't the same as) the input dtype
1133 # The bias is OC
Kevin Cheng3a478572021-01-22 17:21:02 -08001134 if ifm.dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07001135 bias_type = DType.INT32
1136 elif ifm.dtype == DType.INT16:
1137 bias_type = DType.INT48
1138 elif ifm.dtype == DType.FLOAT:
1139 bias_type = DType.FLOAT
1140 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001141 raise Exception(
1142 "Unsupported dtype for transpose_conv2d: {}".format(ifm.dtype)
1143 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001144
1145 bias_arr = self.getRandTensor([filter.shape[0]], bias_type)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001146 bias_tens = self.ser.addConst([filter.shape[0]], bias_type, bias_arr)
Eric Kunzee5e26762020-10-13 16:11:07 -07001147
Kevin Cheng550ccc52021-03-03 11:21:43 -08001148 self.ser.addOperator(
1149 op, [ifm.name, filter.name, bias_tens.name], [result_tens.name], attr, qinfo
1150 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001151 return result_tens
1152
Kevin Cheng550ccc52021-03-03 11:21:43 -08001153 def build_depthwise_conv2d(
1154 self, op, ifm, filter, bias, strides, padding, dilations, qinfo
1155 ):
1156 result_tens = OutputShaper.depthwiseConv2dOp(
1157 self.ser, ifm, filter, strides, padding, dilations
1158 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001159
1160 attr = ts.TosaSerializerAttribute()
1161 attr.Conv2dAttribute(padding, strides, dilations)
1162
Kevin Cheng550ccc52021-03-03 11:21:43 -08001163 self.ser.addOperator(
1164 op, [ifm.name, filter.name, bias.name], [result_tens.name], attr, qinfo
1165 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001166 return result_tens
1167
1168 def build_fully_connected(self, op, ifm, filter, bias, qinfo):
1169 result_tens = OutputShaper.fullyConnectedOp(self.ser, ifm, filter)
1170
Kevin Cheng550ccc52021-03-03 11:21:43 -08001171 self.ser.addOperator(
1172 op, [ifm.name, filter.name, bias.name], [result_tens.name], None, qinfo
1173 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001174 return result_tens
1175
1176 def build_matmul(self, op, a, b, qinfo):
1177 result_tens = OutputShaper.matmulOp(self.ser, a, b)
1178 self.ser.addOperator(op, [a.name, b.name], [result_tens.name], None, qinfo)
1179 return result_tens
1180
1181 def build_reduce(self, op, a, axis):
1182 result_tens = OutputShaper.reduceOp(self.ser, a, axis)
1183
1184 attr = ts.TosaSerializerAttribute()
1185 attr.AxisAttribute(axis)
1186
1187 self.ser.addOperator(op, [a.name], result_tens.name, attr)
1188 return result_tens
1189
1190 def build_clamp(self, op, a):
1191 result_tens = OutputShaper.unaryOp(self.ser, a)
1192
1193 attr = ts.TosaSerializerAttribute()
1194
1195 # Get two random ints
1196 v = [self.randInt(), self.randInt()]
1197
1198 if a.dtype == DType.FLOAT:
1199 attr.ClampAttribute(0, 0, min(v), max(v))
1200 else:
1201 attr.ClampAttribute(min(v), max(v), 0, 0)
1202
1203 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1204 return result_tens
1205
1206 def build_leaky_relu(self, op, a):
1207 result_tens = OutputShaper.unaryOp(self.ser, a)
1208 attr = ts.TosaSerializerAttribute()
1209
1210 attr.LeakyReluAttribute(self.getRandNumberDType(DType.FLOAT))
1211
1212 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1213 return result_tens
1214
1215 # Needs an additional type/input
1216 def build_prelu(self, op, a):
1217 result_tens = OutputShaper.unaryOp(self.ser, a)
1218
1219 self.ser.addOperator(op, [a.name], [result_tens.name])
1220 return result_tens
1221
1222 def build_relun(self, op, a):
1223 result_tens = OutputShaper.unaryOp(self.ser, a)
1224
1225 attr = ts.TosaSerializerAttribute()
1226
1227 if a.dtype == DType.FLOAT:
1228 attr.ReluNAttribute(0, self.getRandNumberDType(a.dtype))
1229 else:
1230 attr.ReluNAttribute(self.getRandNumberDType(a.dtype), 0)
1231
1232 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1233 return result_tens
1234
1235 def build_sigmoid(self, op, a):
1236 result_tens = OutputShaper.unaryOp(self.ser, a)
1237 self.ser.addOperator(op, [a.name], [result_tens.name])
1238 return result_tens
1239
1240 def build_tanh(self, op, a):
1241 result_tens = OutputShaper.unaryOp(self.ser, a)
1242 self.ser.addOperator(op, [a.name], [result_tens.name])
1243 return result_tens
1244
1245 def build_concat(self, op, a, b, axis):
1246 result_tens = OutputShaper.concatOp(self.ser, a, b, axis)
1247
1248 attr = ts.TosaSerializerAttribute()
1249 attr.AxisAttribute(axis)
1250
1251 self.ser.addOperator(op, [a.name, b.name], [result_tens.name], attr)
1252
1253 def build_pad(self, op, a, padding, qinfo):
1254 result_tens = OutputShaper.padOp(self.ser, a, padding)
1255
1256 # Need to turn the padding array into a TOSA tensor here.
1257 # This is one of the few tensor operands that does not get
1258 # randomly generated
Kevin Cheng550ccc52021-03-03 11:21:43 -08001259 padding_tens = self.ser.addConst(padding.shape, DType.INT32, padding)
Eric Kunzee5e26762020-10-13 16:11:07 -07001260
Kevin Cheng550ccc52021-03-03 11:21:43 -08001261 self.ser.addOperator(
1262 op, [a.name, padding_tens.name], [result_tens.name], None, qinfo
1263 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001264
1265 def build_reshape(self, op, a, newShape):
1266 result_tens = OutputShaper.reshapeOp(self.ser, a, newShape)
1267
1268 attr = ts.TosaSerializerAttribute()
1269 attr.ReshapeAttribute(newShape)
1270
1271 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1272 return result_tens
1273
1274 def build_reverse(self, op, a, axis):
1275 result_tens = OutputShaper.unaryOp(self.ser, a)
1276
1277 attr = ts.TosaSerializerAttribute()
1278 attr.AxisAttribute(axis)
1279
1280 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1281 return result_tens
1282
1283 def build_transpose(self, op, a, perms):
1284 result_tens = OutputShaper.transposeOp(self.ser, a, perms)
1285
Kevin Cheng550ccc52021-03-03 11:21:43 -08001286 perms_tens = self.ser.addConst([len(perms)], DType.INT32, np.int32(perms))
Eric Kunzee5e26762020-10-13 16:11:07 -07001287
1288 self.ser.addOperator(op, [a.name, perms_tens.name], [result_tens.name])
1289 return result_tens
1290
1291 def build_slice(self, op, a, begin, size):
1292 result_tens = OutputShaper.sliceOp(self.ser, a, begin, size)
1293
1294 attr = ts.TosaSerializerAttribute()
1295 attr.SliceAttribute(begin, size)
1296
1297 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1298 return result_tens
1299
1300 def build_tile(self, op, a, multiples):
1301 result_tens = OutputShaper.tileOp(self.ser, a, multiples)
1302
1303 attr = ts.TosaSerializerAttribute()
1304 attr.TileAttribute(multiples)
1305
1306 self.ser.addOperator(op, [a.name], [result_tens.name], attr)
1307 return result_tens
1308
Kevin Cheng77d0f762020-11-24 10:26:32 -08001309 def build_gather(self, op, values):
Eric Kunzee5e26762020-10-13 16:11:07 -07001310
1311 # Create a new indicies tensor
1312 # here with data that doesn't exceed the dimensions of the values tensor
1313
Kevin Cheng550ccc52021-03-03 11:21:43 -08001314 K = values.shape[1] # K
1315 W = self.randInt(
1316 self.args.tensor_shape_range[0], self.args.tensor_shape_range[1]
1317 ) # W
1318 indicies_arr = np.int32(
1319 self.rng.integers(low=0, high=K, size=[values.shape[0], W])
1320 ) # (N, W)
1321 indicies = self.ser.addConst(indicies_arr.shape, DType.INT32, indicies_arr)
Eric Kunzee5e26762020-10-13 16:11:07 -07001322
Kevin Cheng77d0f762020-11-24 10:26:32 -08001323 result_tens = OutputShaper.gatherOp(self.ser, values, indicies)
Eric Kunzee5e26762020-10-13 16:11:07 -07001324
Kevin Cheng77d0f762020-11-24 10:26:32 -08001325 self.ser.addOperator(op, [values.name, indicies.name], [result_tens.name])
Eric Kunzee5e26762020-10-13 16:11:07 -07001326
1327 return result_tens
1328
Kevin Cheng77d0f762020-11-24 10:26:32 -08001329 def build_scatter(self, op, values_in, input):
1330
1331 # Create a new indicies tensor
1332 # here with data that doesn't exceed the dimensions of the values_in tensor
1333
Kevin Cheng550ccc52021-03-03 11:21:43 -08001334 K = values_in.shape[1] # K
1335 W = input.shape[1] # W
1336 indicies_arr = np.int32(
1337 self.rng.integers(low=0, high=K, size=[values_in.shape[0], W])
1338 ) # (N, W)
1339 indicies = self.ser.addConst(indicies_arr.shape, DType.INT32, indicies_arr)
Kevin Cheng77d0f762020-11-24 10:26:32 -08001340
1341 result_tens = OutputShaper.scatterOp(self.ser, values_in, indicies, input)
1342
Kevin Cheng550ccc52021-03-03 11:21:43 -08001343 self.ser.addOperator(
1344 op, [values_in.name, indicies.name, input.name], [result_tens.name]
1345 )
Kevin Cheng77d0f762020-11-24 10:26:32 -08001346
1347 return result_tens
1348
Kevin Cheng550ccc52021-03-03 11:21:43 -08001349 def build_resize(
1350 self,
1351 op,
1352 input,
1353 mode,
1354 stride,
1355 offset,
1356 shift,
1357 stride_fp,
1358 offset_fp,
1359 output_dims,
1360 input_dtype,
1361 output_dtype,
1362 ):
1363 result_tens = OutputShaper.resizeOp(
1364 self.ser,
1365 input,
1366 mode,
1367 stride,
1368 offset,
1369 shift,
1370 stride_fp,
1371 offset_fp,
1372 output_dims,
1373 input_dtype,
1374 output_dtype,
1375 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001376
1377 attr = ts.TosaSerializerAttribute()
Kevin Cheng77d0f762020-11-24 10:26:32 -08001378
Kevin Cheng550ccc52021-03-03 11:21:43 -08001379 attr.ResizeAttribute(
1380 output_dims, stride, offset, shift, stride_fp, offset_fp, mode
1381 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001382
1383 self.ser.addOperator(op, [input.name], [result_tens.name], attr)
1384 return result_tens
1385
1386 def build_identityn(self, op, val, val2):
1387
Kevin Cheng550ccc52021-03-03 11:21:43 -08001388 result_tens = OutputShaper.unaryOp(self.ser, val)
Eric Kunzee5e26762020-10-13 16:11:07 -07001389 result_tens2 = OutputShaper.unaryOp(self.ser, val2)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001390 self.ser.addOperator(
1391 op, [val.name, val2.name], [result_tens.name, result_tens2.name]
1392 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001393 return result_tens
1394
1395 def build_placeholder(self, op, val):
1396 # Add an identity op to avoid warning in the reference model
1397 return self.build_unary(Op.IDENTITY, val)
1398
1399 # Type Conversion
1400 def build_cast(self, op, val, out_dtype):
1401 result_tens = OutputShaper.typeConversionOp(self.ser, val, out_dtype)
1402 self.ser.addOperator(op, [val.name], [result_tens.name])
1403 return result_tens
1404
1405 def build_rescale(self, op, val, out_dtype, scale32, double_round, per_channel):
1406 result_tens = OutputShaper.typeConversionOp(self.ser, val, out_dtype)
1407
1408 if per_channel:
1409 nc = val.shape[-1]
1410 else:
1411 nc = 1
1412
1413 in_type_width = self.typeWidth(val.dtype)
1414 out_type_width = self.typeWidth(out_dtype)
1415
Kevin Cheng3a478572021-01-22 17:21:02 -08001416 if val.dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07001417 input_zp = self.randInt()
1418 in_type_width = in_type_width + 1
1419 else:
1420 input_zp = 0
1421
Kevin Cheng3a478572021-01-22 17:21:02 -08001422 if out_dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07001423 output_zp = self.randInt()
1424 out_type_width = out_type_width + 1
1425 else:
1426 output_zp = 0
1427
1428 # Calculate scale based on:
1429 # scale = a *(2^output_width)/(2^input_width))
1430
1431 a = np.float32(self.rng.random(size=[nc]))
1432 scale_arr = a * np.float32((1 << out_type_width) / (1 << in_type_width))
1433
1434 if scale32:
1435 pass
1436 # Cap the scaling at 2^15 - 1 for scale16
1437 scale_arr = np.clip(scale_arr, 1.0 / (1 << 31), (1 << 31) - 1)
1438 else:
1439 # Cap the scaling at 2^15 - 1 for scale16
1440 scale_arr = np.clip(scale_arr, 1.0 / (1 << 31), 32767.0)
1441
Kevin Cheng550ccc52021-03-03 11:21:43 -08001442 # print('{} {} -> {}'.format(out_type_width, in_type_width, scale_arr))
Eric Kunzee5e26762020-10-13 16:11:07 -07001443
1444 multiplier_arr = np.int32(np.zeros(shape=[nc]))
1445 shift_arr = np.int32(np.zeros(shape=[nc]))
1446
1447 for i in range(nc):
Kevin Cheng550ccc52021-03-03 11:21:43 -08001448 multiplier_arr[i], shift_arr[i] = TosaQuantGen.computeMultiplierAndShift(
1449 scale_arr[i], scale32
1450 )
Kevin Chengaee1fac2020-11-11 13:54:06 -08001451 if shift_arr[i] < 2 or shift_arr[i] > 62:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001452 self.ser.setExpectedFailure(True, "OpRescale: invalid shift value")
Eric Kunzee5e26762020-10-13 16:11:07 -07001453
Kevin Cheng550ccc52021-03-03 11:21:43 -08001454 # print('multiplier {} shift {} inzp {} outzp {}'.format(multiplier_arr, shift_arr, input_zp, output_zp))
Eric Kunzee5e26762020-10-13 16:11:07 -07001455
1456 attr = ts.TosaSerializerAttribute()
Kevin Cheng550ccc52021-03-03 11:21:43 -08001457 attr.RescaleAttribute(
1458 input_zp,
1459 output_zp,
1460 multiplier_arr,
1461 shift_arr,
1462 scale32,
1463 double_round,
1464 per_channel,
1465 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001466
1467 self.ser.addOperator(op, [val.name], [result_tens.name], attr)
1468 return result_tens
1469
1470 def build_cond_if_const(self, op, then_tens, else_tens, cond):
1471 # For cond_if with constants, we're supplied with then/else tensors that we ignore
1472 # (except for the generated shap) and the condition. Build Then/Else blocks
1473 # and fill them with const nodes for the body.
1474
1475 # Condition tensor
Kevin Cheng550ccc52021-03-03 11:21:43 -08001476 cond_tens = self.ser.addConst([], DType.BOOL, [cond])
Eric Kunzee5e26762020-10-13 16:11:07 -07001477
1478 # Make then/else tensors
1479 out_shape = then_tens.shape
1480 then_arr = np.int32(self.rng.integers(0, 255, size=out_shape))
1481 else_arr = np.int32(self.rng.integers(0, 255, size=out_shape))
1482
1483 # And the result tensor based on any of the outputs
Kevin Cheng550ccc52021-03-03 11:21:43 -08001484 result_tens = self.ser.addOutput(out_shape, DType.INT32)
Eric Kunzee5e26762020-10-13 16:11:07 -07001485
1486 # Create the attribute with the names of the then/else blocks
Kevin Cheng550ccc52021-03-03 11:21:43 -08001487 then_block = "THEN_BLOCK"
1488 else_block = "ELSE_BLOCK"
Eric Kunzee5e26762020-10-13 16:11:07 -07001489 attr = ts.TosaSerializerAttribute()
1490 attr.CondIfAttribute(then_block, else_block)
1491
1492 # Finally, build the op and the two blocks
1493 self.ser.addOperator(op, [cond_tens.name], [result_tens.name], attr)
1494
1495 self.ser.startBasicBlock(then_block)
1496 # Build the actual then/else tensors inside their blocks
Kevin Cheng550ccc52021-03-03 11:21:43 -08001497 then_tens = self.ser.addConst(out_shape, DType.INT32, then_arr)
Eric Kunzee5e26762020-10-13 16:11:07 -07001498 self.ser.addOutputTensor(then_tens)
1499
1500 self.ser.startBasicBlock(else_block)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001501 else_tens = self.ser.addConst(out_shape, DType.INT32, else_arr)
Eric Kunzee5e26762020-10-13 16:11:07 -07001502 self.ser.addOutputTensor(else_tens)
1503
1504 return result_tens
1505
1506 def build_cond_if_binary(self, op, a, b, cond):
1507 # For cond_if with a binary op in the then/else blocks, take a and b and
1508 # alternately add or subtract them based on the condition
1509
1510 # Condition tensor
Kevin Cheng550ccc52021-03-03 11:21:43 -08001511 cond_tens = self.ser.addConst([], DType.BOOL, [cond])
Eric Kunzee5e26762020-10-13 16:11:07 -07001512
Kevin Cheng550ccc52021-03-03 11:21:43 -08001513 result_tens = self.ser.addOutput(a.shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07001514 self.ser.currBasicBlock.addOutput(result_tens.name)
1515
1516 # Create the attribute with the names of the then/else blocks
Kevin Cheng550ccc52021-03-03 11:21:43 -08001517 then_block = "THEN_BLOCK"
1518 else_block = "ELSE_BLOCK"
Eric Kunzee5e26762020-10-13 16:11:07 -07001519 attr = ts.TosaSerializerAttribute()
1520 attr.CondIfAttribute(then_block, else_block)
1521
1522 # Finally, build the op and the two blocks
Kevin Cheng550ccc52021-03-03 11:21:43 -08001523 self.ser.addOperator(
1524 op, [cond_tens.name, a.name, b.name], [result_tens.name], attr
1525 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001526
1527 self.ser.startBasicBlock(then_block)
1528 self.ser.addInputTensor(a)
1529 self.ser.addInputTensor(b)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001530 then_tens = self.ser.addOutput(a.shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07001531 self.ser.addOperator(Op.ADD, [a.name, b.name], [then_tens.name])
1532
1533 self.ser.startBasicBlock(else_block)
1534 self.ser.addInputTensor(a)
1535 self.ser.addInputTensor(b)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001536 else_tens = self.ser.addOutput(a.shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07001537 self.ser.addOperator(Op.SUB, [a.name, b.name], [else_tens.name])
1538
1539 return result_tens
1540
1541 def build_while_loop(self, op, a, iter_val):
Kevin Cheng550ccc52021-03-03 11:21:43 -08001542 iter = self.ser.addPlaceholder([], DType.INT32, [np.int32(iter_val)])
Eric Kunzee5e26762020-10-13 16:11:07 -07001543
Kevin Cheng550ccc52021-03-03 11:21:43 -08001544 cond_block = "COND_BLOCK"
1545 body_block = "BODY_BLOCK"
Eric Kunzee5e26762020-10-13 16:11:07 -07001546
1547 attr = ts.TosaSerializerAttribute()
1548 attr.WhileLoopAttribute(cond_block, body_block)
1549
1550 # Accumulator tensor
Kevin Cheng550ccc52021-03-03 11:21:43 -08001551 # acc = self.ser.addOutput(a.shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07001552 acc_init_val = np.int32(np.zeros(a.shape))
Kevin Cheng550ccc52021-03-03 11:21:43 -08001553 acc = self.ser.addPlaceholder(a.shape, a.dtype, acc_init_val)
Eric Kunzee5e26762020-10-13 16:11:07 -07001554
1555 # Intermediate/output tensors for everything going through the loop
Kevin Cheng550ccc52021-03-03 11:21:43 -08001556 iter_out = self.ser.addIntermediate(iter.shape, iter.dtype)
1557 a_out = self.ser.addIntermediate(a.shape, a.dtype)
1558 acc_out = self.ser.addIntermediate(acc.shape, acc.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07001559
1560 # While_loop operator
Kevin Cheng550ccc52021-03-03 11:21:43 -08001561 self.ser.addOperator(
1562 op,
1563 [iter.name, a.name, acc.name],
1564 [iter_out.name, a_out.name, acc_out.name],
1565 attr,
1566 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001567
1568 # COND block (input: iter, output: cond_tens )
1569 self.ser.startBasicBlock(cond_block)
1570 self.ser.addInputTensor(iter)
1571 self.ser.addInputTensor(a)
1572 self.ser.addInputTensor(acc)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001573 zero_tens = self.ser.addConst([], DType.INT32, [np.int32(0)])
1574 cond_tens = self.ser.addOutput([], DType.BOOL)
1575 self.ser.addOperator(Op.GREATER, [iter.name, zero_tens.name], [cond_tens.name])
Eric Kunzee5e26762020-10-13 16:11:07 -07001576
1577 # BODY block (input: a, acc, iter, output: a, acc, iter)
1578 # Note that local intermediate tensors need to be declared here for the outputs
1579 self.ser.startBasicBlock(body_block)
1580 self.ser.addInputTensor(iter)
1581 self.ser.addInputTensor(a)
1582 self.ser.addInputTensor(acc)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001583 one_tens = self.ser.addConst([], DType.INT32, [np.int32(1)])
1584 iter_body_out = self.ser.addIntermediate(iter.shape, iter.dtype)
1585 acc_body_out = self.ser.addIntermediate(acc.shape, acc.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07001586 self.ser.addOperator(Op.ADD, [a.name, acc.name], [acc_body_out.name])
1587 self.ser.addOperator(Op.SUB, [iter.name, one_tens.name], [iter_body_out.name])
1588 self.ser.addOutputTensor(iter_body_out)
1589 self.ser.addOutputTensor(a)
1590 self.ser.addOutputTensor(acc_body_out)
1591
1592 return acc_out
1593
Kevin Cheng550ccc52021-03-03 11:21:43 -08001594 def genOpTestList(
1595 self, opName, shapeFilter=[None], rankFilter=None, dtypeFilter=None
1596 ):
Eric Kunzee5e26762020-10-13 16:11:07 -07001597
1598 try:
1599 op = self.TOSA_OP_LIST[opName]
1600 except KeyError as e:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001601 raise Exception("Cannot find op with name {}".format(opName))
Eric Kunzee5e26762020-10-13 16:11:07 -07001602
1603 # Initialize a new random number generator
1604 self.rng = np.random.default_rng(self.random_seed)
1605
Kevin Cheng550ccc52021-03-03 11:21:43 -08001606 build_fcn, tgen_fcn, agen_fcn = op["build_fcn"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001607
1608 # Generate the lists of arguments
Kevin Cheng550ccc52021-03-03 11:21:43 -08001609 rmin, rmax = op["rank"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001610
1611 # Test list consists of a tuple of:
1612 # (opName, testNameStr, dtype, shapeList, argumentsList)
1613 testList = []
1614
1615 if not shapeFilter:
1616 shapeFilter = [None]
1617
1618 for r in range(rmin, rmax + 1):
1619
1620 # Filter out the rank?
1621 if rankFilter is not None and r not in rankFilter:
1622 continue
1623
Kevin Cheng550ccc52021-03-03 11:21:43 -08001624 for t in op["types"]:
Eric Kunzee5e26762020-10-13 16:11:07 -07001625
1626 # Filter tests based on dtype?
1627 if dtypeFilter is not None:
1628 if t not in dtypeFilter:
1629 continue
1630
1631 # Create the placeholder and const tensors
1632 for shape in shapeFilter:
1633 # A None shape chooses a random shape of a given rank
1634
1635 # Filter out by rank
1636 if shape is not None and len(shape) != r:
1637 continue
1638
1639 self.setTargetShape(shape)
1640 shapeList = tgen_fcn(self, op, r)
1641
1642 shapeStr = self.shapeStr(shapeList[0])
1643 typeStr = self.typeStr(t)
1644
1645 # Argument lists consists of tuples of the (str, []) string representation and the build function argument list
1646 argList = []
1647 if agen_fcn:
1648 argList = agen_fcn(self, opName, shapeList, t)
1649 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001650 argList = [("", [])]
Eric Kunzee5e26762020-10-13 16:11:07 -07001651
1652 for argStr, args in argList:
1653 if argStr:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001654 testStr = "{}_{}_{}_{}".format(
1655 opName, shapeStr, typeStr, argStr
1656 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001657 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001658 testStr = "{}_{}_{}".format(opName, shapeStr, typeStr)
Eric Kunzee5e26762020-10-13 16:11:07 -07001659
1660 testList.append((opName, testStr, t, shapeList, args))
1661
1662 return testList
1663
1664 def serializeTest(self, opName, testStr, dtype, shapeList, testArgs):
1665 try:
1666 op = self.TOSA_OP_LIST[opName]
1667 except KeyError as e:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001668 raise Exception("Cannot find op with name {}".format(opName))
Eric Kunzee5e26762020-10-13 16:11:07 -07001669
1670 # Create a serializer
1671 self.createSerializer(opName, testStr)
1672
Kevin Cheng550ccc52021-03-03 11:21:43 -08001673 build_fcn, tgen_fcn, agen_fcn = op["build_fcn"]
1674 pCount, cCount = op["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001675
1676 try:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001677 qgen = op["qgen"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001678 except KeyError:
1679 qgen = None
1680
1681 # Build the random tensor operands and the test
1682 tens = []
Kevin Chengaee1fac2020-11-11 13:54:06 -08001683
1684 # If test is ArithmeticRightShift, force value of operand[1] to be within [0, num_bits]
Kevin Cheng550ccc52021-03-03 11:21:43 -08001685 if op["op"] == Op.ARITHMETIC_RIGHT_SHIFT:
1686 assert (
1687 pCount == 2 and cCount == 0
1688 ), "Op.ArithmeticRightShift must have 2 placeholders, 0 consts"
Kevin Chengaee1fac2020-11-11 13:54:06 -08001689
1690 placeholders = []
1691 for idx, shape in enumerate(shapeList[:]):
1692 if idx == 1:
1693 if dtype == DType.INT8:
1694 arr = np.int32(self.rng.integers(low=0, high=8, size=shape))
1695 elif dtype == DType.INT16:
1696 arr = np.int32(self.rng.integers(low=0, high=16, size=shape))
1697 elif dtype == DType.INT32:
1698 arr = np.int32(self.rng.integers(low=0, high=32, size=shape))
1699 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001700 raise Exception("OpArithmeticRightShift: invalid input dtype")
Kevin Chengaee1fac2020-11-11 13:54:06 -08001701 else:
1702 arr = self.getRandTensor(shapeList[0], dtype)
Kevin Cheng550ccc52021-03-03 11:21:43 -08001703 placeholders.append(self.ser.addPlaceholder(shape, dtype, arr))
Kevin Chengaee1fac2020-11-11 13:54:06 -08001704
1705 tens.extend(placeholders)
1706 else:
1707 tens.extend(self.buildPlaceholderTensors(shapeList[0:pCount], dtype))
1708 tens.extend(self.buildConstTensors(shapeList[pCount:], dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -07001709
1710 if qgen is not None:
1711 qinfo = qgen(self, op, dtype)
1712 else:
1713 qinfo = None
1714
1715 try:
1716 if qinfo is not None:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001717 resultName = build_fcn(self, op["op"], *tens, *testArgs, qinfo)
Eric Kunzee5e26762020-10-13 16:11:07 -07001718 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001719 resultName = build_fcn(self, op["op"], *tens, *testArgs)
Eric Kunzee5e26762020-10-13 16:11:07 -07001720 except TypeError as e:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001721 print(
1722 "build_fcn: {}\nTensors: {}\nArgs: {}\n".format(
1723 build_fcn, tens, testArgs
1724 )
1725 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001726 raise e
1727
1728 # Save the serialized test
Kevin Cheng550ccc52021-03-03 11:21:43 -08001729 self.serialize("test")
Eric Kunzee5e26762020-10-13 16:11:07 -07001730
1731 def createDynamicOpLists(self):
1732
1733 # Dynamically create op lists for convolutions with a list of kernel sizes
Kevin Cheng550ccc52021-03-03 11:21:43 -08001734 KERNELS = [[1, 1], [2, 2], [3, 3], [5, 5], [3, 1], [1, 3]]
Eric Kunzee5e26762020-10-13 16:11:07 -07001735
1736 for k in KERNELS:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001737 testName = "conv2d_{}x{}".format(k[0], k[1])
1738 self.TOSA_OP_LIST[testName] = self.TOSA_OP_LIST["conv2d_TEMPLATE"].copy()
1739 self.TOSA_OP_LIST[testName]["filter"] = k
1740 self.TOSA_OP_LIST[testName]["template"] = False
Eric Kunzee5e26762020-10-13 16:11:07 -07001741
Kevin Cheng550ccc52021-03-03 11:21:43 -08001742 testName = "depthwise_conv2d_{}x{}".format(k[0], k[1])
1743 self.TOSA_OP_LIST[testName] = self.TOSA_OP_LIST[
1744 "depthwise_conv2d_TEMPLATE"
1745 ].copy()
1746 self.TOSA_OP_LIST[testName]["filter"] = k
1747 self.TOSA_OP_LIST[testName]["template"] = False
Eric Kunzee5e26762020-10-13 16:11:07 -07001748
Kevin Cheng550ccc52021-03-03 11:21:43 -08001749 testName = "transpose_conv2d_{}x{}".format(k[0], k[1])
1750 self.TOSA_OP_LIST[testName] = self.TOSA_OP_LIST[
1751 "transpose_conv2d_TEMPLATE"
1752 ].copy()
1753 self.TOSA_OP_LIST[testName]["filter"] = k
1754 self.TOSA_OP_LIST[testName]["template"] = False
Eric Kunzee5e26762020-10-13 16:11:07 -07001755
1756 # Delete any templates after having created any dynamic ops
1757 # This is a two-pass operation because it's bad practice to delete
1758 # keys from dictionaries while iterating
1759 keyList = []
1760 for k in self.TOSA_OP_LIST:
1761 try:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001762 if self.TOSA_OP_LIST[k]["template"] == True:
Eric Kunzee5e26762020-10-13 16:11:07 -07001763 keyList.append(k)
1764 continue
1765 except KeyError:
1766 pass
1767
1768 for k in keyList:
1769 del self.TOSA_OP_LIST[k]
1770
1771 def initOpListDefaults(self):
Kevin Cheng550ccc52021-03-03 11:21:43 -08001772 """Fill in default fields for ops if they aren't already specified.
1773 Look for missing required fields (datastructure linting)."""
Eric Kunzee5e26762020-10-13 16:11:07 -07001774 for op in self.TOSA_OP_LIST:
1775
1776 # Required fields
1777 try:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001778 pl, c = self.TOSA_OP_LIST[op]["operands"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001779 except (KeyError, ValueError, TypeError):
Kevin Cheng550ccc52021-03-03 11:21:43 -08001780 raise Exception(
1781 "Op {} is missing a valid operand tuple in TOSA_OP_LIST".format(op)
1782 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001783
1784 try:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001785 fcn, tgen, arggen = self.TOSA_OP_LIST[op]["build_fcn"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001786 except (KeyError, ValueError, TypeError):
Kevin Cheng550ccc52021-03-03 11:21:43 -08001787 raise Exception(
1788 "Op {} is missing a valid build_fcn tuple in TOSA_OP_LIST".format(
1789 op
1790 )
1791 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001792
1793 try:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001794 types = self.TOSA_OP_LIST[op]["types"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001795 except KeyError as e:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001796 raise Exception(
1797 "Op {} is missing a valid type list in TOSA_OP_LIST".format(op)
1798 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001799
1800 try:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001801 opcode = self.TOSA_OP_LIST[op]["op"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001802 except KeyError as e:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001803 raise Exception(
1804 "Op {} is missing the Op field in TOSA_OP_LIST".format(op)
1805 )
Eric Kunzee5e26762020-10-13 16:11:07 -07001806
1807 # Put in default rank range, if missing
1808 try:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001809 rank = self.TOSA_OP_LIST[op]["rank"]
Eric Kunzee5e26762020-10-13 16:11:07 -07001810 except KeyError:
Kevin Cheng550ccc52021-03-03 11:21:43 -08001811 self.TOSA_OP_LIST[op]["rank"] = self.DEFAULT_RANK_RANGE
Eric Kunzee5e26762020-10-13 16:11:07 -07001812
1813 # Tensor operator list
1814 # 'op': op name
1815 # 'operands': tuple of (placeholder, const) operands
Kevin Cheng3a478572021-01-22 17:21:02 -08001816 # 'rank': optional, restricts rank to tuple inclusive of (min, max),
1817 # if not specified, defaults to (1, 4)
Eric Kunzee5e26762020-10-13 16:11:07 -07001818 # 'build_fcn': tuple of the function to (build_operator(), TensorGen function, ArgGen enum)
1819 # 'types': array of datatypes to be tested
Kevin Cheng550ccc52021-03-03 11:21:43 -08001820 TYPE_FP = [DType.FLOAT]
Eric Kunzee5e26762020-10-13 16:11:07 -07001821
Kevin Cheng550ccc52021-03-03 11:21:43 -08001822 TYPE_INT = [DType.INT8, DType.INT16, DType.INT32] # Excludes INT4
1823 TYPE_INT_FP = [DType.INT8, DType.INT16, DType.INT32, DType.FLOAT] # Excludes INT4
Eric Kunzee5e26762020-10-13 16:11:07 -07001824
Kevin Cheng550ccc52021-03-03 11:21:43 -08001825 TYPE_BOOL = [DType.BOOL]
1826 TYPE_FI32 = [DType.FLOAT, DType.INT32]
1827 TYPE_FIB = [DType.FLOAT, DType.INT8, DType.INT16, DType.INT32, DType.BOOL]
1828 TYPE_FI16 = [DType.FLOAT, DType.INT16]
Eric Kunzee5e26762020-10-13 16:11:07 -07001829
Kevin Cheng550ccc52021-03-03 11:21:43 -08001830 TYPE_NARROW_INT_FP = [DType.INT8, DType.INT16, DType.FLOAT]
Eric Kunzee5e26762020-10-13 16:11:07 -07001831
1832 DEFAULT_RANK_RANGE = (1, 4)
1833
1834 TOSA_OP_LIST = {
1835 # Binary ops
Kevin Cheng550ccc52021-03-03 11:21:43 -08001836 "add": {
1837 "op": Op.ADD,
1838 "operands": (2, 0),
1839 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1840 "types": TYPE_FI32,
1841 },
1842 "arithmetic_right_shift": {
1843 "op": Op.ARITHMETIC_RIGHT_SHIFT,
1844 "operands": (2, 0),
1845 "build_fcn": (
1846 build_arithmetic_right_shift,
1847 TosaTensorGen.tgBroadcastFuzz,
1848 TosaArgGen.agArithmeticRightShift,
1849 ),
1850 "types": TYPE_INT,
1851 },
1852 "bitwise_and": {
1853 "op": Op.BITWISE_AND,
1854 "operands": (2, 0),
1855 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1856 "types": TYPE_INT,
1857 },
1858 "bitwise_or": {
1859 "op": Op.BITWISE_OR,
1860 "operands": (2, 0),
1861 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1862 "types": TYPE_INT,
1863 },
1864 "bitwise_xor": {
1865 "op": Op.BITWISE_XOR,
1866 "operands": (2, 0),
1867 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1868 "types": TYPE_INT,
1869 },
1870 "logical_and": {
1871 "op": Op.LOGICAL_AND,
1872 "operands": (2, 0),
1873 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1874 "types": TYPE_BOOL,
1875 },
1876 "logical_left_shift": {
1877 "op": Op.LOGICAL_LEFT_SHIFT,
1878 "operands": (2, 0),
1879 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1880 "types": TYPE_INT,
1881 },
1882 "logical_right_shift": {
1883 "op": Op.LOGICAL_RIGHT_SHIFT,
1884 "operands": (2, 0),
1885 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1886 "types": TYPE_INT,
1887 },
1888 "logical_or": {
1889 "op": Op.LOGICAL_OR,
1890 "operands": (2, 0),
1891 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1892 "types": TYPE_BOOL,
1893 },
1894 "logical_xor": {
1895 "op": Op.LOGICAL_XOR,
1896 "operands": (2, 0),
1897 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1898 "types": TYPE_BOOL,
1899 },
1900 "max": {
1901 "op": Op.MAXIMUM,
1902 "operands": (2, 0),
1903 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1904 "types": TYPE_FI32,
1905 },
1906 "min": {
1907 "op": Op.MINIMUM,
1908 "operands": (2, 0),
1909 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1910 "types": TYPE_FI32,
1911 },
1912 "mul": {
1913 "op": Op.MUL,
1914 "operands": (2, 0),
1915 "build_fcn": (build_mul, TosaTensorGen.tgBroadcastFuzz, TosaArgGen.agMul),
1916 "types": TYPE_INT_FP,
1917 },
1918 "pow": {
1919 "op": Op.POW,
1920 "operands": (2, 0),
1921 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBasic, None),
1922 "types": TYPE_FP,
1923 },
1924 "sub": {
1925 "op": Op.SUB,
1926 "operands": (2, 0),
1927 "build_fcn": (build_binary_broadcast, TosaTensorGen.tgBroadcastFuzz, None),
1928 "types": TYPE_FI32,
1929 },
1930 "table": {
1931 "op": Op.TABLE,
1932 # Use the automatic generation functions to create the input array
1933 # but create the table tensor in the build function, as it may be
1934 # a different type from the input
1935 "operands": (1, 0),
1936 "build_fcn": (build_table, TosaTensorGen.tgBasic, None),
1937 "types": [DType.INT16],
1938 },
1939 "argmax": {
1940 "op": Op.ARGMAX,
1941 "operands": (1, 0),
1942 "build_fcn": (build_argmax, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
1943 "types": TYPE_NARROW_INT_FP,
1944 },
Eric Kunzee5e26762020-10-13 16:11:07 -07001945 # Templated operator. Filled in by createDynamicOpLists
Kevin Cheng550ccc52021-03-03 11:21:43 -08001946 "conv2d_TEMPLATE": {
1947 "op": Op.CONV2D,
1948 "operands": (1, 2),
1949 "rank": (4, 4),
1950 "build_fcn": (build_conv2d, TosaTensorGen.tgConv2D, TosaArgGen.agConv2D),
1951 "qgen": TosaQuantGen.qgConv,
1952 "types": TYPE_NARROW_INT_FP,
1953 "template": True,
1954 },
Eric Kunzee5e26762020-10-13 16:11:07 -07001955 # Templated operator. Filled in by createDynamicOpLists
Kevin Cheng550ccc52021-03-03 11:21:43 -08001956 "depthwise_conv2d_TEMPLATE": {
1957 "op": Op.DEPTHWISE_CONV2D,
1958 "operands": (1, 2),
1959 "filter": [1, 1],
1960 "rank": (4, 4),
1961 "build_fcn": (
1962 build_depthwise_conv2d,
1963 TosaTensorGen.tgDepthwiseConv2D,
1964 TosaArgGen.agConv2D,
1965 ),
1966 "qgen": TosaQuantGen.qgConv,
1967 "types": TYPE_NARROW_INT_FP,
1968 "template": True,
1969 },
Eric Kunzee5e26762020-10-13 16:11:07 -07001970 # Templated operator. Filled in by createDynamicOpLists
Kevin Cheng550ccc52021-03-03 11:21:43 -08001971 "transpose_conv2d_TEMPLATE": {
1972 "op": Op.TRANSPOSE_CONV2D,
1973 "operands": (1, 1),
1974 "rank": (4, 4),
1975 "build_fcn": (
1976 build_transpose_conv2d,
1977 TosaTensorGen.tgTransposeConv2D,
1978 TosaArgGen.agTransposeConv2D,
1979 ),
1980 "qgen": TosaQuantGen.qgConv,
1981 "types": TYPE_FP,
1982 "template": True,
1983 },
1984 "fully_connected": {
1985 "op": Op.FULLY_CONNECTED,
1986 "operands": (2, 0),
1987 "rank": (2, 2),
1988 "build_fcn": (build_fully_connected, TosaTensorGen.tgFullyConnected, None),
1989 "qgen": TosaQuantGen.qgConv,
1990 "types": TYPE_NARROW_INT_FP,
1991 },
1992 "matmul": {
1993 "op": Op.MATMUL,
1994 "operands": (2, 0),
1995 "rank": (2, 2),
1996 "build_fcn": (build_matmul, TosaTensorGen.tgMatmul, None),
1997 "qgen": TosaQuantGen.qgMatmul,
1998 "types": TYPE_NARROW_INT_FP,
1999 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002000 # Unary operators
Kevin Cheng550ccc52021-03-03 11:21:43 -08002001 "abs": {
2002 "op": Op.ABS,
2003 "operands": (1, 0),
2004 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2005 "types": TYPE_FI32,
2006 },
2007 "bitwise_not": {
2008 "op": Op.BITWISE_NOT,
2009 "operands": (1, 0),
2010 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2011 "types": TYPE_INT,
2012 },
2013 "ceil": {
2014 "op": Op.CEIL,
2015 "operands": (1, 0),
2016 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2017 "types": TYPE_FP,
2018 },
2019 "clz": {
2020 "op": Op.CLZ,
2021 "operands": (1, 0),
2022 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2023 "types": [DType.INT32],
2024 },
2025 "exp": {
2026 "op": Op.EXP,
2027 "operands": (1, 0),
2028 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2029 "types": TYPE_FP,
2030 },
2031 "floor": {
2032 "op": Op.FLOOR,
2033 "operands": (1, 0),
2034 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2035 "types": TYPE_FP,
2036 },
2037 "log": {
2038 "op": Op.LOG,
2039 "operands": (1, 0),
2040 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2041 "types": TYPE_FP,
2042 },
2043 "floor": {
2044 "op": Op.FLOOR,
2045 "operands": (1, 0),
2046 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2047 "types": TYPE_FP,
2048 },
2049 "logical_not": {
2050 "op": Op.LOGICAL_NOT,
2051 "operands": (1, 0),
2052 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2053 "types": TYPE_BOOL,
2054 },
2055 "negate": {
2056 "op": Op.NEGATE,
2057 "operands": (1, 0),
2058 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2059 "qgen": TosaQuantGen.qgUnary,
2060 "types": TYPE_INT_FP,
2061 },
2062 "reciprocal": {
2063 "op": Op.RECIPROCAL,
2064 "operands": (1, 0),
2065 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2066 "types": TYPE_FP,
2067 },
2068 "rsqrt": {
2069 "op": Op.RSQRT,
2070 "operands": (1, 0),
2071 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2072 "types": TYPE_FP,
2073 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002074 # Ternary operators
Kevin Cheng550ccc52021-03-03 11:21:43 -08002075 "select": {
2076 "op": Op.SELECT,
2077 "operands": (3, 0),
2078 "build_fcn": (build_select, TosaTensorGen.tgBroadcastFuzz, None),
2079 "types": TYPE_FIB,
2080 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002081 # Comparison operators
Kevin Cheng550ccc52021-03-03 11:21:43 -08002082 "equal": {
2083 "op": Op.EQUAL,
2084 "operands": (2, 0),
2085 "build_fcn": (build_comparison, TosaTensorGen.tgBroadcastFuzz, None),
2086 "types": TYPE_FI32,
2087 },
2088 "greater_equal": {
2089 "op": Op.GREATER_EQUAL,
2090 "operands": (2, 0),
2091 "build_fcn": (build_comparison, TosaTensorGen.tgBroadcastFuzz, None),
2092 "types": TYPE_FI32,
2093 },
2094 "greater": {
2095 "op": Op.GREATER,
2096 "operands": (2, 0),
2097 "build_fcn": (build_comparison, TosaTensorGen.tgBroadcastFuzz, None),
2098 "types": TYPE_FI32,
2099 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002100 # Pooling operators
Kevin Cheng550ccc52021-03-03 11:21:43 -08002101 "avg_pool2d": {
2102 "op": Op.AVG_POOL2D,
2103 "operands": (1, 0),
2104 "rank": (4, 4),
2105 "build_fcn": (build_pool2d, TosaTensorGen.tgNHWC, TosaArgGen.agPooling),
2106 "qgen": TosaQuantGen.qgUnary,
2107 "types": TYPE_NARROW_INT_FP,
2108 },
2109 "max_pool2d": {
2110 "op": Op.MAX_POOL2D,
2111 "operands": (1, 0),
2112 "rank": (4, 4),
2113 "build_fcn": (build_pool2d, TosaTensorGen.tgNHWC, TosaArgGen.agPooling),
2114 "types": TYPE_NARROW_INT_FP,
2115 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002116 # Reduce operators
Kevin Cheng550ccc52021-03-03 11:21:43 -08002117 "reduce_any": {
2118 "op": Op.REDUCE_ANY,
2119 "operands": (1, 0),
2120 "build_fcn": (build_reduce, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2121 "types": TYPE_BOOL,
2122 },
2123 "reduce_all": {
2124 "op": Op.REDUCE_ALL,
2125 "operands": (1, 0),
2126 "build_fcn": (build_reduce, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2127 "types": TYPE_BOOL,
2128 },
2129 "reduce_max": {
2130 "op": Op.REDUCE_MAX,
2131 "operands": (1, 0),
2132 "build_fcn": (build_reduce, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2133 "types": TYPE_INT_FP,
2134 },
2135 "reduce_min": {
2136 "op": Op.REDUCE_MAX,
2137 "operands": (1, 0),
2138 "build_fcn": (build_reduce, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2139 "types": TYPE_INT_FP,
2140 },
2141 "reduce_product": {
2142 "op": Op.REDUCE_PRODUCT,
2143 "operands": (1, 0),
2144 "build_fcn": (build_reduce, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2145 "types": TYPE_FP,
2146 },
2147 "reduce_sum": {
2148 "op": Op.REDUCE_SUM,
2149 "operands": (1, 0),
2150 "build_fcn": (build_reduce, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2151 "types": TYPE_FI32,
2152 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002153 # Activation functions
Kevin Cheng550ccc52021-03-03 11:21:43 -08002154 "clamp": {
2155 "op": Op.CLAMP,
2156 "operands": (1, 0),
2157 "build_fcn": (build_clamp, TosaTensorGen.tgBasic, None),
2158 "types": TYPE_NARROW_INT_FP,
2159 },
2160 "relun": {
2161 "op": Op.RELUN,
2162 "operands": (1, 0),
2163 "build_fcn": (build_relun, TosaTensorGen.tgBasic, None),
2164 "types": TYPE_FI32,
2165 },
2166 "sigmoid": {
2167 "op": Op.SIGMOID,
2168 "operands": (1, 0),
2169 "build_fcn": (build_sigmoid, TosaTensorGen.tgBasic, None),
2170 "types": TYPE_FP,
2171 },
2172 "tanh": {
2173 "op": Op.TANH,
2174 "operands": (1, 0),
2175 "build_fcn": (build_tanh, TosaTensorGen.tgBasic, None),
2176 "types": TYPE_FP,
2177 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002178 # Data layout operators
Kevin Cheng550ccc52021-03-03 11:21:43 -08002179 "concat": {
2180 "op": Op.CONCAT,
2181 "operands": (2, 0),
2182 "build_fcn": (build_concat, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2183 "types": TYPE_FIB,
2184 },
2185 "pad": {
2186 "op": Op.PAD,
2187 "operands": (1, 0),
2188 "build_fcn": (build_pad, TosaTensorGen.tgBasic, TosaArgGen.agPad),
2189 "qgen": TosaQuantGen.qgPad,
2190 "types": TYPE_FIB,
2191 },
2192 "reshape": {
2193 "op": Op.RESHAPE,
2194 "operands": (1, 0),
2195 "build_fcn": (build_reshape, TosaTensorGen.tgBasic, TosaArgGen.agReshape),
2196 "types": TYPE_FIB,
2197 },
2198 "reverse": {
2199 "op": Op.REVERSE,
2200 "operands": (1, 0),
2201 "build_fcn": (build_reverse, TosaTensorGen.tgBasic, TosaArgGen.agAxis),
2202 "types": TYPE_FIB,
2203 },
2204 "slice": {
2205 "op": Op.SLICE,
2206 "operands": (1, 0),
2207 "build_fcn": (build_slice, TosaTensorGen.tgBasic, TosaArgGen.agSlice),
2208 "types": TYPE_FIB,
2209 },
2210 "tile": {
2211 "op": Op.TILE,
2212 "operands": (1, 0),
2213 "build_fcn": (build_tile, TosaTensorGen.tgBasic, TosaArgGen.agTile),
2214 "types": TYPE_FIB,
2215 },
2216 "transpose": {
2217 "op": Op.TRANSPOSE,
2218 "operands": (1, 0),
2219 "rank": (2, 4), # Do not allow tranpose on rank=1
2220 "build_fcn": (
2221 build_transpose,
2222 TosaTensorGen.tgBasic,
2223 TosaArgGen.agTranspose,
2224 ),
2225 "types": TYPE_FIB,
2226 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002227 # Scatter/Gather
Kevin Cheng550ccc52021-03-03 11:21:43 -08002228 "gather": {
2229 "op": Op.GATHER,
2230 # Only specify 'values' tensor here. 'indices' is generated in op building stage
2231 "operands": (1, 0),
2232 "rank": (3, 3),
2233 "build_fcn": (build_gather, TosaTensorGen.tgBasic, None),
2234 "types": TYPE_INT_FP,
2235 },
2236 "scatter": {
2237 "op": Op.SCATTER,
2238 # Only specify 'values_in' tensor here.
2239 #'indices' and 'input' are generated in op building stage
2240 "operands": (2, 0),
2241 "rank": (3, 3),
2242 "build_fcn": (build_scatter, TosaTensorGen.tgScatter, None),
2243 "types": TYPE_INT_FP,
2244 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002245 # Image operations
Kevin Cheng550ccc52021-03-03 11:21:43 -08002246 "resize": {
2247 "op": Op.RESIZE,
2248 "operands": (1, 0),
2249 "rank": (4, 4),
2250 "build_fcn": (build_resize, TosaTensorGen.tgNHWC, TosaArgGen.agResize),
2251 "types": [DType.INT8, DType.INT16, DType.FLOAT],
2252 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002253 # Data nodes
Kevin Cheng550ccc52021-03-03 11:21:43 -08002254 "placeholder": {
2255 "op": Op.PLACEHOLDER,
2256 "operands": (1, 0),
2257 "build_fcn": (build_placeholder, TosaTensorGen.tgBasic, None),
2258 "types": TYPE_FIB,
2259 },
2260 "const": {
2261 "op": Op.CONST,
2262 "operands": (1, 0),
2263 "build_fcn": (build_placeholder, TosaTensorGen.tgBasic, None),
2264 "types": TYPE_FIB,
2265 },
2266 "identity": {
2267 "op": Op.IDENTITY,
2268 "operands": (1, 0),
2269 "build_fcn": (build_unary, TosaTensorGen.tgBasic, None),
2270 "types": TYPE_FIB,
2271 },
2272 "identityn": {
2273 "op": Op.IDENTITYN,
2274 "operands": (2, 0),
2275 "build_fcn": (build_identityn, TosaTensorGen.tgBasic, None),
2276 "types": TYPE_FIB,
2277 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002278 # Type conversion
Kevin Cheng550ccc52021-03-03 11:21:43 -08002279 "cast": {
2280 "op": Op.CAST,
2281 "operands": (1, 0),
2282 "build_fcn": (build_cast, TosaTensorGen.tgBasic, TosaArgGen.agCast),
2283 "types": [DType.FLOAT, DType.INT8, DType.INT16, DType.INT32, DType.BOOL],
2284 },
2285 "rescale": {
2286 "op": Op.RESCALE,
2287 "operands": (1, 0),
2288 "build_fcn": (build_rescale, TosaTensorGen.tgBasic, TosaArgGen.agRescale),
2289 "types": [DType.INT8, DType.INT16, DType.INT32, DType.INT48],
2290 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002291 # Custom
2292 # Not implemented.
Eric Kunzee5e26762020-10-13 16:11:07 -07002293 # Control flow
Eric Kunzee5e26762020-10-13 16:11:07 -07002294 # Two varients of cond_if, one that generates one of two constant tensors (no
2295 # inputs to the basic blocks, one output) and another that either adds or subtracts two tensors
2296 # (two inputs to the basic blocks, one output)
Kevin Cheng550ccc52021-03-03 11:21:43 -08002297 "cond_if_const": {
2298 "op": Op.COND_IF,
2299 "operands": (0, 2),
2300 "build_fcn": (
2301 build_cond_if_const,
2302 TosaTensorGen.tgBasic,
2303 TosaArgGen.agCondIf,
2304 ),
2305 "types": [DType.BOOL],
2306 },
2307 "cond_if_binary": {
2308 "op": Op.COND_IF,
2309 "operands": (2, 0),
2310 "build_fcn": (
2311 build_cond_if_binary,
2312 TosaTensorGen.tgBasic,
2313 TosaArgGen.agCondIf,
2314 ),
2315 "types": TYPE_FI32,
2316 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002317 # while_loop
Kevin Cheng550ccc52021-03-03 11:21:43 -08002318 "while_loop": {
2319 "op": Op.WHILE_LOOP,
2320 "operands": (0, 1),
2321 "build_fcn": (
2322 build_while_loop,
2323 TosaTensorGen.tgBasic,
2324 TosaArgGen.agWhileLoop,
2325 ),
2326 "types": [DType.INT32],
2327 },
Eric Kunzee5e26762020-10-13 16:11:07 -07002328 }
2329
Kevin Cheng550ccc52021-03-03 11:21:43 -08002330
Eric Kunzee5e26762020-10-13 16:11:07 -07002331class OutputShaper:
2332 # Methods in this class compute the expected output shape and datatype
2333 # for common classes of operations
2334 def __init__(self):
2335 pass
2336
2337 # These methods return arguments that can be used for
2338 # creating a new output tensor
2339 @staticmethod
2340 def binaryBroadcastOp(ser, a, b):
Kevin Cheng550ccc52021-03-03 11:21:43 -08002341 assert len(a.shape) == len(b.shape)
2342 assert a.dtype == b.dtype
Eric Kunzee5e26762020-10-13 16:11:07 -07002343
2344 shape = []
2345 for i in range(len(a.shape)):
2346 if a.shape[i] == 1:
2347 shape.append(b.shape[i])
2348 else:
2349 shape.append(a.shape[i])
2350
Kevin Cheng550ccc52021-03-03 11:21:43 -08002351 return ser.addOutput(shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002352
2353 @staticmethod
2354 def binaryNonBroadcastOp(ser, a, b):
Kevin Cheng550ccc52021-03-03 11:21:43 -08002355 assert len(a.shape) == len(b.shape)
2356 assert a.dtype == b.dtype
Eric Kunzee5e26762020-10-13 16:11:07 -07002357
2358 shape = []
2359 for i in range(len(a.shape)):
Kevin Cheng550ccc52021-03-03 11:21:43 -08002360 assert a.shape[i] == b.shape[i]
Eric Kunzee5e26762020-10-13 16:11:07 -07002361 shape.append(a.shape[i])
2362
Kevin Cheng550ccc52021-03-03 11:21:43 -08002363 return ser.addOutput(shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002364
2365 @staticmethod
2366 def unaryOp(ser, a):
Kevin Cheng550ccc52021-03-03 11:21:43 -08002367 return ser.addOutput(a.shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002368
2369 @staticmethod
2370 def selectOp(ser, cond, a, b):
Kevin Cheng550ccc52021-03-03 11:21:43 -08002371 assert len(a.shape) == len(b.shape) and len(a.shape) == len(cond.shape)
2372 assert a.dtype == b.dtype
Eric Kunzee5e26762020-10-13 16:11:07 -07002373
2374 shape = []
2375 for i in range(len(a.shape)):
2376 shape.append(max(cond.shape[i], a.shape[i], b.shape[i]))
2377
Kevin Cheng550ccc52021-03-03 11:21:43 -08002378 return ser.addOutput(shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002379
2380 @staticmethod
2381 def binaryComparisonOp(ser, a, b):
Kevin Cheng550ccc52021-03-03 11:21:43 -08002382 assert len(a.shape) == len(b.shape)
2383 assert a.dtype == b.dtype
Eric Kunzee5e26762020-10-13 16:11:07 -07002384
2385 # Do broadcast
2386 shape = []
2387 for i in range(len(a.shape)):
2388 if a.shape[i] == 1:
2389 shape.append(b.shape[i])
2390 else:
2391 shape.append(a.shape[i])
2392
2393 # Force the output type to bool
Kevin Cheng550ccc52021-03-03 11:21:43 -08002394 return ser.addOutput(shape, DType.BOOL)
Eric Kunzee5e26762020-10-13 16:11:07 -07002395
2396 @staticmethod
2397 def reduceOp(ser, a, axis):
2398
2399 shape = a.shape.copy()
2400
2401 shape[axis] = 1
2402
Kevin Cheng550ccc52021-03-03 11:21:43 -08002403 return ser.addOutput(shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002404
2405 @staticmethod
2406 def argmaxOp(ser, a, axis):
2407 shape = a.shape.copy()
2408 del shape[axis]
Kevin Cheng550ccc52021-03-03 11:21:43 -08002409 return ser.addOutput(shape, DType.INT32)
Eric Kunzee5e26762020-10-13 16:11:07 -07002410
2411 @staticmethod
2412 def conv2dOp(ser, ifm, filter, strides, padding, dilations):
2413
2414 # IFM: NHWC
2415 # Filter: OHWI
2416 # OFM: NHWC
2417
2418 if len(padding) == 2:
2419 # Expand padding to 4 parameters in the case of transpose_conv2d
2420 # From H,W to T,B,L,R
2421 padding = [padding[0], padding[0], padding[1], padding[1]]
2422
Kevin Cheng550ccc52021-03-03 11:21:43 -08002423 h = (
2424 ifm.shape[1]
2425 - filter.shape[1]
2426 - (filter.shape[1] - 1) * (dilations[0] - 1)
2427 + padding[0]
2428 + padding[1]
2429 ) // strides[0] + 1
Eric Kunzee5e26762020-10-13 16:11:07 -07002430
Kevin Cheng550ccc52021-03-03 11:21:43 -08002431 w = (
2432 ifm.shape[2]
2433 - filter.shape[2]
2434 - (filter.shape[2] - 1) * (dilations[1] - 1)
2435 + padding[2]
2436 + padding[3]
2437 ) // strides[1] + 1
Eric Kunzee5e26762020-10-13 16:11:07 -07002438
2439 if h <= 0 or w <= 0:
2440 # Invalid test parameters?
2441 h = 0
2442 w = 0
Kevin Cheng550ccc52021-03-03 11:21:43 -08002443 ser.setExpectedFailure(True, "Invalid combination of conv2d parameters")
Eric Kunzee5e26762020-10-13 16:11:07 -07002444
2445 ofm_shape = [ifm.shape[0], h, w, filter.shape[0]]
2446
Kevin Cheng3a478572021-01-22 17:21:02 -08002447 if ifm.dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07002448 out_dtype = DType.INT32
2449 elif ifm.dtype == DType.INT16:
2450 out_dtype = DType.INT48
2451 elif ifm.dtype == DType.FLOAT:
2452 out_dtype = DType.FLOAT
2453 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002454 raise Exception("Unsupported input dtype: {}".format(ifm.dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -07002455
Jared Smolens2a76ad22021-03-04 11:18:54 -08002456 if ifm.dtype == DType.INT16:
2457 ser.setExpectedFailure(True, "INT16 support is in progress")
2458
Kevin Cheng550ccc52021-03-03 11:21:43 -08002459 return ser.addOutput(ofm_shape, out_dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002460
2461 @staticmethod
2462 def depthwiseConv2dOp(ser, ifm, filter, strides, padding, dilations):
2463 # IFM: NHWC
2464 # Filter: HWCM
2465 # OFM: NHW C*M
Kevin Cheng550ccc52021-03-03 11:21:43 -08002466 h = (
2467 ifm.shape[1]
2468 - filter.shape[0]
2469 - (filter.shape[0] - 1) * (dilations[0] - 1)
2470 + padding[0]
2471 + padding[1]
2472 ) // strides[0] + 1
Eric Kunzee5e26762020-10-13 16:11:07 -07002473
Kevin Cheng550ccc52021-03-03 11:21:43 -08002474 w = (
2475 ifm.shape[2]
2476 - filter.shape[1]
2477 - (filter.shape[1] - 1) * (dilations[1] - 1)
2478 + padding[2]
2479 + padding[3]
2480 ) // strides[1] + 1
Eric Kunzee5e26762020-10-13 16:11:07 -07002481
2482 if h <= 0 or w <= 0:
2483 # Invalid test parameters?
2484 h = 0
2485 w = 0
Kevin Cheng550ccc52021-03-03 11:21:43 -08002486 ser.setExpectedFailure(True, "Invalid combination of conv2d parameters")
Eric Kunzee5e26762020-10-13 16:11:07 -07002487
2488 ofm_shape = [ifm.shape[0], h, w, filter.shape[2] * filter.shape[3]]
2489
Kevin Cheng3a478572021-01-22 17:21:02 -08002490 if ifm.dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07002491 out_dtype = DType.INT32
2492 elif ifm.dtype == DType.INT16:
2493 out_dtype = DType.INT48
2494 elif ifm.dtype == DType.FLOAT:
2495 out_dtype = DType.FLOAT
2496 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002497 raise Exception("Unsupported input dtype: {}".format(ifm.dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -07002498
Jared Smolens2a76ad22021-03-04 11:18:54 -08002499 if ifm.dtype == DType.INT16:
2500 ser.setExpectedFailure(True, "INT16 support is in progress")
2501
Kevin Cheng550ccc52021-03-03 11:21:43 -08002502 return ser.addOutput(ofm_shape, out_dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002503
2504 @staticmethod
2505 def pool2dOp(ser, ifm, kernel, stride, pad):
2506 # input: NHWC
2507 h = (ifm.shape[1] + pad[0] + pad[1] + stride[0] - kernel[0]) // stride[0]
2508 w = (ifm.shape[2] + pad[2] + pad[3] + stride[1] - kernel[1]) // stride[1]
2509
2510 if h <= 0 or w <= 0:
2511 # Invalid test parameters?
2512 h = 0
2513 w = 0
Kevin Cheng550ccc52021-03-03 11:21:43 -08002514 ser.setExpectedFailure(True, "Invalid combination of pooling parameters")
Eric Kunzee5e26762020-10-13 16:11:07 -07002515
2516 ofm_shape = [ifm.shape[0], h, w, ifm.shape[3]]
Kevin Cheng550ccc52021-03-03 11:21:43 -08002517 return ser.addOutput(ofm_shape, ifm.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002518
2519 @staticmethod
2520 def fullyConnectedOp(ser, input, filter):
2521 # input: N, IC
2522 # filter: OC, IC
2523 # output: N, OC
2524
2525 output_shape = [input.shape[0], filter.shape[0]]
2526
Kevin Cheng3a478572021-01-22 17:21:02 -08002527 if input.dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07002528 out_dtype = DType.INT32
2529 elif input.dtype == DType.INT16:
2530 out_dtype = DType.INT48
2531 elif input.dtype == DType.FLOAT:
2532 out_dtype = DType.FLOAT
2533 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002534 raise Exception("Unsupported input dtype: {}".format(input.dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -07002535
Jared Smolens2a76ad22021-03-04 11:18:54 -08002536 if input.dtype == DType.INT16:
2537 ser.setExpectedFailure(True, "INT16 support is in progress")
2538
Kevin Cheng550ccc52021-03-03 11:21:43 -08002539 return ser.addOutput(output_shape, out_dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002540
2541 @staticmethod
2542 def matmulOp(ser, a, b):
2543 # a: M, K
2544 # b: K, N
2545 # out: M, N
2546
2547 output_shape = [a.shape[0], b.shape[1]]
2548
Kevin Cheng3a478572021-01-22 17:21:02 -08002549 if a.dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07002550 out_dtype = DType.INT32
2551 elif a.dtype == DType.INT16:
2552 out_dtype = DType.INT48
2553 elif a.dtype == DType.FLOAT:
2554 out_dtype = DType.FLOAT
2555 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002556 raise Exception("UNsupported input dtype for matmul: {}".format(a.dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -07002557
Kevin Cheng550ccc52021-03-03 11:21:43 -08002558 return ser.addOutput(output_shape, out_dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002559
2560 @staticmethod
2561 def concatOp(ser, a, b, axis):
2562
2563 output_shape = a.shape.copy()
2564 output_shape[axis] = a.shape[axis] + b.shape[axis]
2565
Kevin Cheng550ccc52021-03-03 11:21:43 -08002566 return ser.addOutput(output_shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002567
2568 @staticmethod
2569 def padOp(ser, a, padding):
2570
2571 output_shape = a.shape.copy()
2572
2573 for i in range(len(output_shape)):
2574 output_shape[i] = padding[i][0] + padding[i][1] + output_shape[i]
2575
Kevin Cheng550ccc52021-03-03 11:21:43 -08002576 return ser.addOutput(output_shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002577
2578 @staticmethod
2579 def reshapeOp(ser, a, shape):
2580 output_shape = shape.copy()
2581
2582 totalElements = 1
2583 for i in a.shape:
2584 totalElements *= i
2585
2586 # If there are any -1 elements, figure out what that dimension must be
2587 totalOutputElements = 1
2588 for i in output_shape:
2589 if i != -1:
2590 totalOutputElements *= i
2591
2592 # And fill it in
2593 for i in range(len(output_shape)):
2594 if output_shape[i] == -1:
2595 output_shape[i] = totalElements // totalOutputElements
2596
Kevin Cheng550ccc52021-03-03 11:21:43 -08002597 return ser.addOutput(output_shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002598
2599 @staticmethod
2600 def sliceOp(ser, a, begin, size):
2601
2602 output_shape = size.copy()
Kevin Cheng550ccc52021-03-03 11:21:43 -08002603 return ser.addOutput(output_shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002604
2605 @staticmethod
2606 def tileOp(ser, a, multiples):
2607
2608 output_shape = a.shape.copy()
Kevin Cheng550ccc52021-03-03 11:21:43 -08002609 assert len(multiples) == len(output_shape)
Eric Kunzee5e26762020-10-13 16:11:07 -07002610
2611 for i in range(len(output_shape)):
2612 output_shape[i] = a.shape[i] * multiples[i]
2613
Kevin Cheng550ccc52021-03-03 11:21:43 -08002614 return ser.addOutput(output_shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002615
2616 @staticmethod
2617 def transposeOp(ser, a, perms):
2618 output_shape = a.shape.copy()
Kevin Cheng550ccc52021-03-03 11:21:43 -08002619 assert len(perms) == len(output_shape)
Eric Kunzee5e26762020-10-13 16:11:07 -07002620
2621 for i in range(len(output_shape)):
2622 output_shape[i] = a.shape[perms[i]]
2623
Kevin Cheng550ccc52021-03-03 11:21:43 -08002624 return ser.addOutput(output_shape, a.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002625
2626 @staticmethod
Kevin Cheng77d0f762020-11-24 10:26:32 -08002627 def gatherOp(ser, values, indices):
2628 assert len(values.shape) == 3
2629 assert len(indices.shape) == 2
2630 assert values.shape[0] == indices.shape[0]
Eric Kunzee5e26762020-10-13 16:11:07 -07002631
Kevin Cheng77d0f762020-11-24 10:26:32 -08002632 output_shape = [values.shape[0], indices.shape[1], values.shape[2]]
2633
Kevin Cheng550ccc52021-03-03 11:21:43 -08002634 return ser.addOutput(output_shape, values.dtype)
Kevin Cheng77d0f762020-11-24 10:26:32 -08002635
2636 @staticmethod
2637 def scatterOp(ser, values_in, indices, input):
2638 assert len(values_in.shape) == 3
2639 assert len(indices.shape) == 2
2640 assert len(input.shape) == 3
Kevin Cheng550ccc52021-03-03 11:21:43 -08002641 assert values_in.shape[0] == indices.shape[0] # N
2642 assert input.shape[1] == indices.shape[1] # W
2643 assert values_in.shape[2] == input.shape[2] # C
Kevin Cheng77d0f762020-11-24 10:26:32 -08002644
2645 output_shape = values_in.shape
2646
Kevin Cheng550ccc52021-03-03 11:21:43 -08002647 return ser.addOutput(output_shape, values_in.dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002648
2649 @staticmethod
2650 def tableOp(ser, input, table):
2651 # Same shape as the input, but with the type of the table.
Kevin Cheng550ccc52021-03-03 11:21:43 -08002652 return ser.addOutput(input.shape, DType.INT32)
Eric Kunzee5e26762020-10-13 16:11:07 -07002653
2654 @staticmethod
Kevin Cheng550ccc52021-03-03 11:21:43 -08002655 def resizeOp(
2656 ser,
2657 input,
2658 mode,
2659 stride,
2660 offset,
2661 shift,
2662 stride_fp,
2663 offset_fp,
2664 output_dims,
2665 input_dtype,
2666 output_dtype,
2667 ):
Eric Kunzee5e26762020-10-13 16:11:07 -07002668
2669 output_dims = [input.shape[0], output_dims[0], output_dims[1], input.shape[3]]
2670
Kevin Cheng77d0f762020-11-24 10:26:32 -08002671 if input_dtype == DType.FLOAT:
2672 if stride_fp[0] <= 0 or stride_fp[1] <= 0:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002673 ser.setExpectedFailure(True, "Negative or zero stride")
Kevin Cheng77d0f762020-11-24 10:26:32 -08002674 else:
2675 if stride[0] <= 0 or stride[1] <= 0:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002676 ser.setExpectedFailure(True, "Negative or zero stride")
Eric Kunzee5e26762020-10-13 16:11:07 -07002677
Kevin Chengaee1fac2020-11-11 13:54:06 -08002678 if mode == ResizeMode.BILINEAR:
2679 if input_dtype == DType.INT8:
2680 if output_dtype != DType.INT32:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002681 ser.setExpectedFailure(True, "Invalid output data type")
Kevin Chengaee1fac2020-11-11 13:54:06 -08002682 elif input_dtype == DType.INT16:
2683 if output_dtype != DType.INT48:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002684 ser.setexpectedfailure(true, "Invalid output data type")
Kevin Cheng77d0f762020-11-24 10:26:32 -08002685 elif input_dtype == DType.FLOAT:
2686 if output_dtype != DType.FLOAT:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002687 ser.setexpectedfailure(true, "Invalid output data type")
Kevin Chengaee1fac2020-11-11 13:54:06 -08002688 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002689 ser.setexpectedfailure(true, "Invalid input data type")
Kevin Chengaee1fac2020-11-11 13:54:06 -08002690
2691 elif mode == ResizeMode.NEAREST:
2692 if input_dtype == DType.INT8:
2693 if output_dtype != DType.INT8:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002694 ser.setExpectedFailure(True, "Invalid output data type")
Kevin Chengaee1fac2020-11-11 13:54:06 -08002695 elif input_dtype == DType.INT16:
2696 if output_dtype != DType.INT16:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002697 ser.setexpectedfailure(true, "Invalid output data type")
Kevin Cheng77d0f762020-11-24 10:26:32 -08002698 elif input_dtype == DType.FLOAT:
2699 if output_dtype != DType.FLOAT:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002700 ser.setexpectedfailure(true, "Invalid output data type")
Kevin Chengaee1fac2020-11-11 13:54:06 -08002701 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002702 ser.setexpectedfailure(true, "Invalid input data type")
Kevin Chengaee1fac2020-11-11 13:54:06 -08002703
2704 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002705 ser.setexpectedfailure(true, "Invalid resize mode")
Kevin Chengaee1fac2020-11-11 13:54:06 -08002706
Kevin Cheng550ccc52021-03-03 11:21:43 -08002707 return ser.addOutput(output_dims, output_dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002708
2709 @staticmethod
2710 def typeConversionOp(ser, val, out_dtype):
Kevin Cheng550ccc52021-03-03 11:21:43 -08002711 return ser.addOutput(val.shape, out_dtype)
Eric Kunzee5e26762020-10-13 16:11:07 -07002712
2713 @staticmethod
2714 def transposeConv2DOp(ser, ifm, output_shape):
Kevin Cheng3a478572021-01-22 17:21:02 -08002715 if ifm.dtype == DType.INT8:
Eric Kunzee5e26762020-10-13 16:11:07 -07002716 out_dtype = DType.INT32
2717 elif ifm.dtype == DType.INT16:
2718 out_dtype = DType.INT48
2719 elif ifm.dtype == DType.FLOAT:
2720 out_dtype = DType.FLOAT
2721 else:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002722 raise Exception("Unsupported input dtype: {}".format(ifm.dtype))
Eric Kunzee5e26762020-10-13 16:11:07 -07002723
2724 if output_shape[1] <= 0 or output_shape[2] <= 0:
Kevin Cheng550ccc52021-03-03 11:21:43 -08002725 ser.setExpectedFailure(True, "Negative output shape")
Eric Kunzee5e26762020-10-13 16:11:07 -07002726
Jared Smolens2a76ad22021-03-04 11:18:54 -08002727 if ifm.dtype == DType.INT16:
2728 ser.setExpectedFailure(True, "INT16 support is in progress")
2729
Kevin Cheng550ccc52021-03-03 11:21:43 -08002730 return ser.addOutput(output_shape, out_dtype)