diff --git a/src/core/include/openvino/core/validation_util.hpp b/src/core/include/openvino/core/validation_util.hpp index 93ad798f27e..f869e4a2c96 100644 --- a/src/core/include/openvino/core/validation_util.hpp +++ b/src/core/include/openvino/core/validation_util.hpp @@ -133,4 +133,24 @@ OPENVINO_API std::shared_ptr get_constant_from_source(const Ou /// \param output_labels Vector of TensorLabel objects representing resulting value labels /// \return boolean status if label evaluation was successful. OPENVINO_API bool default_label_evaluator(const Node* node, TensorLabelVector& output_labels); + +/// \brief Generates transpose default axes order at end of input vector. +/// +/// Default axes order is decreasing sequence numbers which start from `length - 1`. +/// +/// \param axes_order Vector where default order will be generated. +/// \param length Sequence length of axes order. +/// +OPENVINO_API void generate_transpose_default_order(std::vector& axes_order, const size_t length); + +/// \brief Check if vector of axes order has got valid values. +/// +/// Axes order has to be unique numbers in range of [0, size). +/// +/// \param axes_order Vector with axes order to check. +/// \param size Input for transpose rank size. +/// +/// \return true if axes order is valid otherwise false. +/// +OPENVINO_API bool is_valid_axes_order(const std::vector& axes_order, const size_t size); } // namespace ov diff --git a/src/core/include/openvino/op/op.hpp b/src/core/include/openvino/op/op.hpp index 8b2292dbff8..1d30a4129d8 100644 --- a/src/core/include/openvino/op/op.hpp +++ b/src/core/include/openvino/op/op.hpp @@ -25,7 +25,9 @@ the same name and not all of them are overrided in Derived class, the only overrided methods \ will be available from Derived class. We need to explicitly cast Derived to Base class to \ have an access to remaining methods or use this using. */ \ - using ov::op::Op::evaluate; + using ov::op::Op::evaluate; \ + using ov::op::Op::evaluate_lower; \ + using ov::op::Op::evaluate_upper; namespace ov { namespace op { diff --git a/src/core/include/openvino/op/transpose.hpp b/src/core/include/openvino/op/transpose.hpp index 6ecf5cc986e..959b71c2d0c 100644 --- a/src/core/include/openvino/op/transpose.hpp +++ b/src/core/include/openvino/op/transpose.hpp @@ -35,8 +35,17 @@ public: OPENVINO_SUPPRESS_DEPRECATED_START bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override; + bool evaluate_upper(const HostTensorVector& output_values) const override; + bool evaluate_lower(const HostTensorVector& output_values) const override; OPENVINO_SUPPRESS_DEPRECATED_END + bool has_evaluate() const override; + bool evaluate_label(TensorLabelVector& output_labels) const override; + + /// \brief Inputs indexes and count. + enum Ins : size_t { ARG, ORDER, IN_COUNT }; + /// \brief Outputs indexes and count. + enum Outs : size_t { ARG_T, OUT_COUNT }; }; } // namespace v1 } // namespace op diff --git a/src/core/shape_inference/include/compare.hpp b/src/core/shape_inference/include/compare.hpp new file mode 100644 index 00000000000..b72ec06c0e2 --- /dev/null +++ b/src/core/shape_inference/include/compare.hpp @@ -0,0 +1,52 @@ +// Copyright (C) 2018-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +namespace ov { +namespace cmp { +/** \brief Enumerate bounds to compare */ +enum Bound : uint8_t { NONE, LOWER, UPPER, BOTH }; + +/** + * \brief Compare if value is between lower and upper bounds. + * + * The Between comparator has four modes to check value: + * - Bound::None (lower, upper) + * - Bound::LOWER [lower, upper) + * - Bound::UPPER (lower, upper] + * - Bound::BOTH [lower, upper] + * + * \tparam T Value type to compare. + * \tparam BMode Compare bounds mode. + */ +template +class Between { + T _lower_bound, _upper_bound; + +public: + constexpr Between(const T& lower, const T& upper) : _lower_bound{lower}, _upper_bound{upper} {} + + template ::type* = nullptr> + constexpr bool operator()(const T& value) const { + return (_lower_bound < value) && (value < _upper_bound); + } + + template ::type* = nullptr> + constexpr bool operator()(const T& value) const { + return (_lower_bound <= value) && (value < _upper_bound); + } + + template ::type* = nullptr> + constexpr bool operator()(const T& value) const { + return (_lower_bound < value) && (value <= _upper_bound); + } + + template ::type* = nullptr> + constexpr bool operator()(const T& value) const { + return (_lower_bound <= value) && (value <= _upper_bound); + } +}; +} // namespace cmp +} // namespace ov diff --git a/src/core/shape_inference/include/sequnce_generator.hpp b/src/core/shape_inference/include/sequnce_generator.hpp new file mode 100644 index 00000000000..c7a5ae7de3d --- /dev/null +++ b/src/core/shape_inference/include/sequnce_generator.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2018-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +namespace ov { +/** \brief Enumerate directions */ +enum Direction : uint8_t { FORWARD, BACKWARD }; + +/** + * \brief Infinite generator of sequence increasing values. + * + * Start value can be specified. + * + * \tparam T Type of sequence values (must support `++` or '--' operators). + */ +template +class SeqGen { + T _counter; + +public: + constexpr SeqGen(const T& start) : _counter{start} {} + + template ::type* = nullptr> + T operator()() { + return _counter++; + } + + template ::type* = nullptr> + T operator()() { + return _counter--; + } +}; +} // namespace ov diff --git a/src/core/shape_inference/include/transpose_shape_inference.hpp b/src/core/shape_inference/include/transpose_shape_inference.hpp new file mode 100644 index 00000000000..5dba6496a66 --- /dev/null +++ b/src/core/shape_inference/include/transpose_shape_inference.hpp @@ -0,0 +1,78 @@ +// Copyright (C) 2018-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#pragma once + +#include "openvino/op/transpose.hpp" +#include "utils.hpp" + +namespace ov { +namespace op { +namespace v1 { + +/** + * \brief Calculate transpose output shape. + * + * \tparam T Type of shape + * + * \param op Transpose operator pointer. + * \param input_shape Transpose input shape. + * \param axes_order Transpose axes order (modified if empty). + * + * \return Output shape + */ +template +T calc_output_shape(const Transpose* const op, const T& input_shape, std::vector& axes_order) { + const auto output_rank = input_shape.size(); + + if (axes_order.empty()) { + generate_transpose_default_order(axes_order, output_rank); + } else { + NODE_VALIDATION_CHECK(op, + is_valid_axes_order(axes_order, output_rank), + "Permutation ", + AxisVector(axes_order.begin(), axes_order.end()), + " is not valid for input shape ", + input_shape); + } + + T output_shape; + for (auto&& axis : axes_order) { + output_shape.push_back(input_shape[axis]); + } + + return output_shape; +} + +/** + * \brief Do transpose inference on input and output shapes. + * + * \tparam T Type of inference shapes. + * + * \param op Transpose operator pointer. + * \param input_shapes Input shapes of transpose. + * \param output_shapes Output shapes of transpose which be modified by inference. + * \param constant_data Map of constant data. + */ +template +void shape_infer(const Transpose* op, + const std::vector& input_shapes, + std::vector& output_shapes, + const std::map>& constant_data = {}) { + const auto& input_shape = input_shapes[Transpose::ARG]; + auto& output_shape = output_shapes[Transpose::ARG_T]; + + std::vector axes; + const auto has_order = get_data_as_int64(Transpose::ORDER, op, axes, constant_data); + + if (has_order && input_shape.rank().is_static()) { + output_shape = calc_output_shape(op, input_shape, axes); + } else if (has_order) { + output_shape = ov::PartialShape::dynamic(axes.size()); + } else { + output_shape = ov::PartialShape::dynamic(input_shape.rank()); + } +} +} // namespace v1 +} // namespace op +} // namespace ov diff --git a/src/core/src/op/transpose.cpp b/src/core/src/op/transpose.cpp index e2a3443b3ae..385dfb05a40 100644 --- a/src/core/src/op/transpose.cpp +++ b/src/core/src/op/transpose.cpp @@ -4,10 +4,10 @@ #include "ngraph/op/transpose.hpp" -#include - #include "itt.hpp" #include "ngraph/runtime/reference/transpose.hpp" +#include "ngraph/validation_util.hpp" +#include "transpose_shape_inference.hpp" using namespace std; using namespace ngraph; @@ -18,102 +18,81 @@ op::v1::Transpose::Transpose(const Output& arg, const Output& input_ constructor_validate_and_infer_types(); } -bool ngraph::op::v1::Transpose::visit_attributes(AttributeVisitor& visitor) { +bool op::v1::Transpose::visit_attributes(AttributeVisitor& visitor) { OV_OP_SCOPE(v1_Transpose_visit_attributes); return true; } void op::v1::Transpose::validate_and_infer_types() { OV_OP_SCOPE(v1_Transpose_validate_and_infer_types); - const auto& input_order_et = get_input_element_type(1); + const auto& input_order_et = get_input_element_type(ORDER); NODE_VALIDATION_CHECK(this, input_order_et.is_dynamic() || input_order_et.is_integral_number(), "Input order must have an integral number element type."); - const auto& input_order_shape = get_input_partial_shape(1); + const auto& input_order_shape = get_input_partial_shape(ORDER); NODE_VALIDATION_CHECK(this, input_order_shape.rank().compatible(1), "Input order must be a vector."); - const auto& arg_shape = get_input_partial_shape(0); + const auto& arg_shape = get_input_partial_shape(ARG); NODE_VALIDATION_CHECK( this, input_order_shape.compatible(ov::PartialShape{arg_shape.rank()}) || (input_order_shape.is_static() && input_order_shape.rank() == 1 && input_order_shape[0] == 0), "Input order must have shape [n], where n is the rank of arg."); - set_input_is_relevant_to_shape(1); + set_input_is_relevant_to_shape(ORDER); - NGRAPH_SUPPRESS_DEPRECATED_START - if (const auto& input_const = get_constant_from_source(input_value(1))) { - auto permutation = input_const->get_axis_vector_val(); - if (permutation.empty()) { - for (int64_t i = 1; i <= arg_shape.rank().get_length(); ++i) - permutation.emplace_back(arg_shape.rank().get_length() - i); - } - NODE_VALIDATION_CHECK(this, - is_valid_permutation(permutation, arg_shape.rank()), - "Permutation ", - permutation, - " is not valid for input shape ", - arg_shape); - set_output_type(0, get_input_element_type(0), ngraph::apply_permutation(arg_shape, permutation)); - } else { - Rank output_rank = arg_shape.rank(); - if (output_rank.is_dynamic() && input_order_shape.is_static() && input_order_shape[0].get_length()) - output_rank = input_order_shape[0]; - set_output_type(0, get_input_element_type(0), ov::PartialShape::dynamic(output_rank)); - } - NGRAPH_SUPPRESS_DEPRECATED_END + std::vector input_shapes{arg_shape, input_order_shape}; + std::vector output_shapes(OUT_COUNT, ov::PartialShape{}); + + shape_infer(this, input_shapes, output_shapes); + + set_output_size(output_shapes.size()); + set_output_type(ARG, get_input_element_type(ARG), output_shapes[ARG_T]); } shared_ptr op::v1::Transpose::clone_with_new_inputs(const OutputVector& new_args) const { OV_OP_SCOPE(v1_Transpose_clone_with_new_inputs); check_new_args_count(this, new_args); - return make_shared(new_args[0], new_args[1]); + return make_shared(new_args[ARG], new_args[ORDER]); } -namespace transpose { -namespace { -bool evaluate_transpose(const HostTensorPtr& arg1, const HostTensorPtr& arg2, const HostTensorPtr& out) { - NGRAPH_CHECK(arg2->get_element_type().is_integral_number(), - "Transpose axis element type has to be integral data type."); - - std::vector axes_order = host_tensor_2_vector(arg2); - ov::Shape in_shape = arg1->get_shape(); - if (shape_size(arg2->get_shape()) == 0) { - axes_order.resize(in_shape.size()); - std::iota(axes_order.begin(), axes_order.end(), 0); - std::reverse(axes_order.begin(), axes_order.end()); - } else { - std::unordered_set axes_set(axes_order.begin(), axes_order.end()); - bool is_unique_order = axes_set.size() == axes_order.size(); - NGRAPH_CHECK(is_unique_order, "Transpose axes order values must be unique."); - } - - ov::Shape out_shape(in_shape.size()); - std::transform(axes_order.begin(), axes_order.end(), out_shape.begin(), [&](const int64_t& v) { - NGRAPH_CHECK(v >= 0, "Negative values for transpose axes order are not supported."); - NGRAPH_CHECK(v < int64_t(in_shape.size()), "Transpose axis ", v, " is out of shape range."); - return in_shape[v]; - }); - - out->set_shape(out_shape); - out->set_element_type(arg1->get_element_type()); - runtime::reference::transpose(arg1->get_data_ptr(), - out->get_data_ptr(), - arg1->get_shape(), - arg1->get_element_type().size(), - axes_order.data(), - out_shape); - return true; -} -} // namespace -} // namespace transpose bool op::v1::Transpose::evaluate(const HostTensorVector& output_values, const HostTensorVector& input_values) const { OV_OP_SCOPE(v1_Transpose_evaluate); - return transpose::evaluate_transpose(input_values[0], input_values[1], output_values[0]); + + const auto& order = input_values[ORDER]; + OPENVINO_ASSERT(order->get_element_type().is_integral_number(), + "Transpose axis element type has to be integral data type."); + + const auto& arg = input_values[ARG]; + std::vector axes_order = host_tensor_2_vector(order); + auto out_shape = calc_output_shape(this, arg->get_shape(), axes_order); + + auto& out = output_values[ARG_T]; + out->set_shape(out_shape); + out->set_element_type(arg->get_element_type()); + ngraph::runtime::reference::transpose(arg->get_data_ptr(), + out->get_data_ptr(), + arg->get_shape(), + arg->get_element_type().size(), + axes_order.data(), + out_shape); + return true; } bool op::v1::Transpose::has_evaluate() const { OV_OP_SCOPE(v1_Transpose_has_evaluate); return get_input_element_type(1).is_integral_number(); } + +bool op::v1::Transpose::evaluate_lower(const HostTensorVector& output_values) const { + return get_input_tensor(ORDER).has_and_set_bound() && default_lower_bound_evaluator(this, output_values); +} + +bool op::v1::Transpose::evaluate_upper(const HostTensorVector& output_values) const { + return get_input_tensor(ORDER).has_and_set_bound() && default_upper_bound_evaluator(this, output_values); +} + +bool op::v1::Transpose::evaluate_label(TensorLabelVector& output_labels) const { + return get_input_tensor(ORDER).has_and_set_bound() && default_label_evaluator(this, output_labels); +} diff --git a/src/core/src/validation_util.cpp b/src/core/src/validation_util.cpp index 93627bf821e..4812f607fa5 100644 --- a/src/core/src/validation_util.cpp +++ b/src/core/src/validation_util.cpp @@ -10,6 +10,7 @@ #include #include +#include "compare.hpp" #include "ngraph/evaluator.hpp" #include "ngraph/op/concat.hpp" #include "ngraph/op/convert.hpp" @@ -23,6 +24,7 @@ #include "ngraph/shape.hpp" #include "ngraph/type/element_type_traits.hpp" #include "ngraph/util.hpp" +#include "sequnce_generator.hpp" NGRAPH_SUPPRESS_DEPRECATED_START using namespace std; @@ -1642,3 +1644,13 @@ bool ngraph::validate_host_tensor_vector(const HostTensorVector& tensor_vector, return t != nullptr; }); } + +void ov::generate_transpose_default_order(std::vector& axes_order, const size_t length) { + axes_order.reserve(length); + std::generate_n(std::back_inserter(axes_order), length, ov::SeqGen(length - 1)); +} + +bool ov::is_valid_axes_order(const std::vector& axes_order, const size_t size) { + return (std::unordered_set(axes_order.cbegin(), axes_order.cend()).size() == size) && + std::all_of(axes_order.cbegin(), axes_order.cend(), ov::cmp::Between(0, size)); +} diff --git a/src/core/tests/op_eval/transpose.cpp b/src/core/tests/op_eval/transpose.cpp index 8aad4553bc3..f0fad01b276 100644 --- a/src/core/tests/op_eval/transpose.cpp +++ b/src/core/tests/op_eval/transpose.cpp @@ -8,11 +8,13 @@ #include #include "engines_util/execute_tools.hpp" -#include "gtest/gtest.h" +#include "gmock/gmock.h" #include "ngraph/runtime/host_tensor.hpp" #include "ngraph/runtime/reference/transpose.hpp" #include "ngraph/util.hpp" #include "ngraph/validation_util.hpp" +#include "openvino/opsets/opset9.hpp" +#include "sequnce_generator.hpp" #include "util/all_close_f.hpp" #include "util/test_tools.hpp" #include "util/type_prop.hpp" @@ -154,7 +156,7 @@ TEST(op_eval, eval_duplicated_axes_transpose) { FAIL() << "Duplicated axes values not detected"; } catch (const ngraph_error& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("must be unique")); + EXPECT_HAS_SUBSTRING(error.what(), std::string("Permutation AxisVector{2, 1, 2} is not valid for input shape")); } catch (...) { FAIL() << "Failed for unexpected reason"; } @@ -179,7 +181,7 @@ TEST(op_eval, eval_out_of_shape_axes_transpose) { FAIL() << "Out of shape axes not detected"; } catch (const ngraph_error& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("out of shape")); + EXPECT_HAS_SUBSTRING(error.what(), std::string("Permutation AxisVector{0, 1, 3} is not valid for input shape")); } catch (...) { FAIL() << "Failed for unexpected reason"; } @@ -208,8 +210,173 @@ TEST(op_eval, eval_negative_axes_transpose) { ASSERT_EQ(actual_results, expected_result); FAIL() << "Negative axes for Transpose were not supported before."; } catch (const ngraph_error& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("not supported")); + std::stringstream exp_msg; + exp_msg << "Permutation " << AxisVector(perm.begin(), perm.end()) << " is not valid for input shape"; + EXPECT_HAS_SUBSTRING(error.what(), exp_msg.str()); } catch (...) { FAIL() << "Failed for unexpected reason"; } } + +template +std::vector tensor_to_vector(const ov::Tensor& tensor) { + std::vector rc(tensor.data(), tensor.data() + tensor.get_size()); + return rc; +} + +using namespace ov::opset9; +using namespace testing; + +using test_param = std::tuple, PartialShape>; + +class TransposeEvalTest : public TestWithParam { +protected: + void SetUp() override { + std::tie(axes_order, p_shape) = GetParam(); + + std::generate_n(std::back_inserter(lower_values), + ov::shape_size(p_shape.get_min_shape()), + ov::SeqGen(-10)); + std::generate_n(std::back_inserter(upper_values), + ov::shape_size(p_shape.get_min_shape()), + ov::SeqGen(20)); + + lower_v_tensor = std::make_shared(dtype, p_shape.get_min_shape(), lower_values.data()); + upper_v_tensor = std::make_shared(dtype, p_shape.get_min_shape(), upper_values.data()); + axes_v_tensor = std::make_shared(dtype, Shape{axes_order.size()}, axes_order.data()); + + arg = make_shared(dtype, p_shape); + order = make_shared(dtype, Shape{axes_order.size()}); + transpose = make_shared(arg, order); + + // prepare result tensors for evaluation + result = exp_result = ov::TensorVector{ov::Tensor(dtype, {0})}; + } + + void node_set_lower_and_upper(ov::Node* node, const HostTensorPtr& lower, const HostTensorPtr& upper) { + if (lower != nullptr) { + node->get_output_tensor(0).set_lower_value(lower); + } + + if (upper != nullptr) { + node->get_output_tensor(0).set_upper_value(upper); + } + } + + PartialShape p_shape; + ov::element::Type dtype{ov::element::from()}; + ov::element::Type label_dtype{ov::element::u64}; + + std::vector axes_order, lower_values, upper_values; + HostTensorPtr lower_v_tensor, upper_v_tensor, axes_v_tensor; + ov::TensorVector result, exp_result; + std::shared_ptr transpose; + std::shared_ptr arg, order; + + TensorLabel labels; + TensorLabelVector out_labels = TensorLabelVector(Transpose::OUT_COUNT); +}; + +INSTANTIATE_TEST_SUITE_P(op_eval, + TransposeEvalTest, + Values(make_tuple(std::vector{0}, PartialShape{4}), + make_tuple(std::vector{0, 1}, PartialShape{2, 5}), + make_tuple(std::vector{1, 0}, PartialShape{2, 5}), + make_tuple(std::vector{0, 1, 2}, PartialShape{2, 3, 1}), + make_tuple(std::vector{1, 2, 0}, PartialShape{2, 3, 1}), + make_tuple(std::vector{1, 3, 2, 0}, PartialShape{2, 3, 1, 5})), + PrintToStringParamName()); + +TEST_P(TransposeEvalTest, evaluate_lower) { + node_set_lower_and_upper(arg.get(), lower_v_tensor, upper_v_tensor); + node_set_lower_and_upper(order.get(), axes_v_tensor, axes_v_tensor); + + const auto inputs = ov::TensorVector{ov::Tensor(dtype, p_shape.get_min_shape(), lower_values.data()), + ov::Tensor(dtype, Shape{axes_order.size()}, axes_order.data())}; + // evaluate expected values + const auto exp_evaluate = transpose->evaluate(exp_result, inputs); + + ASSERT_EQ(transpose->evaluate_lower(result), exp_evaluate); + ASSERT_EQ(tensor_to_vector(result[Transpose::ARG_T]), + tensor_to_vector(exp_result[Transpose::ARG_T])); +} + +TEST_P(TransposeEvalTest, evaluate_lower_but_arg_lower_values_not_set) { + node_set_lower_and_upper(arg.get(), nullptr, upper_v_tensor); + node_set_lower_and_upper(order.get(), axes_v_tensor, axes_v_tensor); + + ASSERT_FALSE(transpose->evaluate_lower(result)); +} + +TEST_P(TransposeEvalTest, evaluate_lower_but_order_has_no_bounds_set) { + node_set_lower_and_upper(arg.get(), lower_v_tensor, upper_v_tensor); + + ASSERT_FALSE(transpose->evaluate_lower(result)); +} + +TEST_P(TransposeEvalTest, evaluate_upper) { + node_set_lower_and_upper(arg.get(), lower_v_tensor, upper_v_tensor); + node_set_lower_and_upper(order.get(), axes_v_tensor, axes_v_tensor); + + auto inputs = ov::TensorVector{ov::Tensor(dtype, p_shape.get_min_shape(), upper_values.data()), + ov::Tensor(dtype, Shape{axes_order.size()}, axes_order.data())}; + // evaluate expected values + transpose->evaluate(exp_result, inputs); + + ASSERT_TRUE(transpose->evaluate_upper(result)); + ASSERT_EQ(tensor_to_vector(result[Transpose::ARG_T]), + tensor_to_vector(exp_result[Transpose::ARG_T])); +} + +TEST_P(TransposeEvalTest, evaluate_upper_but_arg_upper_values_not_set) { + node_set_lower_and_upper(arg.get(), upper_v_tensor, nullptr); + node_set_lower_and_upper(order.get(), axes_v_tensor, axes_v_tensor); + + ASSERT_FALSE(transpose->evaluate_upper(result)); +} + +TEST_P(TransposeEvalTest, evaluate_upper_but_order_has_no_bounds_set) { + node_set_lower_and_upper(arg.get(), lower_v_tensor, upper_v_tensor); + + ASSERT_FALSE(transpose->evaluate_upper(result)); +} + +TEST_P(TransposeEvalTest, evaluate_label_but_empty_label_set) { + exp_result = ov::TensorVector{ov::Tensor(label_dtype, {0})}; + + labels.resize(ov::shape_size(p_shape.get_shape()), 0); + arg->get_default_output().get_tensor().set_value_label(labels); + + node_set_lower_and_upper(order.get(), axes_v_tensor, axes_v_tensor); + + ASSERT_FALSE(transpose->evaluate_label(out_labels)); +} + +TEST_P(TransposeEvalTest, evaluate_label_but_order_has_no_bound_set) { + exp_result = ov::TensorVector{ov::Tensor(label_dtype, {0})}; + + std::generate_n(std::back_inserter(labels), ov::shape_size(p_shape.get_shape()), ov::SeqGen(30)); + arg->get_default_output().get_tensor().set_value_label(labels); + + ASSERT_FALSE(transpose->evaluate_label(out_labels)); +} + +TEST_P(TransposeEvalTest, evaluate_label) { + exp_result = ov::TensorVector{ov::Tensor(label_dtype, {0})}; + + std::generate_n(std::back_inserter(labels), ov::shape_size(p_shape.get_shape()), ov::SeqGen(5)); + arg->get_default_output().get_tensor().set_value_label(labels); + + node_set_lower_and_upper(order.get(), axes_v_tensor, axes_v_tensor); + + auto labels_u64 = std::vector(labels.cbegin(), labels.cend()); + auto inputs = ov::TensorVector{ov::Tensor(label_dtype, p_shape.get_shape(), labels_u64.data()), + ov::Tensor(dtype, Shape{axes_order.size()}, axes_order.data())}; + + auto exp_eval_result = transpose->evaluate(exp_result, inputs); + + ASSERT_EQ(transpose->evaluate_label(out_labels), exp_eval_result); + ASSERT_THAT( + out_labels[Transpose::ARG_T], + ElementsAreArray(exp_result[Transpose::ARG_T].data(), exp_result[Transpose::ARG_T].get_size())); +} diff --git a/src/core/tests/type_prop/transpose.cpp b/src/core/tests/type_prop/transpose.cpp index 54c4a33b9e2..f8a269148da 100644 --- a/src/core/tests/type_prop/transpose.cpp +++ b/src/core/tests/type_prop/transpose.cpp @@ -2,12 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "gtest/gtest.h" +#include "dimension_tracker.hpp" #include "ngraph/ngraph.hpp" +#include "sequnce_generator.hpp" #include "util/type_prop.hpp" using namespace std; using namespace ngraph; +using namespace testing; +using namespace ov::op; TEST(type_prop, transpose_arg_static_input_order_static_ok) { auto arg = make_shared(element::f32, Shape{2, 4, 6, 8}); @@ -16,7 +19,7 @@ TEST(type_prop, transpose_arg_static_input_order_static_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(4))); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(4)); } TEST(type_prop, transpose_arg_static_input_order_constant_ok) { @@ -26,7 +29,7 @@ TEST(type_prop, transpose_arg_static_input_order_constant_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape{6, 4, 2, 8})); + EXPECT_EQ(r->get_output_partial_shape(0), (PartialShape{6, 4, 2, 8})); } TEST(type_prop, transpose_arg_static_input_order_constant_invalid_perm) { @@ -44,6 +47,21 @@ TEST(type_prop, transpose_arg_static_input_order_constant_invalid_perm) { } } +TEST(type_prop, transpose_with_not_unique_order) { + const auto order = std::vector{1, 0, 1}; + auto arg = make_shared(element::f32, Shape{1, 4, 300}); + auto input_order = make_shared(element::i64, Shape{order.size()}, order); + + try { + auto r = make_shared(arg, input_order); + FAIL() << "Did not detect invalid permutation"; + } catch (const NodeValidationFailure& error) { + EXPECT_HAS_SUBSTRING(error.what(), std::string("Permutation AxisVector{1, 0, 1} is not valid for input shape")); + } catch (...) { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} + TEST(type_prop, transpose_arg_rank_static_dynamic_input_order_static_ok) { auto arg = make_shared(element::f32, PartialShape{2, Dimension::dynamic(), Dimension::dynamic(), 8}); auto input_order = make_shared(element::i64, Shape{4}); @@ -51,7 +69,7 @@ TEST(type_prop, transpose_arg_rank_static_dynamic_input_order_static_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(4))); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(4)); } TEST(type_prop, transpose_arg_static_input_order_rank_static_dynamic_ok) { @@ -61,7 +79,7 @@ TEST(type_prop, transpose_arg_static_input_order_rank_static_dynamic_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(4))); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(4)); } TEST(type_prop, transpose_arg_rank_static_dynamic_input_order_rank_static_dynamic_ok) { @@ -71,7 +89,7 @@ TEST(type_prop, transpose_arg_rank_static_dynamic_input_order_rank_static_dynami auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(4))); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(4)); } TEST(type_prop, transpose_arg_rank_dynamic_input_order_rank_static_dynamic_ok) { @@ -81,7 +99,7 @@ TEST(type_prop, transpose_arg_rank_dynamic_input_order_rank_static_dynamic_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic())); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic()); } TEST(type_prop, transpose_arg_rank_dynamic_input_order_rank_dynamic_ok) { @@ -91,7 +109,7 @@ TEST(type_prop, transpose_arg_rank_dynamic_input_order_rank_dynamic_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic())); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic()); } TEST(type_prop, transpose_arg_rank_static_dynamic_input_order_rank_dynamic_ok) { @@ -101,7 +119,18 @@ TEST(type_prop, transpose_arg_rank_static_dynamic_input_order_rank_dynamic_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(4))); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(4)); +} + +TEST(type_prop, transpose_arg_rank_dynamic_input_order_const_ok) { + const auto axes_order = std::vector{1, 3, 0, 2}; + auto arg = make_shared(element::f32, PartialShape::dynamic()); + auto input_order = op::Constant::create(element::i64, Shape{axes_order.size()}, axes_order); + + auto r = make_shared(arg, input_order); + + EXPECT_EQ(r->get_output_element_type(0), element::f32); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(axes_order.size())); } TEST(type_prop, transpose_dynamic_interval_input_data) { @@ -111,7 +140,7 @@ TEST(type_prop, transpose_dynamic_interval_input_data) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(3))); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(3)); } TEST(type_prop, transpose_arg_static_input_order_static_input_order_not_vector) { @@ -205,7 +234,7 @@ TEST(type_prop, transpose_input_order_et_dynamic_ok) { auto r = make_shared(arg, input_order); EXPECT_EQ(r->get_output_element_type(0), element::f32); - EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape::dynamic(4))); + EXPECT_EQ(r->get_output_partial_shape(0), PartialShape::dynamic(4)); } TEST(type_prop, transpose_input_order_et_wrong) { @@ -230,4 +259,162 @@ TEST(type_prop, transpose_with_empty_order) { EXPECT_EQ(r->get_output_element_type(0), element::f32); EXPECT_TRUE(r->get_output_partial_shape(0).same_scheme(PartialShape({300, 1}))); + EXPECT_EQ(r->get_output_partial_shape(0), (PartialShape{300, 1})); +} + +/** \brief Transpose with order as parameter shape dimensions. */ +TEST(type_prop, transpose_order_as_parameter_shape) { + const auto arg = make_shared(element::f32, PartialShape{Dimension(2, 8), Dimension(4, 16), 6}); + + const auto param = make_shared(element::i64, PartialShape{2, 0, 1}); + const auto shape_of = make_shared(param); + // order after gather [1, 2, 0] + const auto gather = make_shared(shape_of, + op::Constant::create(element::i64, {3}, {2, 0, 1}), + op::Constant::create(element::i64, {}, {0})); + + const auto r = make_shared(arg, gather); + + ASSERT_EQ(r->get_output_element_type(v1::Transpose::ARG_T), element::f32); + ASSERT_EQ(r->get_output_partial_shape(v1::Transpose::ARG_T), PartialShape({Dimension(4, 16), 6, Dimension(2, 8)})); +} + +/** \brief Transpose with order as paramater shape dimensions after multiple transformations. */ +TEST(type_prop, transpose_order_as_parameter_shape_after_transformation) { + const auto arg = make_shared(element::f32, PartialShape{Dimension(2, 8), Dimension(4, 16), 6}); + + const auto param = make_shared(element::i64, PartialShape{8, 20, 1}); + const auto shape_of = make_shared(param); + const auto cast_fp = make_shared(shape_of, element::f32); + const auto mul = make_shared(cast_fp, op::Constant::create(element::f32, {3}, {-2, 1, -2})); + const auto div = make_shared(mul, op::Constant::create(element::f32, {3}, {-10, 41, -1})); + // order after convert [1, 0, 2] + const auto cast_int = make_shared(div, element::i32); + // order after gather [2, 1, 0] + const auto gather = make_shared(cast_int, + op::Constant::create(element::i32, {3}, {2, 0, 1}), + op::Constant::create(element::i32, {}, {0})); + + const auto r = make_shared(arg, gather); + + ASSERT_EQ(r->get_output_element_type(v1::Transpose::ARG_T), element::f32); + ASSERT_EQ(r->get_output_partial_shape(v1::Transpose::ARG_T), PartialShape({6, Dimension(4, 16), Dimension(2, 8)})); +} + +/** + * \brief Transpose when order is dimensions from parameter shape. + * + * One dimension is dynamic, transposed output shape cannot be deduced and will be dynamic. + */ +TEST(type_prop, transpose_when_order_is_shape_of_dynamic_partial_shape) { + const auto arg = make_shared(element::f32, PartialShape{Dimension(2, 8), Dimension(4, 16), 6}); + + const auto param = make_shared(element::i64, PartialShape{0, 2, Dimension(1, 2)}); + const auto shape_of = make_shared(param); + + const auto r = make_shared(arg, shape_of); + + ASSERT_EQ(r->get_output_element_type(v1::Transpose::ARG_T), element::f32); + ASSERT_EQ(r->get_output_partial_shape(v1::Transpose::ARG_T), PartialShape::dynamic(3)); +} + +using transpose_prop_params = tuple, // transpose order + PartialShape, // Input partial shape + PartialShape // Expected partial shape + >; + +// Test pre-defined constants. +static constexpr auto exp_type = element::f32; +static const auto interval_dim_1 = Dimension(3, 5); +static const auto interval_dim_2 = Dimension(1, 8); + +/** \brief Parametrize fixture to test transpose property. */ +class TransposeTest : public TestWithParam { +protected: + PartialShape input_p_shape, exp_p_shape; + vector transpose_order; + + void SetUp() override { + std::tie(transpose_order, input_p_shape, exp_p_shape) = GetParam(); + } + + vector make_seq_labels(const size_t first, const size_t count) { + vector labels; + + generate_n(std::back_inserter(labels), count, ov::SeqGen(first)); + return labels; + } + + vector make_seq_labels_by_order(const size_t first, const vector order) { + vector labels; + transform(order.cbegin(), order.cend(), back_inserter(labels), [&first](const int64_t& dim) { + return dim + first; + }); + return labels; + } +}; + +INSTANTIATE_TEST_SUITE_P( + type_prop, + TransposeTest, + Values(make_tuple(vector{2, 0, 1}, PartialShape{2, interval_dim_2, 4}, PartialShape{4, 2, interval_dim_2}), + make_tuple(vector{0, 2, 1}, + PartialShape{interval_dim_1, interval_dim_2, 4}, + PartialShape{interval_dim_1, 4, interval_dim_2}), + make_tuple(vector{1, 2, 3, 0}, + PartialShape{interval_dim_1, 2, 3, 4}, + PartialShape{2, 3, 4, interval_dim_1}), + make_tuple(vector{3, 0, 2, 1}, + PartialShape{interval_dim_1, 2, interval_dim_2, 4}, + PartialShape{4, interval_dim_1, interval_dim_2, 2}), + make_tuple(vector{1, 0, 3, 2}, + PartialShape{interval_dim_1, interval_dim_2, interval_dim_2, interval_dim_1}, + PartialShape{interval_dim_2, interval_dim_1, interval_dim_1, interval_dim_2})), + PrintToStringParamName()); + +TEST_P(TransposeTest, use_default_ctor) { + const auto input = make_shared(exp_type, input_p_shape); + const auto order = op::Constant::create(element::i64, Shape{transpose_order.size()}, transpose_order); + + const auto output = make_shared(); + output->set_arguments(NodeVector{input, order}); + output->validate_and_infer_types(); + + EXPECT_EQ(output->get_output_element_type(op::Transpose::ARG_T), exp_type); + EXPECT_EQ(output->get_output_partial_shape(op::Transpose::ARG_T), exp_p_shape); +} + +/** + * \brief Test interval dimension propagate in transpose. + * + * The interval dimensions should be moved accordingly to transpose order. + */ +TEST_P(TransposeTest, propagate_interval_shape) { + const auto input = make_shared(exp_type, input_p_shape); + const auto order = op::Constant::create(element::i64, Shape{transpose_order.size()}, transpose_order); + + const auto output = make_shared(input, order); + + EXPECT_EQ(output->get_output_element_type(op::Transpose::ARG_T), exp_type); + EXPECT_EQ(output->get_output_partial_shape(op::Transpose::ARG_T), exp_p_shape); +} + +/** + * \brief Check labels propagation for all dimensions. + * + * The labels should be moved accordingly to transpose order. + */ +TEST_P(TransposeTest, propagate_labels) { + constexpr size_t first_label = 33; + + const auto labels = make_seq_labels(first_label, transpose_order.size()); + const auto exp_labels = make_seq_labels_by_order(first_label, transpose_order); + + set_shape_labels(input_p_shape, labels); + + const auto input = make_shared(exp_type, input_p_shape); + const auto order = op::Constant::create(element::i64, Shape{transpose_order.size()}, transpose_order); + const auto output = make_shared(input, order); + + ASSERT_EQ(get_shape_labels(output->get_output_partial_shape(op::Transpose::ARG_T)), exp_labels); } diff --git a/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp b/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp index b7ab36457aa..7c1a494ebd7 100644 --- a/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp +++ b/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp @@ -14,11 +14,14 @@ #include #include "assign_shape_inference.hpp" +#include "batch_to_space_shape_inference.hpp" +#include "broadcast_shape_inference.hpp" #include "bucketize_shape_inference.hpp" #include "convolution_shape_inference.hpp" #include "ctc_greedy_decoder_seq_len_shape_inference.hpp" #include "ctc_greedy_decoder_shape_inference.hpp" #include "ctc_loss_shape_inference.hpp" +#include "depth_to_space_shape_inference.hpp" #include "detection_output_shape_inference.hpp" #include "einsum_shape_inference.hpp" #include "embedding_segments_sum_shape_inference.hpp" @@ -29,6 +32,7 @@ #include "experimental_detectron_roi_feature_shape_inference.hpp" #include "experimental_detectron_topkrois_shape_inference.hpp" #include "extract_image_patches_shape_inference.hpp" +#include "eye_shape_inference.hpp" #include "fake_quantize.hpp" #include "fft_base_shape_inference.hpp" #include "gather_elements_shape_inference.hpp" @@ -36,25 +40,8 @@ #include "gather_tree_shape_inference.hpp" #include "interpolate_shape_inference.hpp" #include "lstm_cell_shape_inference.hpp" +#include "matmul_shape_inference.hpp" #include "one_hot_shape_inference.hpp" -#include "read_value_shape_inference.hpp" -#include "reduce_shape_inference.hpp" -#include "reverse_sequence_shape_inference.hpp" -#include "scatter_elements_update_shape_inference.hpp" -#include "scatter_nd_base_shape_inference.hpp" -#include "ctc_loss_shape_inference.hpp" -#include "fft_base_shape_inference.hpp" -#include "shape_inference.hpp" -#include "shape_nodes.hpp" -#include "fake_quantize.hpp" -#include "batch_to_space_shape_inference.hpp" -#include "depth_to_space_shape_inference.hpp" -#include "space_to_batch_shape_inference.hpp" -#include "space_to_depth_shape_inference.hpp" -#include "experimental_detectron_detection_output_shape_inference.hpp" -#include "bucketize_shape_inference.hpp" -#include "embedding_segments_sum_shape_inference.hpp" -#include "embeddingbag_offsets_shape_inference.hpp" #include "pad_shape_inference.hpp" #include "proposal_shape_inference.hpp" #include "range_shape_inference.hpp" @@ -68,19 +55,18 @@ #include "scatter_elements_update_shape_inference.hpp" #include "scatter_nd_base_shape_inference.hpp" #include "select_shape_inference.hpp" -#include "shape_inference.hpp" #include "shape_nodes.hpp" #include "shuffle_channels_shape_inference.hpp" +#include "space_to_batch_shape_inference.hpp" +#include "space_to_depth_shape_inference.hpp" #include "split_shape_inference.hpp" -#include "broadcast_shape_inference.hpp" #include "static_shape.hpp" #include "strided_slice_shape_inference.hpp" #include "tile_shape_inference.hpp" #include "topk_shape_inference.hpp" +#include "transpose_shape_inference.hpp" #include "utils.hpp" #include "variadic_split_shape_inference.hpp" -#include "matmul_shape_inference.hpp" -#include "eye_shape_inference.hpp" namespace ov { namespace intel_cpu { @@ -563,6 +549,8 @@ std::shared_ptr make_shape_inference(const std::shared_ptr>(node); } else if (auto node = ov::as_type_ptr(op)) { return std::make_shared>(node); + } else if (auto node = ov::as_type_ptr(op)) { + return make_shared_entryIOC(node); } else { return std::make_shared(op); } diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/transpose_shape_infernece_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/transpose_shape_infernece_test.cpp new file mode 100644 index 00000000000..308674e7a3c --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/transpose_shape_infernece_test.cpp @@ -0,0 +1,106 @@ +// Copyright (C) 2018-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#include "gtest/gtest.h" +#include "openvino/op/constant.hpp" +#include "openvino/op/parameter.hpp" +#include "openvino/op/transpose.hpp" +#include "transpose_shape_inference.hpp" +#include "utils/shape_inference/static_shape.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using namespace testing; + +template +std::shared_ptr make_transpose(const TInput& input_shape, const TOrder& transpose_order) { + const auto input = std::make_shared(element::f32, PartialShape::dynamic(input_shape.size())); + const auto order = + std::make_shared(element::i64, ov::Shape{transpose_order.size()}, transpose_order); + return std::make_shared(input, order); +} + +using transpose_params = std::tuple, // transpose order + StaticShape, // Input shape + StaticShape // Expected shape + >; + +class StaticShapeInferenceTest : public TestWithParam { +protected: + void SetUp() override { + std::tie(transpose_order, input_shape, exp_shape) = GetParam(); + + transpose = make_transpose(input_shape, transpose_order); + } + + StaticShape input_shape, exp_shape; + std::vector transpose_order; + + std::shared_ptr transpose; +}; + +/** \brief Use transpose order -> output shape dimensions shall be as transpose order. */ +INSTANTIATE_TEST_SUITE_P( + transpose_by_order, + StaticShapeInferenceTest, + Values(make_tuple(std::vector{0}, StaticShape({3}), StaticShape({3})), + make_tuple(std::vector{0, 1}, StaticShape({5, 2}), StaticShape({5, 2})), + make_tuple(std::vector{1, 0}, StaticShape({8, 3}), StaticShape({3, 8})), + make_tuple(std::vector{2, 0, 1}, StaticShape({1, 0, 2}), StaticShape({2, 1, 0})), + make_tuple(std::vector{2, 0, 3, 1}, StaticShape({10, 8, 9, 2}), StaticShape({9, 10, 2, 8})), + make_tuple(std::vector{1, 3, 2, 0}, StaticShape({1, 2, 3, 4}), StaticShape({2, 4, 3, 1}))), + PrintToStringParamName()); + +/** \brief Empty transpose order -> output shape dimensions shall be in reverse order. */ +INSTANTIATE_TEST_SUITE_P( + transpose_reverse, + StaticShapeInferenceTest, + Values(make_tuple(std::vector{}, StaticShape({1}), StaticShape({1})), + make_tuple(std::vector{}, StaticShape({23}), StaticShape({23})), + make_tuple(std::vector{}, StaticShape({3, 8}), StaticShape({8, 3})), + make_tuple(std::vector{}, StaticShape({1, 0, 2}), StaticShape({2, 0, 1})), + make_tuple(std::vector{}, StaticShape({21, 1, 5, 9}), StaticShape({9, 5, 1, 21})), + make_tuple(std::vector{}, StaticShape({0, 0, 0}), StaticShape({0, 0, 0})), + make_tuple(std::vector{}, StaticShape({0, 2, 0}), StaticShape({0, 2, 0})), + make_tuple(std::vector{}, StaticShape({0, 2, 0, 0}), StaticShape({0, 0, 2, 0}))), + PrintToStringParamName()); + +/** \brief Check shape_infer for transpose on static shapes. */ +TEST_P(StaticShapeInferenceTest, transpose_static) { + auto output_shapes = std::vector{StaticShape{}}; + + shape_infer(transpose.get(), {input_shape, transpose_order}, output_shapes); + + ASSERT_EQ(output_shapes[op::v1::Transpose::ARG_T], exp_shape); +} + +/** \brief Shape infer when transpose input got dynamic dimensions. */ +TEST(StaticShapeInferenceTest, transpose_input_shape_dim_dynamic) { + const auto input_shape = PartialShape{-1, -1, -1}; + const auto order = std::vector{1, 2, 0}; + const auto transpose = make_transpose(input_shape, order); + + auto output_shapes = std::vector{StaticShape{}}; + + shape_infer(transpose.get(), {StaticShape{2, 6, 3}, order}, output_shapes); + ASSERT_EQ(output_shapes[op::v1::Transpose::ARG_T], StaticShape({6, 3, 2})); +} + +/** \brief Shape inference when transpose order stored in constant map. */ +TEST(StaticShapeInferenceTest, transpose_order_in_constant_map) { + const auto input_shape = PartialShape{2, 4, 6, 8}; + const auto input = std::make_shared(element::f32, input_shape); + const auto order = std::make_shared(element::i64, Shape{4}); + + const auto transpose = std::make_shared(input, order); + + const auto axes_order = std::vector{1, 2, 0, 3}; + const auto axes = std::make_shared(element::i64, ov::Shape{axes_order.size()}, axes_order); + const auto const_tensor = std::make_shared(axes); + const std::map> const_map = {{1, const_tensor}}; + + auto output_shapes = std::vector{StaticShape{}}; + shape_infer(transpose.get(), {StaticShape({2, 4, 6, 8}), StaticShape()}, output_shapes, const_map); + + ASSERT_EQ(output_shapes[op::v1::Transpose::ARG_T], StaticShape({4, 6, 2, 8})); +} diff --git a/src/tests/functional/inference_engine/lp_transformations/transpose_transformation.cpp b/src/tests/functional/inference_engine/lp_transformations/transpose_transformation.cpp index 0e960f49a21..bc30a9fbdd2 100644 --- a/src/tests/functional/inference_engine/lp_transformations/transpose_transformation.cpp +++ b/src/tests/functional/inference_engine/lp_transformations/transpose_transformation.cpp @@ -306,14 +306,14 @@ const std::vector testValues = { } }, { + ngraph::element::u8, + {{}, {}, {}}, ngraph::element::u8, { {ngraph::element::f32}, { {128}, ngraph::element::f32, {}, true, 1, ngraph::element::u8, true }, {0.1f} - }, - ngraph::element::f32, - {{}, {}, {}} + } } }, };