ScatterND

ScatterND - 29

Version

  • name: ScatterND (GitHub)

  • domain: main

  • since_version: 29

  • function: False

  • support_level: SupportType.COMMON

  • shape inference: True

This version of the operator has been available since version 29.

Summary

ScatterND takes data of rank r >= 1, indices of rank q >= 1, and updates of rank q + r - k - 1, where k = indices.shape[-1] and 0 <= k <= r. data and updates must have the same element type. The output has the same shape and element type as data.

Each entry in indices is a k-tuple indexing the first k dimensions of data. For a dimension of size s, index values must be in [-s, s-1]. A negative index i identifies the same position as i + s. Each tuple addresses a single element when k = r or a slice of the trailing r - k dimensions otherwise. When k = 0, each tuple addresses the entire tensor. The shape of updates must equal indices.shape[:-1] + data.shape[k:]. For every coordinate i in indices.shape[:-1], updates[i] is scattered to output[tuple(indices[i])].

When reduction is “none”, updates replace the addressed elements or slices, and multiple tuples must not address the same location. For other reductions, group all updates addressing the same location and reduce elementwise over each group, including the original data value exactly once. Untouched elements retain their original data values. “add”, “mul”, “max”, and “min” compute the sum, product, maximum, and minimum, respectively.

For “mean”, consider an output element addressed by N >= 1 updates. Let x be its original data value and u_1, …, u_N be those update values. Its output value is:

output = (x + u_1 + ... + u_N) / (N + 1)

The “mean” reduction supports integer and floating-point tensors, but not boolean, string, or complex tensors. Integer results are rounded toward negative infinity. Floating-point results are converted to the input element type after division.

A floating-point mean is NaN if x, u_1, …, u_N contain a NaN or infinities of both signs. Otherwise, if any of these values is infinite, the mean is that infinity.

Example:

data = [[1.0, 2.0], [3.0, 4.0]]
indices = [[0], [0]]
updates = [[3.0, 4.0], [5.0, 6.0]]
reduction = &#34;mean&#34;
output = [[3.0, 4.0], [3.0, 4.0]]

Opset 29 adds support for int32 indices; int64 indices remain supported.

Attributes

  • reduction - STRING (default is none):

    Type of reduction to apply: none (default), add, mul, max, min, mean. ‘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. ‘mean’: arithmetic mean of all contributions to each output element.

Inputs

  • data (heterogeneous) - T:

    Tensor of rank r >= 1.

  • indices (heterogeneous) - Tind:

    Tensor of rank q >= 1.

  • updates (heterogeneous) - T:

    Tensor of rank q + r - indices_shape[-1] - 1.

Outputs

  • output (heterogeneous) - T:

    Tensor of rank r >= 1.

Type Constraints

  • T in ( 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) ):

    Constrain input and output types to any tensor type.

  • Tind in ( tensor(int32), tensor(int64) ):

    Constrain indices to int32 or int64 tensors.

Examples

_batched_token_add_int32_indices

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND", ["data", "indices", "updates"], ["output"], reduction="add"
)
data = np.zeros((2, 3, 2), dtype=np.float32)
indices = np.array([[0, 1], [1, 0], [0, 1], [1, -1]], dtype=np.int32)
updates = np.array([[1, 2], [3, 4], [5, 6], [7, 8]], dtype=np.float32)
output = np.array(
    [[[0, 0], [6, 8], [0, 0]], [[3, 4], [0, 0], [7, 8]]],
    dtype=np.float32,
)
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd_batched_token_add_int32_indices",
)

_scatternd_mean

import numpy as np
import onnx

data = np.array([[1, 2], [3, 4]], dtype=np.float32)
indices = np.array([[0], [-2]], dtype=np.int64)
updates = np.array([[3, 4], [5, 6]], dtype=np.float32)
node = onnx.helper.make_node(
    "ScatterND",
    ["data", "indices", "updates"],
    ["y"],
    reduction="mean",
)
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[np.array([[3, 4], [3, 4]], dtype=np.float32)],
    name="test_scatternd_mean",
    opset_imports=[onnx.helper.make_opsetid("", 29)],
)

_scatternd_mean_integer

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    ["data", "indices", "updates"],
    ["y"],
    reduction="mean",
)
data = np.array([[-2, 8], [9, 10]], dtype=np.int64)
indices = np.array([[0, 0], [0, 0], [0, 1]], dtype=np.int64)
updates = np.array([-3, -3, 5], dtype=np.int64)
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[np.array([[-3, 6], [9, 10]], dtype=np.int64)],
    name="test_scatternd_mean_integer",
    opset_imports=[onnx.helper.make_opsetid("", 29)],
)

_scatternd

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    inputs=["data", "indices", "updates"],
    outputs=["y"],
)
data = np.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]],
    ],
    dtype=np.float32,
)
indices = np.array([[0], [2]], dtype=np.int64)
updates = np.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]],
    ],
    dtype=np.float32,
)
# Expecting output as np.array(
#    [[[5, 5, 5, 5], [6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8]],
#     [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
#     [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
#     [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]], dtype=np.float32)
output = scatter_nd_impl(data, indices, updates)
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd",
)

_scatternd_add

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    inputs=["data", "indices", "updates"],
    outputs=["y"],
    reduction="add",
)
data = np.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]],
    ],
    dtype=np.float32,
)
indices = np.array([[0], [0]], dtype=np.int64)
updates = np.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]],
    ],
    dtype=np.float32,
)
# Expecting output as np.array(
#    [[[7, 8, 9, 10], [13, 14, 15, 16], [18, 17, 16, 15], [16, 15, 14, 13]],
#     [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
#     [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
#     [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]], dtype=np.float32)
output = scatter_nd_impl(data, indices, updates, reduction="add")
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd_add",
)

_scatternd_multiply

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    inputs=["data", "indices", "updates"],
    outputs=["y"],
    reduction="mul",
)
data = np.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]],
    ],
    dtype=np.float32,
)
indices = np.array([[0], [0]], dtype=np.int64)
updates = np.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]],
    ],
    dtype=np.float32,
)
# Expecting output as np.array(
#    [[[5, 10, 15, 20], [60, 72, 84, 96], [168, 147, 126, 105], [128, 96, 64, 32]],
#     [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
#     [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
#     [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]], dtype=np.float32)
output = scatter_nd_impl(data, indices, updates, reduction="mul")
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd_multiply",
)

_scatternd_max

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    inputs=["data", "indices", "updates"],
    outputs=["y"],
    reduction="max",
)
data = np.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]],
    ],
    dtype=np.float32,
)
indices = np.array([[0], [0]], dtype=np.int64)
updates = np.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]],
    ],
    dtype=np.float32,
)
# Expecting output as np.array(
#    [[[5, 5, 5, 5], [6, 6, 7, 8], [8, 7, 7, 7], [8, 8 ,8, 8]],
#     [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
#     [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
#     [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]], dtype=np.float32)
output = scatter_nd_impl(data, indices, updates, reduction="max")
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd_max",
)

_scatternd_min

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    inputs=["data", "indices", "updates"],
    outputs=["y"],
    reduction="min",
)
data = np.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]],
    ],
    dtype=np.float32,
)
indices = np.array([[0], [0]], dtype=np.int64)
updates = np.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]],
    ],
    dtype=np.float32,
)
# Expecting output as np.array(
#    [[[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 3, 2, 1]],
#     [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
#     [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
#     [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]], dtype=np.float32)
output = scatter_nd_impl(data, indices, updates, reduction="min")
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd_min",
)

_scatternd_max_with_element_indices

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    inputs=["data", "indices", "updates"],
    outputs=["y"],
    reduction="max",
)
data = np.array([[1, 2], [3, 4]], dtype=np.float32)
# Indices address individual elements (index rank == data rank),
# which exercises the reduction at the element level.
indices = np.array([[0, 0], [1, 1]], dtype=np.int64)
updates = np.array([5, 1], dtype=np.float32)
# Expecting output as np.array([[5, 2], [3, 4]], dtype=np.float32)
output = scatter_nd_impl(data, indices, updates, reduction="max")
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd_max_with_element_indices",
)

_scatternd_min_with_element_indices

import numpy as np
import onnx

node = onnx.helper.make_node(
    "ScatterND",
    inputs=["data", "indices", "updates"],
    outputs=["y"],
    reduction="min",
)
data = np.array([[1, 2], [3, 4]], dtype=np.float32)
indices = np.array([[0, 0], [1, 1]], dtype=np.int64)
updates = np.array([5, 1], dtype=np.float32)
# Expecting output as np.array([[1, 2], [3, 1]], dtype=np.float32)
output = scatter_nd_impl(data, indices, updates, reduction="min")
expect(
    node,
    inputs=[data, indices, updates],
    outputs=[output],
    name="test_scatternd_min_with_element_indices",
)

ScatterND - 18

Version

  • name: ScatterND (GitHub)

  • domain: main

  • since_version: 18

  • function: False

  • support_level: SupportType.COMMON

  • shape inference: True

This version of the operator has been available since version 18.

Summary

ScatterND takes three inputs data tensor of rank r >= 1, indices tensor of rank q >= 1, and updates tensor of rank q + r - indices.shape[-1] - 1. 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.

indices is an integer tensor. Let k denote indices.shape[-1], the last dimension in the shape of indices. indices is treated as a (q-1)-dimensional tensor of k-tuples, where each k-tuple is a partial-index into data. Hence, k can be a value at most the rank of data. When k equals rank(data), each update entry specifies an update to a single element of the tensor. When k is less than rank(data) each update entry specifies an update to a slice of the tensor. For each tuple component i, 0 &lt;= i &lt; k, the index must satisfy -data.shape[i] &lt;= indices[...,i] &lt; data.shape[i]. Negative indices count backwards from the end of the corresponding dimension. An out-of-bounds index is an error.

updates is treated as a (q-1)-dimensional tensor of replacement-slice-values. Thus, the first (q-1) dimensions of updates.shape must match the first (q-1) dimensions of indices.shape. The remaining dimensions of updates correspond to the dimensions of the replacement-slice-values. Each replacement-slice-value is a (r-k) dimensional tensor, corresponding to the trailing (r-k) dimensions of data. Thus, the shape of updates must equal indices.shape[0:q-1] ++ data.shape[k:r], where ++ denotes the concatenation of shapes.

The output is calculated via the following equation:

output = np.copy(data)
update_indices = indices.shape[:-1]
for idx in np.ndindex(update_indices):
    output[tuple(indices[idx])] = updates[idx]

The order of iteration in the above loop is not specified.

reduction allows specification of an optional reduction operation, which is applied to all values in updates tensor into output at the specified indices. When reduction is “none”, no two index tuples may select the same output element or slice, including through equivalent positive and negative indices. This ensures that the output value does not depend on the iteration order. When reduction is set to some reduction function f, output is calculated as follows:

output = np.copy(data)
update_indices = indices.shape[:-1]
for idx in np.ndindex(update_indices):
    output[tuple(indices[idx])] = f(output[tuple(indices[idx])], updates[idx])

where the f is +, *, max or min as specified.

(Opset 18 change): Adds max/min to the set of allowed reduction ops.

Example 1:

data    = [1, 2, 3, 4, 5, 6, 7, 8]
indices = [[4], [3], [1], [7]]
updates = [9, 10, 11, 12]
output  = [1, 11, 3, 10, 9, 6, 7, 12]

Example 2:

data    = [[[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]]]
indices = [[0], [2]]
updates = [[[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]]]
output  = [[[5, 5, 5, 5], [6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8]],
            [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
            [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
            [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]]

Attributes

  • reduction - STRING (default is none):

    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.

Inputs

  • data (heterogeneous) - T:

    Tensor of rank r >= 1.

  • indices (heterogeneous) - tensor(int64):

    Tensor of rank q >= 1.

  • updates (heterogeneous) - T:

    Tensor of rank q + r - indices_shape[-1] - 1.

Outputs

  • output (heterogeneous) - T:

    Tensor of rank r >= 1.

Type Constraints

  • T in ( 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) ):

    Constrain input and output types to any tensor type.

ScatterND - 16

Version

  • name: ScatterND (GitHub)

  • domain: main

  • since_version: 16

  • function: False

  • support_level: SupportType.COMMON

  • shape inference: True

This version of the operator has been available since version 16.

Summary

ScatterND takes three inputs data tensor of rank r >= 1, indices tensor of rank q >= 1, and updates tensor of rank q + r - indices.shape[-1] - 1. 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.

indices is an integer tensor. Let k denote indices.shape[-1], the last dimension in the shape of indices. indices is treated as a (q-1)-dimensional tensor of k-tuples, where each k-tuple is a partial-index into data. Hence, k can be a value at most the rank of data. When k equals rank(data), each update entry specifies an update to a single element of the tensor. When k is less than rank(data) each update entry specifies an update to a slice of the tensor. Index values are allowed to be negative, as per the usual convention for counting backwards from the end, but are expected in the valid range.

updates is treated as a (q-1)-dimensional tensor of replacement-slice-values. Thus, the first (q-1) dimensions of updates.shape must match the first (q-1) dimensions of indices.shape. The remaining dimensions of updates correspond to the dimensions of the replacement-slice-values. Each replacement-slice-value is a (r-k) dimensional tensor, corresponding to the trailing (r-k) dimensions of data. Thus, the shape of updates must equal indices.shape[0:q-1] ++ data.shape[k:r], where ++ denotes the concatenation of shapes.

The output is calculated via the following equation: output = np.copy(data) update_indices = indices.shape[:-1] for idx in np.ndindex(update_indices): output[tuple(indices[idx])] = updates[idx] The order of iteration in the above loop is not specified. In particular, indices should not have duplicate entries: that is, if idx1 != idx2, then indices[idx1] != indices[idx2]. This ensures that the output value does not depend on the iteration order.

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]. This ensures that the output value does not depend on the iteration order. When reduction is set to “add”, output is calculated as follows: output = np.copy(data) update_indices = indices.shape[:-1] for idx in np.ndindex(update_indices): output[tuple(indices[idx])] += updates[idx] When reduction is set to “mul”, output is calculated as follows: output = np.copy(data) update_indices = indices.shape[:-1] for idx in np.ndindex(update_indices): output[tuple(indices[idx])] *= updates[idx] This operator is the inverse of GatherND. Example 1:

  data    = [1, 2, 3, 4, 5, 6, 7, 8]
  indices = [[4], [3], [1], [7]]
  updates = [9, 10, 11, 12]
  output  = [1, 11, 3, 10, 9, 6, 7, 12]

Example 2:

  data    = [[[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]]]
  indices = [[0], [2]]
  updates = [[[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]]]
  output  = [[[5, 5, 5, 5], [6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8]],
             [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
             [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
             [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]]

Attributes

  • reduction - STRING (default is none):

    Type of reduction to apply: none (default), add, mul. ‘none’: no reduction applied. ‘add’: reduction using the addition operation. ‘mul’: reduction using the multiplication operation.

Inputs

  • data (heterogeneous) - T:

    Tensor of rank r >= 1.

  • indices (heterogeneous) - tensor(int64):

    Tensor of rank q >= 1.

  • updates (heterogeneous) - T:

    Tensor of rank q + r - indices_shape[-1] - 1.

Outputs

  • output (heterogeneous) - T:

    Tensor of rank r >= 1.

Type Constraints

  • T in ( 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) ):

    Constrain input and output types to any tensor type.

ScatterND - 13

Version

  • name: ScatterND (GitHub)

  • domain: main

  • since_version: 13

  • function: False

  • support_level: SupportType.COMMON

  • shape inference: True

This version of the operator has been available since version 13.

Summary

ScatterND takes three inputs data tensor of rank r >= 1, indices tensor of rank q >= 1, and updates tensor of rank q + r - indices.shape[-1] - 1. 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. Note that indices should not have duplicate entries. That is, two or more updates for the same index-location is not supported.

indices is an integer tensor. Let k denote indices.shape[-1], the last dimension in the shape of indices. indices is treated as a (q-1)-dimensional tensor of k-tuples, where each k-tuple is a partial-index into data. Hence, k can be a value at most the rank of data. When k equals rank(data), each update entry specifies an update to a single element of the tensor. When k is less than rank(data) each update entry specifies an update to a slice of the tensor. Index values are allowed to be negative, as per the usual convention for counting backwards from the end, but are expected in the valid range.

updates is treated as a (q-1)-dimensional tensor of replacement-slice-values. Thus, the first (q-1) dimensions of updates.shape must match the first (q-1) dimensions of indices.shape. The remaining dimensions of updates correspond to the dimensions of the replacement-slice-values. Each replacement-slice-value is a (r-k) dimensional tensor, corresponding to the trailing (r-k) dimensions of data. Thus, the shape of updates must equal indices.shape[0:q-1] ++ data.shape[k:r], where ++ denotes the concatenation of shapes.

The output is calculated via the following equation:

output = np.copy(data)
update_indices = indices.shape[:-1]
for idx in np.ndindex(update_indices):
    output[tuple(indices[idx])] = updates[idx]

The order of iteration in the above loop is not specified. In particular, indices should not have duplicate entries: that is, if idx1 != idx2, then indices[idx1] != indices[idx2]. This ensures that the output value does not depend on the iteration order.

This operator is the inverse of GatherND.

Example 1:

  data    = [1, 2, 3, 4, 5, 6, 7, 8]
  indices = [[4], [3], [1], [7]]
  updates = [9, 10, 11, 12]
  output  = [1, 11, 3, 10, 9, 6, 7, 12]

Example 2:

  data    = [[[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]]]
  indices = [[0], [2]]
  updates = [[[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]]]
  output  = [[[5, 5, 5, 5], [6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8]],
             [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
             [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
             [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]]

Inputs

  • data (heterogeneous) - T:

    Tensor of rank r >= 1.

  • indices (heterogeneous) - tensor(int64):

    Tensor of rank q >= 1.

  • updates (heterogeneous) - T:

    Tensor of rank q + r - indices_shape[-1] - 1.

Outputs

  • output (heterogeneous) - T:

    Tensor of rank r >= 1.

Type Constraints

  • T in ( 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) ):

    Constrain input and output types to any tensor type.

ScatterND - 11

Version

  • name: ScatterND (GitHub)

  • domain: main

  • since_version: 11

  • function: False

  • support_level: SupportType.COMMON

  • shape inference: True

This version of the operator has been available since version 11.

Summary

ScatterND takes three inputs data tensor of rank r >= 1, indices tensor of rank q >= 1, and updates tensor of rank q + r - indices.shape[-1] - 1. 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. Note that indices should not have duplicate entries. That is, two or more updates for the same index-location is not supported.

indices is an integer tensor. Let k denote indices.shape[-1], the last dimension in the shape of indices. indices is treated as a (q-1)-dimensional tensor of k-tuples, where each k-tuple is a partial-index into data. Hence, k can be a value at most the rank of data. When k equals rank(data), each update entry specifies an update to a single element of the tensor. When k is less than rank(data) each update entry specifies an update to a slice of the tensor. Index values are allowed to be negative, as per the usual convention for counting backwards from the end, but are expected in the valid range.

updates is treated as a (q-1)-dimensional tensor of replacement-slice-values. Thus, the first (q-1) dimensions of updates.shape must match the first (q-1) dimensions of indices.shape. The remaining dimensions of updates correspond to the dimensions of the replacement-slice-values. Each replacement-slice-value is a (r-k) dimensional tensor, corresponding to the trailing (r-k) dimensions of data. Thus, the shape of updates must equal indices.shape[0:q-1] ++ data.shape[k:r], where ++ denotes the concatenation of shapes.

The output is calculated via the following equation:

output = np.copy(data)
update_indices = indices.shape[:-1]
for idx in np.ndindex(update_indices):
    output[tuple(indices[idx])] = updates[idx]

The order of iteration in the above loop is not specified. In particular, indices should not have duplicate entries: that is, if idx1 != idx2, then indices[idx1] != indices[idx2]. This ensures that the output value does not depend on the iteration order.

This operator is the inverse of GatherND.

Example 1:

  data    = [1, 2, 3, 4, 5, 6, 7, 8]
  indices = [[4], [3], [1], [7]]
  updates = [9, 10, 11, 12]
  output  = [1, 11, 3, 10, 9, 6, 7, 12]

Example 2:

  data    = [[[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]]]
  indices = [[0], [2]]
  updates = [[[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]]]
  output  = [[[5, 5, 5, 5], [6, 6, 6, 6], [7, 7, 7, 7], [8, 8, 8, 8]],
             [[1, 2, 3, 4], [5, 6, 7, 8], [8, 7, 6, 5], [4, 3, 2, 1]],
             [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]],
             [[8, 7, 6, 5], [4, 3, 2, 1], [1, 2, 3, 4], [5, 6, 7, 8]]]

Inputs

  • data (heterogeneous) - T:

    Tensor of rank r >= 1.

  • indices (heterogeneous) - tensor(int64):

    Tensor of rank q >= 1.

  • updates (heterogeneous) - T:

    Tensor of rank q + r - indices_shape[-1] - 1.

Outputs

  • output (heterogeneous) - T:

    Tensor of rank r >= 1.

Type Constraints

  • T in ( 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) ):

    Constrain input and output types to any tensor type.