From c9d0292929cecb8988b8473ae4fe13022a5f8a81 Mon Sep 17 00:00:00 2001 From: Mateusz Tabaka Date: Wed, 24 Mar 2021 16:00:30 +0100 Subject: [PATCH] =?UTF-8?q?Add=20DilatedConvolutionConverter,=20BatchToSpa?= =?UTF-8?q?ceFusion=20and=20SpaceToBatchF=E2=80=A6=20(#4689)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch_to_space_fusion.hpp | 37 +++ .../dilated_convolution_converter.hpp | 35 +++ .../space_to_batch_fusion.hpp | 38 +++ .../batch_to_space_fusion.cpp | 119 +++++++++ .../common_optimizations.cpp | 6 + .../dilated_convolution_converter.cpp | 92 +++++++ .../space_to_batch_fusion.cpp | 103 ++++++++ .../transformations/batch_to_space_fusion.cpp | 207 +++++++++++++++ .../dilated_convolution_converter.cpp | 101 ++++++++ .../transformations/space_to_batch_fusion.cpp | 236 ++++++++++++++++++ .../include/ngraph/pattern/op/pattern.hpp | 3 + ngraph/core/src/op/convert.cpp | 2 + ngraph/core/src/pattern/op/pattern.cpp | 7 + ngraph/test/constant_folding.cpp | 34 ++- 14 files changed, 1011 insertions(+), 9 deletions(-) create mode 100644 inference-engine/src/transformations/include/transformations/common_optimizations/batch_to_space_fusion.hpp create mode 100644 inference-engine/src/transformations/include/transformations/common_optimizations/dilated_convolution_converter.hpp create mode 100644 inference-engine/src/transformations/include/transformations/common_optimizations/space_to_batch_fusion.hpp create mode 100644 inference-engine/src/transformations/src/transformations/common_optimizations/batch_to_space_fusion.cpp create mode 100644 inference-engine/src/transformations/src/transformations/common_optimizations/dilated_convolution_converter.cpp create mode 100644 inference-engine/src/transformations/src/transformations/common_optimizations/space_to_batch_fusion.cpp create mode 100644 inference-engine/tests/functional/inference_engine/transformations/batch_to_space_fusion.cpp create mode 100644 inference-engine/tests/functional/inference_engine/transformations/dilated_convolution_converter.cpp create mode 100644 inference-engine/tests/functional/inference_engine/transformations/space_to_batch_fusion.cpp diff --git a/inference-engine/src/transformations/include/transformations/common_optimizations/batch_to_space_fusion.hpp b/inference-engine/src/transformations/include/transformations/common_optimizations/batch_to_space_fusion.hpp new file mode 100644 index 00000000000..0db4233827e --- /dev/null +++ b/inference-engine/src/transformations/include/transformations/common_optimizations/batch_to_space_fusion.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +#include + +#include + +namespace ngraph { +namespace pass { + +class TRANSFORMATIONS_API BatchToSpaceFusion; + +} // namespace pass +} // namespace ngraph + +/** + * @ingroup ie_transformation_common_api + * @brief BatchToSpaceFusion transformation replaces following graph: + * Transpose (or Reshape) -> DepthToSpace -> StridedSlice -> Transpose (or Reshape) + * to BatchToSpace + * Restrictions: + * - input rank must be 4 + * - Transpose permutation must be [1, 0, 2, 3] + * - DepthToSpaceMode must be BLOCKS_FIRST + */ + +class ngraph::pass::BatchToSpaceFusion: public ngraph::pass::MatcherPass { +public: + NGRAPH_RTTI_DECLARATION; + BatchToSpaceFusion(); +}; diff --git a/inference-engine/src/transformations/include/transformations/common_optimizations/dilated_convolution_converter.hpp b/inference-engine/src/transformations/include/transformations/common_optimizations/dilated_convolution_converter.hpp new file mode 100644 index 00000000000..44b070b08d8 --- /dev/null +++ b/inference-engine/src/transformations/include/transformations/common_optimizations/dilated_convolution_converter.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +#include + +#include + +namespace ngraph { +namespace pass { + +class TRANSFORMATIONS_API DilatedConvolutionConverter; + +} // namespace pass +} // namespace ngraph + +/** + * @ingroup ie_transformation_common_api + * @brief DilatedConvolutionConverter transformation replaces following graph: + * SpaceToBatch -> Convolution -> BatchToSpace + * to a single Convolution node with updated pads and dilations + * Restrictions: + * - pads in SpaceToBatch must have 0 on first and second position + */ + +class ngraph::pass::DilatedConvolutionConverter: public ngraph::pass::MatcherPass { +public: + NGRAPH_RTTI_DECLARATION; + DilatedConvolutionConverter(); +}; diff --git a/inference-engine/src/transformations/include/transformations/common_optimizations/space_to_batch_fusion.hpp b/inference-engine/src/transformations/include/transformations/common_optimizations/space_to_batch_fusion.hpp new file mode 100644 index 00000000000..4ae1f45daa0 --- /dev/null +++ b/inference-engine/src/transformations/include/transformations/common_optimizations/space_to_batch_fusion.hpp @@ -0,0 +1,38 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +#include + +#include + +namespace ngraph { +namespace pass { + +class TRANSFORMATIONS_API SpaceToBatchFusion; + +} // namespace pass +} // namespace ngraph + +/** + * @ingroup ie_transformation_common_api + * @brief SpaceToBatchFusion transformation replaces following graph: + * Transpose (or Reshape) -> Pad -> SpaceToDepth -> Transpose (or Reshape) + * to SpaceToBatch + * Restrictions: + * - input rank must be 4 + * - Transpose permutation must be [1, 0, 2, 3] + * - pad value is 0, PadMode is CONSTANT + * - SpaceToDepthMode must be BLOCKS_FIRST + */ + +class ngraph::pass::SpaceToBatchFusion: public ngraph::pass::MatcherPass { +public: + NGRAPH_RTTI_DECLARATION; + SpaceToBatchFusion(); +}; diff --git a/inference-engine/src/transformations/src/transformations/common_optimizations/batch_to_space_fusion.cpp b/inference-engine/src/transformations/src/transformations/common_optimizations/batch_to_space_fusion.cpp new file mode 100644 index 00000000000..3c794e44a0e --- /dev/null +++ b/inference-engine/src/transformations/src/transformations/common_optimizations/batch_to_space_fusion.cpp @@ -0,0 +1,119 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "transformations/common_optimizations/batch_to_space_fusion.hpp" +#include "transformations/utils/utils.hpp" + +#include +#include + +#include +#include +#include +#include +#include "itt.hpp" + + +NGRAPH_RTTI_DEFINITION(ngraph::pass::BatchToSpaceFusion, "BatchToSpaceFusion", 0); + +ngraph::pass::BatchToSpaceFusion::BatchToSpaceFusion() { + MATCHER_SCOPE(BatchToSpaceFusion); + auto data_pattern = pattern::any_input(pattern::has_static_shape()); + auto reshape_before_pattern = pattern::wrap_type({data_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto trans_before_pattern = pattern::wrap_type({data_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto reshape_or_transpose_before_pattern = std::make_shared(OutputVector{reshape_before_pattern, trans_before_pattern}); + auto depth_to_space_pattern = pattern::wrap_type({reshape_or_transpose_before_pattern}); + auto starts_pattern = pattern::wrap_type(); + auto ends_pattern = pattern::wrap_type(); + auto slice_pattern = pattern::wrap_type({depth_to_space_pattern, starts_pattern, ends_pattern, + pattern::wrap_type()}); + auto reshape_after_pattern = pattern::wrap_type({slice_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto trans_after_pattern = pattern::wrap_type({slice_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto reshape_or_transpose_after_pattern = std::make_shared(OutputVector{reshape_after_pattern, trans_after_pattern}); + + ngraph::matcher_pass_callback callback = [=](pattern::Matcher& m) { + const auto& pattern_map = m.get_pattern_value_map(); + + auto get_reshape_or_transpose = [&pattern_map] (const std::shared_ptr& reshape_pattern, + const std::shared_ptr& trans_pattern) -> std::shared_ptr { + if (pattern_map.count(reshape_pattern)) + return pattern_map.at(reshape_pattern).get_node_shared_ptr(); + if (pattern_map.count(trans_pattern)) + return pattern_map.at(trans_pattern).get_node_shared_ptr(); + return nullptr; + }; + auto check_input_output_shape = [] (const std::shared_ptr& node) -> bool { + const auto& input_shape = node->get_input_shape(0); + const auto& output_shape = node->get_output_shape(0); + // Transpose permutation has to be [1, 0, 2, 3] + return input_shape[0] == output_shape[1] && + input_shape[1] == output_shape[0] && + input_shape[2] == output_shape[2] && + input_shape[3] == output_shape[3]; + }; + + std::shared_ptr reshape_or_trans_before = get_reshape_or_transpose(reshape_before_pattern, trans_before_pattern); + if (!reshape_or_trans_before) + return false; + if (!check_input_output_shape(reshape_or_trans_before)) + return false; + std::shared_ptr reshape_or_trans_after = get_reshape_or_transpose(reshape_after_pattern, trans_after_pattern); + if (!reshape_or_trans_after) + return false; + if (!check_input_output_shape(reshape_or_trans_after)) + return false; + + auto depth_to_space = std::dynamic_pointer_cast(pattern_map.at(depth_to_space_pattern).get_node_shared_ptr()); + if (!depth_to_space) + return false; + if (depth_to_space->get_mode() != opset6::DepthToSpace::DepthToSpaceMode::BLOCKS_FIRST) + return false; + const auto& dts_shape = depth_to_space->get_shape(); + if (dts_shape.size() != 4) + return false; + auto block_size = static_cast(depth_to_space->get_block_size()); + auto block_shape = op::Constant::create(element::i64, Shape{4}, + std::vector{1, 1, block_size, block_size}); + auto starts = std::dynamic_pointer_cast(pattern_map.at(starts_pattern).get_node_shared_ptr()); + if (!starts) + return false; + auto ends = std::dynamic_pointer_cast(pattern_map.at(ends_pattern).get_node_shared_ptr()); + if (!ends) + return false; + auto starts_value = starts->cast_vector(); + auto ends_value = ends->cast_vector(); + // Convert StridedSlice's 'ends' input to BatchToSpace's 'crops_ends' + for (size_t i = 0; i < ends_value.size(); i++) { + if (ends_value[i] < 0) { + // negative ends become positive crops + // e.g. ends[i] == -2 means cropping i-th dimension by 2 from the back + ends_value[i] = -ends_value[i]; + } else if (ends_value[i] > static_cast(dts_shape[i])) { + // no cropping from the back if ends[i] > shape[i] + ends_value[i] = 0; + } else { + // else if ends[i] is positive and within [0, shape[i]] - crop the difference: shape[i] - ends[i] + ends_value[i] = dts_shape[i] - ends_value[i]; + } + } + auto crops_begin = op::Constant::create(element::i64, Shape{4}, starts_value); + auto crops_end = op::Constant::create(element::i64, Shape{4}, ends_value); + auto batch_to_space = register_new_node(pattern_map.at(data_pattern), block_shape, crops_begin, crops_end); + batch_to_space->set_friendly_name(reshape_or_trans_after->get_friendly_name()); + + copy_runtime_info({ + reshape_or_trans_before, + depth_to_space, + pattern_map.at(slice_pattern).get_node_shared_ptr(), + reshape_or_trans_after + }, + batch_to_space); + replace_node(reshape_or_trans_after, batch_to_space); + + return true; + }; + + auto m = std::make_shared(reshape_or_transpose_after_pattern, matcher_name); + this->register_matcher(m, callback); +} diff --git a/inference-engine/src/transformations/src/transformations/common_optimizations/common_optimizations.cpp b/inference-engine/src/transformations/src/transformations/common_optimizations/common_optimizations.cpp index 36c239ee267..53b1710619f 100644 --- a/inference-engine/src/transformations/src/transformations/common_optimizations/common_optimizations.cpp +++ b/inference-engine/src/transformations/src/transformations/common_optimizations/common_optimizations.cpp @@ -35,6 +35,9 @@ #include "transformations/common_optimizations/mvn_fusion.hpp" #include "transformations/common_optimizations/binarize_weights.hpp" #include "transformations/common_optimizations/conv_to_binary_conv.hpp" +#include "transformations/common_optimizations/space_to_batch_fusion.hpp" +#include "transformations/common_optimizations/batch_to_space_fusion.hpp" +#include "transformations/common_optimizations/dilated_convolution_converter.hpp" #include "transformations/op_conversions/bidirectional_sequences_decomposition.hpp" #include "transformations/op_conversions/convert_pad_to_group_conv.hpp" #include "transformations/op_conversions/convert_divide.hpp" @@ -104,6 +107,9 @@ bool ngraph::pass::CommonOptimizations::run_on_function(std::shared_ptradd_matcher(); common_fusions->add_matcher(); common_fusions->add_matcher(); + common_fusions->add_matcher(); + common_fusions->add_matcher(); + common_fusions->add_matcher(); common_fusions->set_name("ngraph::pass::CommonFusions"); manager.register_pass(); diff --git a/inference-engine/src/transformations/src/transformations/common_optimizations/dilated_convolution_converter.cpp b/inference-engine/src/transformations/src/transformations/common_optimizations/dilated_convolution_converter.cpp new file mode 100644 index 00000000000..ed4a936755d --- /dev/null +++ b/inference-engine/src/transformations/src/transformations/common_optimizations/dilated_convolution_converter.cpp @@ -0,0 +1,92 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "transformations/common_optimizations/dilated_convolution_converter.hpp" +#include "transformations/utils/utils.hpp" + +#include +#include + +#include +#include +#include +#include "itt.hpp" + + +NGRAPH_RTTI_DEFINITION(ngraph::pass::DilatedConvolutionConverter, "DilatedConvolutionConverter", 0); + +ngraph::pass::DilatedConvolutionConverter::DilatedConvolutionConverter() { + MATCHER_SCOPE(DilatedConvolutionConverter); + auto data_pattern = pattern::any_input(); + auto block_shape_pattern = pattern::wrap_type(); + auto pads_begin_pattern = pattern::wrap_type(); + auto pads_end_pattern = pattern::wrap_type(); + auto space_to_batch_pattern = pattern::wrap_type({data_pattern, block_shape_pattern, pads_begin_pattern, pads_end_pattern}); + auto conv_pattern = pattern::wrap_type({space_to_batch_pattern, pattern::any_input()}); + auto crops_begin_pattern = pattern::wrap_type(); + auto crops_end_pattern = pattern::wrap_type(); + auto batch_to_space_pattern = pattern::wrap_type({conv_pattern, pattern::any_input(), + crops_begin_pattern, crops_end_pattern}); + + matcher_pass_callback callback = [=](pattern::Matcher& m) { + const auto& pattern_map = m.get_pattern_value_map(); + auto block_shape = std::dynamic_pointer_cast(pattern_map.at(block_shape_pattern).get_node_shared_ptr()); + if (!block_shape) + return false; + auto pads_begin = std::dynamic_pointer_cast(pattern_map.at(pads_begin_pattern).get_node_shared_ptr()); + if (!pads_begin) + return false; + auto pads_end = std::dynamic_pointer_cast(pattern_map.at(pads_end_pattern).get_node_shared_ptr()); + if (!pads_end) + return false; + auto crops_begin = std::dynamic_pointer_cast(pattern_map.at(crops_begin_pattern).get_node_shared_ptr()); + if (!crops_begin) + return false; + auto crops_end = std::dynamic_pointer_cast(pattern_map.at(crops_end_pattern).get_node_shared_ptr()); + if (!crops_end) + return false; + auto conv = std::dynamic_pointer_cast(pattern_map.at(conv_pattern).get_node_shared_ptr()); + if (!conv) + return false; + + auto block_shape_val = block_shape->cast_vector(); + + auto dilations = conv->get_dilations(); + for (size_t i = 0; i < dilations.size(); i++) + dilations[i] = block_shape_val[i + 2]; + auto pads_begin_val = pads_begin->cast_vector(); + auto pads_end_val = pads_end->cast_vector(); + if (!(pads_begin_val[0] == 0 && + pads_begin_val[1] == 0 && + pads_end_val[0] == 0 && + pads_end_val[1] == 0)) + return false; + auto crops_begin_val = crops_begin->cast_vector(); + auto crops_end_val = crops_end->cast_vector(); + std::vector new_pads_begin; + for (size_t i = 2; i < pads_begin_val.size(); i++) + new_pads_begin.push_back(pads_begin_val[i] - crops_begin_val[i]); + std::vector new_pads_end; + for (size_t i = 2; i < pads_end_val.size(); i++) + new_pads_end.push_back(pads_end_val[i] - crops_end_val[i]); + auto new_conv = register_new_node(pattern_map.at(data_pattern), conv->input_value(1), + conv->get_strides(), new_pads_begin, new_pads_end, dilations, op::PadType::EXPLICIT); + + auto batch_to_space = pattern_map.at(batch_to_space_pattern).get_node_shared_ptr(); + new_conv->set_friendly_name(batch_to_space->get_friendly_name()); + + copy_runtime_info({ + pattern_map.at(space_to_batch_pattern).get_node_shared_ptr(), + pattern_map.at(conv_pattern).get_node_shared_ptr(), + batch_to_space, + }, + new_conv); + replace_node(batch_to_space, new_conv); + + return true; + }; + + auto m = std::make_shared(batch_to_space_pattern, matcher_name); + this->register_matcher(m, callback); +} diff --git a/inference-engine/src/transformations/src/transformations/common_optimizations/space_to_batch_fusion.cpp b/inference-engine/src/transformations/src/transformations/common_optimizations/space_to_batch_fusion.cpp new file mode 100644 index 00000000000..a9943f76e91 --- /dev/null +++ b/inference-engine/src/transformations/src/transformations/common_optimizations/space_to_batch_fusion.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "transformations/common_optimizations/space_to_batch_fusion.hpp" +#include "transformations/utils/utils.hpp" + +#include +#include + +#include +#include +#include +#include +#include "itt.hpp" + + +NGRAPH_RTTI_DEFINITION(ngraph::pass::SpaceToBatchFusion, "SpaceToBatchFusion", 0); + +ngraph::pass::SpaceToBatchFusion::SpaceToBatchFusion() { + MATCHER_SCOPE(SpaceToBatchFusion); + auto data_pattern = pattern::any_input(); + auto reshape_before_pattern = pattern::wrap_type({data_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto trans_before_pattern = pattern::wrap_type({data_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto reshape_or_transpose_before_pattern = std::make_shared(OutputVector{reshape_before_pattern, trans_before_pattern}); + auto pads_begin_pattern = pattern::wrap_type(); + auto pads_end_pattern = pattern::wrap_type(); + auto pad_value = pattern::wrap_type(); + auto pad_pattern = pattern::wrap_type({reshape_or_transpose_before_pattern, pads_begin_pattern, pads_end_pattern, pad_value}); + auto space_to_depth_pattern = pattern::wrap_type({pad_pattern}, pattern::has_static_shape()); + auto reshape_after_pattern = pattern::wrap_type({space_to_depth_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto trans_after_pattern = pattern::wrap_type({space_to_depth_pattern, pattern::wrap_type()}, pattern::rank_equals(4)); + auto reshape_or_transpose_after_pattern = std::make_shared(OutputVector{reshape_after_pattern, trans_after_pattern}); + + matcher_pass_callback callback = [=](pattern::Matcher& m) { + const auto& pattern_map = m.get_pattern_value_map(); + + auto get_reshape_or_transpose = [&pattern_map] (const std::shared_ptr& reshape_pattern, + const std::shared_ptr& trans_pattern) -> std::shared_ptr { + if (pattern_map.count(reshape_pattern)) + return pattern_map.at(reshape_pattern).get_node_shared_ptr(); + if (pattern_map.count(trans_pattern)) + return pattern_map.at(trans_pattern).get_node_shared_ptr(); + return nullptr; + }; + auto check_input_output_shape = [] (const std::shared_ptr& node) -> bool { + const auto& input_shape = node->get_input_shape(0); + const auto& output_shape = node->get_output_shape(0); + // Transpose permutation has to be [1, 0, 2, 3] + return input_shape[0] == output_shape[1] && + input_shape[1] == output_shape[0] && + input_shape[2] == output_shape[2] && + input_shape[3] == output_shape[3]; + }; + + std::shared_ptr reshape_or_trans_before = get_reshape_or_transpose(reshape_before_pattern, trans_before_pattern); + if (!reshape_or_trans_before) + return false; + std::shared_ptr reshape_or_trans_after = get_reshape_or_transpose(reshape_after_pattern, trans_after_pattern); + if (!reshape_or_trans_after) + return false; + if (!check_input_output_shape(reshape_or_trans_before)) + return false; + if (!check_input_output_shape(reshape_or_trans_after)) + return false; + + auto pad = std::dynamic_pointer_cast(pattern_map.at(pad_pattern).get_node_shared_ptr()); + if (!pad || pad->get_pad_mode() != op::PadMode::CONSTANT) + return false; + auto pad_value_const = std::dynamic_pointer_cast(pattern_map.at(pad_value).get_node_shared_ptr()); + if (!pad_value_const) + return false; + auto pad_value = pad_value_const->cast_vector(); + if (pad_value.size() != 1 || pad_value[0] != 0.0f) + return false; + + auto space_to_depth = std::dynamic_pointer_cast(pattern_map.at(space_to_depth_pattern).get_node_shared_ptr()); + if (!space_to_depth) + return false; + if (space_to_depth->get_mode() != opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST) + return false; + auto block_size = static_cast(space_to_depth->get_block_size()); + auto block_shape = op::Constant::create(element::i64, Shape{4}, + std::vector{1, 1, block_size, block_size}); + auto space_to_batch = register_new_node(pattern_map.at(data_pattern), block_shape, + pattern_map.at(pads_begin_pattern), pattern_map.at(pads_end_pattern)); + space_to_batch->set_friendly_name(reshape_or_trans_after->get_friendly_name()); + + copy_runtime_info({ + reshape_or_trans_before, + pad, + space_to_depth, + reshape_or_trans_after, + }, + space_to_batch); + replace_node(reshape_or_trans_after, space_to_batch); + + return true; + }; + + auto m = std::make_shared(reshape_or_transpose_after_pattern, matcher_name); + this->register_matcher(m, callback); +} diff --git a/inference-engine/tests/functional/inference_engine/transformations/batch_to_space_fusion.cpp b/inference-engine/tests/functional/inference_engine/transformations/batch_to_space_fusion.cpp new file mode 100644 index 00000000000..5525d7d3dab --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/transformations/batch_to_space_fusion.cpp @@ -0,0 +1,207 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common_test_utils/ngraph_test_utils.hpp" + + +using namespace testing; +using namespace ngraph; + + +TEST(TransformationTests, BatchToSpaceFusionTranspose) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::BLOCKS_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 1, -1, 2}), + std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0}); + auto trans_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto batch_to_space = std::make_shared(data, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 1}), + op::Constant::create(element::i64, Shape{4}, {1, 2, 1, 14})); + + f_ref = std::make_shared(NodeVector{batch_to_space}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, BatchToSpaceFusionReshape) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{4, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::BLOCKS_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{4}, {0, 0, 3, 0}), + op::Constant::create(element::i64, Shape{4}, {2, 1, 7, -2}), + std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0}); + auto reshape_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{4}, {1, 2, 4, 14}), false); + f = std::make_shared(NodeVector{reshape_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{4, 3, 4, 8}); + auto batch_to_space = std::make_shared(data, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 3, 0}), + op::Constant::create(element::i64, Shape{4}, {1, 0, 1, 2})); + + f_ref = std::make_shared(NodeVector{batch_to_space}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeBatchToSpaceFusionInvalidTransposePerm) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {2, 0, 1, 3})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::BLOCKS_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 1, -1, 2}), + std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0}); + auto trans_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {2, 0, 1, 3})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::BLOCKS_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 1, -1, 2}), + std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0}); + auto trans_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f_ref = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeBatchToSpaceFusionInvalidMode) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::DEPTH_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 1, -1, 2}), + std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0}); + auto trans_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::DEPTH_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 1, -1, 2}), + std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0}); + auto trans_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f_ref = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeBatchToSpaceFusionInvalidRank) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::BLOCKS_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{5}, {0, 0, 2, 1, 1}), + op::Constant::create(element::i64, Shape{5}, {2, 1, -1, 2, 2}), + std::vector{0, 0, 0, 0, 0}, std::vector{0, 0, 0, 0, 0}); + auto trans_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + auto depth_to_space = std::make_shared(trans_before, opset6::DepthToSpace::DepthToSpaceMode::BLOCKS_FIRST, 2); + auto slice = std::make_shared(depth_to_space, + op::Constant::create(element::i64, Shape{5}, {0, 0, 2, 1, 1}), + op::Constant::create(element::i64, Shape{5}, {2, 1, -1, 2, 2}), + std::vector{0, 0, 0, 0, 0}, std::vector{0, 0, 0, 0, 0}); + auto trans_after = std::make_shared(slice, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + f_ref = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + diff --git a/inference-engine/tests/functional/inference_engine/transformations/dilated_convolution_converter.cpp b/inference-engine/tests/functional/inference_engine/transformations/dilated_convolution_converter.cpp new file mode 100644 index 00000000000..d52123b2639 --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/transformations/dilated_convolution_converter.cpp @@ -0,0 +1,101 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common_test_utils/ngraph_test_utils.hpp" + + +using namespace testing; +using namespace ngraph; + +TEST(TransformationTests, DilatedConvolutionConverter) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{1, 4, 10, 10}); + auto filters = std::make_shared(element::f32, Shape{1, 4, 3, 3}); + auto space_to_batch = std::make_shared(data, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 1, 1})); + auto conv = std::make_shared(space_to_batch, filters, + Strides{1, 1}, CoordinateDiff{0, 0}, CoordinateDiff{0, 0}, Strides{1, 1}); + auto batch_to_space = std::make_shared(conv, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 0, 0}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 3})); + f = std::make_shared(NodeVector{batch_to_space}, ParameterVector{data, filters}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + { + auto data = std::make_shared(element::f32, Shape{1, 4, 10, 10}); + auto filters = std::make_shared(element::f32, Shape{1, 4, 3, 3}); + auto conv = std::make_shared(data, filters, + Strides{1, 1}, CoordinateDiff{1, 1}, CoordinateDiff{-1, -2}, Strides{2, 2}, op::PadType::EXPLICIT); + f_ref = std::make_shared(NodeVector{conv}, ParameterVector{data, filters}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeDilatedConvolutionConverterNonZeroPadsForNC) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{1, 4, 10, 10}); + auto filters = std::make_shared(element::f32, Shape{1, 5, 3, 3}); + auto space_to_batch = std::make_shared(data, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 1, 1})); + auto conv = std::make_shared(space_to_batch, filters, + Strides{1, 1}, CoordinateDiff{0, 0}, CoordinateDiff{0, 0}, Strides{1, 1}); + auto batch_to_space = std::make_shared(conv, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 0, 0}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 3})); + f = std::make_shared(NodeVector{batch_to_space}, ParameterVector{data, filters}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + { + auto data = std::make_shared(element::f32, Shape{1, 4, 10, 10}); + auto filters = std::make_shared(element::f32, Shape{1, 5, 3, 3}); + auto space_to_batch = std::make_shared(data, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 1, 1})); + auto conv = std::make_shared(space_to_batch, filters, + Strides{1, 1}, CoordinateDiff{0, 0}, CoordinateDiff{0, 0}, Strides{1, 1}); + auto batch_to_space = std::make_shared(conv, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 0, 0}), + op::Constant::create(element::i64, Shape{4}, {0, 0, 2, 3})); + f_ref = std::make_shared(NodeVector{batch_to_space}, ParameterVector{data, filters}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} diff --git a/inference-engine/tests/functional/inference_engine/transformations/space_to_batch_fusion.cpp b/inference-engine/tests/functional/inference_engine/transformations/space_to_batch_fusion.cpp new file mode 100644 index 00000000000..d4c02f299a3 --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/transformations/space_to_batch_fusion.cpp @@ -0,0 +1,236 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common_test_utils/ngraph_test_utils.hpp" + + +using namespace testing; +using namespace ngraph; + +TEST(TransformationTests, SpaceToBatchFusionTranspose) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 2, 3, 3}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto space_to_batch = std::make_shared(data, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 2, 3, 3})); + + f_ref = std::make_shared(NodeVector{space_to_batch}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, SpaceToBatchFusionReshape) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto reshape_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {3, 12, 4, 8}), false); + auto pad = std::make_shared(reshape_before, + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 2, 3, 3}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto space_to_batch = std::make_shared(data, + op::Constant::create(element::i64, Shape{4}, {1, 1, 2, 2}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {2, 2, 3, 3})); + + f_ref = std::make_shared(NodeVector{space_to_batch}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeSpaceToBatchFusionInvalidTransposePerm) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {3, 0, 2, 1})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 3, 2}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {3, 0, 2, 1})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{4}, {1, 1, 1, 1}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 3, 2}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f_ref = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeSpaceToBatchFusionInvalidPad) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{4}, {0, 1, 1, 0}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 3, 2}), + op::Constant::create(element::f32, Shape{}, {1}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{4}, {0, 1, 1, 0}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 3, 2}), + op::Constant::create(element::f32, Shape{}, {1}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f_ref = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeSpaceToBatchFusionInvalidMode) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{4}, {0, 1, 1, 0}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 3, 2}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::DEPTH_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{4}, {0, 1, 1, 0}), + op::Constant::create(element::i64, Shape{4}, {1, 1, 3, 2}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::DEPTH_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{4}, {1, 0, 2, 3})); + f_ref = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + +TEST(TransformationTests, NegativeSpaceToBatchFusionInvalidRank) { + std::shared_ptr f(nullptr), f_ref(nullptr); + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{5}, {0, 1, 1, 0, 0}), + op::Constant::create(element::i64, Shape{5}, {1, 1, 3, 2, 2}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + f = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + + pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(f); + ASSERT_NO_THROW(check_rt_info(f)); + } + + { + auto data = std::make_shared(element::f32, Shape{12, 3, 4, 8, 8}); + auto trans_before = std::make_shared(data, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + auto pad = std::make_shared(trans_before, + op::Constant::create(element::i64, Shape{5}, {0, 1, 1, 0, 0}), + op::Constant::create(element::i64, Shape{5}, {1, 1, 3, 2, 2}), + op::Constant::create(element::f32, Shape{}, {0}), op::PadMode::CONSTANT); + auto space_to_depth = std::make_shared(pad, opset6::SpaceToDepth::SpaceToDepthMode::BLOCKS_FIRST, 2); + auto trans_after = std::make_shared(space_to_depth, op::Constant::create(element::i64, Shape{5}, {1, 0, 2, 3, 4})); + f_ref = std::make_shared(NodeVector{trans_after}, ParameterVector{data}); + } + + auto res = compare_functions(f, f_ref, true); + ASSERT_TRUE(res.first) << res.second; +} + diff --git a/ngraph/core/include/ngraph/pattern/op/pattern.hpp b/ngraph/core/include/ngraph/pattern/op/pattern.hpp index 1c9e565703b..fe4dbd2c828 100644 --- a/ngraph/core/include/ngraph/pattern/op/pattern.hpp +++ b/ngraph/core/include/ngraph/pattern/op/pattern.hpp @@ -64,6 +64,9 @@ namespace ngraph NGRAPH_API std::function)> has_static_rank(); + NGRAPH_API + std::function)> rank_equals(const Dimension& expected_rank); + NGRAPH_API std::function)> type_matches(const element::Type& type); diff --git a/ngraph/core/src/op/convert.cpp b/ngraph/core/src/op/convert.cpp index 3eef60c217e..f0ef1f14096 100644 --- a/ngraph/core/src/op/convert.cpp +++ b/ngraph/core/src/op/convert.cpp @@ -96,6 +96,7 @@ namespace convert TYPE_OUT_CASE(f16, arg, out); TYPE_OUT_CASE(f32, arg, out); TYPE_OUT_CASE(f64, arg, out); + TYPE_OUT_CASE(boolean, arg, out); default: rc = false; break; } return rc; @@ -115,6 +116,7 @@ namespace convert NGRAPH_TYPE_CASE(evaluate_convert, u64, arg, out); NGRAPH_TYPE_CASE(evaluate_convert, f16, arg, out); NGRAPH_TYPE_CASE(evaluate_convert, f32, arg, out); + NGRAPH_TYPE_CASE(evaluate_convert, boolean, arg, out); default: rc = false; break; } return rc; diff --git a/ngraph/core/src/pattern/op/pattern.cpp b/ngraph/core/src/pattern/op/pattern.cpp index c61173dd6e4..0c48780926d 100644 --- a/ngraph/core/src/pattern/op/pattern.cpp +++ b/ngraph/core/src/pattern/op/pattern.cpp @@ -102,6 +102,13 @@ namespace ngraph }; } + std::function)> rank_equals(const Dimension& expected_rank) + { + return [=](Output output) -> bool { + return output.get_partial_shape().rank() == expected_rank; + }; + } + std::function)> type_matches(const element::Type& type) { return [=](Output output) -> bool { return output.get_element_type() == type; }; diff --git a/ngraph/test/constant_folding.cpp b/ngraph/test/constant_folding.cpp index 9c8f969e008..3fdcdc6dde7 100644 --- a/ngraph/test/constant_folding.cpp +++ b/ngraph/test/constant_folding.cpp @@ -429,13 +429,11 @@ TEST(constant_folding, constant_unary_binary) ASSERT_NO_THROW(pass_manager.run_passes(func_error)); } -TEST(constant_folding, const_convert) +template +static void test_const_convert(const vector& values_in, const vector& values_expected) { - Shape input_shape{3, 4}; - - vector values_in{1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7}; - auto constant = op::Constant::create(element::f32, input_shape, values_in); - auto convert = make_shared(constant, element::u64); + auto constant = op::Constant::create(element::from(), Shape{values_in.size()}, values_in); + auto convert = make_shared(constant, element::from()); convert->set_friendly_name("test"); auto f = make_shared(convert, ParameterVector{}); @@ -450,13 +448,31 @@ TEST(constant_folding, const_convert) as_type_ptr(f->get_results().at(0)->input_value(0).get_node_shared_ptr()); ASSERT_TRUE(new_const); ASSERT_EQ(new_const->get_friendly_name(), "test"); - ASSERT_EQ(new_const->get_output_element_type(0), element::u64); - auto values_out = new_const->get_vector(); + ASSERT_EQ(new_const->get_output_element_type(0), element::from()); + auto values_out = new_const->template get_vector(); - vector values_expected{1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7}; ASSERT_EQ(values_expected, values_out); } +TEST(constant_folding, const_convert) +{ + { + vector in{1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7}; + vector expected{1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7}; + test_const_convert(in, expected); + } + { + vector in{false, true, true, false, false, false, true}; + vector expected{0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}; + test_const_convert(in, expected); + } + { + vector in{1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f}; + vector expected{true, false, true, false, true, false, true}; + test_const_convert(in, expected); + } +} + TEST(constant_folding, shape_of_v0) { Shape input_shape{3, 4, 0, 22, 608, 909, 3};