From dffe4a4251913c3dd692695ddb485b7e37ca9be8 Mon Sep 17 00:00:00 2001 From: Ivan Tikhonov Date: Tue, 5 Oct 2021 18:45:17 +0300 Subject: [PATCH] If-8 operation: Serializer and Reader parts (#7545) * added ir reader * add serializer * fix code style * fix code style * fix code style * fix codestyle * update IR reader for IF op * move Function comparator to ngraph tests utils, update unit tests * update tests * cleanup * update unit tests * fix build issue * ngraph codestyle * Apply suggestions from code review Co-authored-by: Gleb Kazantaev Co-authored-by: Eugeny Volosenkov Co-authored-by: Gleb Kazantaev --- .../src/transformations/serialize.cpp | 58 ++- .../ir_serialization/models/if_diff_case.bin | Bin 0 -> 16 bytes .../ir_serialization/models/if_diff_case.xml | 438 ++++++++++++++++++ .../ir_serialization/serialize.cpp | 3 +- ngraph/core/src/op/if.cpp | 2 - ngraph/frontend/ir/src/ir_deserializer.cpp | 83 ++-- ngraph/frontend/ir/src/ir_deserializer.hpp | 10 +- ngraph/test/CMakeLists.txt | 4 +- ngraph/test/util/visitor.hpp | 36 ++ ngraph/test/visitors/op/if.cpp | 48 ++ 10 files changed, 624 insertions(+), 58 deletions(-) create mode 100644 inference-engine/tests/functional/inference_engine/ir_serialization/models/if_diff_case.bin create mode 100644 inference-engine/tests/functional/inference_engine/ir_serialization/models/if_diff_case.xml create mode 100644 ngraph/test/visitors/op/if.cpp diff --git a/inference-engine/src/transformations/src/transformations/serialize.cpp b/inference-engine/src/transformations/src/transformations/serialize.cpp index dea722a6364..c69c1ade3bc 100644 --- a/inference-engine/src/transformations/src/transformations/serialize.cpp +++ b/inference-engine/src/transformations/src/transformations/serialize.cpp @@ -270,9 +270,9 @@ class XmlSerializer : public ngraph::AttributeVisitor { } std::vector map_type_from_body(const pugi::xml_node& xml_node, - const std::string& map_type) { + const std::string& map_type, const std::string& body_name = "body") { std::vector output; - for (pugi::xml_node node : xml_node.child("body").child("layers")) { + for (pugi::xml_node node : xml_node.child(body_name.c_str()).child("layers")) { if (!map_type.compare(node.attribute("type").value())) { output.emplace_back(node.attribute("id").value()); } @@ -285,14 +285,14 @@ class XmlSerializer : public ngraph::AttributeVisitor { } void input_descriptions_on_adapter(const std::vector>& input_descriptions, + ngraph::op::util::MultiSubGraphOp::InputDescription>>& input_descriptions, const std::vector& parameter_mapping, const std::vector& result_mapping, - pugi::xml_node& port_map) { + pugi::xml_node& port_map, const std::string& portmap_name) { NGRAPH_CHECK(!parameter_mapping.empty(), "No parameters found in body Function."); - if (!m_xml_node.parent().child("port_map")) { - port_map = m_xml_node.parent().insert_child_before("port_map", m_xml_node.parent().first_child()); + if (!m_xml_node.parent().child(portmap_name.c_str())) { + port_map = m_xml_node.parent().insert_child_before(portmap_name.c_str(), m_xml_node.parent().first_child()); } for (const auto& input_description : input_descriptions) { @@ -319,14 +319,14 @@ class XmlSerializer : public ngraph::AttributeVisitor { } void output_descriptions_on_adapter(const std::vector>& output_descriptions, + ngraph::op::util::MultiSubGraphOp::OutputDescription>>& output_descriptions, const uint32_t& input_count, const std::vector& result_mapping, - pugi::xml_node& port_map) { + pugi::xml_node& port_map, const std::string& portmap_name) { NGRAPH_CHECK(!result_mapping.empty(), "No results found in body Function."); if (!port_map) { - port_map = m_xml_node.parent().insert_child_before("port_map", m_xml_node.parent().first_child()); + port_map = m_xml_node.parent().insert_child_before(portmap_name.c_str(), m_xml_node.parent().first_child()); } for (const auto& output_description : output_descriptions) { @@ -379,25 +379,47 @@ public: } void on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) override { - if (m_xml_node.parent().child("body")) { - std::vector result_mapping = map_type_from_body(m_xml_node.parent(), "Result"); - std::vector parameter_mapping = map_type_from_body(m_xml_node.parent(), "Parameter"); - pugi::xml_node port_map = m_xml_node.parent().child("port_map"); + using BodyTargetNames = std::tuple>; + + const std::vector body_names = { + BodyTargetNames{"body", "port_map", {"input_descriptions", "output_descriptions", "special_body_ports"}}, + BodyTargetNames{"then_body", "then_port_map", {"then_inputs", "then_outputs"}}, + BodyTargetNames{"else_body", "else_port_map", {"else_inputs", "else_outputs"}} }; + BodyTargetNames bnames; + bool is_body_target = false; + for (const auto& _body_target : body_names) { + if (m_xml_node.parent().child(std::get<0>(_body_target).c_str())) { + auto vec_names = std::get<2>(_body_target); + + if (std::find(vec_names.begin(), vec_names.end(), name) != vec_names.end()) { + is_body_target = true; + bnames = _body_target; + break; + } + } + } + if (is_body_target) { + auto body_name = std::get<0>(bnames); + auto portmap_name = std::get<1>(bnames); + std::vector result_mapping = map_type_from_body(m_xml_node.parent(), "Result", body_name); + std::vector parameter_mapping = map_type_from_body(m_xml_node.parent(), "Parameter", body_name); + + pugi::xml_node port_map = m_xml_node.parent().child(portmap_name.c_str()); NGRAPH_CHECK(!parameter_mapping.empty() || !result_mapping.empty(), "No parameters or results found in body Function."); // TI, Loop do not have attributtes as regular ops, it is necessary to append "port_map" and // "back_edges" to layer above (m_xml_node.parent()) as in ngfunction_2_ir() layer (here "m_xml_node") // with empty attributes is removed. if (const auto& a = ngraph::as_type>>>(&adapter)) { - input_descriptions_on_adapter(a->get(), parameter_mapping, result_mapping, port_map); + >>>(&adapter)) { + input_descriptions_on_adapter(a->get(), parameter_mapping, result_mapping, port_map, portmap_name); } else if (const auto& a = ngraph::as_type>>>(&adapter)) { + >>>(&adapter)) { uint32_t op_input_count = 0; for (auto c = m_xml_node.parent().child("input").first_child(); !c.empty(); c = c.next_sibling()) { op_input_count++; } - output_descriptions_on_adapter(a->get(), op_input_count, result_mapping, port_map); + output_descriptions_on_adapter(a->get(), op_input_count, result_mapping, port_map, portmap_name); } else if (const auto& a = ngraph::as_type>(&adapter)) { special_body_ports_on_adapter(a->get(), parameter_mapping, result_mapping, port_map); } @@ -491,7 +513,7 @@ public: void on_adapter( const std::string& name, ngraph::ValueAccessor>& adapter) override { - if (name == "body") { + if (name == "body" || name == "then_body" || name == "else_body") { // TI, Loop do not have attributtes as regular ops, it is necessary to append "body" // to layer above (m_xml_node.parent()) as in ngfunction_2_ir() layer (m_xml_node) with empty attributes // is removed. diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/models/if_diff_case.bin b/inference-engine/tests/functional/inference_engine/ir_serialization/models/if_diff_case.bin new file mode 100644 index 0000000000000000000000000000000000000000..05a13f7ad50b5b90d969e11282aed55d2d053567 GIT binary patch literal 16 NcmZQzfB;4)4FCWb00IC2 literal 0 HcmV?d00001 diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/models/if_diff_case.xml b/inference-engine/tests/functional/inference_engine/ir_serialization/models/if_diff_case.xml new file mode 100644 index 00000000000..091682085e5 --- /dev/null +++ b/inference-engine/tests/functional/inference_engine/ir_serialization/models/if_diff_case.xml @@ -0,0 +1,438 @@ + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + 2 + 4 + + + + + 2 + 4 + + + + + + + + 2 + + + + + + + + 2 + 4 + + + 2 + + + + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + 2 + 4 + + + 2 + 4 + + + 2 + 4 + + + + + 2 + 4 + + + 2 + 4 + + + + + + + + + + + + + + + + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + 2 + 4 + + + + + 2 + 4 + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + 2 + 4 + + + + + 2 + 4 + + + + + + + 2 + 4 + + + + + + + + + + + + + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + 2 + 4 + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + 2 + 4 + + + + + 2 + 4 + + + + + + + 2 + 4 + + + + + + + + 2 + 4 + + + 2 + 4 + + + + + 2 + 4 + + + + + + + 2 + 4 + + + + + + + + + + + + + + + + + + + + 2 + 4 + + + + + 2 + 4 + + + + + + + 2 + 4 + + + + + + + 2 + 4 + + + + + 2 + 4 + + + + + + + 2 + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp b/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp index e1c743f1e65..8f1e29d4d89 100644 --- a/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp +++ b/inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp @@ -77,7 +77,8 @@ INSTANTIATE_TEST_SUITE_P(IRSerialization, SerializationTest, std::make_tuple("pad_with_shape_of.xml", ""), std::make_tuple("conv_with_rt_info.xml", ""), std::make_tuple("loop_2d_add.xml", "loop_2d_add.bin"), - std::make_tuple("nms5_dynamism.xml", "nms5_dynamism.bin"))); + std::make_tuple("nms5_dynamism.xml", "nms5_dynamism.bin"), + std::make_tuple("if_diff_case.xml", "if_diff_case.bin"))); #ifdef NGRAPH_ONNX_FRONTEND_ENABLE diff --git a/ngraph/core/src/op/if.cpp b/ngraph/core/src/op/if.cpp index e94fc8eebac..82bf9e77494 100644 --- a/ngraph/core/src/op/if.cpp +++ b/ngraph/core/src/op/if.cpp @@ -60,8 +60,6 @@ static ov::PartialShape resolve_shape(const ov::PartialShape& then_pshape, const bool op::v8::If::visit_attributes(AttributeVisitor& visitor) { NGRAPH_OP_SCOPE(v8_If_visit_attributes); - m_bodies[THEN_BODY_INDEX] = std::make_shared(OutputVector{}, ParameterVector{}, "then_branch"); - m_bodies[ELSE_BODY_INDEX] = std::make_shared(OutputVector{}, ParameterVector{}, "else_branch"); visitor.on_attribute("then_body", m_bodies[THEN_BODY_INDEX]); visitor.on_attribute("else_body", m_bodies[ELSE_BODY_INDEX]); visitor.on_attribute("then_inputs", m_input_descriptions[THEN_BODY_INDEX]); diff --git a/ngraph/frontend/ir/src/ir_deserializer.cpp b/ngraph/frontend/ir/src/ir_deserializer.cpp index fdfebedf647..0c8d512430d 100644 --- a/ngraph/frontend/ir/src/ir_deserializer.cpp +++ b/ngraph/frontend/ir/src/ir_deserializer.cpp @@ -17,9 +17,7 @@ using namespace ov; -XmlDeserializer::IoMap XmlDeserializer::updated_io_map(const pugi::xml_node& node) { - auto body_node = node.child("body"); - +XmlDeserializer::IoMap XmlDeserializer::updated_io_map(const pugi::xml_node& node, const pugi::xml_node& body_node) { if (body_node.empty()) { IE_THROW() << "Missing body part."; } @@ -42,13 +40,17 @@ XmlDeserializer::IoMap XmlDeserializer::updated_io_map(const pugi::xml_node& nod } std::vector> XmlDeserializer::parseInputDescription( - const pugi::xml_node& node) { + const pugi::xml_node& node, + const std::string& body_name, + const std::string& port_map_name) { std::vector> inputs; - const auto up_io_map = updated_io_map(node); + auto body_node = node.child(body_name.c_str()); + + const auto up_io_map = updated_io_map(node, body_node); // Parse PortMap: external_port_id for inputs does not always appear in consecutive order std::map input_map; - FOREACH_CHILD (input, node.child("port_map"), "input") { + FOREACH_CHILD (input, node.child(port_map_name.c_str()), "input") { int64_t ext_port_id = XMLParseUtils::GetInt64Attr(input, "external_port_id"); input_map.emplace(ext_port_id, input); } @@ -112,14 +114,17 @@ std::vector> Xml return inputs; } -std::vector> XmlDeserializer::parseOutputDescription( - const pugi::xml_node& node) { - std::vector> outputs; - const auto up_io_map = updated_io_map(node); +std::vector> +XmlDeserializer::parseOutputDescription(const pugi::xml_node& node, + const std::string& body_name, + const std::string& port_map_name) { + std::vector> outputs; + auto body_node = node.child(body_name.c_str()); + const auto up_io_map = updated_io_map(node, body_node); // Parse PortMap: outputs std::map output_map; - FOREACH_CHILD (output, node.child("port_map"), "output") { + FOREACH_CHILD (output, node.child(port_map_name.c_str()), "output") { int64_t ext_port_id = XMLParseUtils::GetInt64Attr(output, "external_port_id"); output_map.emplace(ext_port_id, output); } @@ -144,20 +149,22 @@ std::vector> Xm const auto output_index = up_io_map.outputs.at(body_result_index); - outputs.push_back(std::make_shared(output_index, - output_number, - start, - stride, - part_size, - end, - axis)); + outputs.push_back( + std::make_shared(output_index, + output_number, + start, + stride, + part_size, + end, + axis)); } else { // otherwise create ngraph::TensorIterator::BodyOutput. -1 means last iteration. const auto output_index = up_io_map.outputs.at(body_result_index); - outputs.push_back(std::make_shared(output_index, - output_number, - -1)); + outputs.push_back( + std::make_shared(output_index, + output_number, + -1)); } output_number++; } @@ -167,7 +174,8 @@ std::vector> Xm ngraph::op::v5::Loop::SpecialBodyPorts XmlDeserializer::parsePurposeAttribute(const pugi::xml_node& node) { ngraph::op::v5::Loop::SpecialBodyPorts result = {-1, -1}; - const auto up_io_map = updated_io_map(node); + auto body_node = node.child("body"); + const auto up_io_map = updated_io_map(node, body_node); NGRAPH_CHECK(!up_io_map.inputs.empty() || !up_io_map.outputs.empty(), "No parameters or results found in body Function."); @@ -209,18 +217,30 @@ ngraph::op::v5::Loop::SpecialBodyPorts XmlDeserializer::parsePurposeAttribute(co void XmlDeserializer::on_adapter(const std::string& name, ngraph::ValueAccessor& adapter) { static const std::unordered_set skip_names = {"input_descriptions", "output_descriptions", - "special_body_ports"}; + "special_body_ports", + "then_inputs", + "else_inputs", + "then_outputs", + "else_outputs"}; std::string val; // for TensorIterator look for 'port_map' as 'data' does not exist - if (m_node.child("port_map")) { - if (auto a = ngraph::as_type< - ngraph::AttributeAdapter>>>( - &adapter)) { - a->set(parseInputDescription(m_node)); + if (m_node.child("port_map") || m_node.child("then_port_map") || m_node.child("else_port_map")) { + std::string body_name = "body"; + std::string port_map_name = "port_map"; + if (name == "then_inputs" || name == "then_outputs") { + body_name = "then_body"; + port_map_name = "then_port_map"; + } else if (name == "else_inputs" || name == "else_outputs") { + body_name = "else_body"; + port_map_name = "else_port_map"; + } + if (auto a = ngraph::as_type>>>(&adapter)) { + a->set(parseInputDescription(m_node, body_name, port_map_name)); } else if (auto a = ngraph::as_type>>>(&adapter)) { - a->set(parseOutputDescription(m_node)); + std::vector>>>(&adapter)) { + a->set(parseOutputDescription(m_node, body_name, port_map_name)); } else if (auto a = ngraph::as_type>(&adapter)) { a->set(parsePurposeAttribute(m_node)); @@ -362,7 +382,8 @@ void XmlDeserializer::on_adapter(const std::string& name, ngraph::ValueAccessor< void XmlDeserializer::on_adapter(const std::string& name, ngraph::ValueAccessor>& adapter) { std::shared_ptr ngraph_function; - if (!name.compare("body")) { + + if (!name.compare("body") || !name.compare("then_body") || !name.compare("else_body")) { auto body_node = m_node.child(name.c_str()); if (body_node.empty()) { IE_THROW() << "TensorIterator has no body."; diff --git a/ngraph/frontend/ir/src/ir_deserializer.hpp b/ngraph/frontend/ir/src/ir_deserializer.hpp index 7f96c723b13..8182f748d3d 100644 --- a/ngraph/frontend/ir/src/ir_deserializer.hpp +++ b/ngraph/frontend/ir/src/ir_deserializer.hpp @@ -148,16 +148,16 @@ private: /// \brief Traverses port_map in order to create vector of InputDescription shared_ptrs. /// Shall be used only for ops which have port_map attribute. /// \param node xml op representation - std::vector> parseInputDescription( - const pugi::xml_node& node); + std::vector> + parseInputDescription(const pugi::xml_node& node, const std::string& body_name, const std::string& port_map_name); /// \brief Traverses port_map in order to create vector of OutputDescription shared_ptrs. /// Shall be used only for ops which have port_map attribute. /// \param node xml op representation - std::vector> parseOutputDescription( - const pugi::xml_node& node); + std::vector> + parseOutputDescription(const pugi::xml_node& node, const std::string& body_name, const std::string& port_map_name); // TODO consider to call only once per layer/TI-Loop node - IoMap updated_io_map(const pugi::xml_node& node); + IoMap updated_io_map(const pugi::xml_node& node, const pugi::xml_node& body_node); /// \brief Traverses xml node representation in order to create nGraph function for it. /// \param node xml node representation diff --git a/ngraph/test/CMakeLists.txt b/ngraph/test/CMakeLists.txt index a50ee6c8571..e8a3896b6a4 100644 --- a/ngraph/test/CMakeLists.txt +++ b/ngraph/test/CMakeLists.txt @@ -292,6 +292,7 @@ set(SRC visitors/op/grn.cpp visitors/op/group_conv.cpp visitors/op/interpolate.cpp + visitors/op/if.cpp visitors/op/less_equal.cpp visitors/op/less.cpp visitors/op/log.cpp @@ -622,7 +623,8 @@ target_link_libraries(unit-test PRIVATE ngraph_test_util interpreter_backend Threads::Threads openvino::conditional_compilation - frontend_manager) + frontend_manager + PUBLIC commonTestUtils) # Protobuf-lite does not support parsing files from prototxt format # Since most of the onnx models are stored in this format it have to be disabled diff --git a/ngraph/test/util/visitor.hpp b/ngraph/test/util/visitor.hpp index b2ff9ae317f..d7b7a07cf83 100644 --- a/ngraph/test/util/visitor.hpp +++ b/ngraph/test/util/visitor.hpp @@ -96,6 +96,21 @@ public: virtual operator HostTensorPtr&() { NGRAPH_CHECK(false, "Invalid type access"); } + virtual operator std::shared_ptr&() { + NGRAPH_CHECK(false, "Invalid type access"); + } + virtual operator std::shared_ptr&() { + NGRAPH_CHECK(false, "Invalid type access"); + } + virtual operator std::shared_ptr&() { + NGRAPH_CHECK(false, "Invalid type access"); + } + virtual operator std::vector>&() { + NGRAPH_CHECK(false, "Invalid type access"); + } + virtual operator std::vector>&() { + NGRAPH_CHECK(false, "Invalid type access"); + } uint64_t get_index() { return m_index; } @@ -178,11 +193,22 @@ protected: class DeserializeAttributeVisitor : public AttributeVisitor { public: DeserializeAttributeVisitor(ValueMap& value_map) : m_values(value_map) {} + + void on_adapter(const std::string& name, ValueAccessor>& adapter) override { + adapter.set(m_values.get>(name)); + } + void on_adapter(const std::string& name, ValueAccessor& adapter) override { if (auto a = ::ngraph::as_type<::ngraph::AttributeAdapter>>( &adapter)) { auto& data = m_values.get(name); data->read(a->get()->get_ptr(), a->get()->size()); + } else if (auto a = ngraph::as_type>>>(&adapter)) { + a->set(m_values.get>>(name)); + } else if (auto a = ngraph::as_type>>>(&adapter)) { + a->set(m_values.get>>(name)); } else { NGRAPH_CHECK(false, "Attribute \"", name, "\" cannot be unmarshalled"); } @@ -247,12 +273,22 @@ class SerializeAttributeVisitor : public AttributeVisitor { public: SerializeAttributeVisitor(ValueMap& value_map) : m_values(value_map) {} + void on_adapter(const std::string& name, ValueAccessor>& adapter) override { + m_values.insert(name, adapter.get()); + } + void on_adapter(const std::string& name, ValueAccessor& adapter) override { if (auto a = ::ngraph::as_type<::ngraph::AttributeAdapter>>( &adapter)) { HostTensorPtr data = std::make_shared(element::u8, Shape{a->get()->size()}); data->write(a->get()->get_ptr(), a->get()->size()); m_values.insert(name, data); + } else if (auto a = ngraph::as_type>>>(&adapter)) { + m_values.insert_vector(name, a->get()); + } else if (auto a = ngraph::as_type>>>(&adapter)) { + m_values.insert_vector(name, a->get()); } else { NGRAPH_CHECK(false, "Attribute \"", name, "\" cannot be marshalled"); } diff --git a/ngraph/test/visitors/op/if.cpp b/ngraph/test/visitors/op/if.cpp new file mode 100644 index 00000000000..f89eb3f89d5 --- /dev/null +++ b/ngraph/test/visitors/op/if.cpp @@ -0,0 +1,48 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_test_utils/ngraph_test_utils.hpp" +#include "gtest/gtest.h" +#include "ngraph/ngraph.hpp" +#include "ngraph/op/util/attr_types.hpp" +#include "ngraph/opsets/opset8.hpp" +#include "util/visitor.hpp" + +using namespace std; +using namespace ngraph; +using namespace ngraph::opset8; +using ngraph::test::NodeBuilder; +using ngraph::test::ValueMap; + +TEST(attributes, if_op) { + NodeBuilder::get_ops().register_factory(); + auto X = make_shared(element::f32, Shape{1, 2, 2}); + auto Y = make_shared(element::f32, Shape{1, 2, 2}); + auto cond = std::make_shared(element::boolean, Shape{1}, true); + auto cond2 = std::make_shared(element::boolean, Shape{1}, false); + auto Xt = make_shared(element::f32, PartialShape::dynamic()); + auto Yt = make_shared(element::f32, PartialShape::dynamic()); + auto Xe = make_shared(element::f32, PartialShape::dynamic()); + auto Ye = make_shared(element::f32, PartialShape::dynamic()); + auto then_op = std::make_shared(Xt, Yt); + auto res0 = make_shared(then_op); + auto res1 = make_shared(Xe); + auto then_body = make_shared(OutputVector{res0}, ParameterVector{Xt, Yt}); + auto else_body = make_shared(OutputVector{res1}, ParameterVector{Xe}); + auto if_op = make_shared(cond); + if_op->set_then_body(then_body); + if_op->set_else_body(else_body); + if_op->set_input(X, Xt, Xe); + if_op->set_input(Y, Yt, nullptr); + if_op->set_output(res0, res1); + if_op->validate_and_infer_types(); + NodeBuilder builder(if_op); + auto g_if = ov::as_type_ptr(builder.create()); + EXPECT_EQ(g_if->get_input_descriptions(0), if_op->get_input_descriptions(0)); + EXPECT_EQ(g_if->get_input_descriptions(1), if_op->get_input_descriptions(1)); + EXPECT_EQ(g_if->get_output_descriptions(0), if_op->get_output_descriptions(0)); + EXPECT_EQ(g_if->get_output_descriptions(1), if_op->get_output_descriptions(1)); + EXPECT_TRUE(compare_functions(g_if->get_then_body(), if_op->get_then_body()).first); + EXPECT_TRUE(compare_functions(g_if->get_else_body(), if_op->get_else_body()).first); +} \ No newline at end of file