1 //*****************************************************************************
2 // Copyright 2017-2020 Intel Corporation
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //*****************************************************************************
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
25 #include "ngraph/builder/norm.hpp"
26 #include "ngraph/graph_util.hpp"
27 #include "ngraph/ngraph.hpp"
28 #include "ngraph/pass/manager.hpp"
29 #include "ngraph/provenance.hpp"
30 #include "pass/fused_op_decomposition.hpp"
31 #include "pass/opset0_downgrade.hpp"
32 #include "pass/opset1_upgrade.hpp"
33 #include "util/provenance_enabler.hpp"
36 using namespace ngraph;
37 using ::testing::Return;
39 NGRAPH_SUPPRESS_DEPRECATED_START
41 using ProvSet = std::unordered_set<std::string>;
43 TEST(provenance, provenance)
45 test::ProvenanceEnabler provenance_enabler;
67 // * D is the replacement root, and its insertion kills C. We should not, however, consider
68 // A and B to be killed, because they are not post-dominated by D until after C is cut out
72 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
73 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
75 auto a = make_shared<op::Add>(x, y);
76 a->add_provenance_tag("tag_a");
77 auto b = make_shared<op::Multiply>(y, x);
78 b->add_provenance_tag("tag_b");
79 auto c = make_shared<op::Subtract>(a, b);
80 c->add_provenance_tag("tag_c");
82 auto f = make_shared<Function>(c, ParameterVector{x, y});
84 auto new_c = make_shared<op::Subtract>(a, b);
85 replace_node(c, new_c);
87 EXPECT_EQ(new_c->get_provenance_tags(), ProvSet{"tag_c"});
112 // * D is the replacement root, and its insertion kills C. We should not, however, consider
113 // A and B to be killed, because they are not post-dominated by D until after C is cut out
117 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
118 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
120 auto a = make_shared<op::Add>(x, y);
121 a->add_provenance_tag("tag_a");
122 auto b = make_shared<op::Multiply>(y, x);
123 b->add_provenance_tag("tag_b");
124 auto c = make_shared<op::Subtract>(a, b);
125 c->add_provenance_tag("tag_c");
127 auto f = make_shared<Function>(c, ParameterVector{x, y});
129 auto d = make_shared<op::Subtract>(a, b);
130 d->add_provenance_tag("tag_d");
133 EXPECT_EQ(d->get_provenance_tags(), (ProvSet{"tag_c", "tag_d"}));
149 // D{tag_a,tag_b,tag_c,tag_d}
152 // * D is the replacement root, and its insertion kills A, B, and C.
155 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
156 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
158 auto a = make_shared<op::Add>(x, y);
159 a->add_provenance_tag("tag_a");
160 auto b = make_shared<op::Multiply>(y, x);
161 b->add_provenance_tag("tag_b");
162 auto c = make_shared<op::Subtract>(a, b);
163 c->add_provenance_tag("tag_c");
165 auto f = make_shared<Function>(c, ParameterVector{x, y});
167 auto d = make_zero(element::i32, Shape{2, 3, 4});
168 d->add_provenance_tag("tag_d");
171 EXPECT_EQ(d->get_provenance_tags(), (ProvSet{"tag_a", "tag_b", "tag_c", "tag_d"}));
187 // D{tag_a,tag_b,tag_c}
190 // * D is the replacement root, and its insertion kills A, B, and C.
193 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
194 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
196 auto a = make_shared<op::Add>(x, y);
197 a->add_provenance_tag("tag_a");
198 auto b = make_shared<op::Multiply>(y, x);
199 b->add_provenance_tag("tag_b");
200 auto c = make_shared<op::Subtract>(a, b);
201 c->add_provenance_tag("tag_c");
203 auto f = make_shared<Function>(c, ParameterVector{x, y});
205 auto d = make_zero(element::i32, Shape{2, 3, 4});
208 EXPECT_EQ(d->get_provenance_tags(), (ProvSet{"tag_a", "tag_b", "tag_c"}));
237 // * D is the replacement root replacing C and creating a new argument node E
240 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
241 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
243 auto a = make_shared<op::Add>(x, y);
244 a->add_provenance_tag("tag_a");
245 auto b = make_shared<op::Multiply>(y, x);
246 b->add_provenance_tag("tag_b");
247 auto c = make_shared<op::Subtract>(a, b);
248 c->add_provenance_tag("tag_c");
250 auto f = make_shared<Function>(c, ParameterVector{x, y});
252 auto e = make_shared<op::Subtract>(a, x);
253 auto d = make_shared<op::Subtract>(e, b);
254 d->add_provenance_tag("tag_d");
258 EXPECT_EQ(d->get_provenance_tags(), (ProvSet{"tag_c", "tag_d"}));
259 EXPECT_EQ(e->get_provenance_tags(), (ProvSet{"tag_c"}));
283 // E{tag_c, tag_d, tag_e} /
288 // * D is the replacement root replacing C and creating a new argument node E
291 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
292 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
294 auto a = make_shared<op::Add>(x, y);
295 a->add_provenance_tag("tag_a");
296 auto b = make_shared<op::Multiply>(y, x);
297 b->add_provenance_tag("tag_b");
298 auto c = make_shared<op::Subtract>(a, b);
299 c->add_provenance_tag("tag_c");
301 auto f = make_shared<Function>(c, ParameterVector{x, y});
303 auto e = make_shared<op::Subtract>(a, x);
304 e->add_provenance_tag("tag_e");
305 auto d = make_shared<op::Subtract>(e, b);
306 d->add_provenance_tag("tag_d");
310 EXPECT_EQ(d->get_provenance_tags(), (ProvSet{"tag_c", "tag_d"}));
311 EXPECT_EQ(e->get_provenance_tags(), (ProvSet{"tag_c", "tag_e"}));
315 TEST(provenance, add_group_above)
317 auto p1 = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
318 p1->add_provenance_tag("P1");
319 auto p2 = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
320 p2->add_provenance_tag("P2");
322 auto m1 = (a1 * a1)->add_provenance_group_members_above({p1, p2});
323 m1->add_provenance_tag("m1");
324 EXPECT_EQ(p1->get_provenance_tags(), (ProvSet{"P1"}));
325 EXPECT_EQ(p2->get_provenance_tags(), (ProvSet{"P2"}));
326 EXPECT_EQ(a1->get_provenance_tags(), (ProvSet{"m1"}));
327 EXPECT_EQ(m1->get_provenance_tags(), (ProvSet{"m1"}));
330 TEST(provenance, add_tags_above)
332 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
333 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
335 auto a = make_shared<op::Add>(x, y);
336 auto b = make_shared<op::Multiply>(x, y);
337 auto c = make_shared<op::Subtract>(a, b);
338 auto d = make_shared<op::Abs>(c);
340 // Add tags to Subtract and all nodes until Parameters (all above c, until params x, y)
341 c->add_provenance_tags_above(OutputVector{x, y}, {"tag_above_c - until_params"});
342 // Add tags to Abs and Subtract (above d, until c inputs)
343 d->add_provenance_tags_above(c->input_values(), {"tag_above_d - until_c_inputs"});
344 // Add tags to Abs and all nodes above
345 d->add_provenance_tags_above(OutputVector{}, {"tag_all_above_d"});
347 auto x_tags = x->get_provenance_tags();
348 EXPECT_EQ(x_tags.size(), 1);
349 EXPECT_TRUE(x_tags.find("tag_all_above_d") != x_tags.end());
351 auto y_tags = y->get_provenance_tags();
352 EXPECT_EQ(y_tags.size(), 1);
353 EXPECT_TRUE(y_tags.find("tag_all_above_d") != y_tags.end());
355 auto a_tags = a->get_provenance_tags();
356 EXPECT_EQ(a_tags.size(), 2);
357 EXPECT_TRUE(a_tags.find("tag_above_c - until_params") != a_tags.end());
358 EXPECT_FALSE(a_tags.find("tag_above_d - until_c_inputs") != a_tags.end());
359 EXPECT_TRUE(a_tags.find("tag_all_above_d") != a_tags.end());
361 auto b_tags = b->get_provenance_tags();
362 EXPECT_EQ(b_tags.size(), 2);
363 EXPECT_TRUE(b_tags.find("tag_above_c - until_params") != b_tags.end());
364 EXPECT_FALSE(b_tags.find("tag_above_d - until_c_inputs") != b_tags.end());
365 EXPECT_TRUE(b_tags.find("tag_all_above_d") != b_tags.end());
367 auto c_tags = c->get_provenance_tags();
368 EXPECT_EQ(c_tags.size(), 3);
369 EXPECT_TRUE(c_tags.find("tag_above_c - until_params") != c_tags.end());
370 EXPECT_TRUE(c_tags.find("tag_above_d - until_c_inputs") != c_tags.end());
371 EXPECT_TRUE(c_tags.find("tag_all_above_d") != c_tags.end());
373 auto d_tags = d->get_provenance_tags();
374 EXPECT_EQ(d_tags.size(), 2);
375 EXPECT_FALSE(d_tags.find("tag_above_c - until_params") != d_tags.end());
376 EXPECT_TRUE(d_tags.find("tag_above_d - until_c_inputs") != d_tags.end());
377 EXPECT_TRUE(d_tags.find("tag_all_above_d") != d_tags.end());
380 TEST(provenance, builder)
382 auto p1 = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
383 p1->add_provenance_tag("P1");
384 auto norm = builder::opset1::lp_norm(p1, op::Constant::create(element::i64, {}, {0}), 1, 0);
385 norm->add_provenance_tag("norm");
386 for (auto node : topological_sort(NodeVector{norm}))
390 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"P1"}));
394 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"norm"}));
399 TEST(provenance, fused_copy_origin_tags)
401 test::ProvenanceEnabler provenance_enabler;
403 auto p1 = make_shared<op::Parameter>(element::f32, PartialShape{2, 3, 4});
404 p1->add_provenance_tag("P1");
405 auto g = make_shared<op::Gelu>(p1);
406 g->add_provenance_tag("G");
407 auto r = make_shared<op::Result>(g);
408 auto f = make_shared<Function>(ResultVector{r}, ParameterVector{p1});
410 pass::Manager manager;
411 manager.register_pass<pass::FusedOpDecomposition>();
412 manager.run_passes(f);
414 traverse_nodes(f, [&](const std::shared_ptr<Node>& node) {
415 auto tags = node->get_provenance_tags();
418 EXPECT_EQ(tags.size(), 1);
419 EXPECT_TRUE(tags.find("P1") != tags.end());
426 EXPECT_TRUE(tags.find("G") != tags.end());
427 EXPECT_TRUE(tags.find("<Decomposed from Gelu>") != tags.end());
432 TEST(provenance, fused_decomposition_tag)
434 test::ProvenanceEnabler provenance_enabler;
436 auto p1 = make_shared<op::Parameter>(element::f32, PartialShape{2, 3, 4});
437 auto fused_op = make_shared<op::MVN>(p1);
438 auto result = make_shared<op::Result>(fused_op);
439 auto f = make_shared<Function>(ResultVector{result}, ParameterVector{p1});
441 pass::Manager manager;
442 manager.register_pass<pass::FusedOpDecomposition>();
443 manager.run_passes(f);
445 const auto tag = "<Decomposed from MVN>";
446 auto tag_check = [&tag](std::shared_ptr<ngraph::Node> node) {
447 auto tags = node->get_provenance_tags();
448 EXPECT_TRUE(tags.find(tag) != tags.end());
450 const auto decomposed_op = f->get_result()->get_input_node_shared_ptr(0);
451 traverse_nodes(as_node_vector(decomposed_op->outputs()), tag_check, {p1});
454 TEST(provenance, topk_setk)
456 auto p1 = make_shared<op::Parameter>(element::f32, PartialShape{20, 3, 4});
457 p1->add_provenance_tag("P1");
458 auto tk = make_shared<op::TopK>(p1, 0, element::i32, 10);
459 tk->add_provenance_tag("TK");
460 auto tkc0 = tk->input_value(1).get_node_shared_ptr();
461 tkc0->add_provenance_tag("TKC0");
462 for (auto node : topological_sort(NodeVector{tk}))
466 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"P1"}));
468 else if (node == tkc0)
470 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"TK", "TKC0"}));
474 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"TK"}));
478 auto tkc1 = tk->input_value(1).get_node_shared_ptr();
479 tkc1->add_provenance_tag("TKC1");
480 for (auto node : topological_sort(NodeVector{tk}))
484 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"P1"}));
486 else if (node == tkc1)
488 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"TK", "TKC0", "TKC1"}));
492 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"TK"}));
497 TEST(provenance, empty_group)
499 auto p1 = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
500 p1->add_provenance_tag("P1");
501 auto abs = make_shared<op::Abs>(p1);
502 // Make sure group is empty
503 abs->add_provenance_group_members_above({abs});
504 abs->add_provenance_tag("abs");
505 for (auto node : topological_sort(NodeVector{abs}))
509 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"P1"}));
513 EXPECT_EQ(node->get_provenance_tags(), (ProvSet{"abs"}));
518 TEST(provenance, opset1_upgrade_pass_topk)
520 test::ProvenanceEnabler provenance_enabler;
522 const size_t axis = 2;
524 const auto data = make_shared<op::Parameter>(element::i32, Shape{5, 10, 15});
526 const auto topk_v0 = make_shared<op::v0::TopK>(data, axis, element::i32, k);
527 const auto result = make_shared<op::Result>(topk_v0->output(0));
528 auto f = make_shared<Function>(ResultVector{result}, ParameterVector{data});
530 ngraph::pass::Manager pass_manager;
531 pass_manager.register_pass<pass::Opset1Upgrade>();
532 pass_manager.run_passes(f);
534 const auto pass_replacement_node = f->get_result()->get_input_node_shared_ptr(0);
535 const auto topk_v1 = as_type_ptr<op::v1::TopK>(pass_replacement_node);
537 const std::string tag = "<Opset1_Upgrade (v0 TopK)>";
538 auto tag_check = [&tag](std::shared_ptr<ngraph::Node> node) {
539 auto tags = node->get_provenance_tags();
540 EXPECT_TRUE(tags.find(tag) != tags.end());
542 traverse_nodes({topk_v1}, tag_check, as_node_vector(topk_v0->input_values()));
545 TEST(provenance, opset0_downgrade_pass_topk)
547 test::ProvenanceEnabler provenance_enabler;
549 const auto data = make_shared<op::Parameter>(element::i32, Shape{5, 10, 15});
550 const int32_t k = 10;
551 const auto k_node = op::Constant::create(element::i64, Shape{}, {k});
552 const size_t axis = 2;
553 const auto mode = op::v1::TopK::Mode::MAX;
554 const auto sort = op::v1::TopK::SortType::SORT_INDICES;
555 const auto elem_type = element::i64;
557 const auto topk_v1 = make_shared<op::v1::TopK>(data, k_node, axis, mode, sort, elem_type);
558 const auto result = make_shared<op::Result>(topk_v1->output(0));
559 auto f = make_shared<Function>(ResultVector{result}, ParameterVector{data});
561 ngraph::pass::Manager pass_manager;
562 pass_manager.register_pass<pass::Opset0Downgrade>();
563 pass_manager.run_passes(f);
565 const auto pass_replacement_node = f->get_result()->get_input_node_shared_ptr(0);
566 const auto topk_v0 = as_type_ptr<op::v0::TopK>(pass_replacement_node);
568 const std::string tag = "<Opset0_Downgrade (v1 TopK)>";
569 auto tag_check = [&tag](std::shared_ptr<ngraph::Node> node) {
570 auto tags = node->get_provenance_tags();
571 EXPECT_TRUE(tags.find(tag) != tags.end());
573 traverse_nodes({topk_v0}, tag_check, as_node_vector(topk_v1->input_values()));
576 TEST(provenance, opset1_upgrade_pass_graph)
578 test::ProvenanceEnabler provenance_enabler;
580 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
581 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
583 auto a = make_shared<op::v0::Add>(x, y);
584 auto b = make_shared<op::v0::Subtract>(x, y);
585 auto c = make_shared<op::v0::Abs>(b);
586 auto d = make_shared<op::v0::Multiply>(a, b);
588 auto f = make_shared<Function>(d, ParameterVector{x, y});
590 ngraph::pass::Manager pass_manager;
591 pass_manager.register_pass<pass::Opset1Upgrade>();
592 pass_manager.run_passes(f);
594 for (auto node : f->get_ordered_ops())
596 auto tags = node->get_provenance_tags();
597 if (as_type_ptr<op::v1::Add>(node))
599 EXPECT_EQ(tags.size(), 1);
600 EXPECT_TRUE(tags.find("<Opset1_Upgrade (v0 Add)>") != tags.end());
602 else if (as_type_ptr<op::v1::Multiply>(node))
604 EXPECT_EQ(tags.size(), 1);
605 EXPECT_TRUE(tags.find("<Opset1_Upgrade (v0 Multiply)>") != tags.end());
607 else if (as_type_ptr<op::v1::Subtract>(node))
609 EXPECT_EQ(tags.size(), 1);
610 EXPECT_TRUE(tags.find("<Opset1_Upgrade (v0 Subtract)>") != tags.end());
612 else if (as_type_ptr<op::v0::Abs>(node))
614 EXPECT_TRUE(tags.empty());
619 TEST(provenance, opset0_downgrade_pass_graph)
621 test::ProvenanceEnabler provenance_enabler;
623 auto x = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
624 auto y = make_shared<op::Parameter>(element::i32, PartialShape{2, 3, 4});
626 auto a = make_shared<op::v1::Add>(x, y);
627 auto b = make_shared<op::v1::Subtract>(x, y);
628 auto c = make_shared<op::v0::Abs>(b);
629 auto d = make_shared<op::v1::Multiply>(a, b);
631 auto f = make_shared<Function>(d, ParameterVector{x, y});
633 ngraph::pass::Manager pass_manager;
634 pass_manager.register_pass<pass::Opset0Downgrade>();
635 pass_manager.run_passes(f);
637 for (auto node : f->get_ordered_ops())
639 auto tags = node->get_provenance_tags();
640 if (as_type_ptr<op::v0::Add>(node))
642 EXPECT_EQ(tags.size(), 1);
643 EXPECT_TRUE(tags.find("<Opset0_Downgrade (v1 Add)>") != tags.end());
645 else if (as_type_ptr<op::v0::Multiply>(node))
647 EXPECT_EQ(tags.size(), 1);
648 EXPECT_TRUE(tags.find("<Opset0_Downgrade (v1 Multiply)>") != tags.end());
650 else if (as_type_ptr<op::v0::Subtract>(node))
652 EXPECT_EQ(tags.size(), 1);
653 EXPECT_TRUE(tags.find("<Opset0_Downgrade (v1 Subtract)>") != tags.end());
655 else if (as_type_ptr<op::v0::Abs>(node))
657 EXPECT_TRUE(tags.empty());