aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Kunze <eric.kunze@arm.com>2021-08-17 14:57:46 -0700
committerEric Kunze <eric.kunze@arm.com>2021-09-16 10:59:28 -0700
commit173fc16f46b2938ff49a39fb2dad31c54161a874 (patch)
tree0e17d2d391353fab462fa78e854538a9f2e4a629
parent32de3912884dc2dc1425b61c419bbd30f2adbbbd (diff)
downloadspecification-173fc16f46b2938ff49a39fb2dad31c54161a874.tar.gz
Clarify range limitations for tensors
Catch zero and negative sized tensors. Clarify configuration of bool_t in the reference model. int4_t limitations on -8 to stay symmetric around 0. Pad values must be >= 0. Stride,dilation values must be >= 1. Change-Id: Idb6ef740f855912a8340475ba319816f90c9b051 Signed-off-by: Eric Kunze <eric.kunze@arm.com>
-rw-r--r--chapters/activation_funcs.adoc1
-rw-r--r--chapters/data_layout.adoc27
-rw-r--r--chapters/ewise_binary.adoc6
-rw-r--r--chapters/image.adoc6
-rw-r--r--chapters/introduction.adoc28
-rw-r--r--chapters/pseudocode.adoc10
-rw-r--r--chapters/tensor_ops.adoc17
7 files changed, 80 insertions, 15 deletions
diff --git a/chapters/activation_funcs.adoc b/chapters/activation_funcs.adoc
index a27d1bf..59030d8 100644
--- a/chapters/activation_funcs.adoc
+++ b/chapters/activation_funcs.adoc
@@ -27,6 +27,7 @@ Clamp to an arbitrary minimum and maximum value. Note that the maximum and minim
*Operation Function:*
[source,c++]
----
+ERROR_IF(max_val < min_val);
for_each(index in shape) {
acc_t value = tensor_read<in_t>(input, shape, index);
acc = (in_t)apply_clip<acc_t>(value, min_val, max_val);
diff --git a/chapters/data_layout.adoc b/chapters/data_layout.adoc
index e625085..834030c 100644
--- a/chapters/data_layout.adoc
+++ b/chapters/data_layout.adoc
@@ -81,6 +81,11 @@ Zero-pads a tensor along borders of each dimension.
[source,c++]
----
+ERROR_IF(in_t != int8_t && input1_zp != 0); // Zero point only allowed for int8_t
+// Pad values must be >= 0.
+for_each(value in padding) {
+ ERROR_IF(value < 0);
+}
for_each(index in shape) {
index1 = index;
for(i = 0; i < rank(shape); i++) {
@@ -113,7 +118,7 @@ Returns a tensor with the same type/values as the input, with a new shape specif
|Argument|Type|Name|Shape|Description
|Input|in_t*|input1|shape1|Input tensor
-|Attribute|int|new_shape|[rank(output)]|List of values, with each element giving the size of the result tensor for the given dimension. At most one dimension may be given as-1 to automatically calculate the dimension size.
+|Attribute|int|new_shape|[rank(output)]|List of values, with each element giving the size of the result tensor for the given dimension. At most one dimension may be given as -1 to automatically calculate the dimension size.
|Output|in_t*|output|shape|Output tensor of same type, size as the input tensor
|===
@@ -197,6 +202,16 @@ No data conversion happens during a slice operation.
[source,c++]
----
+// Sanity check the given coordinates, ensure start and end are
+// within tensor bounds
+for_each(index in rank(input1)) {
+ ERROR_IF(start[index] < 0 ||
+ start[index] >= shape1[index]);
+ ERROR_IF(start[index] + size[index] < 0 ||
+ start[index] + size[index] >= shape1[index]);
+ ERROR_IF(size[index] <= 0); //Output must be positive size
+}
+
for_each(index in shape) {
tmp_index = index;
for(i = 0; i < rank(shape); i++) {
@@ -270,7 +285,7 @@ Permutes the dimensions based on perm.
|Argument|Type|Name|Shape|Description
|Input|in_t*|input1|shape1|Input tensor with rank from 1 to 4
-|Attribute|int32_t|perms|[rank(input1)]|List of integers of length equal to the rank of input1.
+|Attribute|int32_t|perms|[rank(input1)]|List of integers of length equal to the rank of input1. Values must be valid dimensions within shape1, and may not be repeated.
|Output|in_t*|output|shape|Output tensor of same type, rank as the input tensor
|===
@@ -278,6 +293,14 @@ Permutes the dimensions based on perm.
[source,c++]
----
+for_each(index in perms) {
+ // Ensure each perms value is a valid value
+ ERROR_IF(index > rank(shape1));
+ ERROR_IF(index < 0);
+ // Ensure ranks aren't repeated
+ ERROR_IF(indexes_used[index] == true);
+ indexes_used[index] = true;
+}
for_each(index in shape) {
tmp_index = index;
for(i = 0; i < rank(shape); i++) {
diff --git a/chapters/ewise_binary.adoc b/chapters/ewise_binary.adoc
index 1a54a99..9265c47 100644
--- a/chapters/ewise_binary.adoc
+++ b/chapters/ewise_binary.adoc
@@ -241,6 +241,8 @@ for_each(index in shape) {
in_t value1 = tensor_read<in_t>(input1, shape1, index1);
in_t value2 = tensor_read<in_t>(input2, shape2, index2);
REQUIRE(value2 != 0);
+ // This catches the case where we divide minimum<in_t> by -1
+ // which is not representable in two's complement
REQUIRE((int64_t)value1 / value2 <= maximum<in_t>);
in_t acc = value1 / value2;
tensor_write<in_t>(output, shape, index, acc);
@@ -668,10 +670,12 @@ None
[source,c++]
----
+REQUIRE(length(table) == TABLE_SIZE);
for_each(index in shape) {
in_t value = tensor_read<in_t>(input, shape, index);
if (in_t == int8_t) {
- out_t acc = table[value];
+ // value is a signed int, convert to a 0 based index
+ out_t acc = table[value + 128];
} else {
out_t acc = apply_lookup(table, value);
}
diff --git a/chapters/image.adoc b/chapters/image.adoc
index f997992..b8789ff 100644
--- a/chapters/image.adoc
+++ b/chapters/image.adoc
@@ -56,16 +56,18 @@ None
// Ensure image size is supported by GPU APIs and that for integer
// implementations, position * stride does not overflow int32_t.
ERROR_IF(max(OH,OW,IH,IW) >= 16384);
+ERROR_IF(stride_x <= 0 || stride_y <= 0);
if (resize_t == float_t) {
// The shift attribute is not used for floating point
ERROR_IF(shift != 0);
+ ERROR_IF(stride_x > IW || stride_y > IH);
} else {
// if in_t=int8_t ensure that an int32_t accumulator can be used
ERROR_IF(shift < 1 || shift > 11);
// set a consistent lower limit of 1/16 downscale
// independent of the shift value to simplify implementations
- ERROR_IF(stride_x <= 0 || stride_x >= (16 << shift));
- ERROR_IF(stride_y <= 0 || stride_y >= (16 << shift));
+ ERROR_IF(stride_x >= (16 << shift));
+ ERROR_IF(stride_y >= (16 << shift));
// offset range is similarly limited to maximum 16 pixels irrespective
// of shift. Both stride and offset fit in int16_t when shift=11.
ERROR_IF(offset_x <= (-16 << shift) || offset_x >= (16 << shift));
diff --git a/chapters/introduction.adoc b/chapters/introduction.adoc
index 72c0298..acf3b69 100644
--- a/chapters/introduction.adoc
+++ b/chapters/introduction.adoc
@@ -194,17 +194,17 @@ For details of interpreting the quantized data, see the <<Quantization Scaling>>
|bool_t
| -
| -
-|Boolean value. Size implementation defined.
+|Boolean value. Size implementation defined. The TOSA reference model implements this as int8_t with 0 for false and 1 for true. All non-zero values are accepted on input as true.
|int4_t
| -7
| +7
-|Signed 4-bit twos-complement values.
+|Signed 4-bit two's-complement values. Excludes -8 to maintain a symmetric about zero range for weights.
|int8_t
| -128
| +127
-|Signed 8-bit twos-complement values.
+|Signed 8-bit two's-complement values.
|uint8_t
| 0
@@ -214,17 +214,17 @@ For details of interpreting the quantized data, see the <<Quantization Scaling>>
|int16_t
| -32768
| +32767
-|Signed 16-bit twos-complement values.
+|Signed 16-bit two's-complement values.
|int32_t
| -(1<<31)
| (1<<31)-1
-|Signed 32-bit twos-complement value.
+|Signed 32-bit two's-complement value.
|int48_t
| -(1<<47)
| (1<<47)-1
-|Signed 48-bit twos-complement value.
+|Signed 48-bit two's-complement value.
|float_t
| -infinity
@@ -243,7 +243,9 @@ Tensors have an associated tensorinfo that contains information about the tensor
* Data Type
* Shape
-The number of dimensions in a shape is called the rank. Thus a tensor shape is an array of integers of size rank(shape) with shape[i] giving the the number of elements for dimension i.
+The number of dimensions in a shape is called the rank.
+Thus a tensor shape is an array of integers of size rank(shape) with shape[i] giving the the number of elements for dimension i.
+The tensor shape in each dimension must be greater than or equal to 1.
The following pseudocode represents the operations that will happen to data elements as they are read in to be processed, or have their results written out.
*Functionality of tensor read*
@@ -253,9 +255,13 @@ The padding array represents the before and after pair for each dimension.
[source,c++]
----
-ERROR_IF((pad != NULL) && size(pad) != 2 * size(shape));
out_t tensor_read<in_t>(in_t *address, dim_t shape, dim_t index, in_t zero_point=0, dim_t pad=NULL) {
- ERROR_IF(in_t != int8_t && zero_point != 0)
+ ERROR_IF((pad != NULL) && size(pad) != 2 * size(shape));
+ ERROR_IF(in_t != int8_t && zero_point != 0);
+ // Ensure this is a proper tensor with each dimension having size >= 1
+ for_each(dimension_size in shape) {
+ REQUIRE(dimension_size >= 1);
+ }
unsigned offset = 0;
for (i = 0; i < rank(shape); i++) {
if (index[i] < 0) {
@@ -278,6 +284,10 @@ out_t tensor_read<in_t>(in_t *address, dim_t shape, dim_t index, in_t zero_point
----
tensor_write<type>(<type> *address, dim_t shape, dim_t index, <type> value) {
unsigned offset = 0;
+ // Ensure this is a proper tensor with each dimension having size >= 1
+ for_each(dimension_size in shape) {
+ REQUIRE(dimension_size >= 1);
+ }
for (i = 0; i < rank(shape); i++) {
REQUIRE(index[i] >= 0 && index[i] < shape[i]);
offset = offset * shape[i] + index[i];
diff --git a/chapters/pseudocode.adoc b/chapters/pseudocode.adoc
index d9d8836..e6b7604 100644
--- a/chapters/pseudocode.adoc
+++ b/chapters/pseudocode.adoc
@@ -120,7 +120,7 @@ float_t round_to_nearest_float(in_t f)
The behavior for ties is implementation dependent.
out_t sign_extend(in_t input)
- Only valid for twos complement integer values where out_t has more bits than in_t.
+ Only valid for two's complement integer values where out_t has more bits than in_t.
Output = input
Replicate the top bit of input for all bits between the top bit of input and the top bit of output.
@@ -141,3 +141,11 @@ in_t* flatten(in_t lists[]) {
}
}
----
+
+The following helper function returns the length of a list
+
+[source,c++]
+----
+int length(in_t input)
+ return number of elements in input
+----
diff --git a/chapters/tensor_ops.adoc b/chapters/tensor_ops.adoc
index 6bf9bf2..9c10450 100644
--- a/chapters/tensor_ops.adoc
+++ b/chapters/tensor_ops.adoc
@@ -99,6 +99,9 @@ This performs an average pooling over the given input tensor. A sliding window o
----
ERROR_IF(in_t != int8_t && input_zp != 0); // Zero point only for int8_t
ERROR_IF(in_t != int8_t && output_zp != 0); // Zero point only for int8_t
+ERROR_IF(kernel_y < 1 || kernel_x < 1); // kernel size must be >= 1
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(pad_top < 0 || pad_buttom < 0 || pad_left < 0 || pad_right < 0);
pad = flatten([0,0], pad, [0,0]);
for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W, 0 <= c < C ) {
in_t output_val;
@@ -170,6 +173,9 @@ Performs a 2D convolution over the given tensor input, using the weight tensor.
----
ERROR_IF(in_t != int8_t && input_zp != 0); // Zero point only for int8_t
ERROR_IF(weight_t != int8_t && weight_zp != 0);
+ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(dilation_y < 1 || dilation_x < 1);
pad = flatten([0,0], pad, [0,0]);
for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W; 0 <= oc < OC) {
acc_t acc = 0;
@@ -231,6 +237,9 @@ Performs a 3D convolution over the given input tensor.
----
ERROR_IF(in_t != int8_t && input_zp != 0); // Zero point only for int8_t
ERROR_IF(weight_t != int8_t && weight_zp != 0);
+ERROR_IF(pad_d0 < 0 || pad_d1 < 0 || pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+ERROR_IF(stride_d < 1 || stride_y < 1 || stride_x < 1);
+ERROR_IF(dilation_d < 1 || dilation_y < 1 || dilation_x < 1);
pad = flatten([0,0], pad, [0,0]);
for_each(0 <= n < N, 0 <= od < D, 0 <= oy < H, 0 <= ox < W; 0 <= oc < OC) {
acc_t acc = 0;
@@ -295,6 +304,9 @@ Performs 2D convolutions separately over each channel of the given tensor input,
----
ERROR_IF(in_t != int8_t && input_zp != 0); // Zero point only for int8_t
ERROR_IF(weight_t != int8_t && weight_zp != 0);
+ERROR_IF(pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0);
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(dilation_y < 1 || dilation_x < 1);
pad = flatten([0,0], pad, [0,0]);
for_each(0 <= n<N, 0 <= oy < H, 0 <= ox < W; 0 <= c < (C * M), 0 <= m < M) {
acc_t acc = 0;
@@ -447,6 +459,9 @@ None
[source,c++]
----
+ERROR_IF(kernel_y < 1 || kernel_x < 1); // kernel size must be >= 1
+ERROR_IF(stride_y < 1 || stride_x < 1);
+ERROR_IF(pad_top < 0 || pad_buttom < 0 || pad_left < 0 || pad_right < 0);
for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W, 0 <= c < C ) {
in_t acc = minimum_value<in_t>;
iy = oy * stride_y - pad_top;
@@ -507,6 +522,8 @@ Performs a 2D transposed convolution over the given tensor input, using the weig
----
ERROR_IF(in_t != int8_t && input_zp != 0); // Zero point only allowed for int8_t
ERROR_IF(weight_t != int8_t && weight_zp != 0);
+ERROR_IF(out_pad_top < 0 || out_pad_left < 0);
+ERROR_IF(stride_y < 1 || stride_x < 1);
for_each(index in out_shape) {
tensor_write<acc_t>(output, [N,OH,OW,OC], index, bias[index[3]])
}