Files
openvino/model-optimizer/extensions/ops/scatternd.py
T
Maxim Vafin 2bed9c9277 Fix ScatterND validation and implement evaluate (#4905)
* Fix ScatterND validation and implement evaluate

* Apply review feedback

* Update scatternd.py
2021-03-31 19:01:46 +03:00

93 lines
3.8 KiB
Python

# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import numpy as np
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Node, Graph
from mo.ops.op import Op
class ScatterNDBase(Op):
enabled = False
op = op_type = None
version = None
def __init__(self, graph: Graph, attrs: dict):
assert self.op is not None and self.op_type is not None and self.version is not None, \
'Please use specialized ScatterNDBase operation class, ScatterNDBase is base class'
mandatory_props = {
'op': self.op,
'type': self.op_type,
'version': self.version,
'infer': self.infer,
'in_ports_count': 3,
'out_ports_count': 1,
}
super().__init__(graph, mandatory_props, attrs)
@staticmethod
def infer(node: Node):
node_name = node.soft_get('name', node.id)
input_shape = node.in_port(0).data.get_shape()
indices_shape = node.in_port(1).data.get_shape()
updates_shape = node.in_port(2).data.get_shape()
assert input_shape is not None and updates_shape is not None and indices_shape is not None, \
'The node "{}" input shape is None'.format(node_name)
# check that shapes are correct
# 1. ranks of both input and indices must be at least 1
assert len(input_shape) >= 1 and len(indices_shape) >= 1, \
'The node "{}" input and indices ranks must be at least 1'.format(node_name)
# 2. the last dimension of indices shape must be at most a rank of input
assert indices_shape[-1] <= len(input_shape), \
'The last dimension of indices shape must be at most a rank of input for the node "{}"'.format(node_name)
# 3. updates is a tensor of shape indices_shape[:-1] + input_shape[indices_shape[-1]:]
# if expected updates shape is scalar, updates can be tensor with the single element (for example, of shape [1], [[1]], etc.)
expected_updates_shape = np.concatenate((indices_shape[:-1], input_shape[indices_shape[-1]:]), axis=0)
assert np.array_equal(updates_shape, expected_updates_shape) or\
np.array_equal(expected_updates_shape, []) and np.array_equal(updates_shape, np.ones(len(updates_shape))), \
'The updates shape must be equal to indices_shape[:-1] + input_shape[indices_shape[-1]:] for the node "{}"'.format(node_name)
node.out_port(0).data.set_shape(input_shape)
@staticmethod
def type_infer(node: Node):
assert node.in_port(0).get_source().get_data_type() == node.in_port(2).get_source().get_data_type(), \
'The data type of the first and the third inputs must be equal for the node {}'.format(node.name)
node.out_port(0).set_data_type(node.in_port(0).get_data_type())
class ScatterNDUpdate(ScatterNDBase):
op = op_type = 'ScatterNDUpdate'
version = 'opset4'
@staticmethod
def infer(node: Node):
ScatterNDBase.infer(node)
input_value = node.in_port(0).data.get_value()
indices_shape = node.in_port(1).data.get_shape()
indices_value = node.in_port(1).data.get_value()
updates_value = node.in_port(2).data.get_value()
# compute output value if all inputs are constant
if input_value is not None and indices_value is not None and updates_value is not None:
output_value = input_value.copy()
indx_range = int64_array(indices_shape[:-1])
for indx in np.ndindex(tuple(indx_range)):
if indx == ():
# a case when updates is a scalar
indx = 0
updates_value = [updates_value]
output_value[indices_value[indx]] = updates_value[indx]
node.out_port(0).data.set_value(output_value)