diff --git a/src/common/transformations/tests/utils/convert_precision.cpp b/src/common/transformations/tests/utils/convert_precision.cpp index a2edb0232b40f2..75cf81d295e3ac 100644 --- a/src/common/transformations/tests/utils/convert_precision.cpp +++ b/src/common/transformations/tests/utils/convert_precision.cpp @@ -2441,9 +2441,6 @@ TEST(TransformationTests, ConvertPrecisionExplicitConvertsSingleNodeMultipleOutp auto convert_1 = make_shared(param_1, element::f32); auto axis = opset10::Constant::create(element::i32, Shape{}, {0}); auto split = make_shared(convert_1, axis, 3); - split->get_output_tensor(0).add_names({"split:0"}); - split->get_output_tensor(1).add_names({"split:1"}); - split->get_output_tensor(2).add_names({"split:2"}); auto convert_split_0 = make_shared(split->output(0), element::f64); auto convert_split_1 = make_shared(split->output(1), element::f64); @@ -2567,7 +2564,6 @@ TEST(TransformationTests, ConvertPrecisionExplicitConvertsMultiSubgraphs) { if_op->set_input(convert_1, param_1_then, param_1_else); if_op->set_input(convert_2, param_2_then, param_2_else); auto result = if_op->set_output(result_then, result_else); - result.add_names({"if_result:0"}); auto converted_result = make_shared(result, element::f64); converted_result->get_output_tensor(0).add_names({"if_result:0"}); diff --git a/src/core/include/openvino/op/result.hpp b/src/core/include/openvino/op/result.hpp index 9cad2d9444a267..d7a1cd8fa803a5 100644 --- a/src/core/include/openvino/op/result.hpp +++ b/src/core/include/openvino/op/result.hpp @@ -69,6 +69,12 @@ class OPENVINO_API Result : public Op { /// \param arg Node that produces the input tensor. Result(const Output& arg); + /// \brief Allows a value to be used as a function result. + /// + /// \param arg Node that produces the input tensor. + /// \param use_input_names When true Result will use input node tensor names as Result's output names. + Result(const Output& arg, bool use_input_names); + void validate_and_infer_types() override; std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; diff --git a/src/core/src/model.cpp b/src/core/src/model.cpp index 1493d950cd78ef..e2e4115d925b16 100644 --- a/src/core/src/model.cpp +++ b/src/core/src/model.cpp @@ -949,8 +949,8 @@ ov::Output ov::Model::add_output(const ov::Output& port) { return input.get_node()->output(0); } } - auto result = std::make_shared(port); - m_results.push_back(result); + m_results.emplace_back(std::make_shared(port, true)); + auto& result = m_results.back(); if (m_shared_rt_info->get_use_topological_cache()) { if (cache_valid()) { // Full update of topological cache is not needed, 'result' can be just inserted to the end diff --git a/src/core/src/node_vector.cpp b/src/core/src/node_vector.cpp index 3ef52278ffa5a6..d1bb7ffd9d316b 100644 --- a/src/core/src/node_vector.cpp +++ b/src/core/src/node_vector.cpp @@ -29,7 +29,7 @@ ov::ResultVector ov::as_result_vector(const OutputVector& values) { for (const auto& value : values) { std::shared_ptr node = value.get_node_shared_ptr(); result.push_back(ov::is_type(node) ? ov::as_type_ptr(node) - : std::make_shared(value)); + : std::make_shared(value, true)); } return result; } diff --git a/src/core/src/op/result.cpp b/src/core/src/op/result.cpp index 97dc95a0e53f17..f527c66bb1d4fc 100644 --- a/src/core/src/op/result.cpp +++ b/src/core/src/op/result.cpp @@ -10,6 +10,7 @@ #include "itt.hpp" #include "openvino/core/descriptor_tensor.hpp" +#include "openvino/op/util/op_types.hpp" namespace ov { namespace op { @@ -19,6 +20,13 @@ Result::Result(const Output& arg) : Op({arg}) { constructor_validate_and_infer_types(); } +Result::Result(const Output& arg, bool use_input_names) : Result(arg) { + if (use_input_names && !util::is_parameter(arg.get_node())) { + // On create use inputs names which will be used as model output names (except Paramater, model's inputs names). + get_output_tensor(0).add_names(get_input_tensor(0).get_names()); + } +} + void Result::validate_and_infer_types() { OV_OP_SCOPE(v0_Result_validate_and_infer_types); NODE_VALIDATION_CHECK(this, get_input_size() == 1, "Argument has ", get_input_size(), " outputs (1 expected)."); diff --git a/src/core/tests/preprocess.cpp b/src/core/tests/preprocess.cpp index 99f2789b217b6d..86b8dad63ba01e 100644 --- a/src/core/tests/preprocess.cpp +++ b/src/core/tests/preprocess.cpp @@ -2096,12 +2096,94 @@ TEST(pre_post_process, postprocess_one_node_many_outputs) { p.output(2).tensor().set_element_type(element::f32); model = p.build(); EXPECT_EQ(model->get_results().size(), 3); - // Tensor names on output is lost as origin named tensor is before convert op + // Tensor names on modified outputs are set to Split tensors not model output. // New result has different precision means different tensor. EXPECT_EQ(model->output(0).get_tensor().get_names().count("tensor_Split0"), 0); + EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 0); + // Add output node still on output after pre-processing EXPECT_EQ(model->output(0).get_tensor().get_names().count("output_split0"), 1); + // Not modified output still have name EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1); - EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 0); + EXPECT_EQ(model->get_results()[0]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.0"); + EXPECT_EQ(model->get_results()[1]->input(0).get_source_output().get_node()->get_friendly_name(), "Split"); + EXPECT_EQ(model->get_results()[2]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.2"); +} + +TEST(pre_post_process, postprocess_one_node_many_outputs_results_created_by_model) { + auto data1 = std::make_shared(element::i32, Shape{3}); + auto c1 = opset8::Constant::create(element::i32, Shape{}, {0}); + auto op = std::make_shared(data1, c1, 3); + op->set_friendly_name("Split"); + op->output(0).set_names({"tensor_Split0"}); + auto r1 = std::make_shared(op->output(0)); + + OutputVector outputs{r1}; + for (size_t i = 1; i < op->get_num_splits(); i++) { + auto output = op->output(i); + output.set_names({"tensor_Split" + std::to_string(i)}); + outputs.push_back(std::move(output)); + } + auto model = std::make_shared(outputs, ParameterVector{data1}); + // Set tensor name to model output 0 + model->output(0).set_names({"output_split0"}); + EXPECT_EQ(model->output(0).get_tensor().get_names().count("output_split0"), 1); + // Result input has still tensor_split0 names from split op + EXPECT_EQ(model->output(0).get_node()->get_input_tensor(0).get_names().count("tensor_Split0"), 1); + EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1); + EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 1); + + auto p = PrePostProcessor(model); + p.output(0).tensor().set_element_type(element::f32); + p.output(2).tensor().set_element_type(element::f32); + model = p.build(); + EXPECT_EQ(model->get_results().size(), 3); + + // output 0 by user,not use input nodes names as its own, modified by PPP (tensor_Split0 is on split output) + EXPECT_EQ(model->output(0).get_tensor().get_names().count("tensor_Split0"), 0); + EXPECT_EQ(model->output(0).get_tensor().get_names().count("output_split0"), 1); + // output 1 created by model, assume its names is output name, not modified by PPP + EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1); + // output 2 created by model, assume its names is output name, modified by PPP + EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 1); + EXPECT_EQ(model->get_results()[0]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.0"); + EXPECT_EQ(model->get_results()[1]->input(0).get_source_output().get_node()->get_friendly_name(), "Split"); + EXPECT_EQ(model->get_results()[2]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.2"); +} + +TEST(pre_post_process, postprocess_one_node_many_outputs_results_created_or_added_by_model) { + auto data1 = std::make_shared(element::i32, Shape{3}); + auto c1 = opset8::Constant::create(element::i32, Shape{}, {0}); + auto op = std::make_shared(data1, c1, 3); + op->set_friendly_name("Split"); + for (size_t i = 0; i < op->get_output_size(); ++i) { + op->output(i).set_names({"tensor_Split" + std::to_string(i)}); + } + + OutputVector outputs{std::make_shared(op->output(0)), op->output(1)}; + + auto model = std::make_shared(outputs, ParameterVector{data1}); + model->add_output(op->output(2)); + // Set tensor name to model output 0 + model->output(0).set_names({"output_split0"}); + EXPECT_EQ(model->output(0).get_tensor().get_names().count("output_split0"), 1); + // Result input has still tensor_split0 names from split op + EXPECT_EQ(model->output(0).get_node()->get_input_tensor(0).get_names().count("tensor_Split0"), 1); + EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1); + EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 1); + + auto p = PrePostProcessor(model); + p.output(0).tensor().set_element_type(element::f32); + p.output(2).tensor().set_element_type(element::f32); + model = p.build(); + EXPECT_EQ(model->get_results().size(), 3); + + // output 0 by user,not use input nodes names as its own, modified by PPP (tensor_Split0 is on split output) + EXPECT_EQ(model->output(0).get_tensor().get_names().count("tensor_Split0"), 0); + EXPECT_EQ(model->output(0).get_tensor().get_names().count("output_split0"), 1); + // output 1 created by model, assume its names is output name, not modified by PPP + EXPECT_EQ(model->output(1).get_tensor().get_names().count("tensor_Split1"), 1); + // output 2 created by model, assume its names is output name, modified by PPP + EXPECT_EQ(model->output(2).get_tensor().get_names().count("tensor_Split2"), 1); EXPECT_EQ(model->get_results()[0]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.0"); EXPECT_EQ(model->get_results()[1]->input(0).get_source_output().get_node()->get_friendly_name(), "Split"); EXPECT_EQ(model->get_results()[2]->input(0).get_source_output().get_node()->get_friendly_name(), "Split.2"); diff --git a/src/core/tests/type_prop/result.cpp b/src/core/tests/type_prop/result.cpp index 9776768df052a0..03b7c550f22938 100644 --- a/src/core/tests/type_prop/result.cpp +++ b/src/core/tests/type_prop/result.cpp @@ -10,6 +10,7 @@ namespace ov { namespace test { +using ov::op::v0::Constant; using ov::op::v0::Parameter; using std::make_shared; using testing::UnorderedElementsAre; @@ -135,7 +136,7 @@ TEST_F(TypePropResultV0Test, preserve_specific_name_on_input_replace) { const auto a = std::make_shared(element::f32, PartialShape::dynamic()); a->get_output_tensor(0).set_names({"input a"}); - const auto result = make_op(a); + const auto result = make_op(a, true); result->output(0).set_names({"out"}); EXPECT_THAT(result->input(0).get_tensor().get_names(), UnorderedElementsAre("out", "input a")); @@ -151,5 +152,23 @@ TEST_F(TypePropResultV0Test, preserve_specific_name_on_input_replace) { EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("out")); EXPECT_THAT(a->output(0).get_names(), UnorderedElementsAre("input a")); } + +TEST_F(TypePropResultV0Test, take_input_node_names) { + const auto c = std::make_shared(element::f32, Shape{2}, std::vector{2.f, 1.f}); + c->get_output_tensor(0).set_names({"constant data"}); + const auto result = make_op(c, true); + + EXPECT_THAT(result->input(0).get_tensor().get_names(), UnorderedElementsAre("constant data")); + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("constant data")); + + const auto new_const = std::make_shared(element::f32, Shape{2}, std::vector{0.f, 0.f}); + + result->input(0).replace_source_output(new_const); + result->validate_and_infer_types(); + + EXPECT_THAT(c->get_output_tensor(0).get_names(), testing::IsEmpty()); + EXPECT_THAT(result->get_input_tensor(0).get_names(), UnorderedElementsAre("constant data")); + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("constant data")); +} } // namespace test } // namespace ov diff --git a/src/frontends/ir/src/ir_deserializer.cpp b/src/frontends/ir/src/ir_deserializer.cpp index 62caebeee7d355..d05f7e04bbe5e6 100644 --- a/src/frontends/ir/src/ir_deserializer.cpp +++ b/src/frontends/ir/src/ir_deserializer.cpp @@ -1033,7 +1033,7 @@ std::shared_ptr ov::XmlDeserializer::create_node(const std::vectorget_input_source_output(0).get_node())) { // Copy names if parent node is not parameter, model's input names should not be dedicated // output names as they could be removed from Parameter's tensor during model transformations. - ov::descriptor::copy_tensor_names(result->get_output_tensor(0), result->get_input_tensor(0)); + result->get_output_tensor(0).add_names(result->get_input_tensor(0).get_names()); } } } diff --git a/src/frontends/onnx/frontend/src/input_model.cpp b/src/frontends/onnx/frontend/src/input_model.cpp index 9410f54e428b3f..87f1439eb18b38 100644 --- a/src/frontends/onnx/frontend/src/input_model.cpp +++ b/src/frontends/onnx/frontend/src/input_model.cpp @@ -533,13 +533,6 @@ void InputModel::add_tensor_names(std::shared_ptr& model) { it->add_names(tensor_names.second); } } - - // Set model output names - for (auto&& result : model->get_results()) { - if (!is_type(result->get_input_source_output(0).get_node())) { - result->get_output_tensor(0).add_names(result->get_input_tensor(0).get_names()); - } - } } void InputModel::reshape_model_inputs(std::shared_ptr& model) {