aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Johnson <jeremy.johnson@arm.com>2022-04-26 15:47:21 +0100
committerJeremy Johnson <jeremy.johnson@arm.com>2022-04-28 17:47:03 +0100
commit4a6fb9bdb4188b53516f7df0fbaa3695cce76319 (patch)
tree37f854bc24699c4c885323cc3299d06d0995d255
parent9a66abbd1da6547fd2cba1512d2f07fd1525de4d (diff)
downloadreference_model-4a6fb9bdb4188b53516f7df0fbaa3695cce76319.tar.gz
Update tensor ops ERROR_IF criteria
Update to ref model to check ERROR_IF criteria for pooling and convolution ops to match specification Update to tosa_verif_build_tests to produce valid test ranges and new ERROR_IF tests Plus update pooling ops big kernel to 9 (from 6) for better testing coverage and set dilation to 1 and add out_pad bottom & right for transpose_conv2d to match specification Signed-off-by: Jeremy Johnson <jeremy.johnson@arm.com> Change-Id: Ic5759872d40ae8d3f3d07043d9a0f2fa0244d72e
-rw-r--r--reference_model/src/ops/tensor_ops.cc145
-rw-r--r--verif/generator/tosa_arg_gen.py113
-rw-r--r--verif/generator/tosa_error_if.py196
-rw-r--r--verif/generator/tosa_test_gen.py114
4 files changed, 446 insertions, 122 deletions
diff --git a/reference_model/src/ops/tensor_ops.cc b/reference_model/src/ops/tensor_ops.cc
index 02fdf01..4a56093 100644
--- a/reference_model/src/ops/tensor_ops.cc
+++ b/reference_model/src/ops/tensor_ops.cc
@@ -92,10 +92,22 @@ int check_pool2d_attribute(tosa::TosaPoolAttribute* attribute,
return 1;
}
- if ((OH != (IH + pad_top + pad_bottom + stride_y - kernel_y) / stride_y) ||
- (OW != (IW + pad_left + pad_right + stride_x - kernel_x) / stride_x))
+ int32_t full_H = IH + pad_top + pad_bottom - kernel_y;
+ int32_t full_W = IW + pad_left + pad_right - kernel_x;
+
+ if ((full_H % stride_y != 0) ||
+ (full_W % stride_x != 0))
{
- msg = "Mismatch between output shape provided and expected output shape";
+ msg = "Parameters must yield exact integer output dimensions";
+ return 1;
+ }
+
+ if ((OH != (full_H / stride_y) + 1) ||
+ (OW != (full_W / stride_x) + 1))
+ {
+ msg = "Mismatch between output shape provided and expected output shape (" +
+ std::to_string((full_H / stride_y) + 1) + "," +
+ std::to_string((full_W / stride_x) + 1) + ")";
return 1;
}
@@ -107,6 +119,8 @@ int check_conv_attribute_qinfo(tosa::TosaConvAttribute* attribute,
uint32_t conv_dimension,
std::vector<int32_t> input_shape,
std::vector<int32_t> output_shape,
+ std::vector<int32_t> weights,
+ uint32_t offset_kernel,
DType InDtype,
DType WeightDtype,
std::string& msg)
@@ -156,6 +170,62 @@ int check_conv_attribute_qinfo(tosa::TosaConvAttribute* attribute,
}
}
+ ASSERT_MSG(conv_dimension == 2 || conv_dimension == 3, "Unsupported convolution dimension")
+
+ int32_t offset_d = 1 ? conv_dimension == 3 : 0;
+ int32_t ID = conv_dimension == 3 ? input_shape[1] : 1;
+ int32_t IH = input_shape[1 + offset_d];
+ int32_t IW = input_shape[2 + offset_d];
+ int32_t OD = conv_dimension == 3 ? output_shape[1] : 1;
+ int32_t OH = output_shape[1 + offset_d];
+ int32_t OW = output_shape[2 + offset_d];
+
+ int32_t stride_d = conv_dimension == 3 ? attribute->stride()[0] : 1;
+ int32_t stride_y = attribute->stride()[0 + offset_d];
+ int32_t stride_x = attribute->stride()[1 + offset_d];
+ int32_t kernel_d = conv_dimension == 3 ? weights[offset_kernel] : 1;
+ int32_t kernel_h = weights[offset_kernel + offset_d];
+ int32_t kernel_w = weights[offset_kernel + 1 + offset_d];
+ int32_t dilation_d = conv_dimension == 3 ? attribute->dilation()[0] : 1;
+ int32_t dilation_y = attribute->dilation()[0 + offset_d];
+ int32_t dilation_x = attribute->dilation()[1 + offset_d];
+
+ offset_d *= 2;
+ int32_t pad_d0 = conv_dimension == 3 ? attribute->padding()[0] : 0;
+ int32_t pad_d1 = conv_dimension == 3 ? attribute->padding()[1] : 0;
+ int32_t pad_top = attribute->padding()[0 + offset_d];
+ int32_t pad_bottom = attribute->padding()[1 + offset_d];
+ int32_t pad_left = attribute->padding()[2 + offset_d];
+ int32_t pad_right = attribute->padding()[3 + offset_d];
+
+ int32_t full_D = ID - 1 + pad_d0 + pad_d1 - (kernel_d - 1) * dilation_d;
+ int32_t full_H = IH - 1 + pad_top + pad_bottom - (kernel_h - 1) * dilation_y;
+ int32_t full_W = IW - 1 + pad_left + pad_right - (kernel_w - 1) * dilation_x;
+
+ if ((full_H % stride_y != 0) ||
+ (full_W % stride_x != 0) ||
+ (full_D % stride_d != 0))
+ {
+ msg = "Parameters must yield exact integer output dimensions";
+ return 1;
+ }
+
+ if ((OH != (full_H / stride_y) + 1) ||
+ (OW != (full_W / stride_x) + 1) ||
+ (OD != (full_D / stride_d) + 1))
+ {
+ std::string msg_d = "";
+ if (conv_dimension == 3)
+ {
+ msg_d += std::to_string((full_D / stride_d) + 1) + ",";
+ }
+ msg = "Mismatch between output shape provided and expected output shape (" +
+ msg_d +
+ std::to_string((full_H / stride_y) + 1) + "," +
+ std::to_string((full_W / stride_x) + 1) + ")";
+ return 1;
+ }
+
if (qinfo)
{
if (InDtype != DType_INT8 && qinfo->input_zp() != 0)
@@ -529,7 +599,7 @@ int OpConv2d<InDtype, WeightDtype>::checkTensorAttributes()
std::string msg;
if (check_conv_attribute_qinfo(attribute, qinfo, 2 /* conv_dimension */, input->getShape(), output->getShape(),
- InDtype, WeightDtype, msg))
+ weight->getShape(), 1 /* offset_kernel */, InDtype, WeightDtype, msg))
{
msg = "OpConv2d: " + msg;
printNodeValidationError(msg.c_str());
@@ -715,7 +785,7 @@ int OpConv3d<InDtype, WeightDtype>::checkTensorAttributes()
std::string msg;
if (check_conv_attribute_qinfo(attribute, qinfo, 3 /* conv_dimension */, input->getShape(), output->getShape(),
- InDtype, WeightDtype, msg))
+ weight->getShape(), 1 /* offset_kernel */, InDtype, WeightDtype, msg))
{
msg = "OpConv3d: " + msg;
printNodeValidationError(msg.c_str());
@@ -904,7 +974,7 @@ int OpDepthwiseConv2d<InDtype, WeightDtype>::checkTensorAttributes()
std::string msg;
if (check_conv_attribute_qinfo(attribute, qinfo, 2 /* conv_dimension */, input->getShape(), output->getShape(),
- InDtype, WeightDtype, msg))
+ weight->getShape(), 0 /* offset_kernel */, InDtype, WeightDtype, msg))
{
msg = "OpDepthwiseConv2d: " + msg;
printNodeValidationError(msg.c_str());
@@ -1447,7 +1517,7 @@ int OpTransposeConv2d<InDtype, WeightDtype>::checkTensorAttributes()
bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
output = dynamic_cast<TosaReference::TensorTemplate<TAcc>*>(outputs[0]);
- if (attribute->outpad().size() != 2)
+ if (attribute->outpad().size() != 4)
{
printNodeValidationError("OpTransposeConv2d: illegal size for attribute outpad");
return 1;
@@ -1489,11 +1559,12 @@ int OpTransposeConv2d<InDtype, WeightDtype>::checkTensorAttributes()
}
}
+ // TODO: Remove dilation once schema updated
for (int32_t i : attribute->dilation())
{
- if (i < 1)
+ if (i != 1)
{
- printNodeValidationError("OpTransposeConv2d: At least one dilation is smaller than one");
+ printNodeValidationError("OpTransposeConv2d: Dilation is deprecated and must be set to one");
return 1;
}
}
@@ -1507,6 +1578,33 @@ int OpTransposeConv2d<InDtype, WeightDtype>::checkTensorAttributes()
}
}
+ int32_t IH = input->getShape()[1];
+ int32_t IW = input->getShape()[2];
+ int32_t OH = output->getShape()[1];
+ int32_t OW = output->getShape()[2];
+
+ int32_t stride_y = attribute->stride()[0];
+ int32_t stride_x = attribute->stride()[1];
+ int32_t kernel_h = weight->getShape()[1];
+ int32_t kernel_w = weight->getShape()[2];
+
+ int32_t outpad_top = attribute->outpad()[0];
+ int32_t outpad_bottom = attribute->outpad()[1];
+ int32_t outpad_left = attribute->outpad()[2];
+ int32_t outpad_right = attribute->outpad()[3];
+
+ int32_t H = (IH - 1) * stride_y - outpad_top - outpad_bottom + kernel_h;
+ int32_t W = (IW - 1) * stride_x - outpad_left - outpad_right + kernel_w;
+
+ if ((OH != H) || (OW != W))
+ {
+ std::string msg = "OpTransposeConv2d: Mismatch between output shape provided and expected output shape (" +
+ std::to_string(H) + "," +
+ std::to_string(W) + ")";
+ printNodeValidationError(msg.c_str());
+ return 1;
+ }
+
if (this->qinfo)
{
if (InDtype != DType_INT8)
@@ -1542,12 +1640,13 @@ int OpTransposeConv2d<InDtype, WeightDtype>::eval()
int out_width = this->output->getShape()[2];
int out_channels = this->output->getShape()[3];
- int padding_top = this->attribute->outpad()[0];
- int padding_left = this->attribute->outpad()[1];
- int stride_h = this->attribute->stride()[0];
- int stride_w = this->attribute->stride()[1];
- int dilation_h = this->attribute->dilation()[0];
- int dilation_w = this->attribute->dilation()[1];
+ int outpad_top = this->attribute->outpad()[0];
+ int outpad_bottom = this->attribute->outpad()[1];
+ int outpad_left = this->attribute->outpad()[2];
+ int outpad_right = this->attribute->outpad()[3];
+
+ int stride_h = this->attribute->stride()[0];
+ int stride_w = this->attribute->stride()[1];
ERROR_IF(in_batch != out_batch, "OpTransposeConv2d: tensor batch mismatch %d != %d", in_batch, out_batch);
ERROR_IF(f_in_channels != in_channels, "OpTransposeConv2d: tensor input channel mismatch %d != %d", f_in_channels,
@@ -1559,10 +1658,10 @@ int OpTransposeConv2d<InDtype, WeightDtype>::eval()
DEBUG_INFO(OP,
"perform OpTransposeConv2d, input.shape=[%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d], "
- "output.shape=[%d,%d,%d,%d], stride=[%d,%d], dilation=[%d,%d], padding=[%d,%d]",
- in_batch, in_height, in_width, in_channels, f_height, f_width, f_out_channels, f_in_channels, out_batch,
- out_height, out_width, out_channels, stride_h, stride_w, dilation_h, dilation_w, padding_top,
- padding_left);
+ "output.shape=[%d,%d,%d,%d], stride=[%d,%d], outpad=[%d,%d,%d,%d]",
+ in_batch, in_height, in_width, in_channels, f_height, f_width, f_out_channels, f_in_channels,
+ out_batch, out_height, out_width, out_channels, stride_h, stride_w, outpad_top,
+ outpad_bottom, outpad_left, outpad_right);
TIn input_val = this->input->getTensor();
TWeight weight_val = this->weight->getTensor();
@@ -1595,16 +1694,16 @@ int OpTransposeConv2d<InDtype, WeightDtype>::eval()
{
for (int iw = 0; iw < in_width; iw++)
{
- out_x_origin = iw * stride_w - padding_left;
- out_y_origin = ih * stride_h - padding_top;
+ out_x_origin = iw * stride_w - outpad_left;
+ out_y_origin = ih * stride_h - outpad_top;
for (int ic = 0; ic < in_channels; ic++)
{
for (int fh = 0; fh < f_height; fh++)
{
for (int fw = 0; fw < f_width; fw++)
{
- out_x = out_x_origin + fw * dilation_w;
- out_y = out_y_origin + fh * dilation_h;
+ out_x = out_x_origin + fw;
+ out_y = out_y_origin + fh;
for (int oc = 0; oc < out_channels; oc++)
{
if ((out_x >= 0 && out_x < out_width) && (out_y >= 0 && out_y < out_height))
diff --git a/verif/generator/tosa_arg_gen.py b/verif/generator/tosa_arg_gen.py
index e3492cd..f63a7df 100644
--- a/verif/generator/tosa_arg_gen.py
+++ b/verif/generator/tosa_arg_gen.py
@@ -1031,7 +1031,9 @@ class TosaArgGen:
# Can't use stride=0, as it is used to derive output shape, as a divisor
s_vals = [testGen.rng.choice(range(-5, 0))]
else:
- s_vals = [x for x in range(1, testGen.args.max_conv_stride + 1)]
+ # Stride must be greater than 1 to force non-integer error
+ startStride = 1 if error_name != ErrorIf.PoolingOutputShapeNonInteger else 2
+ s_vals = [x for x in range(startStride, testGen.args.max_conv_stride + 1)]
strides = {x for x in itertools.product(*([s_vals] * k_rank))}
if error_name == ErrorIf.DilationSmallerOne:
d_vals = [testGen.rng.choice(range(-5, 1))]
@@ -1055,7 +1057,7 @@ class TosaArgGen:
# There are too many parameter combinations, so generate them sparsely,
# very sparse for negative tests
- sparsity_factor = 2 if error_name else 100
+ sparsity_factor = 2 if error_name else 120
sparsity = len(paddings) * len(strides) * len(dilations) // sparsity_factor + 1
# If there are only a small number of tests, just select them all
if sparsity < 13:
@@ -1084,16 +1086,37 @@ class TosaArgGen:
and (ifm_shape[2] + p[2] + p[3]) > d[1]
and (k_rank < 3 or ((ifm_shape[3] + p[4] + p[5]) > d[2]))
):
- arg_list.append(
- (
- "st{}_pad{}_dilat{}".format(
- "".join([str(x) for x in s]),
- "".join([str(x) for x in p]),
- "".join([str(x) for x in d]),
- ),
- [s, p, d],
+ remainders = []
+ for index in range(k_rank):
+ pad_offset = index * 2
+ remainders.append(
+ (
+ ifm_shape[index + 1]
+ - 1
+ + p[pad_offset]
+ + p[pad_offset + 1]
+ - (k[index] - 1) * d[index]
+ )
+ % s[index]
+ )
+ if (
+ # the parameters must produce integer exact output
+ error_name != ErrorIf.ConvOutputShapeNonInteger
+ and max(remainders) == 0
+ ) or (
+ error_name == ErrorIf.ConvOutputShapeNonInteger
+ and max(remainders) > 0
+ ):
+ arg_list.append(
+ (
+ "st{}_pad{}_dilat{}".format(
+ "".join([str(x) for x in s]),
+ "".join([str(x) for x in p]),
+ "".join([str(x) for x in d]),
+ ),
+ [s, p, d],
+ )
)
- )
n += 1
return arg_list
@@ -1116,17 +1139,16 @@ class TosaArgGen:
p_vals = [testGen.rng.choice(range(-5, 0))]
else:
p_vals = [x for x in range(0, testGen.args.max_conv_padding + 1)]
- paddings = {x for x in itertools.product(*([p_vals] * 2))}
+ paddings = {x for x in itertools.product(*([p_vals] * 4))}
if error_name == ErrorIf.StrideSmallerOne:
# Can't use stride=0, as it is used to derive output shape, as a divisor
s_vals = [testGen.rng.choice(range(-5, 0))]
else:
s_vals = [x for x in range(1, testGen.args.max_conv_stride + 1)]
strides = {x for x in itertools.product(*([s_vals] * 2))}
- if error_name == ErrorIf.DilationSmallerOne:
- d_vals = [testGen.rng.choice(range(-5, 1))]
- else:
- d_vals = [x for x in range(1, testGen.args.max_conv_dilation + 1)]
+ # Dilation is not supported by the specification for transpose conv2d
+ # TODO: Remove this completely when schema has been updated
+ d_vals = [1]
dilations = {x for x in itertools.product(*([d_vals] * 2))}
if not error_name:
@@ -1134,16 +1156,14 @@ class TosaArgGen:
if max(ifm_shape) < 64:
bigPadding = 9
paddings.update(
- {x for x in itertools.product(*([[0, bigPadding]] * 2))}
+ {x for x in itertools.product(*([[0, bigPadding]] * 4))}
)
bigStride = 8
strides.update({x for x in itertools.product(*([[1, bigStride]] * 2))})
- bigDilation = 7
- dilations.update({x for x in itertools.product(*([[1, bigDilation]] * 2))})
# There are too many parameter combinations, so generate them sparsely,
# very sparse for negative tests
- sparsity_factor = 2 if error_name else 100
+ sparsity_factor = 2 if error_name else 10
sparsity = len(paddings) * len(strides) * len(dilations) // sparsity_factor + 1
# If there are only a small number of tests, just select them all
if sparsity < 13:
@@ -1159,18 +1179,8 @@ class TosaArgGen:
for d in sorted(list(dilations)):
if n % sparsity == 0:
# Determine the output shape
- oh = (
- ifm_shape[1]
- - filter_shape[1]
- - (filter_shape[1] - 1) * (d[0] - 1)
- + 2 * p[0]
- ) // s[0] + 1
- ow = (
- ifm_shape[2]
- - filter_shape[2]
- - (filter_shape[2] - 1) * (d[1] - 1)
- + 2 * p[1]
- ) // s[1] + 1
+ oh = (ifm_shape[1] - 1) * s[0] - p[0] - p[1] + filter_shape[1]
+ ow = (ifm_shape[2] - 1) * s[1] - p[2] - p[3] + filter_shape[2]
os = [ifm_shape[0], oh, ow, filter_shape[0]]
arg_list.append(
(
@@ -1231,7 +1241,9 @@ class TosaArgGen:
# Generate comprehensive argument lists
p_vals = [x for x in range(0, testGen.args.max_pooling_padding + 1)]
paddings = {x for x in itertools.product(*([p_vals] * 4))}
- s_vals = [x for x in range(1, testGen.args.max_pooling_stride + 1)]
+ # Stride must be greater than 1 to force non-integer error
+ startStride = 1 if error_name != ErrorIf.PoolingOutputShapeNonInteger else 2
+ s_vals = [x for x in range(startStride, testGen.args.max_pooling_stride + 1)]
strides = {x for x in itertools.product(*([s_vals] * 2))}
k_vals = [x for x in range(2, testGen.args.max_pooling_kernel + 1)]
kernels = {x for x in itertools.product(*([k_vals] * 2))}
@@ -1239,8 +1251,10 @@ class TosaArgGen:
if testGen.args.oversize:
# add some oversize argument values
bigStride = 7
- strides.update({x for x in itertools.product(*([[1, bigStride]] * 2))})
- bigKernel = 6
+ strides.update(
+ {x for x in itertools.product(*([[startStride, bigStride]] * 2))}
+ )
+ bigKernel = 9
kernels.update({x for x in itertools.product(*([[2, bigKernel]] * 2))})
if max(shape) < 64:
# padding must be less than the kernel size
@@ -1289,16 +1303,27 @@ class TosaArgGen:
and (shape[1] + p[0] + p[1]) > k[0]
and (shape[2] + p[2] + p[3]) > k[1]
):
- arg_list.append(
- (
- "st{}_kern{}_pad{}".format(
- "".join([str(x) for x in s]),
- "".join([str(x) for x in k]),
- "".join([str(x) for x in p]),
- ),
- [s, p, k],
+ remainder_h = (shape[1] + p[0] + p[1] - k[0]) % s[0]
+ remainder_w = (shape[2] + p[2] + p[3] - k[1]) % s[1]
+ if (
+ # the parameters must produce integer exact output
+ error_name != ErrorIf.PoolingOutputShapeNonInteger
+ and remainder_h == 0
+ and remainder_w == 0
+ ) or (
+ error_name == ErrorIf.PoolingOutputShapeNonInteger
+ and (remainder_h != 0 or remainder_w != 0)
+ ):
+ arg_list.append(
+ (
+ "st{}_kern{}_pad{}".format(
+ "".join([str(x) for x in s]),
+ "".join([str(x) for x in k]),
+ "".join([str(x) for x in p]),
+ ),
+ [s, p, k],
+ )
)
- )
n += 1
return arg_list
diff --git a/verif/generator/tosa_error_if.py b/verif/generator/tosa_error_if.py
index caf63e3..e7e758f 100644
--- a/verif/generator/tosa_error_if.py
+++ b/verif/generator/tosa_error_if.py
@@ -42,6 +42,9 @@ class ErrorIf(object):
PadSmallerZero = "PadSmallerZero"
PadLargerEqualKernel = "PadLargerEqualKernel"
PoolingOutputShapeMismatch = "PoolingOutputShapeMismatch"
+ PoolingOutputShapeNonInteger = "PoolingOutputShapeNonInteger"
+ ConvOutputShapeMismatch = "ConvOutputShapeMismatch"
+ ConvOutputShapeNonInteger = "ConvOutputShapeNonInteger"
ScaleNotTrue = "ScaleNotTrue"
ScaleTrue = "ScaleTrue"
TensorSizeInputOutputMismatch = "TensorSizeInputOutputMismatch"
@@ -1226,6 +1229,20 @@ class TosaErrorValidator:
return info_dict
@staticmethod
+ def checkPoolingParams(kernel, stride, pad):
+ return (
+ min(kernel) >= 1
+ and min(stride) >= 1
+ and min(pad) >= 0
+ and not (
+ pad[0] >= kernel[0]
+ or pad[1] >= kernel[0]
+ or pad[2] >= kernel[1]
+ or pad[3] >= kernel[1]
+ )
+ )
+
+ @staticmethod
def evPoolingOutputShapeMismatch(check=False, **kwargs):
error_name = ErrorIf.PoolingOutputShapeMismatch
param_reqs = {"rank": None, "dtype": None, "shape": None}
@@ -1252,25 +1269,11 @@ class TosaErrorValidator:
# calculate correct height, width dimensions
if stride_x != 0 and stride_y != 0:
- y_correct = (
- IH + pad_top + pad_bottom + stride_y - kernel_y
- ) // stride_y
- x_correct = (
- IW + pad_left + pad_right + stride_x - kernel_x
- ) // stride_x
+ y_correct = ((IH + pad_top + pad_bottom - kernel_y) // stride_y) + 1
+ x_correct = ((IW + pad_left + pad_right - kernel_x) // stride_x) + 1
# ensure parameters are valid
- params_valid = (
- min(kernel) >= 1
- and min(stride) >= 1
- and min(pad) >= 0
- and not (
- pad[0] >= kernel[0]
- or pad[1] >= kernel[0]
- or pad[2] >= kernel[1]
- or pad[3] >= kernel[1]
- )
- )
+ params_valid = TosaErrorValidator.checkPoolingParams(kernel, stride, pad)
if params_valid and (OH != y_correct or OW != x_correct):
error_result = True
@@ -1284,6 +1287,165 @@ class TosaErrorValidator:
return info_dict
@staticmethod
+ def evPoolingOutputShapeNonInteger(check=False, **kwargs):
+ error_name = ErrorIf.PoolingOutputShapeNonInteger
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
+ error_result = False
+ error_reason = "Parameters do not yield exact integer output dimensions"
+
+ if check:
+ pad = kwargs["pad"]
+ pad_top, pad_bottom, pad_left, pad_right = pad[0], pad[1], pad[2], pad[3]
+
+ kernel = kwargs["kernel"]
+ kernel_y, kernel_x = kernel[0], kernel[1]
+
+ input_shape = kwargs["input_shape"]
+ IH, IW = input_shape[1], input_shape[2]
+
+ stride = kwargs["stride"]
+ stride_y, stride_x = stride[0], stride[1]
+
+ # calculate remainder of height, width dimensions
+ if stride_x != 0 and stride_y != 0:
+ y_remainder = (IH + pad_top + pad_bottom - kernel_y) % stride_y
+ x_remainder = (IW + pad_left + pad_right - kernel_x) % stride_x
+
+ # ensure parameters are valid
+ params_valid = TosaErrorValidator.checkPoolingParams(kernel, stride, pad)
+ if params_valid and (y_remainder != 0 or x_remainder != 0):
+ 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 checkConvParams(weight_shape, stride, pad, dilation):
+ return (
+ # Check kernel sizes
+ min(weight_shape[1:-1]) >= 1
+ and min(stride) >= 1
+ and min(pad) >= 0
+ and (dilation is None or min(dilation) >= 1)
+ )
+
+ @staticmethod
+ def evConvOutputShapeMismatch(check=False, **kwargs):
+ error_name = ErrorIf.ConvOutputShapeMismatch
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
+ error_result = False
+ error_reason = (
+ "Mismatch between output shape provided and expected output shape"
+ )
+
+ if check:
+ op = kwargs["op"]
+ pad = kwargs["pad"]
+ weight_shape = kwargs["weight_shape"]
+ input_shape = kwargs["input_shape"]
+ output_shape = kwargs["output_shape"]
+ dilation = kwargs["dilation"] if op["op"] != Op.TRANSPOSE_CONV2D else None
+ stride = kwargs["stride"]
+
+ kernel_offset = 0 if op["op"] == Op.DEPTHWISE_CONV2D else 1
+
+ # calculate correct dimensions
+ dims_correct = []
+ if min(stride) > 0:
+ for index in range(len(stride)):
+ pad_offset = index * 2
+ if op["op"] == Op.TRANSPOSE_CONV2D:
+ dims_correct.append(
+ (input_shape[index + 1] - 1) * stride[index]
+ - pad[pad_offset]
+ - pad[pad_offset + 1]
+ + weight_shape[index + kernel_offset]
+ )
+ else:
+ dims_correct.append(
+ (
+ input_shape[index + 1]
+ - 1
+ + pad[pad_offset]
+ + pad[pad_offset + 1]
+ - (weight_shape[index + kernel_offset] - 1)
+ * dilation[index]
+ )
+ // stride[index]
+ + 1
+ )
+
+ # ensure parameters are valid
+ params_valid = TosaErrorValidator.checkConvParams(
+ weight_shape, stride, pad, dilation
+ )
+
+ if params_valid and output_shape[1:-1] != dims_correct:
+ 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 evConvOutputShapeNonInteger(check=False, **kwargs):
+ error_name = ErrorIf.ConvOutputShapeNonInteger
+ param_reqs = {"rank": None, "dtype": None, "shape": None}
+ error_result = False
+ error_reason = "Parameters do not yield exact integer output dimensions"
+
+ if check:
+ op = kwargs["op"]
+ pad = kwargs["pad"]
+ weight_shape = kwargs["weight_shape"]
+ input_shape = kwargs["input_shape"]
+ dilation = kwargs["dilation"]
+ stride = kwargs["stride"]
+
+ kernel_offset = 0 if op["op"] == Op.DEPTHWISE_CONV2D else 1
+
+ # calculate correct height, width dimensions
+ remainders = []
+ if min(stride) > 0:
+ for index in range(len(stride)):
+ pad_offset = index * 2
+ remainders.append(
+ (
+ input_shape[index + 1]
+ - 1
+ + pad[pad_offset]
+ + pad[pad_offset + 1]
+ - (weight_shape[index + kernel_offset] - 1)
+ * dilation[index]
+ )
+ % stride[index]
+ )
+
+ # ensure parameters are valid
+ params_valid = TosaErrorValidator.checkConvParams(
+ weight_shape, stride, pad, dilation
+ )
+ if params_valid and max(remainders) > 0:
+ 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 evArgmaxOutputShapeMismatch(check=False, **kwargs):
error_name = ErrorIf.ArgmaxOutputShapeMismatch
param_reqs = {"rank": [2, 4], "dtype": None, "shape": None}
diff --git a/verif/generator/tosa_test_gen.py b/verif/generator/tosa_test_gen.py
index 38365d0..7c2b9de 100644
--- a/verif/generator/tosa_test_gen.py
+++ b/verif/generator/tosa_test_gen.py
@@ -630,6 +630,8 @@ class TosaTestGen:
stride=strides,
dilation=dilations,
input_shape=ifm.shape,
+ weight_shape=filter.shape,
+ output_shape=result_tens.shape,
):
return None
@@ -692,6 +694,8 @@ class TosaTestGen:
stride=strides,
dilation=dilations,
input_shape=ifm.shape,
+ weight_shape=filter.shape,
+ output_shape=result_tens.shape,
):
return None
@@ -715,7 +719,7 @@ class TosaTestGen:
error_name=None,
qinfo=None,
):
- assert len(outpad) == 2
+ assert len(outpad) == 4
result_tens = OutputShaper.transposeConv2DOp(
self.ser, self.rng, ifm, output_shape, error_name
)
@@ -753,8 +757,9 @@ class TosaTestGen:
output_list=output_list,
pad=outpad,
stride=stride,
- dilation=dilation,
input_shape=ifm.shape,
+ weight_shape=filter.shape,
+ output_shape=result_tens.shape,
):
return None
@@ -816,6 +821,8 @@ class TosaTestGen:
stride=strides,
dilation=dilations,
input_shape=ifm.shape,
+ weight_shape=filter.shape,
+ output_shape=result_tens.shape,
):
return None
@@ -2393,6 +2400,7 @@ class TosaTestGen:
TosaErrorValidator.evOutputZeroPointNotZero,
TosaErrorValidator.evPadLargerEqualKernel,
TosaErrorValidator.evPoolingOutputShapeMismatch,
+ TosaErrorValidator.evPoolingOutputShapeNonInteger,
),
},
# Templated operator. Filled in by createDynamicOpLists
@@ -2420,6 +2428,8 @@ class TosaTestGen:
TosaErrorValidator.evStrideSmallerOne,
TosaErrorValidator.evDilationSmallerOne,
TosaErrorValidator.evWrongRank,
+ TosaErrorValidator.evConvOutputShapeMismatch,
+ TosaErrorValidator.evConvOutputShapeNonInteger,
),
"template": True,
},
@@ -2448,6 +2458,8 @@ class TosaTestGen:
TosaErrorValidator.evStrideSmallerOne,
TosaErrorValidator.evDilationSmallerOne,
TosaErrorValidator.evWrongRank,
+ TosaErrorValidator.evConvOutputShapeMismatch,
+ TosaErrorValidator.evConvOutputShapeNonInteger,
),
"template": True,
},
@@ -2477,6 +2489,8 @@ class TosaTestGen:
TosaErrorValidator.evStrideSmallerOne,
TosaErrorValidator.evDilationSmallerOne,
TosaErrorValidator.evWrongRank,
+ TosaErrorValidator.evConvOutputShapeMismatch,
+ TosaErrorValidator.evConvOutputShapeNonInteger,
),
"template": True,
},
@@ -2546,6 +2560,7 @@ class TosaTestGen:
TosaErrorValidator.evWrongOutputList,
TosaErrorValidator.evPadLargerEqualKernel,
TosaErrorValidator.evPoolingOutputShapeMismatch,
+ TosaErrorValidator.evPoolingOutputShapeNonInteger,
),
},
# Templated operator. Filled in by createDynamicOpLists
@@ -2574,8 +2589,8 @@ class TosaTestGen:
TosaErrorValidator.evWeightZeroPointNotZero,
TosaErrorValidator.evPadSmallerZero,
TosaErrorValidator.evStrideSmallerOne,
- TosaErrorValidator.evDilationSmallerOne,
TosaErrorValidator.evWrongRank,
+ TosaErrorValidator.evConvOutputShapeMismatch,
),
"template": True,
},
@@ -3887,30 +3902,30 @@ class OutputShaper:
# Filter: OHWI
# OFM: NHWC
- if len(padding) == 2:
- # Expand padding to 4 parameters in the case of transpose_conv2d
- # From H,W to T,B,L,R
- padding = [padding[0], padding[0], padding[1], padding[1]]
-
h = (
ifm.shape[1]
- - filter.shape[1]
- - (filter.shape[1] - 1) * (dilations[0] - 1)
+ - 1
+ padding[0]
+ padding[1]
+ - (filter.shape[1] - 1) * dilations[0]
) // strides[0] + 1
w = (
ifm.shape[2]
- - filter.shape[2]
- - (filter.shape[2] - 1) * (dilations[1] - 1)
+ - 1
+ padding[2]
+ padding[3]
+ - (filter.shape[2] - 1) * dilations[1]
) // strides[1] + 1
- # Avoid illegal dimensions, which can be generated in error_if tests
- h = max(h, 1)
- w = max(w, 1)
+ if error_name == ErrorIf.ConvOutputShapeMismatch:
+ choices = [1, 2, 3]
+ change = rng.choice(choices)
+ # increment in multiples of stride to not hit non-integer error case
+ if change in [1, 3]:
+ h = h + (rng.choice(choices) * strides[0])
+ if change in [2, 3]:
+ w = w + (rng.choice(choices) * strides[1])
ofm_shape = [ifm.shape[0], h, w, filter.shape[0]]
@@ -3941,32 +3956,38 @@ class OutputShaper:
d = (
ifm.shape[1]
- - filter.shape[1]
- - (filter.shape[1] - 1) * (dilations[0] - 1)
+ - 1
+ padding[0]
+ padding[1]
+ - (filter.shape[1] - 1) * dilations[0]
) // strides[0] + 1
h = (
ifm.shape[2]
- - filter.shape[2]
- - (filter.shape[2] - 1) * (dilations[1] - 1)
+ - 1
+ padding[2]
+ padding[3]
+ - (filter.shape[2] - 1) * dilations[1]
) // strides[1] + 1
w = (
ifm.shape[3]
- - filter.shape[3]
- - (filter.shape[3] - 1) * (dilations[2] - 1)
+ - 1
+ padding[4]
+ padding[5]
+ - (filter.shape[3] - 1) * dilations[2]
) // strides[2] + 1
- # Avoid illegal dimensions, which can be generated in error_if tests
- d = max(d, 1)
- h = max(h, 1)
- w = max(w, 1)
+ if error_name == ErrorIf.ConvOutputShapeMismatch:
+ choices = [1, 2, 3, 4]
+ change = rng.choice(choices)
+ # increment in multiples of stride to not hit non-integer error case
+ if change in [1, 4]:
+ d = d + (rng.choice(choices) * strides[0])
+ if change in [2, 4]:
+ h = h + (rng.choice(choices) * strides[1])
+ if change in [3, 4]:
+ w = w + (rng.choice(choices) * strides[2])
ofm_shape = [ifm.shape[0], d, h, w, filter.shape[0]]
@@ -3995,25 +4016,31 @@ class OutputShaper:
# IFM: NHWC
# Filter: HWCM
# OFM: NHW C*M
+
h = (
ifm.shape[1]
- - filter.shape[0]
- - (filter.shape[0] - 1) * (dilations[0] - 1)
+ - 1
+ padding[0]
+ padding[1]
+ - (filter.shape[0] - 1) * dilations[0]
) // strides[0] + 1
w = (
ifm.shape[2]
- - filter.shape[1]
- - (filter.shape[1] - 1) * (dilations[1] - 1)
+ - 1
+ padding[2]
+ padding[3]
+ - (filter.shape[1] - 1) * dilations[1]
) // strides[1] + 1
- # Avoid illegal dimensions, which can be generated in error_if tests
- h = max(h, 1)
- w = max(w, 1)
+ if error_name == ErrorIf.ConvOutputShapeMismatch:
+ choices = [1, 2, 3]
+ change = rng.choice(choices)
+ # increment in multiples of stride to not hit non-integer error case
+ if change in [1, 3]:
+ h = h + (rng.choice(choices) * strides[0])
+ if change in [2, 3]:
+ w = w + (rng.choice(choices) * strides[1])
ofm_shape = [ifm.shape[0], h, w, filter.shape[2] * filter.shape[3]]
@@ -4043,14 +4070,17 @@ class OutputShaper:
h = 1
w = 1
else:
- h = (ifm.shape[1] + pad[0] + pad[1] + stride[0] - kernel[0]) // stride[0]
- w = (ifm.shape[2] + pad[2] + pad[3] + stride[1] - kernel[1]) // stride[1]
+ h = (ifm.shape[1] + pad[0] + pad[1] - kernel[0]) // stride[0] + 1
+ w = (ifm.shape[2] + pad[2] + pad[3] - kernel[1]) // stride[1] + 1
if error_name == ErrorIf.PoolingOutputShapeMismatch:
- choices = [1, 2, 3, 4, 5]
- h = h + rng.choice(choices)
- w = w + rng.choice(choices)
-
+ choices = [1, 2, 3]
+ change = rng.choice(choices)
+ # increment in multiples of stride to not hit non-integer error case
+ if change in [1, 3]:
+ h = h + (rng.choice(choices) * stride[0])
+ if change in [2, 3]:
+ w = w + (rng.choice(choices) * stride[1])
ofm_shape = [ifm.shape[0], h, w, ifm.shape[3]]
if error_name == ErrorIf.WrongOutputType:
@@ -4468,6 +4498,14 @@ class OutputShaper:
@staticmethod
def transposeConv2DOp(ser, rng, ifm, output_shape, error_name=None):
+ if error_name == ErrorIf.ConvOutputShapeMismatch:
+ choices = [1, 2, 3]
+ change = rng.choice(choices)
+ if change in [1, 3]:
+ output_shape[1] = output_shape[1] + rng.choice(choices)
+ if change in [2, 3]:
+ output_shape[2] = output_shape[2] + rng.choice(choices)
+
if ifm.dtype == DType.INT8:
out_dtype = DType.INT32
elif ifm.dtype == DType.INT16: