.. _op_ai_onnx_ScatterElements: ScatterElements =============== - **Domain**: ``ai.onnx`` - **Since version**: 18 ScatterElements takes three inputs ``data``, ``updates``, and ``indices`` of the same rank r >= 1 and an optional attribute axis that identifies an axis of ``data`` (by default, the outer-most axis, that is axis 0). The output of the operation is produced by creating a copy of the input ``data``, and then updating its value to values specified by ``updates`` at specific index positions specified by ``indices``. Its output shape is the same as the shape of ``data``. For each entry in ``updates``, the target index in ``data`` is obtained by combining the corresponding entry in ``indices`` with the index of the entry itself: the index-value for dimension = axis is obtained from the value of the corresponding entry in ``indices`` and the index-value for dimension != axis is obtained from the index of the entry itself. ``reduction`` allows specification of an optional reduction operation, which is applied to all values in ``updates`` tensor into ``output`` at the specified ``indices``. In cases where ``reduction`` is set to "none", indices should not have duplicate entries: that is, if idx1 != idx2, then indices[idx1] != indices[idx2]. For instance, in a 2-D tensor case, the update corresponding to the [i][j] entry is performed as below: .. code-block:: output[indices[i][j]][j] = updates[i][j] if axis = 0, output[i][indices[i][j]] = updates[i][j] if axis = 1, When ``reduction`` is set to some reduction function ``f``, the update corresponding to the [i][j] entry is performed as below: .. code-block:: output[indices[i][j]][j] = f(output[indices[i][j]][j], updates[i][j]) if axis = 0, output[i][indices[i][j]] = f(output[i][indices[i][j]], updates[i][j]) if axis = 1, where the ``f`` is ``+``, ``*``, ``max`` or ``min`` as specified. This operator is the inverse of GatherElements. It is similar to Torch's Scatter operation. (Opset 18 change): Adds max/min to the set of allowed reduction ops. Example 1: .. code-block:: data = [ [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], ] indices = [ [1, 0, 2], [0, 2, 1], ] updates = [ [1.0, 1.1, 1.2], [2.0, 2.1, 2.2], ] output = [ [2.0, 1.1, 0.0] [1.0, 0.0, 2.2] [0.0, 2.1, 1.2] ] Example 2: .. code-block:: data = [[1.0, 2.0, 3.0, 4.0, 5.0]] indices = [[1, 3]] updates = [[1.1, 2.1]] axis = 1 output = [[1.0, 1.1, 3.0, 2.1, 5.0]] **Inputs** - **data** (*T*): Tensor of rank r >= 1. - **indices** (*Tind*): Tensor of int32/int64 indices, of r >= 1 (same rank as input). All index values are expected to be within bounds [-s, s-1] along axis of size s. It is an error if any of the index values are out of bounds. - **updates** (*T*): Tensor of rank r >=1 (same rank and shape as indices) **Outputs** - **output** (*T*): Tensor of rank r >= 1 (same rank as input). **Attributes** - **axis** (*int*): Which axis to scatter on. Negative value means counting dimensions from the back. Accepted range is [-r, r-1] where r = rank(data). - **reduction** (*string*): Type of reduction to apply: none (default), add, mul, max, min. 'none': no reduction applied. 'add': reduction using the addition operation. 'mul': reduction using the multiplication operation.'max': reduction using the maximum operation.'min': reduction using the minimum operation. **Type Constraints** - **T**: Input and output types can be of any tensor type. Allowed types: tensor(bfloat16), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8). - **Tind**: Constrain indices to integer types Allowed types: tensor(int32), tensor(int64). Examples -------- **test_cc_scatter_elements_with_axis** .. code-block:: text Node: ScatterElements(data, indices, updates) -> (y) Attributes: axis = 1 .. code-block:: text Inputs: data: shape=(1, 5), dtype=float32 [[1., 2., 3., 4., 5.]] indices: shape=(1, 2), dtype=int64 [[1, 3]] updates: shape=(1, 2), dtype=float32 [[1.1, 2.1]] Outputs: y: shape=(1, 5), dtype=float32 [[1. , 1.1, 3. , 2.1, 5. ]] **test_cc_scatter_elements_with_duplicate_indices** .. code-block:: text Node: ScatterElements(data, indices, updates) -> (y) Attributes: axis = 1 reduction = "add" .. code-block:: text Inputs: data: shape=(1, 5), dtype=float32 [[1., 2., 3., 4., 5.]] indices: shape=(1, 2), dtype=int64 [[1, 1]] updates: shape=(1, 2), dtype=float32 [[1.1, 2.1]] Outputs: y: shape=(1, 5), dtype=float32 [[1. , 5.2, 3. , 4. , 5. ]] **test_cc_scatter_elements_with_negative_indices** .. code-block:: text Node: ScatterElements(data, indices, updates) -> (y) Attributes: axis = 1 .. code-block:: text Inputs: data: shape=(1, 5), dtype=float32 [[1., 2., 3., 4., 5.]] indices: shape=(1, 2), dtype=int64 [[ 1, -3]] updates: shape=(1, 2), dtype=float32 [[1.1, 2.1]] Outputs: y: shape=(1, 5), dtype=float32 [[1. , 1.1, 2.1, 4. , 5. ]] **test_cc_scatter_elements_with_reduction_max** .. code-block:: text Node: ScatterElements(data, indices, updates) -> (y) Attributes: axis = 1 reduction = "max" .. code-block:: text Inputs: data: shape=(1, 5), dtype=float32 [[1., 2., 3., 4., 5.]] indices: shape=(1, 2), dtype=int64 [[1, 1]] updates: shape=(1, 2), dtype=float32 [[1.1, 2.1]] Outputs: y: shape=(1, 5), dtype=float32 [[1. , 2.1, 3. , 4. , 5. ]] **test_cc_scatter_elements_with_reduction_min** .. code-block:: text Node: ScatterElements(data, indices, updates) -> (y) Attributes: axis = 1 reduction = "min" .. code-block:: text Inputs: data: shape=(1, 5), dtype=float32 [[1., 2., 3., 4., 5.]] indices: shape=(1, 2), dtype=int64 [[1, 1]] updates: shape=(1, 2), dtype=float32 [[1.1, 2.1]] Outputs: y: shape=(1, 5), dtype=float32 [[1. , 1.1, 3. , 4. , 5. ]] **test_cc_scatter_elements_with_reduction_mul** .. code-block:: text Node: ScatterElements(data, indices, updates) -> (y) Attributes: axis = 1 reduction = "mul" .. code-block:: text Inputs: data: shape=(1, 5), dtype=float32 [[1., 2., 3., 4., 5.]] indices: shape=(1, 2), dtype=int64 [[1, 1]] updates: shape=(1, 2), dtype=float32 [[1.1, 2.1]] Outputs: y: shape=(1, 5), dtype=float32 [[1. , 4.62, 3. , 4. , 5. ]] **test_cc_scatter_elements_without_axis** .. code-block:: text Node: ScatterElements(data, indices, updates) -> (y) .. code-block:: text Inputs: data: shape=(3, 3), dtype=float32 [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]] indices: shape=(2, 3), dtype=int64 [[1, 0, 2], [0, 2, 1]] updates: shape=(2, 3), dtype=float32 [[1. , 1.1, 1.2], [2. , 2.1, 2.2]] Outputs: y: shape=(3, 3), dtype=float32 [[2. , 1.1, 0. ], [1. , 0. , 2.2], [0. , 2.1, 1.2]] Differences with previous version (16) -------------------------------------- **SchemaDiff**: ``ScatterElements`` (domain ``'ai.onnx'``) * old version: 16 * new version: 18 * breaking: no **Documentation:** * line similarity: 0.89 (+6/-8 lines) .. code-block:: diff --- ScatterElements v16 +++ ScatterElements v18 @@ -21,18 +21,16 @@ output[indices[i][j]][j] = updates[i][j] if axis = 0, output[i][indices[i][j]] = updates[i][j] if axis = 1, ``` -When `reduction` is set to "add", the update corresponding to the [i][j] entry is performed as below: +When `reduction` is set to some reduction function `f`, the update corresponding to the [i][j] entry is performed as below: ``` -output[indices[i][j]][j] += updates[i][j] if axis = 0, -output[i][indices[i][j]] += updates[i][j] if axis = 1, +output[indices[i][j]][j] = f(output[indices[i][j]][j], updates[i][j]) if axis = 0, +output[i][indices[i][j]] = f(output[i][indices[i][j]], updates[i][j]) if axis = 1, ``` -When `reduction` is set to "mul", the update corresponding to the [i][j] entry is performed as below: -``` -output[indices[i][j]][j] *= updates[i][j] if axis = 0, -output[i][indices[i][j]] *= updates[i][j] if axis = 1, -``` +where the `f` is `+`, `*`, `max` or `min` as specified. This operator is the inverse of GatherElements. It is similar to Torch's Scatter operation. + +(Opset 18 change): Adds max/min to the set of allowed reduction ops. Example 1: ``` Version History --------------- - :doc:`Version 16 ` - :doc:`Version 13 ` - :doc:`Version 11 `