ScatterNDUpdate#
Versioned name: ScatterNDUpdate-15
Category: Data movement
Short description: Creates a copy of the data input tensor with updates for elements specified by indices by elements from updates according to reduction attribute.
Detailed description: The operation produces a copy of data tensor and updates its value using logic from reduction attribute, using values specified
by updates at specific index positions specified by indices. The output shape is the same as the shape of data.
If multiple indices point to the same output location then the order of updating the values is undefined.
The last dimension of indices corresponds to indices into elements if indices.shape[-1] = data.shape.rank or slices
if indices.shape[-1] < data.shape.rank.
Input updates is a tensor with shape indices.shape[:-1] + data.shape[indices.shape[-1]:].
The operation to perform between the corresponding elements is specified by reduction attribute, by default the elements of data tensor are simply overwritten by the values from updates input.
Operator ScatterNDUpdate-15 is an equivalent to following NumPy snippet:
def scatter_nd_update_15(data, indices, updates, reduction=None):
func = lambda x, y: y
if reduction == "sum":
func = lambda x, y: x + y
elif reduction == "sub":
func = lambda x, y: x - y
elif reduction == "prod":
func = lambda x, y: x * y
elif reduction == "max":
func = max
elif reduction == "min":
func = min
out = np.copy(data)
# Order of loop iteration is undefined.
for ndidx in np.ndindex(indices.shape[:-1]):
out[tuple(indices[ndidx])] = func(tuple(out[indices[ndidx]]), updates[ndidx])
return out
Example 1 that shows simple case of update with reduction set to none.:
data = [1, 2, 3, 4, 5, 6, 7, 8]
indices = [[4], [3], [1], [7], [-2], [-4]]
updates = [9, 10, 11, 12, 13, 14]
output = [1, 11, 3, 10, 4, 6, 13, 12]
Example that shows update of two slices of 4x4 shape in data, with reduction set to none:
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
Description: The type of operation to perform on the inputs.
Range of values: one of
none,sum,sub,prod,min,maxType: string
Default value:
noneRequired: no
Inputs:
1:
datatensor of arbitrary rankr>= 1 and of type T. Required.2:
indicestensor with indices of arbitrary rankq>= 1 and of type T_IND. All index valuesi_jin index entry(i_0, i_1, ...,i_k)(wherek = indices.shape[-1]) must be within bounds[-s_j, s_j - 1]wheres_j = data.shape[j].kmust be at mostr. If multiple indices point to the same output location then the order of updating the values is undefined. Negative value of index means reverse indexing and will be normalized to valuelen(data.shape[j] + index). If an index points to non-existing element then exception is raised. Required.3:
updatestensor of rankr - indices.shape[-1] + q - 1of type T. If expectedupdatesrank is 0D it can be a tensor with single element. Required.
Outputs:
1: tensor with shape equal to
datatensor of the type T.
Types
T: any numeric type. For boolean type, reduction sum, sub, prod behaves like logical OR, XOR, AND accordingly.
T_IND:
int32orint64
Example
Example 1
<layer ... reduction="none" type="ScatterNdUpdate">
<input>
<port id="0" precision="FP32"> <!-- data -->
<dim>4</dim> <!-- values: [1, 2, 3, 4] -->
</port>
<port id="1" precision="I32"> <!-- indices -->
<dim>5</dim> <!-- values: [0, 2, -3, -3, 0] -->
</port>
<port id="2" precision="FP32"> <!-- updates -->
<dim>5</dim> <!-- values: [10, 20, 30, 40, 50] -->
</port>
</input>
<output>
<port id="3" precision="FP32">
<dim>4</dim> <!-- values: [50, 20, 20, 4] -->
</port>
</output>
</layer>
Example 2
<layer ... reduction="sum" type="ScatterNdUpdate">
<input>
<port id="0" precision="FP16"> <!-- data -->
<dim>4</dim> <!-- values: [1, 2, 3, 4] -->
</port>
<port id="1" precision="I32"> <!-- indices -->
<dim>5</dim> <!-- values: [0, 2, -3, -3, 0] -->
</port>
<port id="2" precision="FP16"> <!-- updates -->
<dim>5</dim> <!-- values: [10, 20, 30, 40, 50] -->
</port>
</input>
<output>
<port id="3" precision="FP16">
<dim>4</dim> <!-- values: [61, 72, 23, 4] -->
</port>
</output>
</layer>
Example 3
<layer ... reduction="sub" type="ScatterNdUpdate">
<input>
<port id="0" precision="I32"> <!-- data -->
<dim>4</dim> <!-- values: [1, 2, 3, 4] -->
</port>
<port id="1" precision="I32"> <!-- indices -->
<dim>5</dim> <!-- values: [0, 2, -3, -3, 0] -->
</port>
<port id="2" precision="I32"> <!-- updates -->
<dim>5</dim> <!-- values: [10, 20, 30, 40, 50] -->
</port>
</input>
<output>
<port id="3" precision="I32">
<dim>4</dim> <!-- values: [-59, -68, -17, 4] -->
</port>
</output>
</layer>
Example 4
<layer ... reduction="prod" type="ScatterNdUpdate">
<input>
<port id="0" precision="FP32"> <!-- data -->
<dim>4</dim> <!-- values: [1, 2, 3, 4] -->
</port>
<port id="1" precision="I32"> <!-- indices -->
<dim>5</dim> <!-- values: [0, 2, -3, -3, 0] -->
</port>
<port id="2" precision="FP32"> <!-- updates -->
<dim>5</dim> <!-- values: [10, 20, 30, 40, 50] -->
</port>
</input>
<output>
<port id="3" precision="FP32">
<dim>4</dim> <!-- values: [500, 3600, 40, 4] -->
</port>
</output>
</layer>
Example 5
<layer ... reduction="none" type="ScatterNDUpdate">
<input>
<port id="0">
<dim>1000</dim>
<dim>256</dim>
<dim>10</dim>
<dim>15</dim>
</port>
<port id="1">
<dim>25</dim>
<dim>125</dim>
<dim>3</dim>
</port>
<port id="2">
<dim>25</dim>
<dim>125</dim>
<dim>15</dim>
</port>
</input>
<output>
<port id="3">
<dim>1000</dim>
<dim>256</dim>
<dim>10</dim>
<dim>15</dim>
</port>
</output>
</layer>