diff options
author | Kevin Petit <kevin.petit@arm.com> | 2024-01-08 15:27:25 +0000 |
---|---|---|
committer | Kevin Petit <kevin.petit@arm.com> | 2024-01-15 10:19:01 +0000 |
commit | a7ac313a13f8f82b4b3ca9730bd746392f6600d9 (patch) | |
tree | 242c97650a951f48a5de96b44e0d5c124ea1c212 | |
parent | 8754ec288dc491f3a9e936e68a1fd35783c9808e (diff) | |
download | specification-a7ac313a13f8f82b4b3ca9730bd746392f6600d9.tar.gz |
Move operator pseudocode to separate files
This makes it easier to process the pseudocode automatically.
Change-Id: I84394192598e589de07d43a7af60b96788e14f86
Signed-off-by: Kevin Petit <kevin.petit@arm.com>
87 files changed, 1888 insertions, 1158 deletions
@@ -1,7 +1,7 @@ # # This confidential and proprietary software may be used only as # authorised by a licensing agreement from ARM Limited -# (C) COPYRIGHT 2020-2022 ARM Limited +# (C) COPYRIGHT 2020-2024 ARM Limited # ALL RIGHTS RESERVED # The entire notice above must be reproduced on all authorised # copies and copies may only be made to the extent permitted @@ -17,11 +17,13 @@ XMLLINT = xmllint HTMLDIR=out/html PDFDIR=out/pdf GENDIR=out/gen +PSEUDOCODEDIR := pseudocode -COMMON_ARGS= -a generated="$(abspath $(GENDIR))" +COMMON_ARGS= -a generated="$(abspath $(GENDIR))" -a pseudocode="$(abspath $(PSEUDOCODEDIR))" SPECSRC := tosa_spec.adoc ADOCFILES = $(wildcard chapters/[A-Za-z]*.adoc) $(wildcard $(GENDIR)/*/*.adoc) +PSEUDOCODEFILES = $(wildcard $(PSEUDOCODEDIR)/operators/*.tosac) $(wildcard $(PSEUDOCODEDIR)/operators/tables/*.tosac) SPECFILES = $(ADOCFILES) tosa.css FIGURES = $(wildcard figures/*.svg) SPECXML := tosa.xml @@ -76,11 +78,11 @@ $(GEN): $(SPECXML) $(GENSCRIPTS) tools/genspec.py --xml $(SPECXML) --outdir $(GENDIR) @touch $@ -$(HTMLDIR)/tosa_spec.html: $(SPECSRC) $(SPECFILES) $(GEN) +$(HTMLDIR)/tosa_spec.html: $(SPECSRC) $(SPECFILES) $(GEN) $(PSEUDOCODEFILES) $(MKDIR) $(HTMLDIR) $(ASCIIDOC) -b html5 -a stylesheet=tosa.css $(COMMON_ARGS) -o $@ $< -$(PDFDIR)/tosa_spec.pdf: $(SPECSRC) $(SPECFILES) $(GEN) +$(PDFDIR)/tosa_spec.pdf: $(SPECSRC) $(SPECFILES) $(GEN) $(PSEUDOCODEFILES) $(MKDIR) $(PDFDIR) $(ASCIIDOC) -r asciidoctor-pdf -b pdf $(COMMON_ARGS) -o $@ $(SPECSRC) diff --git a/chapters/activation_funcs.adoc b/chapters/activation_funcs.adoc index 1acaf56..1be3168 100644 --- a/chapters/activation_funcs.adoc +++ b/chapters/activation_funcs.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2021 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -20,12 +20,7 @@ include::{generated}/operators/CLAMP.adoc[] [source,c++] ---- -ERROR_IF(max_val < min_val); -for_each(index in shape) { - in_out_t value = tensor_read<in_out_t>(input, shape, index); - value = apply_clip<in_out_t>(value, min_val, max_val); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/CLAMP.tosac[lines=10..-1] ---- ==== ERF @@ -43,13 +38,7 @@ The ERF table has 513 entries each of 16-bit precision and covering the input ra [source,c++] ---- -int16_t erf_reference(int16_t x) { // input x range is -256 to + 256 inclusive - F64 v = (double)x / (double)64; - v = erf(v); - return round_to_nearest_int(32768.0 * v); -} - -generate_lookup_table(&erf_table, &erf_reference); +include::{pseudocode}/operators/tables/ERF.tosac[lines=10..-1] ---- include::{generated}/operators/ERF.adoc[] @@ -72,24 +61,14 @@ This sigmoid table has 513 entries each of 16-bit precision and covering the inp .Code for generating 16-bit sigmoid table [source,c++] ---- -int16_t sigmoid_reference(int16_t x) { // input x range is -256 to + 256 inclusive - fp64_t v = (fp64_t)x / (fp64_t)16; - v = 1.0/(1.0 + exp(-v)); - return round_to_nearest_int(32768.0 * v); -} - -generate_lookup_table(&sigmoid_table, &sigmoid_reference); +include::{pseudocode}/operators/tables/SIGMOID.tosac[lines=10..-1] ---- include::{generated}/operators/SIGMOID.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input, shape, index); - value = sigmoid<in_out_t>(value1); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/SIGMOID.tosac[lines=10..-1] ---- ==== TANH @@ -109,23 +88,12 @@ This tanh_table has 513 entries each of 16-bit precision and covering the input .Calculation of an example 16-bit tanh table [source,c++] ---- -int16_t tanh_reference(int16_t x) { // input x range is -256 to +256 inclusive - fp64_t v = (fp64_t)x/(fp64_t)32; - v = exp(-2.0*v); - v = (1.0-v)/(1.0+v); - return round_to_nearest_int(32768.0 * v); -} - -generate_lookup_table(&tanh_table, &tanh_reference); +include::{pseudocode}/operators/tables/TANH.tosac[lines=10..-1] ---- include::{generated}/operators/TANH.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input, shape, index); - value = tanh<in_out_t>(value1); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/TANH.tosac[lines=10..-1] ---- diff --git a/chapters/comparison.adoc b/chapters/comparison.adoc index 4ef52d6..5535b40 100644 --- a/chapters/comparison.adoc +++ b/chapters/comparison.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2022 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -17,19 +17,7 @@ include::{generated}/operators/EQUAL.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_t value1 = tensor_read<in_t>(input1, shape1, index1); - in_t value2 = tensor_read<in_t>(input2, shape2, index2); - out_t result; - if (isNaN(value1) || isNaN(value2)) - result = False; - else - result = (value1 == value2) ? True : False; - tensor_write<out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/EQUAL.tosac[lines=10..-1] ---- ==== GREATER @@ -40,19 +28,7 @@ include::{generated}/operators/GREATER.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_t value1 = tensor_read<in_t>(input1, shape1, index1); - in_t value2 = tensor_read<in_t>(input2, shape2, index2); - out_t result; - if (isNaN(value1) || isNaN(value2)) - result = False; - else - result = (value1 > value2) ? True : False; - tensor_write<out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/GREATER.tosac[lines=10..-1] ---- ==== GREATER_EQUAL @@ -63,17 +39,5 @@ include::{generated}/operators/GREATER_EQUAL.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_t value1 = tensor_read<in_t>(input1, shape1, index1); - in_t value2 = tensor_read<in_t>(input2, shape2, index2); - out_t result; - if (isNaN(value1) || isNaN(value2)) - result = False; - else - result = (value1 >= value2) ? True : False; - tensor_write<out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/GREATER_EQUAL.tosac[lines=10..-1] ---- diff --git a/chapters/control_flow.adoc b/chapters/control_flow.adoc index 9de9c72..2da2424 100644 --- a/chapters/control_flow.adoc +++ b/chapters/control_flow.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2021 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -19,20 +19,7 @@ include::{generated}/operators/COND_IF.adoc[] [source,c++] ---- -ERROR_IF(tosa_nesting_depth >= MAX_NESTING); -ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(then_graph)); -ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(else_graph)); -ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(then_graph)); -ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(else_graph)); -ERROR_IF(tensor_size(shape) != 1); - -tosa_nesting_depth++; -if (condition[0]) { - tosa_execute_graph(then_graph, input_list, output_list); -} else { - tosa_execute_graph(else_graph, input_list, output_list); -} -tosa_nesting_depth--; +include::{pseudocode}/operators/COND_IF.tosac[lines=10..-1] ---- ==== WHILE_LOOP @@ -43,28 +30,5 @@ include::{generated}/operators/WHILE_LOOP.adoc[] [source,c++] ---- -ERROR_IF(tosa_nesting_depth >= MAX_NESTING); -ERROR_IF(tensor_list_shape(input_list) != tosa_list_shape(output_list)); -ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(cond_graph)); -ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(body_graph)); -ERROR_IF(tensor_list_shape(input_list) != tosa_output_shape(body_graph)); -// Condition graph output must be a single element tensor with a single bool value -ERROR_IF(tensor_size(tosa_output_shape(cond_graph)) != 1); -ERROR_IF(tosa_output_type(cond_graph) != bool_t); - -// The iteration number 'i' is included to give unique names to variables -// in each iteration of the loop and is not required by implementations -int32_t i=0; // iteration number -tensor_list_t list[]; // array of tensor lists indexed by iteration -bool_t *condition[]; // array of condition tensors indexed by iteration -list[i] = input_list; // copy input data as list[0] -tosa_nesting_depth++; -tosa_execute_graph(cond_graph, list[i], [ condition[i] ]); // initial condition -while (condition[i][0]) { - tosa_execute_graph(body_graph, list[i], list[i+1]); - i = i+1; - tosa_execute_graph(cond_graph, list[i], [ condition[i] ]); -} -tosa_nesting_depth--; -output_list = list[i]; +include::{pseudocode}/operators/WHILE_LOOP.tosac[lines=10..-1] ---- diff --git a/chapters/data_layout.adoc b/chapters/data_layout.adoc index 2d48eb1..1ce92be 100644 --- a/chapters/data_layout.adoc +++ b/chapters/data_layout.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2023 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -17,30 +17,7 @@ include::{generated}/operators/CONCAT.adoc[] [source,c] ---- -ERROR_IF(axis < 0 || axis >= max(1,rank(shapes1[0]))); -ERROR_IF(shape[axis] != sum(shape_dim(shapes1[k], axis) for all k)) -ERROR_IF(in_out_t == shape_t && rank(shape) > 1); -// The following checks ensure all inputs are compatible for concatenation -for_each(input_shape in shapes1) { - ERROR_IF(rank(input_shape) != rank(shapes1[0])); - for_each(index in input_shape) { - ERROR_IF(index != axis && input_shape[index] != shapes1[0][index]); - } -} -for_each(index1 in shape) { - dim_t index2 = index1; - for (tensor t = 0; t < length(input1); t++) { - // Continue to concatenate along axis from each tensor - // For each output location, we are looking for the - // appropriate input tensor - if (index2[axis] >= 0 && index2[axis] < shape_dim(shapes1[t], axis)) { - in_out_t value = tensor_read<in_out_t>(input1[t], shapes1[t], index2); - tensor_write<in_out_t>(output, shape, index1, value); - } - index2[axis] = index2[axis] - shape_dim(shapes1[t], axis); - } -} - +include::{pseudocode}/operators/CONCAT.tosac[lines=10..-1] ---- ==== PAD @@ -53,24 +30,7 @@ include::{generated}/operators/PAD.adoc[] [source,c++] ---- -// Check output shape matches the padded input shape -ERROR_IF(rank(shape) != rank(shape1)); -for (i = 0; i < rank(shape); i++) { - ERROR_IF(padding[i,0] < 0 || padding[i,1] < 0); - ERROR_IF(shape[i] != padding[i, 0] + shape1[i] + padding[i, 1]); -} -for_each(index in shape) { - dim_t index1 = index; - bool_t is_pad = false; - for(i = 0; i < rank(shape); i++) { - index1[i] = index1[i] - padding[i,0]; - if (index1[i] < 0 || index[i] >= length(shape[i])) { - is_pad = true; - } - } - in_out_t value = is_pad ? pad_const : tensor_read<in_out_t>(input1, shape1, index1); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/PAD.tosac[lines=10..-1] ---- ==== DIM @@ -81,8 +41,7 @@ include::{generated}/operators/DIM.adoc[] [source,c++] ---- -ERROR_IF(axis >= rank(shape)); -tensor_write<shape_t>(output, [], [], shape_dim(shape, axis)); +include::{pseudocode}/operators/DIM.tosac[lines=10..-1] ---- ==== RESHAPE @@ -93,18 +52,7 @@ include::{generated}/operators/RESHAPE.adoc[] [source,c++] ---- -ERROR_IF(tensor_size(shape1) != tensor_size(shape)); - -for_each(index in shape) { - // Calculate flattened index for the output location (index) - size_t offset = tensor_index_to_offset(shape, index); - // Now convert to the location in the input - dim_t tmp_index = tensor_offset_to_index(shape1, offset); - - // Now read/write the value - in_out_t val = tensor_read<in_out_t>(input1, shape1, tmp_index); - tensor_write<in_out_t>(output, shape, index, val); -} +include::{pseudocode}/operators/RESHAPE.tosac[lines=10..-1] ---- ==== REVERSE @@ -115,13 +63,7 @@ include::{generated}/operators/REVERSE.adoc[] [source,c++] ---- -ERROR_IF(axis < 0 || axis >= rank(shape)); -for_each(index in shape) { - dim_t tmp_index = index; - tmp_index[axis] = shape[axis] - 1 - index[axis]; - in_out_t value = tensor_read<in_out_t>(input, shape, tmp_index); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/REVERSE.tosac[lines=10..-1] ---- ==== SLICE @@ -133,25 +75,7 @@ include::{generated}/operators/SLICE.adoc[] [source,c++] ---- -ERROR_IF(rank(shape1) != length(start) || rank(shape1) != length(size)); -ERROR_IF(rank(shape1) != rank(shape)); -// Sanity check the given coordinates, ensure start and end are -// within tensor bounds -for_each(index in rank(shape1)) { - ERROR_IF(start[index] < 0); - ERROR_IF(size[index] <= 0); //Output must be positive size - ERROR_IF(start[index] + size[index] > shape1[index]); - ERROR_IF(shape[index] != size[index]); -} - -for_each(index in shape) { - dim_t tmp_index = index; - for(i = 0; i < rank(shape); i++) { - tmp_index[i] = index[i] + start[i]; - } - in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/SLICE.tosac[lines=10..-1] ---- ==== TILE @@ -162,17 +86,7 @@ include::{generated}/operators/TILE.adoc[] [source,c++] ---- -ERROR_IF(rank(shape1) != rank(shape)); - -for_each(index in shape) { - dim_t tmp_index = index; - for(i = 0; i < rank(shape); i++) { - ERROR_IF(shape1[i] * multiples[i] != shape[i]); - tmp_index[i] = index[i] % shape1[i]; - } - in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/TILE.tosac[lines=10..-1] ---- ==== TRANSPOSE @@ -184,30 +98,5 @@ include::{generated}/operators/TRANSPOSE.adoc[] [source,c++] ---- -ERROR_IF(rank(shape1) != rank(shape)); -ERROR_IF(tensor_size(shape1) != tensor_size(shape)); - -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; -} - -// Ensure that the output shapes have the properly -// permuted shapes -for(i = 0; i < rank(shape); i++) { - ERROR_IF(shape1[perms[i]] != shape[i]) -} - -for_each(index in shape) { - dim_t tmp_index = index; - for(i = 0; i < rank(shape); i++) { - tmp_index[perms[i]] = index[i] - } - in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index); - tensor_write<in_out_t>(output, shape, index, value); -} +include::{pseudocode}/operators/TRANSPOSE.tosac[lines=10..-1] ---- diff --git a/chapters/ewise_binary.adoc b/chapters/ewise_binary.adoc index 876ab4b..3cc2ecb 100644 --- a/chapters/ewise_binary.adoc +++ b/chapters/ewise_binary.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2023 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -18,23 +18,7 @@ include::{generated}/operators/ADD.adoc[] [source,c++] ---- -if (in_out_t == shape_t) { - ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); - shape_t value1 = tensor_read<shape_t>(input1, [], []); - shape_t value2 = tensor_read<shape_t>(input2, [], []); - shape_t result = apply_add_s<shape_t>(value1, value2); - tensor_write<shape_t>(output, [], [], result); -} else { - ERROR_IF(shape != broadcast_shape(shape1, shape2)); - for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = apply_add_s<in_out_t>(value1, value2); - tensor_write<in_out_t>(output, shape, index, result); - } -} +include::{pseudocode}/operators/ADD.tosac[lines=10..-1] ---- ==== ARITHMETIC_RIGHT_SHIFT @@ -46,26 +30,7 @@ include::{generated}/operators/ARITHMETIC_RIGHT_SHIFT.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - - // Ensure that shift amount is appropriate for the data type - REQUIRE((in_out_t == i32_t && 0 <= value2 && value2 <= 31) || - (in_out_t == i16_t && 0 <= value2 && value2 <= 15) || - (in_out_t == i8_t && 0 <= value2 && value2 <= 7)); - - in_out_t result = apply_arith_rshift<in_out_t>(value1, value2); - if (round == true && static_cast<int32_t>(value2) > 0 && - (apply_arith_rshift<in_out_t>(value1, apply_sub_s<in_out_t>(value2, 1)) & 1 != 0) { - result = result + 1; - } - result = apply_clip_s<in_out_t>(result, minimum_s<in_out_t>, maximum_s<in_out_t>); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/ARITHMETIC_RIGHT_SHIFT.tosac[lines=10..-1] ---- ==== BITWISE_AND @@ -77,15 +42,7 @@ include::{generated}/operators/BITWISE_AND.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = value1 & value2; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/BITWISE_AND.tosac[lines=10..-1] ---- ==== BITWISE_OR @@ -97,15 +54,7 @@ include::{generated}/operators/BITWISE_OR.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = value1 | value2; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/BITWISE_OR.tosac[lines=10..-1] ---- ==== BITWISE_XOR @@ -117,15 +66,7 @@ include::{generated}/operators/BITWISE_XOR.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = value1 ^ value2; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/BITWISE_XOR.tosac[lines=10..-1] ---- ==== INTDIV @@ -140,28 +81,7 @@ include::{generated}/operators/INTDIV.adoc[] [source,c++] ---- -if (in_out_t == shape_t) { - ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); - shape_t value1 = tensor_read<shape_t>(input1, [], []); - shape_t value2 = tensor_read<shape_t>(input2, [], []); - REQUIRE(value2 != 0); - shape_t result = value1 / value2; - tensor_write<shape_t>(output, [], [], result); -} else { - ERROR_IF(shape != broadcast_shape(shape1, shape2)); - for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - REQUIRE(value2 != 0); - // This catches the case where we divide minimum<in_out_t> by -1 - // which is not representable in two's complement - REQUIRE(static_cast<int64_t>(value1) / static_cast<int64_t>(value2) <= maximum_s<in_out_t>); - in_out_t result = apply_intdiv_s<in_out_t>(value1, value2); - tensor_write<in_out_t>(output, shape, index, result); - } -} +include::{pseudocode}/operators/INTDIV.tosac[lines=10..-1] ---- ==== LOGICAL_AND @@ -173,15 +93,7 @@ include::{generated}/operators/LOGICAL_AND.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = value1 && value2; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/LOGICAL_AND.tosac[lines=10..-1] ---- ==== LOGICAL_LEFT_SHIFT @@ -193,16 +105,7 @@ include::{generated}/operators/LOGICAL_LEFT_SHIFT.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - REQUIRE(0 <= value2 && value2 <= 31); - in_out_t result = value1 << value2; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/LOGICAL_LEFT_SHIFT.tosac[lines=10..-1] ---- ==== LOGICAL_RIGHT_SHIFT @@ -214,17 +117,7 @@ include::{generated}/operators/LOGICAL_RIGHT_SHIFT.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - REQUIRE(0 <= static_cast<int32_t>(value2) && static_cast<int32_t>(value2) <= 31); - // Logical shifts happen as unsigned types internally - in_out_t result = apply_logical_rshift<in_out_t>(value1, value2); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/LOGICAL_RIGHT_SHIFT.tosac[lines=10..-1] ---- ==== LOGICAL_OR @@ -236,15 +129,7 @@ include::{generated}/operators/LOGICAL_OR.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = value1 || value2; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/LOGICAL_OR.tosac[lines=10..-1] ---- ==== LOGICAL_XOR @@ -256,15 +141,7 @@ include::{generated}/operators/LOGICAL_XOR.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = value1 != value2; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/LOGICAL_XOR.tosac[lines=10..-1] ---- ==== MAXIMUM @@ -276,15 +153,7 @@ include::{generated}/operators/MAXIMUM.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = apply_max_s<in_out_t>(value1, value2); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/MAXIMUM.tosac[lines=10..-1] ---- ==== MINIMUM @@ -296,15 +165,7 @@ include::{generated}/operators/MINIMUM.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = apply_min_s(value1, value2); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/MINIMUM.tosac[lines=10..-1] ---- ==== MUL @@ -316,34 +177,7 @@ include::{generated}/operators/MUL.adoc[] [source,c++] ---- -if (in_out_t == shape_t) { - ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); - shape_t value1 = tensor_read<shape_t>(input1, [], []); - shape_t value2 = tensor_read<shape_t>(input2, [], []); - shape_t result = value1 * value2; - tensor_write<shape_t>(output, [], [], result); -} else { - REQUIRE(0 <= shift && shift <= 63); - REQUIRE(in_t == int32_t || shift == 0); - ERROR_IF(shape != broadcast_shape(shape1, shape2)); - for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_t value1 = tensor_read<in_t>(input1, shape1, index1); - in_t value2 = tensor_read<in_t>(input2, shape2, index2); - out_t result; - if (in_t == i32_t && shift > 0) { - int64_t product = sign_extend<int64_t>(value1) * sign_extend<int64_t>(value2); - int64_t round = static_cast<int64_t>(1) << (shift - 1); - product = (product + round) >> shift; - REQUIRE(product >= minimum_s<i32_t> && product <= maximum_s<i32_t>) - result = product; - } else { - result = apply_mul_s(value1, value2); // low 32-bits of result for i32_t - } - tensor_write<out_t>(output, shape, index, result); - } -} +include::{pseudocode}/operators/MUL.tosac[lines=10..-1] ---- ==== POW @@ -355,15 +189,7 @@ include::{generated}/operators/POW.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(shape1, shape2)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = apply_pow<in_out_t>(value1, value2); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/POW.tosac[lines=10..-1] ---- ==== SUB @@ -375,23 +201,7 @@ include::{generated}/operators/SUB.adoc[] [source,c++] ---- -if (in_out_t == shape_t) { - ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); - shape_t value1 = tensor_read<shape_t>(input1, [], []); - shape_t value2 = tensor_read<shape_t>(input2, [], []); - shape_t result = apply_sub<shape_t>(value1, value2); - tensor_write<shape_t>(output, [], [], result); -} else { - ERROR_IF(shape != broadcast_shape(shape1, shape2)); - for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t result = apply_sub_s<in_out_t>(value1, value2); - tensor_write<in_out_t>(output, shape, index, result); - } -} +include::{pseudocode}/operators/SUB.tosac[lines=10..-1] ---- ==== TABLE @@ -414,16 +224,5 @@ include::{generated}/operators/TABLE.adoc[] [source,c++] ---- -REQUIRE(length(table) == TABLE_SIZE); -for_each(index in shape) { - in_t value = tensor_read<in_t>(input, shape, index); - out_t result; - if (in_t == i8_t) { - // value is a signed int, convert to a 0 based index - result = table[static_cast<int16_t>(value) + 128]; - } else { - result = apply_lookup_s(static_cast<int16_t>(table), static_cast<int16_t>(value)); - } - tensor_write<out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/TABLE.tosac[lines=10..-1] ---- diff --git a/chapters/ewise_ternary.adoc b/chapters/ewise_ternary.adoc index 57cf599..d6ea453 100644 --- a/chapters/ewise_ternary.adoc +++ b/chapters/ewise_ternary.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2021 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -17,20 +17,5 @@ include::{generated}/operators/SELECT.adoc[] [source,c++] ---- -ERROR_IF(shape != broadcast_shape(broadcast_shape(shape1, shape2), shape3)); -for_each(index in shape) { - dim_t index1 = apply_broadcast(shape, shape1, index); - dim_t index2 = apply_broadcast(shape, shape2, index); - dim_t index3 = apply_broadcast(shape, shape3, index); - bool_t value1 = tensor_read<bool_t>(input1, shape1, index1); - in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); - in_out_t value3 = tensor_read<in_out_t>(input3, shape3, index3); - in_out_t result; - if (value1) { - result = value2; - } else { - result = value3; - } - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/SELECT.tosac[lines=10..-1] ---- diff --git a/chapters/ewise_unary.adoc b/chapters/ewise_unary.adoc index d3eacc4..4b8cd4d 100644 --- a/chapters/ewise_unary.adoc +++ b/chapters/ewise_unary.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2023 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -24,16 +24,7 @@ include::{generated}/operators/ABS.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - if (is_floating_point(in_out_t) && value1 == -0.0) { - value1 = 0.0; - } - if (static_cast<int32_t>(value1) < 0.0) { - value1 = apply_sub_s<in_out_t>(0, value1); - } - tensor_write<in_out_t>(output, shape, index, value1); -} +include::{pseudocode}/operators/ABS.tosac[lines=10..-1] ---- ==== BITWISE_NOT @@ -44,11 +35,7 @@ include::{generated}/operators/BITWISE_NOT.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - in_out_t result = ~value1; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/BITWISE_NOT.tosac[lines=10..-1] ---- ==== CEIL @@ -66,11 +53,7 @@ include::{generated}/operators/CEIL.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - in_out_t result = apply_ceil<in_out_t>(value1); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/CEIL.tosac[lines=10..-1] ---- ==== CLZ @@ -81,11 +64,7 @@ include::{generated}/operators/CLZ.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - in_out_t result = count_leading_zeros(value1); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/CLZ.tosac[lines=10..-1] ---- ==== EXP @@ -103,11 +82,7 @@ include::{generated}/operators/EXP.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - in_out_t result = apply_exp<in_out_t>(value1); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/EXP.tosac[lines=10..-1] ---- ==== FLOOR @@ -125,11 +100,7 @@ include::{generated}/operators/FLOOR.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - in_out_t result = apply_floor<in_out_t>(value1); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/FLOOR.tosac[lines=10..-1] ---- ==== LOG @@ -147,11 +118,7 @@ include::{generated}/operators/LOG.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - in_out_t result = apply_log<in_out_t>(value1); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/LOG.tosac[lines=10..-1] ---- ==== LOGICAL_NOT @@ -162,11 +129,7 @@ include::{generated}/operators/LOGICAL_NOT.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index); - in_out_t result = !value1; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/LOGICAL_NOT.tosac[lines=10..-1] ---- ==== NEGATE @@ -184,19 +147,7 @@ include::{generated}/operators/NEGATE.adoc[] [source,c++] ---- -ERROR_IF(in_out_t != i8_t && input1_zp != 0) // Zero point only for int8_t -ERROR_IF(in_out_t != i8_t && output_zp != 0) // Zero point only for int8_t -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); - acc_t value = apply_sub_s<acc_t>(sign_extend<acc_t>(value1), - sign_extend<acc_t>(input1_zp)); - value = apply_sub_s<acc_t>(0, value); - value = apply_add_s<acc_t>(value, sign_extend<acc_t>(output_zp)); - in_out_t result = truncate<in_out_t>(apply_clip_s<acc_t>(value, - minimum_s<in_out_t>, - maximum_s<in_out_t>)); - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/NEGATE.tosac[lines=10..-1] ---- ==== RECIPROCAL @@ -214,11 +165,7 @@ include::{generated}/operators/RECIPROCAL.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index); - in_out_t result = 1.0 / value1; - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/RECIPROCAL.tosac[lines=10..-1] ---- ==== RSQRT @@ -236,15 +183,5 @@ include::{generated}/operators/RSQRT.adoc[] [source,c++] ---- -for_each(index in shape) { - in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index); - in_out_t result; - if (value1 < 0) { - result = NaN; - } - else { - result = 1.0 / apply_sqrt<in_out_t>(value1); - } - tensor_write<in_out_t>(output, shape, index, result); -} +include::{pseudocode}/operators/RSQRT.tosac[lines=10..-1] ---- diff --git a/chapters/image.adoc b/chapters/image.adoc index 59e5ddf..1d2a212 100644 --- a/chapters/image.adoc +++ b/chapters/image.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2023 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -61,69 +61,5 @@ include::{generated}/operators/RESIZE.adoc[] [source,c++] ---- -// Ensure the 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(scale_y_n <= 0 || scale_y_d <= 0 || scale_x_n <= 0 || scale_x_d <= 0); -// if in_t=int8_t ensure that an int32_t accumulator can be used -ERROR_IF(scale_y_n > (1 << 11) || scale_x_n > (1 << 11)); -// set a consistent lower limit of 1/16 downscale to simplify implementations -ERROR_IF(scale_y_d >= 16 * scale_y_n || scale_x_d >= 16 * scale_x_n); -ERROR_IF(offset_y < -scale_y_n || offset_y >= 16 * scale_y_n); -ERROR_IF(offset_x < -scale_x_n || offset_x >= 16 * scale_x_n); -ERROR_IF(border_y < -16 * scale_y_n || border_y >= scale_y_n); -ERROR_IF(border_x < -16 * scale_x_n || border_x >= scale_x_n); -ERROR_IF(OH != idiv_check((IH - 1) * scale_y_n - offset_y + border_y, scale_y_d) + 1); -ERROR_IF(OW != idiv_check((IW - 1) * scale_x_n - offset_x + border_x, scale_x_d) + 1); -for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C) { - out_t acc; - resize_t dx, dy; - resize_t unit_x, unit_y; - - unit_x = (is_floating_point(resize_t)) ? 1.0 : scale_x_n; - unit_y = (is_floating_point(resize_t)) ? 1.0 : scale_y_n; - - int32_t y = oy * scale_y_d + offset_y; - int32_t x = ox * scale_x_d + offset_x; - int16_t iy = idiv_floor(y, scale_y_n); - int16_t ix = idiv_floor(x, scale_x_n); - int16_t ry = y - iy * scale_y_n; // (y % scale_y_n) - int16_t rx = x - ix * scale_x_n; // (x % scale_x_n) - - if (is_floating_point(resize_t)) { - dy = static_cast<resize_t>(ry) / static_cast<resize_t>(scale_y_n); - dx = static_cast<resize_t>(rx) / static_cast<resize_t>(scale_x_n); - } else { - dy = ry; - dx = rx; - } - // Note that -1 <= iy < IH and -1 <= ix < IW - int16_t iy0 = apply_max_s(iy, 0); - int16_t iy1 = apply_min_s(iy + 1, IH - 1); - int16_t ix0 = apply_max_s(ix, 0); - int16_t ix1 = apply_min_s(ix + 1, IW - 1); - if (mode==BILINEAR) { - using in_s_t = make_signed(in_t); // Use signed calculations for i8/i16 - in_s_t v00 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix0,c])); - in_s_t v01 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix1,c])); - in_s_t v10 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix0,c])); - in_s_t v11 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix1,c])); - acc = v00 * (unit_y - dy) * (unit_x - dx); - acc += v01 * (unit_y - dy) * dx; - acc += v10 * dy * (unit_x - dx); - acc += v11 * dy * dx; - tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc); - } else if (mode==NEAREST) { - int32_t iy, ix; - if (is_floating_point(resize_t)) { - iy = (dy >= 0.5) ? iy1 : iy0; - ix = (dx >= 0.5) ? ix1 : ix0; - } else { - iy = (2 * dy >= scale_y_n) ? iy1 : iy0; - ix = (2 * dx >= scale_x_n) ? ix1 : ix0; - } - in_t v = tensor_read<in_t>(input, [N,IH,IW,C], [n,iy,ix,c]); - tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], v); - } -} +include::{pseudocode}/operators/RESIZE.tosac[lines=10..-1] ---- diff --git a/chapters/reduction.adoc b/chapters/reduction.adoc index 19ff4ed..e3692de 100644 --- a/chapters/reduction.adoc +++ b/chapters/reduction.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2023 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -17,22 +17,7 @@ include::{generated}/operators/REDUCE_ALL.adoc[] [source,c] ---- -ERROR_IF(axis < 0 || axis >= rank(shape1)); -ERROR_IF(shape[axis] != 1); -left_shape = (axis > 1) ? shape[0:axis-1] : []; -right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; -for_each(left_index in left_shape) { - for_each(right_index in right_shape) { - in_out_t acc = true; - for (i = 0; i < shape1[axis]; i++) { - index = flatten(left_index, [i], right_index); - in_out_t value = tensor_read<in_out_t>(input, shape1, index); - acc = acc && value; - } - out_index = flatten(left_index, [0], right_index); - tensor_write<in_out_t>(output, shape, out_index, acc); - } -} +include::{pseudocode}/operators/REDUCE_ALL.tosac[lines=10..-1] ---- ==== REDUCE_ANY @@ -43,22 +28,7 @@ include::{generated}/operators/REDUCE_ANY.adoc[] [source,c] ---- -ERROR_IF(axis < 0 || axis >= rank(shape1)); -ERROR_IF(shape[axis] != 1); -left_shape = (axis > 1) ? shape[0:axis-1] : []; -right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; -for_each(left_index in left_shape) { - for_each(right_index in right_shape) { - in_out_t acc = false; - for (i = 0; i < shape1[axis]; i++) { - index = flatten(left_index, [i], right_index); - in_out_t value = tensor_read<in_out_t>(input, shape1, index); - acc = acc || value; - } - out_index = flatten(left_index, [0], right_index); - tensor_write<in_out_t>(output, shape, out_index, acc); - } -} +include::{pseudocode}/operators/REDUCE_ANY.tosac[lines=10..-1] ---- ==== REDUCE_MAX @@ -69,22 +39,7 @@ include::{generated}/operators/REDUCE_MAX.adoc[] [source,c] ---- -ERROR_IF(axis < 0 || axis >= rank(shape1)); -ERROR_IF(shape[axis] != 1); -left_shape = (axis > 1) ? shape[0:axis-1] : []; -right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; -for_each(left_index in left_shape) { - for_each(right_index in right_shape) { - in_out_t acc = minimum<in_out_t>; - for (i = 0; i < shape1[axis]; i++) { - index = flatten(left_index, [i], right_index); - in_out_t value = tensor_read<in_out_t>(input, shape1, index); - acc = apply_max_s<in_out_t>(acc, value); - } - out_index = flatten(left_index, [0], right_index); - tensor_write<in_out_t>(output, shape, out_index, acc); - } -} +include::{pseudocode}/operators/REDUCE_MAX.tosac[lines=10..-1] ---- ==== REDUCE_MIN @@ -95,22 +50,7 @@ include::{generated}/operators/REDUCE_MIN.adoc[] [source,c] ---- -ERROR_IF(axis < 0 || axis >= rank(shape1)); -ERROR_IF(shape[axis] != 1); -left_shape = (axis > 1) ? shape[0:axis-1] : []; -right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; -for_each(left_index in left_shape) { - for_each(right_index in right_shape) { - in_out_t acc = maximum<in_out_t>; - for (i = 0; i < shape1[axis]; i++) { - index = flatten(left_index, [i], right_index); - in_out_t value = tensor_read<in_out_t>(input, shape1, index); - acc = apply_min_s<in_out_t>(acc, value); - } - out_index = flatten(left_index, [0], right_index); - tensor_write<in_out_t>(output, shape, out_index, out); - } -} +include::{pseudocode}/operators/REDUCE_MIN.tosac[lines=10..-1] ---- ==== REDUCE_PRODUCT @@ -121,22 +61,7 @@ include::{generated}/operators/REDUCE_PRODUCT.adoc[] [source,c] ---- -ERROR_IF(axis < 0 || axis >= rank(shape1)); -ERROR_IF(shape[axis] != 1); -left_shape = (axis > 1) ? shape[0:axis-1] : []; -right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; -for_each(left_index in left_shape) { - for_each(right_index in right_shape) { - in_out_t acc = 1.0; - for (i = 0; i < shape1[axis]; i++) { - index = flatten(left_index, [i], right_index); - in_out_t value = tensor_read<in_out_t>(input, shape1, index); - acc = apply_mul_s<in_out_t>(acc, value); - } - out_index = flatten(left_index, [0], right_index); - tensor_write<in_out_t>(output, shape, out_index, acc); - } -} +include::{pseudocode}/operators/REDUCE_PRODUCT.tosac[lines=10..-1] ---- ==== REDUCE_SUM @@ -147,21 +72,5 @@ include::{generated}/operators/REDUCE_SUM.adoc[] [source,c] ---- -ERROR_IF(axis < 0 || axis >= rank(shape1)); -ERROR_IF(shape[axis] != 1); -left_shape = (axis > 1) ? shape[0:axis-1] : []; -right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; -for_each(left_index in left_shape) { - for_each(right_index in right_shape) { - acc_t acc = 0; - for (i = 0; i < shape1[axis]; i++) { - index = flatten(left_index, [i], right_index); - acc_t value = tensor_read<in_out_t>(input, shape1, index); - acc = apply_add_s<acc_t>(acc, value); - } - out_index = flatten(left_index, [0], right_index); - in_out_t result = static_cast<in_out_t>(acc); - tensor_write<in_out_t>(output, shape, out_index, result); - } -} +include::{pseudocode}/operators/REDUCE_SUM.tosac[lines=10..-1] ---- diff --git a/chapters/scatter_gather.adoc b/chapters/scatter_gather.adoc index d694d39..4632e3a 100644 --- a/chapters/scatter_gather.adoc +++ b/chapters/scatter_gather.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2021 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -18,12 +18,7 @@ include::{generated}/operators/GATHER.adoc[] [source,c++] ---- -for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) { - index_t k = tensor_read<index_t>(indices, [N,W], [n,w]); - REQUIRE(0 <= k && k < K); - in_out_t value = tensor_read<in_out_t>(values, [N,K,C], [n,k,c]); - tensor_write<in_out_t>(output, [N,W,C], [n,w,c], value); -} +include::{pseudocode}/operators/GATHER.tosac[lines=10..-1] ---- ==== SCATTER @@ -38,26 +33,5 @@ include::{generated}/operators/SCATTER.adoc[] [source,c++] ---- - -// The following array is used to check compliance that an output position -// is modified at most once. -bool_t output_modified[N,K,C]; - -// Copy the values_in tensor to the values_out tensor. -// Values not written by the scatter operation are unchanged in the output. -for_each(0 <= n < N, 0 <= k < K, 0 <= c < C) { - in_out_t value = tensor_read<in_out_t>(values_in, [N,K,C], [n,k,c]); - tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value); - output_modified[n,k,c]=false; -} - -// Now perform the SCATTER operation, modifying the positions from the indices tensor -for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) { - index_t k = tensor_read<index_t>(indices, [N,W], [n,w]); - REQUIRE(0 <= k && k < K); - REQUIRE(output_modified[n,k,c] == false); - in_out_t value = tensor_read<in_out_t>(input, [N,W,C], [n,w,c]); - tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value); - output_modified[n,k,c] = true; -} +include::{pseudocode}/operators/SCATTER.tosac[lines=10..-1] ---- diff --git a/chapters/tensor_ops.adoc b/chapters/tensor_ops.adoc index b3de433..3de5150 100644 --- a/chapters/tensor_ops.adoc +++ b/chapters/tensor_ops.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2023 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -17,34 +17,7 @@ include::{generated}/operators/ARGMAX.adoc[] [source,c++] ---- -ERROR_IF(axis < 0 || axis >= rank(shape1)); -if (axis == 0) { - left_shape = []; -} else { - left_shape = shape1[0:axis - 1]; -} -if (axis == rank(shape1)-1) { - right_shape = []; -} else { - right_shape = shape1[axis+1:rank(shape1) - 1]; -} -ERROR_IF(flatten(left_shape, right_shape) != shape); -for_each(left_index in left_shape) { - for_each(right_index in right_shape) { - in_t max_value = minimum_s<in_t>; - out_t max_index = 0; - for (i = 0; i < shape[axis]; i++) { - dim_t index = flatten(left_index, [i], right_index); - in_t value = tensor_read<in_t>(input, shape1, index); - if (apply_max_s<in_t>(value, max_value) != max_value) { - max_value = value; - max_index = i; - } - } - dim_t index = flatten(left_index, right_index); - tensor_write<out_t>(output, shape, index, max_index); - } -} +include::{pseudocode}/operators/ARGMAX.tosac[lines=10..-1] ---- ==== AVG_POOL2D @@ -57,47 +30,7 @@ include::{generated}/operators/AVG_POOL2D.adoc[] [source,c++] ---- -ERROR_IF(in_out_t != i8_t && input_zp != 0); // Zero point only for int8_t -ERROR_IF(in_out_t != i8_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_bottom < 0 || pad_left < 0 || pad_right < 0); -// Padding must be less than kernel size to avoid -// a divide-by-zero. -ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x); -ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y); -ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1); -ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1); - -for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW, 0 <= c < C ) { - in_out_t output_val; - acc_t acc = 0; - int count = 0; - index_t iy = oy * stride_y - pad_top; - index_t ix = ox * stride_x - pad_left; - for_each(0 <= ky < kernel_y, 0 <= kx < kernel_x) { - index_t y = iy + ky; - index_t x = ix + kx; - // Only values from the input tensor are used to calculate the - // average, padding does not count - if (0 <= y < IH and 0 <= x < IW) { - count++; - acc_t value = sign_extend<acc_t>(tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c])); - value = apply_sub_s<acc_t>(value, sign_extend<acc_t>(input_zp)); - acc = apply_add_s<acc_t>(acc, value); - } - } - if (is_float(in_out_t)) { - output_val = acc / static_cast<in_out_t>(count); - } else { - scale_t scale = reciprocal_scale(count); - acc = apply_scale_32(acc, scale.multiplier, scale.shift, false); - acc = apply_add_s<acc_t>(acc, sign_extend<acc_t>(output_zp)); - acc = apply_clip_s<acc_t>(acc, minimum_s<in_out_t>, maximum_s<in_out_t>); - output_val = static_cast<in_out_t>(acc); - } - tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], output_val); -} +include::{pseudocode}/operators/AVG_POOL2D.tosac[lines=10..-1] ---- ==== CONV2D @@ -108,37 +41,7 @@ include::{generated}/operators/CONV2D.adoc[] [source,c++] ---- -ERROR_IF(in_t != i8_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); -ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1); -ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1); -ERROR_IF(BC != OC && BC != 1); - -for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) { - out_t acc = 0; - index_t iy = oy * stride_y - pad_top; - index_t ix = ox * stride_x - pad_left; - for_each(0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) { - index_t y = iy + ky * dilation_y; - index_t x = ix + kx * dilation_x; - if (0 <= y < IH && 0 <= x < IW) { - out_t value = static_cast<out_t>(tensor_read<in_t>(input, - [N,IH,IW,IC], - [n,y,x,ic])); - out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, - [OC,KH,KW,IC], - [oc,ky,kx,ic])); - value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); - weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); - acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); - } - } - acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]); - tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc); -} +include::{pseudocode}/operators/CONV2D.tosac[lines=10..-1] ---- ==== CONV3D @@ -149,40 +52,7 @@ include::{generated}/operators/CONV3D.adoc[] [source,c++] ---- -ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t -ERROR_IF(weight_t != i8_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); -ERROR_IF(OD != idiv_check(ID - 1 + pad_d0 + pad_d1 - (KD - 1) * dilation_d, stride_d) + 1); -ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1); -ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1); -ERROR_IF(BC != OC && BC != 1); - -for_each(0 <= n < N, 0 <= od < OD, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) { - out_t acc = 0; - index_t id = od * stride_d - pad_d0; - index_t iy = oy * stride_y - pad_top; - index_t ix = ox * stride_x - pad_left; - for_each(0 <= kd < KD, 0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) { - index_t d = id + kd * dilation_d; - index_t y = iy + ky * dilation_y; - index_t x = ix + kx * dilation_x; - if (0 <= x < IW && 0 <= y < IH && 0 <= d < ID) { - out_t value = static_cast<out_t>(tensor_read<in_t>(input, - [N,ID,IH,IW,IC], - [n,d,y,x,ic])); - out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, - [OC,KD,KH,KW,IC], - [oc,kd,ky,kx,ic])); - value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); - weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); - acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); - } - } - acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]); - tensor_write<out_t>(output, [N,OD,OH,OW,OC], [n,od,oy,ox,oc], acc); -} +include::{pseudocode}/operators/CONV3D.tosac[lines=10..-1] ---- ==== DEPTHWISE_CONV2D @@ -193,37 +63,7 @@ include::{generated}/operators/DEPTHWISE_CONV2D.adoc[] [source,c++] ---- -ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t -ERROR_IF(weight_t != i8_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); -ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1); -ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1); -ERROR_IF(BC != C*M && BC != 1); - -for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C, 0 <= m < M) { - out_t acc = 0; - index_t iy = oy * stride_y - pad_top; - index_t ix = ox * stride_x - pad_left; - for_each(0 <= ky < KH, 0 <= kx < KW) { - index_t y = iy + ky * dilation_y; - index_t x = ix + kx * dilation_x; - if (0 <= y < IH && 0 <= x < IW) { - out_t value = static_cast<out_t>(tensor_read<in_t>(input, - [N,IH,IW,C], - [n,y,x,c])); - out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, - [KH,KW,C,M], - [ky,kx,c,m])); - value = apply_sub_s<out_t>(value, static_cast<out_t>input_zp); - weight = apply_sub_s<out_t>(weight, static_cast<out_t>weight_zp); - acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); - } - } - acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : (c * M) + m]); - tensor_write<out_t>(output, [N,OH,OW,C * M], [n,oy,ox,c * M + m], acc); -} +include::{pseudocode}/operators/DEPTHWISE_CONV2D.tosac[lines=10..-1] ---- ==== FFT2D @@ -247,28 +87,7 @@ include::{generated}/operators/FFT2D.adoc[] [source,c++] ---- -ERROR_IF(!power_of_two(H)); -ERROR_IF(!power_of_two(W)); - -float sign_val = 1.0; - -if (inverse) { - sign_val = -1.0; -} - -for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W) { - in_out_t sum_real = 0.0; - in_out_t sum_imag = 0.0; - for_each(0 <= iy < H, 0 <= ix < W) { - in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]); - in_out_t val_imag = tensor_read<in_out_t>(input_imag, [N,H,W], [n,iy,ix]); - float_t a = sign_val * 2 * pi() * ((iy * oy) / H + (ix * ox) / W); - sum_real += val_real * cos(a) + val_imag * sin(a); - sum_imag += -val_real * sin(a) + val_imag * cos(a); - } - tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real); - tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag); -} +include::{pseudocode}/operators/FFT2D.tosac[lines=10..-1] ---- ==== FULLY_CONNECTED @@ -279,22 +98,7 @@ include::{generated}/operators/FULLY_CONNECTED.adoc[] [source,c++] ---- -ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t -ERROR_IF(weight_t != i8_t && weight_zp != 0); -ERROR_IF(BC != OC && BC != 1); - -for_each(0 <= n < N, 0 <= oc < OC) { - out_t acc = 0; - for_each(0 <= ic < IC) { - out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IC], [n,ic])); - out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,IC], [oc,ic])); - value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); - weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); - acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); - } - acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]); - tensor_write<out_t>(output, [N,OC], [n,oc], acc); -} +include::{pseudocode}/operators/FULLY_CONNECTED.tosac[lines=10..-1] ---- ==== MATMUL @@ -305,18 +109,7 @@ include::{generated}/operators/MATMUL.adoc[] [source,c++] ---- -ERROR_IF(in_t != i8_t && (A_zp != 0 || B_zp != 0)); // Zero point only for int8_t -for_each(0 <= n < N, 0 <= h < H, 0 <= w < W) { - out_t acc = 0; - for_each(0 <= c < C) { - out_t value1 = static_cast<out_t>(tensor_read<in_t>(A, [N,H,C], [n,h,c])); - out_t value2 = static_cast<out_t>(tensor_read<in_t>(B, [N,C,W], [n,c,w])); - value1 = apply_sub_s<out_t>(value1, static_cast<out_t>(A_zp)); - value2 = apply_sub_s<out_t>(value2, static_cast<out_t>(B_zp)); - acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value1 * value2)); - } - tensor_write<out_t>(output, [N,H,W], [n,h,w], acc); -} +include::{pseudocode}/operators/MATMUL.tosac[lines=10..-1] ---- ==== MAX_POOL2D @@ -327,30 +120,7 @@ include::{generated}/operators/MAX_POOL2D.adoc[] [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_bottom < 0 || pad_left < 0 || pad_right < 0); -// Padding must be less than kernel size, otherwise no -// input values will be used. -ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x); -ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y); -ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1); -ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1); - -for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W, 0 <= c < C ) { - in_out_t acc = minimum_value<in_out_t>; - index_t iy = oy * stride_y - pad_top; - index_t ix = ox * stride_x - pad_left; - for_each( 0 <= ky < kernel_y, 0 <= kx < kernel_x ) { - index_t y = iy + ky; - index_t x = ix + kx; - if (y >= 0 && y < IH && x >= 0 && x < IW) { - in_out_t value = tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c]); - acc = apply_max_s<in_out_t>(acc, value); - } - } - tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc); -} +include::{pseudocode}/operators/MAX_POOL2D.tosac[lines=10..-1] ---- ==== RFFT2D @@ -368,21 +138,7 @@ include::{generated}/operators/RFFT2D.adoc[] [source,c++] ---- -ERROR_IF(!power_of_two(H)); -ERROR_IF(!power_of_two(W)); - -for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W/2 + 1) { - in_out_t sum_real = 0.0; - in_out_t sum_imag = 0.0; - for_each(0 <= iy < H, 0 <= ix < W) { - in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]); - float_t a = 2 * pi() * ((iy * oy) / H + (ix * ox) / W); - sum_real += val_real * cos(a); - sum_imag += -val_real * sin(a); - } - tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real); - tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag); -} +include::{pseudocode}/operators/RFFT2D.tosac[lines=10..-1] ---- ==== TRANSPOSE_CONV2D @@ -393,30 +149,5 @@ include::{generated}/operators/TRANSPOSE_CONV2D.adoc[] [source,c++] ---- -ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only allowed for int8_t -ERROR_IF(weight_t != i8_t && weight_zp != 0); -ERROR_IF(out_pad_top <= -KH || out_pad_bottom <= -KH); -ERROR_IF(out_pad_left <= -KW || out_pad_right <= -KW); -ERROR_IF(stride_y < 1 || stride_x < 1); -ERROR_IF(OH != (IH - 1) * stride_y + out_pad_top + out_pad_bottom + KH); -ERROR_IF(OW != (IW - 1) * stride_x + out_pad_left + out_pad_right + KW); -ERROR_IF(BC != OC && BC != 1); - -for_each(index in [N, OH, OW, OC]) { - tensor_write<out_t>(output, [N,OH,OW,OC], index, bias[(BC == 1) ? 0 : index[3]]) -} -for_each(0 <= n < N, 0 <= iy < IH, 0 <= ix < IW, 0 <= oc < OC, - 0 <= ic < IC, 0 <= ky < KH, 0 <= kx < KW) { - index_t oy = iy * stride_y + out_pad_top + ky; - index_t ox = ix * stride_x + out_pad_left + kx; - if (oy >= 0 && oy < OH && ox >= 0 && ox < OW) { - out_t acc = static_cast<out_t>(tensor_read<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc])); - out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IH,IW,IC], [n,iy,ix,ic])); - out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,KH,KW,IC], [oc,ky,kx,ic])); - value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); - weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); - acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); - tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc); - } -} +include::{pseudocode}/operators/TRANSPOSE_CONV2D.tosac[lines=10..-1] ---- diff --git a/chapters/type_conversion.adoc b/chapters/type_conversion.adoc index 90aca2d..71be3ac 100644 --- a/chapters/type_conversion.adoc +++ b/chapters/type_conversion.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2020-2023 ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -17,24 +17,7 @@ include::{generated}/operators/CAST.adoc[] [source,c++] ---- -for_each(index in shape) { - in_t in = tensor_read<in_t>(input, shape, index); - out_t out; - if (out_t == bool_t) { - out = (in != 0) ? true : false; - } else if (in_t == bool_t) { - out = (in) ? 1 : 0; - } else if (out_t == fp16_t || out_t == bf16_t || out_t == fp32_t) { - out = round_to_nearest_float(in); - } else if (in_t == fp16_t || in_t == bf16_t || in_t == fp32_t) { - out = apply_clip<out_t>(round_to_nearest_int(in), minimum<out_t>, maximum<out_t>); - } else if (sizeof(out_t) >= sizeof(in_t)) { - out = sign_extend<out_t>(in); - } else { - out = truncate(in); - } - tensor_write<out_t>(output, shape, index, out) -} +include::{pseudocode}/operators/CAST.tosac[lines=10..-1] ---- ==== RESCALE @@ -45,53 +28,5 @@ include::{generated}/operators/RESCALE.adoc[] [source,c++] ---- -for_each(index in shape) { - // uint16 values can have zero_point 0 or 32768 - // int8/uint8 can have zero point within their valid range - // No other types can have zero point != 0 - ERROR_IF(in_t != i8_t && - (in_t != i16_t || input_unsigned == False) && input_zp != 0); - ERROR_IF(out_t != i8_t && - (out_t != i16_t || output_unsigned == False) && output_zp != 0); - ERROR_IF(in_t == i16_t && input_unsigned == True && input_zp != 0 && input_zp != 32768); - ERROR_IF(out_t == i16_t && output_unsigned == True && output_zp != 0 && output_zp != 32768); - ERROR_IF(scale32 && in_t == i48_t); - ERROR_IF(!scale32 && double_round); - ERROR_IF(in_t == i16_t && out_t == i32_t && input_unsigned); - ERROR_IF(in_t == i32_t && out_t == i16_t && output_unsigned); - - in_t in_value = tensor_read<in_t>(input, shape, index); - - int48_t value, extended_in_zp; - if (input_unsigned) { - value = zero_extend<int48_t>(in_value); - extended_in_zp = zero_extend<int48_t>(input_zp); - } - else { - value = sign_extend<int48_t>(value); - extended_in_zp = sign_extend<int48_t>(input_zp); - } - - value = value - extended_in_zp; - int c = (per_channel) ? index[rank(input) - 1] : 0; - int32_t result = (scale32) ? - apply_scale_32(value, multiplier[c], shift[c], double_round) : - apply_scale_16(value, multiplier[c], shift[c]); - - if (output_unsigned) { - int32_t extended_out_zp = zero_extend<int32_t>(output_zp); - result = apply_add_s<int32_t>(result, extended_out_zp); - out_t out = static_cast<out_t>(apply_clip<int32_t>(result, - minimum_u<out_t>, - maximum_u<out_t>)); - } - else { - int32_t extended_out_zp = sign_extend<int32_t>(output_zp); - result = apply_add_s<int32_t>(result, extended_out_zp); - out_t out = static_cast<out_t>(apply_clip<int32_t>(result, - minimum_s<out_t>, - maximum_s<out_t>)); - } - tensor_write<out_t>(output, shape, index, out); -} +include::{pseudocode}/operators/RESCALE.tosac[lines=10..-1] ---- diff --git a/chapters/variable.adoc b/chapters/variable.adoc index 1f7da51..8522f5e 100644 --- a/chapters/variable.adoc +++ b/chapters/variable.adoc @@ -1,7 +1,7 @@ // // This confidential and proprietary software may be used only as // authorised by a licensing agreement from ARM Limited -// (C) COPYRIGHT 2023 ARM Limited +// (C) COPYRIGHT 2023-2024 ARM Limited // ALL RIGHTS RESERVED // The entire notice above must be reproduced on all authorised // copies and copies may only be made to the extent permitted @@ -21,31 +21,7 @@ include::{generated}/operators/VARIABLE.adoc[] [source,c++] ---- - -tensor_t var_tensor = variable_tensor_lookup(uid); - -// Invocation for the first time -if (var_tensor == NULL) { - // Allocate the persistent mutable memory for the variable tensor - tensor_t var_tensor = variable_tensor_allocate<var_t>(var_shape, uid); - - if (initial_value != NULL) { - REQUIRE(var_t == in_t); - REQUIRE(var_shape == shape); - for_each (index in shape) { - // Copy data from initial_value to var_tensor - in_t value = tensor_read<in_t>(initial_value, shape, index); - tensor_write<in_t>(var_tensor.data, var_shape, index, value); - } - var_tensor.is_written = true; - } -} else { // Variable tensor has already been declared - // It's invalid to declare the second variable with the same uid in a single graph execution, - REQUIRE(!var_tensor.seen); -} - -var_tensor.seen = true; - +include::{pseudocode}/operators/VARIABLE.tosac[lines=10..-1] ---- ==== VARIABLE_WRITE @@ -56,26 +32,7 @@ include::{generated}/operators/VARIABLE_WRITE.adoc[] [source,c++] ---- - -tensor_t. variable_tensor = variable_tensor_lookup(uid); -// Check this variable tensor has been declared -REQUIRE(variable_tensor); -// The tensor has to be seen before to be written to -// The seen variable is cleared before each graph execution and set in declaration -REQUIRE(variable_tensor.seen); -// Input tensor's shape and variable_tensor's shape have to match -REQUIRE(variable_tensor.shape == shape); -// Input tensor's shape and variable_tensor's type have to match -REQUIRE(variable_tensor.type == in_t); - -for_each (index in shape) { - // Write data from the input to the pseudo-buffer resource - in_t value = tensor_read<in_t>(input1, shape, index); - tensor_write<tensor_t>(variable_tensor.data, variable_tensor.shape, index, value); -} - -variable_tensor.is_written = true; - +include::{pseudocode}/operators/VARIABLE_WRITE.tosac[lines=10..-1] ---- ==== VARIABLE_READ @@ -86,21 +43,5 @@ include::{generated}/operators/VARIABLE_READ.adoc[] [source,c++] ---- - -tensor_t variable_tensor = variable_tensor_lookup(uid); -// Check this variable tensor has been decalred -REQUIRE(variable_tensor != NULL); -// Check this variable tensor has been written -REQUIRE(variable_tensor.is_written); -// Output tensor's shape and variable_tensor's shape have to match -REQUIRE(variable_tensor.shape == shape); -// Output tensor's shape and variable_tensor's type have to match -REQUIRE(variable_tensor.type == out_t); - -for_each (index in shape) { - // Read data from pseudo-buffer resource to the output - out_t value = tensor_read<tensor_t>(variable_tensor.data, variable_tensor.shape, index); - tensor_write<out_t>(input1, shape, index, value); -} - +include::{pseudocode}/operators/VARIABLE_READ.tosac[lines=10..-1] ---- diff --git a/pseudocode/operators/ABS.tosac b/pseudocode/operators/ABS.tosac new file mode 100644 index 0000000..5f7f56e --- /dev/null +++ b/pseudocode/operators/ABS.tosac @@ -0,0 +1,19 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + if (is_floating_point(in_out_t) && value1 == -0.0) { + value1 = 0.0; + } + if (static_cast<int32_t>(value1) < 0.0) { + value1 = apply_sub_s<in_out_t>(0, value1); + } + tensor_write<in_out_t>(output, shape, index, value1); +} diff --git a/pseudocode/operators/ADD.tosac b/pseudocode/operators/ADD.tosac new file mode 100644 index 0000000..61db3f6 --- /dev/null +++ b/pseudocode/operators/ADD.tosac @@ -0,0 +1,26 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +if (in_out_t == shape_t) { + ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); + shape_t value1 = tensor_read<shape_t>(input1, [], []); + shape_t value2 = tensor_read<shape_t>(input2, [], []); + shape_t result = apply_add_s<shape_t>(value1, value2); + tensor_write<shape_t>(output, [], [], result); +} else { + ERROR_IF(shape != broadcast_shape(shape1, shape2)); + for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = apply_add_s<in_out_t>(value1, value2); + tensor_write<in_out_t>(output, shape, index, result); + } +} diff --git a/pseudocode/operators/ARGMAX.tosac b/pseudocode/operators/ARGMAX.tosac new file mode 100644 index 0000000..bbe301f --- /dev/null +++ b/pseudocode/operators/ARGMAX.tosac @@ -0,0 +1,37 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape1)); +if (axis == 0) { + left_shape = []; +} else { + left_shape = shape1[0:axis - 1]; +} +if (axis == rank(shape1)-1) { + right_shape = []; +} else { + right_shape = shape1[axis+1:rank(shape1) - 1]; +} +ERROR_IF(flatten(left_shape, right_shape) != shape); +for_each(left_index in left_shape) { + for_each(right_index in right_shape) { + in_t max_value = minimum_s<in_t>; + out_t max_index = 0; + for (i = 0; i < shape[axis]; i++) { + dim_t index = flatten(left_index, [i], right_index); + in_t value = tensor_read<in_t>(input, shape1, index); + if (apply_max_s<in_t>(value, max_value) != max_value) { + max_value = value; + max_index = i; + } + } + dim_t index = flatten(left_index, right_index); + tensor_write<out_t>(output, shape, index, max_index); + } +} diff --git a/pseudocode/operators/ARITHMETIC_RIGHT_SHIFT.tosac b/pseudocode/operators/ARITHMETIC_RIGHT_SHIFT.tosac new file mode 100644 index 0000000..4077146 --- /dev/null +++ b/pseudocode/operators/ARITHMETIC_RIGHT_SHIFT.tosac @@ -0,0 +1,29 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + + // Ensure that shift amount is appropriate for the data type + REQUIRE((in_out_t == i32_t && 0 <= value2 && value2 <= 31) || + (in_out_t == i16_t && 0 <= value2 && value2 <= 15) || + (in_out_t == i8_t && 0 <= value2 && value2 <= 7)); + + in_out_t result = apply_arith_rshift<in_out_t>(value1, value2); + if (round == true && static_cast<int32_t>(value2) > 0 && + (apply_arith_rshift<in_out_t>(value1, apply_sub_s<in_out_t>(value2, 1)) & 1 != 0) { + result = result + 1; + } + result = apply_clip_s<in_out_t>(result, minimum_s<in_out_t>, maximum_s<in_out_t>); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/AVG_POOL2D.tosac b/pseudocode/operators/AVG_POOL2D.tosac new file mode 100644 index 0000000..f452d12 --- /dev/null +++ b/pseudocode/operators/AVG_POOL2D.tosac @@ -0,0 +1,50 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_out_t != i8_t && input_zp != 0); // Zero point only for int8_t +ERROR_IF(in_out_t != i8_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_bottom < 0 || pad_left < 0 || pad_right < 0); +// Padding must be less than kernel size to avoid +// a divide-by-zero. +ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x); +ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y); +ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1); +ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1); + +for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW, 0 <= c < C ) { + in_out_t output_val; + acc_t acc = 0; + int count = 0; + index_t iy = oy * stride_y - pad_top; + index_t ix = ox * stride_x - pad_left; + for_each(0 <= ky < kernel_y, 0 <= kx < kernel_x) { + index_t y = iy + ky; + index_t x = ix + kx; + // Only values from the input tensor are used to calculate the + // average, padding does not count + if (0 <= y < IH and 0 <= x < IW) { + count++; + acc_t value = sign_extend<acc_t>(tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c])); + value = apply_sub_s<acc_t>(value, sign_extend<acc_t>(input_zp)); + acc = apply_add_s<acc_t>(acc, value); + } + } + if (is_float(in_out_t)) { + output_val = acc / static_cast<in_out_t>(count); + } else { + scale_t scale = reciprocal_scale(count); + acc = apply_scale_32(acc, scale.multiplier, scale.shift, false); + acc = apply_add_s<acc_t>(acc, sign_extend<acc_t>(output_zp)); + acc = apply_clip_s<acc_t>(acc, minimum_s<in_out_t>, maximum_s<in_out_t>); + output_val = static_cast<in_out_t>(acc); + } + tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], output_val); +} diff --git a/pseudocode/operators/BITWISE_AND.tosac b/pseudocode/operators/BITWISE_AND.tosac new file mode 100644 index 0000000..5efe3ee --- /dev/null +++ b/pseudocode/operators/BITWISE_AND.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = value1 & value2; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/BITWISE_NOT.tosac b/pseudocode/operators/BITWISE_NOT.tosac new file mode 100644 index 0000000..1831e75 --- /dev/null +++ b/pseudocode/operators/BITWISE_NOT.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + in_out_t result = ~value1; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/BITWISE_OR.tosac b/pseudocode/operators/BITWISE_OR.tosac new file mode 100644 index 0000000..b2b7fc6 --- /dev/null +++ b/pseudocode/operators/BITWISE_OR.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = value1 | value2; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/BITWISE_XOR.tosac b/pseudocode/operators/BITWISE_XOR.tosac new file mode 100644 index 0000000..98f0d2c --- /dev/null +++ b/pseudocode/operators/BITWISE_XOR.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = value1 ^ value2; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/CAST.tosac b/pseudocode/operators/CAST.tosac new file mode 100644 index 0000000..f6306b3 --- /dev/null +++ b/pseudocode/operators/CAST.tosac @@ -0,0 +1,27 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_t in = tensor_read<in_t>(input, shape, index); + out_t out; + if (out_t == bool_t) { + out = (in != 0) ? true : false; + } else if (in_t == bool_t) { + out = (in) ? 1 : 0; + } else if (out_t == fp16_t || out_t == bf16_t || out_t == fp32_t) { + out = round_to_nearest_float(in); + } else if (in_t == fp16_t || in_t == bf16_t || in_t == fp32_t) { + out = apply_clip<out_t>(round_to_nearest_int(in), minimum<out_t>, maximum<out_t>); + } else if (sizeof(out_t) >= sizeof(in_t)) { + out = sign_extend<out_t>(in); + } else { + out = truncate(in); + } + tensor_write<out_t>(output, shape, index, out) +} diff --git a/pseudocode/operators/CEIL.tosac b/pseudocode/operators/CEIL.tosac new file mode 100644 index 0000000..890eb8a --- /dev/null +++ b/pseudocode/operators/CEIL.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + in_out_t result = apply_ceil<in_out_t>(value1); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/CLAMP.tosac b/pseudocode/operators/CLAMP.tosac new file mode 100644 index 0000000..7a26d50 --- /dev/null +++ b/pseudocode/operators/CLAMP.tosac @@ -0,0 +1,15 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(max_val < min_val); +for_each(index in shape) { + in_out_t value = tensor_read<in_out_t>(input, shape, index); + value = apply_clip<in_out_t>(value, min_val, max_val); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/CLZ.tosac b/pseudocode/operators/CLZ.tosac new file mode 100644 index 0000000..a3a7180 --- /dev/null +++ b/pseudocode/operators/CLZ.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + in_out_t result = count_leading_zeros(value1); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/CONCAT.tosac b/pseudocode/operators/CONCAT.tosac new file mode 100644 index 0000000..f9329af --- /dev/null +++ b/pseudocode/operators/CONCAT.tosac @@ -0,0 +1,32 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= max(1,rank(shapes1[0]))); +ERROR_IF(shape[axis] != sum(shape_dim(shapes1[k], axis) for all k)) +ERROR_IF(in_out_t == shape_t && rank(shape) > 1); +// The following checks ensure all inputs are compatible for concatenation +for_each(input_shape in shapes1) { + ERROR_IF(rank(input_shape) != rank(shapes1[0])); + for_each(index in input_shape) { + ERROR_IF(index != axis && input_shape[index] != shapes1[0][index]); + } +} +for_each(index1 in shape) { + dim_t index2 = index1; + for (tensor t = 0; t < length(input1); t++) { + // Continue to concatenate along axis from each tensor + // For each output location, we are looking for the + // appropriate input tensor + if (index2[axis] >= 0 && index2[axis] < shape_dim(shapes1[t], axis)) { + in_out_t value = tensor_read<in_out_t>(input1[t], shapes1[t], index2); + tensor_write<in_out_t>(output, shape, index1, value); + } + index2[axis] = index2[axis] - shape_dim(shapes1[t], axis); + } +} diff --git a/pseudocode/operators/COND_IF.tosac b/pseudocode/operators/COND_IF.tosac new file mode 100644 index 0000000..584cc83 --- /dev/null +++ b/pseudocode/operators/COND_IF.tosac @@ -0,0 +1,23 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(tosa_nesting_depth >= MAX_NESTING); +ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(then_graph)); +ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(else_graph)); +ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(then_graph)); +ERROR_IF(tensor_list_shape(output_list) != tosa_output_shape(else_graph)); +ERROR_IF(tensor_size(shape) != 1); + +tosa_nesting_depth++; +if (condition[0]) { + tosa_execute_graph(then_graph, input_list, output_list); +} else { + tosa_execute_graph(else_graph, input_list, output_list); +} +tosa_nesting_depth--; diff --git a/pseudocode/operators/CONV2D.tosac b/pseudocode/operators/CONV2D.tosac new file mode 100644 index 0000000..12620e9 --- /dev/null +++ b/pseudocode/operators/CONV2D.tosac @@ -0,0 +1,40 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_t != i8_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); +ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1); +ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1); +ERROR_IF(BC != OC && BC != 1); + +for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) { + out_t acc = 0; + index_t iy = oy * stride_y - pad_top; + index_t ix = ox * stride_x - pad_left; + for_each(0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) { + index_t y = iy + ky * dilation_y; + index_t x = ix + kx * dilation_x; + if (0 <= y < IH && 0 <= x < IW) { + out_t value = static_cast<out_t>(tensor_read<in_t>(input, + [N,IH,IW,IC], + [n,y,x,ic])); + out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, + [OC,KH,KW,IC], + [oc,ky,kx,ic])); + value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); + weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); + acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); + } + } + acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]); + tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc); +} diff --git a/pseudocode/operators/CONV3D.tosac b/pseudocode/operators/CONV3D.tosac new file mode 100644 index 0000000..a0f9130 --- /dev/null +++ b/pseudocode/operators/CONV3D.tosac @@ -0,0 +1,43 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t +ERROR_IF(weight_t != i8_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); +ERROR_IF(OD != idiv_check(ID - 1 + pad_d0 + pad_d1 - (KD - 1) * dilation_d, stride_d) + 1); +ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1); +ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1); +ERROR_IF(BC != OC && BC != 1); + +for_each(0 <= n < N, 0 <= od < OD, 0 <= oy < OH, 0 <= ox < OW; 0 <= oc < OC) { + out_t acc = 0; + index_t id = od * stride_d - pad_d0; + index_t iy = oy * stride_y - pad_top; + index_t ix = ox * stride_x - pad_left; + for_each(0 <= kd < KD, 0 <= ky < KH, 0 <= kx < KW, 0 <= ic < IC) { + index_t d = id + kd * dilation_d; + index_t y = iy + ky * dilation_y; + index_t x = ix + kx * dilation_x; + if (0 <= x < IW && 0 <= y < IH && 0 <= d < ID) { + out_t value = static_cast<out_t>(tensor_read<in_t>(input, + [N,ID,IH,IW,IC], + [n,d,y,x,ic])); + out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, + [OC,KD,KH,KW,IC], + [oc,kd,ky,kx,ic])); + value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); + weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); + acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); + } + } + acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]); + tensor_write<out_t>(output, [N,OD,OH,OW,OC], [n,od,oy,ox,oc], acc); +} diff --git a/pseudocode/operators/DEPTHWISE_CONV2D.tosac b/pseudocode/operators/DEPTHWISE_CONV2D.tosac new file mode 100644 index 0000000..c7a0f0f --- /dev/null +++ b/pseudocode/operators/DEPTHWISE_CONV2D.tosac @@ -0,0 +1,40 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t +ERROR_IF(weight_t != i8_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); +ERROR_IF(OH != idiv_check(IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y, stride_y) + 1); +ERROR_IF(OW != idiv_check(IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x, stride_x) + 1); +ERROR_IF(BC != C*M && BC != 1); + +for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C, 0 <= m < M) { + out_t acc = 0; + index_t iy = oy * stride_y - pad_top; + index_t ix = ox * stride_x - pad_left; + for_each(0 <= ky < KH, 0 <= kx < KW) { + index_t y = iy + ky * dilation_y; + index_t x = ix + kx * dilation_x; + if (0 <= y < IH && 0 <= x < IW) { + out_t value = static_cast<out_t>(tensor_read<in_t>(input, + [N,IH,IW,C], + [n,y,x,c])); + out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, + [KH,KW,C,M], + [ky,kx,c,m])); + value = apply_sub_s<out_t>(value, static_cast<out_t>input_zp); + weight = apply_sub_s<out_t>(weight, static_cast<out_t>weight_zp); + acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); + } + } + acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : (c * M) + m]); + tensor_write<out_t>(output, [N,OH,OW,C * M], [n,oy,ox,c * M + m], acc); +} diff --git a/pseudocode/operators/DIM.tosac b/pseudocode/operators/DIM.tosac new file mode 100644 index 0000000..7131173 --- /dev/null +++ b/pseudocode/operators/DIM.tosac @@ -0,0 +1,11 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis >= rank(shape)); +tensor_write<shape_t>(output, [], [], shape_dim(shape, axis)); diff --git a/pseudocode/operators/EQUAL.tosac b/pseudocode/operators/EQUAL.tosac new file mode 100644 index 0000000..67e0b64 --- /dev/null +++ b/pseudocode/operators/EQUAL.tosac @@ -0,0 +1,22 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_t value1 = tensor_read<in_t>(input1, shape1, index1); + in_t value2 = tensor_read<in_t>(input2, shape2, index2); + out_t result; + if (isNaN(value1) || isNaN(value2)) + result = False; + else + result = (value1 == value2) ? True : False; + tensor_write<out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/EXP.tosac b/pseudocode/operators/EXP.tosac new file mode 100644 index 0000000..4ef74a0 --- /dev/null +++ b/pseudocode/operators/EXP.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + in_out_t result = apply_exp<in_out_t>(value1); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/FFT2D.tosac b/pseudocode/operators/FFT2D.tosac new file mode 100644 index 0000000..a958aa4 --- /dev/null +++ b/pseudocode/operators/FFT2D.tosac @@ -0,0 +1,31 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(!power_of_two(H)); +ERROR_IF(!power_of_two(W)); + +float sign_val = 1.0; + +if (inverse) { + sign_val = -1.0; +} + +for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W) { + in_out_t sum_real = 0.0; + in_out_t sum_imag = 0.0; + for_each(0 <= iy < H, 0 <= ix < W) { + in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]); + in_out_t val_imag = tensor_read<in_out_t>(input_imag, [N,H,W], [n,iy,ix]); + float_t a = sign_val * 2 * pi() * ((iy * oy) / H + (ix * ox) / W); + sum_real += val_real * cos(a) + val_imag * sin(a); + sum_imag += -val_real * sin(a) + val_imag * cos(a); + } + tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real); + tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag); +} diff --git a/pseudocode/operators/FLOOR.tosac b/pseudocode/operators/FLOOR.tosac new file mode 100644 index 0000000..fc893b6 --- /dev/null +++ b/pseudocode/operators/FLOOR.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + in_out_t result = apply_floor<in_out_t>(value1); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/FULLY_CONNECTED.tosac b/pseudocode/operators/FULLY_CONNECTED.tosac new file mode 100644 index 0000000..fbacccc --- /dev/null +++ b/pseudocode/operators/FULLY_CONNECTED.tosac @@ -0,0 +1,25 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only for int8_t +ERROR_IF(weight_t != i8_t && weight_zp != 0); +ERROR_IF(BC != OC && BC != 1); + +for_each(0 <= n < N, 0 <= oc < OC) { + out_t acc = 0; + for_each(0 <= ic < IC) { + out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IC], [n,ic])); + out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,IC], [oc,ic])); + value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); + weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); + acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); + } + acc = apply_add_s<out_t>(acc, bias[(BC == 1) ? 0 : oc]); + tensor_write<out_t>(output, [N,OC], [n,oc], acc); +} diff --git a/pseudocode/operators/GATHER.tosac b/pseudocode/operators/GATHER.tosac new file mode 100644 index 0000000..82c159d --- /dev/null +++ b/pseudocode/operators/GATHER.tosac @@ -0,0 +1,15 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) { + index_t k = tensor_read<index_t>(indices, [N,W], [n,w]); + REQUIRE(0 <= k && k < K); + in_out_t value = tensor_read<in_out_t>(values, [N,K,C], [n,k,c]); + tensor_write<in_out_t>(output, [N,W,C], [n,w,c], value); +} diff --git a/pseudocode/operators/GREATER.tosac b/pseudocode/operators/GREATER.tosac new file mode 100644 index 0000000..af8b4c9 --- /dev/null +++ b/pseudocode/operators/GREATER.tosac @@ -0,0 +1,22 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_t value1 = tensor_read<in_t>(input1, shape1, index1); + in_t value2 = tensor_read<in_t>(input2, shape2, index2); + out_t result; + if (isNaN(value1) || isNaN(value2)) + result = False; + else + result = (value1 > value2) ? True : False; + tensor_write<out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/GREATER_EQUAL.tosac b/pseudocode/operators/GREATER_EQUAL.tosac new file mode 100644 index 0000000..e45990d --- /dev/null +++ b/pseudocode/operators/GREATER_EQUAL.tosac @@ -0,0 +1,22 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_t value1 = tensor_read<in_t>(input1, shape1, index1); + in_t value2 = tensor_read<in_t>(input2, shape2, index2); + out_t result; + if (isNaN(value1) || isNaN(value2)) + result = False; + else + result = (value1 >= value2) ? True : False; + tensor_write<out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/INTDIV.tosac b/pseudocode/operators/INTDIV.tosac new file mode 100644 index 0000000..b6d46cc --- /dev/null +++ b/pseudocode/operators/INTDIV.tosac @@ -0,0 +1,31 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +if (in_out_t == shape_t) { + ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); + shape_t value1 = tensor_read<shape_t>(input1, [], []); + shape_t value2 = tensor_read<shape_t>(input2, [], []); + REQUIRE(value2 != 0); + shape_t result = value1 / value2; + tensor_write<shape_t>(output, [], [], result); +} else { + ERROR_IF(shape != broadcast_shape(shape1, shape2)); + for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + REQUIRE(value2 != 0); + // This catches the case where we divide minimum<in_out_t> by -1 + // which is not representable in two's complement + REQUIRE(static_cast<int64_t>(value1) / static_cast<int64_t>(value2) <= maximum_s<in_out_t>); + in_out_t result = apply_intdiv_s<in_out_t>(value1, value2); + tensor_write<in_out_t>(output, shape, index, result); + } +} diff --git a/pseudocode/operators/LOG.tosac b/pseudocode/operators/LOG.tosac new file mode 100644 index 0000000..2c56105 --- /dev/null +++ b/pseudocode/operators/LOG.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + in_out_t result = apply_log<in_out_t>(value1); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/LOGICAL_AND.tosac b/pseudocode/operators/LOGICAL_AND.tosac new file mode 100644 index 0000000..ff03804 --- /dev/null +++ b/pseudocode/operators/LOGICAL_AND.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = value1 && value2; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/LOGICAL_LEFT_SHIFT.tosac b/pseudocode/operators/LOGICAL_LEFT_SHIFT.tosac new file mode 100644 index 0000000..4057163 --- /dev/null +++ b/pseudocode/operators/LOGICAL_LEFT_SHIFT.tosac @@ -0,0 +1,19 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + REQUIRE(0 <= value2 && value2 <= 31); + in_out_t result = value1 << value2; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/LOGICAL_NOT.tosac b/pseudocode/operators/LOGICAL_NOT.tosac new file mode 100644 index 0000000..b07e668 --- /dev/null +++ b/pseudocode/operators/LOGICAL_NOT.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index); + in_out_t result = !value1; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/LOGICAL_OR.tosac b/pseudocode/operators/LOGICAL_OR.tosac new file mode 100644 index 0000000..4e3aa9e --- /dev/null +++ b/pseudocode/operators/LOGICAL_OR.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = value1 || value2; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/LOGICAL_RIGHT_SHIFT.tosac b/pseudocode/operators/LOGICAL_RIGHT_SHIFT.tosac new file mode 100644 index 0000000..5e80211 --- /dev/null +++ b/pseudocode/operators/LOGICAL_RIGHT_SHIFT.tosac @@ -0,0 +1,20 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + REQUIRE(0 <= static_cast<int32_t>(value2) && static_cast<int32_t>(value2) <= 31); + // Logical shifts happen as unsigned types internally + in_out_t result = apply_logical_rshift<in_out_t>(value1, value2); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/LOGICAL_XOR.tosac b/pseudocode/operators/LOGICAL_XOR.tosac new file mode 100644 index 0000000..fbb485b --- /dev/null +++ b/pseudocode/operators/LOGICAL_XOR.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = value1 != value2; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/MATMUL.tosac b/pseudocode/operators/MATMUL.tosac new file mode 100644 index 0000000..3afda4d --- /dev/null +++ b/pseudocode/operators/MATMUL.tosac @@ -0,0 +1,21 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_t != i8_t && (A_zp != 0 || B_zp != 0)); // Zero point only for int8_t +for_each(0 <= n < N, 0 <= h < H, 0 <= w < W) { + out_t acc = 0; + for_each(0 <= c < C) { + out_t value1 = static_cast<out_t>(tensor_read<in_t>(A, [N,H,C], [n,h,c])); + out_t value2 = static_cast<out_t>(tensor_read<in_t>(B, [N,C,W], [n,c,w])); + value1 = apply_sub_s<out_t>(value1, static_cast<out_t>(A_zp)); + value2 = apply_sub_s<out_t>(value2, static_cast<out_t>(B_zp)); + acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value1 * value2)); + } + tensor_write<out_t>(output, [N,H,W], [n,h,w], acc); +} diff --git a/pseudocode/operators/MAXIMUM.tosac b/pseudocode/operators/MAXIMUM.tosac new file mode 100644 index 0000000..0f4f3b7 --- /dev/null +++ b/pseudocode/operators/MAXIMUM.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = apply_max_s<in_out_t>(value1, value2); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/MAX_POOL2D.tosac b/pseudocode/operators/MAX_POOL2D.tosac new file mode 100644 index 0000000..a33b80a --- /dev/null +++ b/pseudocode/operators/MAX_POOL2D.tosac @@ -0,0 +1,33 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +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_bottom < 0 || pad_left < 0 || pad_right < 0); +// Padding must be less than kernel size, otherwise no +// input values will be used. +ERROR_IF(pad_right >= kernel_x || pad_left >= kernel_x); +ERROR_IF(pad_top >= kernel_y || pad_bottom >= kernel_y); +ERROR_IF(OH != idiv_check(IH + pad_top + pad_bottom - kernel_y, stride_y) + 1); +ERROR_IF(OW != idiv_check(IW + pad_left + pad_right - kernel_x, stride_x) + 1); + +for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W, 0 <= c < C ) { + in_out_t acc = minimum_value<in_out_t>; + index_t iy = oy * stride_y - pad_top; + index_t ix = ox * stride_x - pad_left; + for_each( 0 <= ky < kernel_y, 0 <= kx < kernel_x ) { + index_t y = iy + ky; + index_t x = ix + kx; + if (y >= 0 && y < IH && x >= 0 && x < IW) { + in_out_t value = tensor_read<in_out_t>(input, [N,IH,IW,C], [n,y,x,c]); + acc = apply_max_s<in_out_t>(acc, value); + } + } + tensor_write<in_out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc); +} diff --git a/pseudocode/operators/MINIMUM.tosac b/pseudocode/operators/MINIMUM.tosac new file mode 100644 index 0000000..fa47b03 --- /dev/null +++ b/pseudocode/operators/MINIMUM.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = apply_min_s(value1, value2); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/MUL.tosac b/pseudocode/operators/MUL.tosac new file mode 100644 index 0000000..078525e --- /dev/null +++ b/pseudocode/operators/MUL.tosac @@ -0,0 +1,37 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +if (in_out_t == shape_t) { + ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); + shape_t value1 = tensor_read<shape_t>(input1, [], []); + shape_t value2 = tensor_read<shape_t>(input2, [], []); + shape_t result = value1 * value2; + tensor_write<shape_t>(output, [], [], result); +} else { + REQUIRE(0 <= shift && shift <= 63); + REQUIRE(in_t == int32_t || shift == 0); + ERROR_IF(shape != broadcast_shape(shape1, shape2)); + for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_t value1 = tensor_read<in_t>(input1, shape1, index1); + in_t value2 = tensor_read<in_t>(input2, shape2, index2); + out_t result; + if (in_t == i32_t && shift > 0) { + int64_t product = sign_extend<int64_t>(value1) * sign_extend<int64_t>(value2); + int64_t round = static_cast<int64_t>(1) << (shift - 1); + product = (product + round) >> shift; + REQUIRE(product >= minimum_s<i32_t> && product <= maximum_s<i32_t>) + result = product; + } else { + result = apply_mul_s(value1, value2); // low 32-bits of result for i32_t + } + tensor_write<out_t>(output, shape, index, result); + } +} diff --git a/pseudocode/operators/NEGATE.tosac b/pseudocode/operators/NEGATE.tosac new file mode 100644 index 0000000..8c235f1 --- /dev/null +++ b/pseudocode/operators/NEGATE.tosac @@ -0,0 +1,22 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_out_t != i8_t && input1_zp != 0) // Zero point only for int8_t +ERROR_IF(in_out_t != i8_t && output_zp != 0) // Zero point only for int8_t +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape, index); + acc_t value = apply_sub_s<acc_t>(sign_extend<acc_t>(value1), + sign_extend<acc_t>(input1_zp)); + value = apply_sub_s<acc_t>(0, value); + value = apply_add_s<acc_t>(value, sign_extend<acc_t>(output_zp)); + in_out_t result = truncate<in_out_t>(apply_clip_s<acc_t>(value, + minimum_s<in_out_t>, + maximum_s<in_out_t>)); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/PAD.tosac b/pseudocode/operators/PAD.tosac new file mode 100644 index 0000000..4adf114 --- /dev/null +++ b/pseudocode/operators/PAD.tosac @@ -0,0 +1,27 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +// Check output shape matches the padded input shape +ERROR_IF(rank(shape) != rank(shape1)); +for (i = 0; i < rank(shape); i++) { + ERROR_IF(padding[i,0] < 0 || padding[i,1] < 0); + ERROR_IF(shape[i] != padding[i, 0] + shape1[i] + padding[i, 1]); +} +for_each(index in shape) { + dim_t index1 = index; + bool_t is_pad = false; + for(i = 0; i < rank(shape); i++) { + index1[i] = index1[i] - padding[i,0]; + if (index1[i] < 0 || index[i] >= length(shape[i])) { + is_pad = true; + } + } + in_out_t value = is_pad ? pad_const : tensor_read<in_out_t>(input1, shape1, index1); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/POW.tosac b/pseudocode/operators/POW.tosac new file mode 100644 index 0000000..6ecc8e6 --- /dev/null +++ b/pseudocode/operators/POW.tosac @@ -0,0 +1,18 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(shape1, shape2)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = apply_pow<in_out_t>(value1, value2); + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/RECIPROCAL.tosac b/pseudocode/operators/RECIPROCAL.tosac new file mode 100644 index 0000000..9bc086b --- /dev/null +++ b/pseudocode/operators/RECIPROCAL.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index); + in_out_t result = 1.0 / value1; + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/REDUCE_ALL.tosac b/pseudocode/operators/REDUCE_ALL.tosac new file mode 100644 index 0000000..7438513 --- /dev/null +++ b/pseudocode/operators/REDUCE_ALL.tosac @@ -0,0 +1,25 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape1)); +ERROR_IF(shape[axis] != 1); +left_shape = (axis > 1) ? shape[0:axis-1] : []; +right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; +for_each(left_index in left_shape) { + for_each(right_index in right_shape) { + in_out_t acc = true; + for (i = 0; i < shape1[axis]; i++) { + index = flatten(left_index, [i], right_index); + in_out_t value = tensor_read<in_out_t>(input, shape1, index); + acc = acc && value; + } + out_index = flatten(left_index, [0], right_index); + tensor_write<in_out_t>(output, shape, out_index, acc); + } +} diff --git a/pseudocode/operators/REDUCE_ANY.tosac b/pseudocode/operators/REDUCE_ANY.tosac new file mode 100644 index 0000000..3303fac --- /dev/null +++ b/pseudocode/operators/REDUCE_ANY.tosac @@ -0,0 +1,25 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape1)); +ERROR_IF(shape[axis] != 1); +left_shape = (axis > 1) ? shape[0:axis-1] : []; +right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; +for_each(left_index in left_shape) { + for_each(right_index in right_shape) { + in_out_t acc = false; + for (i = 0; i < shape1[axis]; i++) { + index = flatten(left_index, [i], right_index); + in_out_t value = tensor_read<in_out_t>(input, shape1, index); + acc = acc || value; + } + out_index = flatten(left_index, [0], right_index); + tensor_write<in_out_t>(output, shape, out_index, acc); + } +} diff --git a/pseudocode/operators/REDUCE_MAX.tosac b/pseudocode/operators/REDUCE_MAX.tosac new file mode 100644 index 0000000..57f291a --- /dev/null +++ b/pseudocode/operators/REDUCE_MAX.tosac @@ -0,0 +1,25 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape1)); +ERROR_IF(shape[axis] != 1); +left_shape = (axis > 1) ? shape[0:axis-1] : []; +right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; +for_each(left_index in left_shape) { + for_each(right_index in right_shape) { + in_out_t acc = minimum<in_out_t>; + for (i = 0; i < shape1[axis]; i++) { + index = flatten(left_index, [i], right_index); + in_out_t value = tensor_read<in_out_t>(input, shape1, index); + acc = apply_max_s<in_out_t>(acc, value); + } + out_index = flatten(left_index, [0], right_index); + tensor_write<in_out_t>(output, shape, out_index, acc); + } +} diff --git a/pseudocode/operators/REDUCE_MIN.tosac b/pseudocode/operators/REDUCE_MIN.tosac new file mode 100644 index 0000000..5eaa702 --- /dev/null +++ b/pseudocode/operators/REDUCE_MIN.tosac @@ -0,0 +1,25 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape1)); +ERROR_IF(shape[axis] != 1); +left_shape = (axis > 1) ? shape[0:axis-1] : []; +right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; +for_each(left_index in left_shape) { + for_each(right_index in right_shape) { + in_out_t acc = maximum<in_out_t>; + for (i = 0; i < shape1[axis]; i++) { + index = flatten(left_index, [i], right_index); + in_out_t value = tensor_read<in_out_t>(input, shape1, index); + acc = apply_min_s<in_out_t>(acc, value); + } + out_index = flatten(left_index, [0], right_index); + tensor_write<in_out_t>(output, shape, out_index, out); + } +} diff --git a/pseudocode/operators/REDUCE_PRODUCT.tosac b/pseudocode/operators/REDUCE_PRODUCT.tosac new file mode 100644 index 0000000..3c867d7 --- /dev/null +++ b/pseudocode/operators/REDUCE_PRODUCT.tosac @@ -0,0 +1,25 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape1)); +ERROR_IF(shape[axis] != 1); +left_shape = (axis > 1) ? shape[0:axis-1] : []; +right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; +for_each(left_index in left_shape) { + for_each(right_index in right_shape) { + in_out_t acc = 1.0; + for (i = 0; i < shape1[axis]; i++) { + index = flatten(left_index, [i], right_index); + in_out_t value = tensor_read<in_out_t>(input, shape1, index); + acc = apply_mul_s<in_out_t>(acc, value); + } + out_index = flatten(left_index, [0], right_index); + tensor_write<in_out_t>(output, shape, out_index, acc); + } +} diff --git a/pseudocode/operators/REDUCE_SUM.tosac b/pseudocode/operators/REDUCE_SUM.tosac new file mode 100644 index 0000000..2f22ada --- /dev/null +++ b/pseudocode/operators/REDUCE_SUM.tosac @@ -0,0 +1,26 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape1)); +ERROR_IF(shape[axis] != 1); +left_shape = (axis > 1) ? shape[0:axis-1] : []; +right_shape = (axis < rank(shape)-1) ? shape[axis+1:rank(shape)-1] : []; +for_each(left_index in left_shape) { + for_each(right_index in right_shape) { + acc_t acc = 0; + for (i = 0; i < shape1[axis]; i++) { + index = flatten(left_index, [i], right_index); + acc_t value = tensor_read<in_out_t>(input, shape1, index); + acc = apply_add_s<acc_t>(acc, value); + } + out_index = flatten(left_index, [0], right_index); + in_out_t result = static_cast<in_out_t>(acc); + tensor_write<in_out_t>(output, shape, out_index, result); + } +} diff --git a/pseudocode/operators/RESCALE.tosac b/pseudocode/operators/RESCALE.tosac new file mode 100644 index 0000000..86a939b --- /dev/null +++ b/pseudocode/operators/RESCALE.tosac @@ -0,0 +1,58 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + // uint16 values can have zero_point 0 or 32768 + // int8/uint8 can have zero point within their valid range + // No other types can have zero point != 0 + ERROR_IF(in_t != i8_t && + (in_t != i16_t || input_unsigned == False) && input_zp != 0); + ERROR_IF(out_t != i8_t && + (out_t != i16_t || output_unsigned == False) && output_zp != 0); + ERROR_IF(in_t == i16_t && input_unsigned == True && input_zp != 0 && input_zp != 32768); + ERROR_IF(out_t == i16_t && output_unsigned == True && output_zp != 0 && output_zp != 32768); + ERROR_IF(scale32 && in_t == i48_t); + ERROR_IF(!scale32 && double_round); + ERROR_IF(in_t == i16_t && out_t == i32_t && input_unsigned); + ERROR_IF(in_t == i32_t && out_t == i16_t && output_unsigned); + + in_t in_value = tensor_read<in_t>(input, shape, index); + + int48_t value, extended_in_zp; + if (input_unsigned) { + value = zero_extend<int48_t>(in_value); + extended_in_zp = zero_extend<int48_t>(input_zp); + } + else { + value = sign_extend<int48_t>(value); + extended_in_zp = sign_extend<int48_t>(input_zp); + } + + value = value - extended_in_zp; + int c = (per_channel) ? index[rank(input) - 1] : 0; + int32_t result = (scale32) ? + apply_scale_32(value, multiplier[c], shift[c], double_round) : + apply_scale_16(value, multiplier[c], shift[c]); + + if (output_unsigned) { + int32_t extended_out_zp = zero_extend<int32_t>(output_zp); + result = apply_add_s<int32_t>(result, extended_out_zp); + out_t out = static_cast<out_t>(apply_clip<int32_t>(result, + minimum_u<out_t>, + maximum_u<out_t>)); + } + else { + int32_t extended_out_zp = sign_extend<int32_t>(output_zp); + result = apply_add_s<int32_t>(result, extended_out_zp); + out_t out = static_cast<out_t>(apply_clip<int32_t>(result, + minimum_s<out_t>, + maximum_s<out_t>)); + } + tensor_write<out_t>(output, shape, index, out); +} diff --git a/pseudocode/operators/RESHAPE.tosac b/pseudocode/operators/RESHAPE.tosac new file mode 100644 index 0000000..41439c3 --- /dev/null +++ b/pseudocode/operators/RESHAPE.tosac @@ -0,0 +1,21 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(tensor_size(shape1) != tensor_size(shape)); + +for_each(index in shape) { + // Calculate flattened index for the output location (index) + size_t offset = tensor_index_to_offset(shape, index); + // Now convert to the location in the input + dim_t tmp_index = tensor_offset_to_index(shape1, offset); + + // Now read/write the value + in_out_t val = tensor_read<in_out_t>(input1, shape1, tmp_index); + tensor_write<in_out_t>(output, shape, index, val); +} diff --git a/pseudocode/operators/RESIZE.tosac b/pseudocode/operators/RESIZE.tosac new file mode 100644 index 0000000..fd66530 --- /dev/null +++ b/pseudocode/operators/RESIZE.tosac @@ -0,0 +1,74 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +// Ensure the 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(scale_y_n <= 0 || scale_y_d <= 0 || scale_x_n <= 0 || scale_x_d <= 0); +// if in_t=int8_t ensure that an int32_t accumulator can be used +ERROR_IF(scale_y_n > (1 << 11) || scale_x_n > (1 << 11)); +// set a consistent lower limit of 1/16 downscale to simplify implementations +ERROR_IF(scale_y_d >= 16 * scale_y_n || scale_x_d >= 16 * scale_x_n); +ERROR_IF(offset_y < -scale_y_n || offset_y >= 16 * scale_y_n); +ERROR_IF(offset_x < -scale_x_n || offset_x >= 16 * scale_x_n); +ERROR_IF(border_y < -16 * scale_y_n || border_y >= scale_y_n); +ERROR_IF(border_x < -16 * scale_x_n || border_x >= scale_x_n); +ERROR_IF(OH != idiv_check((IH - 1) * scale_y_n - offset_y + border_y, scale_y_d) + 1); +ERROR_IF(OW != idiv_check((IW - 1) * scale_x_n - offset_x + border_x, scale_x_d) + 1); +for_each(0 <= n < N, 0 <= oy < OH, 0 <= ox < OW; 0 <= c < C) { + out_t acc; + resize_t dx, dy; + resize_t unit_x, unit_y; + + unit_x = (is_floating_point(resize_t)) ? 1.0 : scale_x_n; + unit_y = (is_floating_point(resize_t)) ? 1.0 : scale_y_n; + + int32_t y = oy * scale_y_d + offset_y; + int32_t x = ox * scale_x_d + offset_x; + int16_t iy = idiv_floor(y, scale_y_n); + int16_t ix = idiv_floor(x, scale_x_n); + int16_t ry = y - iy * scale_y_n; // (y % scale_y_n) + int16_t rx = x - ix * scale_x_n; // (x % scale_x_n) + + if (is_floating_point(resize_t)) { + dy = static_cast<resize_t>(ry) / static_cast<resize_t>(scale_y_n); + dx = static_cast<resize_t>(rx) / static_cast<resize_t>(scale_x_n); + } else { + dy = ry; + dx = rx; + } + // Note that -1 <= iy < IH and -1 <= ix < IW + int16_t iy0 = apply_max_s(iy, 0); + int16_t iy1 = apply_min_s(iy + 1, IH - 1); + int16_t ix0 = apply_max_s(ix, 0); + int16_t ix1 = apply_min_s(ix + 1, IW - 1); + if (mode==BILINEAR) { + using in_s_t = make_signed(in_t); // Use signed calculations for i8/i16 + in_s_t v00 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix0,c])); + in_s_t v01 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy0,ix1,c])); + in_s_t v10 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix0,c])); + in_s_t v11 = static_cast<in_s_t>(tensor_read<in_t>(input, [N,IH,IW,C], [n,iy1,ix1,c])); + acc = v00 * (unit_y - dy) * (unit_x - dx); + acc += v01 * (unit_y - dy) * dx; + acc += v10 * dy * (unit_x - dx); + acc += v11 * dy * dx; + tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], acc); + } else if (mode==NEAREST) { + int32_t iy, ix; + if (is_floating_point(resize_t)) { + iy = (dy >= 0.5) ? iy1 : iy0; + ix = (dx >= 0.5) ? ix1 : ix0; + } else { + iy = (2 * dy >= scale_y_n) ? iy1 : iy0; + ix = (2 * dx >= scale_x_n) ? ix1 : ix0; + } + in_t v = tensor_read<in_t>(input, [N,IH,IW,C], [n,iy,ix,c]); + tensor_write<out_t>(output, [N,OH,OW,C], [n,oy,ox,c], v); + } +} diff --git a/pseudocode/operators/REVERSE.tosac b/pseudocode/operators/REVERSE.tosac new file mode 100644 index 0000000..63830d2 --- /dev/null +++ b/pseudocode/operators/REVERSE.tosac @@ -0,0 +1,16 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(axis < 0 || axis >= rank(shape)); +for_each(index in shape) { + dim_t tmp_index = index; + tmp_index[axis] = shape[axis] - 1 - index[axis]; + in_out_t value = tensor_read<in_out_t>(input, shape, tmp_index); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/RFFT2D.tosac b/pseudocode/operators/RFFT2D.tosac new file mode 100644 index 0000000..f664826 --- /dev/null +++ b/pseudocode/operators/RFFT2D.tosac @@ -0,0 +1,24 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(!power_of_two(H)); +ERROR_IF(!power_of_two(W)); + +for_each(0 <= n < N, 0 <= oy < H, 0 <= ox < W/2 + 1) { + in_out_t sum_real = 0.0; + in_out_t sum_imag = 0.0; + for_each(0 <= iy < H, 0 <= ix < W) { + in_out_t val_real = tensor_read<in_out_t>(input_real, [N,H,W], [n,iy,ix]); + float_t a = 2 * pi() * ((iy * oy) / H + (ix * ox) / W); + sum_real += val_real * cos(a); + sum_imag += -val_real * sin(a); + } + tensor_write<in_out_t>(output_real, [N,H,W], [n,oy,ox], sum_real); + tensor_write<in_out_t>(output_imag, [N,H,W], [n,oy,ox], sum_imag); +} diff --git a/pseudocode/operators/RSQRT.tosac b/pseudocode/operators/RSQRT.tosac new file mode 100644 index 0000000..15d884a --- /dev/null +++ b/pseudocode/operators/RSQRT.tosac @@ -0,0 +1,20 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index); + in_out_t result; + if (value1 < 0) { + result = NaN; + } + else { + result = 1.0 / apply_sqrt<in_out_t>(value1); + } + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/SCATTER.tosac b/pseudocode/operators/SCATTER.tosac new file mode 100644 index 0000000..5027816 --- /dev/null +++ b/pseudocode/operators/SCATTER.tosac @@ -0,0 +1,31 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + + +// The following array is used to check compliance that an output position +// is modified at most once. +bool_t output_modified[N,K,C]; + +// Copy the values_in tensor to the values_out tensor. +// Values not written by the scatter operation are unchanged in the output. +for_each(0 <= n < N, 0 <= k < K, 0 <= c < C) { + in_out_t value = tensor_read<in_out_t>(values_in, [N,K,C], [n,k,c]); + tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value); + output_modified[n,k,c]=false; +} + +// Now perform the SCATTER operation, modifying the positions from the indices tensor +for_each(0 <= n < N, 0 <= w < W, 0 <= c < C) { + index_t k = tensor_read<index_t>(indices, [N,W], [n,w]); + REQUIRE(0 <= k && k < K); + REQUIRE(output_modified[n,k,c] == false); + in_out_t value = tensor_read<in_out_t>(input, [N,W,C], [n,w,c]); + tensor_write<in_out_t>(values_out, [N,K,C], [n, k, c], value); + output_modified[n,k,c] = true; +} diff --git a/pseudocode/operators/SELECT.tosac b/pseudocode/operators/SELECT.tosac new file mode 100644 index 0000000..fe8d760 --- /dev/null +++ b/pseudocode/operators/SELECT.tosac @@ -0,0 +1,25 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(shape != broadcast_shape(broadcast_shape(shape1, shape2), shape3)); +for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + dim_t index3 = apply_broadcast(shape, shape3, index); + bool_t value1 = tensor_read<bool_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t value3 = tensor_read<in_out_t>(input3, shape3, index3); + in_out_t result; + if (value1) { + result = value2; + } else { + result = value3; + } + tensor_write<in_out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/SIGMOID.tosac b/pseudocode/operators/SIGMOID.tosac new file mode 100644 index 0000000..048523e --- /dev/null +++ b/pseudocode/operators/SIGMOID.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input, shape, index); + value = sigmoid<in_out_t>(value1); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/SLICE.tosac b/pseudocode/operators/SLICE.tosac new file mode 100644 index 0000000..0ae0214 --- /dev/null +++ b/pseudocode/operators/SLICE.tosac @@ -0,0 +1,28 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(rank(shape1) != length(start) || rank(shape1) != length(size)); +ERROR_IF(rank(shape1) != rank(shape)); +// Sanity check the given coordinates, ensure start and end are +// within tensor bounds +for_each(index in rank(shape1)) { + ERROR_IF(start[index] < 0); + ERROR_IF(size[index] <= 0); //Output must be positive size + ERROR_IF(start[index] + size[index] > shape1[index]); + ERROR_IF(shape[index] != size[index]); +} + +for_each(index in shape) { + dim_t tmp_index = index; + for(i = 0; i < rank(shape); i++) { + tmp_index[i] = index[i] + start[i]; + } + in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/SUB.tosac b/pseudocode/operators/SUB.tosac new file mode 100644 index 0000000..ac88b76 --- /dev/null +++ b/pseudocode/operators/SUB.tosac @@ -0,0 +1,26 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +if (in_out_t == shape_t) { + ERROR_IF(rank(shape) != 0 || rank(shape1) != 0 || rank(shape2) != 0); + shape_t value1 = tensor_read<shape_t>(input1, [], []); + shape_t value2 = tensor_read<shape_t>(input2, [], []); + shape_t result = apply_sub<shape_t>(value1, value2); + tensor_write<shape_t>(output, [], [], result); +} else { + ERROR_IF(shape != broadcast_shape(shape1, shape2)); + for_each(index in shape) { + dim_t index1 = apply_broadcast(shape, shape1, index); + dim_t index2 = apply_broadcast(shape, shape2, index); + in_out_t value1 = tensor_read<in_out_t>(input1, shape1, index1); + in_out_t value2 = tensor_read<in_out_t>(input2, shape2, index2); + in_out_t result = apply_sub_s<in_out_t>(value1, value2); + tensor_write<in_out_t>(output, shape, index, result); + } +} diff --git a/pseudocode/operators/TABLE.tosac b/pseudocode/operators/TABLE.tosac new file mode 100644 index 0000000..602d49b --- /dev/null +++ b/pseudocode/operators/TABLE.tosac @@ -0,0 +1,21 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +REQUIRE(length(table) == TABLE_SIZE); +for_each(index in shape) { + in_t value = tensor_read<in_t>(input, shape, index); + out_t result; + if (in_t == i8_t) { + // value is a signed int, convert to a 0 based index + result = table[static_cast<int16_t>(value) + 128]; + } else { + result = apply_lookup_s(static_cast<int16_t>(table), static_cast<int16_t>(value)); + } + tensor_write<out_t>(output, shape, index, result); +} diff --git a/pseudocode/operators/TANH.tosac b/pseudocode/operators/TANH.tosac new file mode 100644 index 0000000..1b5edba --- /dev/null +++ b/pseudocode/operators/TANH.tosac @@ -0,0 +1,14 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +for_each(index in shape) { + in_out_t value1 = tensor_read<in_out_t>(input, shape, index); + value = tanh<in_out_t>(value1); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/TILE.tosac b/pseudocode/operators/TILE.tosac new file mode 100644 index 0000000..d053b8f --- /dev/null +++ b/pseudocode/operators/TILE.tosac @@ -0,0 +1,20 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(rank(shape1) != rank(shape)); + +for_each(index in shape) { + dim_t tmp_index = index; + for(i = 0; i < rank(shape); i++) { + ERROR_IF(shape1[i] * multiples[i] != shape[i]); + tmp_index[i] = index[i] % shape1[i]; + } + in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/TRANSPOSE.tosac b/pseudocode/operators/TRANSPOSE.tosac new file mode 100644 index 0000000..a8f0815 --- /dev/null +++ b/pseudocode/operators/TRANSPOSE.tosac @@ -0,0 +1,35 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(rank(shape1) != rank(shape)); +ERROR_IF(tensor_size(shape1) != tensor_size(shape)); + +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; +} + +// Ensure that the output shapes have the properly +// permuted shapes +for(i = 0; i < rank(shape); i++) { + ERROR_IF(shape1[perms[i]] != shape[i]) +} + +for_each(index in shape) { + dim_t tmp_index = index; + for(i = 0; i < rank(shape); i++) { + tmp_index[perms[i]] = index[i] + } + in_out_t value = tensor_read<in_out_t>(input1, shape1, tmp_index); + tensor_write<in_out_t>(output, shape, index, value); +} diff --git a/pseudocode/operators/TRANSPOSE_CONV2D.tosac b/pseudocode/operators/TRANSPOSE_CONV2D.tosac new file mode 100644 index 0000000..3aa0e23 --- /dev/null +++ b/pseudocode/operators/TRANSPOSE_CONV2D.tosac @@ -0,0 +1,35 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(in_t != i8_t && input_zp != 0); // Zero point only allowed for int8_t +ERROR_IF(weight_t != i8_t && weight_zp != 0); +ERROR_IF(out_pad_top <= -KH || out_pad_bottom <= -KH); +ERROR_IF(out_pad_left <= -KW || out_pad_right <= -KW); +ERROR_IF(stride_y < 1 || stride_x < 1); +ERROR_IF(OH != (IH - 1) * stride_y + out_pad_top + out_pad_bottom + KH); +ERROR_IF(OW != (IW - 1) * stride_x + out_pad_left + out_pad_right + KW); +ERROR_IF(BC != OC && BC != 1); + +for_each(index in [N, OH, OW, OC]) { + tensor_write<out_t>(output, [N,OH,OW,OC], index, bias[(BC == 1) ? 0 : index[3]]) +} +for_each(0 <= n < N, 0 <= iy < IH, 0 <= ix < IW, 0 <= oc < OC, + 0 <= ic < IC, 0 <= ky < KH, 0 <= kx < KW) { + index_t oy = iy * stride_y + out_pad_top + ky; + index_t ox = ix * stride_x + out_pad_left + kx; + if (oy >= 0 && oy < OH && ox >= 0 && ox < OW) { + out_t acc = static_cast<out_t>(tensor_read<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc])); + out_t value = static_cast<out_t>(tensor_read<in_t>(input, [N,IH,IW,IC], [n,iy,ix,ic])); + out_t weight = static_cast<out_t>(tensor_read<weight_t>(weight, [OC,KH,KW,IC], [oc,ky,kx,ic])); + value = apply_sub_s<out_t>(value, static_cast<out_t>(input_zp)); + weight = apply_sub_s<out_t>(weight, static_cast<out_t>(weight_zp)); + acc = apply_add_s<out_t>(acc, apply_mul_s<out_t>(value, weight)); + tensor_write<out_t>(output, [N,OH,OW,OC], [n,oy,ox,oc], acc); + } +} diff --git a/pseudocode/operators/VARIABLE.tosac b/pseudocode/operators/VARIABLE.tosac new file mode 100644 index 0000000..4d2fce7 --- /dev/null +++ b/pseudocode/operators/VARIABLE.tosac @@ -0,0 +1,33 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + + +tensor_t var_tensor = variable_tensor_lookup(uid); + +// Invocation for the first time +if (var_tensor == NULL) { + // Allocate the persistent mutable memory for the variable tensor + tensor_t var_tensor = variable_tensor_allocate<var_t>(var_shape, uid); + + if (initial_value != NULL) { + REQUIRE(var_t == in_t); + REQUIRE(var_shape == shape); + for_each (index in shape) { + // Copy data from initial_value to var_tensor + in_t value = tensor_read<in_t>(initial_value, shape, index); + tensor_write<in_t>(var_tensor.data, var_shape, index, value); + } + var_tensor.is_written = true; + } +} else { // Variable tensor has already been declared + // It's invalid to declare the second variable with the same uid in a single graph execution, + REQUIRE(!var_tensor.seen); +} + +var_tensor.seen = true; diff --git a/pseudocode/operators/VARIABLE_READ.tosac b/pseudocode/operators/VARIABLE_READ.tosac new file mode 100644 index 0000000..c2e0e38 --- /dev/null +++ b/pseudocode/operators/VARIABLE_READ.tosac @@ -0,0 +1,26 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + + +tensor_t variable_tensor = variable_tensor_lookup(uid); +// Check this variable tensor has been decalred +REQUIRE(variable_tensor != NULL); +// Check this variable tensor has been written +REQUIRE(variable_tensor.is_written); +// Output tensor's shape and variable_tensor's shape have to match +REQUIRE(variable_tensor.shape == shape); +// Output tensor's shape and variable_tensor's type have to match +REQUIRE(variable_tensor.type == out_t); + +for_each (index in shape) { + // Read data from pseudo-buffer resource to the output + out_t value = tensor_read<tensor_t>(variable_tensor.data, variable_tensor.shape, index); + tensor_write<out_t>(input1, shape, index, value); +} + diff --git a/pseudocode/operators/VARIABLE_WRITE.tosac b/pseudocode/operators/VARIABLE_WRITE.tosac new file mode 100644 index 0000000..83c5525 --- /dev/null +++ b/pseudocode/operators/VARIABLE_WRITE.tosac @@ -0,0 +1,29 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + + +tensor_t. variable_tensor = variable_tensor_lookup(uid); +// Check this variable tensor has been declared +REQUIRE(variable_tensor); +// The tensor has to be seen before to be written to +// The seen variable is cleared before each graph execution and set in declaration +REQUIRE(variable_tensor.seen); +// Input tensor's shape and variable_tensor's shape have to match +REQUIRE(variable_tensor.shape == shape); +// Input tensor's shape and variable_tensor's type have to match +REQUIRE(variable_tensor.type == in_t); + +for_each (index in shape) { + // Write data from the input to the pseudo-buffer resource + in_t value = tensor_read<in_t>(input1, shape, index); + tensor_write<tensor_t>(variable_tensor.data, variable_tensor.shape, index, value); +} + +variable_tensor.is_written = true; + diff --git a/pseudocode/operators/WHILE_LOOP.tosac b/pseudocode/operators/WHILE_LOOP.tosac new file mode 100644 index 0000000..dae96d5 --- /dev/null +++ b/pseudocode/operators/WHILE_LOOP.tosac @@ -0,0 +1,33 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +ERROR_IF(tosa_nesting_depth >= MAX_NESTING); +ERROR_IF(tensor_list_shape(input_list) != tosa_list_shape(output_list)); +ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(cond_graph)); +ERROR_IF(tensor_list_shape(input_list) != tosa_input_shape(body_graph)); +ERROR_IF(tensor_list_shape(input_list) != tosa_output_shape(body_graph)); +// Condition graph output must be a single element tensor with a single bool value +ERROR_IF(tensor_size(tosa_output_shape(cond_graph)) != 1); +ERROR_IF(tosa_output_type(cond_graph) != bool_t); + +// The iteration number 'i' is included to give unique names to variables +// in each iteration of the loop and is not required by implementations +int32_t i=0; // iteration number +tensor_list_t list[]; // array of tensor lists indexed by iteration +bool_t *condition[]; // array of condition tensors indexed by iteration +list[i] = input_list; // copy input data as list[0] +tosa_nesting_depth++; +tosa_execute_graph(cond_graph, list[i], [ condition[i] ]); // initial condition +while (condition[i][0]) { + tosa_execute_graph(body_graph, list[i], list[i+1]); + i = i+1; + tosa_execute_graph(cond_graph, list[i], [ condition[i] ]); +} +tosa_nesting_depth--; +output_list = list[i]; diff --git a/pseudocode/operators/tables/ERF.tosac b/pseudocode/operators/tables/ERF.tosac new file mode 100644 index 0000000..0ff29ff --- /dev/null +++ b/pseudocode/operators/tables/ERF.tosac @@ -0,0 +1,16 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +int16_t erf_reference(int16_t x) { // input x range is -256 to + 256 inclusive + F64 v = (double)x / (double)64; + v = erf(v); + return round_to_nearest_int(32768.0 * v); +} + +generate_lookup_table(&erf_table, &erf_reference); diff --git a/pseudocode/operators/tables/SIGMOID.tosac b/pseudocode/operators/tables/SIGMOID.tosac new file mode 100644 index 0000000..dad1e1d --- /dev/null +++ b/pseudocode/operators/tables/SIGMOID.tosac @@ -0,0 +1,16 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +int16_t sigmoid_reference(int16_t x) { // input x range is -256 to + 256 inclusive + fp64_t v = (fp64_t)x / (fp64_t)16; + v = 1.0/(1.0 + exp(-v)); + return round_to_nearest_int(32768.0 * v); +} + +generate_lookup_table(&sigmoid_table, &sigmoid_reference); diff --git a/pseudocode/operators/tables/TANH.tosac b/pseudocode/operators/tables/TANH.tosac new file mode 100644 index 0000000..b45f121 --- /dev/null +++ b/pseudocode/operators/tables/TANH.tosac @@ -0,0 +1,17 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +int16_t tanh_reference(int16_t x) { // input x range is -256 to +256 inclusive + fp64_t v = (fp64_t)x/(fp64_t)32; + v = exp(-2.0*v); + v = (1.0-v)/(1.0+v); + return round_to_nearest_int(32768.0 * v); +} + +generate_lookup_table(&tanh_table, &tanh_reference); |