diff --git a/.all-contributorsrc b/.all-contributorsrc index 7f624a7d6..befc08f85 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -269,6 +269,23 @@ "contributions": [ "code" ] + }, + { + "login": "tekkac", + "name": "Trunks @ Carbonable", + "avatar_url": "https://avatars.githubusercontent.com/u/98529704?v=4", + "profile": "https://github.com/tekkac", + "contributions": [ + "doc" + ], + }, + "login": "canacechan", + "name": "canacechan", + "avatar_url": "https://avatars.githubusercontent.com/u/127183619?v=4", + "profile": "https://github.com/canacechan", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 33e343ae4..c1fc2cfd6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,5 +9,5 @@ jobs: - uses: actions/checkout@v3 - uses: software-mansion/setup-scarb@v1 with: - scarb-version: "2.4.0" + scarb-version: "2.5.3" - run: scarb test --workspace && scarb fmt --workspace \ No newline at end of file diff --git a/.tool-versions b/.tool-versions index 21cfc8077..ebe254233 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -scarb 2.4.0 +scarb 2.5.3 diff --git a/README.md b/README.md index 1f48201d3..cc2cf1ef2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ # Orion: An Open-source Framework for Validity and ZK ML ✨ -[![All Contributors](https://img.shields.io/badge/all_contributors-29-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-30-orange.svg?style=flat-square)](#contributors-) Orion is an open-source, community-driven framework dedicated to Provable Machine Learning. It provides essential components and a new ONNX runtime for building verifiable Machine Learning models using [STARKs](https://starkware.co/stark/). @@ -47,7 +47,7 @@ Join the community and help build a safer and transparent AI in our [Discord](ht ## 🚀 Orion Usage - For an insightful overview of impressive proof of concepts, models, and tutorials created by our community, please visit [Orion Usage](https://github.com/gizatechxyz/orion/blob/main/orion-usage.md). -- Discover a currated list of tutorials and models developed using Orion in [Orion-Hub](https://github.com/gizatechxyz/Orion-Hub). +- Discover a curated list of tutorials and models developed using Orion in [Orion-Hub](https://github.com/gizatechxyz/Orion-Hub). ## ✍️ Authors & contributors @@ -106,6 +106,8 @@ Thanks goes to these wonderful people:
`) - The input tensor. * `x_scale`(`@Tensor`) - Scale for input `x`. * `x_zero_point`(`@Tensor `) - Zero point for input `x`. diff --git a/docs/framework/operators/tensor/tensor.dynamic_quantize_linear.md b/docs/framework/operators/tensor/tensor.dynamic_quantize_linear.md new file mode 100644 index 000000000..d79fa2d98 --- /dev/null +++ b/docs/framework/operators/tensor/tensor.dynamic_quantize_linear.md @@ -0,0 +1,54 @@ +# tensor.dynamic_quantize_linear + +```rust +fn dynamic_quantize_linear(self: @Tensor ) -> (Tensor:: , Tensor, Tensor ); +``` + +Quantizes a Tensor using dynamic linear quantization. + +The dynamic linear quantization operator. It consumes a high precision tensor +to compute the low precision / quantized tensor dynamicly. +Right now only uint8 is supported, it saturates to [0, 255]. + +## Args + +* `self`(`@Tensor `) - The input tensor. + +## Returns + +A new `Tensor ` with the same shape as the input tensor, containing the quantized values. +* `y_scale`(`@Tensor`) - Scale for doing quantization to get `y`. +* `y_zero_point`(`@Tensor `) - Zero point for doing quantization to get `y`. + +## Type Constraints + +* `T` in (`Tensor `, `Tensor `, `Tensor `, `tensor `) +* `Q` in (`Tensor `)- Constrain `y` to 8-bit unsigned integer tensor. + +## Examples + +```rust +use array::{ArrayTrait, SpanTrait}; + +use orion::operators::tensor::{TensorTrait, Tensor, I8Tensor, I32Tensor}; +use orion::numbers::{u8, i32, IntegerTrait}; + +fn dynamic_quantize_linear_example() -> (Tensor , Tensor , Tensor ) { + // We instantiate a 1D Tensor here. + let x = TensorTrait:: ::new( + shape: array![6].span(), + data: array![ + FP16x16 { mag: 10945, sign: false }, + FP16x16 { mag: 190054, sign: false }, + FP16x16 { mag: 196608, sign: false }, + FP16x16 { mag: 229376, sign: false }, + FP16x16 { mag: 196608, sign: true }, + FP16x16 { mag: 229376, sign: true }, + ] + .span(), + ); + + return x.dynamic_quantize_linear(); +} +>>> ([133, 233, 236, 255, -18, -0], [0.02745], [128] +``` diff --git a/docs/framework/operators/tensor/tensor.hamming_window.md b/docs/framework/operators/tensor/tensor.hamming_window.md new file mode 100644 index 000000000..1ce1a25f1 --- /dev/null +++ b/docs/framework/operators/tensor/tensor.hamming_window.md @@ -0,0 +1,32 @@ +# tensor.hamming_window + +```rust + fn hamming_window(size: T, periodic: Option ) -> Tensor ; +``` + +Generates a Hamming window as described in the paper https://ieeexplore.ieee.org/document/1455106. + + +* `size`(`T`) - A scalar value indicating the length of the window. +* `periodic`(Option ) - If 1, returns a window to be used as periodic function. If 0, return a symmetric window. When 'periodic' is specified, hann computes a window of length size + 1 and returns the first size points. The default value is 1. + +## Returns + +A Hamming window with length: size. The output has the shape: [size]. + +## Examples + +```rust +use core::array::{ArrayTrait, SpanTrait}; +use orion::operators::tensor::FP8x23TensorPartialEq; +use orion::operators::tensor::{FP8x23Tensor, FP8x23TensorAdd}; +use orion::operators::tensor::{TensorTrait, Tensor}; +use orion::utils::{assert_eq, assert_seq_eq}; +use orion::numbers::{FixedTrait, FP8x23}; + + +fn hamming_window_example() -> Tensor { + return TensorTrait::hamming_window(FP8x23 { mag: 33554432, sign: false }, Option::Some(0)); // size: 4 +} +>>> [729444 6473817 6473817 729444] +``` diff --git a/docs/framework/operators/tensor/tensor.hann_window.md b/docs/framework/operators/tensor/tensor.hann_window.md new file mode 100644 index 000000000..c3119342c --- /dev/null +++ b/docs/framework/operators/tensor/tensor.hann_window.md @@ -0,0 +1,32 @@ +# tensor.hann_window + +```rust + fn hann_window(size: T, periodic: Option ) -> Tensor ; +``` + +Generates a Hann window as described in the paper https://ieeexplore.ieee.org/document/1455106. + + +* `size`(`T`) - A scalar value indicating the length of the window. +* `periodic`(Option ) - If 1, returns a window to be used as periodic function. If 0, return a symmetric window. When 'periodic' is specified, hann computes a window of length size + 1 and returns the first size points. The default value is 1. + +## Returns + +A Hann window with length: size. The output has the shape: [size]. + +## Examples + +```rust +use core::array::{ArrayTrait, SpanTrait}; +use orion::operators::tensor::FP8x23TensorPartialEq; +use orion::operators::tensor::{FP8x23Tensor, FP8x23TensorAdd}; +use orion::operators::tensor::{TensorTrait, Tensor}; +use orion::utils::{assert_eq, assert_seq_eq}; +use orion::numbers::{FixedTrait, FP8x23}; + + +fn hann_window_example() -> Tensor { + return TensorTrait::hann_window(FP8x23 { mag: 33554432, sign: false }, Option::Some(0)); // size: 4 +} +>>> [0 6291455 6291456 0] +``` diff --git a/docs/framework/operators/tensor/tensor.optional.md b/docs/framework/operators/tensor/tensor.optional.md new file mode 100644 index 000000000..7f11b175c --- /dev/null +++ b/docs/framework/operators/tensor/tensor.optional.md @@ -0,0 +1,42 @@ +# tensor.optional + +```rust + fn optional(self: @Tensor ) -> Option >; +``` + +Constructs an optional-type value containing either an empty optional of a certain +type specified by the attribute, or a non-empty value containing the input element. + +## Args + +* `self`(`@Tensor `) - The input tensor. + +## Returns + +The optional output enclosing the input element. + +## Examples + +```rust +use core::option::OptionTrait; +fn optional_example() -> Option > { + let a = TensorTrait::< + FP16x16 + >::new( + shape: array![4, 2].span(), + data: array![ + 1_i8, + 2_i8, + 3_i8, + 4_i8, + 5_i8, + 6_i8, + 7_i8, + 8_i8 + ].span(), + ); + a.optional() +} +>>> Option[Tensor[1,2,3,4,5,6,7,8]] + +``` diff --git a/docs/framework/operators/tensor/tensor.quantize_linear.md b/docs/framework/operators/tensor/tensor.quantize_linear.md index aae8e088b..1d1702645 100644 --- a/docs/framework/operators/tensor/tensor.quantize_linear.md +++ b/docs/framework/operators/tensor/tensor.quantize_linear.md @@ -20,7 +20,7 @@ For (x / y_scale), it's rounding to the nearest even. ## Returns -A new `Tensor ` with the same shape as the input tensor, containing the quantized values. +A new `Tensor ` with the same shape as the input tensor, containing the quantized values. ## Type Constraints diff --git a/docs/framework/operators/tensor/tensor.random_uniform_like.md b/docs/framework/operators/tensor/tensor.random_uniform_like.md new file mode 100644 index 000000000..06ca8e772 --- /dev/null +++ b/docs/framework/operators/tensor/tensor.random_uniform_like.md @@ -0,0 +1,61 @@ +# TensorTrait::random_uniform_like + +```rust + fn random_uniform_like(tensor: @Tensor, high: Option , low: Option , seed: Option ) -> Tensor ; +``` + +RandomUniformLike generates a tensor with random values using a uniform distribution, matching the shape of the input tensor. + +This operation creates a new tensor with the same shape as the input tensor, where each element is initialized with a random value sampled from a uniform distribution. + +## Args + +* `tensor`(`@Tensor `) - The input tensor of [N,C,H,W], where N is the batch axis, C is the channel or depth, H is the height and W is the width. +* `high`(Option ) - An optional parameter specifying the upper bound (exclusive) of the uniform distribution. If not provided, defaults to 1.0. +* `low`(Option ) - An optional parameter specifying the lower bound (inclusive) of the uniform distribution. If not provided, defaults to 0.0. +* `seed`(Option ) - An optional parameter specifying the seed for the random number generator. If not provided, a random seed will be used. + +## Returns + +* A `Tensor ` with the same shape as the input tensor, filled with random values from a uniform distribution within the specified range. + +## Examples + +```rust +use orion::operators::tensor::{FP8x23Tensor, FP8x23TensorAdd}; +use core::array::{ArrayTrait, SpanTrait}; +use orion::operators::tensor::{TensorTrait, Tensor}; +use orion::utils::{assert_eq, assert_seq_eq}; +use orion::operators::tensor::FP8x23TensorPartialEq; +use orion::numbers::{FixedTrait, FP8x23}; + + +fn example() -> Tensor { + let mut shape = ArrayTrait:: ::new(); + shape.append(1); + shape.append(8); + shape.append(1); + shape.append(2); + + let mut data = ArrayTrait::new(); + data.append(FP8x23 { mag: 70016, sign: true }); + data.append(FP8x23 { mag: 57536, sign: false }); + data.append(FP8x23 { mag: 116032, sign: false }); + data.append(FP8x23 { mag: 162944, sign: true }); + data.append(FP8x23 { mag: 43360, sign: false }); + data.append(FP8x23 { mag: 128960, sign: false }); + data.append(FP8x23 { mag: 151808, sign: true }); + data.append(FP8x23 { mag: 28368, sign: false }); + data.append(FP8x23 { mag: 21024, sign: false }); + data.append(FP8x23 { mag: 24992, sign: false }); + data.append(FP8x23 { mag: 125120, sign: true }); + data.append(FP8x23 { mag: 79168, sign: true }); + data.append(FP8x23 { mag: 136960, sign: true }); + data.append(FP8x23 { mag: 10104, sign: true }); + data.append(FP8x23 { mag: 136704, sign: false }); + data.append(FP8x23 { mag: 184960, sign: true }); + let tensor = TensorTrait::new(shape.span(), data.span()); + return TensorTrait::random_uniform_like(@tensor, Option::Some(FP8x23 { mag: 83886080, sign: false }),Option::Some(FP8x23 { mag: 8388608, sign: false }), Option::Some(354145)); +} +>>> [[[[7299130, 4884492]], [[2339070, 1559536]], [[3448557, 984617]], [[5745934, 3670947]], [[4665989, 3079292]], [[3375288, 948254]], [[3749966, 4911069]], [[1358829, 4368105]]]] +``` diff --git a/docs/framework/operators/tensor/tensor.range.md b/docs/framework/operators/tensor/tensor.range.md new file mode 100644 index 000000000..90530dedc --- /dev/null +++ b/docs/framework/operators/tensor/tensor.range.md @@ -0,0 +1,33 @@ +# tensor.range + +```rust + fn range(start: T, end: T, step: T) -> Tensor ; +``` + +Generate a tensor containing a sequence of numbers that begin at start and extends by increments of delta up to limit (exclusive). + + +* `start`(`T`) - First entry for the range of output values. +* `end`(`T`) - Exclusive upper limit for the range of output values. +* `step `(`T`) - Value to step by. + +## Returns + +A 1-D tensor with same type as the inputs containing generated range of values. + +## Examples + +```rust +use core::array::{ArrayTrait, SpanTrait}; +use orion::operators::tensor::I32TensorPartialEq; +use orion::operators::tensor::{TensorTrait, Tensor}; +use orion::operators::tensor::{I32Tensor, I32TensorAdd}; +use orion::utils::{assert_eq, assert_seq_eq}; +use orion::numbers::NumberTrait; + + +fn range_example() -> Tensor { + return TensorTrait::range(21,2,-3); +} +>>> [21 18 15 12 9 6 3] +``` diff --git a/docs/framework/operators/tensor/tensor.reverse_sequence.md b/docs/framework/operators/tensor/tensor.reverse_sequence.md new file mode 100644 index 000000000..3cfbf5b18 --- /dev/null +++ b/docs/framework/operators/tensor/tensor.reverse_sequence.md @@ -0,0 +1,49 @@ +# tensor.reverse_sequence + +```rust + fn reverse_sequence(self: @Tensor , sequence_lens: @Tensor , batch_axis: Option , time_axis: Option ) -> + Tensor ; +``` + +Reverse batch of sequences having different lengths specified by sequence_lens. + +* `self`(`@Array >`) - Tensor of rank r >= 2. +* `sequence_lens`(`@Tensor `) - Tensor specifying lengths of the sequences in a batch. It has shape [batch_size]. +* `batch_axis`(`Option `) - (Optional) Specify which axis is batch axis. Must be one of 1 (default), or 0. +* `time_axis`(`Option `) - (Optional) Specify which axis is time axis. Must be one of 0 (default), or 1. + +## Panics + +* Panics if the 'batch_axis' == 'time_axis'. +* Panics if the 'batch_axis' and 'time_axis' are not 0 and 1. +* Panics if the 'sequence_len' exceeding the sequence range. + +## Returns + +Tensor with same shape of input. + +## Example +```rust +use core::array::{ArrayTrait, SpanTrait}; +use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor}; +use core::option::OptionTrait; +fn reverse_sequence_example() -> Tensor { + let tensor: Tensor = TensorTrait:: ::new( + shape: array![4,4].span(), + data: array![ + 0, 1, 2, 3, 4, 5, 6, 7,8,9,10,11,12,13,14,15,16 + ].span(), + ); + let sequence_lens = TensorTrait:: ::new(array![4].span(), array![1,2,3,4].span()); + let batch_axis = Option::Some(0); + let time_axis = Option::Some(1); + // We can call `split` function as follows. + return tensor.reverse_sequence(sequence_lens, batch_axis, time_axis); +} +>>> [ + [0,1,2,3], + [5,4,6,7], + [10,9,8,11], + [15,14,13,12] + ] +``` diff --git a/docs/framework/operators/tensor/tensor.scatter_nd.md b/docs/framework/operators/tensor/tensor.scatter_nd.md new file mode 100644 index 000000000..f68c5121b --- /dev/null +++ b/docs/framework/operators/tensor/tensor.scatter_nd.md @@ -0,0 +1,76 @@ +# tensor.scatter_nd + +```rust + fn scatter_nd(self: @Tensor , updates: Tensor , indices: Tensor , reduction: Option ) -> Tensor ; +``` + +Produces a copy of input data, and updates value to values specified by updates at specific index positions specified by indices. + +## Args + +* `self`(`@Tensor `) - The input tensor. +* `updates`(`Tensor `) - The updates tensor. +* `indices`(`Tensor `) - Tensor of indices. +* `reduction`(`Option `) - Reduction operation. Default: reduction='none'. + +## Panics + +* Panics if index values are not within bounds [-s, s-1] along axis of size s. +* Panics if indices last axis is greater than data rank. + +## Returns + +A new `Tensor ` . + +## Example + +```rust +use core::array::{ArrayTrait, SpanTrait}; + +use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor}; + +fn scatter_nd_example() -> Tensor { + let tensor = TensorTrait:: ::new( + shape: array![4, 4, 4].span(), + data: array![1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, + 7, 8, 8, 7, 6, 5, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, + 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8].span() + ); + + let updates = TensorTrait:: ::new( + shape: array![2, 4, 4].span(), + data: array![5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 1, 1, 1, 1, 2, 2, + 2, 2, 3, 3, 3, 3, 4, 4, 4, 4].span(), + ); + + let indices = TensorTrait:: ::new( + shape: array![2, 1].span(), + data: array![0, 2].span(), + ); + + return tensor.scatter_nd( + updates: updates + indices: indices, + reduction: Option::Some('add'), + ); +} +>>> [[[ 6., 7., 8., 9.], + [11., 12., 13., 14.], + [15., 14., 13., 12.], + [12., 11., 10., 9.]], + + [[ 1., 2., 3., 4.], + [ 5., 6., 7., 8.], + [ 8., 7., 6., 5.], + [ 4., 3., 2., 1.]], + + [[ 9., 8., 7., 6.], + [ 6., 5., 4., 3.], + [ 4., 5., 6., 7.], + [ 9., 10., 11., 12.]], + + [[ 8., 7., 6., 5.], + [ 4., 3., 2., 1.], + [ 1., 2., 3., 4.], + [ 5., 6., 7., 8.]]] +``` diff --git a/docs/framework/operators/tensor/tensor.split.md b/docs/framework/operators/tensor/tensor.split.md index 26b4a546f..20e52d93a 100644 --- a/docs/framework/operators/tensor/tensor.split.md +++ b/docs/framework/operators/tensor/tensor.split.md @@ -4,9 +4,10 @@ fn split(self: @Tensor , axis: usize, num_outputs: Option , split: Option > ) -> Array >; ``` - +## Args Split a tensor into a list of tensors, along the specified ‘axis’ +## Args * `self`(`@Tensor `) - The input tensor. * `axis`(`usize`) - The axis along which to split on. @@ -40,7 +41,7 @@ fn split_tensor_example() -> Array > { // split = Option::Some(array![1, 1].span()); let split_num: Option > = Option::None(()); // We can call `split` function as follows. - return tensor.split(0, num_outputs, split_num); + return tensor.split(1, num_outputs, split_num); } >>> [[0,1],[4,5]] [[2,3],[6,7]] diff --git a/docs/framework/operators/tensor/tensor.split_to_sequence.md b/docs/framework/operators/tensor/tensor.split_to_sequence.md new file mode 100644 index 000000000..110039f5b --- /dev/null +++ b/docs/framework/operators/tensor/tensor.split_to_sequence.md @@ -0,0 +1,50 @@ +# tensor.split_to_sequence + +```rust + fn split_to_sequence( + self: @Tensor , axis: usize, keepdims: usize, split: Option > + ) -> Array >; +``` + +Split a tensor into a sequence of tensors, along the specified ‘axis’ + + +## Args +* `self`(`@Tensor `) - The input tensor to split. +* `axis`(`usize`) - The axis along which to split on. +* `keepdims `(`usize`) - Keep the split dimension or not. If input ‘split’ is specified, this attribute is ignored. +* `split `(`Option >`) - Length of each output. It can be either a scalar(tensor of empty shape), or a 1-D tensor. All values must be >= 0. + +## Panics + +* Panics if the 'axis' accepted range is not [-rank, rank-1] where r = rank(input). +* Panics if the 'split' is not either a scalar (tensor of empty shape), or a 1-D tensor. + +## Returns + +One or more outputs forming a sequence of tensors after splitting. + +## Examples + +```rust +use core::array::{ArrayTrait, SpanTrait}; +use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor}; +use core::option::OptionTrait; +fn split_to_sequence_example() -> Array > { + let tensor: Tensor = TensorTrait:: ::new( + shape: array![2,4].span(), + data: array![ + 0, 1, 2, 3, 4, 5, 6, 7 + ].span(), + ); + let num_outputs = Option::Some(2); + // let split = Option::Some(TensorTrait::new(array![1].span(), array![2].span())); + let split: Option > = Option::Some(TensorTrait::new(array![2].span(), array![2, 2].span())); + // We can call `split_to_sequence` function as follows. + return tensor.split_to_sequence(1, 1, split); +} +>>> [ + [[0,1],[4,5]], + [[2,3],[6,7]] + ] +``` diff --git a/nodegen/node/blackman_window.py b/nodegen/node/blackman_window.py new file mode 100644 index 000000000..fdbacc615 --- /dev/null +++ b/nodegen/node/blackman_window.py @@ -0,0 +1,130 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait, get_data_statement + +def blackman_window(size, output_datatype=None, periodic=None) -> np.ndarray: # type: ignore + if periodic == 1: + N_1 = size + else: + N_1 = size - 1 + ni = np.arange(size, dtype=output_datatype) + alpha = 0.42 + beta = 0.08 + y = np.cos((ni * (np.float64(np.pi).astype(output_datatype) * 2)) / N_1).astype(output_datatype) * (-0.5) + y += np.cos((ni * (np.float64(np.pi).astype(output_datatype) * 4)) / N_1) * beta + y += alpha + return y.astype(output_datatype) + +class Blackman_window(RunAll): + + @staticmethod + # We test here with fp8x23 implementation. + def fp8x23(): + args = [3] + # x = np.float64(4) + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP8x23), Dtype.FP8x23) + y = blackman_window(*args, np.float64) + + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP8x23, y.shape, to_fp(y.flatten(), FixedImpl.FP8x23)) + + # Define the name of the generated folder. + name = "blackman_window_fp8x23" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::blackman_window({','.join(args_str)}, Option::Some(0))", # The code signature. + name # The name of the generated folder. + ) + + @staticmethod + # We test here with fp16x16 implementation. + def fp16x16(): + print(get_data_statement(to_fp(np.array([np.pi]).flatten(), FixedImpl.FP16x16), Dtype.FP16x16)) + args = [3] + # x = np.float64(4) + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP16x16), Dtype.FP16x16) + y = blackman_window(*args, np.float16, 1) + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + # Define the name of the generated folder. + name = "blackman_window_fp16x16" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::blackman_window({','.join(args_str)}, Option::Some(1))", # The code signature. + name # The name of the generated folder. + ) + + # @staticmethod + # # We test here with i8 implementation. + # def i8(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.I8)) + # args = [5] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.I8) + # y = blackman_window(*args, np.int8) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.I8, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "blackman_window_i8" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::blackman_window({','.join(args_str)}, Option::Some(1))", # The code signature. + # name # The name of the generated folder. + # ) + + # @staticmethod + # # We test here with i32 implementation. + # def i32(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.I32)) + # args = [4] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.I32) + # y = blackman_window(*args, np.int32) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.I32, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "blackman_window_i32" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::blackman_window({','.join(args_str)}, Option::Some(0))", # The code signature. + # name # The name of the generated folder. + # ) + + # @staticmethod + # # We test here with u32 implementation. + # def u32(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.U32)) + # args = [4] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.U32) + # y = blackman_window(*args, np.uint32) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.U32, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "blackman_window_u32" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::blackman_window({','.join(args_str)}, Option::Some(0))", # The code signature. + # name # The name of the generated folder. + # ) + \ No newline at end of file diff --git a/nodegen/node/col2im.py b/nodegen/node/col2im.py new file mode 100644 index 000000000..159d25dd0 --- /dev/null +++ b/nodegen/node/col2im.py @@ -0,0 +1,320 @@ + + +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait + + +def col2im(data, image_shape, block_shape, dilations=None, pads=None, strides=None): # type: ignore + if dilations is None: + dilations = [1 for s in image_shape] + if pads is None: + pads = [0 for s in image_shape] * 2 + if strides is None: + strides = [1 for s in image_shape] + bl = np.prod(block_shape) + C = data.shape[1] // bl + data = data.reshape(data.shape[:1] + (C,) + (bl,) + data.shape[2:]) + ks = tuple(block_shape) + res = None + for n in range(data.shape[0]): + for c in range(data.shape[1]): + out = col2im_naive_implementation( + data[n, c, ...], image_shape, ks, dilations, pads, strides + ) + if res is None: + new_shape = data.shape[:2] + out.shape + res = np.empty(new_shape, dtype=data.dtype) + res[n, c, ...] = out + return (res,) # type: ignore + +def _get_indices(i, shape): + res = np.empty((len(shape),), dtype=np.int64) + k = len(shape) - 1 + while k > 0: + m = i % shape[k] + res[k] = m + i -= m + i /= shape[k] + k -= 1 + res[0] = i + return res + +def _col2im_shape_check(X, output_shape, kernel_shape, dilations, pads, strides): # type: ignore + n_input_plane = X.shape[0] + + kernel_size = np.prod(kernel_shape) + + if n_input_plane % kernel_size != 0: + raise ValueError( + f"Expected size of input's dimension 1 to be divisible by the " + f"product of kernel_size={kernel_size}, " + f"but got input.size(1)={n_input_plane} " + f"and kernel_shape={kernel_shape}, X.shape={X.shape}, output_shape={output_shape}." + ) + + input_length = X.shape[1] + n_dims = len(output_shape) + n_blocks = [] + + + for i in range(n_dims): + n_block = ( + output_shape[i] + + pads[i, :].sum() + - dilations[i] * (kernel_shape[i] - 1) + - 1 + ) // strides[i] + 1 + n_blocks.append(n_block) + + + block_size = np.prod(n_blocks) + if input_length != block_size: + raise ValueError( + f"Given n_input_plane={n_input_plane}, X.shape={X.shape}, " + f"output_shape={output_shape}, kernel_shape={kernel_shape}, " + f"dilations={dilations}, pads={pads}, strides={strides}, " + f"expected size of input's dimension 2 to match the calculated number of " + f"sliding blocks {n_blocks} = {block_size}, " + f"but got input.size(2)={input_length}.", + ) + + +def col2im_naive_implementation(data, image_shape, kernel_shape, dilations, pads, strides): # type: ignore + + n_dims = len(pads) // 2 + new_pads = np.array([(pads[i], pads[i + n_dims]) for i in range(n_dims)]) + _col2im_shape_check(data, image_shape, kernel_shape, dilations, new_pads, strides) + + data_col = data + data_im = np.zeros(image_shape, dtype=data.dtype) + + dim_col = [] + for i in range(n_dims): + col = ( + image_shape[i] + + new_pads[i, :].sum() + - (dilations[i] * (kernel_shape[i] - 1) + 1) + ) // strides[i] + 1 + dim_col.append(col) + kernel_size = np.prod(kernel_shape) + col_size = np.prod(dim_col) + for c_col in range(kernel_size): + offset = _get_indices(c_col, kernel_shape) + + for col in range(col_size): + + ind_col = _get_indices(col, dim_col) + ind_im = [] + for i in range(n_dims): + ind = ( + ind_col[i] * strides[i] - new_pads[i, 0] + offset[i] * dilations[i] + ) + ind_im.append(ind) + if not _is_out(ind_im, data_im.shape): + data_im[tuple(ind_im)] += data_col[c_col, col] + + + return data_im + + +def _is_out(ind, shape): + for i, s in zip(ind, shape): + if i < 0: + return True + if i >= s: + return True + return False + + + +class Col2im(RunAll): + + @staticmethod + def export_col2im() -> None: + x = np.array( + [ + [ + [1.0, 6.0, 11.0, 16.0, 21.0], # (1, 5, 5) + [2.0, 7.0, 12.0, 17.0, 22.0], + [3.0, 8.0, 13.0, 18.0, 23.0], + [4.0, 9.0, 14.0, 19.0, 24.0], + [5.0, 0.0, 15.0, 20.0, 25.0], + ] + ] + ).astype(np.float32) + + image_shape = np.array([5, 5]).astype(np.int64) + block_shape = np.array([1, 5]).astype(np.int64) + + y = col2im(x,image_shape,block_shape) + y = np.array(y[0]) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "col2im" + func_sig = "NNTrait::col2im(" + func_sig += "@input_0," + func_sig += "array![5, 5].span()," + func_sig += "array![1, 5].span()," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_col2im_strides() -> None: + x = np.array( + [ + [ + [0.0, 0.0, 0.0, 0.0], # (1, 9, 4) + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0], + ] + ] + ).astype(np.float32) + image_shape = np.array([5, 5]).astype(np.int64) + block_shape = np.array([3, 3]).astype(np.int64) + + y = col2im(x,image_shape,block_shape,strides=[2, 2]) + y = np.array(y[0]) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "col2im_strides" + func_sig = "NNTrait::col2im(" + func_sig += "@input_0," + func_sig += "array![5, 5].span()," + func_sig += "array![3, 3].span()," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![2, 2].span()))" + make_test( + [x], y, func_sig, name, Trait.NN) + + @staticmethod + def export_col2im_pads() -> None: + x = np.array( + [ + [ + [ + 1.0, 6.0, 11.0, 16.0, 21.0, 26, 31, 36, 41, 46, 51, 56, 61, 66, 71, + ], # (1, 5, 15) + [ + 2.0, 7.0, 12.0, 17.0, 22.0, 27, 32, 37, 42, 47, 52, 57, 62, 67, 72, + ], + [ + 3.0, 8.0, 13.0, 18.0, 23.0, 28, 33, 38, 43, 48, 53, 58, 63, 68, 73, + ], + [ + 4.0, 9.0, 14.0, 19.0, 24.0, 29, 34, 39, 44, 49, 54, 59, 64, 69, 74, + ], + [ + 5.0, 10.0, 15.0, 20.0, 25.0, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, + ], + ] + ] + ).astype(np.float32) + image_shape = np.array([5, 5]).astype(np.int64) + block_shape = np.array([1, 5]).astype(np.int64) + + y = col2im(x,image_shape,block_shape,pads=[0, 1, 0, 1]) + y = np.array(y[0]) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "col2im_pads" + func_sig = "NNTrait::col2im(" + func_sig += "@input_0," + func_sig += "array![5, 5].span()," + func_sig += "array![1, 5].span()," + func_sig += "Option::None," + func_sig += "Option::Some(array![0, 1, 0, 1].span())," + func_sig += "Option::None)" + make_test( + [x], y, func_sig, name, Trait.NN) + + @staticmethod + def export_col2im_dilations() -> None: + x = np.array( + [ + [ + [1.0, 5.0, 9.0, 13.0, 17], # (1, 4, 5) + [2.0, 6.0, 10.0, 14.0, 18], + [3.0, 7.0, 11.0, 15.0, 19], + [4.0, 8.0, 12.0, 16.0, 20], + ] + ] + ).astype(np.float32) + image_shape = np.array([6, 6]).astype(np.int64) + block_shape = np.array([2, 2]).astype(np.int64) + + + y = col2im(x,image_shape,block_shape, dilations=[1, 5]) + y = np.array(y[0]) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "col2im_dilations" + func_sig = "NNTrait::col2im(" + func_sig += "@input_0," + func_sig += "array![6, 6].span()," + func_sig += "array![2, 2].span()," + func_sig += "Option::Some(array![1, 5].span())," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x], y, func_sig, name, Trait.NN) + + @staticmethod + def export_col2im_5D() -> None: + x = np.array( + [ + [ + [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56], # (1, 10, 12) + [2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57], + [3, 8, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58], + [4, 9, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59], + [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60], + [61, 66, 71, 76, 81, 86, 91, 96, 101, 106, 111, 116], + [62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117], + [63, 68, 73, 78, 83, 88, 93, 98, 103, 108, 113, 118], + [64, 69, 74, 79, 84, 89, 94, 99, 104, 109, 114, 119], + [65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120], + ] + ] + ).astype(np.float32) + image_shape = np.array([3, 4, 5]).astype(np.int64) + block_shape = np.array([1, 1, 5]).astype(np.int64) + + y = col2im(x,image_shape,block_shape) + y = np.array(y[0]) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "col2im_5D" + func_sig = "NNTrait::col2im(" + func_sig += "@input_0," + func_sig += "array![3, 4, 5].span()," + func_sig += "array![1, 1, 5].span()," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x], y, func_sig, name, Trait.NN) + + + \ No newline at end of file diff --git a/nodegen/node/conv.py b/nodegen/node/conv.py new file mode 100644 index 000000000..3b05b621d --- /dev/null +++ b/nodegen/node/conv.py @@ -0,0 +1,1079 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait + +import numpy as np + +def r_index_check(r_index, shape_out): + for i in range(len(r_index)): + if r_index[i] >= shape_out[i]: + return False + return True + +def stride(arr): + stride = np.zeros(len(arr)) + acc = 1 + for i in range(len(arr)): + stride[i] = acc + acc *= arr[-(i + 1)] + return np.flip(stride) + +def conv( + X, + W, + B=None, + auto_pad=None, + dilations=None, + group=None, + kernel_shape=None, + pads=None, + strides=None, +): + if dilations is None: + dilations = [1 for s in X.shape[2:]] + if kernel_shape is None: + kernel_shape = W.shape[2:] + if pads is None: + pads = [0 for s in X.shape[2:]] * 2 + if strides is None: + strides = [1 for s in X.shape[2:]] + + if X.shape[1] != W.shape[1] * group or W.shape[0] % group != 0: + raise ValueError( + f"Shape inconsistencies, X.shape={X.shape}, W.shape={W.shape}, group={group}, " + f"W should be {(W.shape[0], X.shape[1] // group, np.prod(W.shape[1:]) // X.shape[1] * group)}." + ) + if group > 1: + res = [] + td = 0 + mg = W.shape[0] // group + dw = W.shape[1] + + for b in range(X.shape[0]): + for g in range(group): + gx = X[b : b + 1, g * dw : (g + 1) * dw] + gw = W[g * mg : (g + 1) * mg] + try: + cv = conv( + gx, + gw, + None, + auto_pad, + dilations, + 1, + kernel_shape, + pads, + strides, + ) + except (ValueError, RuntimeError) as e: + raise ValueError( + f"Shape inconsistencies, X.shape={X.shape}, W.shape={W.shape}, group={g}/{group}, " + f"gx.shape={gx.shape}, gw.shape={gw.shape}, auto_pad={auto_pad}, " + f"dilations={dilations}, kernel_shape={kernel_shape}, pads={pads}, " + f"strides={strides}." + ) from e + if b == 0: + td += cv.shape[1] + res.append((b, cv)) + + new_shape = [X.shape[0], *list(res[0][1].shape[1:])] + + new_shape[1] = td + final = np.zeros(tuple(new_shape), dtype=res[0][1].dtype) + p = 0 + for b, cv in res: + final[b : b + 1, p : p + cv.shape[1]] = cv + p += cv.shape[1] + if p >= final.shape[1]: + p = 0 + if B is not None: + new_shape = [1 for s in final.shape] + new_shape[1] = B.shape[0] + b = B.reshape(tuple(new_shape)) + final += b + return final + + if dilations[0] != 1 or min(dilations) != max(dilations): + # Let's compute the dilated kernel. + nd = len(dilations) + new_kernel_shape = [] + new_shape = list(W.shape[:-nd]) + for i, d in enumerate(dilations): + di = len(W.shape) - nd + i + new_shape.append(W.shape[di] + (W.shape[di] - 1) * (d - 1)) + new_kernel_shape.append(kernel_shape[i] + (kernel_shape[i] - 1) * (d - 1)) + new_w = np.zeros(tuple(new_shape), dtype=W.dtype) + indices = [slice(0, new_w.shape[0]), slice(0, new_w.shape[1])] + for i, d in enumerate(dilations): + di = len(W.shape) - nd + i + indices.append(slice(0, new_w.shape[di], d)) + new_w[tuple(indices)] = W + W = new_w + kernel_shape = new_kernel_shape + + if auto_pad in {"SAME_LOWER", "SAME_UPPER", "VALID"}: + head = [] + tail = [] + for i in range(len(X.shape) - 2): + d = X.shape[i] + target_size = (d + strides[i] - 1) // strides[i] + pad_needed = (target_size - 1) * strides[i] + kernel_shape[i] - d + if auto_pad == "SAME_LOWER": + pad_head = (pad_needed + 1) // 2 + else: + pad_head = pad_needed // 2 + pad_tail = pad_needed - pad_head + head.append(pad_head) + tail.append(pad_tail) + pads = head + tail + + if len(X.shape) == 3: + sN, sC, sH = X.shape + # M, C_group, kH, kW = W.shape + (kh,) = kernel_shape + (sth,) = strides + + h_out = int(((sH - kh + pads[0] + pads[1]) / sth) + 1) + + h0 = pads[0] + oh = -1 * (kh % 2) + bh = -h0 + eh = h_out * sth + res = np.zeros((X.shape[0], W.shape[0], h_out)) # type: ignore[assignment] + if B is not None: + res[:, :, :] += B.reshape((1, -1, 1)) # type: ignore + + for n in range(0, sN): + for nw in range(W.shape[0]): + for c in range(0, sC): + w = W[nw : nw + 1, c : c + 1] + for io in range(bh, eh, sth): + hr = (io - bh) // sth + if hr >= h_out: + continue + i = io + kh % 2 + ih1, ih2 = max(0, i + oh), min(i + oh + kh, sH) + img = X[n : n + 1, c : c + 1, ih1:ih2] + if img.shape != w.shape: + jh1, jh2 = max(-oh - i, 0), min(kh, kh + sH - (i + oh + kh)) + w_ = w[:1, :1, jh1:jh2] + + if img.shape != w_.shape: + raise RuntimeError( + f"Unexpected shape {img.shape} != {w_.shape}, oh={oh}, " + f"i={i}, kh={kh}, sH={sH}, sth={sth}." + ) + s = np.dot(img.reshape((1, -1)), w_.reshape((-1, 1)))[ + 0, 0 + ] # (img * w_).sum() + else: + s = np.dot(img.reshape((1, -1)), w.reshape((-1, 1)))[ + 0, 0 + ] # (img * w).sum() + res[n, nw, hr] += s # type: ignore + + return res + + if len(X.shape) == 4: + sN, sC, sH, sW = X.shape + # M, C_group, kH, kW = W.shape + kh, kw = kernel_shape + sth, stw = strides + + h_out = int(((sH - kh + pads[0] + pads[2]) / sth) + 1) + w_out = int(((sW - kw + pads[1] + pads[3]) / stw) + 1) + + h0, w0 = pads[0], pads[1] + oh, ow = -1 * (kh % 2), -1 * (kw % 2) + bh, bw = -h0, -w0 + eh, ew = h_out * sth, w_out * stw + res = np.zeros((X.shape[0], W.shape[0], h_out, w_out)) # type: ignore[assignment] + if B is not None: + res[:, :, :, :] = B.reshape((1, -1, 1, 1)) # type: ignore + + for n in range(0, sN): + for nw in range(W.shape[0]): + for c in range(0, sC): + w = W[nw : nw + 1, c : c + 1] + for io in range(bh, eh, sth): + hr = (io - bh) // sth + if hr >= h_out: + continue + i = io + kh % 2 + ih1, ih2 = max(0, i + oh), min(i + oh + kh, sH) + for jo in range(bw, ew, stw): + wr = (jo - bw) // stw + if wr >= w_out: + continue + + j = jo + kw % 2 + iw1, iw2 = max(0, j + ow), min(j + ow + kw, sW) + img = X[n : n + 1, c : c + 1, ih1:ih2, iw1:iw2] + + if img.shape != w.shape: + jh1, jh2 = max(-oh - i, 0), min( + kh, kh + sH - (i + oh + kh) + ) + jw1, jw2 = max(-ow - j, 0), min( + kw, kw + sW - (j + ow + kw) + ) + w_ = w[:1, :1, jh1:jh2, jw1:jw2] + if img.shape != w_.shape: + raise RuntimeError( + f"Unexpected shape {img.shape} != {w_.shape}, oh={oh}, ow={ow}, " + f"i={i}, j={j}, kh={kh}, kw={kw}, sH={sH}, sW={sW}, sth={sth}, stw={stw}." + ) + s = np.dot(img.reshape((1, -1)), w_.reshape((-1, 1)))[ + 0, 0 + ] # (img * w_).sum() + else: + s = np.dot(img.reshape((1, -1)), w.reshape((-1, 1)))[ + 0, 0 + ] # (img * w).sum() + res[n, nw, hr, wr] += s # type: ignore + + return res + + if len(X.shape) == 5: + sN, sC, sH, sW, sZ = X.shape + kh, kw, kz = kernel_shape + sth, stw, stz = strides + + h_out = int(((sH - kh + pads[0] + pads[3]) / sth) + 1) + w_out = int(((sW - kw + pads[1] + pads[4]) / stw) + 1) + z_out = int(((sZ - kz + pads[2] + pads[5]) / stz) + 1) + + h0, w0, z0 = pads[0], pads[1], pads[2] + oh, ow, oz = -1 * (kh % 2), -1 * (kw % 2), -1 * (kz % 2) + bh, bw, bz = -h0, -w0, -z0 + eh, ew, ez = h_out * sth, w_out * stw, z_out * stz + res = np.zeros((X.shape[0], W.shape[0], h_out, w_out, z_out)) # type: ignore[assignment] + if B is not None: + res[:, :, :, :, :] = B.reshape((1, -1, 1, 1, 1)) # type: ignore + + for n in range(0, sN): + for nw in range(W.shape[0]): + for c in range(0, sC): + w = W[nw : nw + 1, c : c + 1] + for io in range(bh, eh, sth): + hr = (io - bh) // sth + if hr >= h_out: + continue + i = io + kh % 2 + ih1, ih2 = max(0, i + oh), min(i + oh + kh, sH) + for jo in range(bw, ew, stw): + wr = (jo - bw) // stw + if wr >= w_out: + continue + j = jo + kw % 2 + iw1, iw2 = max(0, j + ow), min(j + ow + kw, sW) + for zo in range(bz, ez, stz): + zr = (zo - bz) // stz + if zr >= z_out: + continue + z = zo + kz % 2 + iz1, iz2 = max(0, z + oz), min(z + oz + kz, sZ) + img = X[n : n + 1, c : c + 1, ih1:ih2, iw1:iw2, iz1:iz2] + + ### ICI + if img.shape != w.shape: + jh1, jh2 = max(-oh - i, 0), min( + kh, kh + sH - (i + oh + kh) + ) + jw1, jw2 = max(-ow - j, 0), min( + kw, kw + sW - (j + ow + kw) + ) + jz1, jz2 = max(-oz - z, 0), min( + kz, kz + sZ - (z + oz + kz) + ) + w_ = w[:1, :1, jh1:jh2, jw1:jw2, jz1:jz2] + if img.shape != w_.shape: + raise RuntimeError( + f"Unexpected shape {img.shape} != {w_.shape}, oh={oh}, ow={ow}, oz={oz}, " + f"i={i}, j={j}, z={z}, kh={kh}, kw={kw}, kz={kz}, " + f"sH={sH}, sW={sW}, sZ={sZ}, sth={sth}, stw={stw}, stz={stz}." + ) + + s = np.dot( + img.reshape((1, -1)), w_.reshape((-1, 1)) + )[ + 0, 0 + ] + else: + + s = np.dot( + img.reshape((1, -1)), w.reshape((-1, 1)) + )[ + 0, 0 + ] + res[n, nw, hr, wr, zr] += s # type: ignore + + return res + + else: + nd = len(X.shape[2:]) + sN, sC = X.shape[:2] + + x_stride = stride(X.shape) + w_stride = stride(W.shape) + x_flatten = X.reshape(int(x_stride[0] * X.shape[0])) + + + shape_out = [int(((X.shape[2+i] - kernel_shape[i] + pads[i] + pads[i + nd]) / strides[i]) + 1) for i in range(nd)] + o_index = [-1 * (kernel_shape[i] % 2) for i in range(nd)] + b_index = [-pads[i] for i in range(nd)] + e_index = [shape_out[i] * strides[i] for i in range(nd)] + + + range_len = [e_index[i] - b_index[i] / strides[i] for i in range(nd)] + range_stride = stride(range_len) + + res_shape = [X.shape[0], W.shape[0]] + shape_out + res = np.zeros(res_shape) + + res_strides = stride(res_shape) + if B is not None: + res[:, :, :, :, :] = B.reshape((1, -1, 1, 1, 1)) # type: ignore + + for n in range(0, sN): + for nw in range(W.shape[0]): + for c in range(0, sC): + w = W[nw : nw + 1, c : c + 1] + for i in range(int(range_len[0] * range_stride[0])): + flatten_index = i + io_index = np.zeros(nd) + r_index = np.zeros(nd) + for nx in range(nd): + n_index, rem = divmod(flatten_index, range_stride[nx]) + flatten_index = rem + io_index[nx] = n_index * strides[nx] + b_index[nx] + r_index[nx] = n_index + if r_index_check(r_index, shape_out): + indices = [io_index[nx] + (kernel_shape[nx] % 2) for nx in range(nd)] + i1_index = [max(0, indices[nx] + o_index[nx]) for nx in range(nd)] + i2_index = [min(X.shape[2 + nx], indices[nx] + o_index[nx] + kernel_shape[nx]) for nx in range(nd)] + idiff_index = [int(i2_index[nx] - i1_index[nx]) for nx in range(nd - 1)] + + i_stride = stride(idiff_index) + img = [] + for ii in range(int(i_stride[0] * idiff_index[0])): + flatten_index = ii + start = n * x_stride[0] + c * x_stride[1] + for nx in range(nd - 1): + ii_index, rem = divmod(flatten_index, i_stride[nx]) + flatten_index = rem + start += (i1_index[nx] + ii_index) * x_stride[2 + nx] + start += i1_index[nd-1] + end = start + (i2_index[nd-1] - i1_index[nd-1]) + img.append(x_flatten[int(start):int(end)]) + img_shape = [1, 1] + idiff_index + w = w.reshape(np.prod(kernel_shape)) + if len(img) != len(w): + j1_index = [max(0, -indices[nx] - o_index[nx]) for nx in range(nd)] + j2_index = [min(X.shape[2 + nx] - indices[nx] - o_index[nx], kernel_shape[nx]) for nx in range(nd)] + jdiff_index = [j2_index[nx] - j1_index[nx] for nx in range(nd - 1)] + + + w_ = [] + + j_stride = stride(jdiff_index) + + for jj in range(int(j_stride[0] * jdiff_index[0])): + flatten_index = jj + start = 0 + for nx in range(nd): + jj_index, rem = divmod(flatten_index, range_stride[nx]) + flatten_index = rem + start += (j1_index[nx] + jj_index) * kernel_shape[nx] + w_.append(w[int(start + j1_index[-1]):int(start + j1_index[-1] + j2_index[nd-1] - j1_index[nd-1])]) + + + img = np.array(img) + s = np.dot( + np.array(img).reshape((1, -1)), np.array(w_).reshape((-1, 1)) + )[ + 0, 0 + ] + else: + img = np.array(img) + s = np.dot( + np.array(img).reshape((1, -1)), np.array(w_).reshape((-1, 1)) + )[ + 0, 0 + ] + + res_index = [] + for nx in range(nd): + res_index.append(int(r_index[nx])) + + index = tuple([n, nw]) + tuple(res_index) + res[index] += s # type: ignore + return res + + + +class Conv(RunAll): + + @staticmethod + def export_conv_1D_no_padding() -> None: + x = np.array( + [ + [ + [ + 0.0, 1.0, 2.0, 3.0, 4.0 + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + 1.0, 1.0, 1.0 + ] + ] + ] + ).astype(np.float32) + + + y = conv(x, w, group = 1) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_1D_no_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_1D_with_padding() -> None: + x = np.array( + [ + [ + [ + 0.0, 1.0, 2.0, 3.0, 4.0 + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + 1.0, 1.0, 1.0 + ] + ] + ] + ).astype(np.float32) + + + y = conv(x, w, group = 1, pads=[1, 1]) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_1D_with_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![1, 1].span())," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_2D_no_padding() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + [20.0, 21.0, 22.0, 23.0, 24.0], + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + ] + ] + ] + ).astype(np.float32) + + + y = conv(x, w, group = 1, kernel_shape=[3, 3],pads=[0, 0, 0, 0],) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_2D_with_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_con_2D_with_padding() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + [20.0, 21.0, 22.0, 23.0, 24.0], + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1, kernel_shape=[3, 3],pads=[1, 1, 1, 1],) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_2D_with_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![1, 1, 1, 1].span())," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_conv_3D_no_padding() -> None: + x = np.array( + [ + [ + [ + [ + [ 0, 1, 2, 3, 4],[ 5, 6, 7, 8, 9],[ 10, 11, 12, 13, 14],[ 15, 16, 17, 18, 19],[ 20, 21, 22, 23, 24] + ], + [ + [ 25, 26, 27, 28, 29],[ 30, 31, 32, 33, 34],[ 35, 36, 37, 38, 39],[ 40, 41, 42, 43, 44],[ 45, 46, 47, 48, 49] + ], + [ + [ 50, 51, 52, 53, 54],[ 55, 56, 57, 58, 59],[ 60, 61, 62, 63, 64],[ 65, 66, 67, 68, 69],[ 70, 71, 72, 73, 74] + ], + [ + [ 75, 76, 77, 78, 79],[ 80, 81, 82, 83, 84],[ 85, 86, 87, 88, 89],[ 90, 91, 92, 93, 94],[ 95, 96, 97, 98, 99] + ], + [ + [100, 101, 102, 103, 104],[105, 106, 107, 108, 109],[110, 111, 112, 113, 114],[115, 116, 117, 118, 119],[120, 121, 122, 123, 124] + ] + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [ + [1., 1., 1.],[1., 1., 1.],[1., 1., 1.] + ], + [ + [1., 1., 1.],[1., 1., 1.],[1., 1., 1.] + ], + [ + [1., 1., 1.],[1., 1., 1.],[1., 1., 1.] + ] + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_3D_no_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_3D_with_padding() -> None: + x = np.array( + [ + [ + [ + [ + [ 0, 1, 2, 3, 4],[ 5, 6, 7, 8, 9],[ 10, 11, 12, 13, 14],[ 15, 16, 17, 18, 19],[ 20, 21, 22, 23, 24] + ], + [ + [ 25, 26, 27, 28, 29],[ 30, 31, 32, 33, 34],[ 35, 36, 37, 38, 39],[ 40, 41, 42, 43, 44],[ 45, 46, 47, 48, 49] + ], + [ + [ 50, 51, 52, 53, 54],[ 55, 56, 57, 58, 59],[ 60, 61, 62, 63, 64],[ 65, 66, 67, 68, 69],[ 70, 71, 72, 73, 74] + ], + [ + [ 75, 76, 77, 78, 79],[ 80, 81, 82, 83, 84],[ 85, 86, 87, 88, 89],[ 90, 91, 92, 93, 94],[ 95, 96, 97, 98, 99] + ], + [ + [100, 101, 102, 103, 104],[105, 106, 107, 108, 109],[110, 111, 112, 113, 114],[115, 116, 117, 118, 119],[120, 121, 122, 123, 124] + ] + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [ + [1., 1., 1.],[1., 1., 1.],[1., 1., 1.] + ], + [ + [1., 1., 1.],[1., 1., 1.],[1., 1., 1.] + ], + [ + [1., 1., 1.],[1., 1., 1.],[1., 1., 1.] + ] + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1, pads=[1, 1, 1, 1, 1, 1]) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_3D_with_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![1, 1, 1, 1, 1, 1].span())," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_4D_no_padding() -> None: + x = np.array( + [ + [ + [ + [ + [ + [ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8] + ], + [ + [ 9, 10, 11],[12, 13, 14],[15, 16, 17] + ], + [ + [18, 19, 20],[21, 22, 23],[24, 25, 26] + ] + ], + [ + [ + [27, 28, 29],[30, 31, 32],[33, 34, 35] + ], + [ + [36, 37, 38],[39, 40, 41],[42, 43, 44] + ], + [ + [45, 46, 47],[48, 49, 50],[51, 52, 53] + ] + ], + [ + [ + [54, 55, 56],[57, 58, 59],[60, 61, 62] + ], + [ + [63, 64, 65],[66, 67, 68],[69, 70, 71] + ], + [ + [72, 73, 74],[75, 76, 77],[78, 79, 80] + ] + ] + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [ + [ + [1., 1.],[1., 1.] + ], + [ + [1., 1.],[1., 1.] + ] + ], + [ + [ + [1., 1.],[1., 1.] + ], + [ + [1., 1.],[1., 1.] + ] + ] + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_4D_no_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_4D_with_padding() -> None: + x = np.array( + [ + [ + [ + [ + [ + [ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8] + ], + [ + [ 9, 10, 11],[12, 13, 14],[15, 16, 17] + ], + [ + [18, 19, 20],[21, 22, 23],[24, 25, 26] + ] + ], + [ + [ + [27, 28, 29],[30, 31, 32],[33, 34, 35] + ], + [ + [36, 37, 38],[39, 40, 41],[42, 43, 44] + ], + [ + [45, 46, 47],[48, 49, 50],[51, 52, 53] + ] + ], + [ + [ + [54, 55, 56],[57, 58, 59],[60, 61, 62] + ], + [ + [63, 64, 65],[66, 67, 68],[69, 70, 71] + ], + [ + [72, 73, 74],[75, 76, 77],[78, 79, 80] + ] + ] + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [ + [ + [1., 1.],[1., 1.] + ], + [ + [1., 1.],[1., 1.] + ] + ], + [ + [ + [1., 1.],[1., 1.] + ], + [ + [1., 1.],[1., 1.] + ] + ] + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1, pads=[1, 1, 1, 1, 1, 1, 1, 1]) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) # + + name = "conv_4D_with_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![1, 1, 1, 1, 1, 1, 1, 1].span())," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + + @staticmethod + def export_conv_with_autopad_same() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + [20.0, 21.0, 22.0, 23.0, 24.0], + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1, kernel_shape=[3, 3],auto_pad="SAME_LOWER",strides = [2, 2]) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_2D_with_autopad_same" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::Some(AUTO_PAD::SAME_LOWER)," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![3, 3].span())," + func_sig += "Option::None," + func_sig += "Option::Some(array![2, 2].span()))" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_with_strides_asymmetric_padding() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + [20.0, 21.0, 22.0, 23.0, 24.0], + [25.0, 26.0, 27.0, 28.0, 29.0], + [30.0, 31.0, 32.0, 33.0, 34.0], + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1, kernel_shape=[3, 3],pads=[1, 0, 1, 0],strides = [2, 2]) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_2D_with_strides_asymmetric_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![3, 3].span())," + func_sig += "Option::Some(array![1, 0, 1, 0].span())," + func_sig += "Option::Some(array![2, 2].span()))" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_with_strides_with_padding() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + [20.0, 21.0, 22.0, 23.0, 24.0], + [25.0, 26.0, 27.0, 28.0, 29.0], + [30.0, 31.0, 32.0, 33.0, 34.0], + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [ + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + ] + ] + ] + ).astype(np.float32) + + y = conv(x, w, group = 1, kernel_shape=[3, 3],pads=[1, 1, 1, 1],strides = [2, 2]) + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_2D_with_strides_with_padding" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![3, 3].span())," + func_sig += "Option::Some(array![1, 1, 1, 1].span())," + func_sig += "Option::Some(array![2, 2].span()))" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_conv_with_2_groups() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]], + [ + [9.0, 10.0, 11.0], [12.0, 13.0, 14.0], [15.0, 16.0, 17.0]] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ], + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ] + ] + ).astype(np.float32) + y = conv(x, w, group = 2) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_2D_with_2_groups" + func_sig = "NNTrait::conv(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(2)," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + \ No newline at end of file diff --git a/nodegen/node/conv_transpose.py b/nodegen/node/conv_transpose.py new file mode 100644 index 000000000..2b9259397 --- /dev/null +++ b/nodegen/node/conv_transpose.py @@ -0,0 +1,629 @@ + + +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait + + +def conv_transpose( + X, + W, + B=None, + auto_pad=None, + dilations=None, + group=None, + kernel_shape=None, + output_padding=None, + output_shape=None, + pads=None, + strides=None, +): + if dilations is None: + dilations = [1 for s in X.shape[2:]] + if kernel_shape is None: + kernel_shape = W.shape[2:] + if output_padding is None: + output_padding = [0 for s in X.shape[2:]] * 2 + if strides is None: + strides = [1 for s in X.shape[2:]] + if pads is None and auto_pad not in {"SAME_UPPER", "SAME_LOWER"}: + pads = [0 for i in range(2 * len(strides))] + if pads is None: + if output_shape is None: + output_shape = [ + X.shape[i + 2] * strides[i] for i in range(len(strides)) + ] + total_padding = [ + strides[i] * (X.shape[i + 2] - 1) + + output_padding[i] + + ((kernel_shape[i] - 1) * dilations[i] + 1) + - output_shape[i] + for i in range(len(output_shape)) + ] + pads_1 = [] + pads_2 = [] + for i in range(len(output_shape)): + if auto_pad == "SAME_UPPER": + pads_1.append(total_padding[i] // 2) + pads_2.append(total_padding[i] - (total_padding[i] // 2)) + else: + pads_1.append(total_padding[i] - (total_padding[i] // 2)) + pads_2.append(total_padding[i] // 2) + pads = pads_1 + pads_2 + n_dims = len(pads) // 2 + else: + n_dims = len(X.shape) - 2 + new_pads = np.array([(pads[i], pads[i + n_dims]) for i in range(n_dims)]) + if output_shape is None: + output_shape = [ + strides[i] * (X.shape[i + 2] - 1) + + output_padding[i] + + ((kernel_shape[i] - 1) * dilations[i] + 1) + - new_pads[i, :].sum() + for i in range(n_dims) + ] + kernel_shape = W.shape[2:] + kernel_size = np.prod(kernel_shape) + num_output_channels = W.shape[1] * group + kernel_dim = num_output_channels // group * kernel_size + C = X.shape[1] # num_inputs_channels + m = kernel_dim # kernel_dim + n = np.prod(X.shape[2:]) # input_image_size + k = C // group + w_reshaped = W.reshape((group, k, m)) + final = None + + # N x C x H x W = X.shape + # C x M/group x k1 x k2 = W.shape + if group == 1: + for image_id in range(X.shape[0]): + w_t = w_reshaped[0].T + gemm = np.matmul(w_t, X[image_id].reshape((k, n))) + gemmc = gemm.reshape((num_output_channels, -1, gemm.shape[-1])) + for c in range(num_output_channels): + res = col2im_naive_implementation( + gemmc[c], output_shape, kernel_shape, dilations, pads, strides + ) + if final is None: + final = np.empty( + X.shape[:1] + (num_output_channels,) + res.shape, + dtype=X.dtype, + ) + if B is not None: + res += B[c] + final[image_id, c, ...] = res[...] + else: + final = np.zeros((X.shape[0], num_output_channels ) + tuple(output_shape)) + + output_array = [] + + for group_id in range(group): + group_X = X[:, group_id * C // group : (group_id + 1) * C // group, ...] + group_W = W[group_id * num_output_channels // group : (group_id + 1) * num_output_channels // group, ...] + + group_output = conv_transpose( + group_X, + group_W, + B=B, + auto_pad=auto_pad, + dilations=dilations, + group=1, + kernel_shape=kernel_shape, + output_padding=output_padding, + output_shape=output_shape, + pads=pads, + strides=strides, + ) + group_output = np.array(group_output[0]) + + output_array.append(group_output) + + + for image_id in range(X.shape[0]): + for group_id in range(group): + group_output = output_array[group_id] + final[image_id, group_id:(group_id+1), ...] = group_output[image_id, ...] + + + return (final.astype(X.dtype),) + + + +def _get_indices(i, shape): + res = np.empty((len(shape),), dtype=np.int64) + k = len(shape) - 1 + while k > 0: + m = i % shape[k] + res[k] = m + i -= m + i /= shape[k] + k -= 1 + res[0] = i + return res + +def _col2im_shape_check(X, output_shape, kernel_shape, dilations, pads, strides): # type: ignore + n_input_plane = X.shape[0] + + kernel_size = np.prod(kernel_shape) + + if n_input_plane % kernel_size != 0: + raise ValueError( + f"Expected size of input's dimension 1 to be divisible by the " + f"product of kernel_size={kernel_size}, " + f"but got input.size(1)={n_input_plane} " + f"and kernel_shape={kernel_shape}, X.shape={X.shape}, output_shape={output_shape}." + ) + + input_length = X.shape[1] + n_dims = len(output_shape) + n_blocks = [] + + + for i in range(n_dims): + n_block = ( + output_shape[i] + + pads[i, :].sum() + - dilations[i] * (kernel_shape[i] - 1) + - 1 + ) // strides[i] + 1 + n_blocks.append(n_block) + + block_size = np.prod(n_blocks) + if input_length != block_size: + raise ValueError( + f"Given n_input_plane={n_input_plane}, X.shape={X.shape}, " + f"output_shape={output_shape}, kernel_shape={kernel_shape}, " + f"dilations={dilations}, pads={pads}, strides={strides}, " + f"expected size of input's dimension 2 to match the calculated number of " + f"sliding blocks {n_blocks} = {block_size}, " + f"but got input.size(2)={input_length}.", + ) + + +def col2im_naive_implementation(data, image_shape, kernel_shape, dilations, pads, strides): # type: ignore + + n_dims = len(pads) // 2 + new_pads = np.array([(pads[i], pads[i + n_dims]) for i in range(n_dims)]) + _col2im_shape_check(data, image_shape, kernel_shape, dilations, new_pads, strides) + + data_col = data + data_im = np.zeros(image_shape, dtype=data.dtype) + + dim_col = [] + for i in range(n_dims): + col = ( + image_shape[i] + + new_pads[i, :].sum() + - (dilations[i] * (kernel_shape[i] - 1) + 1) + ) // strides[i] + 1 + dim_col.append(col) + kernel_size = np.prod(kernel_shape) + col_size = np.prod(dim_col) + for c_col in range(kernel_size): + offset = _get_indices(c_col, kernel_shape) + + for col in range(col_size): + + ind_col = _get_indices(col, dim_col) + ind_im = [] + for i in range(n_dims): + ind = ( + ind_col[i] * strides[i] - new_pads[i, 0] + offset[i] * dilations[i] + ) + ind_im.append(ind) + if not _is_out(ind_im, data_im.shape): + data_im[tuple(ind_im)] += data_col[c_col, col] + + + return data_im + + +def _is_out(ind, shape): + for i, s in zip(ind, shape): + if i < 0: + return True + if i >= s: + return True + return False + + +class Conv_transpose(RunAll): + + @staticmethod + def export_conv_transpose() -> None: + x = np.array( + [[[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]]] # (1, 1, 3, 3) + ).astype(np.float32) + + w = np.array( + [ + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], # (1, 2, 3, 3) + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ] + ] + ).astype(np.float32) + + y = conv_transpose(x, w, group=1)[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_transpose" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_convtranspose_1d() -> None: + x = np.array([[[0.0, 1.0, 2.0]]]).astype(np.float32) # (1, 1, 3) + + w = np.array([[[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]]).astype( # (1, 2, 3) + np.float32 + ) + + y = conv_transpose(x, w, group=1)[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_transpose_1d" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_convtranspose_3d() -> None: + x = np.array( + [ + [ + [ + [ + [0.0, 1.0, 2.0, 3.0, 4.0], # (1, 1, 3, 4, 5) + [5.0, 6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0, 14.0], + [15.0, 16.0, 17.0, 18.0, 19.0], + ], + [ + [20.0, 21.0, 22.0, 23.0, 24.0], + [25.0, 26.0, 27.0, 28.0, 29.0], + [30.0, 31.0, 32.0, 33.0, 34.0], + [35.0, 36.0, 37.0, 38.0, 39.0], + ], + [ + [40.0, 41.0, 42.0, 43.0, 44.0], + [45.0, 46.0, 47.0, 48.0, 49.0], + [50.0, 51.0, 52.0, 53.0, 54.0], + [55.0, 56.0, 57.0, 58.0, 59.0], + ], + ] + ] + ] + ).astype(np.float32) + + w = np.array( + [ + [ + [ + [ + [1.0, 1.0, 1.0], # (1, 2, 3, 3, 3) + [1.0, 1.0, 1.0], + [1.0, 1.0, 1.0], + ], + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ], + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ], + ] + ] + ).astype(np.float32) + + y = conv_transpose(x, w, group=1)[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "conv_transpose_3d" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + + @staticmethod + def export_convtranspose_attributes() -> None: + x = np.array( + [[[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]]] # (1, 1, 3, 3) + ).astype(np.float32) + + w = np.array( + [ + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], # (1, 2, 3, 3) + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ] + ] + ).astype(np.float32) + + y = conv_transpose(x, w, group=1)[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "conv_transpose_attributes" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + + + @staticmethod + def export_convtranspose_pads() -> None: + x = np.array( + [[[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]]] # (1, 1, 3, 3) + ).astype(np.float32) + + w = np.array( + [ + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], # (1, 2, 3, 3) + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ] + ] + ).astype(np.float32) + + y = conv_transpose(x, w, group=1,strides=[3, 2],output_shape=[10, 8], kernel_shape=[3, 3], output_padding=[1, 1],)[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "conv_transpose_pads" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![3, 3].span())," + func_sig += "Option::Some(array![1, 1].span())," + func_sig += "Option::Some(array![10, 8].span())," + func_sig += "Option::None," + func_sig += "Option::Some(array![3, 2].span()))" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_convtranspose_dilations() -> None: + x = np.array( + [[[[3.0, 8.0, 1.0], [9.0, 5.0, 7.0], [3.0, 2.0, 6.0]]]] # (1, 1, 3, 3) + ).astype(np.float32) + w = np.array([[[[7.0, 2.0], [1.0, 9.0]]]]).astype(np.float32) # (1, 1, 2, 2) + + y = conv_transpose(x, w, group=1, dilations=[2, 2])[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_transpose_dilations" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![2, 2].span())," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None,)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_convtranspose_autopad_same() -> None: + x = np.array( + [[[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]]] # (1, 1, 3, 3) + ).astype(np.float32) + + w = np.array( + [ + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], # (1, 2, 3, 3) + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ] + ] + ).astype(np.float32) + + y = conv_transpose(x, w, group=1, auto_pad="SAME_UPPER", strides=[2, 2])[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_transpose_autopad_same" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::Some(AUTO_PAD::SAME_UPPER)," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(array![2, 2].span()))" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_convtranspose_group_2() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]], + [ + [9.0, 10.0, 11.0], [12.0, 13.0, 14.0], [15.0, 16.0, 17.0]] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ], + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ] + ] + ).astype(np.float32) + + y = conv_transpose(x, w, group=2)[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_transpose_group_2" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(2)," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None,)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + + @staticmethod + def export_convtranspose_group_2_image_3() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]], + [ + [9.0, 10.0, 11.0], [12.0, 13.0, 14.0], [15.0, 16.0, 17.0] + ] + ], + [ + [ + [18.0, 19.0, 20.0], [21.0, 22.0, 23.0], [24.0, 25.0, 26.0] + ], + [ + [9.0, 10.0, 11.0], [12.0, 13.0, 14.0], [15.0, 16.0, 17.0] + ] + ], + [ + [ + [0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0] + ], + [ + [9.0, 10.0, 11.0], [12.0, 13.0, 14.0], [15.0, 16.0, 17.0] + ] + ] + ] + ).astype(np.float32) + w = np.array( + [ + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ], + [ + [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], + ] + ] + ).astype(np.float32) + + y = conv_transpose(x, w, group=2)[0] + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + w = Tensor(Dtype.FP16x16, w.shape, to_fp(w.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + name = "conv_transpose_group_2_image_3" + func_sig = "NNTrait::conv_transpose(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(2)," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None,)" + make_test( + [x, w], y, func_sig, name, Trait.NN) + diff --git a/nodegen/node/depth_to_space.py b/nodegen/node/depth_to_space.py new file mode 100644 index 000000000..c07af64a4 --- /dev/null +++ b/nodegen/node/depth_to_space.py @@ -0,0 +1,120 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait + +def depth_to_space(data: np.ndarray, blocksize: int = 2, mode = "DCR") -> np.ndarray: + if len(data.shape) != 4: + raise RuntimeError(f"Unexpected shape {data.shape!r}.") + b, c, h, w = data.shape + if mode == "DCR": + tmpshape = ( + b, + blocksize, + blocksize, + c // (blocksize * blocksize), + h, + w, + ) + reshaped = data.reshape(tmpshape) + transposed = np.transpose(reshaped, [0, 3, 4, 1, 5, 2]) + else: + # assert mode == "CRD" + tmpshape = ( + b, + c // (blocksize * blocksize), + blocksize, + blocksize, + h, + w, + ) + reshaped = data.reshape(tmpshape) + transposed = np.transpose(reshaped, [0, 1, 4, 2, 5, 3]) + finalshape = ( + b, + c // (blocksize * blocksize), + h * blocksize, + w * blocksize, + ) + y = np.reshape(transposed, finalshape) + return y + +class Depth_to_space(RunAll): + + @staticmethod + def fp8x23(): + x = np.random.uniform(-3, 3, (1, 4, 2, 2)).astype(np.float64) + y = depth_to_space(x) + + x = Tensor(Dtype.FP8x23, x.shape, to_fp( + x.flatten(), FixedImpl.FP8x23)) + y = Tensor(Dtype.FP8x23, y.shape, to_fp( + y.flatten(), FixedImpl.FP8x23)) + + name = "depth_to_space_fp8x23" + make_test([x], y, "NNTrait::depth_to_space(@input_0, 2, 'DCR')", + name, Trait.NN) + + @staticmethod + def fp16x16(): + x = np.random.uniform(-3, 3, (1, 4, 2, 2)).astype(np.float16) + y = depth_to_space(x) + + x = Tensor(Dtype.FP16x16, x.shape, to_fp( + x.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp( + y.flatten(), FixedImpl.FP16x16)) + + name = "depth_to_space_fp16x16" + make_test([x], y, "NNTrait::depth_to_space(@input_0, 2, 'DCR')", + name, Trait.NN) + + # @staticmethod + # def fp64x64(): + # x = np.random.uniform(-3, 3, (1, 4, 2, 2)).astype(np.float64) + # y = depth_to_space(x) + + # x = Tensor(Dtype.FP64x64, x.shape, to_fp( + # x.flatten(), FixedImpl.FP64x64)) + # y = Tensor(Dtype.FP64x64, y.shape, to_fp( + # y.flatten(), FixedImpl.FP64x64)) + + # name = "depth_to_space_fp64x64" + # make_test([x], y, "NNTrait::depth_to_space(@input_0, 2, 'DCR')", + # name, Trait.NN) + + @staticmethod + def fpi8(): + x = np.random.randint(-3, 3, (1, 4, 2, 2)).astype(np.int8) + y = depth_to_space(x) + + x = Tensor(Dtype.I8, x.shape, x.flatten()) + y = Tensor(Dtype.I8, y.shape, y.flatten()) + + name = "depth_to_space_i8" + make_test([x], y, "NNTrait::depth_to_space(@input_0, 2, 'DCR')", + name, Trait.NN) + + @staticmethod + def fpi32(): + x = np.random.randint(-3, 3, (1, 4, 2, 2)).astype(np.int32) + y = depth_to_space(x) + + x = Tensor(Dtype.I32, x.shape, x.flatten()) + y = Tensor(Dtype.I32, y.shape, y.flatten()) + + name = "depth_to_space_i32" + make_test([x], y, "NNTrait::depth_to_space(@input_0, 2, 'CRD')", + name, Trait.NN) + + + @staticmethod + def fpu32(): + x = np.random.randint(-3, 3, (1, 4, 2, 2)).astype(np.uint32) + y = depth_to_space(x) + + x = Tensor(Dtype.U32, x.shape, x.flatten()) + y = Tensor(Dtype.U32, y.shape, y.flatten()) + + name = "depth_to_space_u32" + make_test([x], y, "NNTrait::depth_to_space(@input_0, 2, 'CRD')", + name, Trait.NN) diff --git a/nodegen/node/grid_sample.py b/nodegen/node/grid_sample.py new file mode 100644 index 000000000..a0d1af78e --- /dev/null +++ b/nodegen/node/grid_sample.py @@ -0,0 +1,700 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait +from .resize import _get_all_coords +import numbers +from typing import List + +import numpy as np + +#from onnx.reference.ops.op_resize import _get_all_coords + +def grid_sample(X, grid, mode='linear', padding_mode='zeros', align_corners=0): + x_dims = X.shape + grid_dims = grid.shape + N = x_dims[0] + C = x_dims[1] + y_dims = (N, C, *grid_dims[1:-1]) + if np.prod(y_dims) == 0: + return np.array([], dtype=X.dtype) + Y = np.empty(y_dims, dtype=X.dtype) + for n in range(N): + grid_data = grid[n] + for c in range(C): + X_data = X[n, c] + num_dims = len(x_dims[2:]) + dims = x_dims[2:] + border = _prepare_border(dims, align_corners=align_corners) + for ox in _get_all_coords(Y[n, c]): + nx = grid_data[tuple(ox)] + nx = nx[::-1] + x = _gs_denormalize_coordinates( + n=nx, dims=dims, align_corners=align_corners + ) + if mode == "nearest": + x = np.rint(x) + for i, v in enumerate(x): + x_min = border[i] + x_max = border[i + num_dims] + if v < x_min or v > x_max: + if padding_mode == "border": + x[i] = _clamp(v, 0, dims[i] - 1) + elif padding_mode == "reflection": + x[i] = _gs_reflect(v, x_min, x_max) + if mode == "nearest": + x = x.astype(np.int32) + Y[n][c][tuple(ox)] = _pixel_at_ndarray( + ndarray=X_data, + x=x, + border=border, + padding_mode=padding_mode, + ) + + elif mode == "linear": + Y[n][c][tuple(ox)] = _gs_linear_interpolation_nd_with_x( + data=X_data, x=x, border=border, padding_mode=padding_mode + ) + elif mode == "cubic": + Y[n][c][tuple(ox)] = _gs_cubic_interpolation_nd_with_x( + data=X_data, x=x, border=border, padding_mode=padding_mode + ) + else: + raise RuntimeError( + "GridSample interpolation only supports nearest, linear, and cubic modes." + ) + return (Y.astype(X.dtype),) + + +def _gs_denormalize(n, length: int, align_corners: bool): + if align_corners: + x = (n + 1) / 2.0 * (length - 1) + else: + x = ((n + 1) * length - 1) / 2.0 + return x + +def _gs_denormalize_coordinates(n, dims, align_corners: bool): + x = np.zeros(len(n), dtype=np.float32) + for i, (v, dim) in enumerate(zip(n, dims)): + x[i] = _gs_denormalize(n=v, length=dim, align_corners=align_corners) + return x + +def _gs_reflect(x, x_min, x_max): # type: ignore + """Reflect by the near border till within the borders + Use float for borders to avoid potential issues with integer T + """ + fx = x + rng = x_max - x_min + if fx < x_min: + dx = x_min - fx + n = int(dx / rng) + r = dx - n * rng + if n % 2 == 0: + fx = x_min + r + else: + fx = x_max - r + elif fx > x_max: + dx = fx - x_max + n = int(dx / rng) + r = dx - n * rng + if n % 2 == 0: + fx = x_max - r + else: + fx = x_min + r + return fx + +def _gs_get_cubic_coeffs(x, coeffs): # type: ignore + """Calculate cubic convolution interpolation coefficients + ROBERT G. KEYS https://ieeexplore.ieee.org/document/1163711 + Use float to avoid potential issues with integer. + """ + cubic_alpha = -0.75 + x = abs(x) + coeffs[0] = ( + (cubic_alpha * (x + 1) - 5 * cubic_alpha) * (x + 1) + 8 * cubic_alpha + ) * (x + 1) - 4 * cubic_alpha + coeffs[1] = ((cubic_alpha + 2) * x - (cubic_alpha + 3)) * x * x + 1 + coeffs[2] = ((cubic_alpha + 2) * (1 - x) - (cubic_alpha + 3)) * (1 - x) * ( + 1 - x + ) + 1 + coeffs[3] = ( + (cubic_alpha * (2 - x) - 5 * cubic_alpha) * (2 - x) + 8 * cubic_alpha + ) * (2 - x) - 4 * cubic_alpha + +def _gs_get_linear_coeffs(x, coeffs): + x = abs(x) + coeffs[0] = 1 - x + coeffs[1] = x + +def _gs_bicubic_interpolate(p, x, y): # type: ignore + v = np.empty((4,), dtype=p.dtype) + coeffs = np.empty((4,), dtype=p.dtype) + _gs_get_cubic_coeffs(x, coeffs) + for i in range(4): + v[i] = coeffs @ p[i, :] + _gs_get_cubic_coeffs(y, coeffs) + return coeffs @ v + +def _gs_cubic_interpolation_1d_with_x(data, x, border, padding_mode): + v = np.empty((4,), dtype=data.dtype) + coeffs = np.empty((4,), dtype=data.dtype) + x_0 = int(np.floor(x)) + x_1 = x_0 + 1 + x_2 = x_0 + 2 + x_minus_1 = x_0 - 1 + _gs_get_cubic_coeffs(x - x_0, coeffs) + v[0] = _pixel_at_array( + array=data, i=x_minus_1, border=border, padding_mode=padding_mode + ) + v[1] = _pixel_at_array( + array=data, i=x_0, border=border, padding_mode=padding_mode + ) + v[2] = _pixel_at_array( + array=data, i=x_1, border=border, padding_mode=padding_mode + ) + v[3] = _pixel_at_array( + array=data, i=x_2, border=border, padding_mode=padding_mode + ) + return coeffs @ v + +def _gs_linear_interpolation_1d_with_x(data, x, border, padding_mode): + v = np.empty((2,), dtype=data.dtype) + coeffs = np.empty((2,), dtype=data.dtype) + x_0 = int(np.floor(x)) + x_1 = x_0 + 1 + _gs_get_linear_coeffs(x - x_0, coeffs) + v[0] = _pixel_at_array( + array=data, i=x_0, border=border, padding_mode=padding_mode + ) + v[1] = _pixel_at_array( + array=data, i=x_1, border=border, padding_mode=padding_mode + ) + return coeffs @ v + +def _gs_linear_interpolation_nd_with_x(data, x, border, padding_mode): + num_dims = data.ndim + assert num_dims == len(x) == int(len(border) / 2) + if num_dims == 1: + return _gs_linear_interpolation_1d_with_x( + data=data, x=x[0], border=border, padding_mode=padding_mode + ) + res1d = [] + for i in range(data.shape[0]): + r = _gs_linear_interpolation_nd_with_x( + data=data[i], + x=x[1:], + border=list(border[1:num_dims]) + + list(border[1 + num_dims : 2 * num_dims]), + padding_mode=padding_mode, + ) + res1d.append(r) + res1d = np.array(res1d) + return _gs_linear_interpolation_1d_with_x( + data=res1d, + x=x[0], + border=[border[0], border[num_dims]], + padding_mode=padding_mode, + ) + +def _gs_cubic_interpolation_nd_with_x(data, x, border, padding_mode): + num_dims = data.ndim + assert num_dims == len(x) == int(len(border) / 2) + if num_dims == 1: + return _gs_cubic_interpolation_1d_with_x( + data=data, x=x[0], border=border, padding_mode=padding_mode + ) + res1d = [] + for i in range(data.shape[0]): + r = _gs_cubic_interpolation_nd_with_x( + data=data[i], + x=x[1:], + border=list(border[1:num_dims]) + + list(border[1 + num_dims : 2 * num_dims]), + padding_mode=padding_mode, + ) + res1d.append(r) + res1d = np.array(res1d) + return _gs_cubic_interpolation_1d_with_x( + data=res1d, + x=x[0], + border=[border[0], border[num_dims]], + padding_mode=padding_mode, + ) + +def _clamp(val, lo, hi): # type: ignore + if val < lo: + return lo + if val > hi: + return hi + return val + +def _pixel_at_ndarray(ndarray, x: List, border, padding_mode): # type: ignore + # boarder: [x_1_min, x_2_min, ..., x_1_max, x_2_max, ...] + num_dims = ndarray.ndim + assert num_dims == len(x) == int(len(border) / 2) + if num_dims == 1: + return _pixel_at_array( + array=ndarray, i=x[0], border=border, padding_mode=padding_mode + ) + i = x[0] + d = ndarray.shape[0] + if padding_mode == "zeros": + if i >= 0 and i < d: + ndarray = ndarray[i] + else: + # Trick + i = 0 + ndarray = np.zeros_like(ndarray[i]) + elif padding_mode == "border": + i = _clamp(i, 0, d - 1) + ndarray = ndarray[i] + else: + i = int(_gs_reflect(i, border[0], border[num_dims])) + ndarray = ndarray[i] + return _pixel_at_ndarray( + ndarray=ndarray, + x=x[1:], + border=list(border[1:num_dims]) + list(border[1 + num_dims : 2 * num_dims]), + padding_mode=padding_mode, + ) +def _pixel_at_array(array, i: int, border, padding_mode): # type: ignore + assert array.ndim == 1 + d = array.shape[0] + if padding_mode == "zeros": + if i >= 0 and i < d: + pixel = array[i] + else: + pixel = 0 + elif padding_mode == "border": + i = _clamp(i, 0, d - 1) + pixel = array[i] + else: + i = int(_gs_reflect(i, border[0], border[1])) + pixel = array[i] + return pixel + +def _prepare_border(dims, align_corners: bool): + # boarder: [x_1_min, x_2_min, ..., x_1_max, x_2_max, ...] + num_dims = len(dims) + borders = np.zeros(num_dims * 2) + for i in range(num_dims): + # min + borders[i] = -0.5 + # max + borders[i + num_dims] = dims[i] - 0.5 + if align_corners: + # min + borders[i] = 0.0 + # max + borders[i + num_dims] = dims[i] - 1.0 + return borders + + +class Grid_sample(RunAll): + + @staticmethod + def export_gridsample() -> None: + x = np.array( + [ + [ + [ + [0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0], + [12.0, 13.0, 14.0, 15.0], + ] + ] + ], + dtype=np.float32, + ) + + grid = np.array( + [ + [ + [ + [-1.0000, -1.0000], + [-0.6000, -1.0000], + [-0.2000, -1.0000], + [0.2000, -1.0000], + [0.6000, -1.0000], + [1.0000, -1.0000], + ], + [ + [-1.0000, -0.6000], + [-0.6000, -0.6000], + [-0.2000, -0.6000], + [0.2000, -0.6000], + [0.6000, -0.6000], + [1.0000, -0.6000], + ], + [ + [-1.0000, -0.2000], + [-0.6000, -0.2000], + [-0.2000, -0.2000], + [0.2000, -0.2000], + [0.6000, -0.2000], + [1.0000, -0.2000], + ], + [ + [-1.0000, 0.2000], + [-0.6000, 0.2000], + [-0.2000, 0.2000], + [0.2000, 0.2000], + [0.6000, 0.2000], + [1.0000, 0.2000], + ], + [ + [-1.0000, 0.6000], + [-0.6000, 0.6000], + [-0.2000, 0.6000], + [0.2000, 0.6000], + [0.6000, 0.6000], + [1.0000, 0.6000], + ], + [ + [-1.0000, 1.0000], + [-0.6000, 1.0000], + [-0.2000, 1.0000], + [0.2000, 1.0000], + [0.6000, 1.0000], + [1.0000, 1.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="linear") + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, grid], y, func_sig, name, Trait.NN) + + @staticmethod + def export_gridsample_paddingmode_zeros() -> None: + x = np.array( + [[[[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]]], + dtype=np.float32, + ) + grid = np.array( + [ + [ + [ + [-10.0000, -10.0000], + [-5.0000, -5.0000], + [-0.2000, -0.2000], + [10.0000, 10.0000], + ], + [ + [10.0000, 10.0000], + [-0.2000, -0.2000], + [5.0000, 5.0000], + [10.0000, 10.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="linear") + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample_padding_zeros" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, grid], y, func_sig, name, Trait.NN) + + @staticmethod + def export_gridsample_paddingmode_border() -> None: + x = np.array( + [[[[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]]], + dtype=np.float32, + ) + grid = np.array( + [ + [ + [ + [-10.0000, -10.0000], + [-5.0000, -5.0000], + [-0.2000, -0.2000], + [10.0000, 10.0000], + ], + [ + [10.0000, 10.0000], + [-0.2000, -0.2000], + [5.0000, 5.0000], + [10.0000, 10.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="linear", padding_mode="border") + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample_padding_border" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(PADDING_MODE::BORDER))" + make_test( + [x, grid], y, func_sig, name, Trait.NN) + + @staticmethod + def export_gridsample_paddingmode_reflection() -> None: + x = np.array( + [[[[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]]], + dtype=np.float32, + ) + grid = np.array( + [ + [ + [ + [-10.0000, -10.0000], + [-5.0000, -5.0000], + [-0.2000, -0.2000], + [10.0000, 10.0000], + ], + [ + [10.0000, 10.0000], + [-0.2000, -0.2000], + [5.0000, 5.0000], + [10.0000, 10.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="linear", padding_mode="reflection") + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample_padding_reflection" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::None," + func_sig += "Option::None," + func_sig += "Option::Some(PADDING_MODE::REFLECTION))" + make_test( + [x, grid], y, func_sig, name, Trait.NN) + + @staticmethod + def export_gridsample_mode_aligncorners() -> None: + x = np.array( + [[[[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]]], + dtype=np.float32, + ) + grid = np.array( + [ + [ + [ + [-1.0000, -1.0000], + [-0.5000, -0.5000], + [-0.2000, -0.2000], + [0.0000, 0.0000], + ], + [ + [0.0000, 0.0000], + [-0.2000, -0.2000], + [0.5000, 0.5000], + [1.0000, 1.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="linear", align_corners=1) + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample_aligncorners" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::Some(1)," + func_sig += "Option::None," + func_sig += "Option::None)" + make_test( + [x, grid], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_gridsample_nearest() -> None: + x = np.array( + [[[[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]]], + dtype=np.float32, + ) + grid = np.array( + [ + [ + [ + [-1.0000, -1.0000], + [-0.5000, -0.5000], + [-0.2000, -0.2000], + [0.0000, 0.0000], + ], + [ + [0.0000, 0.0000], + [-0.2000, -0.2000], + [0.5000, 0.5000], + [1.0000, 1.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="nearest", align_corners=0) + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample_nearest" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::Some(0)," + func_sig += "Option::Some(MODE::NEAREST)," + func_sig += "Option::None)" + make_test( + [x, grid], y, func_sig, name, Trait.NN) + + + @staticmethod + def export_gridsample_nearest_align_corner() -> None: + x = np.array( + [[[[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]]], + dtype=np.float32, + ) + grid = np.array( + [ + [ + [ + [-1.0000, -1.0000], + [-0.5000, -0.5000], + [-0.2000, -0.2000], + [0.0000, 0.0000], + ], + [ + [0.0000, 0.0000], + [-0.2000, -0.2000], + [0.5000, 0.5000], + [1.0000, 1.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="nearest", align_corners=1) + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample_nearest_aligncorner" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::Some(1)," + func_sig += "Option::Some(MODE::NEAREST)," + func_sig += "Option::None)" + make_test( + [x, grid], y, func_sig, name, Trait.NN) + + @staticmethod + def export_gridsample_cubic() -> None: + x = np.array( + [[[[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]]]], + dtype=np.float32, + ) + grid = np.array( + [ + [ + [ + [-1.0000, -1.0000], + [-0.5000, -0.5000], + [-0.2000, -0.2000], + [0.0000, 0.0000], + ], + [ + [0.0000, 0.0000], + [-0.2000, -0.2000], + [0.5000, 0.5000], + [1.0000, 1.0000], + ], + ] + ], + dtype=np.float32, + ) + + y = grid_sample(x, grid, mode ="cubic", align_corners=0) + y = np.array(y[0]) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp(x.flatten(), FixedImpl.FP16x16)) + grid = Tensor(Dtype.FP16x16, grid.shape, to_fp(grid.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + + name = "grid_sample_cubic" + func_sig = "NNTrait::grid_sample(" + func_sig += "@input_0," + func_sig += "@input_1," + func_sig += "Option::Some(0)," + func_sig += "Option::Some(MODE::CUBIC)," + func_sig += "Option::None)" + make_test( + [x, grid], y, func_sig, name, Trait.NN) diff --git a/nodegen/node/hamming_window.py b/nodegen/node/hamming_window.py new file mode 100644 index 000000000..295db4fad --- /dev/null +++ b/nodegen/node/hamming_window.py @@ -0,0 +1,128 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait, get_data_statement + +def hamming_window(size, output_datatype=None, periodic=None) -> np.ndarray: # type: ignore + if periodic == 1: + N_1 = size + else: + N_1 = size - 1 + ni = np.arange(size, dtype=output_datatype) + alpha = 25.0 / 46.0 + beta = 1 - alpha + res = alpha - np.cos(ni * np.float64(np.pi).astype(output_datatype) * 2 / N_1).astype(output_datatype) * beta + return res.astype(output_datatype) + +class Hamming_window(RunAll): + + @staticmethod + # We test here with fp8x23 implementation. + def fp8x23(): + args = [4] + # x = np.float64(4) + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP8x23), Dtype.FP8x23) + y = hamming_window(*args, np.float64) + + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP8x23, y.shape, to_fp(y.flatten(), FixedImpl.FP8x23)) + + # Define the name of the generated folder. + name = "hamming_window_fp8x23" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::hamming_window({','.join(args_str)}, Option::Some(0))", # The code signature. + name # The name of the generated folder. + ) + + @staticmethod + # We test here with fp16x16 implementation. + def fp16x16(): + args = [10] + # x = np.float64(4) + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP16x16), Dtype.FP16x16) + y = hamming_window(*args, np.float16) + + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + # Define the name of the generated folder. + name = "hamming_window_fp16x16" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::hamming_window({','.join(args_str)}, Option::Some(0))", # The code signature. + name # The name of the generated folder. + ) + + # @staticmethod + # # We test here with i8 implementation. + # def i8(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.I8)) + # args = [5] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.I8) + # y = hamming_window(*args, np.int8) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.I8, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "hamming_window_i8" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::hamming_window({','.join(args_str)}, Option::Some(1))", # The code signature. + # name # The name of the generated folder. + # ) + + # @staticmethod + # # We test here with i32 implementation. + # def i32(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.I32)) + # args = [4] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.I32) + # y = hamming_window(*args, np.int32) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.I32, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "hamming_window_i32" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::hamming_window({','.join(args_str)}, Option::Some(0))", # The code signature. + # name # The name of the generated folder. + # ) + + # @staticmethod + # # We test here with u32 implementation. + # def u32(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.U32)) + # args = [4] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.U32) + # y = hamming_window(*args, np.uint32) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.U32, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "hamming_window_u32" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::hamming_window({','.join(args_str)}, Option::Some(0))", # The code signature. + # name # The name of the generated folder. + # ) + \ No newline at end of file diff --git a/nodegen/node/hann_window.py b/nodegen/node/hann_window.py new file mode 100644 index 000000000..b8d94025b --- /dev/null +++ b/nodegen/node/hann_window.py @@ -0,0 +1,130 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait, get_data_statement + +def hann_window(size, output_datatype=None, periodic=None) -> np.ndarray: # type: ignore + if periodic == 1: + N_1 = size + else: + N_1 = size - 1 + ni = np.arange(size, dtype=output_datatype) + res = np.sin((ni * np.float64(np.pi).astype(output_datatype) / N_1).astype(output_datatype)) ** 2 + return res.astype(output_datatype) + +class Hann_window(RunAll): + + @staticmethod + # We test here with fp8x23 implementation. + def fp8x23(): + print(get_data_statement(to_fp(np.array([np.pi]).flatten(), FixedImpl.FP8x23), Dtype.FP8x23)) + args = [4] + # x = np.float64(4) + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP8x23), Dtype.FP8x23) + y = hann_window(*args, np.float64) + print(y) + + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP8x23, y.shape, to_fp(y.flatten(), FixedImpl.FP8x23)) + + # Define the name of the generated folder. + name = "hann_window_fp8x23" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::hann_window({','.join(args_str)}, Option::Some(0))", # The code signature. + name # The name of the generated folder. + ) + + @staticmethod + # We test here with fp16x16 implementation. + def fp16x16(): + print(get_data_statement(to_fp(np.array([np.pi]).flatten(), FixedImpl.FP16x16), Dtype.FP16x16)) + args = [10] + # x = np.float64(4) + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP16x16), Dtype.FP16x16) + y = hann_window(*args, np.float16) + print(y) + + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + # Define the name of the generated folder. + name = "hann_window_fp16x16" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::hann_window({','.join(args_str)}, Option::Some(0))", # The code signature. + name # The name of the generated folder. + ) + + # @staticmethod + # # We test here with i8 implementation. + # def i8(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.I8)) + # args = [5] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.I8) + # y = hann_window(*args, np.int8) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.I8, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "hann_window_i8" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::hann_window({','.join(args_str)}, Option::Some(1))", # The code signature. + # name # The name of the generated folder. + # ) + + # @staticmethod + # # We test here with i32 implementation. + # def i32(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.I32)) + # args = [4] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.I32) + # y = hann_window(*args, np.int32) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.I32, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "hann_window_i32" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::hann_window({','.join(args_str)}, Option::Some(0))", # The code signature. + # name # The name of the generated folder. + # ) + + # @staticmethod + # # We test here with u32 implementation. + # def u32(): + # print(get_data_statement(np.array([np.pi]).flatten(), Dtype.U32)) + # args = [4] + # # x = np.float64(4) + # args_str = get_data_statement(np.array(args).flatten(), Dtype.U32) + # y = hann_window(*args, np.uint32) + # print(y) + + # # Convert the floats values in `y` to fixed points with `to_fp` method: + # y = Tensor(Dtype.U32, y.shape, y.flatten()) + + # # Define the name of the generated folder. + # name = "hann_window_u32" + # # Invoke `make_test` method to generate corresponding Cairo tests: + # make_test( + # [], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::hann_window({','.join(args_str)}, Option::Some(0))", # The code signature. + # name # The name of the generated folder. + # ) + \ No newline at end of file diff --git a/nodegen/node/random_uniform_like.py b/nodegen/node/random_uniform_like.py new file mode 100644 index 000000000..6669093e2 --- /dev/null +++ b/nodegen/node/random_uniform_like.py @@ -0,0 +1,125 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait + +def random_uniform_like(x: np.ndarray, high: int=1,low: int=0,seed: int=25) ->np.ndarray: + dtype = np.float64 + if seed is None or np.isnan(seed): # type: ignore + state = np.random.RandomState() + else: + state = np.random.RandomState(seed=int(seed)) # type: ignore + res = state.rand(*x.shape).astype(dtype) + res *= high - low # type: ignore + res += low # type: ignore + return (res.astype(dtype),) + +def get_data_statement(data: np.ndarray, dtype: Dtype) -> list[str]: + match dtype: + case Dtype.FP8x23: + return ["Option::Some(FP8x23 { "+f"mag: {abs(int(x))}, sign: {str(x < 0).lower()} "+"})" for x in data.flatten()] + case Dtype.FP16x16: + return ["Option::Some(FP16x16 { "+f"mag: {abs(int(x))}, sign: {str(x < 0).lower()} "+"})" for x in data.flatten()] + case Dtype.U32: + return [f"Option::Some({int(x)})" for x in data.flatten()] + +class Random_uniform_like(RunAll): + + @staticmethod + def fp8x23(): + x = np.random.uniform(1, 10, (1, 2, 2, 4)).astype(np.float64) + y = random_uniform_like(x) + + args = [10, 1] + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP8x23), Dtype.FP8x23) + x = Tensor(Dtype.FP8x23, x.shape, to_fp( + x.flatten(), FixedImpl.FP8x23)) + y = Tensor(Dtype.FP8x23, y[0].shape, to_fp( + y[0].flatten(), FixedImpl.FP8x23)) + + name = "random_uniform_like_fp8x23" + make_test( + [x], # List of input tensors. + y, # The expected output result. + f"TensorTrait::random_uniform_like(@input_0, {','.join(args_str)}, Option::Some(354145))", # The code signature. + name # The name of the generated folder. + ) + + @staticmethod + def fp16x16(): + x = np.random.uniform(1, 10, (1, 2, 2, 4)).astype(np.float16) + y = random_uniform_like(x) + + args = [10, 1] + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP16x16), Dtype.FP16x16) + + + x = Tensor(Dtype.FP16x16, x.shape, to_fp( + x.flatten(), FixedImpl.FP16x16)) + y = Tensor(Dtype.FP16x16, y[0].shape, to_fp( + y[0].flatten(), FixedImpl.FP16x16)) + + name = "random_uniform_like_fp16x16" + make_test( + [x], # List of input tensors. + y, # The expected output result. + f"TensorTrait::random_uniform_like(@input_0, {','.join(args_str)}, Option::Some(354145))", # The code signature. + name # The name of the generated folder. + ) + + # @staticmethod + # def fp64x64(): + # x = np.random.uniform(-3, 3, (1, 2, 2, 4)).astype(np.float64) + # y = random_uniform_like(x) + + # x = Tensor(Dtype.FP64x64, x.shape, to_fp( + # x.flatten(), FixedImpl.FP64x64)) + # y = Tensor(Dtype.FP64x64, y[0].shape, to_fp( + # y[0].flatten(), FixedImpl.FP64x64)) + + # name = "random_uniform_like_fp64x64" + # make_test([x], y, "TensorTrait::random_uniform_like(@input_0, 5, 1, 10)", + # name) + + # @staticmethod + # def fpi8(): + # x = np.random.randint(-3, 3, (1, 2, 2, 4)).astype(np.int8) + # y = random_uniform_like(x) + + # x = Tensor(Dtype.I8, x.shape, x.flatten()) + # y = Tensor(Dtype.I8, y[0].shape, y[0].flatten()) + + # name = "random_uniform_like_i8" + # make_test([x], y, "TensorTrait::random_uniform_like(@input_0, 5, 1, 10)", + # name) + + # @staticmethod + # def fpi32(): + # x = np.random.randint(-3, 3, (1, 2, 2, 4)).astype(np.int32) + # y = random_uniform_like(x) + + # x = Tensor(Dtype.I32, x.shape, x.flatten()) + # y = Tensor(Dtype.I32, y[0].shape, y[0].flatten()) + + # name = "random_uniform_like_i32" + # make_test([x], y, "TensorTrait::random_uniform_like(@input_0, 5, 1, 10)", + # name) + + + # @staticmethod + # def fpu32(): + # x = np.random.randint(-3, 3, (1, 2, 2, 4)).astype(np.uint32) + # y = random_uniform_like(x) + # args = [5, 1, 10] + # args_str = get_data_statement(np.array(args).flatten(), Dtype.U32) + + + # x = Tensor(Dtype.U32, x.shape, x.flatten()) + # y = Tensor(Dtype.U32, y[0].shape, y[0].flatten()) + + # name = "random_uniform_like_u32" + # make_test( + # [x], # List of input tensors. + # y, # The expected output result. + # f"TensorTrait::random_uniform_like(@input_0, {','.join(args_str)})", # The code signature. + # name # The name of the generated folder. + # ) diff --git a/nodegen/node/range.py b/nodegen/node/range.py new file mode 100644 index 000000000..6eb7751bd --- /dev/null +++ b/nodegen/node/range.py @@ -0,0 +1,108 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl, Trait, get_data_statement + + + +class Range(RunAll): + + @staticmethod + # We test here with fp8x23 implementation. + def fp8x23(): + args = [1, 5, 0.3] + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP8x23), Dtype.FP8x23) + y = np.arange(*args) + print(y) + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP8x23, y.shape, to_fp(y.flatten(), FixedImpl.FP8x23)) + + # Define the name of the generated folder. + name = "range_fp8x23" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::range({','.join(args_str)})", # The code signature. + name, # The name of the generated folder. + ) + + @staticmethod + # We test here with fp16x16 implementation. + def fp16x16(): + args = [1, 25, 3] + args_str = get_data_statement(to_fp(np.array(args).flatten(), FixedImpl.FP16x16), Dtype.FP16x16) + y = np.arange(*args) + print(y) + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.FP16x16, y.shape, to_fp(y.flatten(), FixedImpl.FP16x16)) + + # Define the name of the generated folder. + name = "range_fp16x16" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::range({','.join(args_str)})", # The code signature. + name, # The name of the generated folder. + ) + + @staticmethod + # We test here with i8 implementation. + def i8(): + args = [-1, 25, 3] + args_str = get_data_statement(np.array(args).flatten(), Dtype.I8) + y = np.arange(*args) + print(y) + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.I8, y.shape, y.flatten()) + + # Define the name of the generated folder. + name = "range_i8" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::range({','.join(args_str)})", # The code signature. + name, # The name of the generated folder. + ) + + @staticmethod + # We test here with i32 implementation. + def i32(): + args = [21, 2, -3] + args_str = get_data_statement(np.array(args).flatten(), Dtype.I32) + y = np.arange(*args) + print(y) + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.I32, y.shape, y.flatten()) + + # Define the name of the generated folder. + name = "range_i32" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::range({','.join(args_str)})", # The code signature. + name, # The name of the generated folder. + ) + + @staticmethod + # We test here with u32 implementation. + def u32(): + args = [1, 25, 3] + args_str = get_data_statement(np.array(args).flatten(), Dtype.U32) + y = np.arange(*args) + print(y) + # Convert the floats values in `y` to fixed points with `to_fp` method: + y = Tensor(Dtype.U32, y.shape, y.flatten()) + + # Define the name of the generated folder. + name = "range_u32" + # Invoke `make_test` method to generate corresponding Cairo tests: + make_test( + [], # List of input tensors. + y, # The expected output result. + f"TensorTrait::range({','.join(args_str)})", # The code signature. + name, # The name of the generated folder. + ) + \ No newline at end of file diff --git a/nodegen/node/reverse_sequence.py b/nodegen/node/reverse_sequence.py new file mode 100644 index 000000000..ee8a8bbae --- /dev/null +++ b/nodegen/node/reverse_sequence.py @@ -0,0 +1,225 @@ +import numpy as np +from nodegen.node import RunAll +from ..helpers import make_test, to_fp, Tensor, Dtype, FixedImpl + + +class Reverse_sequence(RunAll): + @staticmethod + def Reverse_sequence_u32(): + def reverse_sequence_u32_4x4_batch(): + x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], dtype=np.uint32).reshape((4, 4)) + y = np.array([0, 1, 2, 3, 5, 4, 6, 7, 10, 9, 8, 11, 15, 14, 13, 12], dtype=np.uint32).reshape((4, 4)) + _x = Tensor(Dtype.U32, x.shape, x.flatten()) + _y = Tensor(Dtype.U32, y.shape, y.flatten()) + name = "reverse_sequence_u32_4x4_batch" + + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![4].span(), array![1,2,3,4].span()), Option::Some(0), Option::Some(1))", + name + ) + + def reverse_sequence_u32_4x4_time(): + x = np.array([0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15], dtype=np.uint32).reshape((4, 4)) + y = np.array([3, 6, 9, 12, 2, 5, 8, 13, 1, 4, 10, 14, 0, 7, 11, 15], dtype=np.uint32).reshape((4, 4)) + _x = Tensor(Dtype.U32, x.shape, x.flatten()) + _y = Tensor(Dtype.U32, y.shape, y.flatten()) + name = "reverse_sequence_u32_4x4_time" + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![4].span(), array![4,3,2,1].span()), Option::Some(1), Option::Some(0))", + name + ) + def reverse_sequence_u32_3x3_batch(): + x = np.array([0,1,2,3,4,5,6,7,8], dtype=np.uint32).reshape(3,3) + y = np.array([2,1,0,3,4,5,7,6,8], dtype=np.uint32).reshape(3,3) + _x = Tensor(Dtype.U32, x.shape, x.flatten()) + _y = Tensor(Dtype.U32, y.shape, y.flatten()) + name = "reverse_sequence_u32_3x3_batch" + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![3].span(), array![3,1,2].span()), Option::Some(0), Option::Some(1))", + name + ) + def reverse_sequence_u32_3x3_time(): + x = np.array([0,1,2,3,4,5,6,7,8], dtype=np.uint32).reshape(3,3) + y = np.array([0,7,8,3,4,5,6,1,2], dtype=np.uint32).reshape(3,3) + _x = Tensor(Dtype.U32, x.shape, x.flatten()) + _y = Tensor(Dtype.U32, y.shape, y.flatten()) + name = "reverse_sequence_u32_3x3_time" + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![3].span(), array![1,3,3].span()), Option::Some(1), Option::Some(0))", + name + ) + + reverse_sequence_u32_4x4_batch() + reverse_sequence_u32_4x4_time() + reverse_sequence_u32_3x3_batch() + reverse_sequence_u32_3x3_time() + + + @staticmethod + def Reverse_sequence_i32(): + def reverse_sequence_i32_batch(): + x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], dtype=np.int32).reshape((4, 4)) + y = np.array([0, 1, 2, 3, 5, 4, 6, 7, 10, 9, 8, 11, 15, 14, 13, 12], dtype=np.int32).reshape((4, 4)) + _x = Tensor(Dtype.I32, x.shape, x.flatten()) + _y = Tensor(Dtype.I32, y.shape, y.flatten()) + name = "reverse_sequence_i32_batch_equal_parts" + + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![4].span(), array![1,2,3,4].span()), Option::Some(0), Option::Some(1))", + name + ) + + def reverse_sequence_i32_time(): + x = np.array([0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15], dtype=np.int32).reshape((4, 4)) + y = np.array([3, 6, 9, 12, 2, 5, 8, 13, 1, 4, 10, 14, 0, 7, 11, 15], dtype=np.int32).reshape((4, 4)) + _x = Tensor(Dtype.I32, x.shape, x.flatten()) + _y = Tensor(Dtype.I32, y.shape, y.flatten()) + name = "reverse_sequence_i32_time_equal_parts" + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![4].span(), array![4,3,2,1].span()), Option::Some(1), Option::Some(0))", + name + ) + + reverse_sequence_i32_batch() + reverse_sequence_i32_time() + + @staticmethod + def Reverse_sequence_i8(): + def reverse_sequence_batch(): + x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], dtype=np.int8).reshape((4, 4)) + y = np.array([0, 1, 2, 3, 5, 4, 6, 7, 10, 9, 8, 11, 15, 14, 13, 12], dtype=np.int8).reshape((4, 4)) + _x = Tensor(Dtype.I8, x.shape, x.flatten()) + _y = Tensor(Dtype.I8, y.shape, y.flatten()) + name = "reverse_sequence_i8_batch_equal_parts" + + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![4].span(), array![1,2,3,4].span()), Option::Some(0), Option::Some(1))", + name + ) + + def reverse_sequence_time(): + x = np.array([0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15], dtype=np.uint32).reshape((4, 4)) + y = np.array([3, 6, 9, 12, 2, 5, 8, 13, 1, 4, 10, 14, 0, 7, 11, 15], dtype=np.uint32).reshape((4, 4)) + _x = Tensor(Dtype.U32, x.shape, x.flatten()) + _y = Tensor(Dtype.U32, y.shape, y.flatten()) + name = "reverse_sequence_i8_time_equal_parts" + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait:: ::new(array![4].span(), array![4,3,2,1].span()), Option::Some(1), Option::Some(0))", + name + ) + reverse_sequence_batch() + reverse_sequence_time() + + def Reverse_sequence_fp16x16(): + def reverse_sequence_batch(): + x = to_fp(np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], dtype=np.int64).reshape(4, 4), FixedImpl.FP16x16) + y = to_fp(np.array([0, 1, 2, 3, 5, 4, 6, 7, 10, 9, 8, 11, 15, 14, 13, 12], dtype=np.int64).reshape(4, 4), FixedImpl.FP16x16) + _x = Tensor(Dtype.FP16x16, x.shape, x.flatten()) + _y = Tensor(Dtype.FP16x16, y.shape, y.flatten()) + name = "reverse_sequence_fp16x16_batch_equal_parts" + make_test( + [_x], + _y, "input_0.reverse_sequence(TensorTrait:: ::new(array![4].span(), array![1,2,3,4].span()), Option::Some(0), Option::Some(1))", + name + ) + def reverse_sequence_time(): + x = to_fp(np.array([0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15], dtype=np.int64).reshape(4, 4), FixedImpl.FP16x16) + y = to_fp(np.array([3, 6, 9, 12, 2, 5, 8, 13, 1, 4, 10, 14, 0, 7, 11, 15], dtype=np.int64).reshape(4, 4), FixedImpl.FP16x16) + _x = Tensor(Dtype.FP16x16, x.shape, x.flatten()) + _y = Tensor(Dtype.FP16x16, y.shape, y.flatten()) + name = "reverse_sequence_fp16x16_time_equal_parts" + make_test( + [_x], + _y, + "input_0.reverse_sequence(TensorTrait::