diff --git a/.all-contributorsrc b/.all-contributorsrc
index 6e798d849..84be25e8a 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -188,6 +188,15 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "andresmayorca",
+ "name": "Andres",
+ "avatar_url": "https://avatars.githubusercontent.com/u/70079260?v=4",
+ "profile": "http://andresmayorca.github.io",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.gitignore b/.gitignore
index b69dbeef7..5a0c79267 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@ target
__pycache__
neural_network/.venv
+
+Scarb.lock
\ No newline at end of file
diff --git a/README.md b/README.md
index 9e547c851..a89893233 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-20-orange.svg?style=flat-square)](#contributors-)
+[![All Contributors](https://img.shields.io/badge/all_contributors-21-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/).
@@ -93,6 +93,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
0xfulanito 💻 |
0x73e 💻 |
Thomas S. Bauer 💻 |
+ Andres 💻 |
diff --git a/Scarb.lock b/Scarb.lock
deleted file mode 100644
index 85e5484e6..000000000
--- a/Scarb.lock
+++ /dev/null
@@ -1,20 +0,0 @@
-# Code generated by scarb DO NOT EDIT.
-version = 1
-
-[[package]]
-name = "alexandria_data_structures"
-version = "0.1.0"
-source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=f37d73d#f37d73d8a8248e4d8dc65de3949333e30bda022f"
-
-[[package]]
-name = "cubit"
-version = "1.2.0"
-source = "git+https://github.com/raphaelDkhn/cubit.git#e6331ebf98c5d5f442a0e5edefe0b367c8e270d9"
-
-[[package]]
-name = "orion"
-version = "0.1.2"
-dependencies = [
- "alexandria_data_structures",
- "cubit",
-]
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index fd3cfa146..be5423456 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased] - 2023-11-03
+
+## Added
+- Scatter Elements Operator.
+
## [Unreleased] - 2023-09-27
## Added
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index 409c1715e..f96dd7a6f 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -40,7 +40,9 @@
* [Tensor](framework/operators/tensor/README.md)
* [tensor.new](framework/operators/tensor/tensor.new.md)
* [tensor.at](framework/operators/tensor/tensor.at.md)
+ * [tensor.min_in_tensor](framework/operators/tensor/tensor.min_in_tensor.md)
* [tensor.min](framework/operators/tensor/tensor.min.md)
+ * [tensor.max\_in\_tensor](framework/operators/tensor/tensor.max\_in\_tensor.md)
* [tensor.max](framework/operators/tensor/tensor.max.md)
* [tensor.stride](framework/operators/tensor/tensor.stride.md)
* [tensor.ravel\_index](framework/operators/tensor/tensor.ravel\_index.md)
@@ -82,6 +84,7 @@
* [tensor.gather](framework/operators/tensor/tensor.gather.md)
* [tensor.quantize\_linear](framework/operators/tensor/tensor.quantize\_linear.md)
* [tensor.dequantize\_linear](framework/operators/tensor/tensor.dequantize\_linear.md)
+ * [tensor.qlinear\_matmul](framework/operators/tensor/tensor.qlinear\_matmul.md)
* [tensor.nonzero](framework/operators/tensor/tensor.nonzero.md)
* [tensor.squeeze](framework/operators/tensor/tensor.squeeze.md)
* [tensor.unsqueeze](framework/operators/tensor/tensor.unsqueeze.md)
@@ -90,6 +93,8 @@
* [tensor.identity](framework/operators/tensor/tensor.identity.md)
* [tensor.and](framework/operators/tensor/tensor.and.md)
* [tensor.where](framework/operators/tensor/tensor.where.md)
+ * [tensor.round](framework/operators/tensor/tensor.round.md)
+ * [tensor.scatter](framework/operators/tensor/tensor.scatter.md)
* [Neural Network](framework/operators/neural-network/README.md)
* [nn.relu](framework/operators/neural-network/nn.relu.md)
* [nn.leaky\_relu](framework/operators/neural-network/nn.leaky\_relu.md)
diff --git a/docs/framework/compatibility.md b/docs/framework/compatibility.md
index 29d537ff6..e5f7f39cf 100644
--- a/docs/framework/compatibility.md
+++ b/docs/framework/compatibility.md
@@ -56,6 +56,7 @@ You can see below the list of current supported ONNX Operators:
| [Gather](operators/tensor/tensor.gather.md) | :white\_check\_mark: |
| [QuantizeLinear](operators/tensor/tensor.quantize\_linear.md) | :white\_check\_mark: |
| [DequantizeLinear](operators/tensor/tensor.quantize\_linear.md) | :white\_check\_mark: |
+| [QLinearMatmul](operators/tensor/tensor.qlinear\_matmul.md) | :white\_check\_mark: |
| [Nonzero](operators/tensor/tensor.nonzero.md) | :white\_check\_mark: |
| [Squeeze](operators/tensor/tensor.squeeze.md) | :white\_check\_mark: |
| [Unsqueeze](operators/tensor/tensor.unsqueeze.md) | :white\_check\_mark: |
@@ -66,6 +67,12 @@ You can see below the list of current supported ONNX Operators:
| [Xor](operators/tensor/tensor.xor.md) | :white\_check\_mark: |
| [Or](operators/tensor/tensor.or.md) | :white\_check\_mark: |
| [Gemm](operators/neural-network/nn.gemm.md) | :white\_check\_mark: |
+| [MinInTensor](operators/tensor/tensor.min\_in\_tensor.md) | :white\_check\_mark: |
+| [Min](operators/tensor/tensor.min.md) | :white\_check\_mark: |
| [Where](operators/tensor/tensor.where.md) | :white\_check\_mark: |
+| [Round](operators/tensor/tensor.round.md) | :white\_check\_mark: |
+| [MaxInTensor](operators/tensor/tensor.max\_in\_tensor.md) | :white\_check\_mark: |
+| [Max](operators/tensor/tensor.max.md) | :white\_check\_mark: |
+| [Scatter](operators/tensor/scatter.max.md) | :white\_check\_mark: |
-Current Operators support: **61/156 (39%)**
+Current Operators support: **68/156 (43%)**
diff --git a/docs/framework/operators/tensor/README.md b/docs/framework/operators/tensor/README.md
index 361401570..369713044 100644
--- a/docs/framework/operators/tensor/README.md
+++ b/docs/framework/operators/tensor/README.md
@@ -50,6 +50,8 @@ use orion::operators::tensor::TensorTrait;
| [`tensor.xor`](tensor.xor.md) | Computes the logical XOR of two tensors element-wise. |
| [`tensor.stride`](tensor.stride.md) | Computes the stride of each dimension in the tensor. |
| [`tensor.onehot`](tensor.onehot.md) | Produces one-hot tensor based on input. |
+| [`tensor.max_in_tensor`](tensor.max\_in\_tensor.md) | Returns the maximum value in the tensor. |
+| [`tensor.min_in_tensor`](tensor.min\_in\_tensor.md) | Returns the minimum value in the tensor. |
| [`tensor.min`](tensor.min.md) | Returns the minimum value in the tensor. |
| [`tensor.max`](tensor.max.md) | Returns the maximum value in the tensor. |
| [`tensor.reduce_sum`](tensor.reduce\_sum.md) | Reduces a tensor by summing its elements along a specified axis. |
@@ -77,6 +79,7 @@ use orion::operators::tensor::TensorTrait;
| [`tensor.concat`](tensor.concat.md) | Concatenate a list of tensors into a single tensor. |
| [`tensor.quantize_linear`](tensor.quantize\_linear.md) | Quantizes a Tensor to i8 using linear quantization. |
| [`tensor.dequantize_linear`](tensor.dequantize\_linear.md) | Dequantizes an i8 Tensor using linear dequantization. |
+| [`tensor.qlinear_matmul`](tensor.qlinear\_matmul.md) | Performs the product of two quantized i8 Tensors. |
| [`tensor.gather`](tensor.gather.md) | Gather entries of the axis dimension of data. |
| [`tensor.nonzero`](tensor.nonzero.md) | Produces indices of the elements that are non-zero (in row-major order - by dimension). |
| [`tensor.squeeze`](tensor.squeeze.md) | Removes dimensions of size 1 from the shape of a tensor. |
@@ -86,6 +89,8 @@ use orion::operators::tensor::TensorTrait;
| [`tensor.and`](tensor.and.md) | Computes the logical AND of two tensors element-wise. |
| [`tensor.identity`](tensor.identity.md) | Return a Tensor with the same shape and contents as input. |
| [`tensor.where`](tensor.where.md) | Return elements chosen from x or y depending on condition. |
+| [`tensor.round`](tensor.round.md) | Computes the round value of all elements in the input tensor. |
+| [`tensor.scatter`](tensor.scatter.md) | Produces a copy of input data, and updates value to values specified by updates at specific index positions specified by indices. |
## Arithmetic Operations
diff --git a/docs/framework/operators/tensor/tensor.max.md b/docs/framework/operators/tensor/tensor.max.md
index e115cc559..966405862 100644
--- a/docs/framework/operators/tensor/tensor.max.md
+++ b/docs/framework/operators/tensor/tensor.max.md
@@ -1,33 +1,63 @@
# tensor.max
```rust
- fn max(self: @Tensor) -> T;
+ fn max(tensors: Span>) -> Tensor;
```
-Returns the maximum value in the tensor.
+Returns the element-wise maximum values from a list of input tensors
+The input tensors must have either:
+* Exactly the same shape
+* The same number of dimensions and the length of each dimension is either a common length or 1.
## Args
-* `self`(`@Tensor`) - The input tensor.
+* `tensors`(` Span>,`) - Array of the input tensors
-## Returns
+## Returns
-The maximum `T` value in the tensor.
+A new `Tensor` containing the element-wise maximum values
-Examples
+## Panics
+
+* Panics if tensor array is empty
+* Panics if the shapes are not equal or broadcastable
+
+## Examples
+
+Case 1: Process tensors with same shape
```rust
use array::{ArrayTrait, SpanTrait};
use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
-fn max_example() -> u32 {
- let tensor = TensorTrait::new(
- shape: array![2, 2, 2].span(), data: array![0, 1, 2, 3, 4, 5, 6, 7].span(),
- );
+fn max_example() -> Tensor {
+ let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ let tensor2 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 3, 1, 2].span(),);
+ let result = TensorTrait::max(tensors: array![tensor1, tensor2].span());
+ return result;
+}
+>>> [0, 3, 2, 3]
+
+ result.shape
+>>> (2, 2)
+```
+
+Case 2: Process tensors with different shapes
- // We can call `max` function as follows.
- return tensor.max();
+```rust
+use array::{ArrayTrait, SpanTrait};
+
+use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+
+fn max_example() -> Tensor {
+ let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ let tensor2 = TensorTrait::new(shape: array![1, 2].span(), data: array![1, 4].span(),);
+ let result = TensorTrait::max(tensors: array![tensor1, tensor2].span());
+ return result;
}
->>> 7
+>>> [1, 4, 2, 4]
+
+ result.shape
+>>> (2, 2)
```
diff --git a/docs/framework/operators/tensor/tensor.max_in_tensor.md b/docs/framework/operators/tensor/tensor.max_in_tensor.md
new file mode 100644
index 000000000..08ac0f82f
--- /dev/null
+++ b/docs/framework/operators/tensor/tensor.max_in_tensor.md
@@ -0,0 +1,33 @@
+# tensor.max_in_tensor
+
+```rust
+ fn max_in_tensor(self: @Tensor) -> T;
+```
+
+Returns the maximum value in the tensor.
+
+## Args
+
+* `self`(`@Tensor`) - The input tensor.
+
+## Returns
+
+The maximum `T` value in the tensor.
+
+Examples
+
+```rust
+use array::{ArrayTrait, SpanTrait};
+
+use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+
+fn max_in_tensor_example() -> u32 {
+ let tensor = TensorTrait::new(
+ shape: array![2, 2, 2].span(), data: array![0, 1, 2, 3, 4, 5, 6, 7].span(),
+ );
+
+ // We can call `max_in_tensor` function as follows.
+ return tensor.max_in_tensor();
+}
+>>> 7
+```
diff --git a/docs/framework/operators/tensor/tensor.min.md b/docs/framework/operators/tensor/tensor.min.md
index 037c4067f..31157c2dc 100644
--- a/docs/framework/operators/tensor/tensor.min.md
+++ b/docs/framework/operators/tensor/tensor.min.md
@@ -1,34 +1,63 @@
# tensor.min
```rust
- fn min(self: @Tensor) -> T;
+ fn min(tensors: Span>) -> Tensor;
```
-Returns the minimum value in the tensor.
+Returns the element-wise minumum values from a list of input tensors
+The input tensors must have either:
+* Exactly the same shape
+* The same number of dimensions and the length of each dimension is either a common length or 1.
## Args
-* `self`(`@Tensor`) - The input tensor.
+* `tensors`(` Span>,`) - Array of the input tensors
-## Returns
+## Returns
-The minimum `T` value in the tensor.
+A new `Tensor` containing the element-wise minimum values
+
+## Panics
+
+* Panics if tensor array is empty
+* Panics if the shapes are not equal or broadcastable
## Examples
+Case 1: Process tensors with same shape
+
```rust
use array::{ArrayTrait, SpanTrait};
use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
-fn min_example() -> u32 {
- let tensor = TensorTrait::new(
- shape: array![2, 2, 2].span(),
- data: array![0, 1, 2, 3, 4, 5, 6, 7].span(),
- );
+fn min_example() -> Tensor {
+ let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ let tensor2 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 3, 1, 2].span(),);
+ let result = TensorTrait::min(tensors: array![tensor1, tensor2].span());
+ return result;
+}
+>>> [0, 1, 1, 2]
+
+ result.shape
+>>> (2, 2)
+```
+
+Case 2: Process tensors with different shapes
- // We can call `min` function as follows.
- return tensor.min();
+```rust
+use array::{ArrayTrait, SpanTrait};
+
+use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+
+fn min_example() -> Tensor {
+ let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ let tensor2 = TensorTrait::new(shape: array![1, 2].span(), data: array![1, 4].span(),);
+ let result = TensorTrait::min(tensors: array![tensor1, tensor2].span());
+ return result;
}
->>> 0
+>>> [0, 1, 1, 4]
+
+ result.shape
+>>> (2, 2)
```
diff --git a/docs/framework/operators/tensor/tensor.min_in_tensor.md b/docs/framework/operators/tensor/tensor.min_in_tensor.md
new file mode 100644
index 000000000..65ac74034
--- /dev/null
+++ b/docs/framework/operators/tensor/tensor.min_in_tensor.md
@@ -0,0 +1,34 @@
+# tensor.min_in_tensor
+
+```rust
+ fn min_in_tensor(self: @Tensor) -> T;
+```
+
+Returns the minimum value in the tensor.
+
+## Args
+
+* `self`(`@Tensor`) - The input tensor.
+
+## Returns
+
+The minimum `T` value in the tensor.
+
+## Examples
+
+```rust
+use array::{ArrayTrait, SpanTrait};
+
+use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+
+fn min_in_tensor_example() -> u32 {
+ let tensor = TensorTrait::new(
+ shape: array![2, 2, 2].span(),
+ data: array![0, 1, 2, 3, 4, 5, 6, 7].span(),
+ );
+
+ // We can call `min_in_tensor` function as follows.
+ return tensor.min_in_tensor();
+}
+>>> 0
+```
diff --git a/docs/framework/operators/tensor/tensor.qlinear_matmul.md b/docs/framework/operators/tensor/tensor.qlinear_matmul.md
new file mode 100644
index 000000000..df66f92e4
--- /dev/null
+++ b/docs/framework/operators/tensor/tensor.qlinear_matmul.md
@@ -0,0 +1,95 @@
+# tensor.qlinear_matmul
+
+```rust
+ fn qlinear_matmul(self: @Tensor, a_scale: @Tensor, a_zero_point: @Tensor, b: @Tensor, b_scale: @Tensor, b_zero_point: @Tensor, y_scale: @Tensor, y_zero_point: @Tensor) -> Tensor::;
+```
+
+Multiplies quantized Tensors
+
+It consumes two quantized input tensors, their scales and zero points, scale and zero point of output, and computes the quantized output.
+The quantization formula is y = saturate((x / y_scale) + y_zero_point).
+It perfoms the multiplication of the two vectors once dequantized. If either argument is N-D, N > 2, it is treated as a stack of matrices residing in the last two indexes.
+Then return the quantization of the result of the multiplication.
+Scale and zero point must have same shape and the same type. They must be either scalar (per tensor) or N-D tensor (per row for 'a' and per column for 'b').
+Scalar refers to per tensor quantization whereas N-D refers to per row or per column quantization.
+
+## Args
+
+* `self`(`@Tensor`) - The first tensor to be multiplied (a).
+* `a_scale`(`@Tensor`) - Scale for input `a`.
+* `a_zero_point`(`@Tensor`) - Zero point for input `a`.
+* `b`(`@Tensor`) - The second tensor to be multiplied
+* `b_scale`(`@Tensor`) - Scale for input `b`.
+* `b_zero_point`(`@Tensor`) - Zero point for input `b`.
+* `y_scale`(`@Tensor`) - Scale for outut.
+* `y_zero_point`(`@Tensor`) - Zero point for output.
+
+## Returns
+
+A new `Tensor`, containing the quantized result of the multiplication of the dequantized inputs.
+
+## Type Constraints
+
+u32 tensor, not supported.
+
+## Example
+
+```rust
+use array::{ArrayTrait, SpanTrait};
+
+use orion::operators::tensor::{TensorTrait, Tensor, I8Tensor, FP16x16Tensor};
+use orion::numbers::{i8, FP16x16, FP16x16Impl, IntegerTrait, FixedTrait};
+fn qlinear_matmul_example() -> Tensor {
+ let a = TensorTrait::<
+ i8
+ >::new(
+ shape: array![2, 3].span(),
+ data: array![
+ IntegerTrait::::new(3_u8, false),
+ IntegerTrait::::new(4_u8, false),
+ IntegerTrait::::new(5_u8, false),
+ IntegerTrait::::new(2_u8, false),
+ IntegerTrait::::new(4_u8, false),
+ IntegerTrait::::new(3_u8, false)
+ ]
+ .span(),
+ );
+ let b = TensorTrait::<
+ i8
+ >::new(
+ shape: array![3, 1].span(),
+ data: array![
+ IntegerTrait::::new(4_u8, false),
+ IntegerTrait::::new(8_u8, false),
+ IntegerTrait::::new(4_u8, false)
+ ]
+ .span(),
+ );
+
+ let a_scale = TensorTrait::<
+ FP16x16
+ >::new(shape: array![1].span(), data: array![FixedTrait::::new(131072, false)].span(),);
+ let a_zero_point = TensorTrait::<
+ FP16x16
+ >::new(shape: array![1].span(), data: array![FixedTrait::::new(65536, false)].span(),);
+ let b_scale = TensorTrait::<
+ FP16x16
+ >::new(shape: array![1].span(), data: array![FixedTrait::::new(16384, false)].span(),);
+ let b_zero_point = TensorTrait::<
+ FP16x16
+ >::new(shape: array![1].span(), data: array![FixedTrait::::new(0, false)].span(),);
+
+ let y_scale = TensorTrait::<
+ FP16x16
+ >::new(shape: array![1].span(), data: array![FixedTrait::::new(393216, false)].span(),);
+ let y_zero_point = TensorTrait::<
+ FP16x16
+ >::new(shape: array![1].span(), data: array![FixedTrait::::new(655360, false)].span(),);
+
+ return a
+ .qlinear_matmul(
+ @a_scale, @a_zero_point, @b, @b_scale, @b_zero_point, @y_scale, @y_zero_point
+ );
+}
+>>> [14, 13]
+```
\ No newline at end of file
diff --git a/docs/framework/operators/tensor/tensor.round.md b/docs/framework/operators/tensor/tensor.round.md
new file mode 100644
index 000000000..9a6c949eb
--- /dev/null
+++ b/docs/framework/operators/tensor/tensor.round.md
@@ -0,0 +1,39 @@
+#tensor.round
+
+```rust
+ fn round(self: @Tensor) -> Tensor;
+```
+
+Computes the round value of all elements in the input tensor.
+
+## Args
+
+* `self`(`@Tensor`) - The input tensor.
+
+
+## Returns
+
+A new `Tensor` of the same shape as the input tensor with
+the round value of all elements in the input tensor.
+
+## Example
+
+```rust
+use array::{ArrayTrait, SpanTrait};
+
+use orion::operators::tensor::{TensorTrait, Tensor, FP16x16Tensor};
+use orion::numbers::{FixedTrait, FP16x16};
+
+fn round_example() -> Tensor {
+ let tensor = TensorTrait::::new(
+ shape: array![3].span(),
+ data: array![
+ FixedTrait::new(190054, false), // 2.9
+ ]
+ .span(),
+ );
+
+ return tensor.round();
+}
+>>> [3]
+```
diff --git a/docs/framework/operators/tensor/tensor.scatter.md b/docs/framework/operators/tensor/tensor.scatter.md
new file mode 100644
index 000000000..439e7ab0b
--- /dev/null
+++ b/docs/framework/operators/tensor/tensor.scatter.md
@@ -0,0 +1,62 @@
+# tensor.scatter
+
+```rust
+ fn scatter(self: @Tensor, updates: Tensor, indices: Tensor, axis: Option, 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.
+* `axis`(`Option`) - Axis to scatter on. Default: axis=0.
+* `reduction`(`Option`) - Reduction operation. Default: reduction='none'.
+
+## Panics
+
+* Panics if index values are not within bounds [-s, s-1] along axis of size s.
+
+## Returns
+
+A new `Tensor` .
+
+## Example
+
+```rust
+use array::{ArrayTrait, SpanTrait};
+
+use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+
+fn scatter_example() -> Tensor {
+ let tensor = TensorTrait::::new(
+ shape: array![3, 5].span(),
+ data: array![[ 0, 0, 0, 0, 0],
+ [ 0, 0, 0, 0, 0],
+ [ 0, 0, 0, 0, 0]].span(),
+ );
+ let updates = TensorTrait::::new(
+ shape: array![3, 3].span(),
+ data: array![[ 1, 2, 3],
+ [ 4, 5, 6],
+ [ 7, 8, 9]].span(),
+ );
+ let indices = TensorTrait::::new(
+ shape: array![3, 3].span(),
+ data: array![[ 0, 1, 2],
+ [ 2, 0, 1],
+ [ 1, 0, 1]].span(),
+ );
+
+ return tensor.scatter(
+ updates: updates
+ indices: indices,
+ axis: Option::None(()),
+ reduction: Option::None(()),
+ );
+}
+>>> [[ 1, 8, 0, 0, 0],
+ [ 7, 2, 9, 0, 0],
+ [ 4, 0, 3, 0, 0]]
+```
diff --git a/docs/framework/operators/tensor/tensor.where.md b/docs/framework/operators/tensor/tensor.where.md
index 02d3b0059..187f12ecb 100644
--- a/docs/framework/operators/tensor/tensor.where.md
+++ b/docs/framework/operators/tensor/tensor.where.md
@@ -37,7 +37,7 @@ fn where_example() -> Tensor {
let tensor_x = TensorTrait::::new(
shape: array![2, 2].span(), data: array![2, 4, 6, 8].span(),
);
-
+
let tensor_y = TensorTrait::::new(
shape: array![2, 2].span(), data: array![1, 3, 5, 9].span(),
);
diff --git a/nodegen/node/max.py b/nodegen/node/max.py
new file mode 100644
index 000000000..b1079a30c
--- /dev/null
+++ b/nodegen/node/max.py
@@ -0,0 +1,364 @@
+import numpy as np
+from nodegen.node import RunAll
+from ..helpers import make_node, make_test, to_fp, Tensor, Dtype, FixedImpl, Trait
+
+class Max(RunAll):
+
+ @staticmethod
+ def max_u32_two_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+
+ name = "max_u32_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.uint32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.uint32)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+
+ name = "max_u32_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_i32_two_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+
+ name = "max_i32_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int32)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+
+ name = "max_i32_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_i8_two_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+
+ name = "max_i8_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int8)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int8)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+
+ name = "max_i8_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_fp8x23_two_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+
+ name = "max_fp8x23_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+
+ name = "max_fp8x23_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_fp16x16_two_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+
+ name = "max_fp16x16_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.maximum(x, y)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+
+ name = "max_fp16x16_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::max(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+
+ @staticmethod
+ def max_u32_three_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ z = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+ m = Tensor(Dtype.U32, m.shape, m.flatten())
+
+ name = "max_u32_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.uint32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.uint32)
+ z = np.random.randint(0, 6, (1, 1)).astype(np.uint32)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+ m = Tensor(Dtype.U32, m.shape, m.flatten())
+
+ name = "max_u32_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_i32_three_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ z = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+ m = Tensor(Dtype.I32, m.shape, m.flatten())
+
+ name = "max_i32_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int32)
+ z = np.random.randint(0, 6, (1, 1)).astype(np.int32)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+ m = Tensor(Dtype.I32, m.shape, m.flatten())
+
+ name = "max_i32_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_i8_three_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ z = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+ m = Tensor(Dtype.I8, m.shape, m.flatten())
+
+ name = "max_i8_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int8)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int8)
+ z = np.random.randint(0, 6, (1, 1)).astype(np.int8)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+ m = Tensor(Dtype.I8, m.shape, m.flatten())
+
+ name = "max_i8_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_fp8x23_three_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+ m = Tensor(Dtype.FP8x23, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP8x23))
+
+ name = "max_fp8x23_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.random.randint(-3, 3, (1, 1)).astype(np.float64)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+ m = Tensor(Dtype.FP8x23, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP8x23))
+
+ name = "max_fp8x23_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def max_fp16x16_three_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+ m = Tensor(Dtype.FP16x16, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP16x16))
+
+ name = "max_fp16x16_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.random.randint(-3, 3, (1, 1)).astype(np.float64)
+ m = np.maximum(np.maximum(x, y), z)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+ m = Tensor(Dtype.FP16x16, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP16x16))
+
+ name = "max_fp16x16_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::max(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
\ No newline at end of file
diff --git a/nodegen/node/min.py b/nodegen/node/min.py
new file mode 100644
index 000000000..a6299cea0
--- /dev/null
+++ b/nodegen/node/min.py
@@ -0,0 +1,364 @@
+import numpy as np
+from nodegen.node import RunAll
+from ..helpers import make_node, make_test, to_fp, Tensor, Dtype, FixedImpl, Trait
+
+class Min(RunAll):
+
+ @staticmethod
+ def min_u32_two_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+
+ name = "min_u32_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.uint32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.uint32)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+
+ name = "min_u32_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_i32_two_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+
+ name = "min_i32_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int32)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+
+ name = "min_i32_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_i8_two_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+
+ name = "min_i8_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int8)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int8)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+
+ name = "min_i8_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_fp8x23_two_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+
+ name = "min_fp8x23_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+
+ name = "min_fp8x23_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_fp16x16_two_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+
+ name = "min_fp16x16_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.minimum(x, y)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+
+ name = "min_fp16x16_broadcast_two_tensors"
+ make_node([x, y], [z], name)
+ make_test([x, y], z, "TensorTrait::min(array![input_0, input_1].span());", name)
+
+ default()
+ broadcast()
+
+
+ @staticmethod
+ def min_u32_three_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ z = np.random.randint(0, 6, (3, 3, 3)).astype(np.uint32)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+ m = Tensor(Dtype.U32, m.shape, m.flatten())
+
+ name = "min_u32_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.uint32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.uint32)
+ z = np.random.randint(0, 6, (1, 1)).astype(np.uint32)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.U32, x.shape, x.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+ z = Tensor(Dtype.U32, z.shape, z.flatten())
+ m = Tensor(Dtype.U32, m.shape, m.flatten())
+
+ name = "min_u32_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_i32_three_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ z = np.random.randint(0, 6, (3, 3, 3)).astype(np.int32)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+ m = Tensor(Dtype.I32, m.shape, m.flatten())
+
+ name = "min_i32_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int32)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int32)
+ z = np.random.randint(0, 6, (1, 1)).astype(np.int32)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.I32, x.shape, x.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+ z = Tensor(Dtype.I32, z.shape, z.flatten())
+ m = Tensor(Dtype.I32, m.shape, m.flatten())
+
+ name = "min_i32_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_i8_three_tensors():
+ def default():
+ x = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ y = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ z = np.random.randint(0, 6, (3, 3, 3)).astype(np.int8)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+ m = Tensor(Dtype.I8, m.shape, m.flatten())
+
+ name = "min_i8_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(0, 6, (2, 2)).astype(np.int8)
+ y = np.random.randint(0, 6, (1, 2)).astype(np.int8)
+ z = np.random.randint(0, 6, (1, 1)).astype(np.int8)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.I8, x.shape, x.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+ z = Tensor(Dtype.I8, z.shape, z.flatten())
+ m = Tensor(Dtype.I8, m.shape, m.flatten())
+
+ name = "min_i8_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_fp8x23_three_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+ m = Tensor(Dtype.FP8x23, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP8x23))
+
+ name = "min_fp8x23_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.random.randint(-3, 3, (1, 1)).astype(np.float64)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.FP8x23, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP8x23))
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP8x23))
+ z = Tensor(Dtype.FP8x23, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP8x23))
+ m = Tensor(Dtype.FP8x23, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP8x23))
+
+ name = "min_fp8x23_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
+
+ @staticmethod
+ def min_fp16x16_three_tensors():
+ def default():
+ x = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ y = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ z = np.random.randint(-3, 3, (3, 3, 3)).astype(np.float64)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+ m = Tensor(Dtype.FP16x16, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP16x16))
+
+ name = "min_fp16x16_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ def broadcast():
+ x = np.random.randint(-3, 3, (2, 2)).astype(np.float64)
+ y = np.random.randint(-3, 3, (1, 2)).astype(np.float64)
+ z = np.random.randint(-3, 3, (1, 1)).astype(np.float64)
+ m = np.minimum(np.minimum(x, y), z)
+
+ x = Tensor(Dtype.FP16x16, x.shape, to_fp(
+ x.flatten(), FixedImpl.FP16x16))
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+ z = Tensor(Dtype.FP16x16, z.shape, to_fp(
+ z.flatten(), FixedImpl.FP16x16))
+ m = Tensor(Dtype.FP16x16, m.shape, to_fp(
+ m.flatten(), FixedImpl.FP16x16))
+
+ name = "min_fp16x16_broadcast_three_tensors"
+ make_node([x, y, z], [m], name)
+ make_test([x, y, z], m, "TensorTrait::min(array![input_0, input_1, input_2].span());", name)
+
+ default()
+ broadcast()
diff --git a/nodegen/node/round.py b/nodegen/node/round.py
new file mode 100644
index 000000000..1d12c3fdc
--- /dev/null
+++ b/nodegen/node/round.py
@@ -0,0 +1,29 @@
+import numpy as np
+from nodegen.node import RunAll
+from ..helpers import make_node, make_test, to_fp, Tensor, Dtype, FixedImpl
+
+class Round(RunAll):
+
+ @staticmethod
+ def round_fp8x23():
+ x = np.array([0.1, 0.5, 0.9, 1.2, 1.5, 1.8, 2.3, 2.5, 2.7, -1.1, -1.5, -1.9, -2.2, -2.5, -2.8]).astype(np.float64)
+ y = np.array([0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, -1.0, -2.0, -2.0, -2.0, -3.0, -3.0]).astype(np.float64)
+
+ 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 = "round_fp8x23"
+ make_node([x], [y], name)
+ make_test([x], y, "input_0.round()", name)
+
+ @staticmethod
+ def round_fp16x16():
+ x = np.array([0.1, 0.5, 0.9, 1.2, 1.5, 1.8, 2.3, 2.5, 2.7, -1.1, -1.5, -1.9, -2.2, -2.5, -2.8]).astype(np.float64)
+ y = np.array([0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, -1.0, -2.0, -2.0, -2.0, -3.0, -3.0]).astype(np.float64)
+
+ 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 = "round_fp16x16"
+ make_node([x], [y], name)
+ make_test([x], y, "input_0.round()", name)
\ No newline at end of file
diff --git a/nodegen/node/scatter.py b/nodegen/node/scatter.py
new file mode 100644
index 000000000..3e833a840
--- /dev/null
+++ b/nodegen/node/scatter.py
@@ -0,0 +1,439 @@
+import numpy as np
+import torch
+from nodegen.node import RunAll
+from ..helpers import make_node, make_test, to_fp, Tensor, Dtype, FixedImpl, Trait
+
+# The below ScatterElements' numpy implementation is from https://stackoverflow.com/a/46204790/11767360
+def scatter_elements(data, indices, updates, axis=0, reduction="none"): # type: ignore
+ if axis < 0:
+ axis = data.ndim + axis
+
+ idx_xsection_shape = indices.shape[:axis] + indices.shape[axis + 1 :]
+
+ def make_slice(arr, axis, i): # type: ignore
+ slc = [slice(None)] * arr.ndim
+ slc[axis] = i
+ return slc
+
+ def unpack(packed): # type: ignore
+ unpacked = packed[0]
+ for i in range(1, len(packed)):
+ unpacked = unpacked, packed[i]
+ return unpacked
+
+ def make_indices_for_duplicate(idx): # type: ignore
+ final_idx = []
+ for i in range(len(idx[0])):
+ final_idx.append(tuple(idx_element[i] for idx_element in idx))
+ return list(final_idx)
+
+ # We use indices and axis parameters to create idx
+ # idx is in a form that can be used as a NumPy advanced indices for scattering of updates param. in data
+ idx = [
+ [
+ unpack(np.indices(idx_xsection_shape).reshape(indices.ndim - 1, -1)),
+ indices[tuple(make_slice(indices, axis, i))].reshape(1, -1)[0],
+ ]
+ for i in range(indices.shape[axis])
+ ]
+ idx = list(np.concatenate(idx, axis=1))
+ idx.insert(axis, idx.pop())
+
+ # updates_idx is a NumPy advanced indices for indexing of elements in the updates
+ updates_idx = list(idx)
+ updates_idx.pop(axis)
+ updates_idx.insert(
+ axis, np.repeat(np.arange(indices.shape[axis]), np.prod(idx_xsection_shape))
+ )
+
+ scattered = np.copy(data)
+ if reduction == "none":
+ scattered[tuple(idx)] = updates[tuple(updates_idx)]
+ else:
+ idx, updates_idx = make_indices_for_duplicate(idx), make_indices_for_duplicate(
+ updates_idx
+ )
+ for iter, idx_set in enumerate(idx):
+ if reduction == "add":
+ scattered[idx_set] += updates[updates_idx[iter]]
+ elif reduction == "mul":
+ scattered[idx_set] *= updates[updates_idx[iter]]
+ elif reduction == "max":
+ scattered[idx_set] = np.maximum(
+ scattered[idx_set], updates[updates_idx[iter]]
+ )
+ elif reduction == "min":
+ scattered[idx_set] = np.minimum(
+ scattered[idx_set], updates[updates_idx[iter]]
+ )
+ return scattered
+
+class Scatter(RunAll):
+
+ @staticmethod
+ def scatter_fp16x16():
+
+ def scatter():
+ def default():
+ x1 = np.zeros((3, 3)).astype(np.int64)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int64)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 0, 'none')
+
+ x1 = Tensor(Dtype.FP16x16, x1.shape, to_fp(x1.flatten(), FixedImpl.FP16x16))
+ x2 = Tensor(Dtype.FP16x16, x2.shape, to_fp(x2.flatten(), FixedImpl.FP16x16))
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+
+ name = "scatter_fp16x16_3d_default"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(0), reduction:Option::Some('none'))",
+ file_name= name)
+
+ def axis_1():
+ x1 = np.zeros((3, 3)).astype(np.int64)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int64)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'none')
+
+ x1 = Tensor(Dtype.FP16x16, x1.shape, to_fp(x1.flatten(), FixedImpl.FP16x16))
+ x2 = Tensor(Dtype.FP16x16, x2.shape, to_fp(x2.flatten(), FixedImpl.FP16x16))
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+
+ name = "scatter_fp16x16_3d_axis1"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('none'))",
+ file_name= name)
+
+ def axis_1_add():
+ x1 = np.zeros((3, 3)).astype(np.int64)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int64)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'add')
+
+ x1 = Tensor(Dtype.FP16x16, x1.shape, to_fp(x1.flatten(), FixedImpl.FP16x16))
+ x2 = Tensor(Dtype.FP16x16, x2.shape, to_fp(x2.flatten(), FixedImpl.FP16x16))
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.FP16x16, y.shape, to_fp(
+ y.flatten(), FixedImpl.FP16x16))
+
+ name = "scatter_fp16x16_3d_axis1_add"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('add'))",
+ file_name= name)
+
+ default()
+ axis_1()
+ axis_1_add()
+ scatter()
+
+ @staticmethod
+ def scatter_fp8x23():
+
+ def scatter():
+ def default():
+
+ x1 = np.zeros((3, 3)).astype(np.int64)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int64)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 0, 'none')
+
+ x1 = Tensor(Dtype.FP8x23, x1.shape, to_fp(x1.flatten(), FixedImpl.FP8x23))
+ x2 = Tensor(Dtype.FP8x23, x2.shape, to_fp(x2.flatten(), FixedImpl.FP8x23))
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(y.flatten(), FixedImpl.FP8x23))
+
+ name = "scatter_fp8x23_default"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(0), reduction:Option::Some('none'))",
+ file_name= name)
+
+
+ def axis1():
+ x1 = np.zeros((3, 3)).astype(np.int64)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int64)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'none')
+
+ x1 = Tensor(Dtype.FP8x23, x1.shape, to_fp(x1.flatten(), FixedImpl.FP8x23))
+ x2 = Tensor(Dtype.FP8x23, x2.shape, to_fp(x2.flatten(), FixedImpl.FP8x23))
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(y.flatten(), FixedImpl.FP8x23))
+
+ name = "scatter_fp8x23_axis1"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('none'))",
+ file_name= name)
+
+ def axis1_mul():
+ x1 = np.zeros((3, 3)).astype(np.int64)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int64)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 0, 'mul')
+
+ x1 = Tensor(Dtype.FP8x23, x1.shape, to_fp(x1.flatten(), FixedImpl.FP8x23))
+ x2 = Tensor(Dtype.FP8x23, x2.shape, to_fp(x2.flatten(), FixedImpl.FP8x23))
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.FP8x23, y.shape, to_fp(y.flatten(), FixedImpl.FP8x23))
+
+ name = "scatter_fp8x23_mul"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(0), reduction:Option::Some('mul'))",
+ file_name= name)
+
+ default()
+ axis1()
+ axis1_mul()
+ scatter()
+
+ @staticmethod
+ def scatter_i8():
+
+ def scatter_3D():
+ def default():
+ x1 = np.zeros((3, 3)).astype(np.int8)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int8)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 0, 'none')
+
+ x1 = Tensor(Dtype.I8, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.I8, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+
+ name = "scatter_i8_default"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(0), reduction:Option::Some('none'))",
+ file_name= name)
+
+ def axis1():
+ x1 = np.zeros((3, 3)).astype(np.int8)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int8)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'none')
+
+ x1 = Tensor(Dtype.I8, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.I8, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+
+ name = "scatter_i8_axis1"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('none'))",
+ file_name= name)
+
+
+ def axis1_max():
+ x1 = np.zeros((3, 3)).astype(np.int8)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int8)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'max')
+
+ x1 = Tensor(Dtype.I8, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.I8, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.I8, y.shape, y.flatten())
+
+ name = "scatter_i8_axis1_max"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('max'))",
+ file_name= name)
+
+ default()
+ axis1()
+ axis1_max()
+ scatter_3D()
+
+
+ @staticmethod
+ def scatter_i32():
+ def scatter_3D():
+ def default():
+ x1 = np.zeros((3, 3)).astype(np.int32)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int32)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 0, 'none')
+
+ x1 = Tensor(Dtype.I32, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.I32, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+
+ name = "scatter_i8_default"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(0), reduction:Option::Some('none'))",
+ file_name= name)
+
+ def axis1():
+ x1 = np.zeros((3, 3)).astype(np.int32)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int32)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'none')
+
+ x1 = Tensor(Dtype.I32, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.I32, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+
+ name = "scatter_i8_axis1"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('none'))",
+ file_name= name)
+
+ def axis_min():
+ x1 = np.zeros((3, 3)).astype(np.int32)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.int32)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'min')
+
+ x1 = Tensor(Dtype.I32, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.I32, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.I32, y.shape, y.flatten())
+
+ name = "scatter_i8_default"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('min'))",
+ file_name= name)
+
+ default()
+ axis1()
+ axis_min()
+ scatter_3D()
+
+
+ @staticmethod
+ def scatter_u32():
+
+ def scatter_3D():
+ def default():
+ x1 = np.zeros((3, 3)).astype(np.uint32)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.uint32)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 0, 'none')
+
+ x1 = Tensor(Dtype.U32, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.U32, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+
+ name = "scatter_u32_default"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(0), reduction:Option::Some('none'))",
+ file_name= name)
+
+
+ def axis1():
+ x1 = np.zeros((3, 3)).astype(np.uint32)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.uint32)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 1, 'none')
+
+ x1 = Tensor(Dtype.U32, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.U32, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+
+ name = "scatter_u32_axis1"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(1), reduction:Option::Some('none'))",
+ file_name= name)
+
+ def axis_add():
+ x1 = np.zeros((3, 3)).astype(np.uint32)
+ x2 = np.arange(1, 10).reshape((3, 3)).astype(np.uint32)
+ x3 = np.array(
+ [[0,1,2],
+ [2,0,1],
+ [1,0,1]],
+ )
+ y = scatter_elements(x1, x3, x2, 0, 'add')
+
+ x1 = Tensor(Dtype.U32, x1.shape, x1.flatten())
+ x2 = Tensor(Dtype.U32, x2.shape, x2.flatten())
+ x3 = Tensor(Dtype.U32, x3.shape, x3.flatten())
+ y = Tensor(Dtype.U32, y.shape, y.flatten())
+
+ name = "scatter_u32_add"
+ make_node([x1, x2, x3], [y], name)
+ make_test(
+ inputs = [x1, x2, x3], output = y, func_sig = "input_0.scatter(updates:input_1, indices:input_2, axis:Option::Some(0), reduction:Option::Some('add'))",
+ file_name= name)
+
+ default()
+ axis1()
+ axis_add()
+ scatter_3D()
\ No newline at end of file
diff --git a/src/operators/tensor.cairo b/src/operators/tensor.cairo
index 976102b52..30403831f 100644
--- a/src/operators/tensor.cairo
+++ b/src/operators/tensor.cairo
@@ -12,6 +12,11 @@ use orion::operators::tensor::implementations::tensor_fp8x23::{
FP8x23TensorPartialEq,
};
+use orion::operators::tensor::implementations::tensor_fp32x32::{
+ FP32x32Tensor, FP32x32TensorAdd, FP32x32TensorSub, FP32x32TensorMul, FP32x32TensorDiv,
+ FP32x32TensorPartialEq,
+};
+
use orion::operators::tensor::implementations::tensor_fp16x16::{
FP16x16Tensor, FP16x16TensorAdd, FP16x16TensorSub, FP16x16TensorMul, FP16x16TensorDiv,
FP16x16TensorPartialEq,
diff --git a/src/operators/tensor/core.cairo b/src/operators/tensor/core.cairo
index 94a9baac7..8a3ae7e68 100644
--- a/src/operators/tensor/core.cairo
+++ b/src/operators/tensor/core.cairo
@@ -46,6 +46,8 @@ impl TensorSerde, impl TDrop: Drop> of Serde, impl TDrop: Drop> of Serde, impl TDrop: Drop> of Serde {
/// # tensor.new
///
@@ -184,10 +188,10 @@ trait TensorTrait {
/// ```
///
fn at(self: @Tensor, indices: Span) -> T;
- /// # tensor.min
+ /// # tensor.min_in_tensor
///
/// ```rust
- /// fn min(self: @Tensor) -> T;
+ /// fn min_in_tensor(self: @Tensor) -> T;
/// ```
///
/// Returns the minimum value in the tensor.
@@ -207,23 +211,88 @@ trait TensorTrait {
///
/// use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
///
- /// fn min_example() -> u32 {
+ /// fn min_in_tensor_example() -> u32 {
/// let tensor = TensorTrait::new(
/// shape: array![2, 2, 2].span(),
/// data: array![0, 1, 2, 3, 4, 5, 6, 7].span(),
/// );
///
- /// // We can call `min` function as follows.
- /// return tensor.min();
+ /// // We can call `min_in_tensor` function as follows.
+ /// return tensor.min_in_tensor();
/// }
/// >>> 0
/// ```
///
- fn min(self: @Tensor) -> T;
- /// # tensor.max
+ fn min_in_tensor(self: @Tensor) -> T;
+ /// # tensor.min
+ ///
+ /// ```rust
+ /// fn min(tensors: Span>) -> Tensor;
+ /// ```
+ ///
+ /// Returns the element-wise minumum values from a list of input tensors
+ /// The input tensors must have either:
+ /// * Exactly the same shape
+ /// * The same number of dimensions and the length of each dimension is either a common length or 1.
+ ///
+ /// ## Args
+ ///
+ /// * `tensors`(` Span>,`) - Array of the input tensors
+ ///
+ /// ## Returns
+ ///
+ /// A new `Tensor` containing the element-wise minimum values
+ ///
+ /// ## Panics
+ ///
+ /// * Panics if tensor array is empty
+ /// * Panics if the shapes are not equal or broadcastable
+ ///
+ /// ## Examples
+ ///
+ /// Case 1: Process tensors with same shape
+ ///
+ /// ```rust
+ /// use array::{ArrayTrait, SpanTrait};
+ ///
+ /// use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+ ///
+ /// fn min_example() -> Tensor {
+ /// let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ /// let tensor2 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 3, 1, 2].span(),);
+ /// let result = TensorTrait::min(tensors: array![tensor1, tensor2].span());
+ /// return result;
+ /// }
+ /// >>> [0, 1, 1, 2]
+ ///
+ /// result.shape
+ /// >>> (2, 2)
+ /// ```
+ ///
+ /// Case 2: Process tensors with different shapes
+ ///
+ /// ```rust
+ /// use array::{ArrayTrait, SpanTrait};
+ ///
+ /// use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+ ///
+ /// fn min_example() -> Tensor {
+ /// let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ /// let tensor2 = TensorTrait::new(shape: array![1, 2].span(), data: array![1, 4].span(),);
+ /// let result = TensorTrait::min(tensors: array![tensor1, tensor2].span());
+ /// return result;
+ /// }
+ /// >>> [0, 1, 1, 4]
+ ///
+ /// result.shape
+ /// >>> (2, 2)
+ /// ```
+ ///
+ fn min(tensors: Span>) -> Tensor;
+ /// # tensor.max_in_tensor
///
/// ```rust
- /// fn max(self: @Tensor) -> T;
+ /// fn max_in_tensor(self: @Tensor) -> T;
/// ```
///
/// Returns the maximum value in the tensor.
@@ -243,18 +312,83 @@ trait TensorTrait {
///
/// use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
///
- /// fn max_example() -> u32 {
+ /// fn max_in_tensor_example() -> u32 {
/// let tensor = TensorTrait::new(
/// shape: array![2, 2, 2].span(), data: array![0, 1, 2, 3, 4, 5, 6, 7].span(),
/// );
///
- /// // We can call `max` function as follows.
- /// return tensor.max();
+ /// // We can call `max_in_tensor` function as follows.
+ /// return tensor.max_in_tensor();
/// }
/// >>> 7
/// ```
///
- fn max(self: @Tensor) -> T;
+ fn max_in_tensor(self: @Tensor) -> T;
+ /// # tensor.max
+ ///
+ /// ```rust
+ /// fn max(tensors: Span>) -> Tensor;
+ /// ```
+ ///
+ /// Returns the element-wise maximum values from a list of input tensors
+ /// The input tensors must have either:
+ /// * Exactly the same shape
+ /// * The same number of dimensions and the length of each dimension is either a common length or 1.
+ ///
+ /// ## Args
+ ///
+ /// * `tensors`(` Span>,`) - Array of the input tensors
+ ///
+ /// ## Returns
+ ///
+ /// A new `Tensor` containing the element-wise maximum values
+ ///
+ /// ## Panics
+ ///
+ /// * Panics if tensor array is empty
+ /// * Panics if the shapes are not equal or broadcastable
+ ///
+ /// ## Examples
+ ///
+ /// Case 1: Process tensors with same shape
+ ///
+ /// ```rust
+ /// use array::{ArrayTrait, SpanTrait};
+ ///
+ /// use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+ ///
+ /// fn max_example() -> Tensor {
+ /// let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ /// let tensor2 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 3, 1, 2].span(),);
+ /// let result = TensorTrait::max(tensors: array![tensor1, tensor2].span());
+ /// return result;
+ /// }
+ /// >>> [0, 3, 2, 3]
+ ///
+ /// result.shape
+ /// >>> (2, 2)
+ /// ```
+ ///
+ /// Case 2: Process tensors with different shapes
+ ///
+ /// ```rust
+ /// use array::{ArrayTrait, SpanTrait};
+ ///
+ /// use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+ ///
+ /// fn max_example() -> Tensor {
+ /// let tensor1 = TensorTrait::new(shape: array![2, 2].span(), data: array![0, 1, 2, 3].span(),);
+ /// let tensor2 = TensorTrait::new(shape: array![1, 2].span(), data: array![1, 4].span(),);
+ /// let result = TensorTrait::max(tensors: array![tensor1, tensor2].span());
+ /// return result;
+ /// }
+ /// >>> [1, 4, 2, 4]
+ ///
+ /// result.shape
+ /// >>> (2, 2)
+ /// ```
+ ///
+ fn max(tensors: Span>) -> Tensor;
/// # tensor.stride
///
/// ```rust
@@ -2405,6 +2539,111 @@ trait TensorTrait {
fn dequantize_linear(
self: @Tensor, x_scale: @Tensor, x_zero_point: @Tensor
) -> Tensor::;
+ /// # tensor.qlinear_matmul
+ ///
+ /// ```rust
+ /// fn qlinear_matmul(self: @Tensor, a_scale: @Tensor, a_zero_point: @Tensor, b: @Tensor, b_scale: @Tensor, b_zero_point: @Tensor, y_scale: @Tensor, y_zero_point: @Tensor) -> Tensor::;
+ /// ```
+ ///
+ /// Multiplies quantized Tensors
+ ///
+ /// It consumes two quantized input tensors, their scales and zero points, scale and zero point of output, and computes the quantized output.
+ /// The quantization formula is y = saturate((x / y_scale) + y_zero_point).
+ /// It perfoms the multiplication of the two vectors once dequantized. If either argument is N-D, N > 2, it is treated as a stack of matrices residing in the last two indexes.
+ /// Then return the quantization of the result of the multiplication.
+ /// Scale and zero point must have same shape and the same type. They must be either scalar (per tensor) or N-D tensor (per row for 'a' and per column for 'b').
+ /// Scalar refers to per tensor quantization whereas N-D refers to per row or per column quantization.
+ ///
+ /// ## Args
+ ///
+ /// * `self`(`@Tensor`) - The first tensor to be multiplied (a).
+ /// * `a_scale`(`@Tensor`) - Scale for input `a`.
+ /// * `a_zero_point`(`@Tensor`) - Zero point for input `a`.
+ /// * `b`(`@Tensor`) - The second tensor to be multiplied
+ /// * `b_scale`(`@Tensor`) - Scale for input `b`.
+ /// * `b_zero_point`(`@Tensor`) - Zero point for input `b`.
+ /// * `y_scale`(`@Tensor`) - Scale for outut.
+ /// * `y_zero_point`(`@Tensor`) - Zero point for output.
+ ///
+ /// ## Returns
+ ///
+ /// A new `Tensor`, containing the quantized result of the multiplication of the dequantized inputs.
+ ///
+ /// ## Type Constraints
+ ///
+ /// u32 tensor, not supported.
+ ///
+ /// ## Example
+ ///
+ /// ```rust
+ /// use array::{ArrayTrait, SpanTrait};
+ ///
+ /// use orion::operators::tensor::{TensorTrait, Tensor, I8Tensor, FP16x16Tensor};
+ /// use orion::numbers::{i8, FP16x16, FP16x16Impl, IntegerTrait, FixedTrait};
+ /// fn qlinear_matmul_example() -> Tensor {
+ /// let a = TensorTrait::<
+ /// i8
+ /// >::new(
+ /// shape: array![2, 3].span(),
+ /// data: array![
+ /// IntegerTrait::::new(3_u8, false),
+ /// IntegerTrait::::new(4_u8, false),
+ /// IntegerTrait::::new(5_u8, false),
+ /// IntegerTrait::::new(2_u8, false),
+ /// IntegerTrait::::new(4_u8, false),
+ /// IntegerTrait::::new(3_u8, false)
+ /// ]
+ /// .span(),
+ /// );
+ /// let b = TensorTrait::<
+ /// i8
+ /// >::new(
+ /// shape: array![3, 1].span(),
+ /// data: array![
+ /// IntegerTrait::::new(4_u8, false),
+ /// IntegerTrait::::new(8_u8, false),
+ /// IntegerTrait::::new(4_u8, false)
+ /// ]
+ /// .span(),
+ /// );
+ ///
+ /// let a_scale = TensorTrait::<
+ /// FP16x16
+ /// >::new(shape: array![1].span(), data: array![FixedTrait::::new(131072, false)].span(),);
+ /// let a_zero_point = TensorTrait::<
+ /// FP16x16
+ /// >::new(shape: array![1].span(), data: array![FixedTrait::::new(65536, false)].span(),);
+ /// let b_scale = TensorTrait::<
+ /// FP16x16
+ /// >::new(shape: array![1].span(), data: array![FixedTrait::::new(16384, false)].span(),);
+ /// let b_zero_point = TensorTrait::<
+ /// FP16x16
+ /// >::new(shape: array![1].span(), data: array![FixedTrait::::new(0, false)].span(),);
+ ///
+ /// let y_scale = TensorTrait::<
+ /// FP16x16
+ /// >::new(shape: array![1].span(), data: array![FixedTrait::::new(393216, false)].span(),);
+ /// let y_zero_point = TensorTrait::<
+ /// FP16x16
+ /// >::new(shape: array![1].span(), data: array![FixedTrait::::new(655360, false)].span(),);
+ ///
+ /// return a
+ /// .qlinear_matmul(
+ /// @a_scale, @a_zero_point, @b, @b_scale, @b_zero_point, @y_scale, @y_zero_point
+ /// );
+ /// }
+ /// >>> [14, 13]
+ /// ```
+ fn qlinear_matmul(
+ self: @Tensor,
+ a_scale: @Tensor,
+ a_zero_point: @Tensor,
+ b: @Tensor,
+ b_scale: @Tensor,
+ b_zero_point: @Tensor,
+ y_scale: @Tensor,
+ y_zero_point: @Tensor
+ ) -> Tensor::;
/// # tensor.slice
///
/// ```rust
@@ -2868,6 +3107,111 @@ trait TensorTrait {
/// ```
///
fn where(self: @Tensor, x: @Tensor, y: @Tensor) -> Tensor;
+ /// #tensor.round
+ ///
+ /// ```rust
+ /// fn round(self: @Tensor) -> Tensor;
+ /// ```
+ ///
+ /// Computes the round value of all elements in the input tensor.
+ ///
+ /// ## Args
+ ///
+ /// * `self`(`@Tensor`) - The input tensor.
+ ///
+ ///
+ /// ## Returns
+ ///
+ /// A new `Tensor` of the same shape as the input tensor with
+ /// the round value of all elements in the input tensor.
+ ///
+ /// ## Example
+ ///
+ /// ```rust
+ /// use array::{ArrayTrait, SpanTrait};
+ ///
+ /// use orion::operators::tensor::{TensorTrait, Tensor, FP16x16Tensor};
+ /// use orion::numbers::{FixedTrait, FP16x16};
+ ///
+ /// fn round_example() -> Tensor {
+ /// let tensor = TensorTrait::::new(
+ /// shape: array![3].span(),
+ /// data: array![
+ /// FixedTrait::new(190054, false), // 2.9
+ /// ]
+ /// .span(),
+ /// );
+ ///
+ /// return tensor.round();
+ /// }
+ /// >>> [3]
+ /// ```
+ ///
+ fn round(self: @Tensor) -> Tensor;
+ /// # tensor.scatter
+ ///
+ /// ```rust
+ /// fn scatter(self: @Tensor, updates: Tensor, indices: Tensor, axis: Option, 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.
+ /// * `axis`(`Option`) - Axis to scatter on. Default: axis=0.
+ /// * `reduction`(`Option`) - Reduction operation. Default: reduction='none'.
+ ///
+ /// ## Panics
+ ///
+ /// * Panics if index values are not within bounds [-s, s-1] along axis of size s.
+ ///
+ /// ## Returns
+ ///
+ /// A new `Tensor` .
+ ///
+ /// ## Example
+ ///
+ /// ```rust
+ /// use array::{ArrayTrait, SpanTrait};
+ ///
+ /// use orion::operators::tensor::{TensorTrait, Tensor, U32Tensor};
+ ///
+ /// fn scatter_example() -> Tensor {
+ /// let tensor = TensorTrait::::new(
+ /// shape: array![3, 5].span(),
+ /// data: array![[ 0, 0, 0, 0, 0],
+ /// [ 0, 0, 0, 0, 0],
+ /// [ 0, 0, 0, 0, 0]].span(),
+ /// );
+ /// let updates = TensorTrait::::new(
+ /// shape: array![3, 3].span(),
+ /// data: array![[ 1, 2, 3],
+ /// [ 4, 5, 6],
+ /// [ 7, 8, 9]].span(),
+ /// );
+ /// let indices = TensorTrait::::new(
+ /// shape: array![3, 3].span(),
+ /// data: array![[ 0, 1, 2],
+ /// [ 2, 0, 1],
+ /// [ 1, 0, 1]].span(),
+ /// );
+ ///
+ /// return tensor.scatter(
+ /// updates: updates
+ /// indices: indices,
+ /// axis: Option::None(()),
+ /// reduction: Option::None(()),
+ /// );
+ /// }
+ /// >>> [[ 1, 8, 0, 0, 0],
+ /// [ 7, 2, 9, 0, 0],
+ /// [ 4, 0, 3, 0, 0]]
+ /// ```
+ ///
+ fn scatter(self: @Tensor, updates: Tensor, indices: Tensor, axis: Option, reduction: Option) -> Tensor;
}
/// Cf: TensorTrait::new docstring
diff --git a/src/operators/tensor/implementations/tensor_fp16x16.cairo b/src/operators/tensor/implementations/tensor_fp16x16.cairo
index f726c8d1e..508134a2f 100644
--- a/src/operators/tensor/implementations/tensor_fp16x16.cairo
+++ b/src/operators/tensor/implementations/tensor_fp16x16.cairo
@@ -20,12 +20,20 @@ impl FP16x16Tensor of TensorTrait {
*at_tensor(self, indices)
}
- fn min(self: @Tensor) -> FP16x16 {
- math::min::min_in_tensor::(*self.data)
+ fn min_in_tensor(self: @Tensor) -> FP16x16 {
+ math::min_in_tensor::min_in_tensor::(*self.data)
}
- fn max(self: @Tensor) -> FP16x16 {
- math::max::max_in_tensor(*self.data)
+ fn min(tensors: Span>) -> Tensor {
+ math::min::min(tensors)
+ }
+
+ fn max_in_tensor(self: @Tensor) -> FP16x16 {
+ math::max_in_tensor::max_in_tensor(*self.data)
+ }
+
+ fn max(tensors: Span>) -> Tensor {
+ math::max::max(tensors)
}
fn stride(self: @Tensor) -> Span {
@@ -198,6 +206,30 @@ impl FP16x16Tensor of TensorTrait {
quantization::dequantize_linear::dequantize_linear(self, x_scale, x_zero_point)
}
+ fn qlinear_matmul(
+ self: @Tensor,
+ a_scale: @Tensor,
+ a_zero_point: @Tensor,
+ b: @Tensor,
+ b_scale: @Tensor,
+ b_zero_point: @Tensor,
+ y_scale: @Tensor,
+ y_zero_point: @Tensor
+ ) -> Tensor:: {
+ quantization::qlinear_matmul::qlinear_matmul(
+ self,
+ a_scale,
+ a_zero_point,
+ b,
+ b_scale,
+ b_zero_point,
+ y_scale,
+ y_zero_point,
+ NumberTrait::new_unscaled(128, true),
+ NumberTrait::new_unscaled(127, false)
+ )
+ }
+
fn slice(
self: @Tensor,
starts: Span,
@@ -245,6 +277,17 @@ impl FP16x16Tensor of TensorTrait {
fn where(self: @Tensor, x: @Tensor, y: @Tensor) -> Tensor {
math::where::where(self, x, y)
}
+
+ fn round(self: @Tensor) -> Tensor {
+ math::round::round(*self)
+ }
+
+ fn scatter(
+ self: @Tensor, updates: Tensor, indices: Tensor, axis: Option, reduction: Option)
+ -> Tensor {
+ math::scatter::scatter(self, updates, indices, axis, reduction)
+ }
+
}
/// Implements addition for `Tensor` using the `Add` trait.
diff --git a/src/operators/tensor/implementations/tensor_fp16x16wide.cairo b/src/operators/tensor/implementations/tensor_fp16x16wide.cairo
index c4f2de169..d87f6dd9d 100644
--- a/src/operators/tensor/implementations/tensor_fp16x16wide.cairo
+++ b/src/operators/tensor/implementations/tensor_fp16x16wide.cairo
@@ -20,12 +20,20 @@ impl FP16x16WTensor of TensorTrait {
*at_tensor(self, indices)
}
- fn min(self: @Tensor) -> FP16x16W {
- math::min::min_in_tensor::(*self.data)
+ fn min_in_tensor(self: @Tensor) -> FP16x16W {
+ math::min_in_tensor::min_in_tensor::(*self.data)
}
- fn max(self: @Tensor) -> FP16x16W {
- math::max::max_in_tensor(*self.data)
+ fn min(tensors: Span>) -> Tensor {
+ math::min::min(tensors)
+ }
+
+ fn max_in_tensor(self: @Tensor) -> FP16x16W {
+ math::max_in_tensor::max_in_tensor(*self.data)
+ }
+
+ fn max(tensors: Span>) -> Tensor {
+ math::max::max(tensors)
}
fn stride(self: @Tensor) -> Span {
@@ -204,6 +212,19 @@ impl FP16x16WTensor of TensorTrait {
panic(array!['not supported!'])
}
+ fn qlinear_matmul(
+ self: @Tensor,
+ a_scale: @Tensor,
+ a_zero_point: @Tensor