diff --git a/src/core/include/openvino/op/divide.hpp b/src/core/include/openvino/op/divide.hpp index 010e0e79590..90eb50fcc13 100644 --- a/src/core/include/openvino/op/divide.hpp +++ b/src/core/include/openvino/op/divide.hpp @@ -49,6 +49,10 @@ public: bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override; OPENVINO_SUPPRESS_DEPRECATED_END bool has_evaluate() const override; + OPENVINO_SUPPRESS_DEPRECATED_START + bool evaluate_lower(const HostTensorVector& outputs) const override; + bool evaluate_upper(const HostTensorVector& outputs) const override; + OPENVINO_SUPPRESS_DEPRECATED_END protected: bool m_pythondiv{true}; diff --git a/src/core/src/op/divide.cpp b/src/core/src/op/divide.cpp index 553ed4d3ff7..e6ae6a0fc0e 100644 --- a/src/core/src/op/divide.cpp +++ b/src/core/src/op/divide.cpp @@ -4,7 +4,14 @@ #include "ngraph/op/divide.hpp" +#include + #include "itt.hpp" +#include "ngraph/op/and.hpp" +#include "ngraph/op/equal.hpp" +#include "ngraph/op/less.hpp" +#include "ngraph/op/not.hpp" +#include "ngraph/op/select.hpp" #include "ngraph/runtime/host_tensor.hpp" #include "ngraph/runtime/reference/divide.hpp" @@ -50,6 +57,127 @@ bool evaluate_divide(const HostTensorPtr& arg0, } return rc; } + +bool evaluate_bound(const Node* node, const HostTensorVector& output_values, bool is_upper) { + // for positive arg2 divide will have limits [low/up , up/low] + // for negative arg2 limits for divide will be [up/low, low/up] + // for arg2 range with both positive and negative values, divide can give any result [-inf, inf] + NGRAPH_CHECK(node, validate_host_tensor_vector(output_values, 1)); + const auto& input1 = node->input_value(0); + const auto& input2 = node->input_value(1); + + const auto& input2_low = input2.get_tensor().get_lower_value(); + if (input2_low == nullptr) + return false; + const auto& input2_up = input2.get_tensor().get_upper_value(); + if (input2_up == nullptr) + return false; + const auto& input1_low = input1.get_tensor().get_lower_value(); + if (input1_low == nullptr) + return false; + const auto& input1_up = input1.get_tensor().get_upper_value(); + if (input1_up == nullptr) + return false; + + auto zeros_const = op::Constant::create(input2.get_element_type(), {}, {0}); + + // mask to find out positive values for arg2 + auto input2_positive_up_mask = std::make_shared(element::boolean, input2.get_shape()); + // mask to find out ranges around 0 for arg2 + auto input2_low_negative_up_positive_mask = std::make_shared(element::boolean, input2.get_shape()); + + bool status = + op::v1::Less().evaluate({input2_positive_up_mask}, {std::make_shared(zeros_const), input2_up}); + if (!status) + return status; + + // mask to find out negative values for arg2 + auto input2_negative_low_mask = std::make_shared(element::boolean, input2.get_shape()); + status = + op::v1::Less().evaluate({input2_negative_low_mask}, {input2_low, std::make_shared(zeros_const)}); + if (!status) + return status; + status = op::v1::LogicalAnd().evaluate({input2_low_negative_up_positive_mask}, + {input2_negative_low_mask, input2_positive_up_mask}); + if (!status) + return status; + + if (!is_upper) { + auto value1 = std::make_shared(input1.get_element_type(), input1.get_shape()); + status = op::v1::Select().evaluate({value1}, {input2_positive_up_mask, input1_low, input1_up}); + if (!status) + return status; + + auto value2 = std::make_shared(input2.get_element_type(), input2.get_shape()); + status = op::v1::Select().evaluate({value2}, {input2_positive_up_mask, input2_up, input2_low}); + if (!status) + return status; + + OPENVINO_SUPPRESS_DEPRECATED_START + status = node->evaluate(output_values, {value1, value2}); + OPENVINO_SUPPRESS_DEPRECATED_END + if (!status) + return status; + + // replace values where zeros inside range of second arg to maximum values + auto output_minimum_value = get_constant_min_of_type(output_values[0]->get_element_type()); + if (output_minimum_value == nullptr) + return false; + status = op::v1::Select().evaluate(output_values, + {input2_low_negative_up_positive_mask, + std::make_shared(output_minimum_value), + output_values[0]}); + if (!status) + return status; + } else { + auto value1 = std::make_shared(input1.get_element_type(), input1.get_shape()); + status = op::v1::Select().evaluate({value1}, {input2_positive_up_mask, input1_up, input1_low}); + if (!status) + return status; + + auto value2 = std::make_shared(input2.get_element_type(), input2.get_shape()); + status = op::v1::Select().evaluate({value2}, {input2_positive_up_mask, input2_low, input2_up}); + if (!status) + return status; + + // create mask where zeros in the second argument are placed + auto input2_zeros_mask = std::make_shared(element::boolean, input2.get_shape()); + bool status = + op::v1::Equal().evaluate({input2_zeros_mask}, {value2, std::make_shared(zeros_const)}); + if (!status) + return status; + + // replace zeros by 1 values to get result of divide for other values of arguments + auto ones = op::Constant::create(input2.get_element_type(), input2.get_shape(), {1}); + status = op::v1::Select().evaluate({value2}, {input2_zeros_mask, std::make_shared(ones), value2}); + if (!status) + return status; + + OPENVINO_SUPPRESS_DEPRECATED_START + status = node->evaluate(output_values, {value1, value2}); + OPENVINO_SUPPRESS_DEPRECATED_END + if (!status) + return status; + + // replace values where zeros were found in the second argument to maximum values + auto output_maximum_value = get_constant_max_of_type(output_values[0]->get_element_type()); + if (output_maximum_value == nullptr) + return false; + status = op::v1::Select().evaluate( + output_values, + {input2_zeros_mask, std::make_shared(output_maximum_value), output_values[0]}); + return status; + + // replace values where zeros inside [low, ip] values range of second arg to maximum values + status = op::v1::Select().evaluate(output_values, + {input2_low_negative_up_positive_mask, + std::make_shared(output_maximum_value), + output_values[0]}); + if (!status) + return status; + } + return status; +} } // namespace } // namespace divide @@ -105,3 +233,11 @@ bool op::v1::Divide::has_evaluate() const { } return false; } + +bool ov::op::v1::Divide::evaluate_lower(const HostTensorVector& outputs) const { + return divide::evaluate_bound(this, outputs, false); +} + +bool ov::op::v1::Divide::evaluate_upper(const HostTensorVector& outputs) const { + return divide::evaluate_bound(this, outputs, true); +} diff --git a/src/core/tests/type_prop/reshape.cpp b/src/core/tests/type_prop/reshape.cpp index 0d74ac2e4db..6eab452ef6c 100644 --- a/src/core/tests/type_prop/reshape.cpp +++ b/src/core/tests/type_prop/reshape.cpp @@ -121,6 +121,23 @@ TEST(type_prop, interval_value_propagation_mul_div) { ASSERT_EQ(r->get_output_partial_shape(0), PartialShape({Dimension(2, 8), Dimension(4, 16), 2})); } +TEST(type_prop, interval_value_propagation_mul_div_rhs_shape) { + auto param = + make_shared(element::f32, PartialShape{Dimension(1, 5), Dimension(0, 4), Dimension(2, 3)}); + + auto shape_of = make_shared(param); + + auto cast_fp = make_shared(shape_of, element::f32); + auto mul = make_shared(op::Constant::create(element::f32, {}, {2}), cast_fp); + auto div = make_shared(op::Constant::create(element::f32, {3}, {10, 16, 12}), mul); + auto cast_int = make_shared(div, element::i32); + + auto r = make_shared(param, cast_int, false); + + ASSERT_EQ(r->get_element_type(), element::f32); + ASSERT_EQ(r->get_output_partial_shape(0), PartialShape({Dimension(1, 5), Dimension(2, -1), Dimension(2, 3)})); +} + TEST(type_prop, interval_value_propagation_mul_div_lhs_scalar) { auto param = make_shared(element::f32, PartialShape{Dimension(2, 8), Dimension(4, 16), 6}); auto shape_of = make_shared(param); diff --git a/tools/mo/openvino/tools/mo/back/offline_transformations.py b/tools/mo/openvino/tools/mo/back/offline_transformations.py index eee84463aaf..1cdb4ef0455 100644 --- a/tools/mo/openvino/tools/mo/back/offline_transformations.py +++ b/tools/mo/openvino/tools/mo/back/offline_transformations.py @@ -74,6 +74,7 @@ if __name__ == "__main__": parser.add_argument("--framework") parser.add_argument("--transform") parser.add_argument("--compress_fp16", action='store_true') + args = parser.parse_args() apply_offline_transformations(args.input_model, args.framework, parse_transform(args.transform), args.compress_fp16)