aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Johnson <jeremy.johnson@arm.com>2022-06-13 17:48:09 +0100
committerEric Kunze <eric.kunze@arm.com>2022-08-11 16:02:15 -0700
commita0e03f3f9a44d79367e2624f571009e4657c775a (patch)
tree60bff664a080831e0376b1fde12bad344b9008d1
parentf732609a51630c98bc3f448937988fbcf20dc854 (diff)
downloadreference_model-a0e03f3f9a44d79367e2624f571009e4657c775a.tar.gz
Update RESIZE operator in test generator for spec updates
* Add common screen aspect ratios with borders to random pool of tests * Add up/downscaling to random pool Signed-off-by: Jeremy Johnson <jeremy.johnson@arm.com> Change-Id: Iee8e3f5ed6bd5c941816474df20a7fd433646d6b Signed-off-by: James Ward <james.ward@arm.com>
-rw-r--r--verif/conformance/test_select.py5
-rw-r--r--verif/conformance/tosa_base_profile_ops_info.json4
-rw-r--r--verif/generator/tosa_arg_gen.py338
-rw-r--r--verif/generator/tosa_error_if.py381
-rw-r--r--verif/generator/tosa_test_gen.py130
-rw-r--r--verif/generator/tosa_utils.py3
-rw-r--r--verif/generator/tosa_verif_build_tests.py11
7 files changed, 489 insertions, 383 deletions
diff --git a/verif/conformance/test_select.py b/verif/conformance/test_select.py
index 1013b6e..8b60fbb 100644
--- a/verif/conformance/test_select.py
+++ b/verif/conformance/test_select.py
@@ -616,11 +616,10 @@ class ResizeOperator(Operator):
"shape",
"type",
"mode",
- "shift",
- "output_dims",
"output_type",
- "stride",
+ "scale",
"offset",
+ "border",
]
diff --git a/verif/conformance/tosa_base_profile_ops_info.json b/verif/conformance/tosa_base_profile_ops_info.json
index b9f392c..d05b881 100644
--- a/verif/conformance/tosa_base_profile_ops_info.json
+++ b/verif/conformance/tosa_base_profile_ops_info.json
@@ -2000,7 +2000,9 @@
"--target-shape",
"1,3,16383,1",
"--target-dtype",
- "int8"
+ "int8",
+ "--max-resize-output-dim",
+ "9500"
]
],
"params": {},
diff --git a/verif/generator/tosa_arg_gen.py b/verif/generator/tosa_arg_gen.py
index 8e00fab..2181735 100644
--- a/verif/generator/tosa_arg_gen.py
+++ b/verif/generator/tosa_arg_gen.py
@@ -6,6 +6,7 @@ import math
import numpy as np
from generator.tosa_error_if import ErrorIf
from generator.tosa_error_if import TosaErrorIfArgGen
+from generator.tosa_utils import MAX_RESIZE_DIMENSION
from serializer.tosa_serializer import DTypeNames
from tosa.DType import DType
from tosa.Op import Op
@@ -1609,10 +1610,107 @@ class TosaArgGen:
@staticmethod
def agResize(testGen, opName, shapeList, dtype, error_name=None):
arg_list = []
-
ifm_shape = shapeList[0]
- for mode in [ResizeMode.NEAREST, ResizeMode.BILINEAR]:
+ def get_aspect_ratio_resize_params():
+ common_aspect_ratios = ((3, 2), (16, 9), (4, 3))
+ aspect_ratio = testGen.rng.choice(common_aspect_ratios)
+ invert = testGen.rng.choice((False, True))
+ letterbox = testGen.rng.choice((False, True))
+
+ scale_y_n = aspect_ratio[0] if invert else aspect_ratio[1]
+ scale_x_n = aspect_ratio[1] if invert else aspect_ratio[0]
+ scale_y_d = scale_x_d = 1
+ offset_x = offset_y = 0
+
+ if letterbox:
+ max_border = scale_y_n
+ border_y = testGen.randInt(low=0, high=max_border)
+ border_x = 0
+ else:
+ # Pillarboxing
+ border_y = 0
+ max_border = scale_x_n
+ border_x = testGen.randInt(low=0, high=max_border)
+
+ scale = (scale_y_n, scale_y_d, scale_x_n, scale_x_d)
+ offset = (offset_y, offset_x)
+ border = (border_y, border_x)
+
+ return scale, offset, border
+
+ def get_upscale_downscale_params():
+ valid_params = False
+ while not valid_params:
+ upscale = testGen.rng.choice((False, True))
+
+ # True if sampling begins from (0,0). Otherwise (-0.5,-0.5)
+ origin_sampling = testGen.rng.choice((False, True))
+
+ if upscale:
+ shift = testGen.randInt(low=1, high=4)
+ scale_x_d = scale_y_d = 1
+ scale_x_n = scale_y_n = (
+ 1 << shift if origin_sampling else 2 << shift
+ )
+ border_x = border_y = 0 if origin_sampling else (1 << shift) - 1
+ offset_x = offset_y = 0 if origin_sampling else -(1 << shift) + 1
+ else:
+ scale_x_n = 1
+ scale_y_n = 1
+
+ # Return list of valid scale_*_d values (max value 4) given input dim shape
+ def get_valid_denom(ifm_dim):
+ return [x for x in range(1, 5) if ifm_dim % x == 1]
+
+ # Generate list of valid downscale values and choose one randomly
+ valid_scale_y_ds = get_valid_denom(ifm_shape[1])
+ valid_scale_x_ds = get_valid_denom(ifm_shape[2])
+
+ if not valid_scale_y_ds and not valid_scale_x_ds:
+ # Bad parameters, skip
+ continue
+
+ if not valid_scale_y_ds:
+ scale_y_d = 1
+ else:
+ scale_y_d = testGen.rng.choice(valid_scale_y_ds)
+
+ if not valid_scale_x_ds:
+ scale_x_d = 1
+ else:
+ scale_x_d = testGen.rng.choice(valid_scale_x_ds)
+
+ border_x = border_y = 0
+ offset_y = testGen.randInt(0, 16 * scale_y_n)
+ offset_x = testGen.randInt(0, 16 * scale_x_n)
+ valid_params = True
+
+ scale = (scale_y_n, scale_y_d, scale_x_n, scale_x_d)
+ offset = (offset_y, offset_x)
+ border = (border_y, border_x)
+ return scale, offset, border
+
+ def get_rand_params():
+ # Scale
+ scale_y_n = testGen.randInt(low=1, high=(1 << 11))
+ scale_x_n = testGen.randInt(low=1, high=(1 << 11))
+
+ scale_y_d = testGen.randInt(low=1, high=(16 * scale_y_n))
+ scale_x_d = testGen.randInt(low=1, high=(16 * scale_x_n))
+
+ # Offsets and border within the scale
+ offset_y = testGen.randInt(low=-scale_y_n, high=(16 * scale_y_n))
+ offset_x = testGen.randInt(low=-scale_x_n, high=(16 * scale_x_n))
+ border_y = testGen.randInt(low=(-16 * scale_y_n), high=scale_y_n)
+ border_x = testGen.randInt(low=(-16 * scale_x_n), high=scale_x_n)
+
+ scale = (scale_y_n, scale_y_d, scale_x_n, scale_x_d)
+ offset = (offset_y, offset_x)
+ border = (border_y, border_x)
+ return scale, offset, border
+
+ for mode in [ResizeMode.NEAREST, ResizeMode.BILINEAR]:
# Exclude illegal {mode, type} configurations. Pick legal output types
if mode == ResizeMode.NEAREST and dtype == DType.INT8:
outputDTypeList = [DType.INT8]
@@ -1631,114 +1729,98 @@ class TosaArgGen:
else:
continue
+ arg_str = "mode{}_out{}_sc{}x{}x{}x{}_off{}x{}_bor{}x{}"
+
for outputDType in outputDTypeList:
- for perm in range(testGen.args.num_rand_permutations):
- # Randomly generate legal output dimensions and shift
- # and then compute the stride and offset based on them
- # A output_dim of 1 will cause offset to exceed allowed range
- # so minimum value 2 produced below
- output_dims = [testGen.randInt(1) + 1, testGen.randInt(1) + 1]
- while (float(ifm_shape[1]) / float(output_dims[0])) >= 16:
- output_dims[0] += 1
- while (float(ifm_shape[2]) / float(output_dims[1])) >= 16:
- output_dims[1] += 1
-
- in_center_h = (ifm_shape[1] - 1) / 2.0
- in_center_w = (ifm_shape[2] - 1) / 2.0
- out_center_h = (output_dims[0] - 1) / 2.0
- out_center_w = (output_dims[1] - 1) / 2.0
-
- fp_stride_y = float(ifm_shape[1]) / float(output_dims[0])
- fp_stride_x = float(ifm_shape[2]) / float(output_dims[1])
- fp_offset_y = in_center_h - fp_stride_y * out_center_h
- fp_offset_x = in_center_w - fp_stride_x * out_center_w
-
- if outputDType == DType.FLOAT:
- float_op = True
- arg_str = (
- "mode{}_shift{}_odim{}x{}_out{}"
- "_st{:.2f}x{:.2f}_off{:.2f}x{:.2f}"
+ perm = 0
+ while perm < testGen.args.num_rand_permutations:
+ # Random choice of type of params we are testing
+ _rnd_param_fn = testGen.rng.choice(
+ (
+ get_rand_params,
+ get_upscale_downscale_params,
+ get_aspect_ratio_resize_params,
)
- shift = 0
- stride = [0, 0]
- offset = [0, 0]
- stride_fp = [fp_stride_y, fp_stride_x]
- offset_fp = [fp_offset_y, fp_offset_x]
+ )
+ scale, offset, border = _rnd_param_fn()
- else:
- float_op = False
- arg_str = "mode{}_shift{}_odim{}x{}_out{}_st{}x{}_off{}x{}"
- shift = testGen.randInt(1, 12)
- # Now search for a shift value (1 to 11) that will produce
- # a valid and predictable resize operation
- count = 0
- while count < 12:
- unit = float(1 << shift)
- stride_y = int(round(fp_stride_y * unit))
- stride_x = int(round(fp_stride_x * unit))
- offset_y = int(round(fp_offset_y * unit))
- offset_x = int(round(fp_offset_x * unit))
-
- if (
- stride_y <= 0
- or stride_x <= 0
- or stride_y >= (16 << shift)
- or stride_x >= (16 << shift)
- or offset_y >= (16 << shift)
- or offset_x >= (16 << shift)
- or offset_y <= (-16 << shift)
- or offset_x <= (-16 << shift)
- ):
- # Change the shift value and check again
- count += 1
- shift = (shift % 11) + 1
- continue
-
- def RESIZE_REQUIRE_CALC(
- length_in, length_out, stride, offset, shift
- ):
- # Perform the pseudo loop to look for out of bounds
- for pos in range(0, length_out):
- a = pos * stride + offset
- ia = a >> shift
- ia0 = max(ia, 0)
- ia1 = min(ia + 1, length_in - 1)
- if ia0 > ia1:
- # Found a problem value
- break
- return ia0, ia1
-
- iy0, iy1 = RESIZE_REQUIRE_CALC(
- ifm_shape[1], output_dims[0], stride_y, offset_y, shift
- )
- ix0, ix1 = RESIZE_REQUIRE_CALC(
- ifm_shape[2], output_dims[1], stride_x, offset_x, shift
- )
- if ix0 > ix1 or iy0 > iy1:
- # Change the shift value and check again
- count += 1
- shift = (shift % 11) + 1
- continue
- break
-
- if count >= 12:
- # Couldn't find a good set of values for this test, skip it
+ # Expand params for bounds-checking
+ (scale_y_n, scale_y_d, scale_x_n, scale_x_d) = scale
+ (offset_y, offset_x) = offset
+ (border_y, border_x) = border
+
+ # Make sure output dimensions OH and OW are integers
+ partial_output_y = (
+ (ifm_shape[1] - 1) * scale_y_n - offset_y + border_y
+ )
+ partial_output_x = (
+ (ifm_shape[2] - 1) * scale_x_n - offset_x + border_x
+ )
+ if error_name == ErrorIf.ResizeOutputShapeNonInteger:
+ if (
+ partial_output_y % scale_y_d == 0
+ and partial_output_x % scale_x_d == 0
+ ):
+ # Skip this test as it doesn't produce NonInteger output
+ perm += 1
continue
+ else:
+ while partial_output_y % scale_y_d != 0:
+ scale_y_d -= 1
+ while partial_output_x % scale_x_d != 0:
+ scale_x_d -= 1
+
+ output_y = partial_output_y // scale_y_d + 1
+ output_x = partial_output_x // scale_x_d + 1
- stride = [stride_y, stride_x]
- offset = [offset_y, offset_x]
+ if (
+ output_y >= testGen.args.max_resize_output_dim
+ or output_x >= testGen.args.max_resize_output_dim
+ ) and error_name is None:
+ # Skip positive test if output dim will be too high
+ # Avoid high test latency and OOM issues
+ perm += 1
+ continue
- stride_fp = [0.0, 0.0]
- offset_fp = [0.0, 0.0]
+ if (
+ output_y <= 0
+ or output_y >= MAX_RESIZE_DIMENSION
+ or output_x <= 0
+ or output_x >= MAX_RESIZE_DIMENSION
+ ):
+ # Output dimensions out of scope
+ if error_name is not None and perm > 0:
+ # As long as we have one ERROR_IF test, don't worry
+ # about creating all the other permutations
+ perm += 1
+ continue
+
+ if error_name == ErrorIf.ResizeOutputShapeMismatch and (
+ (
+ output_y + scale_y_d >= MAX_RESIZE_DIMENSION
+ and output_y - scale_y_d < 1
+ )
+ or (
+ output_x + scale_x_d >= MAX_RESIZE_DIMENSION
+ and output_x - scale_x_d < 1
+ )
+ ):
+ # Can't create a negative test with these params as it
+ # will create invalid output size
+ if perm > 0:
+ perm += 1
+ continue
+
+ scale = [scale_y_n, scale_y_d, scale_x_n, scale_x_d]
+ offset = [offset_y, offset_x]
+ border = [border_y, border_x]
# Common for all data types
if error_name is not None:
(
- shift,
- stride,
- stride_fp,
+ scale,
offset,
- offset_fp,
+ border,
outputDTypeNew,
) = TosaErrorIfArgGen.eiResizeErrorIf(
testGen,
@@ -1747,42 +1829,42 @@ class TosaArgGen:
dtype,
shapeList,
outputDType,
- shift,
- stride,
- stride_fp,
+ scale,
offset,
- offset_fp,
+ border,
)
else:
outputDTypeNew = outputDType
- arg_list.append(
- (
- arg_str.format(
- "N" if mode == ResizeMode.NEAREST else "B",
- shift,
- output_dims[0],
- output_dims[1],
- testGen.typeStr(outputDTypeNew),
- stride_fp[0] if float_op else stride[0],
- stride_fp[1] if float_op else stride[1],
- offset_fp[0] if float_op else offset[0],
- offset_fp[1] if float_op else offset[1],
- ),
- [
- mode,
- stride,
- offset,
- shift,
- stride_fp,
- offset_fp,
- output_dims,
- dtype,
- outputDTypeNew,
- ],
- )
+ arg_to_append = (
+ arg_str.format(
+ "N" if mode == ResizeMode.NEAREST else "B",
+ testGen.typeStr(outputDTypeNew),
+ scale[0],
+ scale[1],
+ scale[2],
+ scale[3],
+ offset[0],
+ offset[1],
+ border[0],
+ border[1],
+ ),
+ [
+ mode,
+ scale,
+ offset,
+ border,
+ dtype,
+ outputDTypeNew,
+ ],
)
+ if arg_to_append in arg_list:
+ # Skip already generated test params
+ continue
+ # Valid permutation
+ perm += 1
+ arg_list.append(arg_to_append)
return arg_list
@staticmethod
diff --git a/verif/generator/tosa_error_if.py b/verif/generator/tosa_error_if.py
index b331a42..1651d95 100644
--- a/verif/generator/tosa_error_if.py
+++ b/verif/generator/tosa_error_if.py
@@ -1,6 +1,7 @@
# Copyright (c) 2021-2022, ARM Limited.
# SPDX-License-Identifier: Apache-2.0
import numpy as np
+from generator.tosa_utils import MAX_RESIZE_DIMENSION
from generator.tosa_utils import product
from generator.tosa_utils import usableDTypes
from generator.tosa_utils import valueToName
@@ -11,14 +12,15 @@ from tosa.ResizeMode import ResizeMode
class ErrorIf(object):
MaxDimExceeded = "MaxDimExceeded"
- StrideSmallerEqualZero = "StrideSmallerEqualZero"
- StrideLargerEqualMax = "StrideLargerEqualMax"
- StrideLargerDimension = "StrideLargerDimension"
- OffsetSmallerEqualMin = "OffsetSmallerEqualMin"
+ ScaleSmallerEqualZero = "ScaleSmallerEqualZero"
+ ScaleNLargerMax = "ScaleNLargerMax"
+ ScaleDLargerMax = "ScaleDLargerMax"
+ OffsetSmallerMin = "OffsetSmallerMin"
OffsetLargerEqualMax = "OffsetLargerEqualMax"
- ShiftNotZero = "ShiftNotZero"
- ShiftSmallerOne = "ShiftSmallerOne"
- ShiftLargerEleven = "ShiftLargerEleven"
+ BorderSmallerMin = "BorderSmallerMin"
+ BorderLargerEqualMax = "BorderLargerEqualMax"
+ ResizeOutputShapeMismatch = "ResizeOutputShapeMismatch"
+ ResizeOutputShapeNonInteger = "ResizeOutputShapeNonInteger"
WrongInputType = "WrongInputType"
WrongOutputType = "WrongOutputType"
WrongInputList = "WrongInputList"
@@ -81,63 +83,33 @@ class TosaErrorIfArgGen:
dtype,
shapeList,
outputDType,
- shift,
- stride,
- stride_fp,
+ scale,
offset,
- offset_fp,
+ border,
):
-
- if outputDType == DType.FLOAT:
- if error_name == ErrorIf.StrideSmallerEqualZero:
- stride_fp = testGen.rng.random(size=[2]) - 2
- elif error_name == ErrorIf.ShiftNotZero:
- shift = testGen.rng.integers(1, 5)
- elif error_name == ErrorIf.StrideLargerDimension:
- shape = shapeList[0]
- transform_height = testGen.rng.choice([False, True])
- if transform_height:
- stride_fp[0] = shape[1] + testGen.rng.integers(1, 10)
- else:
- stride_fp[1] = shape[2] + testGen.rng.integers(1, 10)
- else:
- if error_name == ErrorIf.StrideSmallerEqualZero:
- stride = np.int16(testGen.rng.integers(-1, 1, size=[2]))
- elif error_name == ErrorIf.ShiftSmallerOne:
- shift = testGen.rng.integers(-3, 1)
- if shift <= 0:
- stride = [
- (16 >> -shift) - 1,
- (16 >> -shift) - 1,
- ] # avoids other ERROR_IF checks
- offset = [
- (16 >> -shift) - 1,
- (16 >> -shift) - 1,
- ] # avoids other ERROR_IF checks
- else:
- stride = [
- (16 << shift) - 1,
- (16 << shift) - 1,
- ] # avoids other ERROR_IF checks
- offset = [
- (16 << shift) - 1,
- (16 << shift) - 1,
- ] # avoids other ERROR_IF checks
- elif error_name == ErrorIf.ShiftLargerEleven:
- shift = np.int16(testGen.rng.integers(12, 15))
- elif error_name == ErrorIf.StrideLargerDimension:
- shape = shapeList[0]
- transform_height = testGen.rng.choice([False, True])
- if transform_height:
- stride[0] = shape[1] + testGen.rng.integers(1, 10)
- else:
- stride[1] = shape[2] + testGen.rng.integers(1, 10)
- elif error_name == ErrorIf.StrideLargerEqualMax:
- stride = [(16 << shift) + 1, (16 << shift) + 1]
- elif error_name == ErrorIf.OffsetLargerEqualMax:
- offset = [(16 << shift) + 1, (16 << shift) + 1]
- elif error_name == ErrorIf.OffsetSmallerEqualMin:
- offset = [(-16 << shift) - 1, (-16 << shift) - 1]
+ if error_name == ErrorIf.ScaleSmallerEqualZero:
+ index = testGen.randInt(low=0, high=4)
+ scale[index] = testGen.rng.choice([-2, -1, 0])
+ elif error_name == ErrorIf.ScaleNLargerMax:
+ index = testGen.rng.choice([0, 2])
+ scale[index] = (1 << 11) + testGen.rng.choice([1, 2, 3])
+ elif error_name == ErrorIf.ScaleDLargerMax:
+ index = testGen.rng.choice([1, 3])
+ scale[index] = 16 * scale[index - 1] + testGen.rng.choice([0, 1, 2])
+
+ if error_name == ErrorIf.OffsetLargerEqualMax:
+ index = testGen.rng.choice([0, 1])
+ offset[index] = 16 * scale[index * 2] + testGen.rng.choice([0, 1, 2])
+ elif error_name == ErrorIf.OffsetSmallerMin:
+ index = testGen.rng.choice([0, 1])
+ offset[index] = -scale[index * 2] - testGen.rng.choice([1, 2, 3])
+
+ if error_name == ErrorIf.BorderLargerEqualMax:
+ index = testGen.rng.choice([0, 1])
+ border[index] = scale[index * 2] + testGen.rng.choice([0, 1, 2])
+ elif error_name == ErrorIf.BorderSmallerMin:
+ index = testGen.rng.choice([0, 1])
+ border[index] = -16 * scale[index * 2] - testGen.rng.choice([1, 2, 3])
if error_name == ErrorIf.WrongOutputType:
if mode == ResizeMode.NEAREST and dtype == DType.INT8:
@@ -182,7 +154,7 @@ class TosaErrorIfArgGen:
)
outputDType = testGen.rng.choice(a=incorrect_types)
- return shift, stride, stride_fp, offset, offset_fp, outputDType
+ return scale, offset, border, outputDType
@staticmethod
def eiPoolingErrorIf(testGen, error_name, stride, pad, kernel):
@@ -630,18 +602,16 @@ class TosaErrorValidator:
"shape": [[1, 16584, 5, 1], [1, 2, 16499, 4]],
}
error_result = False
- error_reason = (
- "At least one maximum dimension is greater than or equal to 16384"
- )
+ error_reason = f"At least one maximum dimension is greater than or equal to {MAX_RESIZE_DIMENSION}"
if check:
input_shape = kwargs["input_shape"]
- output_shape = kwargs["output_shape"] # Note this is just (OH, OW)
+ output_shape = kwargs["output_shape"]
if (
- (input_shape[1] >= 16384)
- or (input_shape[2] >= 16384)
- or (output_shape[0] >= 16384)
- or (output_shape[1] >= 16384)
+ (input_shape[1] >= MAX_RESIZE_DIMENSION)
+ or (input_shape[2] >= MAX_RESIZE_DIMENSION)
+ or (output_shape[1] >= MAX_RESIZE_DIMENSION)
+ or (output_shape[2] >= MAX_RESIZE_DIMENSION)
):
error_result = True
@@ -711,27 +681,16 @@ class TosaErrorValidator:
return info_dict
@staticmethod
- def evStrideSmallerEqualZero(check=False, **kwargs):
- error_name = ErrorIf.StrideSmallerEqualZero
+ def evScaleSmallerEqualZero(check=False, **kwargs):
+ error_name = ErrorIf.ScaleSmallerEqualZero
param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
- error_reason = "Stride value smaller than or equal zero"
+ error_reason = "Scale value smaller than or equal zero"
if check:
- input_dtype = kwargs["input_dtype"]
- output_dtype = kwargs["output_dtype"]
- if input_dtype != DType.FLOAT and output_dtype == DType.FLOAT:
- stride = kwargs["stride"] # Work around wrong input/output type tests
- elif output_dtype == DType.FLOAT:
- stride = kwargs["stride_fp"]
- elif input_dtype == DType.FLOAT and output_dtype != DType.FLOAT:
- stride = kwargs[
- "stride_fp"
- ] # Work around wrong input/output type tests
- else:
- stride = kwargs["stride"]
+ scale = kwargs["scale"]
- if min(stride) <= 0:
+ if min(scale) <= 0:
error_result = True
info_dict = {
@@ -743,25 +702,17 @@ class TosaErrorValidator:
return info_dict
@staticmethod
- def evStrideLargerEqualMax(check=False, **kwargs):
- error_name = ErrorIf.StrideLargerEqualMax
- param_reqs = {"rank": None, "dtype": [DType.INT8, DType.INT16], "shape": None}
+ def evScaleNLargerMax(check=False, **kwargs):
+ error_name = ErrorIf.ScaleNLargerMax
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
- error_reason = "Stride value larger than or equal to maximum value"
+ error_reason = "Scale N value larger than maximum value"
if check:
- shift = kwargs["shift"]
- input_dtype = kwargs["input_dtype"]
- stride = kwargs["stride"]
- if input_dtype in [DType.INT8, DType.INT16]:
- if shift >= 0 and (
- stride[0] >= (16 << shift) or stride[1] >= (16 << shift)
- ):
- error_result = True
- elif shift < 0 and (
- stride[0] >= (16 >> -shift) or stride[1] >= (16 >> -shift)
- ):
- error_result = True
+ scale = kwargs["scale"]
+
+ if scale[0] > (1 << 11) or scale[2] > (1 << 11):
+ error_result = True
info_dict = {
"error_name": error_name,
@@ -772,21 +723,17 @@ class TosaErrorValidator:
return info_dict
@staticmethod
- def evStrideLargerDimension(check=False, **kwargs):
- error_name = ErrorIf.StrideLargerDimension
- param_reqs = {"rank": None, "dtype": [DType.FLOAT], "shape": None}
+ def evScaleDLargerMax(check=False, **kwargs):
+ error_name = ErrorIf.ScaleDLargerMax
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
- error_reason = "Stride value larger than or equal to H/W dimension"
+ error_reason = "Scale D value larger than maximum value"
if check:
- shape = kwargs["input_shape"]
- input_dtype = kwargs["input_dtype"]
- stride = kwargs["stride_fp"]
+ scale = kwargs["scale"]
- if (
- input_dtype == DType.FLOAT
- and (stride[0] > shape[1])
- or (stride[1] > shape[2])
+ if (scale[0] > 0 and scale[1] >= (16 * scale[0])) or (
+ scale[2] > 0 and scale[3] >= (16 * scale[2])
):
error_result = True
@@ -799,27 +746,19 @@ class TosaErrorValidator:
return info_dict
@staticmethod
- def evOffsetSmallerEqualMin(check=False, **kwargs):
- error_name = ErrorIf.OffsetSmallerEqualMin
- param_reqs = {"rank": None, "dtype": [DType.INT8, DType.INT16], "shape": None}
+ def evOffsetSmallerMin(check=False, **kwargs):
+ error_name = ErrorIf.OffsetSmallerMin
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
- error_reason = "Offset value smaller than or equal to minimum value"
+ error_reason = "Offset value smaller than minimum value"
if check:
- shift = kwargs["shift"]
- output_dtype = kwargs["output_dtype"]
- if output_dtype == DType.FLOAT:
- offset = kwargs["offset_fp"]
- else:
- offset = kwargs["offset"]
+ scale = kwargs["scale"]
+ offset = kwargs["offset"]
- if shift >= 0 and (
- offset[0] <= (-16 << shift) or offset[1] <= (-16 << shift)
- ):
+ if scale[0] > 0 and scale[0] <= (1 << 11) and (offset[0] < -scale[0]):
error_result = True
- elif shift < 0 and (
- offset[0] <= (-16 >> -shift) or offset[1] <= (-16 >> -shift)
- ):
+ elif scale[2] > 0 and scale[2] <= (1 << 11) and (offset[1] < -scale[2]):
error_result = True
info_dict = {
@@ -833,28 +772,18 @@ class TosaErrorValidator:
@staticmethod
def evOffsetLargerEqualMax(check=False, **kwargs):
error_name = ErrorIf.OffsetLargerEqualMax
- param_reqs = {"rank": None, "dtype": [DType.INT8, DType.INT16], "shape": None}
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
error_reason = "Offset value larger than or equal to maximum value"
if check:
- shift = kwargs["shift"]
- output_dtype = kwargs["output_dtype"]
- if output_dtype == DType.FLOAT:
- offset = kwargs["offset_fp"]
- else:
- offset = kwargs["offset"]
-
- if shift >= 0:
- if offset[0] >= (16 << shift) or offset[1] >= (16 << shift):
- error_result = True
+ scale = kwargs["scale"]
+ offset = kwargs["offset"]
- if shift >= 0 and (
- offset[0] >= (16 << shift) or offset[1] >= (16 << shift)
- ):
+ if scale[0] > 0 and scale[0] <= (1 << 11) and (offset[0] >= 16 * scale[0]):
error_result = True
- elif shift < 0 and (
- offset[0] >= (16 >> -shift) or offset[1] >= (16 >> -shift)
+ elif (
+ scale[2] > 0 and scale[2] <= (1 << 11) and (offset[1] >= 16 * scale[2])
):
error_result = True
@@ -867,20 +796,26 @@ class TosaErrorValidator:
return info_dict
@staticmethod
- def evShiftNotZero(check=False, **kwargs):
- error_name = ErrorIf.ShiftNotZero
- param_reqs = {"rank": None, "dtype": [DType.FLOAT], "shape": None}
+ def evBorderSmallerMin(check=False, **kwargs):
+ error_name = ErrorIf.BorderSmallerMin
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
- error_reason = "Shift value must be zero for float input"
+ error_reason = "Border value smaller than minimum value"
if check:
- shift = kwargs["shift"]
- input_dtype = kwargs["input_dtype"]
- output_dtype = kwargs["output_dtype"]
+ scale = kwargs["scale"]
+ border = kwargs["border"]
+
if (
- input_dtype == DType.FLOAT
- and output_dtype == DType.FLOAT
- and shift != 0
+ scale[0] > 0
+ and scale[0] <= (1 << 11)
+ and (border[0] < (-16 * scale[0]))
+ ):
+ error_result = True
+ elif (
+ scale[2] > 0
+ and scale[2] <= (1 << 11)
+ and (border[1] < (-16 * scale[2]))
):
error_result = True
@@ -893,17 +828,19 @@ class TosaErrorValidator:
return info_dict
@staticmethod
- def evShiftSmallerOne(check=False, **kwargs):
- error_name = ErrorIf.ShiftSmallerOne
- param_reqs = {"rank": None, "dtype": [DType.INT8, DType.INT16], "shape": None}
+ def evBorderLargerEqualMax(check=False, **kwargs):
+ error_name = ErrorIf.BorderLargerEqualMax
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
- error_reason = "Shift value smaller than one"
+ error_reason = "Border value larger than or equal to maximum value"
if check:
- shift = kwargs["shift"]
- input_dtype = kwargs["input_dtype"]
- output_dtype = kwargs["output_dtype"]
- if shift < 1 and input_dtype != DType.FLOAT and output_dtype != DType.FLOAT:
+ scale = kwargs["scale"]
+ border = kwargs["border"]
+
+ if scale[0] > 0 and scale[0] <= (1 << 11) and (border[0] >= scale[0]):
+ error_result = True
+ elif scale[2] > 0 and scale[2] <= (1 << 11) and (border[1] >= scale[2]):
error_result = True
info_dict = {
@@ -915,16 +852,90 @@ class TosaErrorValidator:
return info_dict
@staticmethod
- def evShiftLargerEleven(check=False, **kwargs):
- error_name = ErrorIf.ShiftLargerEleven
- param_reqs = {"rank": None, "dtype": [DType.INT8, DType.INT16], "shape": None}
+ def checkResizeParams(scale, offset, border):
+ return (
+ min(scale) > 0
+ and max(scale[0], scale[2]) <= (1 << 11)
+ and scale[1] < 16 * scale[0]
+ and scale[3] < 16 * scale[2]
+ and offset[0] >= -scale[0]
+ and offset[1] >= -scale[2]
+ and offset[0] < 16 * scale[0]
+ and offset[1] < 16 * scale[2]
+ and border[0] >= -16 * scale[0]
+ and border[1] >= -16 * scale[2]
+ and border[0] < scale[0]
+ and border[1] < scale[2]
+ )
+
+ @staticmethod
+ def evResizeOutputShapeMismatch(check=False, **kwargs):
+ error_name = ErrorIf.ResizeOutputShapeMismatch
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
error_result = False
- error_reason = "Shift value larger than eleven"
+ error_reason = (
+ "Mismatch between output shape provided and expected output shape"
+ )
if check:
- shift = kwargs["shift"]
- if shift > 11:
- error_result = True
+ input_shape = kwargs["input_shape"]
+ output_shape = kwargs["output_shape"]
+ scale = kwargs["scale"]
+ offset = kwargs["offset"]
+ border = kwargs["border"]
+
+ # Ensure parameters are valid
+ params_valid = TosaErrorValidator.checkResizeParams(scale, offset, border)
+
+ if (
+ params_valid
+ and max(output_shape) < MAX_RESIZE_DIMENSION
+ and max(input_shape) < MAX_RESIZE_DIMENSION
+ ):
+ output_y = (
+ (input_shape[1] - 1) * scale[0] - offset[0] + border[0]
+ ) // scale[1] + 1
+ output_x = (
+ (input_shape[2] - 1) * scale[2] - offset[1] + border[1]
+ ) // scale[3] + 1
+
+ if [output_y, output_x] != output_shape[1:-1]:
+ error_result = True
+
+ info_dict = {
+ "error_name": error_name,
+ "error_result": error_result,
+ "error_reason": error_reason,
+ "param_reqs": param_reqs,
+ }
+ return info_dict
+
+ @staticmethod
+ def evResizeOutputShapeNonInteger(check=False, **kwargs):
+ error_name = ErrorIf.ResizeOutputShapeNonInteger
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
+ error_result = False
+ error_reason = "Parameters do not yield exact integer output dimensions"
+
+ if check:
+ input_shape = kwargs["input_shape"]
+ scale = kwargs["scale"]
+ offset = kwargs["offset"]
+ border = kwargs["border"]
+
+ # Ensure parameters are valid
+ params_valid = TosaErrorValidator.checkResizeParams(scale, offset, border)
+
+ if params_valid:
+ remainder_y = (
+ (input_shape[1] - 1) * scale[0] - offset[0] + border[0]
+ ) % scale[1]
+ remainder_x = (
+ (input_shape[2] - 1) * scale[2] - offset[1] + border[1]
+ ) % scale[3]
+
+ if max(remainder_y, remainder_x) > 0:
+ error_result = True
info_dict = {
"error_name": error_name,
@@ -2191,45 +2202,25 @@ class TosaInvalidValidator:
input_dtype = kwargs["input_dtype"]
args = kwargs["args"]
mode = args[0]
- output_dtype = args[8]
+ output_dtype = args[5]
if mode == ResizeMode.BILINEAR:
# Invalid output data type / Invalid input datatype
return (
not (input_dtype == DType.INT8 and output_dtype == DType.INT32)
- or not (input_dtype == DType.INT16 and output_dtype == DType.INT48)
- or not (input_dtype == DType.FLOAT and output_dtype == DType.FLOAT)
- or (input_dtype not in [DType.INT8, DType.INT32, DType.FLOAT])
+ and not (input_dtype == DType.INT16 and output_dtype == DType.INT48)
+ and not (input_dtype == DType.FLOAT and output_dtype == DType.FLOAT)
)
elif mode == ResizeMode.NEAREST:
# Invalid output data type / Invalid input datatype
return (input_dtype != output_dtype) or (
- input_dtype not in [DType.INT8, DType.INT32, DType.FLOAT]
+ input_dtype not in [DType.INT8, DType.INT16, DType.FLOAT]
)
else:
# Invalid resize mode
return True
@staticmethod
- def ivBadStride(**kwargs):
- input_dtype = kwargs["input_dtype"]
- args = kwargs["args"]
- stride_x = args[1][0]
- stride_y = args[1][1]
- stride_fp_x = args[4][0]
- stride_fp_y = args[4][1]
-
- if input_dtype == DType.FLOAT:
- if stride_fp_x <= 0 or stride_fp_y <= 0:
- # Negative or zero stride
- return True
- else:
- if stride_x <= 0 or stride_y <= 0:
- # Negative or zero stride
- return True
- return False
-
- @staticmethod
def ivHeightWidthInvalid(**kwargs):
opName = kwargs["opName"]
diff --git a/verif/generator/tosa_test_gen.py b/verif/generator/tosa_test_gen.py
index 583e1ed..eeb0ac7 100644
--- a/verif/generator/tosa_test_gen.py
+++ b/verif/generator/tosa_test_gen.py
@@ -13,6 +13,7 @@ from generator.tosa_error_if import ErrorIf
from generator.tosa_error_if import TosaErrorIfArgGen
from generator.tosa_error_if import TosaErrorValidator
from generator.tosa_error_if import TosaInvalidValidator
+from generator.tosa_utils import MAX_RESIZE_DIMENSION
from generator.tosa_utils import usableDTypes
from tosa.DType import DType
from tosa.Op import Op
@@ -1450,12 +1451,9 @@ class TosaTestGen:
op,
input,
mode,
- stride,
+ scale,
offset,
- shift,
- stride_fp,
- offset_fp,
- output_dims,
+ border,
input_dtype,
output_dtype,
validator_fcns,
@@ -1466,12 +1464,9 @@ class TosaTestGen:
self.rng,
input,
mode,
- stride,
+ scale,
offset,
- shift,
- stride_fp,
- offset_fp,
- output_dims,
+ border,
input_dtype,
output_dtype,
error_name,
@@ -1492,15 +1487,13 @@ class TosaTestGen:
error_name,
op=op,
mode=mode,
- shift=shift,
+ scale=scale,
input_dtype=input_dtype,
output_dtype=output_dtype,
input_shape=input.shape,
- output_shape=output_dims,
+ output_shape=result_tens.shape,
offset=offset,
- offset_fp=offset_fp,
- stride=stride,
- stride_fp=stride_fp,
+ border=border,
input_list=input_list,
output_list=output_list,
result_tensor=result_tens,
@@ -1510,9 +1503,7 @@ class TosaTestGen:
attr = ts.TosaSerializerAttribute()
- attr.ResizeAttribute(
- output_dims, stride, offset, shift, stride_fp, offset_fp, mode
- )
+ attr.ResizeAttribute(scale, offset, border, mode)
self.ser.addOperator(op["op"], input_list, output_list, attr)
return result_tens
@@ -3619,18 +3610,16 @@ class TosaTestGen:
"types": [DType.INT8, DType.INT16, DType.FLOAT],
"invalid_test_validators": (
TosaInvalidValidator.ivWrongDataTypeOrModeResize,
- TosaInvalidValidator.ivBadStride,
),
"error_if_validators": (
TosaErrorValidator.evMaxDimExceeded,
- TosaErrorValidator.evStrideSmallerEqualZero,
- TosaErrorValidator.evStrideLargerDimension,
- TosaErrorValidator.evStrideLargerEqualMax,
- TosaErrorValidator.evOffsetSmallerEqualMin,
+ TosaErrorValidator.evScaleSmallerEqualZero,
+ TosaErrorValidator.evScaleNLargerMax,
+ TosaErrorValidator.evScaleDLargerMax,
+ TosaErrorValidator.evOffsetSmallerMin,
TosaErrorValidator.evOffsetLargerEqualMax,
- TosaErrorValidator.evShiftNotZero,
- TosaErrorValidator.evShiftSmallerOne,
- TosaErrorValidator.evShiftLargerEleven,
+ TosaErrorValidator.evBorderSmallerMin,
+ TosaErrorValidator.evBorderLargerEqualMax,
TosaErrorValidator.evWrongInputType,
TosaErrorValidator.evWrongOutputType,
TosaErrorValidator.evWrongRank,
@@ -3638,6 +3627,8 @@ class TosaTestGen:
TosaErrorValidator.evWrongOutputList,
TosaErrorValidator.evBatchMismatch,
TosaErrorValidator.evChannelMismatch,
+ TosaErrorValidator.evResizeOutputShapeMismatch,
+ TosaErrorValidator.evResizeOutputShapeNonInteger,
),
},
# Type conversion
@@ -4470,45 +4461,76 @@ class OutputShaper:
rng,
input,
mode,
- stride,
+ scale,
offset,
- shift,
- stride_fp,
- offset_fp,
- output_dims,
+ border,
input_dtype,
output_dtype,
error_name=None,
):
+ # Calculate OH, OW
+ scale_y_n = scale[0]
+ scale_y_d = scale[1]
+ scale_x_n = scale[2]
+ scale_x_d = scale[3]
+ if error_name == ErrorIf.ScaleSmallerEqualZero:
+ scale_y_n = max(scale_y_n, 1)
+ scale_y_d = max(scale_y_d, 1)
+ scale_x_n = max(scale_x_n, 1)
+ scale_x_d = max(scale_x_d, 1)
+
+ oh = ((input.shape[1] - 1) * scale_y_n - offset[0] + border[0]) // scale_y_d + 1
+ ow = ((input.shape[2] - 1) * scale_x_n - offset[1] + border[1]) // scale_x_d + 1
+
+ if error_name is not None:
+ # Make sure the output tensor is valid, which can occur when
+ # scale, offset or border have been changed for ERROR_IFs
+ oh = max(oh, 1)
+ ow = max(ow, 1)
+ if error_name != ErrorIf.MaxDimExceeded:
+ oh = min(oh, MAX_RESIZE_DIMENSION - 1)
+ ow = min(ow, MAX_RESIZE_DIMENSION - 1)
+
+ if error_name == ErrorIf.ResizeOutputShapeMismatch:
+ choices = [1, 2, 3]
+ change = rng.choice(choices)
+ # increment in multiples of scale_y/x_d so we don't hit non-integer error case
+ if change in [1, 3]:
+ if oh + scale_y_d >= MAX_RESIZE_DIMENSION:
+ oh -= scale_y_d
+ assert oh > 0 # Should have been caught in agResize
+ else:
+ oh += scale_y_d
+ if change in [2, 3]:
+ if ow + scale_x_d >= MAX_RESIZE_DIMENSION:
+ ow -= scale_x_d
+ assert ow > 0 # Should have been caught in agResize
+ else:
+ ow += scale_x_d
+
if error_name == ErrorIf.WrongRank:
output_dims = [
input.shape[0],
- output_dims[0],
- output_dims[0],
+ oh,
+ ow,
+ input.shape[0],
+ ]
+ elif error_name == ErrorIf.BatchMismatch:
+ output_dims = [
+ input.shape[0] + rng.integers(1, 10),
+ oh,
+ ow,
+ input.shape[3],
+ ]
+ elif error_name == ErrorIf.ChannelMismatch:
+ output_dims = [
input.shape[0],
+ oh,
+ ow,
+ input.shape[3] + rng.integers(1, 10),
]
else:
- if error_name == ErrorIf.BatchMismatch:
- output_dims = [
- input.shape[0] + rng.integers(1, 10),
- output_dims[0],
- output_dims[1],
- input.shape[3],
- ]
- elif error_name == ErrorIf.ChannelMismatch:
- output_dims = [
- input.shape[0],
- output_dims[0],
- output_dims[1],
- input.shape[3] + rng.integers(1, 10),
- ]
- else:
- output_dims = [
- input.shape[0],
- output_dims[0],
- output_dims[1],
- input.shape[3],
- ]
+ output_dims = [input.shape[0], oh, ow, input.shape[3]]
return serializer.addOutput(output_dims, output_dtype)
diff --git a/verif/generator/tosa_utils.py b/verif/generator/tosa_utils.py
index a4ef31a..6a689d0 100644
--- a/verif/generator/tosa_utils.py
+++ b/verif/generator/tosa_utils.py
@@ -2,6 +2,9 @@
# SPDX-License-Identifier: Apache-2.0
from tosa.DType import DType
+# Maximum dimension size for output and inputs for RESIZE
+MAX_RESIZE_DIMENSION = 16384
+
def valueToName(item, value):
"""Get the name of an attribute with the given value.
diff --git a/verif/generator/tosa_verif_build_tests.py b/verif/generator/tosa_verif_build_tests.py
index 69322cc..6ee873f 100644
--- a/verif/generator/tosa_verif_build_tests.py
+++ b/verif/generator/tosa_verif_build_tests.py
@@ -123,6 +123,14 @@ def parseArgs():
help="Number of random permutations for a given shape/rank for randomly-sampled parameter spaces",
)
+ parser.add_argument(
+ "--max-resize-output-dim",
+ dest="max_resize_output_dim",
+ default=1000,
+ type=int,
+ help="Upper limit on width and height output dimensions for `resize` op. Default: 1000",
+ )
+
# Targetting a specific shape/rank/dtype
parser.add_argument(
"--target-shape",
@@ -212,12 +220,11 @@ def main():
for opName, testStr, dtype, error, shapeList, testArgs in testList:
# Check for and skip duplicate tests
if testStr in testStrings:
+ print(f"Skipping duplicate test: {testStr}")
continue
else:
testStrings.append(testStr)
- if args.verbose:
- print(testStr)
results.append(
ttg.serializeTest(opName, testStr, dtype, error, shapeList, testArgs)
)