diff --git a/src/plugins/intel_gna/src/backend/gna_limitations.cpp b/src/plugins/intel_gna/src/backend/gna_limitations.cpp index f1b5dce58b9806..27d85be5eb5922 100644 --- a/src/plugins/intel_gna/src/backend/gna_limitations.cpp +++ b/src/plugins/intel_gna/src/backend/gna_limitations.cpp @@ -753,6 +753,9 @@ bool Limitations::is_transpose_supported(const ov::Shape& shape) { max_input_dim <= kTransposeMaxSize) { return true; } + } else if (squeezed_shape.size() == 1) { + // it means that transpose has only one valuable dimension (!=1) + return true; } return false; } diff --git a/src/plugins/intel_gna/src/gna_transformations_pipeline.cpp b/src/plugins/intel_gna/src/gna_transformations_pipeline.cpp index 255d9021d69d58..68462239018f5e 100644 --- a/src/plugins/intel_gna/src/gna_transformations_pipeline.cpp +++ b/src/plugins/intel_gna/src/gna_transformations_pipeline.cpp @@ -62,6 +62,7 @@ #include "transformations/split_eltwise.hpp" #include "transformations/substitute_softsign.hpp" #include "transformations/swap_input_matmul_gna.hpp" +#include "transformations/transpose_compress.hpp" #include "transformations/transpose_sinking/ts_concat.hpp" #include "transformations/transpose_sinking/ts_fuse.hpp" #include "transformations/transpose_sinking/ts_general.hpp" @@ -139,6 +140,7 @@ void TransformationsPipeline::apply(const std::shared_ptr& model, manager.register_pass(); manager.register_pass(); manager.register_pass(); + manager.register_pass(); manager.register_pass(); manager.register_pass(); manager.register_pass(); diff --git a/src/plugins/intel_gna/src/transformations/handle_transposes_around_matmul.cpp b/src/plugins/intel_gna/src/transformations/handle_transposes_around_matmul.cpp index 498f973ba0320d..c00bec4ea84586 100644 --- a/src/plugins/intel_gna/src/transformations/handle_transposes_around_matmul.cpp +++ b/src/plugins/intel_gna/src/transformations/handle_transposes_around_matmul.cpp @@ -190,7 +190,8 @@ HandleTransposeBeforeMatMul::HandleTransposeBeforeMatMul() { } if (prev_node) { - if (Limitations::is_transpose_supported(prev_node->get_output_shape(0))) { + if (graph_utils::is_shape_2d(prev_node->get_output_shape(0)) && + Limitations::is_transpose_supported(prev_node->get_output_shape(0))) { InsertTranspose(prev_node, matmul_node->get_friendly_name(), true); } } diff --git a/src/plugins/intel_gna/src/transformations/transpose_compress.cpp b/src/plugins/intel_gna/src/transformations/transpose_compress.cpp new file mode 100644 index 00000000000000..bd13b1d788be07 --- /dev/null +++ b/src/plugins/intel_gna/src/transformations/transpose_compress.cpp @@ -0,0 +1,94 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "transformations/transpose_compress.hpp" + +#include "common/graph_utils.hpp" +#include "openvino/cc/ngraph/itt.hpp" +#include "openvino/opsets/opset11.hpp" +#include "openvino/pass/pattern/op/wrap_type.hpp" + +using namespace ov::opset11; +using namespace ov::pass; +using namespace ov::intel_gna::pass; + +namespace { + +inline std::vector fix_indexes(const std::vector& ids) { + std::vector ids_fixed(ids.size()); + std::iota(ids_fixed.begin(), ids_fixed.end(), 0); + stable_sort(ids_fixed.begin(), ids_fixed.end(), [&ids](size_t a, size_t b) { + return ids[a] < ids[b]; + }); + return ids_fixed; +} + +} // namespace + +TransposeCompress::TransposeCompress() { + MATCHER_SCOPE(TransposeCompress); + + auto transpose_const = pattern::wrap_type(); + auto transpose = + pattern::wrap_type({pattern::any_input(), transpose_const}, [](const ov::Output& node) { + return !limitations::Limitations::is_transpose_supported(node.get_node_shared_ptr()); + }); + + ov::matcher_pass_callback callback = [=](pattern::Matcher& m) { + const auto& pattern_map = m.get_pattern_value_map(); + const auto transpose_order = as_type_ptr(pattern_map.at(transpose_const).get_node_shared_ptr()); + const auto transpose_node = pattern_map.at(transpose).get_node_shared_ptr(); + const ov::Shape& shape = transpose_node->get_input_shape(0); + const ov::Shape& shape_out = transpose_node->get_output_shape(0); + ov::AxisVector axis = transpose_order->get_axis_vector_val(); + ov::AxisVector axis_compressed = {}; + ov::Shape shape_fused_out = {}; + for (const size_t& axis : axis) { + if (axis_compressed.empty() || (axis - axis_compressed.back()) != 1) { + axis_compressed.push_back(axis); + shape_fused_out.push_back(shape[axis]); + } else { + shape_fused_out.back() *= shape[axis]; + } + } + // check that fusing is required + if (axis.size() == axis_compressed.size()) { + return false; + } + + // correct fused indexes, e.g. (2, 0, 3) -> (1, 0, 2) + ov::AxisVector axis_fused_fixed = fix_indexes(axis_compressed); + size_t fused_sz = axis_fused_fixed.size(); + // Restore input shape + ov::Shape shape_fused_in(fused_sz); + for (size_t i = 0; i < fused_sz; ++i) { + shape_fused_in[i] = shape_fused_out[axis_fused_fixed[i]]; + } + + if (!limitations::Limitations::is_transpose_supported(shape_fused_in)) { + return false; + } + + // Reshape in + auto reshape_in_const = + std::make_shared(ov::element::i32, ov::Shape{shape_fused_in.size()}, shape_fused_in); + auto reshape_in = std::make_shared(transpose_node->input_value(0), reshape_in_const, false); + // Transpose + auto transpose_const = + std::make_shared(ov::element::i8, ov::Shape{axis_fused_fixed.size()}, axis_fused_fixed); + auto transpose = std::make_shared(reshape_in, transpose_const); + // Reshape out + auto reshape_out_const = std::make_shared(ov::element::i32, ov::Shape{shape_out.size()}, shape_out); + auto reshape_out = std::make_shared(transpose, reshape_out_const, false); + // + ov::replace_output_update_name(transpose_node->output(0), reshape_out->output(0)); + ov::copy_runtime_info({transpose_node, transpose_order}, + {transpose, transpose_const, reshape_in, reshape_in_const}); + + return true; + }; + + auto m = std::make_shared(transpose, matcher_name); + this->register_matcher(m, callback); +} diff --git a/src/plugins/intel_gna/src/transformations/transpose_compress.hpp b/src/plugins/intel_gna/src/transformations/transpose_compress.hpp new file mode 100644 index 00000000000000..c1f142289e5d21 --- /dev/null +++ b/src/plugins/intel_gna/src/transformations/transpose_compress.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/pass/graph_rewrite.hpp" + +namespace ov { +namespace intel_gna { +namespace pass { + +/** + * @brief Reduce the rank of Transpose shape by fusing dimensions + * [A, B, C, D] [A, B, C, D] + * | | + * Transpose Reshape + * | | + * [A, D, B, C] [A, B*C, D] + * | | + * | Transpose + * | -> | + * | <- [A, D, B*C] + * | | + * | Reshape + * | | + * | [A, D, B, C] + * | | + * Any Layer Any Layer + */ +class TransposeCompress : public ov::pass::MatcherPass { +public: + OPENVINO_RTTI("TransposeCompress", "0"); + TransposeCompress(); +}; + +} // namespace pass +} // namespace intel_gna +} // namespace ov diff --git a/src/plugins/intel_gna/tests/unit/transformations/transpose_compress.cpp b/src/plugins/intel_gna/tests/unit/transformations/transpose_compress.cpp new file mode 100644 index 00000000000000..9b5617a917c7e7 --- /dev/null +++ b/src/plugins/intel_gna/tests/unit/transformations/transpose_compress.cpp @@ -0,0 +1,144 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "transformations/transpose_compress.hpp" + +#include + +#include "common_test_utils/ngraph_test_utils.hpp" +#include "openvino/core/model.hpp" +#include "openvino/opsets/opset12.hpp" + +using namespace ov::opset12; + +namespace transpose_compress_test { + +struct TestData { + ov::Shape shape_src; + ov::AxisVector tr_order_src; + ov::Shape shape_ref; + ov::AxisVector tr_order_ref; +}; + +typedef std::tuple // Transpose order + test_params; + +class TransposeCompressTest : public CommonTestUtils::TestsCommon, public ::testing::WithParamInterface { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + TestData test_shapes; + std::tie(test_shapes) = obj.param; + + std::ostringstream result; + result << "InputShape=" << test_shapes.shape_src << "_"; + result << "TransposeOrder=" << test_shapes.tr_order_src << "_"; + + return result.str(); + } + + virtual void set_test_model() { + auto param = std::make_shared(m_type, m_test_shapes.shape_src); + + auto transpose_const = std::make_shared(ov::element::i32, + ov::Shape{m_test_shapes.tr_order_src.size()}, + m_test_shapes.tr_order_src); + auto transpose = std::make_shared(param, transpose_const); + + m_shape_out = transpose->get_output_shape(0); + + auto result = std::make_shared(transpose); + m_model_test = + std::make_shared(ov::ResultVector{result}, ov::ParameterVector{param}, "transpose_compress"); + } + + virtual void set_ref_model() { + auto param = std::make_shared(m_type, m_test_shapes.shape_src); + + auto shape_in_const = std::make_shared(ov::element::i32, + ov::Shape{m_test_shapes.shape_ref.size()}, + m_test_shapes.shape_ref); + auto shape_in = std::make_shared(param, shape_in_const, false); + + auto transpose_const = std::make_shared(ov::element::i8, + ov::Shape{m_test_shapes.tr_order_ref.size()}, + m_test_shapes.tr_order_ref); + auto transpose = std::make_shared(shape_in, transpose_const); + + auto shape_out_const = std::make_shared(ov::element::i32, ov::Shape{m_shape_out.size()}, m_shape_out); + auto shape_out = std::make_shared(transpose, shape_out_const, false); + + auto result = std::make_shared(shape_out); + m_model_ref = + std::make_shared(ov::ResultVector{result}, ov::ParameterVector{param}, "transpose_compress"); + } + + void SetUp() override { + std::tie(m_test_shapes) = this->GetParam(); + set_test_model(); + set_ref_model(); + }; + + void Validate() { + ov::pass::Manager m; + m.register_pass(); + m.register_pass(); + m.run_passes(m_model_test); + + check_rt_info(m_model_test); + + const FunctionsComparator func_comparator = + FunctionsComparator::with_default().enable(FunctionsComparator::ATTRIBUTES); + const FunctionsComparator::Result result = func_comparator(m_model_test, m_model_ref); + + ASSERT_TRUE(result.valid) << result.message; + } + + void Run() { + SetUp(); + Validate(); + } + +public: + TestData m_test_shapes; + ov::Shape m_shape_out; + ov::element::Type m_type = ov::element::f32; + std::shared_ptr m_model_test; + std::shared_ptr m_model_ref; +}; + +class TransposeCompressNegTest : public TransposeCompressTest { + void set_ref_model() override { + m_model_ref = m_model_test->clone(); + } +}; + +TEST_P(TransposeCompressTest, CompareWithRefs) { + Run(); +} + +TEST_P(TransposeCompressNegTest, CompareWithRefs) { + Run(); +} + +const std::vector test_shapes = {{{1, 2, 3}, {1, 2, 0}, {1, 6}, {1, 0}}, + {{1, 2, 4}, {1, 2, 0}, {1, 8}, {1, 0}}, + {{2, 2, 4}, {1, 2, 0}, {2, 8}, {1, 0}}, + {{2, 2, 4, 4}, {2, 3, 0, 1}, {4, 16}, {1, 0}}}; + +const std::vector test_neg_shapes = {{{1, 2, 3, 4}, {1, 0, 2, 3}, {}, {}}, + {{1, 2, 3, 4}, {0, 2, 1, 3}, {}, {}}, + {{1, 2, 3, 4}, {2, 3, 0, 1}, {}, {}}, + {{10, 20}, {1, 0}, {}, {}}}; + +INSTANTIATE_TEST_SUITE_P(smoke_transpose_compress_test, + TransposeCompressTest, + ::testing::Combine(::testing::ValuesIn(test_shapes)), + TransposeCompressTest::getTestCaseName); + +INSTANTIATE_TEST_SUITE_P(smoke_transpose_compress_test, + TransposeCompressNegTest, + ::testing::Combine(::testing::ValuesIn(test_neg_shapes)), + TransposeCompressNegTest::getTestCaseName); + +} // namespace transpose_compress_test