Skip to content

Commit 5fadde6

Browse files
authored
Enable ResolveNamesCollisions transformation in MOC #2 (openvinotoolkit#18772)
* ResolveNamesCollisions transformation refactoring; enable it in MOC * fix the description * call ResolveNamesCollisions transformation in the frontends; resolve review comments * Resolve review comments * fix EliminateUnsqueezeGather and AlignMixedTypes transformations
1 parent 5113900 commit 5fadde6

File tree

15 files changed

+210
-108
lines changed

15 files changed

+210
-108
lines changed

src/common/transformations/include/transformations/resolve_names_collisions.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace pass {
1313
/**
1414
* @ingroup ie_transformation_common_api
1515
* @brief ResolveNameCollisions transformation helps to fix names collisions
16-
* if some internal nodes or nodes with autogenerated names have conflicts with other nodes from the original graph
16+
* if nodes with autogenerated names have conflicts with other node names.
1717
*
1818
* Every transformation call can change the graph structure and create some additional operations,
1919
* autogenerated name is used if new operation doesn't have friendly name.

src/common/transformations/src/transformations/common_optimizations/eliminate_unsqueeze_gather.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,30 @@ ov::pass::EliminateUnsqueezeGather::EliminateUnsqueezeGather() {
6565

6666
ov::pass::EliminateGatherUnsqueeze::EliminateGatherUnsqueeze() {
6767
MATCHER_SCOPE(EliminateGatherUnsqueeze);
68-
68+
const auto are_all_outputs_unsqueezes = [](const Output<Node>& out) -> bool {
69+
const auto& target_inputs = out.get_target_inputs();
70+
bool res = out.get_partial_shape().rank() == 0 && !target_inputs.empty();
71+
for (const auto& target_input : target_inputs) {
72+
if (!res) {
73+
break;
74+
}
75+
auto unsqueeze = ov::as_type<ov::op::v0::Unsqueeze>(target_input.get_node());
76+
res = unsqueeze != nullptr && unsqueeze->output(0).get_partial_shape().rank() == 1;
77+
}
78+
return res;
79+
};
6980
const auto gather_indices_label = ngraph::pattern::wrap_type<ov::op::v0::Constant>(pattern::rank_equals(0));
7081
const auto gather_axis_label = ngraph::pattern::wrap_type<ov::op::v0::Constant>();
7182
const auto gather_label = ngraph::pattern::wrap_type<op::util::GatherBase>(
7283
{pass::pattern::any_input(), gather_indices_label, gather_axis_label},
73-
pattern::rank_equals(0));
74-
75-
const auto unsqueeze_label =
76-
ngraph::pattern::wrap_type<ov::op::v0::Unsqueeze>({gather_label, pass::pattern::any_input()},
77-
pattern::rank_equals(1));
84+
are_all_outputs_unsqueezes);
7885

7986
ov::matcher_pass_callback callback = [=](ngraph::pattern::Matcher& m) {
8087
auto pattern_nodes = m.get_pattern_map();
8188

8289
auto& gather_indices = pattern_nodes.at(gather_indices_label);
8390
auto& gather = pattern_nodes.at(gather_label);
84-
auto& unsqueeze = pattern_nodes.at(unsqueeze_label);
91+
const auto& target_unsqueezes = gather->output(0).get_target_inputs();
8592

8693
auto new_indices =
8794
ov::op::util::make_try_fold<ov::op::v1::Reshape>(gather_indices,
@@ -90,11 +97,13 @@ ov::pass::EliminateGatherUnsqueeze::EliminateGatherUnsqueeze() {
9097
auto new_gather = gather->clone_with_new_inputs({gather->input_value(0), new_indices, gather->input_value(2)});
9198

9299
new_gather->set_friendly_name(gather->get_friendly_name());
93-
ngraph::copy_runtime_info({unsqueeze, gather}, {new_gather, new_indices});
94-
ngraph::replace_node(unsqueeze, new_gather);
100+
ngraph::copy_runtime_info({gather}, {new_gather, new_indices});
101+
for (const auto& unsqueeze : target_unsqueezes) {
102+
unsqueeze.get_node()->output(0).replace(new_gather);
103+
}
95104
return true;
96105
};
97106

98-
auto m = std::make_shared<ngraph::pattern::Matcher>(unsqueeze_label, matcher_name);
107+
auto m = std::make_shared<ngraph::pattern::Matcher>(gather_label, matcher_name);
99108
register_matcher(m, callback);
100109
}

src/common/transformations/src/transformations/common_optimizations/moc_transformations.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
#include <transformations/smart_reshape/reshape_sinking.hpp>
8585

8686
#include "itt.hpp"
87+
#include "transformations/resolve_names_collisions.hpp"
8788

8889
bool ov::pass::MOCTransformations::run_on_model(const std::shared_ptr<ngraph::Function>& f) {
8990
RUN_ON_FUNCTION_SCOPE(MOCTransformations);
@@ -246,6 +247,7 @@ bool ov::pass::MOCTransformations::run_on_model(const std::shared_ptr<ngraph::Fu
246247
REGISTER_PASS(manager, AlignEltwiseInputRanks)
247248
REGISTER_PASS(manager, SharedOpOptimization)
248249
REGISTER_PASS(manager, ConstantFolding)
250+
REGISTER_PASS(manager, ResolveNameCollisions)
249251
manager.run_passes(f);
250252

251253
if (!m_use_shapes) {

src/common/transformations/src/transformations/fp16_compression/align_mixed_fp32_fp16_types.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ using namespace ov;
1616

1717
bool ov::pass::AlignMixedFP32FP16Types::run_on_model(const std::shared_ptr<ov::Model>& model) {
1818
RUN_ON_MODEL_SCOPE(AlignMixedFP32FP16Types);
19+
std::unordered_set<std::string> new_friendly_names;
20+
21+
auto generate_uniq_name = [&new_friendly_names](const std::string& initial_name) {
22+
int idx = 0;
23+
auto cur_name = initial_name;
24+
while (new_friendly_names.find(cur_name) != new_friendly_names.end()) {
25+
cur_name = initial_name + ":" + std::to_string(idx++);
26+
}
27+
new_friendly_names.insert(cur_name);
28+
return cur_name;
29+
};
1930

2031
std::function<bool(const std::shared_ptr<Node>&)> insert_converts_before_if_needed =
2132
[&](const std::shared_ptr<Node>& node) {
@@ -32,7 +43,8 @@ bool ov::pass::AlignMixedFP32FP16Types::run_on_model(const std::shared_ptr<ov::M
3243

3344
auto convert =
3445
std::make_shared<ov::op::v0::Convert>(incoming_output, incoming_output.get_element_type());
35-
convert->set_friendly_name(incoming_node->get_friendly_name() + "_decompressed_to_f32");
46+
auto init_name = incoming_node->get_friendly_name() + "_decompressed_to_f32";
47+
convert->set_friendly_name(generate_uniq_name(init_name));
3648
copy_runtime_info(incoming_node, convert);
3749
input.replace_source_output(convert);
3850
disable_fp16_compression(convert);
@@ -61,7 +73,8 @@ bool ov::pass::AlignMixedFP32FP16Types::run_on_model(const std::shared_ptr<ov::M
6173
// ConvertPrecision(f32 -> f16). It's kept here f32 to keep ov::Model validatable
6274
auto convert = std::make_shared<ov::op::v0::Convert>(output, out_inputs.get_element_type());
6375
copy_runtime_info(node, convert);
64-
convert->set_friendly_name(node->get_friendly_name() + "_compressed_to_f16");
76+
auto init_name = node->get_friendly_name() + "_compressed_to_f16";
77+
convert->set_friendly_name(generate_uniq_name(init_name));
6578
out_inputs.replace_source_output(convert);
6679
pass::disable_constant_folding(convert);
6780
is_changed = true;

src/common/transformations/src/transformations/resolve_names_collisions.cpp

Lines changed: 36 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,27 @@
66

77
#include <algorithm>
88
#include <memory>
9-
#include <numeric>
109

1110
#include "itt.hpp"
12-
#include "openvino/op/parameter.hpp"
13-
#include "openvino/op/result.hpp"
14-
#include "openvino/op/sink.hpp"
11+
#include "openvino/op/util/multi_subgraph_base.hpp"
12+
13+
namespace {
14+
15+
void collect_name_collisions_map(const std::shared_ptr<ov::Model>& model,
16+
std::unordered_map<std::string, std::list<ov::Node*>>& name_collisions_map) {
17+
for (const auto& node : model->get_ordered_ops()) {
18+
// Collect a names collision map for all nodes in the graph
19+
const auto& friendly_name = node->get_friendly_name();
20+
name_collisions_map[friendly_name].emplace_back(node.get());
21+
if (auto msn = ov::as_type_ptr<ov::op::util::MultiSubGraphOp>(node)) {
22+
for (const auto& body : msn->get_functions()) {
23+
collect_name_collisions_map(body, name_collisions_map);
24+
}
25+
}
26+
}
27+
}
28+
29+
} // namespace
1530

1631
bool ov::pass::ResolveNameCollisions::run_on_model(const std::shared_ptr<ov::Model>& model) {
1732
// Next containers are used to fix collisions in autogenerated names
@@ -20,63 +35,28 @@ bool ov::pass::ResolveNameCollisions::run_on_model(const std::shared_ptr<ov::Mod
2035
std::vector<Node*> nodes_with_conflicts;
2136
std::unordered_map<std::string, std::list<Node*>> visited_nodes;
2237

23-
for (const auto& node : model->get_ordered_ops()) {
24-
// Detect names collisions only for nodes with autogenerated names
25-
const auto& friendly_name = node->get_friendly_name();
26-
visited_nodes[friendly_name].emplace_back(node.get());
27-
}
38+
collect_name_collisions_map(model, visited_nodes);
2839

29-
for (const auto& l_nodes : visited_nodes) {
30-
if (l_nodes.second.size() == 1)
40+
for (const auto& it : visited_nodes) {
41+
const auto& same_named_ops = it.second;
42+
if (same_named_ops.size() < 2) {
3143
continue;
32-
const size_t nodes_size = l_nodes.second.size();
33-
bool has_public_node = false; // Parameter, Result ans Sinks
34-
size_t i(0);
35-
for (auto* node : l_nodes.second) {
36-
i++;
37-
// Skip the last node if we don't have public nodes with collisions
38-
if (i == nodes_size && !has_public_node)
39-
break;
40-
if (dynamic_cast<const ov::op::v0::Result*>(node)) {
41-
// Result is a service node
44+
}
45+
46+
int64_t cnt = 2;
47+
for (const auto& op : same_named_ops) {
48+
// check if op has OV autogenerated friendly name. unique and friendly names have to be equal.
49+
bool is_autogenerated = op->m_friendly_name.empty();
50+
if (!is_autogenerated) {
4251
continue;
4352
}
44-
if (dynamic_cast<const ov::op::Sink*>(node) || dynamic_cast<const ov::op::v0::Parameter*>(node)) {
45-
// Resolve names for public ops with autogenerated name
46-
if (node->m_friendly_name.empty())
47-
nodes_with_conflicts.emplace_back(node);
48-
has_public_node = true;
49-
continue;
50-
} else {
51-
// For result we need to avoid changes in previous operation
52-
bool is_public = false;
53-
for (const auto& output : node->outputs()) {
54-
for (const auto input : output.get_target_inputs()) {
55-
if (dynamic_cast<const ov::op::v0::Result*>(input.get_node())) {
56-
has_public_node = true;
57-
is_public = true;
58-
break;
59-
}
60-
}
61-
if (is_public)
62-
break;
63-
}
64-
if (is_public)
65-
continue;
53+
// add a prefix "_counter" to the autogenerated name to make it unique.
54+
auto new_name = op->get_friendly_name() + "_" + std::to_string(cnt++);
55+
while (visited_nodes.find(new_name) != visited_nodes.end()) {
56+
new_name = op->get_friendly_name() + "_" + std::to_string(cnt++);
6657
}
67-
nodes_with_conflicts.emplace_back(node);
58+
op->set_friendly_name(new_name);
6859
}
6960
}
70-
71-
// Resolve names collisions
72-
for (auto* node : nodes_with_conflicts) {
73-
size_t idx = 2;
74-
const auto friendly_name = node->get_friendly_name();
75-
while (visited_nodes.find(friendly_name + "_" + std::to_string(idx)) != visited_nodes.end())
76-
idx++;
77-
const auto new_friendly_name = friendly_name + "_" + std::to_string(idx);
78-
node->set_friendly_name(new_friendly_name);
79-
visited_nodes[new_friendly_name].emplace_back(node);
80-
}
81-
return true;
61+
return false;
8262
}

src/common/transformations/tests/resolve_names_collisions.cpp

Lines changed: 88 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
#include "openvino/opsets/opset8.hpp"
99
#include "openvino/pass/manager.hpp"
1010

11+
using namespace ov;
12+
using namespace ov::opset8;
13+
1114
TEST(ResolveNameCollisionsTest, FixGeneratedNames) {
12-
auto arg0 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 3, 3, 3});
15+
auto arg0 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 3, 3, 3});
1316
const auto gen_friendly_name = arg0->get_friendly_name();
1417

1518
std::string name = "Parameter_";
@@ -19,10 +22,10 @@ TEST(ResolveNameCollisionsTest, FixGeneratedNames) {
1922

2023
arg0->set_friendly_name(name);
2124

22-
auto arg1 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3, 3});
25+
auto arg1 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3, 3});
2326

24-
auto concat = std::make_shared<ov::opset8::Concat>(ov::NodeVector{arg0, arg1}, 1);
25-
auto result1 = std::make_shared<ov::opset8::Result>(concat);
27+
auto concat = std::make_shared<Concat>(ov::NodeVector{arg0, arg1}, 1);
28+
auto result1 = std::make_shared<Result>(concat);
2629

2730
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1}, ov::ParameterVector{arg0, arg1});
2831

@@ -38,50 +41,102 @@ TEST(ResolveNameCollisionsTest, FixGeneratedNames) {
3841
EXPECT_EQ(arg1->get_friendly_name(), arg0->get_friendly_name() + "_2");
3942
}
4043

41-
TEST(ResolveNameCollisionsTest, DoNotFixFriendlyNamesForParameters) {
42-
auto arg0 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 3, 3, 3});
44+
TEST(ResolveNameCollisionsTest, FixFriendlyNamesForAutogeneratedNames) {
45+
auto arg0 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 3, 3, 3});
4346
const auto gen_friendly_name = arg0->get_friendly_name();
4447

45-
arg0->set_friendly_name(gen_friendly_name);
46-
47-
auto arg1 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3, 3});
48+
auto arg1 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3, 3});
49+
// set the same name as for the first Parameter
4850
arg1->set_friendly_name(gen_friendly_name);
4951

50-
auto concat = std::make_shared<ov::opset8::Concat>(ov::NodeVector{arg0, arg1}, 1);
51-
auto result1 = std::make_shared<ov::opset8::Result>(concat);
52+
auto concat1 = std::make_shared<Concat>(ov::NodeVector{arg0, arg1}, 1);
53+
concat1->set_friendly_name("concat");
54+
auto concat = std::make_shared<Concat>(ov::NodeVector{concat1, arg1}, 1);
55+
concat->set_friendly_name("concat");
56+
57+
auto result1 = std::make_shared<Result>(concat);
5258

5359
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1}, ov::ParameterVector{arg0, arg1});
5460

55-
EXPECT_EQ(gen_friendly_name, arg0->get_friendly_name());
56-
EXPECT_EQ(arg1->get_friendly_name(), arg0->get_friendly_name());
57-
EXPECT_NE(arg1->get_friendly_name(), arg0->get_friendly_name() + "_2");
61+
EXPECT_EQ(concat->get_friendly_name(), concat1->get_friendly_name());
5862

5963
ov::pass::Manager pass_manager;
6064
pass_manager.register_pass<ov::pass::ResolveNameCollisions>();
6165
pass_manager.run_passes(model);
62-
EXPECT_EQ(gen_friendly_name, arg0->get_friendly_name());
63-
EXPECT_EQ(arg1->get_friendly_name(), arg0->get_friendly_name());
64-
EXPECT_NE(arg1->get_friendly_name(), arg0->get_friendly_name() + "_2");
65-
}
66-
67-
TEST(ResolveNameCollisionsTest, FixFriendlyNamesForInternalOperations) {
68-
auto arg0 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 3, 3, 3});
69-
const auto gen_friendly_name = arg0->get_friendly_name();
70-
71-
auto arg1 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 2, 3, 3});
72-
73-
auto concat1 = std::make_shared<ov::opset8::Concat>(ov::NodeVector{arg0, arg1}, 1);
74-
concat1->set_friendly_name("concat");
75-
auto concat = std::make_shared<ov::opset8::Concat>(ov::NodeVector{concat1, arg1}, 1);
76-
concat->set_friendly_name("concat");
77-
auto result1 = std::make_shared<ov::opset8::Result>(concat);
78-
79-
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1}, ov::ParameterVector{arg0, arg1});
8066

67+
// these names weren't set automatically, and have to remain the same.
8168
EXPECT_EQ(concat->get_friendly_name(), concat1->get_friendly_name());
69+
// arg0's name was set automatically and matches with another name in the graph,
70+
// so it have to be changed.
71+
EXPECT_NE(arg0->get_friendly_name(), arg1->get_friendly_name());
72+
EXPECT_EQ(arg0->get_friendly_name(), arg1->get_friendly_name() + "_2");
73+
}
74+
75+
TEST(ResolveNameCollisionsTest, FixFriendlyNamesForAutogeneratedNamesMultiSubgraphOp) {
76+
// external params
77+
auto X = std::make_shared<Parameter>(element::f32, Shape{4});
78+
auto Y = std::make_shared<Parameter>(element::f32, Shape{4});
79+
auto Z = std::make_shared<Parameter>(element::f32, Shape{8});
80+
81+
auto axis = std::make_shared<Constant>(element::i32, Shape{}, 0);
82+
auto external_split = std::make_shared<Split>(X, axis, 2);
83+
84+
// internal params
85+
auto Xt = std::make_shared<Parameter>(element::f32, PartialShape::dynamic());
86+
Xt->set_friendly_name(X->get_friendly_name());
87+
auto Yt = std::make_shared<Parameter>(element::f32, PartialShape::dynamic());
88+
Yt->set_friendly_name(Y->get_friendly_name());
89+
auto Ze = std::make_shared<Parameter>(element::f32, PartialShape::dynamic());
90+
91+
// then body
92+
auto cond = std::make_shared<Constant>(element::boolean, Shape{1}, true);
93+
auto axis_then = std::make_shared<Constant>(element::i32, Shape{}, 0);
94+
auto split_y = std::make_shared<Split>(Yt, axis_then, 2);
95+
split_y->set_friendly_name(external_split->get_friendly_name());
96+
auto then_op = std::make_shared<Subtract>(Xt, split_y->output(0));
97+
auto res0 = std::make_shared<Result>(then_op);
98+
99+
// else body
100+
auto axis_else = std::make_shared<Constant>(element::i32, Shape{}, 0);
101+
auto split_z = std::make_shared<Split>(Ze, axis_else, 4);
102+
split_z->set_friendly_name(external_split->get_friendly_name());
103+
auto else_op = std::make_shared<Relu>(split_z);
104+
else_op->set_friendly_name(then_op->get_friendly_name());
105+
auto res1 = std::make_shared<Result>(else_op);
106+
107+
// If set up
108+
auto then_body = std::make_shared<ov::Model>(OutputVector{res0}, ParameterVector{Yt, Xt}, "then_body");
109+
auto else_body = std::make_shared<ov::Model>(OutputVector{res1}, ParameterVector{Ze}, "else_body");
110+
auto if_op = std::make_shared<op::v8::If>(cond);
111+
if_op->set_then_body(then_body);
112+
if_op->set_else_body(else_body);
113+
if_op->set_input(external_split->output(0), Xt, nullptr);
114+
if_op->set_input(Y, Yt, nullptr);
115+
if_op->set_input(Z, nullptr, Ze);
116+
auto result = if_op->set_output(res0, res1);
117+
118+
auto res = std::make_shared<Result>(result);
119+
auto model = std::make_shared<Model>(OutputVector{res}, ParameterVector{X, Y, Z});
120+
121+
EXPECT_EQ(external_split->get_friendly_name(), split_y->get_friendly_name());
122+
EXPECT_EQ(external_split->get_friendly_name(), split_z->get_friendly_name());
123+
124+
EXPECT_EQ(X->get_friendly_name(), Xt->get_friendly_name());
125+
EXPECT_EQ(Y->get_friendly_name(), Yt->get_friendly_name());
126+
127+
EXPECT_EQ(then_op->get_friendly_name(), else_op->get_friendly_name());
82128

83129
ov::pass::Manager pass_manager;
84130
pass_manager.register_pass<ov::pass::ResolveNameCollisions>();
85131
pass_manager.run_passes(model);
86-
EXPECT_NE(concat->get_friendly_name(), concat1->get_friendly_name());
132+
133+
EXPECT_EQ(external_split->get_friendly_name(), split_y->get_friendly_name() + "_2");
134+
135+
EXPECT_EQ(X->get_friendly_name(), Xt->get_friendly_name() + "_2");
136+
EXPECT_EQ(Y->get_friendly_name(), Yt->get_friendly_name() + "_2");
137+
138+
EXPECT_EQ(then_op->get_friendly_name(), else_op->get_friendly_name() + "_2");
139+
// remain the same, because they were set via "set_friendly_name" method
140+
// and are not autogenerated.
141+
EXPECT_EQ(split_y->get_friendly_name(), split_z->get_friendly_name());
87142
}

0 commit comments

Comments
 (0)