From f5767d4a5b9f07df602f1b9d0e2503eb14a34a51 Mon Sep 17 00:00:00 2001 From: Vladislav Golubev Date: Thu, 9 Sep 2021 13:05:32 +0300 Subject: [PATCH] [LPT][Transformations] Dynamic shapes support: functional issues fixes (#7159) * [LPT] ReshapeTransformation fix * [LPT] FQDequantization::empty() fix * [LPT] ConcatTransformation fix * [LPT] MVNTransformation quick refactoring * [TESTS] AddTransformation functional tests: removed unused param * [nGraph] TypeRelaxed::evaluate * [nGraph] Tile fix * [nGraph] Intervals addition: overflow handling * [LPT] MultiplyTransformation: canBeTransformed fix * [TESTS] TypeRelaxed::evaluate: added test * [LPT] MVNTransformation compilation error fix * TypeRelaxed: evaluate fix * macos fix * TypeRelaxed::evaluate: postreview fixes * ConcatTransformation: postreview fix --- .../src/concat.cpp | 31 +++++++- .../src/fake_quantize_dequantization.cpp | 2 +- .../src/multiply.cpp | 12 +-- .../low_precision_transformations/src/mvn.cpp | 8 +- .../src/reshape.cpp | 2 +- .../include/ngraph_ops/type_relaxed.hpp | 66 +++++++++++++++++ .../lp_transformations/add_transformation.cpp | 32 -------- .../reshape_transformation.cpp | 24 ++++++ .../transformations/type_relaxed_tests.cpp | 74 ++++++++++++++++++- ngraph/core/src/interval.cpp | 12 ++- ngraph/core/src/op/tile.cpp | 2 +- ngraph/test/intervals.cpp | 17 +++++ 12 files changed, 231 insertions(+), 51 deletions(-) diff --git a/inference-engine/src/low_precision_transformations/src/concat.cpp b/inference-engine/src/low_precision_transformations/src/concat.cpp index ae95a834d7d..da040d1f897 100644 --- a/inference-engine/src/low_precision_transformations/src/concat.cpp +++ b/inference-engine/src/low_precision_transformations/src/concat.cpp @@ -182,18 +182,36 @@ bool ConcatTransformation::canBeTransformed(const TransformationContext& context return false; } - const auto axis = concat->get_axis(); - const auto outPShape = concat->get_output_partial_shape(0); - const size_t normalizedAxis = ngraph::normalize_axis(concat->get_friendly_name(), axis, outPShape.rank()); + const auto& axis = concat->get_axis(); + const auto& outPShape = concat->get_output_partial_shape(0); + const auto& outRank = outPShape.rank(); + if (outRank.is_dynamic()) { + return false; + } + + const size_t normalizedAxis = ngraph::normalize_axis(concat->get_friendly_name(), axis, outRank); if (normalizedAxis != 1ul) { return false; } - if (outPShape.rank().is_dynamic() || outPShape[normalizedAxis].is_dynamic()) { + if (outPShape[normalizedAxis].is_dynamic()) { return false; } + auto checkConstShape = [&normalizedAxis, &outRank](const std::shared_ptr& constant) { + const size_t rankValue = outRank.get_length(); + Shape constantShape = constant->get_shape(); + + while (constantShape.size() < rankValue) { + constantShape.insert(constantShape.begin(), 1ul); + } + + const auto dqDimensionsCount = std::count_if(constantShape.begin(), constantShape.end(), [](size_t elem) { return elem > 1; }); + const bool dqOnlyByConcatAxis = (dqDimensionsCount == 0) || (dqDimensionsCount == 1 && constantShape[normalizedAxis] != 1ul); + return dqOnlyByConcatAxis; + }; + element::Type precision; for (size_t i = 0ul; i < concat->get_input_size(); i++) { const FakeQuantizeDequantization dequantization = NetworkHelper::getDequantization(concat, i); @@ -201,6 +219,11 @@ bool ConcatTransformation::canBeTransformed(const TransformationContext& context return false; } + if (((dequantization.subtract != nullptr) && (!checkConstShape(dequantization.subtractConstant))) || + ((dequantization.multiply != nullptr) && (!checkConstShape(dequantization.multiplyConstant)))) { + return false; + } + if (precision == element::undefined) { precision = dequantization.data.get_element_type(); } else if (precision != dequantization.data.get_element_type()) { diff --git a/inference-engine/src/low_precision_transformations/src/fake_quantize_dequantization.cpp b/inference-engine/src/low_precision_transformations/src/fake_quantize_dequantization.cpp index 14a0104a46c..aecd9c072ae 100644 --- a/inference-engine/src/low_precision_transformations/src/fake_quantize_dequantization.cpp +++ b/inference-engine/src/low_precision_transformations/src/fake_quantize_dequantization.cpp @@ -34,7 +34,7 @@ FakeQuantizeDequantization::FakeQuantizeDequantization( } bool FakeQuantizeDequantization::empty() const { - return (convert == nullptr) && (subtract == nullptr) && (multiply == nullptr); + return (subtract == nullptr) && (multiply == nullptr); } bool FakeQuantizeDequantization::multiplyHasZeroOrDenormal() const { diff --git a/inference-engine/src/low_precision_transformations/src/multiply.cpp b/inference-engine/src/low_precision_transformations/src/multiply.cpp index cf15d24dc02..ea713ee6b27 100644 --- a/inference-engine/src/low_precision_transformations/src/multiply.cpp +++ b/inference-engine/src/low_precision_transformations/src/multiply.cpp @@ -159,17 +159,17 @@ bool MultiplyTransformation::canBeTransformed(const TransformationContext& conte FakeQuantizeDequantization dequantization1 = pass::low_precision::NetworkHelper::getDequantization(layer, 0ul); FakeQuantizeDequantization dequantization2 = pass::low_precision::NetworkHelper::getDequantization(layer, 1ul); - if ((dequantization1.data.get_node() == nullptr) || - (dequantization1.empty() && !ov::is_type(dequantization1.data.get_node_shared_ptr()) && - !ov::is_type(dequantization2.data.get_node_shared_ptr()))) { + if (dequantization1.data.get_node() == nullptr || dequantization2.data.get_node() == nullptr) { return false; } - if ((dequantization2.data.get_node() == nullptr) || - (dequantization2.empty() && !ov::is_type(dequantization2.data.get_node_shared_ptr()) && - !ov::is_type(dequantization1.data.get_node_shared_ptr()))) { + const bool nonConstantData = !ov::is_type(dequantization1.data.get_node_shared_ptr()) && + !ov::is_type(dequantization2.data.get_node_shared_ptr()); + + if (((dequantization1.empty() || dequantization2.empty()) && nonConstantData)) { return false; } + return EltwiseBaseTransformation::canBeTransformed(context, layer); } diff --git a/inference-engine/src/low_precision_transformations/src/mvn.cpp b/inference-engine/src/low_precision_transformations/src/mvn.cpp index 093747a68b6..34f43655604 100644 --- a/inference-engine/src/low_precision_transformations/src/mvn.cpp +++ b/inference-engine/src/low_precision_transformations/src/mvn.cpp @@ -78,8 +78,6 @@ bool MVNTransformation::canBeTransformed(const TransformationContext& context, s } } - bool isScalarScales = NetworkHelper::isScalarLike(dequantization.multiplyConstant); - AxisSet reduction_axes; if (ov::is_type(mvn)) { reduction_axes = ov::as_type_ptr(mvn)->get_reduction_axes(); @@ -104,6 +102,7 @@ bool MVNTransformation::canBeTransformed(const TransformationContext& context, s } } + bool isScalarScales = NetworkHelper::isScalarLike(dequantization.multiplyConstant); return perTensor && isScalarScales; } @@ -127,9 +126,9 @@ bool MVNTransformation::transform(TransformationContext &context, ngraph::patter FakeQuantizeDequantization dequantization = NetworkHelper::getDequantization(mvn); const auto scalesConst = dequantization.multiplyConstant; + const auto type = scalesConst->get_element_type(); - auto newScalesConst = dequantization.multiplyConstant; - const auto type = scalesConst->get_output_element_type(0); + auto newScalesConst = scalesConst; if (normalizeVariance) { switch (type) { case ngraph::element::Type_t::f16: { @@ -145,6 +144,7 @@ bool MVNTransformation::transform(TransformationContext &context, ngraph::patter } } } + std::shared_ptr newMVN; if (ov::is_type(mvn)) { newMVN = mvn->copy_with_new_inputs({dequantization.data}); diff --git a/inference-engine/src/low_precision_transformations/src/reshape.cpp b/inference-engine/src/low_precision_transformations/src/reshape.cpp index da44763ba0d..ee8e02e1045 100644 --- a/inference-engine/src/low_precision_transformations/src/reshape.cpp +++ b/inference-engine/src/low_precision_transformations/src/reshape.cpp @@ -83,7 +83,7 @@ void reshapeDequantizationConstant(const std::shared_ptr& resha Shape newOperationConstantBroadcastedShape = constant->get_shape(); // add dimensions to broadcast values if (newOperationConstantBroadcastedShape.size() == 2ul) { - newOperationConstantBroadcastedShape.push_back(dimensionsToBroadcast); + newOperationConstantBroadcastedShape[0] = dimensionsToBroadcast; } else { newOperationConstantBroadcastedShape[2] = dimensionsToBroadcast; } diff --git a/inference-engine/src/transformations/include/ngraph_ops/type_relaxed.hpp b/inference-engine/src/transformations/include/ngraph_ops/type_relaxed.hpp index acc7682bb02..ee7a9e4fb04 100644 --- a/inference-engine/src/transformations/include/ngraph_ops/type_relaxed.hpp +++ b/inference-engine/src/transformations/include/ngraph_ops/type_relaxed.hpp @@ -11,6 +11,7 @@ #include +#include #include "ngraph/op/op.hpp" namespace ngraph { @@ -84,6 +85,7 @@ protected: // to infer output data types element::TypeVector m_input_data_types; element::TypeVector m_output_data_types; + element::TypeVector m_original_output_data_types; }; /// Set another type for a specified output for the period of time when an instance of the class exists. @@ -161,6 +163,7 @@ public: } void validate_and_infer_types() override; + bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override; std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; @@ -170,6 +173,61 @@ private: } }; +template +bool TypeRelaxed::evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const { + std::shared_ptr convert; + HostTensorVector casted_inputs(BaseOp::get_input_size()); + for (size_t i = 0; i < BaseOp::get_input_size(); ++i) { + const auto expected_input_type = get_origin_input_type(i); + + if (inputs[i]->get_element_type() == expected_input_type || expected_input_type == element::undefined) { + casted_inputs[i] = inputs[i]; + } else { + if (convert == nullptr) { + convert = std::make_shared(); + } + + convert->set_destination_type(expected_input_type); + casted_inputs[i] = std::make_shared(expected_input_type, inputs[i]->get_shape()); + if (!convert->evaluate({ casted_inputs[i] }, { inputs[i] })) { + return false; + } + } + } + + HostTensorVector original_outputs(BaseOp::get_output_size()); + for (size_t i = 0; i < BaseOp::get_output_size(); ++i) { + const auto expected_output_type = get_overridden_output_type(i); + if (expected_output_type == element::undefined || expected_output_type == m_original_output_data_types[i]) { + original_outputs[i] = outputs[i]; + } else { + original_outputs[i] = std::make_shared(m_original_output_data_types[i], BaseOp::get_output_partial_shape(i)); + } + } + + if (!BaseOp::evaluate(original_outputs, casted_inputs)) { + return false; + } + + for (size_t i = 0; i < BaseOp::get_output_size(); ++i) { + const auto expected_output_type = get_overridden_output_type(i); + + if (expected_output_type != element::undefined && original_outputs[i]->get_element_type() != expected_output_type) { + if (convert == nullptr) { + convert = std::make_shared(); + } + + convert->set_destination_type(expected_output_type); + const auto casted_output = std::make_shared(expected_output_type, original_outputs[i]->get_shape()); + if (!convert->evaluate({ outputs[i] }, { original_outputs[i] })) { + return false; + } + } + } + + return true; +} + template void TypeRelaxed::validate_and_infer_types() { // Remember all input data types @@ -195,6 +253,14 @@ void TypeRelaxed::validate_and_infer_types() { BaseOp::get_input_tensor(i).set_tensor_type(old_input_types[i], BaseOp::get_input_partial_shape(i)); } + if (m_original_output_data_types.empty()) { + m_original_output_data_types = element::TypeVector(BaseOp::get_output_size()); + } + + // Save inferred output types + for (size_t i = 0; i < BaseOp::get_output_size(); ++i) { + m_original_output_data_types[i] = BaseOp::get_output_element_type(i); + } // Override (some) output types for (size_t i = 0; i < BaseOp::get_output_size(); ++i) { diff --git a/inference-engine/tests/functional/inference_engine/lp_transformations/add_transformation.cpp b/inference-engine/tests/functional/inference_engine/lp_transformations/add_transformation.cpp index 529cd008547..82ce2c47332 100644 --- a/inference-engine/tests/functional/inference_engine/lp_transformations/add_transformation.cpp +++ b/inference-engine/tests/functional/inference_engine/lp_transformations/add_transformation.cpp @@ -62,7 +62,6 @@ public: operationType(std::move(operationType)) {} }; - ngraph::element::Type precision; bool broadcast; int constInput; TestTransformationParams params; @@ -169,7 +168,6 @@ const std::vector> inputSh const std::vector testValuesWithoutConstantBranches = { // Multiply with zero on the first branch { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -192,7 +190,6 @@ const std::vector testValuesWithoutConstantBranches }, // Multiply with zero on the second branch { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -245,7 +242,6 @@ const std::vector testValuesWithoutConstantBranches // \ / // Add { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -298,7 +294,6 @@ const std::vector testValuesWithoutConstantBranches // \ / // Add { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -328,7 +323,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -350,7 +344,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -372,7 +365,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -394,7 +386,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -419,7 +410,6 @@ const std::vector testValuesWithoutConstantBranches // I8 + broadcast { - ngraph::element::f32, true, -1, LayerTransformation::createParamsU8I8(), @@ -441,7 +431,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, true, -1, LayerTransformation::createParamsU8I8(), @@ -463,7 +452,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, true, -1, LayerTransformation::createParamsU8I8(), @@ -485,7 +473,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, true, -1, LayerTransformation::createParamsU8I8(), @@ -507,7 +494,6 @@ const std::vector testValuesWithoutConstantBranches "" }, { - ngraph::element::f32, true, -1, LayerTransformation::createParamsU8I8(), @@ -531,7 +517,6 @@ const std::vector testValuesWithoutConstantBranches // convolution before FQ (choose that branch) { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -554,7 +539,6 @@ const std::vector testValuesWithoutConstantBranches }, // convolution with multiple consumers before FQ ( FP32 on other branch due to possible quantize fusing ) { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -577,7 +561,6 @@ const std::vector testValuesWithoutConstantBranches }, // group convolution before FQ (choose that branch) { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -645,7 +628,6 @@ const std::vector testValuesWithFirstConstantBranch // \ / // Multiply { - ngraph::element::f32, false, 0, LayerTransformation::createParamsU8I8(), @@ -722,7 +704,6 @@ const std::vector testValuesWithSecondConstantBranc // \ / // Multiply { - ngraph::element::f32, false, 1, LayerTransformation::createParamsU8I8(), @@ -772,7 +753,6 @@ const std::vector> inputSh const std::vector specialTestValues = { // constant input: Add -> Subtract { - ngraph::element::f32, false, 1, LayerTransformation::createParamsU8I8(), @@ -797,7 +777,6 @@ const std::vector specialTestValues = { // constant input: Add -> Subtract { - ngraph::element::f32, false, 0, LayerTransformation::createParamsU8I8(), @@ -839,7 +818,6 @@ const std::vector> inputSh const std::vector specialTestValues = { { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -888,7 +866,6 @@ const std::vector> inputSh const std::vector testValues = { { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -910,7 +887,6 @@ const std::vector testValues = { "" }, { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -933,7 +909,6 @@ const std::vector testValues = { }, // multiply with zero { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -982,7 +957,6 @@ const std::vector> inputSh const std::vector testValues = { { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -1004,7 +978,6 @@ const std::vector testValues = { "" }, { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -1027,7 +1000,6 @@ const std::vector testValues = { }, // multiply with zero { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8(), @@ -1077,7 +1049,6 @@ const std::vector> inputSh const std::vector testValues = { // FP32 model, quantized branch: 1 { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8().setUpdatePrecisions(false), @@ -1100,7 +1071,6 @@ const std::vector testValues = { }, // FP32 model, quantized branch: 0 { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8().setUpdatePrecisions(false), @@ -1123,7 +1093,6 @@ const std::vector testValues = { }, // INT8 model (FQ decomposition before LPT), quantized branch: 1 { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8().setUpdatePrecisions(false), @@ -1146,7 +1115,6 @@ const std::vector testValues = { }, // INT8 model (FQ decomposition before LPT), quantized branch: 0 { - ngraph::element::f32, false, -1, LayerTransformation::createParamsU8I8().setUpdatePrecisions(false), diff --git a/inference-engine/tests/functional/inference_engine/lp_transformations/reshape_transformation.cpp b/inference-engine/tests/functional/inference_engine/lp_transformations/reshape_transformation.cpp index 6a67202125c..38502dc6834 100644 --- a/inference-engine/tests/functional/inference_engine/lp_transformations/reshape_transformation.cpp +++ b/inference-engine/tests/functional/inference_engine/lp_transformations/reshape_transformation.cpp @@ -931,6 +931,30 @@ const std::vector testValues = { {{0.1f, 0.01f, 0.1f}, ngraph::element::f32, {1, 3}} } } + }, + // U8: without subtract 2D -> 2D + { + { Dimension::dynamic(), 2 }, + { -1, 6 }, + LayerTransformation::createParamsU8I8(), + { + ngraph::element::u8, + { + {ngraph::element::f32}, + {}, + {{0.1f, 0.02f}, ngraph::element::f32, {1, 2}} + } + }, + { + ngraph::element::u8, + {{}, {}, {}}, + ngraph::element::u8, + { + {ngraph::element::f32}, + {}, + {{0.1f, 0.02f, 0.1f, 0.02f, 0.1f, 0.02f}, ngraph::element::f32, {1, 6}} + } + } } }; diff --git a/inference-engine/tests/functional/inference_engine/transformations/type_relaxed_tests.cpp b/inference-engine/tests/functional/inference_engine/transformations/type_relaxed_tests.cpp index 44a2a74f20b..8cd8894bf66 100644 --- a/inference-engine/tests/functional/inference_engine/transformations/type_relaxed_tests.cpp +++ b/inference-engine/tests/functional/inference_engine/transformations/type_relaxed_tests.cpp @@ -5,6 +5,7 @@ #include #include "common_test_utils/test_common.hpp" #include +#include #include @@ -295,4 +296,75 @@ TEST_F(TypeRelaxedTests, OneOutputMultipleInputPorts) { relaxed_op->validate_and_infer_types(); ASSERT_EQ(param1->output(0).get_element_type(), element::i64); } -} \ No newline at end of file +} + +TEST_F(TypeRelaxedTests, ConstantFoldingCheck) { + std::shared_ptr f; + { + auto const1 = ngraph::opset1::Constant::create(element::i32, ngraph::Shape{}, { 2 }); + auto const2 = ngraph::opset1::Constant::create(element::i32, ngraph::Shape{}, { 2 }); + auto equal = ngraph::opset1::Equal(const1, const2); + auto relaxed_equal = make_shared>(equal, TypeVector{}, TypeVector{ element::u8 }); + + f = make_shared(ngraph::OutputVector{ relaxed_equal }, ngraph::ParameterVector{}); + + ASSERT_NO_THROW(ngraph::pass::ConstantFolding().run_on_function(f)); + auto layer_before_result = f->get_result()->get_input_node_shared_ptr(0); + ASSERT_TRUE(ngraph::is_type(layer_before_result)); + } +} + +TEST_F(TypeRelaxedTests, ConstantFoldingCheck1) { + std::shared_ptr f; + { + auto const1 = ngraph::opset1::Constant::create(element::i32, ngraph::Shape{}, { 2 }); + auto const2 = ngraph::opset1::Constant::create(element::i32, ngraph::Shape{}, { 2 }); + auto equal = ngraph::opset1::Equal(const1, const2); + auto relaxed_equal = make_shared>(equal, TypeVector{}, TypeVector{ element::boolean }); + + f = make_shared(ngraph::OutputVector{ relaxed_equal }, ngraph::ParameterVector{}); + + ASSERT_NO_THROW(ngraph::pass::ConstantFolding().run_on_function(f)); + auto layer_before_result = f->get_result()->get_input_node_shared_ptr(0); + ASSERT_TRUE(ngraph::is_type(layer_before_result)); + } +} + +TEST_F(TypeRelaxedTests, ConstantFoldingCheck2) { + std::shared_ptr f; + { + auto const1 = ngraph::opset1::Constant::create(element::u8, ngraph::Shape{}, { 2 }); + auto const2 = ngraph::opset1::Constant::create(element::i8, ngraph::Shape{}, { 2 }); + + auto original_input_types = TypeVector{ element::i32, element::i32 }; + auto relaxed_equal = std::make_shared>( + ngraph::element::TypeVector{ element::i32, element::i32 }, + ngraph::element::TypeVector{ element::u8 }, + ngraph::op::TemporaryReplaceOutputType(const1, element::i32).get(), + ngraph::op::TemporaryReplaceOutputType(const2, element::i32).get()); + + f = make_shared(ngraph::OutputVector{ relaxed_equal }, ngraph::ParameterVector{}); + + ASSERT_NO_THROW(ngraph::pass::ConstantFolding().run_on_function(f)); + auto layer_before_result = f->get_result()->get_input_node_shared_ptr(0); + ASSERT_TRUE(ngraph::is_type(layer_before_result)); + } +} + +TEST_F(TypeRelaxedTests, ConstantFoldingCheck3) { + std::shared_ptr f; + { + auto const1 = ngraph::opset1::Constant::create(element::i32, ngraph::Shape{}, { 2 }); + auto const2 = ngraph::opset1::Constant::create(element::i32, ngraph::Shape{}, { 2 }); + auto equal = ngraph::opset1::Equal(const1, const2); + + auto original_input_types = TypeVector{ element::f32, element::f32 }; + auto relaxed_equal = make_shared>(equal, original_input_types, TypeVector{ element::u8 }); + + f = make_shared(ngraph::OutputVector{ relaxed_equal }, ngraph::ParameterVector{}); + + ASSERT_NO_THROW(ngraph::pass::ConstantFolding().run_on_function(f)); + auto layer_before_result = f->get_result()->get_input_node_shared_ptr(0); + ASSERT_TRUE(ngraph::is_type(layer_before_result)); + } +} diff --git a/ngraph/core/src/interval.cpp b/ngraph/core/src/interval.cpp index 469132f3576..d9aa326772d 100644 --- a/ngraph/core/src/interval.cpp +++ b/ngraph/core/src/interval.cpp @@ -21,7 +21,17 @@ Interval::value_type clip_times(Interval::value_type a, Interval::value_type b) } } Interval::value_type clip_add(Interval::value_type a, Interval::value_type b) { - return (a == Interval::s_max || b == Interval::s_max) ? Interval::s_max : a + b; + if (a == Interval::s_max || b == Interval::s_max) { + return Interval::s_max; + } + + // check overflow without undefined behavior: a + b <= max + const static auto max = std::numeric_limits::max(); + if (b > (max - a)) { + return Interval::s_max; + } + + return a + b; } Interval::value_type clip_minus(Interval::value_type a, Interval::value_type b) { if (a <= b) { diff --git a/ngraph/core/src/op/tile.cpp b/ngraph/core/src/op/tile.cpp index 49cf2530ab8..0dbffbc08bf 100644 --- a/ngraph/core/src/op/tile.cpp +++ b/ngraph/core/src/op/tile.cpp @@ -73,7 +73,7 @@ bool op::v0::Tile::evaluate_tile(const HostTensorVector& outputs, const HostTens const auto& data = inputs[0]; const auto& axis = inputs[1]; auto& output = outputs[0]; - auto repeats_val = read_vector(axis); + auto repeats_val = read_index_vector(axis); auto repeats_rank = repeats_val.size(); ov::StaticShape data_shape = data->get_shape(); auto data_rank = data_shape.size(); diff --git a/ngraph/test/intervals.cpp b/ngraph/test/intervals.cpp index 7f1e383cdd4..314e8248e3e 100644 --- a/ngraph/test/intervals.cpp +++ b/ngraph/test/intervals.cpp @@ -143,3 +143,20 @@ TEST(intervals, sets) { } EXPECT_TRUE(Interval(min_int, max_int) == a_int_b); } + +TEST(intervals, corner_cases) { + Interval::value_type max = numeric_limits::max(); + Interval almost_max(0, max - 10); + Interval dynamic(0, max); + Interval zero(0, 0); + + EXPECT_TRUE(almost_max + almost_max == dynamic); + EXPECT_TRUE(dynamic + almost_max == dynamic); + EXPECT_TRUE(almost_max + dynamic == dynamic); + EXPECT_TRUE(dynamic - almost_max == dynamic); + + EXPECT_TRUE(dynamic * almost_max == dynamic); + EXPECT_TRUE(almost_max * dynamic == dynamic); + EXPECT_TRUE(zero * almost_max == zero); + EXPECT_TRUE(almost_max * zero == zero); +}