Add Round-5 operation (#2328)

* Add Round-5 operation

* Add ONNX Round to supported operation list

* Add ngraph implementation for Round operation

* Update MO part

* Create UnaryElementwise class, update Round Operation

* Fix mode attr in mxnet extractor

* Add tests for Round shape infer

* Update 'enable' attr

* Update MO IR Reader to support UnaryElementwise operations

* Minor test refactor

* Update ngraph Round operation

* Add reference implementation

* Add test for reference implementation

* Add test for shape infer

* Add test for IE IR Reader

* AddRound operation to python api

* Fix missed mode attr

* Update Round operation version

* Fix codestyle

* Add MxNet Round to supported layers list

* Fix error in reference

* Fix comments style

* Update CMake file

* Update Ngraph reference test

* Update IE IR Reader tests

* Return v0::Round operation

* Update shape infer tests

* Fix v0::Round reference

* Fix codestyle

* Enum instead of string

* Fix codestyle

* Add Mode attribute adapter

* Update Mode attr

* Fix reference for v0::Round

* Fix codestyle

* Fix mode attr

* Fix get() method

* Fix codestyle in python api

* Update test info

* Fix ngraph api part

* Ad round v5 to interpreter tests

* Fix codestyle is ie reader test

* Update ngraph python api __init__.py file

* Adde opser5 to dafault opsets in ie_ir reader

* Add parser for Round layer

* Remove redundant spaces

* Add round creator to appropriate list

* Remove redundant import

* Commit to bump infrastructure version

I'm sorry for this, but this commit will be squashed on merge to master anyway and it is needed for your PR to correctly pass the pipeline

* Fix import

* fix codestyle

* Fix ngraph api part

* Add shape infer tests in python api

* Add .upper() for mode attr

* Refactor MO shape infer test for Round op

* Update tests and add comments

* Revert "Commit to bump infrastructure version"

This reverts commit 56e6ae1e4c.

* remove parser for Round layer

* Update Ronund-5 evaluate test

* Resolve review comments

Co-authored-by: User <user@nnlvdp-achetver.inn.intel.com>
Co-authored-by: Andrey Babushkin <andrey.babushkin@intel.com>
Co-authored-by: Anton Chetverikov <anton.chetverikov@.intel.com>
This commit is contained in:
authored and GitHub committed 2020-10-20 18:36:19 +03:00
1 parent a2e49469b5
commit 44406691e5
20 files changed
+550 -64

No files matched your search

@@ -70,6 +70,7 @@ Standard MXNet\* symbols:
| repeat | No |
| rnn | No |
| rnn_param_concat | No |
| round | No |
| sigmoid | No |
| slice | No |
| slice_axis | No |
@@ -385,6 +386,7 @@ Standard ONNX\* operators:
| Reshape | No |
| Resize | Coordinate transformation mode `tf_crop_and_resize` is not supported, `nearest` mode is not supported for 5D+ inputs. |
| ReverseSequence | No |
| Round | No |
| Scatter | Supported if fuse-able to ScatterUpdate. MYRIAD only |
| ScatterND | No |
| ScatterElements | Supported if fuse-able to ScatterUpdate. MYRIAD only |
@@ -0,0 +1,191 @@
// Copyright (C) 2018-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <string>
#include "ngraph_reader_tests.hpp"
TEST_F(NGraphReaderTests, ReadRoundEvenNetwork) {
std::string model = R"V0G0N(
<net name="Network" version="10">
<layers>
<layer name="in1" type="Parameter" id="0" version="opset1">
<data element_type="f32" shape="1,3,22,22"/>
<output>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="Round" id="1" type="Round" version="opset5">
<data mode="half_to_even"/>
<input>
<port id="1" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
<output>
<port id="2" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="output" type="Result" id="2" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
<edge from-layer="1" from-port="2" to-layer="2" to-port="0"/>
</edges>
</net>
)V0G0N";
std::string modelV5 = R"V0G0N(
<net name="Network" version="5" precision="FP32" batch="1">
<layers>
<layer name="in1" type="Input" precision="FP32" id="0">
<output>
<port id="0">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="Round" id="1" type="Round" version="opset5">
<data mode="half_to_even"/>
<input>
<port id="1">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
<output>
<port id="2">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
</edges>
</net>
)V0G0N";
compareIRs(model, modelV5, 0);
}
TEST_F(NGraphReaderTests, ReadRoundAwayNetwork) {
std::string model = R"V0G0N(
<net name="Network" version="10">
<layers>
<layer name="in1" type="Parameter" id="0" version="opset1">
<data element_type="f32" shape="1,3,22,22"/>
<output>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="Round" id="1" type="Round" version="opset5">
<data mode="half_away_from_zero"/>
<input>
<port id="1" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
<output>
<port id="2" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="output" type="Result" id="2" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
<edge from-layer="1" from-port="2" to-layer="2" to-port="0"/>
</edges>
</net>
)V0G0N";
std::string modelV5 = R"V0G0N(
<net name="Network" version="5" precision="FP32" batch="1">
<layers>
<layer name="in1" type="Input" precision="FP32" id="0">
<output>
<port id="0">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
<layer name="Round" id="1" type="Round" version="opset5">
<data mode="half_away_from_zero"/>
<input>
<port id="1">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</input>
<output>
<port id="2">
<dim>1</dim>
<dim>3</dim>
<dim>22</dim>
<dim>22</dim>
</port>
</output>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
</edges>
</net>
)V0G0N";
compareIRs(model, modelV5, 0);
}
@@ -16,7 +16,7 @@
import numpy as np
from extensions.ops.elementwise import Mul, Sub, Add, Maximum, Minimum, Div, Greater, GreaterEqual, Equal, Less, \
LessEqual, Pow, NotEqual, LogicalAnd, LogicalOr
LessEqual, Pow, NotEqual, LogicalAnd, LogicalOr, Round
from mo.front.extractor import FrontExtractorOp
from mo.front.mxnet.extractors.utils import get_mxnet_layer_attrs
from mo.graph.graph import Node
@@ -414,3 +414,13 @@ class OnesFrontExtractor(FrontExtractorOp):
def extract(cls, node):
AttributedPower.update_node_stat(node, {'scale': 0, 'shift': 1})
return cls.enabled
class RoundExtractor(FrontExtractorOp):
op = 'round'
enabled = True
@classmethod
def extract(cls, node):
Round.update_node_stat(node, {'mode': 'half_away_from_zero'})
return cls.enabled
@@ -16,7 +16,7 @@
import numpy as np
from extensions.ops.elementwise import Add, Sub, Mul, Div, Pow, Less, Equal, Greater, \
LogicalAnd, LogicalOr, LogicalXor
LogicalAnd, LogicalOr, LogicalXor, Round
from mo.front.extractor import FrontExtractorOp
from mo.front.onnx.extractors.utils import onnx_attr
from mo.graph.graph import Node
@@ -188,3 +188,13 @@ class XorExtractor(FrontExtractorOp):
def extract(cls, node):
LogicalXor.update_node_stat(node)
return cls.enabled
class RoundFrontExtractor(FrontExtractorOp):
op = 'Round'
enabled = True
@classmethod
def extract(cls, node: Node):
Round.update_node_stat(node, {'mode': 'half_to_even'})
return cls.enabled
@@ -14,7 +14,7 @@
limitations under the License.
"""
from extensions.ops.elementwise import Add, Mul, Sub, Div, Maximum, Minimum, Pow, LogicalAnd, LogicalOr, Equal, \
GreaterEqual, Greater, Less, LessEqual, NotEqual, FloorMod, BiasAdd, SquaredDifference
GreaterEqual, Greater, Less, LessEqual, NotEqual, FloorMod, BiasAdd, SquaredDifference, Round
from mo.front.extractor import FrontExtractorOp
from mo.front.tf.extractors.utils import tf_dtype_extractor
from mo.ops.eltwise_n import EltwiseNAdd
@@ -271,3 +271,13 @@ class FloorModFrontExtractor(FrontExtractorOp):
def extract(cls, node):
FloorMod.update_node_stat(node)
return cls.enabled
class RoundExtractor(FrontExtractorOp):
op = 'Round'
enabled = True
@classmethod
def extract(cls, node):
Round.update_node_stat(node, {'mode': 'half_to_even'})
return cls.enabled
+45 -30
View File
@@ -49,12 +49,13 @@ class Elementwise(Op):
operation = None
op = None
op_type = None
version = 'opset1'
def __init__(self, graph: Graph, attrs: dict):
super().__init__(graph, {
'op': self.op,
'type': self.op_type,
'version': 'opset1',
'version': self.version,
'infer': lambda node: eltwise_infer(node, self.operation),
'type_infer': self.type_infer,
'can_be_bias': True,
@@ -71,8 +72,18 @@ class Elementwise(Op):
node.out_port(0).set_data_type(node.in_port(0).get_data_type())
class UnaryElementwise(Elementwise):
def __init__(self, graph: Graph, attrs: dict):
super().__init__(graph, {**{
'in_ports_count': 1,
}, **attrs})
@staticmethod
def type_infer(node):
copy_type_infer(node)
class Add(Elementwise):
enabled = False
op = 'Add'
op_type = 'Add'
operation = staticmethod(lambda a, b: a + b)
@@ -87,14 +98,12 @@ class BiasAdd(Add):
class Sub(Elementwise):
enabled = False
op = 'Sub'
op_type = 'Subtract'
operation = staticmethod(lambda a, b: a - b)
class Mul(Elementwise):
enabled = False
op = 'Mul'
op_type = 'Multiply'
operation = staticmethod(lambda a, b: a * b)
@@ -105,21 +114,18 @@ def both_types_are_integer(a, b):
class Div(Elementwise):
enabled = False
op = 'Div'
op_type = 'Divide'
operation = staticmethod(lambda a, b: a // b if both_types_are_integer(a, b) else a / b)
class SquaredDifference(Elementwise):
enabled = False
op = 'SquaredDifference'
op_type = 'SquaredDifference'
operation = staticmethod(lambda a, b: (a - b) * (a - b))
class Pow(Elementwise):
enabled = False
op = 'Pow'
op_type = 'Power'
@@ -147,103 +153,112 @@ class LogicalElementwise(Elementwise):
class Greater(LogicalElementwise):
enabled = False
op = 'Greater'
op_type = 'Greater'
operation = staticmethod(lambda a, b: a > b)
class GreaterEqual(LogicalElementwise):
enabled = False
op = 'GreaterEqual'
op_type = 'GreaterEqual'
operation = staticmethod(lambda a, b: a >= b)
class Less(LogicalElementwise):
enabled = False
op = 'Less'
op_type = 'Less'
operation = staticmethod(lambda a, b: a < b)
class LessEqual(LogicalElementwise):
enabled = False
op = 'LessEqual'
op_type = 'LessEqual'
operation = staticmethod(lambda a, b: a <= b)
class Equal(LogicalElementwise):
enabled = False
op = 'Equal'
op_type = 'Equal'
operation = staticmethod(lambda a, b: a == b)
class NotEqual(LogicalElementwise):
enabled = False
op = 'NotEqual'
op_type = 'NotEqual'
operation = staticmethod(lambda a, b: a != b)
class Maximum(Elementwise):
enabled = False
op = 'Maximum'
op_type = 'Maximum'
operation = staticmethod(lambda a, b: np.maximum(a, b))
class Minimum(Elementwise):
enabled = False
op = 'Minimum'
op_type = 'Minimum'
operation = staticmethod(lambda a, b: np.minimum(a, b))
class Round(Elementwise):
enabled = False
class Round(UnaryElementwise):
op = 'Round'
op_type = None
version = 'extension'
operation = staticmethod(lambda a: np.round(a))
op_type = 'Round'
version = 'opset5'
def __init__(self, graph: Graph, attrs):
round_attrs = {'mode': 'half_to_even',
'infer': self.infer
}
round_attrs.update(attrs)
super().__init__(graph, round_attrs)
def backend_attrs(self):
return ['mode']
@classmethod
def infer(cls, node: Node):
node.out_port(0).data.set_shape(node.in_port(0).data.get_shape())
a = node.in_port(0).data.get_value()
if a is not None:
assert node.soft_get('mode') in ['half_to_even', 'half_away_from_zero'], \
'Round node {} has unsupported "mode" attribute value: {}'.format(node.soft_get('name', node.id),
node.soft_get('mode'))
if node.mode == 'half_away_from_zero':
mask = (a >= 0)
out = np.empty_like(a)
out[mask] = np.floor(a[mask] + 0.5)
out[~mask] = np.ceil(a[~mask] - 0.5)
else:
out = np.round(a)
node.out_port(0).data.set_value(out)
class LogicalOr(LogicalElementwise):
enabled = False
op = 'LogicalOr'
op_type = 'LogicalOr'
operation = staticmethod(lambda a, b: np.logical_or(a, b))
class LogicalXor(Elementwise):
enabled = False
op = 'LogicalXor'
op_type = 'LogicalXor'
operation = staticmethod(lambda a, b: np.logical_xor(a, b))
class LogicalAnd(LogicalElementwise):
enabled = False
op = 'LogicalAnd'
op_type = 'LogicalAnd'
operation = staticmethod(lambda a, b: np.logical_and(a, b))
class FloorMod(Elementwise):
enabled = False
op = 'FloorMod'
op_type = 'FloorMod'
operation = staticmethod(lambda a, b: a % b)
class Negative(Elementwise):
enabled = False
class Negative(UnaryElementwise):
op = 'Negative'
op_type = 'Negative'
operation = staticmethod(lambda a: -a)
@staticmethod
def type_infer(node):
copy_type_infer(node)
@@ -0,0 +1,92 @@
"""
Copyright (C) 2018-2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import unittest
import numpy as np
from extensions.ops.elementwise import Round
from mo.graph.graph import Node
from mo.utils.unittest.graph import build_graph
def round_test_graph(nodes_attributes, value, mode: str):
graph = build_graph(nodes_attributes,
[
('node_1', 'elementwise_node'),
('elementwise_node', 'node_3')
],
{
'node_1': {
'value': value
},
'elementwise_node': {
'op': 'Round',
'mode': mode,
},
'node_3': {
'value': None
}
})
return graph
class TestElementwiseOp(unittest.TestCase):
nodes_attributes = {
'node_1': {
'shape': np.array([13]),
'value': None
},
'elementwise_node': {
'op': None,
'kind': 'op',
'operation': None
},
'node_3': {
'shape': None
}
}
value = np.array([-23.5, -22.5, -2.5, -1.5, -0.5, 0.5, 0.9, 1.5, 2.3, 2.5, 3.5, 22.5, 23.5])
def test_elementwise_round_even_infer(self):
graph = round_test_graph(self.nodes_attributes, self.value, 'half_to_even')
graph.graph['layout'] = 'NCHW'
elementwise_node = Node(graph, 'elementwise_node')
Round.infer(elementwise_node)
exp_shape = np.array([13])
res_shape = graph.node['node_3']['shape']
res_value = graph.node['node_3']['value']
exp_value = np.array([-24., -22., -2., -2., -0., 0., 1., 2., 2., 2., 4., 22., 24.,])
for i, value in enumerate(exp_shape):
self.assertEqual(res_shape[i], value)
for i, value in enumerate(exp_value):
self.assertAlmostEqual(res_value[i], value)
def test_elementwise_round_away_infer(self):
graph = round_test_graph(self.nodes_attributes, self.value, 'half_away_from_zero')
graph.graph['layout'] = 'NCHW'
elementwise_node = Node(graph, 'elementwise_node')
Round.infer(elementwise_node)
exp_shape = np.array([13])
res_shape = graph.node['node_3']['shape']
res_value = graph.node['node_3']['value']
exp_value = np.array([-24., -23., -3., -2., -1., 1., 1., 2., 2., 3., 4., 23., 24.])
for i, value in enumerate(exp_shape):
self.assertEqual(res_shape[i], value)
for i, value in enumerate(exp_value):
self.assertAlmostEqual(res_value[i], value)
@@ -23,7 +23,7 @@ from extensions.back.TopKNormalizer import TopKNormalizer
from extensions.ops.Cast import Cast
from extensions.ops.ReduceOps import ReduceOp
from extensions.ops.activation_ops import Activation
from extensions.ops.elementwise import Elementwise, LogicalElementwise, BiasAdd, Div, Mul, Pow, Sub
from extensions.ops.elementwise import Elementwise, UnaryElementwise, LogicalElementwise, BiasAdd, Div, Mul, Pow, Sub
from extensions.ops.embedding_bag import EmbeddingBagBase
from extensions.ops.psroipooling import DeformablePSROIPoolingOp
from extensions.ops.scatter import Scatter
@@ -69,8 +69,8 @@ def collect_ops(path: str):
"""
import_by_path(os.path.join(path, 'mo', 'ops'), ['mo', 'ops'])
import_by_path(os.path.join(path, 'extensions', 'ops'), ['extensions', 'ops'])
update_registration(classes=[Op, Activation, Elementwise, EmbeddingBagBase,
LogicalElementwise, ReduceOp, Scatter, ScatterNDBase],
update_registration(classes=[Op, Activation, Elementwise, UnaryElementwise, LogicalElementwise,
EmbeddingBagBase, ReduceOp, Scatter, ScatterNDBase],
enabled_transforms=[], disabled_transforms=[])
+3
View File
@@ -90,6 +90,9 @@ namespace ngraph
virtual std::shared_ptr<Node>
clone_with_new_inputs(const OutputVector& new_args) const override;
bool evaluate(const HostTensorVector& outputs,
const HostTensorVector& inputs) const override;
RoundMode get_mode() const { return m_mode; }
private:
RoundMode m_mode;
@@ -40,11 +40,18 @@ namespace ngraph
}
template <typename T>
void round(const T* arg, T* out, size_t count)
void round(const T* arg, T* out, size_t count, const op::v5::Round::RoundMode mode)
{
for (size_t i = 0; i < count; ++i)
{
out[i] = round_to_nearest_even(arg[i]);
if (mode == op::v5::Round::RoundMode::HALF_TO_EVEN)
{
out[i] = round_to_nearest_even(arg[i]);
}
else
{
out[i] = std::round(arg[i]);
}
}
}
}
+25 -6
View File
@@ -45,10 +45,14 @@ namespace roundop
{
// function used by TYPE_CASE
template <element::Type_t ET>
inline bool evaluate(const HostTensorPtr& arg0, const HostTensorPtr& out, const size_t count)
inline bool evaluate(const HostTensorPtr& arg0,
const HostTensorPtr& out,
const size_t count,
const op::v5::Round::RoundMode mode)
{
using T = typename element_type_traits<ET>::value_type;
runtime::reference::round<T>(arg0->get_data_ptr<ET>(), out->get_data_ptr<ET>(), count);
runtime::reference::round<T>(
arg0->get_data_ptr<ET>(), out->get_data_ptr<ET>(), count, mode);
return true;
}
@@ -60,7 +64,10 @@ namespace roundop
return true;
}
bool evaluate_round(const HostTensorPtr& arg0, const HostTensorPtr& out, const size_t count)
bool evaluate_round(const HostTensorPtr& arg0,
const HostTensorPtr& out,
const size_t count,
const op::v5::Round::RoundMode mode)
{
bool rc = true;
out->set_unary(arg0);
@@ -85,9 +92,11 @@ namespace roundop
break;
COPY_TENSOR(u64)(arg0, out, count);
break;
TYPE_CASE(f16)(arg0, out, count);
TYPE_CASE(f16)(arg0, out, count, mode);
break;
TYPE_CASE(f32)(arg0, out, count);
TYPE_CASE(f32)(arg0, out, count, mode);
break;
TYPE_CASE(bf16)(arg0, out, count, mode);
break;
default: rc = false; break;
}
@@ -98,7 +107,10 @@ namespace roundop
bool op::v0::Round::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const
{
OV_ITT_SCOPED_TASK(itt::domains::nGraphOp, "op::v0::Round::evaluate");
return roundop::evaluate_round(inputs[0], outputs[0], shape_size(get_output_shape(0)));
return roundop::evaluate_round(inputs[0],
outputs[0],
shape_size(get_output_shape(0)),
op::v5::Round::RoundMode::HALF_TO_EVEN);
}
NGRAPH_SUPPRESS_DEPRECATED_END
@@ -129,6 +141,13 @@ shared_ptr<Node> op::v5::Round::clone_with_new_inputs(const OutputVector& new_ar
return make_shared<v5::Round>(new_args.at(0), m_mode);
}
bool op::v5::Round::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const
{
OV_ITT_SCOPED_TASK(itt::domains::nGraphOp, "op::v5::Round::evaluate");
return roundop::evaluate_round(
inputs[0], outputs[0], shape_size(get_output_shape(0)), get_mode());
}
namespace ngraph
{
template <>
+1
View File
@@ -141,6 +141,7 @@ from ngraph.opset5 import reverse_sequence
from ngraph.opset5 import rnn_cell
from ngraph.opset5 import roi_align
from ngraph.opset5 import roi_pooling
from ngraph.opset5 import round
from ngraph.opset5 import scatter_elements_update
from ngraph.opset5 import scatter_update
from ngraph.opset5 import select
@@ -128,6 +128,7 @@ from ngraph.opset1.ops import reverse_sequence
from ngraph.opset3.ops import rnn_cell
from ngraph.opset3.ops import roi_align
from ngraph.opset2.ops import roi_pooling
from ngraph.opset5.ops import round
from ngraph.opset3.ops import scatter_elements_update
from ngraph.opset3.ops import scatter_update
from ngraph.opset1.ops import select
+14
View File
@@ -90,3 +90,17 @@ def log_softmax(data: NodeInput, axis: int, name: Optional[str] = None) -> Node:
:return: The new node with LogSoftmax operation applied on each element.
"""
return _get_node_factory_opset5().create("LogSoftmax", [as_node(data)], {"axis": axis})
@nameable_op
def round(data: NodeInput, mode: str = "half_to_even", name: Optional[str] = None) -> Node:
"""Apply Round operation on each element of input tensor.
:param data: The tensor providing input data.
:param mode: Rule to round halfway cases. If set to 'half_to_even' then halfs round to the nearest even
integer or rounding in such a way that the result heads away from zero if `mode` attribute is
'half_away_from_zero`.
:param name: An optional name of the output node.
:return: The new node with Round operation applied on each element.
"""
return _get_node_factory_opset5().create("Round", as_nodes(data), {"mode": mode.upper()})
@@ -141,3 +141,41 @@ def test_hswish():
assert node.get_output_size() == 1
assert list(node.get_output_shape(0)) == [3, 10]
assert node.get_output_element_type(0) == Type.f32
def test_round_even():
float_dtype = np.float32
data = ng.parameter(Shape([3, 10]), dtype=float_dtype, name="data")
node = ng.round(data, "HALF_TO_EVEN")
assert node.get_type_name() == "Round"
assert node.get_output_size() == 1
assert list(node.get_output_shape(0)) == [3, 10]
assert node.get_output_element_type(0) == Type.f32
# Excluded because this part needs mklddn implementation of Round operation
# Need to uncomment and check when 37651 will be done.
# input_tensor = np.array([-2.5, -1.5, -0.5, 0.5, 0.9, 1.5, 2.3, 2.5, 3.5], dtype=np.float32)
# expected = [-2.0, -2.0, 0.0, 0.0, 1.0, 2.0, 2.0, 2.0, 4.0]
# result = run_op_node([input_tensor], ng.round, "HALF_TO_EVEN")
# assert np.allclose(result, expected)
def test_round_away():
float_dtype = np.float32
data = ng.parameter(Shape([3, 10]), dtype=float_dtype, name="data")
node = ng.round(data, "HALF_AWAY_FROM_ZERO")
assert node.get_type_name() == "Round"
assert node.get_output_size() == 1
assert list(node.get_output_shape(0)) == [3, 10]
assert node.get_output_element_type(0) == Type.f32
# Excluded because this part needs mklddn implementation of Round operation
# Need to uncomment and check when 37651 will be done.
# input_tensor = np.array([-2.5, -1.5, -0.5, 0.5, 0.9, 1.5, 2.3, 2.5, 3.5], dtype=np.float32)
# expected = [-3.0, -2.0, -1.0, 1.0, 1.0, 2.0, 2.0, 3.0, 4.0]
# result = run_op_node([input_tensor], ng.round, "HALF_AWAY_FROM_ZERO")
# assert np.allclose(result, expected)
+2
View File
@@ -79,6 +79,7 @@ set(SRC
op_eval/reduce_l1.cpp
op_eval/reduce_l2.cpp
op_eval/roi_align.cpp
op_eval/round.cpp
op_eval/softplus.cpp
op_eval/split.cpp
op_eval/swish.cpp
@@ -166,6 +167,7 @@ set(SRC
type_prop/round.cpp
type_prop/rnn_cell.cpp
type_prop/rnn_sequence.cpp
type_prop/round.cpp
type_prop/scatter_elements_update.cpp
type_prop/scatter_nd_update.cpp
type_prop/scatter_update.cpp
+67
View File
@@ -0,0 +1,67 @@
//*****************************************************************************
// Copyright 2017-2020 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "ngraph/op/round.hpp"
#include "ngraph/runtime/host_tensor.hpp"
#include "ngraph/validation_util.hpp"
#include "runtime/backend.hpp"
#include "util/test_tools.hpp"
using namespace std;
using namespace ngraph;
TEST(op_eval, rounding_to_even)
{
auto p = make_shared<op::Parameter>(element::f32, Shape{9});
auto round = make_shared<op::v5::Round>(p, op::v5::Round::RoundMode::HALF_TO_EVEN);
auto fun = make_shared<Function>(OutputVector{round}, ParameterVector{p});
std::vector<float> inputs{-2.5f, -1.5f, -0.5f, 0.5f, 0.9f, 1.5f, 2.3f, 2.5f, 3.5f};
std::vector<float> expected_result{-2.f, -2.f, -0.f, 0.f, 1.f, 2.f, 2.f, 2.f, 4.f};
auto result = make_shared<HostTensor>();
ASSERT_TRUE(
fun->evaluate({result}, {make_host_tensor<element::Type_t::f32>(Shape{9}, inputs)}));
EXPECT_EQ(result->get_element_type(), element::f32);
EXPECT_EQ(result->get_shape(), Shape{9});
auto result_data = read_vector<float>(result);
for (auto i = 0; i < inputs.size(); i++)
EXPECT_NEAR(result_data[i], expected_result[i], 0.000001);
}
TEST(op_eval, rounding_away)
{
auto p = make_shared<op::Parameter>(element::f32, Shape{9});
auto round = make_shared<op::v5::Round>(p, op::v5::Round::RoundMode::HALF_AWAY_FROM_ZERO);
auto fun = make_shared<Function>(OutputVector{round}, ParameterVector{p});
std::vector<float> inputs{-2.5f, -1.5f, -0.5f, 0.5f, 0.9f, 1.5f, 2.3f, 2.5f, 3.5f};
std::vector<float> expected_result{-3.f, -2.f, -1.f, 1.f, 1.f, 2.f, 2.f, 3.f, 4.f};
auto result = make_shared<HostTensor>();
ASSERT_TRUE(
fun->evaluate({result}, {make_host_tensor<element::Type_t::f32>(Shape{9}, inputs)}));
EXPECT_EQ(result->get_element_type(), element::f32);
EXPECT_EQ(result->get_shape(), Shape{9});
auto result_data = read_vector<float>(result);
for (auto i = 0; i < inputs.size(); i++)
EXPECT_NEAR(result_data[i], expected_result[i], 0.000001);
}
@@ -1258,8 +1258,10 @@ protected:
case OP_TYPEID::Round:
{
size_t element_count = shape_size(node.get_output_shape(0));
reference::round<T>(
args[0]->get_data_ptr<const T>(), out[0]->get_data_ptr<T>(), element_count);
reference::round<T>(args[0]->get_data_ptr<const T>(),
out[0]->get_data_ptr<T>(),
element_count,
op::v5::Round::RoundMode::HALF_TO_EVEN);
break;
}
case OP_TYPEID::Select:
@@ -1495,6 +1497,7 @@ protected:
case OP_TYPEID::Range:
case OP_TYPEID::Reshape:
case OP_TYPEID::Result:
case OP_TYPEID::Round_v5:
case OP_TYPEID::ShapeOf_v3:
case OP_TYPEID::ShapeOf:
case OP_TYPEID::Softmax:
@@ -57,5 +57,6 @@ NGRAPH_OP(GatherND, op::v5)
NGRAPH_OP(LSTMSequence, op::v5)
NGRAPH_OP(GRUSequence, op::v5)
NGRAPH_OP(RNNSequence, op::v5)
NGRAPH_OP(Round, op::v5)
NGRAPH_OP(LogSoftmax, op::v5)
#undef ID_SUFFIX
+18 -18
View File
@@ -41,51 +41,51 @@ TEST(type_prop, rounding_away)
TEST(type_prop, rounding_to_even_partial)
{
auto data = make_shared<op::Parameter>(element::f32, PartialShape{1, Dimension::dynamic(), 6});
auto softplus_func = make_shared<op::v5::Round>(data, op::v5::Round::RoundMode::HALF_TO_EVEN);
EXPECT_EQ(softplus_func->get_element_type(), element::f32);
ASSERT_TRUE(softplus_func->get_output_partial_shape(0).same_scheme(
auto round_func = make_shared<op::v5::Round>(data, op::v5::Round::RoundMode::HALF_TO_EVEN);
EXPECT_EQ(round_func->get_element_type(), element::f32);
ASSERT_TRUE(round_func->get_output_partial_shape(0).same_scheme(
(PartialShape{1, Dimension::dynamic(), 6})));
// rank unknown
auto softplus_partial = make_shared<op::v5::Round>(
auto round_partial = make_shared<op::v5::Round>(
make_shared<op::Parameter>(element::f32, PartialShape::dynamic()),
op::v5::Round::RoundMode::HALF_TO_EVEN);
ASSERT_TRUE(softplus_partial->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
ASSERT_TRUE(round_partial->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
}
TEST(type_prop, rounding_away_partial)
{
auto data = make_shared<op::Parameter>(element::f32, PartialShape{1, Dimension::dynamic(), 6});
auto softplus_func =
auto round_func =
make_shared<op::v5::Round>(data, op::v5::Round::RoundMode::HALF_AWAY_FROM_ZERO);
EXPECT_EQ(softplus_func->get_element_type(), element::f32);
ASSERT_TRUE(softplus_func->get_output_partial_shape(0).same_scheme(
EXPECT_EQ(round_func->get_element_type(), element::f32);
ASSERT_TRUE(round_func->get_output_partial_shape(0).same_scheme(
(PartialShape{1, Dimension::dynamic(), 6})));
// rank unknown
auto softplus_partial = make_shared<op::v5::Round>(
auto round_partial = make_shared<op::v5::Round>(
make_shared<op::Parameter>(element::f32, PartialShape::dynamic()),
op::v5::Round::RoundMode::HALF_AWAY_FROM_ZERO);
ASSERT_TRUE(softplus_partial->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
ASSERT_TRUE(round_partial->get_output_partial_shape(0).same_scheme(PartialShape::dynamic()));
}
TEST(type_prop, rounding_to_even_partial_static_rank)
{
auto data = make_shared<op::Parameter>(element::f32, PartialShape{1, Dimension::dynamic(), 6});
auto softplus_func = make_shared<op::v5::Round>(data, op::v5::Round::RoundMode::HALF_TO_EVEN);
EXPECT_EQ(softplus_func->get_element_type(), element::f32);
ASSERT_TRUE(softplus_func->get_output_partial_shape(0).same_scheme(
auto round_func = make_shared<op::v5::Round>(data, op::v5::Round::RoundMode::HALF_TO_EVEN);
EXPECT_EQ(round_func->get_element_type(), element::f32);
ASSERT_TRUE(round_func->get_output_partial_shape(0).same_scheme(
(PartialShape{1, Dimension::dynamic(), 6})));
ASSERT_TRUE(softplus_func->get_output_partial_shape(0).rank().is_static());
ASSERT_TRUE(round_func->get_output_partial_shape(0).rank().is_static());
}
TEST(type_prop, rounding_away_partial_static_rank)
{
auto data = make_shared<op::Parameter>(element::f32, PartialShape{1, Dimension::dynamic(), 6});
auto softplus_func =
auto round_func =
make_shared<op::v5::Round>(data, op::v5::Round::RoundMode::HALF_AWAY_FROM_ZERO);
EXPECT_EQ(softplus_func->get_element_type(), element::f32);
ASSERT_TRUE(softplus_func->get_output_partial_shape(0).same_scheme(
EXPECT_EQ(round_func->get_element_type(), element::f32);
ASSERT_TRUE(round_func->get_output_partial_shape(0).same_scheme(
(PartialShape{1, Dimension::dynamic(), 6})));
ASSERT_TRUE(softplus_func->get_output_partial_shape(0).rank().is_static());
ASSERT_TRUE(round_func->get_output_partial_shape(0).rank().is_static());
}