diff --git a/src/plugins/intel_gpu/src/graph/tile.cpp b/src/plugins/intel_gpu/src/graph/tile.cpp index ba961c744d4..85aa8685a49 100644 --- a/src/plugins/intel_gpu/src/graph/tile.cpp +++ b/src/plugins/intel_gpu/src/graph/tile.cpp @@ -51,13 +51,19 @@ std::vector tile_inst::calc_output_layouts(tile_node const& /*node*/, co }; auto& constant_mem = impl_param.memory_deps; - if (constant_mem.count(1)) { - auto repeats_mem = constant_mem.at(1); - cldnn::mem_lock repeats_lock(repeats_mem, impl_param.prog->get_stream()); - const auto& layout = repeats_mem->get_layout(); - const auto repeats_tensor = - ov::Tensor(data_type_to_element_type(layout.data_type), layout.get_shape(), repeats_lock.data()); - output_shapes = ov::op::v0::shape_infer(&op, input_shapes, {{1, repeats_tensor}}); + if (desc->input_size() == 2) { + if (constant_mem.count(1)) { + auto repeats_mem = constant_mem.at(1); + cldnn::mem_lock repeats_lock(repeats_mem, impl_param.prog->get_stream()); + const auto& layout = repeats_mem->get_layout(); + const auto repeats_tensor = + ov::Tensor(data_type_to_element_type(layout.data_type), layout.get_shape(), repeats_lock.data()); + output_shapes = ov::op::v0::shape_infer(&op, input_shapes, {{1, repeats_tensor}}); + } else if (repeats_shape.size() > 0 && repeats_shape[0].is_static()) { + output_shapes = { ov::PartialShape::dynamic(std::max(input_shapes[0].size(), repeats_shape[0].get_length())) }; + } else { + output_shapes = { ov::PartialShape::dynamic() }; + } } else { auto repeats_data = desc->repeats; const auto repeats_tensor = diff --git a/src/plugins/intel_gpu/src/plugin/ops/tile.cpp b/src/plugins/intel_gpu/src/plugin/ops/tile.cpp index d81118e208e..8d090dde673 100644 --- a/src/plugins/intel_gpu/src/plugin/ops/tile.cpp +++ b/src/plugins/intel_gpu/src/plugin/ops/tile.cpp @@ -17,43 +17,42 @@ static void CreateTileOp(Program& p, const std::shared_ptr validate_inputs_count(op, {2}); auto inputs = p.GetInputInfo(op); std::string layerName = layer_type_name_ID(op); + if (auto repeats_const = std::dynamic_pointer_cast(op->get_input_node_shared_ptr(1))) { + std::vector repeats = repeats_const->cast_vector(); - auto repeatsNode = std::dynamic_pointer_cast(op->get_input_node_shared_ptr(1)); - if (!repeatsNode) - IE_THROW() << "Unsupported parameter nodes type in " << op->get_friendly_name() << - " (" << op->get_type_name() << ")"; - std::vector repeats = repeatsNode->cast_vector(); + // TODO: Remove code below once new shape infer is enabled + if (!op->is_dynamic() && !p.use_new_shape_infer()) { + size_t rank = op->get_input_shape(0).size(); + int64_t defaultSize = 1; + for (size_t i = repeats.size(); i < rank; ++i) { + repeats.insert(repeats.begin(), defaultSize); + } - // TODO: Remove code below once new shape infer is enabled - if (!op->is_dynamic() && !p.use_new_shape_infer()) { - size_t rank = op->get_input_shape(0).size(); - int64_t defaultSize = 1; - for (size_t i = repeats.size(); i < rank; ++i) { - repeats.insert(repeats.begin(), defaultSize); + if (repeats.size() > rank) { + std::string reshapeName = layerName + "_reshape"; + auto inputDims = op->get_input_shape(0); + + // Extend input dimensions to the same size as repeats dimensions by prepending ones + inputDims.insert(inputDims.begin(), repeats.size() - rank, defaultSize); + + auto targetShape = tensor_from_dims(inputDims); + + auto reshapePrim = cldnn::reshape(reshapeName, inputs[0], targetShape); + + p.add_primitive(*op, reshapePrim); + + inputs[0] = cldnn::input_info(reshapeName); + } } - if (repeats.size() > rank) { - std::string reshapeName = layerName + "_reshape"; - auto inputDims = op->get_input_shape(0); - - // Extend input dimensions to the same size as repeats dimensions by prepending ones - inputDims.insert(inputDims.begin(), repeats.size() - rank, defaultSize); - - auto targetShape = tensor_from_dims(inputDims); - - auto reshapePrim = cldnn::reshape(reshapeName, inputs[0], targetShape); - - p.add_primitive(*op, reshapePrim); - - inputs[0] = cldnn::input_info(reshapeName); - } + p.add_primitive(*op, cldnn::tile(layerName, + inputs[0], + repeats)); + } else { + p.add_primitive(*op, cldnn::tile(layerName, + inputs[0], + inputs[1])); } - - auto tilePrim = cldnn::tile(layerName, - inputs[0], - repeats); - - p.add_primitive(*op, tilePrim); } REGISTER_FACTORY_IMPL(v0, Tile); diff --git a/src/plugins/intel_gpu/tests/shape_infer/tile_si_test.cpp b/src/plugins/intel_gpu/tests/shape_infer/tile_si_test.cpp index fa2cfa86d8d..76b62ddf960 100644 --- a/src/plugins/intel_gpu/tests/shape_infer/tile_si_test.cpp +++ b/src/plugins/intel_gpu/tests/shape_infer/tile_si_test.cpp @@ -50,9 +50,16 @@ TEST_P(tile_test_two_inputs, shape_infer) { program_wrapper::add_connection(prog, repeats_node, tile_node); auto params = tile_node.get_kernel_impl_params(); - params->memory_deps = {{1, repeats_mem}}; auto res = tile_inst::calc_output_layouts(tile_node, *params); + auto expected_layout_dyn = p.expected_layout; + expected_layout_dyn.set_partial_shape(ov::PartialShape::dynamic(expected_layout_dyn.get_partial_shape().size())); + ASSERT_EQ(res.size(), 1); + ASSERT_EQ(res[0], expected_layout_dyn); + + params->memory_deps = {{1, repeats_mem}}; + res = tile_inst::calc_output_layouts(tile_node, *params); + ASSERT_EQ(res.size(), 1); ASSERT_EQ(res[0], p.expected_layout); } diff --git a/src/tests/functional/plugin/gpu/single_layer_tests/dynamic/tile.cpp b/src/tests/functional/plugin/gpu/single_layer_tests/dynamic/tile.cpp new file mode 100644 index 00000000000..15998e3de2f --- /dev/null +++ b/src/tests/functional/plugin/gpu/single_layer_tests/dynamic/tile.cpp @@ -0,0 +1,234 @@ +// Copyright (C) 2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include +#include +#include "ngraph_functions/utils/ngraph_helpers.hpp" +#include "ngraph_functions/builders.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" +#include "shared_test_classes/single_layer/tile.hpp" +#include "common_test_utils/test_constants.hpp" +#include "common_test_utils/ov_tensor_utils.hpp" + +using namespace InferenceEngine; +using namespace ov::test; + +namespace GPULayerTestsDefinitions { + +using TileLayerTestParamsSet = typename std::tuple< + std::vector, // Input shapes + std::vector, // Repeats + ov::element::Type_t, // Network precision + bool, // Is Repeats input constant + std::string>; // Device name + + +class TileLayerGPUTest : public testing::WithParamInterface, + public ov::test::SubgraphBaseTest { +public: + static std::string getTestCaseName(testing::TestParamInfo obj) { + TileLayerTestParamsSet basicParamsSet = obj.param; + + std::vector inputShapes; + std::vector repeats; + ov::element::Type_t netPrecision; + bool isRepeatsConst; + std::string deviceName; + std::tie(inputShapes, repeats, netPrecision, isRepeatsConst, deviceName) = basicParamsSet; + + std::ostringstream result; + result << "IS=("; + for (const auto& shape : inputShapes) { + result << CommonTestUtils::partialShape2str({shape.first}) << "_"; + } + result << ")_TS=("; + for (const auto& shape : inputShapes) { + for (const auto& item : shape.second) { + result << CommonTestUtils::vec2str(item) << "_"; + } + } + result << "Repeats=" << CommonTestUtils::vec2str(repeats) << "_"; + result << "netPrec=" << netPrecision << "_"; + result << "constRepeats=" << (isRepeatsConst ? "True" : "False") << "_"; + result << "trgDev=" << deviceName; + + return result.str(); + } + +protected: + void SetUp() override { + TileLayerTestParamsSet basicParamsSet = this->GetParam(); + + std::vector inputShapes; + ov::element::Type_t netPrecision; + bool isRepeatsConst; + std::tie(inputShapes, repeatsData, netPrecision, isRepeatsConst, targetDevice) = basicParamsSet; + + if (inputShapes.front().first.rank() != 0) { + inputDynamicShapes.push_back(inputShapes.front().first); + if (!isRepeatsConst) { + inputDynamicShapes.push_back({ static_cast(repeatsData.size()) }); + } + } + const size_t targetStaticShapeSize = inputShapes.front().second.size(); + targetStaticShapes.resize(targetStaticShapeSize); + for (size_t i = 0lu; i < targetStaticShapeSize; ++i) { + targetStaticShapes[i].push_back(inputShapes.front().second[i]); + if (!isRepeatsConst) + targetStaticShapes[i].push_back({ repeatsData.size() }); + } + + ov::ParameterVector functionParams; + if (inputDynamicShapes.empty()) { + functionParams.push_back(std::make_shared(netPrecision, targetStaticShapes.front().front())); + } else { + functionParams.push_back(std::make_shared(netPrecision, inputDynamicShapes.front())); + if (!isRepeatsConst) { + functionParams.push_back(std::make_shared(ov::element::i64, inputDynamicShapes[1])); + functionParams.back()->set_friendly_name("repeats"); + } + } + functionParams.front()->set_friendly_name("data"); + + auto paramOuts = ngraph::helpers::convert2OutputVector(ngraph::helpers::castOps2Nodes(functionParams)); + std::shared_ptr tileNode; + if (isRepeatsConst) { + tileNode = std::make_shared(paramOuts[0], + ov::op::v0::Constant::create(ov::element::i64, { repeatsData.size() }, repeatsData)); + } else { + tileNode = std::make_shared(paramOuts[0], paramOuts[1]); + } + + ngraph::ResultVector results; + for (size_t i = 0; i < tileNode->get_output_size(); i++) { + results.push_back(std::make_shared(tileNode->output(i))); + } + + function = std::make_shared(results, functionParams, "Tile"); + } + + void generate_inputs(const std::vector& targetInputStaticShapes) override { + inputs.clear(); + const auto& funcInputs = function->inputs(); + for (size_t i = 0lu; i < funcInputs.size(); i++) { + const auto& funcInput = funcInputs[i]; + ov::Tensor tensor; + if (funcInput.get_node()->get_friendly_name() == "repeats") { + tensor = ov::Tensor{ov::element::i64, targetInputStaticShapes[i]}; + auto data = tensor.data::value_type>(); + for (size_t i = 0lu; i < repeatsData.size(); i++) { + data[i] = repeatsData[i]; + } + } else { + if (funcInput.get_element_type().is_real()) { + tensor = ov::test::utils::create_and_fill_tensor( + funcInput.get_element_type(), targetInputStaticShapes[i], 10, 0, 1000); + } else { + tensor = ov::test::utils::create_and_fill_tensor(funcInput.get_element_type(), targetInputStaticShapes[i]); + } + } + inputs.insert({funcInput.get_node_shared_ptr(), tensor}); + } + } + + std::vector repeatsData; +}; + +TEST_P(TileLayerGPUTest, CompareWithRefs) { + run(); +} + +namespace { + +const std::vector netPrecisions = { + ov::element::f32, + ov::element::f16, +}; + +const std::vector> dynamicInputShapes4D = { + { + { // Origin dynamic shapes + {ov::Dimension(1, 20), ov::Dimension(10, 20), ov::Dimension(1, 20), ov::Dimension(1, 20)}, + { // Dynamic shapes instances + {2, 16, 3, 4}, + {1, 16, 1, 1}, + {1, 16, 2, 3} + } + } + }, + { + { // Origin dynamic shapes + {-1, -1, -1, -1}, + { // Dynamic shapes instances + {3, 15, 5, 7}, + {4, 55, 8, 24} + } + } + } +}; + +const std::vector> dynamicInputShapes5D = { + { + { // Origin dynamic shapes + {ov::Dimension(1, 20), ov::Dimension(1, 20), ov::Dimension(1, 20), ov::Dimension(1, 20), ov::Dimension(1, 70)}, + { // Dynamic shapes instances + {2, 16, 2, 3, 4}, + {1, 16, 8, 5, 4}, + {8, 1, 2, 3, 64} + } + } + }, + { + { // Origin dynamic shapes + {-1, -1, -1, -1, -1}, + { // Dynamic shapes instances + {2, 16, 2, 3, 4}, + {1, 16, 8, 5, 4}, + {8, 1, 2, 3, 64} + } + } + } +}; + +const std::vector> repeats4D = { + {2, 3}, + {1, 2, 3}, + {1, 1, 1, 1}, + {1, 1, 2, 3}, + {1, 2, 1, 3}, + {2, 1, 1, 1}, + {2, 3, 1, 1} +}; +const std::vector> repeats5D = { + {1, 2, 3}, + {1, 1, 2, 3}, + {1, 1, 1, 2, 3}, + {1, 2, 1, 1, 3}, + {2, 1, 1, 1, 1}, + {2, 3, 1, 1, 1} +}; + +INSTANTIATE_TEST_CASE_P(DynamicShape4D, TileLayerGPUTest, + ::testing::Combine( + ::testing::ValuesIn(dynamicInputShapes4D), + ::testing::ValuesIn(repeats4D), + ::testing::ValuesIn(netPrecisions), + ::testing::Values(true, false), + ::testing::Values(CommonTestUtils::DEVICE_GPU)), + TileLayerGPUTest::getTestCaseName); + +INSTANTIATE_TEST_CASE_P(DynamicShape5D, TileLayerGPUTest, + ::testing::Combine( + ::testing::ValuesIn(dynamicInputShapes5D), + ::testing::ValuesIn(repeats5D), + ::testing::ValuesIn(netPrecisions), + ::testing::Values(true, false), + ::testing::Values(CommonTestUtils::DEVICE_GPU)), + TileLayerGPUTest::getTestCaseName); + +} // namespace + +} // namespace GPULayerTestsDefinitions