// Copyright (C) 2020 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // // ! [ov:include] #include #include // ! [ov:include] #include #include #include #include #include #include #include // ! [ov:create_simple_model] std::shared_ptr create_simple_model() { // This example shows how to create ov::Model // // Parameter--->Multiply--->Add--->Result // Constant---' / // Constant---' // Create opset8::Parameter operation with static shape auto data = std::make_shared(ov::element::f32, ov::Shape{3, 1, 2}); auto mul_constant = ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {1.5}); auto mul = std::make_shared(data, mul_constant); auto add_constant = ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {0.5}); auto add = std::make_shared(mul, add_constant); // Create opset8::Result operation auto res = std::make_shared(mul); // Create nGraph function return std::make_shared(ov::ResultVector{res}, ov::ParameterVector{data}); } // ! [ov:create_simple_model] // ! [ov:create_advanced_model] std::shared_ptr create_advanced_model() { // Advanced example with multi output operation // // Parameter->Split---0-->Result // | `--1-->Relu-->Result // `----2-->Result auto data = std::make_shared(ov::element::f32, ov::Shape{1, 3, 64, 64}); // Create Constant for axis value auto axis_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{} /*scalar shape*/, {1}); // Create opset8::Split operation that splits input to three slices across 1st dimension auto split = std::make_shared(data, axis_const, 3); // Create opset8::Relu operation that takes 1st Split output as input auto relu = std::make_shared(split->output(1) /*specify explicit output*/); // Results operations will be created automatically based on provided OutputVector return std::make_shared(ov::OutputVector{split->output(0), relu, split->output(2)}, ov::ParameterVector{data}); } // ! [ov:create_advanced_model] void ov_api_examples() { std::shared_ptr node = std::make_shared(ov::element::f32, ov::PartialShape{ov::Dimension::dynamic(), 3, 64, 64}); // ! [ov:partial_shape] ov::Shape static_shape; ov::PartialShape partial_shape = node->output(0).get_partial_shape(); // get zero output partial shape if (!partial_shape.is_dynamic() /* or partial_shape.is_static() */) { static_shape = partial_shape.get_shape(); } // ! [ov:partial_shape] } // ! [ov:serialize] void serialize_example(const std::shared_ptr& model) { ov::serialize(model, "/path/to/file/model.xml", "/path/to/file/model.bin"); } // ! [ov:serialize] // ! [ov:visualize] void visualize_example(const std::shared_ptr& m) { // Need include: // * openvino/pass/manager.hpp // * openvino/pass/visualize_tree.hpp ov::pass::Manager manager; // Serialize ov::Model to before.svg file before transformation manager.register_pass("image.svg"); manager.run_passes(m); } // ! [ov:visualize] void model_inputs() { ov::Core core; std::shared_ptr model = core.read_model("model.xml"); //! [all_inputs_ouputs] /* Take information about all topology inputs */ auto inputs = model->inputs(); /* Take information about all topology outputs */ auto outputs = model->outputs(); //! [all_inputs_ouputs] } void pattern_matcher_examples(std::shared_ptr node) { { // ! [pattern:simple_example] // Pattern example auto input = std::make_shared(ov::element::i64, ov::Shape{1}); auto shapeof = std::make_shared(input); // Create Matcher with Parameter->ShapeOf pattern auto m = std::make_shared(shapeof, "MyPatternBasedTransformation"); // ! [pattern:simple_example] // ! [pattern:callback_example] ov::graph_rewrite_callback callback = [](ov::pass::pattern::Matcher& m) { // Get root node std::shared_ptr root_node = m.get_match_root(); // Get all nodes matched by pattern ov::NodeVector nodes = m.get_matched_nodes(); // Transformation code return false; }; // ! [pattern:callback_example] } { // ! [pattern:label_example] // Detect Multiply with arbitrary first input and second as Constant // ov::pattern::op::Label - represent arbitrary input auto input = ov::pass::pattern::any_input(); auto value = ov::opset8::Constant::create(ov::element::f32, ov::Shape{1}, {0.5}); auto mul = std::make_shared(input, value); auto m = std::make_shared(mul, "MultiplyMatcher"); // ! [pattern:label_example] } { // ! [pattern:concat_example] // Detect Concat operation with arbitrary number of inputs auto concat = ov::pass::pattern::wrap_type(); auto m = std::make_shared(concat, "ConcatMatcher"); // ! [pattern:concat_example] } { // ! [pattern:predicate_example] // Detect Multiply->Add sequence where mul has exactly one consumer auto mul = ov::pass::pattern::wrap_type(ov::pass::pattern::consumers_count(1)/*сheck consumers count*/); auto add = ov::pass::pattern::wrap_type({mul, ov::pass::pattern::any_input()}); auto m = std::make_shared(add, "MultiplyAddMatcher"); // Matcher can be used to match pattern manually on given node if (m->match(node->output(0))) { // Successfully matched } // ! [pattern:predicate_example] } } bool openvino_api_examples(std::shared_ptr 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 ov::Input data = node->input(0); ov::Input weights = node->input(1); ov::Output output = node->output(0); // Getting shape and type auto pshape = data.get_partial_shape(); auto el_type = data.get_element_type(); // Getting parent for input port ov::Output parent_output; parent_output = data.get_source_output(); // Another short way to get partent for output port parent_output = node->input_value(0); // Getting all consumers for output port auto consumers = output.get_target_inputs(); // ! [ov:ports_example] } { // ! [ngraph:shape_check] auto partial_shape = node->input(0).get_partial_shape(); // get zero input partial shape // Check that input shape rank is static if (!partial_shape.rank().is_static()) { return false; } auto rank_size = partial_shape.rank().get_length(); // Check that second dimension is not dynamic if (rank_size < 2 || partial_shape[1].is_dynamic()) { return false; } auto dim = partial_shape[1].get_length(); // ! [ngraph:shape_check] } return true; } // ! [ov:replace_node] bool ov_replace_node(std::shared_ptr node) { // Step 1. Verify that node has opset8::Negative type auto neg = std::dynamic_pointer_cast(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(neg->input_value(0), ov::opset8::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1})); mul->set_friendly_name(neg->get_friendly_name()); ov::copy_runtime_info(neg, mul); // Step 3. Replace Negative operation with Multiply operation ov::replace_node(neg, mul); return true; // Step 4. Negative operation will be removed automatically because all consumers was moved to Multiply operation } // ! [ov:replace_node] bool ov_manual_replace_node(std::shared_ptr node) { auto neg = std::dynamic_pointer_cast(node); if (!neg) { return false; } auto mul = std::make_shared(neg->input_value(0), ov::opset8::Constant::create(neg->get_element_type(), ov::Shape{1}, {-1})); mul->set_friendly_name(neg->get_friendly_name()); ov::copy_runtime_info(neg, mul); // ! [ov:manual_replace] // All neg->output(0) consumers will be moved to mul->output(0) port neg->output(0).replace(mul->output(0)); // ! [ov:manual_replace] return true; } // ! [ov:insert_node] // Step 1. Lets suppose that we have a node with single output port and we want to insert additional operation new_node after it void insert_example(std::shared_ptr 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(node); // Step 3. Reconnect all consumers to new_node for (auto input : consumers) { input.replace_source_output(new_node); } } // ! [ov:insert_node] // ! [ov:insert_node_with_copy] void insert_example_with_copy(std::shared_ptr 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(node_copy); ov::replace_node(node, new_node); } // ! [ov:insert_node_with_copy] void eliminate_example(std::shared_ptr node) { // ! [ov:eliminate_node] // Suppose we have a node that we want to remove bool success = ov::replace_output_update_name(node->output(0), node->input_value(0)); // ! [ov:eliminate_node] } void replace_friendly_name() { auto div = std::make_shared(); // ! [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(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(div->input(0).get_source_output(), pow); mul->set_friendly_name(div->get_friendly_name()); ngraph::replace_node(div, mul); // ! [ov:replace_friendly_name] } void constant_subgraph() { // ! [ov:constant_subgraph] // After ConstantFolding pass Power will be replaced with Constant auto input = std::make_shared(ov::element::f32, ov::Shape{1}); auto pow = std::make_shared(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(input /* not constant input */, pow); // ! [ov:constant_subgraph] } void copy_runtime_info_snippet() { std::shared_ptr transpose, reshape, div, pow, mul, conv, bias, conv_fused, a, b, c, e, f; // ! [ov:copy_runtime_info] // Replace Transpose with Reshape operation (1:1) ov::copy_runtime_info(transpose, reshape); // Replace Div operation with Power and Multiply sub-graph (1:N) ov::copy_runtime_info(div, {pow, mul}); // Fuse Convolution with Add operation (N:1) ov::copy_runtime_info({conv, bias}, {conv_fused}); // Any other transformation that replaces one sub-graph with another sub-graph (N:M) ov::copy_runtime_info({a, b, c}, {e, f}); // ! [ov:copy_runtime_info] }