[DOCS] Transformations python api docs for 24.2 (#24949)
Porting: https://github.com/openvinotoolkit/openvino/pull/24121
This commit is contained in:
parent
c2b15154b4
commit
23fc29a6db
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b78713020891a6655e3860338b7db430840560d8f2860162eca3d00b84533f24
|
||||
size 36362
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:353c5b7f35a8f9f6c2c3074485a5a4e51f8c6ee776214444666da4037cc44832
|
||||
size 45347
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:696672a8c38260147f2fa68d266866ab4390ef919fa092cea1149b0eb529c68e
|
||||
size 58557
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d378718b968f6a40555b31bf54af83fac978c3bfc07e234f9b1370229222cccd
|
||||
size 31092
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6b3511899521a49c1bfdc569e16b8ad1e12157fe107d2b86d25ee04ff67a85b2
|
||||
size 43066
|
||||
|
|
@ -171,8 +171,8 @@ if (m->match(node->output(0))) {
|
|||
bool openvino_api_examples(std::shared_ptr<ov::Node> node) {
|
||||
{
|
||||
// ! [ov:ports_example]
|
||||
// Let's suppose that node is opset8::Convolution operation
|
||||
// as we know opset8::Convolution has two input ports (data, weights) and one output port
|
||||
// Let's suppose that node is of ov::op::v0::Convolution type.
|
||||
// As we know ov::op::v0::Convolution has two input ports (data, weights) and one output port.
|
||||
ov::Input<ov::Node> data = node->input(0);
|
||||
ov::Input<ov::Node> weights = node->input(1);
|
||||
ov::Output<ov::Node> output = node->output(0);
|
||||
|
|
@ -181,7 +181,7 @@ ov::Output<ov::Node> output = node->output(0);
|
|||
auto pshape = data.get_partial_shape();
|
||||
auto el_type = data.get_element_type();
|
||||
|
||||
// Getting parent for input port
|
||||
// Getting parent for input port i.e. Output mapped by the input
|
||||
ov::Output<ov::Node> parent_output;
|
||||
parent_output = data.get_source_output();
|
||||
|
||||
|
|
@ -216,15 +216,15 @@ return true;
|
|||
|
||||
// ! [ov:replace_node]
|
||||
bool ov_replace_node(std::shared_ptr<ov::Node> node) {
|
||||
// Step 1. Verify that node has opset8::Negative type
|
||||
auto neg = std::dynamic_pointer_cast<ov::opset8::Negative>(node);
|
||||
// Step 1. Verify that node is of type ov::op::v0::Negative
|
||||
auto neg = std::dynamic_pointer_cast<ov::op::v0::Negative>(node);
|
||||
if (!neg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2. Create opset8::Multiply operation where the first input is negative operation input and second as Constant with -1 value
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(neg->input_value(0),
|
||||
ov::opset8::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
// Step 2. Create ov::op::v1::Multiply operation with the first input being the output going into Negative and second as Constant with -1 value
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(neg->input_value(0),
|
||||
ov::op::v0::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
|
||||
mul->set_friendly_name(neg->get_friendly_name());
|
||||
ov::copy_runtime_info(neg, mul);
|
||||
|
|
@ -233,18 +233,18 @@ bool ov_replace_node(std::shared_ptr<ov::Node> node) {
|
|||
ov::replace_node(neg, mul);
|
||||
return true;
|
||||
|
||||
// Step 4. Negative operation will be removed automatically because all consumers was moved to Multiply operation
|
||||
// Step 4. Negative operation will be removed automatically because all consumers were moved to Multiply operation
|
||||
}
|
||||
// ! [ov:replace_node]
|
||||
|
||||
bool ov_manual_replace_node(std::shared_ptr<ov::Node> node) {
|
||||
auto neg = std::dynamic_pointer_cast<ov::opset8::Negative>(node);
|
||||
auto neg = std::dynamic_pointer_cast<ov::op::v0::Negative>(node);
|
||||
if (!neg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(neg->input_value(0),
|
||||
ov::opset8::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(neg->input_value(0),
|
||||
ov::op::v0::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1}));
|
||||
|
||||
mul->set_friendly_name(neg->get_friendly_name());
|
||||
ov::copy_runtime_info(neg, mul);
|
||||
|
|
@ -262,8 +262,8 @@ void insert_example(std::shared_ptr<ov::Node> node) {
|
|||
// Get all consumers for node
|
||||
auto consumers = node->output(0).get_target_inputs();
|
||||
|
||||
// Step 2. Create new node. Let it be opset8::Relu.
|
||||
auto new_node = std::make_shared<ov::opset8::Relu>(node);
|
||||
// Step 2. Create new node ov::op::v0::Relu.
|
||||
auto new_node = std::make_shared<ov::op::v0::Relu>(node);
|
||||
|
||||
// Step 3. Reconnect all consumers to new_node
|
||||
for (auto input : consumers) {
|
||||
|
|
@ -277,7 +277,7 @@ void insert_example_with_copy(std::shared_ptr<ov::Node> node) {
|
|||
// Make a node copy
|
||||
auto node_copy = node->clone_with_new_inputs(node->input_values());
|
||||
// Create new node
|
||||
auto new_node = std::make_shared<ov::opset8::Relu>(node_copy);
|
||||
auto new_node = std::make_shared<ov::op::v0::Relu>(node_copy);
|
||||
ov::replace_node(node, new_node);
|
||||
}
|
||||
// ! [ov:insert_node_with_copy]
|
||||
|
|
@ -290,12 +290,12 @@ bool success = ov::replace_output_update_name(node->output(0), node->input_value
|
|||
}
|
||||
|
||||
void replace_friendly_name() {
|
||||
auto div = std::make_shared<ov::opset8::Divide>();
|
||||
auto div = std::make_shared<ov::op::v1::Divide>();
|
||||
// ! [ov:replace_friendly_name]
|
||||
// Replace Div operation with Power and Multiply sub-graph and set original friendly name to Multiply operation
|
||||
auto pow = std::make_shared<ov::opset8::Power>(div->input(1).get_source_output(),
|
||||
auto pow = std::make_shared<ov::op::v1::Power>(div->input(1).get_source_output(),
|
||||
ov::op::v0::Constant::create(div->get_input_element_type(1), ov::Shape{1}, {-1}));
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(div->input(0).get_source_output(), pow);
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(div->input(0).get_source_output(), pow);
|
||||
mul->set_friendly_name(div->get_friendly_name());
|
||||
ov::replace_node(div, mul);
|
||||
// ! [ov:replace_friendly_name]
|
||||
|
|
@ -304,10 +304,10 @@ ov::replace_node(div, mul);
|
|||
void constant_subgraph() {
|
||||
// ! [ov:constant_subgraph]
|
||||
// After ConstantFolding pass Power will be replaced with Constant
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::Shape{1});
|
||||
auto pow = std::make_shared<ov::opset8::Power>(ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {2}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {3}));
|
||||
auto mul = std::make_shared<ov::opset8::Multiply>(input /* not constant input */, pow);
|
||||
auto input = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1});
|
||||
auto pow = std::make_shared<ov::op::v1::Power>(ov::op::v0::Constant::create(ov::element::f32, ov::Shape{1}, {2}),
|
||||
ov::op::v0::Constant::create(ov::element::f32, ov::Shape{1}, {3}));
|
||||
auto mul = std::make_shared<ov::op::v1::Multiply>(input /* not constant input */, pow);
|
||||
// ! [ov:constant_subgraph]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// ! [ov:imports]
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common_test_utils/matcher.hpp"
|
||||
#include "openvino/op/abs.hpp"
|
||||
#include "openvino/op/add.hpp"
|
||||
#include "openvino/op/matmul.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/op/relu.hpp"
|
||||
#include "openvino/op/sigmoid.hpp"
|
||||
#include "openvino/pass/pattern/op/optional.hpp"
|
||||
#include "openvino/pass/pattern/op/or.hpp"
|
||||
#include "openvino/pass/pattern/op/wrap_type.hpp"
|
||||
#include "transformations/utils/utils.hpp"
|
||||
|
||||
using namespace ov;
|
||||
using namespace ov::pass;
|
||||
using namespace std;
|
||||
// ! [ov:imports]
|
||||
|
||||
// ! [ov:create_simple_model_and_pattern]
|
||||
TEST(pattern, simple_model_and_pattern) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = std::make_shared<ov::op::v0::MatMul>(pattern::any_input(), pattern::any_input(), false, false);
|
||||
auto pattern_abs = std::make_shared<ov::op::v0::Abs>(pattern_mul->output(0));
|
||||
auto pattern_relu = std::make_shared<ov::op::v0::Relu>(pattern_abs->output(0));
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:create_simple_model_and_pattern]
|
||||
|
||||
|
||||
// ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
TEST(pattern, simple_model_and_pattern_wrap_type) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
|
||||
// ! [ov:wrap_type_list]
|
||||
TEST(pattern, wrap_type_list) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
auto model_sig = std::make_shared<ov::op::v0::Sigmoid>(model_abs->output(0));
|
||||
auto model_result1 = std::make_shared<ov::op::v0::Result>(model_sig->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu, ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_sig));
|
||||
}
|
||||
// ! [ov:wrap_type_list]
|
||||
|
||||
void patterns_misc() {
|
||||
// ! [ov:any_input]
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
// ! [ov:any_input]
|
||||
|
||||
// ! [ov:wrap_type_predicate]
|
||||
ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern::any_input()}, pattern::consumers_count(2));
|
||||
// ! [ov:wrap_type_predicate]
|
||||
|
||||
|
||||
// ! [ov:any_input_predicate]
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input([](const Output<Node>& value){
|
||||
return value.get_shape().size() == 4;}),
|
||||
pattern::any_input([](const Output<Node>& value){
|
||||
return value.get_shape().size() == 4;})});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
// ! [ov:any_input_predicate]
|
||||
|
||||
|
||||
// ! [ov:optional_predicate]
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu, pattern::consumers_count(2));
|
||||
// ! [ov:optional_predicate]
|
||||
}
|
||||
|
||||
|
||||
// ! [ov:pattern_or]
|
||||
TEST(pattern, pattern_or) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a red branch
|
||||
auto red_pattern_add = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto red_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({red_pattern_add->output(0)});
|
||||
auto red_pattern_sigmoid = ov::pass::pattern::wrap_type<ov::op::v0::Sigmoid>({red_pattern_relu->output(0)});
|
||||
|
||||
// Create a blue branch
|
||||
auto blue_pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto blue_pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({blue_pattern_mul->output(0)});
|
||||
auto blue_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({blue_pattern_abs->output(0)});
|
||||
|
||||
// Create Or node
|
||||
auto pattern_or = std::make_shared<ov::pass::pattern::op::Or>(OutputVector{red_pattern_sigmoid->output(0), blue_pattern_relu->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_or, model_relu));
|
||||
}
|
||||
// ! [ov:pattern_or]
|
||||
|
||||
|
||||
// ! [ov:pattern_optional_middle]
|
||||
TEST(pattern, pattern_optional_middle) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern with an Optional node in the middle
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_sig_opt->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:pattern_optional_middle]
|
||||
|
||||
|
||||
// ! [ov:pattern_optional_top]
|
||||
TEST(pattern, pattern_optional_top) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern::any_input());
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern_sig_opt, pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:pattern_optional_top]
|
||||
|
||||
|
||||
// ! [ov:pattern_optional_root]
|
||||
TEST(pattern, pattern_optional_root) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu);
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
// ! [ov:pattern_optional_root]
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
# Copyright (C) 2018-2024 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# ! [ov:imports]
|
||||
import pytest
|
||||
from openvino import PartialShape
|
||||
from openvino.runtime import opset13 as ops
|
||||
from openvino.runtime.passes import Matcher, WrapType, Or, AnyInput, Optional
|
||||
# ! [ov:imports]
|
||||
from openvino.runtime.passes import (
|
||||
consumers_count,
|
||||
has_static_dim,
|
||||
has_static_dims,
|
||||
has_static_shape,
|
||||
has_static_rank,
|
||||
type_matches,
|
||||
type_matches_any,
|
||||
)
|
||||
|
||||
# ! [ov:create_simple_model_and_pattern]
|
||||
def simple_model_and_pattern():
|
||||
# Create a sample model
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = ops.matmul(AnyInput(), AnyInput(), False, False)
|
||||
pattern_abs = ops.abs(pattern_mul)
|
||||
pattern_relu = ops.relu(pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:create_simple_model_and_pattern]
|
||||
|
||||
# ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
def simple_model_and_pattern_wrap_type():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
# ! [ov:wrap_type_list]
|
||||
def wrap_type_list():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
model_sig = ops.sigmoid(model_abs) # Note that we've added a Sigmoid node after Abs
|
||||
model_result1 = ops.result(model_sig)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType(["opset13.Relu", "opset13.Sigmoid"], pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
assert matcher.match(model_sig)
|
||||
# ! [ov:wrap_type_list]
|
||||
|
||||
def any_input():
|
||||
# ! [ov:any_input]
|
||||
# Create a pattern with a MatMul node taking any inputs.
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
# ! [ov:any_input]
|
||||
|
||||
def wrap_type_predicate():
|
||||
# ! [ov:wrap_type_predicate]
|
||||
WrapType("opset13.Relu", AnyInput(), consumers_count(2))
|
||||
# ! [ov:wrap_type_predicate]
|
||||
|
||||
# ! [ov:any_input_predicate]
|
||||
# Create a pattern with an MatMul node taking any input that has a rank 4.
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(lambda output: len(output.get_shape()) == 4), AnyInput(lambda output: len(output.get_shape()) == 4)])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
# ! [ov:any_input_predicate]
|
||||
|
||||
# ! [ov:pattern_or]
|
||||
def pattern_or():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a red branch
|
||||
red_pattern_add = WrapType("opset13.Add", [AnyInput(), AnyInput()])
|
||||
red_pattern_relu = WrapType("opset13.Relu", red_pattern_add)
|
||||
red_pattern_sigmoid = WrapType(["opset13.Sigmoid"], red_pattern_relu)
|
||||
|
||||
# Create a blue branch
|
||||
blue_pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
blue_pattern_abs = WrapType("opset13.Abs", blue_pattern_mul)
|
||||
blue_pattern_relu = WrapType(["opset13.Relu"], blue_pattern_abs)
|
||||
|
||||
#Create Or node
|
||||
pattern_or = Or([red_pattern_sigmoid, blue_pattern_relu])
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_or, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:pattern_or]
|
||||
|
||||
# ! [ov:pattern_optional_middle]
|
||||
def pattern_optional_middle():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern with an Optional node in the middle
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_abs)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_sig_opt)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:pattern_optional_middle]
|
||||
|
||||
# ! [ov:pattern_optional_top]
|
||||
def pattern_optional_top():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern an optional top node
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], AnyInput())
|
||||
pattern_mul = WrapType("opset13.MatMul", [pattern_sig_opt, AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid going into MatMul
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:pattern_optional_top]
|
||||
|
||||
# ! [ov:pattern_optional_root]
|
||||
def pattern_optional_root():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu)
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu)
|
||||
|
||||
matcher = Matcher(pattern_sig_opt, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid as root
|
||||
assert matcher.match(model_relu)
|
||||
# ! [ov:pattern_optional_root]
|
||||
|
||||
# ! [ov:optional_predicate]
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu, consumers_count(1))
|
||||
# ! [ov:optional_predicate]
|
||||
|
|
@ -16,25 +16,26 @@ Overview of Transformations API
|
|||
transformation-api/model-pass
|
||||
transformation-api/matcher-pass
|
||||
transformation-api/graph-rewrite-pass
|
||||
transformation-api/patterns-python-api
|
||||
|
||||
OpenVINO Transformation mechanism allows to develop transformation passes to modify ``ov::Model``. You can use this mechanism to apply additional optimizations to the original Model or transform unsupported subgraphs and operations to new operations which are supported by the plugin.
|
||||
This guide contains all necessary information that you need to start implementing OpenVINO™ transformations.
|
||||
OpenVINO Transformation mechanism allows to develop transformation passes to modify ``ov::Model``. You can use this mechanism to apply additional optimizations to the original Model or transform unsupported subgraphs and operations to new operations supported by the plugin.
|
||||
This guide contains all the necessary information to start implementing OpenVINO™ transformations.
|
||||
|
||||
Working with Model
|
||||
##################
|
||||
|
||||
Before the moving to transformation part it is needed to say several words about functions which allow to modify ``ov::Model``.
|
||||
This chapter extends the :doc:`model representation guide <../../openvino-workflow/running-inference/integrate-openvino-with-your-application/model-representation>` and shows an API that allows us to manipulate with ``ov::Model``.
|
||||
Before moving to the transformation part, it is important to say a few words about the functions which allow modifying ``ov::Model``.
|
||||
This section extends the :doc:`model representation guide <../../openvino-workflow/running-inference/integrate-openvino-with-your-application/model-representation>` and introduces an API for ``ov::Model`` manipulation.
|
||||
|
||||
Working with node input and output ports
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
First of all let's talk about ``ov::Node`` input/output ports. Each OpenVINO™ operation has input and output ports except cases when operation has ``Parameter`` or ``Constant`` type.
|
||||
Each OpenVINO operation has ``ov::Node`` input and output ports, except for ``Parameter`` and ``Constant`` types.
|
||||
The terms ``node`` and ``operation`` are used interchangeably in OpenVINO, but this article will maintain consistency in their use.
|
||||
|
||||
Every port belongs to its node, so using a port we can access parent node, get shape and type for particular input/output, get all consumers in case of output port, and get producer node in case of input port.
|
||||
With output port we can set inputs for newly created operations.
|
||||
Every port is associated with a node, allowing access to the node it belongs to, including its shape, type, all consumers for output ports and the producer node for input ports.
|
||||
|
||||
Lets look at the code example.
|
||||
Take a look at the code example:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
|
|
@ -47,7 +48,7 @@ OpenVINO™ provides two ways for node replacement: via OpenVINO™ helper funct
|
|||
|
||||
Let's start with OpenVINO™ helper functions. The most popular function is ``ov::replace_node(old_node, new_node)``.
|
||||
|
||||
We will review real replacement case where Negative operation is replaced with Multiply.
|
||||
Let's review a replacement case where a Negative operation is replaced with Multiply.
|
||||
|
||||
.. image:: ../../assets/images/ov_replace_node.png
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ We will review real replacement case where Negative operation is replaced with M
|
|||
:language: cpp
|
||||
:fragment: [ov:replace_node]
|
||||
|
||||
``ov::replace_node`` has a constraint that number of output ports for both of ops must be the same; otherwise, it raises an exception.
|
||||
``ov::replace_node`` has a constraint that number of output ports for both nodes must be the same. Otherwise, the attempt to replace the nodes will result in an exception.
|
||||
|
||||
The alternative way to do the same replacement is the following:
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ The alternative way to do the same replacement is the following:
|
|||
:language: cpp
|
||||
:fragment: [ov:manual_replace]
|
||||
|
||||
Another transformation example is insertion.
|
||||
Another transformation example is insertion. Let's insert an additional Relu node.
|
||||
|
||||
.. image:: ../../assets/images/ov_insert_node.png
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ Another transformation example is insertion.
|
|||
:language: cpp
|
||||
:fragment: [ov:insert_node]
|
||||
|
||||
The alternative way to the insert operation is to make a node copy and use ``ov::replace_node()``:
|
||||
The alternative way of inserting a node is to make a copy of the node and use ``ov::replace_node()``:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
|
|
@ -80,15 +81,15 @@ The alternative way to the insert operation is to make a node copy and use ``ov:
|
|||
Node elimination
|
||||
++++++++++++++++
|
||||
|
||||
Another type of node replacement is its elimination.
|
||||
Another type of node replacement is elimination of a node.
|
||||
|
||||
To eliminate operation, OpenVINO™ has special method that considers all limitations related to OpenVINO™ Runtime.
|
||||
To eliminate a node, OpenVINO provides a method that considers all limitations of the OpenVINO Runtime.
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:eliminate_node]
|
||||
|
||||
``ov::replace_output_update_name()`` in case of successful replacement it automatically preserves friendly name and runtime info.
|
||||
If the replacement is successful, ``ov::replace_output_update_name()`` automatically preserves the friendly name and runtime info.
|
||||
|
||||
.. _transformations_types:
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ OpenVINO™ Runtime has three main transformation types:
|
|||
|
||||
* :doc:`Model pass <transformation-api/model-pass>` - straightforward way to work with ``ov::Model`` directly
|
||||
* :doc:`Matcher pass <transformation-api/matcher-pass>` - pattern-based transformation approach
|
||||
* :doc:`Graph rewrite pass <transformation-api/graph-rewrite-pass>` - container for matcher passes needed for efficient execution
|
||||
* :doc:`Graph rewrite pass <transformation-api/graph-rewrite-pass>` - container for matcher passes used for efficient execution
|
||||
|
||||
.. image:: ../../assets/images/transformations_structure.png
|
||||
|
||||
|
|
@ -116,36 +117,36 @@ Transformation library has two internal macros to support conditional compilatio
|
|||
Transformation writing essentials
|
||||
#################################
|
||||
|
||||
When developing a transformation, you need to follow these transformation rules:
|
||||
To develop a transformation, follow these transformation rules:
|
||||
|
||||
1. Friendly Names
|
||||
+++++++++++++++++
|
||||
|
||||
Each ``ov::Node`` has an unique name and a friendly name. In transformations we care only about friendly name because it represents the name from the model.
|
||||
To avoid losing friendly name when replacing node with other node or subgraph, set the original friendly name to the latest node in replacing subgraph. See the example below.
|
||||
Each ``ov::Node`` has a unique name and a friendly name. In transformations, only the friendly name matters because it represents the name from the model's perspective.
|
||||
To prevent losing the friendly name when replacing a node with another node or a subgraph, the original friendly name is set to the last node in the replacing subgraph. See the example below.
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:replace_friendly_name]
|
||||
|
||||
In more advanced cases, when replaced operation has several outputs and we add additional consumers to its outputs, we make a decision how to set friendly name by arrangement.
|
||||
In more complicated cases, when a replaced operation has several outputs and additional consumers are added to its outputs, the decision on how to set the friendly name is determined by an agreement.
|
||||
|
||||
2. Runtime Info
|
||||
+++++++++++++++
|
||||
|
||||
Runtime info is a map ``std::map<std::string, ov::Any>`` located inside ``ov::Node`` class. It represents additional attributes in ``ov::Node``.
|
||||
These attributes can be set by users or by plugins and when executing transformation that changes ``ov::Model`` we need to preserve these attributes as they will not be automatically propagated.
|
||||
Runtime info is a map ``std::map<std::string, ov::Any>`` located inside the ``ov::Node`` class. It represents additional attributes of the ``ov::Node``.
|
||||
These attributes, which can be set by users or plugins, need to be preserved when executing a transformation that changes ``ov::Model``, as they are not automatically propagated.
|
||||
In most cases, transformations have the following types: 1:1 (replace node with another node), 1:N (replace node with a sub-graph), N:1 (fuse sub-graph into a single node), N:M (any other transformation).
|
||||
Currently, there is no mechanism that automatically detects transformation types, so we need to propagate this runtime information manually. See the examples below.
|
||||
Currently, there is no mechanism that automatically detects transformation types, so this runtime information needs to be propagated manually. See the example below:
|
||||
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_model_snippets.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:copy_runtime_info]
|
||||
|
||||
When transformation has multiple fusions or decompositions, ``ov::copy_runtime_info`` must be called multiple times for each case.
|
||||
When a transformation has multiple fusions or decompositions, ``ov::copy_runtime_info`` must be called multiple times for each case.
|
||||
|
||||
.. note:: ``copy_runtime_info`` removes ``rt_info`` from destination nodes. If you want to keep it, you need to specify them in source nodes like this: ``copy_runtime_info({a, b, c}, {a, b})``
|
||||
.. note:: ``copy_runtime_info`` removes ``rt_info`` from destination nodes. If you want to keep it, specify them in source nodes as following: ``copy_runtime_info({a, b, c}, {a, b})``
|
||||
|
||||
3. Constant Folding
|
||||
+++++++++++++++++++
|
||||
|
|
@ -172,21 +173,21 @@ Common mistakes in transformations
|
|||
|
||||
In transformation development process:
|
||||
|
||||
* Do not use deprecated OpenVINO™ API. Deprecated methods has the ``OPENVINO_DEPRECATED`` macros in its definition.
|
||||
* Do not pass ``shared_ptr<Node>`` as an input for other node if type of node is unknown or it has multiple outputs. Use explicit output port.
|
||||
* If you replace node with another node that produces different shape, remember that new shape will not be propagated until the first ``validate_nodes_and_infer_types`` call for ``ov::Model``. If you are using ``ov::pass::Manager``, it will automatically call this method after each transformation execution.
|
||||
* Do not use deprecated OpenVINO™ API. Deprecated methods are marked with ``OPENVINO_DEPRECATED`` macro in their definition.
|
||||
* Do not pass ``shared_ptr<Node>`` as input for another node if the type of the node is unknown or if it has multiple outputs. Instead, use explicit output ports.
|
||||
* If you replace a node with another node that produces different shape, note that the new shape will not be propagated until the first ``validate_nodes_and_infer_types`` call for ``ov::Model``. If you are using ``ov::pass::Manager``, it will automatically call this method after each transformation execution.
|
||||
* Do not forget to call the ``ov::pass::ConstantFolding`` pass if your transformation creates constant subgraphs.
|
||||
* Use latest OpSet if you are not developing downgrade transformation pass.
|
||||
* When developing a callback for ``ov::pass::MatcherPass``, do not change nodes that come after the root node in topological order.
|
||||
* When developing a callback for ``ov::pass::MatcherPass``, do not change nodes that come after the root node in the topological order.
|
||||
|
||||
.. _using_pass_manager:
|
||||
|
||||
Using pass manager
|
||||
##################
|
||||
|
||||
``ov::pass::Manager`` is a container class that can store the list of transformations and execute them. The main idea of this class is to have high-level representation for grouped list of transformations.
|
||||
It can register and apply any `transformation pass <#transformations_types>`__ on model.
|
||||
In addition, ``ov::pass::Manager`` has extended debug capabilities (find more information in the `how to debug transformations <#how_to_debug_transformations>`__ section).
|
||||
``ov::pass::Manager`` is a container class that can store a list of transformations and execute them. The main idea of this class is to have a high-level representation for grouped list of transformations.
|
||||
It can register and apply any `transformation pass <#transformations_types>`__ on a model.
|
||||
In addition, ``ov::pass::Manager`` has extended debug capabilities (find more information in the `how to debug transformations <#how-to-debug-transformations>`__ section).
|
||||
|
||||
The example below shows basic usage of ``ov::pass::Manager``
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
|
||||
Transformation Patterns with OpenVINO API
|
||||
==================================================
|
||||
|
||||
.. meta::
|
||||
:description: Learn how to apply additional model optimizations or transform
|
||||
unsupported subgraphs and operations using OpenVINO™ Transformations API.
|
||||
|
||||
Pattern matching is an essential component of OpenVINO™ transformations. Before performing any transformation on a subgraph of a graph, it is necessary to find that subgraph in the graph.
|
||||
Patterns serve as a searching utility to identify nodes intended for transformations. This article covers the basics of pattern
|
||||
creation using OpenVINO™ API and helpful utilities to facilitate working with them. While this guide focuses on creating patterns, if you want to learn more about ``MatcherPass``, refer to the :doc:`OpenVINO Matcher Pass article <./matcher-pass>`. Note that some examples may be intentionally simplified for ease of understanding.
|
||||
|
||||
Before proceeding further, it is necessary to add some imports. These imports include the operations to be used and additional utilities described in this guide.
|
||||
Add the following lines to your file:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:imports]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:imports]
|
||||
|
||||
Pattern Creation
|
||||
+++++++++++++++++++++
|
||||
|
||||
A pattern is a simplified model comprised of nodes aimed to be matched. It lacks some features of a model and cannot function as one.
|
||||
|
||||
Consider a straightforward pattern consisting of three nodes to be found in a given model.
|
||||
|
||||
.. image:: ./../../../assets/images/simple_pattern_example.png
|
||||
|
||||
Let's create the model and the pattern:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:create_simple_model_and_pattern]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:create_simple_model_and_pattern]
|
||||
|
||||
.. note:: This example uses testing utilities that directly compare given sequences of nodes. In reality, the process of finding a pattern within a model is more complicated. However, to focus only on patterns and their functionality, these details are intentionally omitted.
|
||||
|
||||
Although the code is functional, in OpenVINO, patterns are typically not created using the same nodes as those used for creating the model. Instead, wrappers are preferred, providing additional functionality.
|
||||
For the given case, ``WrapType`` is used and the code looks as following:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:create_simple_model_and_pattern_wrap_type]
|
||||
|
||||
1. WrapType
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
``WrapType`` is a wrapper used to store one or many types to match them. As demonstrated earlier, it is possible to specify a single type in ``WrapType`` and use it for matching.
|
||||
However, you can also list all possible types for a given node, for example:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:wrap_type_list]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:wrap_type_list]
|
||||
|
||||
Note that ``pattern_sig`` is created with the list ``["opset13.Relu", "opset13.Sigmoid"]``, meaning it can be either a ``Relu`` or a ``Sigmoid``.
|
||||
This feature enables matching the same pattern against different nodes. Essentially, ``WrapType`` can represent "one of listed" types. ``WrapType`` supports specifying more than two types.
|
||||
|
||||
To add additional checks for your node, create a predicate by providing a function or a lambda. This function will be executed during matching, performing the additional validation specified in the logic of the function. For example, you might want to check the consumers count of a given node:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:wrap_type_predicate]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:wrap_type_predicate]
|
||||
|
||||
2. AnyInput
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
``AnyInput`` is used when there is no need to specify a particular input for a given node.
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:any_input]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:any_input]
|
||||
|
||||
You can also create ``AnyInput()`` with a predicate, if you want additional checks for you input. It will look similar to ``WrapType`` with a lambda or a function. For example, to ensure that the input has a rank of 4:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:any_input_predicate]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:any_input_predicate]
|
||||
|
||||
3. Or
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
``Or`` functions similar to ``WrapType``, however, while ``WrapType`` can only match one of the types provided in the list, ``Or`` is used to match different branches of nodes.
|
||||
Suppose the goal is to match the model against two different sequences of nodes. The ``Or`` type
|
||||
facilitates this by creating two different branches (``Or`` supports more than two branches), looking as follows:
|
||||
|
||||
.. image:: ./../../../assets/images/or_branches.png
|
||||
|
||||
The red branch will not match, but it will work perfectly for the blue one.
|
||||
Here is how it looks in code:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_or]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_or]
|
||||
|
||||
Note that matching will succeed for the first matching branch and the remaining ones will not be checked.
|
||||
|
||||
4. Optional
|
||||
++++++++++++++++++++++++++++++++++++++++
|
||||
``Optional`` is a bit tricky. It allows specifying whether a node might be present or absent in the model. Under the hood,
|
||||
the pattern will create two branches using ``Or``: one with the optional node present and another one without it. Here is what it would look like with the ``Optional``
|
||||
unfolding into two branches:
|
||||
|
||||
.. image:: ./../../../assets/images/optional.png
|
||||
|
||||
The code for our model looks as follows:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_optional_middle]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_optional_middle]
|
||||
|
||||
The ``Optional`` does not necessarily have to be in the middle of the pattern. It can be a top node and a root node.
|
||||
|
||||
|
||||
Top node:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_optional_top]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_optional_top]
|
||||
|
||||
Root node:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:pattern_optional_root]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:pattern_optional_root]
|
||||
|
||||
``Optional`` also supports adding a predicate the same way ``WrapType`` and ``AnyInput`` do:
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.py
|
||||
:language: python
|
||||
:fragment: [ov:optional_predicate]
|
||||
|
||||
.. doxygensnippet:: docs/articles_en/assets/snippets/ov_patterns.cpp
|
||||
:language: cpp
|
||||
:fragment: [ov:optional_predicate]
|
||||
|
|
@ -21,6 +21,173 @@ from openvino.runtime.utils.types import get_element_type
|
|||
from tests.test_transformations.utils.utils import expect_exception
|
||||
|
||||
|
||||
def test_simple_model_and_pattern():
|
||||
# Create a sample model
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = ops.matmul(AnyInput(), AnyInput(), False, False)
|
||||
pattern_abs = ops.abs(pattern_mul)
|
||||
pattern_relu = ops.relu(pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_simple_model_and_pattern_wrap_type():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_wrap_type_list():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
model_sig = ops.sigmoid(model_abs) # Note that we've added a Sigmoid node after Abs
|
||||
model_result1 = ops.result(model_sig) # noqa
|
||||
|
||||
# Create a sample pattern
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType(["opset13.Relu", "opset13.Sigmoid"], pattern_abs)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
assert matcher.match(model_sig)
|
||||
|
||||
|
||||
def test_pattern_or():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a red branch
|
||||
red_pattern_add = WrapType("opset13.Add", [AnyInput(), AnyInput()])
|
||||
red_pattern_relu = WrapType("opset13.Relu", red_pattern_add)
|
||||
red_pattern_sigmoid = WrapType(["opset13.Sigmoid"], red_pattern_relu)
|
||||
|
||||
# Create a blue branch
|
||||
blue_pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
blue_pattern_abs = WrapType("opset13.Abs", blue_pattern_mul)
|
||||
blue_pattern_relu = WrapType(["opset13.Relu"], blue_pattern_abs)
|
||||
|
||||
# Create Or node
|
||||
pattern_or = Or([red_pattern_sigmoid, blue_pattern_relu])
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_or, "FindPattern")
|
||||
|
||||
# The same pattern perfectly matches 2 different nodes
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_pattern_optional_middle():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern with an Optional node in the middle
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_abs)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_sig_opt)
|
||||
|
||||
# Create a matcher and try to match the nodes
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_pattern_optional_top():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern an optional top node
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], AnyInput())
|
||||
pattern_mul = WrapType("opset13.MatMul", [pattern_sig_opt, AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
|
||||
matcher = Matcher(pattern_relu, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid going into MatMul
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_pattern_optional_root():
|
||||
model_param1 = ops.parameter(PartialShape([2, 2]))
|
||||
model_param2 = ops.parameter(PartialShape([2, 2]))
|
||||
model_add = ops.add(model_param1, model_param2)
|
||||
model_param3 = ops.parameter(PartialShape([2, 2]))
|
||||
model_mul = ops.matmul(model_add, model_param3, False, False)
|
||||
model_abs = ops.abs(model_mul)
|
||||
model_relu = ops.relu(model_abs)
|
||||
model_result = ops.result(model_relu) # noqa
|
||||
|
||||
# Create a sample pattern with an optional root node
|
||||
pattern_mul = WrapType("opset13.MatMul", [AnyInput(), AnyInput()])
|
||||
pattern_abs = WrapType("opset13.Abs", pattern_mul)
|
||||
pattern_relu = WrapType("opset13.Relu", pattern_abs)
|
||||
pattern_sig_opt = Optional(["opset13.Sigmoid"], pattern_relu)
|
||||
|
||||
matcher = Matcher(pattern_sig_opt, "FindPattern")
|
||||
|
||||
# Should perfectly match even though there's no Sigmoid as root
|
||||
assert matcher.match(model_relu)
|
||||
|
||||
|
||||
def test_wrap_type_pattern_type():
|
||||
last_opset_number = 15
|
||||
for i in range(1, last_opset_number + 1):
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@
|
|||
#include "openvino/op/equal.hpp"
|
||||
#include "openvino/op/exp.hpp"
|
||||
#include "openvino/op/greater.hpp"
|
||||
#include "openvino/op/matmul.hpp"
|
||||
#include "openvino/op/multiply.hpp"
|
||||
#include "openvino/op/non_max_suppression.hpp"
|
||||
#include "openvino/op/parameter.hpp"
|
||||
#include "openvino/op/reduce_sum.hpp"
|
||||
#include "openvino/op/relu.hpp"
|
||||
#include "openvino/op/sigmoid.hpp"
|
||||
#include "openvino/op/strided_slice.hpp"
|
||||
#include "openvino/op/subtract.hpp"
|
||||
#include "openvino/op/transpose.hpp"
|
||||
|
|
@ -1101,3 +1103,188 @@ TEST(pattern, wrap_type_multi_op) {
|
|||
ASSERT_FALSE(matcher->match(static_pointer_cast<Node>(c)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(pattern, simple_model_and_pattern) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = std::make_shared<ov::op::v0::MatMul>(pattern::any_input(), pattern::any_input(), false, false);
|
||||
auto pattern_abs = std::make_shared<ov::op::v0::Abs>(pattern_mul->output(0));
|
||||
auto pattern_relu = std::make_shared<ov::op::v0::Relu>(pattern_abs->output(0));
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, simple_model_and_pattern_wrap_type) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, wrap_type_list) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
auto model_sig = std::make_shared<ov::op::v0::Sigmoid>(model_abs->output(0));
|
||||
auto model_result1 = std::make_shared<ov::op::v0::Result>(model_sig->output(0));
|
||||
|
||||
// Create a sample model
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu, ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_sig));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_or) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a red branch
|
||||
auto red_pattern_add =
|
||||
ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto red_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({red_pattern_add->output(0)});
|
||||
auto red_pattern_sigmoid = ov::pass::pattern::wrap_type<ov::op::v0::Sigmoid>({red_pattern_relu->output(0)});
|
||||
|
||||
// Create a blue branch
|
||||
auto blue_pattern_mul =
|
||||
ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto blue_pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({blue_pattern_mul->output(0)});
|
||||
auto blue_pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({blue_pattern_abs->output(0)});
|
||||
|
||||
// Create Or node
|
||||
auto pattern_or = std::make_shared<ov::pass::pattern::op::Or>(
|
||||
OutputVector{red_pattern_sigmoid->output(0), blue_pattern_relu->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// The same pattern perfectly matches 2 different nodes
|
||||
ASSERT_TRUE(tm.match(pattern_or, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_optional_middle) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern with an Optional node in the middle
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>({pattern_abs->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_sig_opt->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_optional_top) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern::any_input());
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern_sig_opt, pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
|
||||
TEST(pattern, pattern_optional_root) {
|
||||
// Create a sample model
|
||||
PartialShape shape{2, 2};
|
||||
auto model_param1 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_param2 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_add = std::make_shared<ov::op::v1::Add>(model_param1->output(0), model_param2->output(0));
|
||||
auto model_param3 = std::make_shared<ov::op::v0::Parameter>(element::i32, shape);
|
||||
auto model_mul = std::make_shared<ov::op::v0::MatMul>(model_add->output(0), model_param3->output(0), false, false);
|
||||
auto model_abs = std::make_shared<ov::op::v0::Abs>(model_mul->output(0));
|
||||
auto model_relu = std::make_shared<ov::op::v0::Relu>(model_abs->output(0));
|
||||
auto model_result = std::make_shared<ov::op::v0::Result>(model_relu->output(0));
|
||||
|
||||
// Create a sample pattern an optional top node
|
||||
auto pattern_mul = ov::pass::pattern::wrap_type<ov::op::v0::MatMul>({pattern::any_input(), pattern::any_input()});
|
||||
auto pattern_abs = ov::pass::pattern::wrap_type<ov::op::v0::Abs>({pattern_mul->output(0)});
|
||||
auto pattern_relu = ov::pass::pattern::wrap_type<ov::op::v0::Relu>({pattern_abs->output(0)});
|
||||
auto pattern_sig_opt = ov::pass::pattern::optional<ov::op::v0::Sigmoid>(pattern_relu);
|
||||
|
||||
// Create a matcher and try to match the nodes
|
||||
TestMatcher tm;
|
||||
|
||||
// Should perfectly match
|
||||
ASSERT_TRUE(tm.match(pattern_relu, model_relu));
|
||||
}
|
||||
Loading…
Reference in New Issue