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 <gleb.nnstu@gmail.com>

Co-authored-by: Eugeny Volosenkov <eugeny.volosenkov@intel.com>
Co-authored-by: Gleb Kazantaev <gleb.nnstu@gmail.com>
This commit is contained in:
Ivan Tikhonov 2021-10-05 18:45:17 +03:00 committed by GitHub
parent 275d4838c5
commit dffe4a4251
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 624 additions and 58 deletions

View File

@ -270,9 +270,9 @@ class XmlSerializer : public ngraph::AttributeVisitor {
} }
std::vector<std::string> map_type_from_body(const pugi::xml_node& xml_node, std::vector<std::string> 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<std::string> output; std::vector<std::string> 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())) { if (!map_type.compare(node.attribute("type").value())) {
output.emplace_back(node.attribute("id").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<std::shared_ptr< void input_descriptions_on_adapter(const std::vector<std::shared_ptr<
ngraph::op::util::SubGraphOp::InputDescription>>& input_descriptions, ngraph::op::util::MultiSubGraphOp::InputDescription>>& input_descriptions,
const std::vector<std::string>& parameter_mapping, const std::vector<std::string>& parameter_mapping,
const std::vector<std::string>& result_mapping, const std::vector<std::string>& 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."); NGRAPH_CHECK(!parameter_mapping.empty(), "No parameters found in body Function.");
if (!m_xml_node.parent().child("port_map")) { if (!m_xml_node.parent().child(portmap_name.c_str())) {
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& input_description : input_descriptions) { for (const auto& input_description : input_descriptions) {
@ -319,14 +319,14 @@ class XmlSerializer : public ngraph::AttributeVisitor {
} }
void output_descriptions_on_adapter(const std::vector<std::shared_ptr< void output_descriptions_on_adapter(const std::vector<std::shared_ptr<
ngraph::op::util::SubGraphOp::OutputDescription>>& output_descriptions, ngraph::op::util::MultiSubGraphOp::OutputDescription>>& output_descriptions,
const uint32_t& input_count, const uint32_t& input_count,
const std::vector<std::string>& result_mapping, const std::vector<std::string>& 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."); NGRAPH_CHECK(!result_mapping.empty(), "No results found in body Function.");
if (!port_map) { 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) { for (const auto& output_description : output_descriptions) {
@ -379,25 +379,47 @@ public:
} }
void on_adapter(const std::string& name, ngraph::ValueAccessor<void>& adapter) override { void on_adapter(const std::string& name, ngraph::ValueAccessor<void>& adapter) override {
if (m_xml_node.parent().child("body")) { using BodyTargetNames = std::tuple<std::string, std::string, std::vector<std::string>>;
std::vector<std::string> result_mapping = map_type_from_body(m_xml_node.parent(), "Result");
std::vector<std::string> parameter_mapping = map_type_from_body(m_xml_node.parent(), "Parameter"); const std::vector<BodyTargetNames> body_names = {
pugi::xml_node port_map = m_xml_node.parent().child("port_map"); 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<std::string> result_mapping = map_type_from_body(m_xml_node.parent(), "Result", body_name);
std::vector<std::string> 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."); 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 // 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") // "back_edges" to layer above (m_xml_node.parent()) as in ngfunction_2_ir() layer (here "m_xml_node")
// with empty attributes is removed. // with empty attributes is removed.
if (const auto& a = ngraph::as_type<ngraph::AttributeAdapter<std::vector<std::shared_ptr if (const auto& a = ngraph::as_type<ngraph::AttributeAdapter<std::vector<std::shared_ptr
<ngraph::op::util::SubGraphOp::InputDescription>>>>(&adapter)) { <ngraph::op::util::MultiSubGraphOp::InputDescription>>>>(&adapter)) {
input_descriptions_on_adapter(a->get(), parameter_mapping, result_mapping, port_map); input_descriptions_on_adapter(a->get(), parameter_mapping, result_mapping, port_map, portmap_name);
} else if (const auto& a = ngraph::as_type<ngraph::AttributeAdapter<std::vector<std::shared_ptr } else if (const auto& a = ngraph::as_type<ngraph::AttributeAdapter<std::vector<std::shared_ptr
<ngraph::op::util::SubGraphOp::OutputDescription>>>>(&adapter)) { <ngraph::op::util::MultiSubGraphOp::OutputDescription>>>>(&adapter)) {
uint32_t op_input_count = 0; uint32_t op_input_count = 0;
for (auto c = m_xml_node.parent().child("input").first_child(); !c.empty(); c = c.next_sibling()) { for (auto c = m_xml_node.parent().child("input").first_child(); !c.empty(); c = c.next_sibling()) {
op_input_count++; 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<ngraph::AttributeAdapter<ngraph::op::v5::Loop::SpecialBodyPorts>>(&adapter)) { } else if (const auto& a = ngraph::as_type<ngraph::AttributeAdapter<ngraph::op::v5::Loop::SpecialBodyPorts>>(&adapter)) {
special_body_ports_on_adapter(a->get(), parameter_mapping, result_mapping, port_map); special_body_ports_on_adapter(a->get(), parameter_mapping, result_mapping, port_map);
} }
@ -491,7 +513,7 @@ public:
void on_adapter( void on_adapter(
const std::string& name, const std::string& name,
ngraph::ValueAccessor<std::shared_ptr<Function>>& adapter) override { ngraph::ValueAccessor<std::shared_ptr<Function>>& 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" // 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 // to layer above (m_xml_node.parent()) as in ngfunction_2_ir() layer (m_xml_node) with empty attributes
// is removed. // is removed.

View File

@ -0,0 +1,438 @@
<?xml version="1.0" ?>
<net name="if_diff_case" version="10">
<layers>
<layer id="0" name="x" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="Func/PartitionedCall/input/_0:0,x:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="1" name="w" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="Func/PartitionedCall/input/_3:0,w:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="2" name="PartitionedCall/model/if/Less" type="Less" version="opset1">
<data auto_broadcast="numpy"/>
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="2" names="PartitionedCall/model/if/Less:0" precision="BOOL">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="3" name="PartitionedCall/model/if/Const" type="Const" version="opset1">
<data element_type="i64" offset="0" shape="2" size="16"/>
<output>
<port id="0" names="PartitionedCall/model/if/Const:0" precision="I64">
<dim>2</dim>
</port>
</output>
</layer>
<layer id="4" name="PartitionedCall/model/if/All" type="ReduceLogicalAnd" version="opset1">
<data keep_dims="false"/>
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>2</dim>
</port>
</input>
<output>
<port id="2" names="PartitionedCall/model/if/All:0" precision="BOOL"/>
</output>
</layer>
<layer id="5" name="y" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="Func/PartitionedCall/input/_1:0,y:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="6" name="z" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="Func/PartitionedCall/input/_2:0,z:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="7" name="PartitionedCall/model/if/cond" type="If" version="opset8">
<input>
<port id="0"/>
<port id="1">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="2">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="3">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="4">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="5" names="PartitionedCall/model/if/cond/Identity:0,PartitionedCall/model/if/cond:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="6" names="PartitionedCall/model/if/cond/Identity_1:0,PartitionedCall/model/if/cond:1" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
<then_port_map>
<input external_port_id="1" internal_layer_id="0"/>
<input external_port_id="2" internal_layer_id="1"/>
<input external_port_id="3" internal_layer_id="4"/>
<output external_port_id="1" internal_layer_id="6"/>
<output external_port_id="0" internal_layer_id="3"/>
</then_port_map>
<else_port_map>
<input external_port_id="2" internal_layer_id="1"/>
<input external_port_id="3" internal_layer_id="0"/>
<input external_port_id="4" internal_layer_id="2"/>
<output external_port_id="0" internal_layer_id="5"/>
<output external_port_id="1" internal_layer_id="7"/>
</else_port_map>
<then_body>
<layers>
<layer id="0" name="add_x" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="add_x:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="1" name="add_w" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="add_w:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="2" name="Add" type="Add" version="opset1">
<data auto_broadcast="numpy"/>
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="2" names="Add:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="3" name="Identity/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
</layer>
<layer id="4" name="add_1_y" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="add_1_y:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="5" name="Add_1" type="Add" version="opset1">
<data auto_broadcast="numpy"/>
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="2" names="Add_1:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="6" name="Identity_1/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="2" to-port="0"/>
<edge from-layer="1" from-port="0" to-layer="2" to-port="1"/>
<edge from-layer="2" from-port="2" to-layer="3" to-port="0"/>
<edge from-layer="0" from-port="0" to-layer="5" to-port="0"/>
<edge from-layer="4" from-port="0" to-layer="5" to-port="1"/>
<edge from-layer="5" from-port="2" to-layer="6" to-port="0"/>
</edges>
</then_body>
<else_body>
<layers>
<layer id="0" name="add_y" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="add_y:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="1" name="mul_w" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="mul_w:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="2" name="mul_z" type="Parameter" version="opset1">
<data element_type="f32" shape="2,4"/>
<output>
<port id="0" names="mul_z:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="3" name="Mul" type="Multiply" version="opset1">
<data auto_broadcast="numpy"/>
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="2" names="Mul:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="4" name="Add" type="Add" version="opset1">
<data auto_broadcast="numpy"/>
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="2" names="Add:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="5" name="Identity/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
</layer>
<layer id="6" name="Add_1" type="Add" version="opset1">
<data auto_broadcast="numpy"/>
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="2" names="Add_1:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="7" name="Identity_1/sink_port_0" type="Result" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="1" from-port="0" to-layer="3" to-port="0"/>
<edge from-layer="2" from-port="0" to-layer="3" to-port="1"/>
<edge from-layer="0" from-port="0" to-layer="4" to-port="0"/>
<edge from-layer="3" from-port="2" to-layer="4" to-port="1"/>
<edge from-layer="4" from-port="2" to-layer="5" to-port="0"/>
<edge from-layer="2" from-port="0" to-layer="6" to-port="0"/>
<edge from-layer="1" from-port="0" to-layer="6" to-port="1"/>
<edge from-layer="6" from-port="2" to-layer="7" to-port="0"/>
</edges>
</else_body>
</layer>
<layer id="8" name="PartitionedCall/model/if/Relu_1" type="ReLU" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="1" names="Func/PartitionedCall/output/_5:0,Identity_1:0,PartitionedCall/Identity_1:0,PartitionedCall/model/if/Relu_1:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="9" name="Func/PartitionedCall/output/_5:0" type="Result" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
</layer>
<layer id="10" name="PartitionedCall/model/if/Relu" type="ReLU" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
<output>
<port id="1" names="Func/PartitionedCall/output/_4:0,Identity:0,PartitionedCall/Identity:0,PartitionedCall/model/if/Relu:0" precision="FP32">
<dim>2</dim>
<dim>4</dim>
</port>
</output>
</layer>
<layer id="11" name="Func/PartitionedCall/output/_4:0" type="Result" version="opset1">
<input>
<port id="0">
<dim>2</dim>
<dim>4</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="2" to-port="0"/>
<edge from-layer="1" from-port="0" to-layer="2" to-port="1"/>
<edge from-layer="2" from-port="2" to-layer="4" to-port="0"/>
<edge from-layer="3" from-port="0" to-layer="4" to-port="1"/>
<edge from-layer="4" from-port="2" to-layer="7" to-port="0"/>
<edge from-layer="0" from-port="0" to-layer="7" to-port="1"/>
<edge from-layer="1" from-port="0" to-layer="7" to-port="2"/>
<edge from-layer="5" from-port="0" to-layer="7" to-port="3"/>
<edge from-layer="6" from-port="0" to-layer="7" to-port="4"/>
<edge from-layer="7" from-port="6" to-layer="8" to-port="0"/>
<edge from-layer="8" from-port="1" to-layer="9" to-port="0"/>
<edge from-layer="7" from-port="5" to-layer="10" to-port="0"/>
<edge from-layer="10" from-port="1" to-layer="11" to-port="0"/>
</edges>
<meta_data>
<MO_version value="custom_mo/if_e1c378603739261e2f0e7334708eb6cf0b3c8ed1"/>
<cli_parameters>
<caffe_parser_path value="DIR"/>
<data_type value="FP32"/>
<disable_nhwc_to_nchw value="False"/>
<disable_omitting_optional value="False"/>
<disable_resnet_optimization value="False"/>
<disable_weights_compression value="False"/>
<enable_concat_optimization value="False"/>
<enable_flattening_nested_params value="False"/>
<enable_ssd_gluoncv value="False"/>
<extensions value="DIR"/>
<framework value="tf"/>
<freeze_placeholder_with_value value="{}"/>
<generate_deprecated_IR_V7 value="False"/>
<input value="x,y,z,w"/>
<input_model_is_text value="False"/>
<input_shape value="(2,4),(2,4),(2,4),(2,4)"/>
<k value="DIR\CustomLayersMapping.xml"/>
<keep_shape_ops value="True"/>
<legacy_ir_generation value="False"/>
<legacy_mxnet_model value="False"/>
<log_level value="ERROR"/>
<mean_scale_values value="{}"/>
<mean_values value="()"/>
<model_name value="if_diff_case"/>
<output_dir value="DIR"/>
<placeholder_data_types value="{}"/>
<placeholder_shapes value="{'x': array([2, 4], dtype=int64), 'y': array([2, 4], dtype=int64), 'z': array([2, 4], dtype=int64), 'w': array([2, 4], dtype=int64)}"/>
<progress value="False"/>
<remove_memory value="False"/>
<remove_output_softmax value="False"/>
<reverse_input_channels value="False"/>
<save_params_from_nd value="False"/>
<saved_model_dir value="DIR"/>
<scale_values value="()"/>
<silent value="False"/>
<static_shape value="False"/>
<stream_output value="False"/>
<transform value=""/>
<unset unset_cli_parameters="batch, counts, disable_fusing, disable_gfusing, finegrain_fusing, input_checkpoint, input_meta_graph, input_model, input_proto, input_symbol, mean_file, mean_file_offsets, move_to_preprocess, nd_prefix_name, output, pretrained_model_name, saved_model_tags, scale, tensorboard_logdir, tensorflow_custom_layer_libraries, tensorflow_custom_operations_config_update, tensorflow_object_detection_api_pipeline_config, tensorflow_use_custom_operations_config, transformations_config"/>
</cli_parameters>
</meta_data>
</net>

View File

@ -77,7 +77,8 @@ INSTANTIATE_TEST_SUITE_P(IRSerialization, SerializationTest,
std::make_tuple("pad_with_shape_of.xml", ""), std::make_tuple("pad_with_shape_of.xml", ""),
std::make_tuple("conv_with_rt_info.xml", ""), std::make_tuple("conv_with_rt_info.xml", ""),
std::make_tuple("loop_2d_add.xml", "loop_2d_add.bin"), 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 #ifdef NGRAPH_ONNX_FRONTEND_ENABLE

View File

@ -60,8 +60,6 @@ static ov::PartialShape resolve_shape(const ov::PartialShape& then_pshape, const
bool op::v8::If::visit_attributes(AttributeVisitor& visitor) { bool op::v8::If::visit_attributes(AttributeVisitor& visitor) {
NGRAPH_OP_SCOPE(v8_If_visit_attributes); NGRAPH_OP_SCOPE(v8_If_visit_attributes);
m_bodies[THEN_BODY_INDEX] = std::make_shared<ngraph::Function>(OutputVector{}, ParameterVector{}, "then_branch");
m_bodies[ELSE_BODY_INDEX] = std::make_shared<ngraph::Function>(OutputVector{}, ParameterVector{}, "else_branch");
visitor.on_attribute("then_body", m_bodies[THEN_BODY_INDEX]); visitor.on_attribute("then_body", m_bodies[THEN_BODY_INDEX]);
visitor.on_attribute("else_body", m_bodies[ELSE_BODY_INDEX]); visitor.on_attribute("else_body", m_bodies[ELSE_BODY_INDEX]);
visitor.on_attribute("then_inputs", m_input_descriptions[THEN_BODY_INDEX]); visitor.on_attribute("then_inputs", m_input_descriptions[THEN_BODY_INDEX]);

View File

@ -17,9 +17,7 @@
using namespace ov; using namespace ov;
XmlDeserializer::IoMap XmlDeserializer::updated_io_map(const pugi::xml_node& node) { XmlDeserializer::IoMap XmlDeserializer::updated_io_map(const pugi::xml_node& node, const pugi::xml_node& body_node) {
auto body_node = node.child("body");
if (body_node.empty()) { if (body_node.empty()) {
IE_THROW() << "Missing body part."; IE_THROW() << "Missing body part.";
} }
@ -42,13 +40,17 @@ XmlDeserializer::IoMap XmlDeserializer::updated_io_map(const pugi::xml_node& nod
} }
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>> XmlDeserializer::parseInputDescription( std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>> 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<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>> inputs; std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>> 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 // Parse PortMap: external_port_id for inputs does not always appear in consecutive order
std::map<uint64_t, pugi::xml_node> input_map; std::map<uint64_t, pugi::xml_node> 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"); int64_t ext_port_id = XMLParseUtils::GetInt64Attr(input, "external_port_id");
input_map.emplace(ext_port_id, input); input_map.emplace(ext_port_id, input);
} }
@ -112,14 +114,17 @@ std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>> Xml
return inputs; return inputs;
} }
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>> XmlDeserializer::parseOutputDescription( std::vector<std::shared_ptr<ngraph::op::util::MultiSubGraphOp::OutputDescription>>
const pugi::xml_node& node) { XmlDeserializer::parseOutputDescription(const pugi::xml_node& node,
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>> outputs; const std::string& body_name,
const auto up_io_map = updated_io_map(node); const std::string& port_map_name) {
std::vector<std::shared_ptr<ngraph::op::util::MultiSubGraphOp::OutputDescription>> outputs;
auto body_node = node.child(body_name.c_str());
const auto up_io_map = updated_io_map(node, body_node);
// Parse PortMap: outputs // Parse PortMap: outputs
std::map<int64_t, pugi::xml_node> output_map; std::map<int64_t, pugi::xml_node> 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"); int64_t ext_port_id = XMLParseUtils::GetInt64Attr(output, "external_port_id");
output_map.emplace(ext_port_id, output); output_map.emplace(ext_port_id, output);
} }
@ -144,20 +149,22 @@ std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>> Xm
const auto output_index = up_io_map.outputs.at(body_result_index); const auto output_index = up_io_map.outputs.at(body_result_index);
outputs.push_back(std::make_shared<ngraph::op::util::SubGraphOp::ConcatOutputDescription>(output_index, outputs.push_back(
output_number, std::make_shared<ngraph::op::util::MultiSubGraphOp::ConcatOutputDescription>(output_index,
start, output_number,
stride, start,
part_size, stride,
end, part_size,
axis)); end,
axis));
} else { } else {
// otherwise create ngraph::TensorIterator::BodyOutput. -1 means last iteration. // otherwise create ngraph::TensorIterator::BodyOutput. -1 means last iteration.
const auto output_index = up_io_map.outputs.at(body_result_index); const auto output_index = up_io_map.outputs.at(body_result_index);
outputs.push_back(std::make_shared<ngraph::op::util::SubGraphOp::BodyOutputDescription>(output_index, outputs.push_back(
output_number, std::make_shared<ngraph::op::util::MultiSubGraphOp::BodyOutputDescription>(output_index,
-1)); output_number,
-1));
} }
output_number++; output_number++;
} }
@ -167,7 +174,8 @@ std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>> Xm
ngraph::op::v5::Loop::SpecialBodyPorts XmlDeserializer::parsePurposeAttribute(const pugi::xml_node& node) { ngraph::op::v5::Loop::SpecialBodyPorts XmlDeserializer::parsePurposeAttribute(const pugi::xml_node& node) {
ngraph::op::v5::Loop::SpecialBodyPorts result = {-1, -1}; 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(), NGRAPH_CHECK(!up_io_map.inputs.empty() || !up_io_map.outputs.empty(),
"No parameters or results found in body Function."); "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<void>& adapter) { void XmlDeserializer::on_adapter(const std::string& name, ngraph::ValueAccessor<void>& adapter) {
static const std::unordered_set<std::string> skip_names = {"input_descriptions", static const std::unordered_set<std::string> skip_names = {"input_descriptions",
"output_descriptions", "output_descriptions",
"special_body_ports"}; "special_body_ports",
"then_inputs",
"else_inputs",
"then_outputs",
"else_outputs"};
std::string val; std::string val;
// for TensorIterator look for 'port_map' as 'data' does not exist // for TensorIterator look for 'port_map' as 'data' does not exist
if (m_node.child("port_map")) { if (m_node.child("port_map") || m_node.child("then_port_map") || m_node.child("else_port_map")) {
if (auto a = ngraph::as_type< std::string body_name = "body";
ngraph::AttributeAdapter<std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>>>>( std::string port_map_name = "port_map";
&adapter)) { if (name == "then_inputs" || name == "then_outputs") {
a->set(parseInputDescription(m_node)); 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<ngraph::AttributeAdapter<
std::vector<std::shared_ptr<ngraph::op::util::MultiSubGraphOp::InputDescription>>>>(&adapter)) {
a->set(parseInputDescription(m_node, body_name, port_map_name));
} else if (auto a = ngraph::as_type<ngraph::AttributeAdapter< } else if (auto a = ngraph::as_type<ngraph::AttributeAdapter<
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>>>>(&adapter)) { std::vector<std::shared_ptr<ngraph::op::util::MultiSubGraphOp::OutputDescription>>>>(&adapter)) {
a->set(parseOutputDescription(m_node)); a->set(parseOutputDescription(m_node, body_name, port_map_name));
} else if (auto a = } else if (auto a =
ngraph::as_type<ngraph::AttributeAdapter<ngraph::op::v5::Loop::SpecialBodyPorts>>(&adapter)) { ngraph::as_type<ngraph::AttributeAdapter<ngraph::op::v5::Loop::SpecialBodyPorts>>(&adapter)) {
a->set(parsePurposeAttribute(m_node)); 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, void XmlDeserializer::on_adapter(const std::string& name,
ngraph::ValueAccessor<std::shared_ptr<ngraph::Function>>& adapter) { ngraph::ValueAccessor<std::shared_ptr<ngraph::Function>>& adapter) {
std::shared_ptr<ngraph::Function> ngraph_function; std::shared_ptr<ngraph::Function> 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()); auto body_node = m_node.child(name.c_str());
if (body_node.empty()) { if (body_node.empty()) {
IE_THROW() << "TensorIterator has no body."; IE_THROW() << "TensorIterator has no body.";

View File

@ -148,16 +148,16 @@ private:
/// \brief Traverses port_map in order to create vector of InputDescription shared_ptrs. /// \brief Traverses port_map in order to create vector of InputDescription shared_ptrs.
/// Shall be used only for ops which have port_map attribute. /// Shall be used only for ops which have port_map attribute.
/// \param node xml op representation /// \param node xml op representation
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>> parseInputDescription( std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>>
const pugi::xml_node& node); 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. /// \brief Traverses port_map in order to create vector of OutputDescription shared_ptrs.
/// Shall be used only for ops which have port_map attribute. /// Shall be used only for ops which have port_map attribute.
/// \param node xml op representation /// \param node xml op representation
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>> parseOutputDescription( std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>>
const pugi::xml_node& node); 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 // 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. /// \brief Traverses xml node representation in order to create nGraph function for it.
/// \param node xml node representation /// \param node xml node representation

View File

@ -292,6 +292,7 @@ set(SRC
visitors/op/grn.cpp visitors/op/grn.cpp
visitors/op/group_conv.cpp visitors/op/group_conv.cpp
visitors/op/interpolate.cpp visitors/op/interpolate.cpp
visitors/op/if.cpp
visitors/op/less_equal.cpp visitors/op/less_equal.cpp
visitors/op/less.cpp visitors/op/less.cpp
visitors/op/log.cpp visitors/op/log.cpp
@ -622,7 +623,8 @@ target_link_libraries(unit-test PRIVATE ngraph_test_util
interpreter_backend interpreter_backend
Threads::Threads Threads::Threads
openvino::conditional_compilation openvino::conditional_compilation
frontend_manager) frontend_manager
PUBLIC commonTestUtils)
# Protobuf-lite does not support parsing files from prototxt format # 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 # Since most of the onnx models are stored in this format it have to be disabled

View File

@ -96,6 +96,21 @@ public:
virtual operator HostTensorPtr&() { virtual operator HostTensorPtr&() {
NGRAPH_CHECK(false, "Invalid type access"); NGRAPH_CHECK(false, "Invalid type access");
} }
virtual operator std::shared_ptr<ov::Function>&() {
NGRAPH_CHECK(false, "Invalid type access");
}
virtual operator std::shared_ptr<ngraph::op::util::MultiSubGraphOp::OutputDescription>&() {
NGRAPH_CHECK(false, "Invalid type access");
}
virtual operator std::shared_ptr<ngraph::op::util::MultiSubGraphOp::InputDescription>&() {
NGRAPH_CHECK(false, "Invalid type access");
}
virtual operator std::vector<std::shared_ptr<ngraph::op::util::MultiSubGraphOp::OutputDescription>>&() {
NGRAPH_CHECK(false, "Invalid type access");
}
virtual operator std::vector<std::shared_ptr<ngraph::op::util::MultiSubGraphOp::InputDescription>>&() {
NGRAPH_CHECK(false, "Invalid type access");
}
uint64_t get_index() { uint64_t get_index() {
return m_index; return m_index;
} }
@ -178,11 +193,22 @@ protected:
class DeserializeAttributeVisitor : public AttributeVisitor { class DeserializeAttributeVisitor : public AttributeVisitor {
public: public:
DeserializeAttributeVisitor(ValueMap& value_map) : m_values(value_map) {} DeserializeAttributeVisitor(ValueMap& value_map) : m_values(value_map) {}
void on_adapter(const std::string& name, ValueAccessor<std::shared_ptr<ov::Function>>& adapter) override {
adapter.set(m_values.get<std::shared_ptr<ov::Function>>(name));
}
void on_adapter(const std::string& name, ValueAccessor<void>& adapter) override { void on_adapter(const std::string& name, ValueAccessor<void>& adapter) override {
if (auto a = ::ngraph::as_type<::ngraph::AttributeAdapter<std::shared_ptr<ngraph::runtime::AlignedBuffer>>>( if (auto a = ::ngraph::as_type<::ngraph::AttributeAdapter<std::shared_ptr<ngraph::runtime::AlignedBuffer>>>(
&adapter)) { &adapter)) {
auto& data = m_values.get<HostTensorPtr>(name); auto& data = m_values.get<HostTensorPtr>(name);
data->read(a->get()->get_ptr(), a->get()->size()); data->read(a->get()->get_ptr(), a->get()->size());
} else if (auto a = ngraph::as_type<ngraph::AttributeAdapter<
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>>>>(&adapter)) {
a->set(m_values.get<std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>>>(name));
} else if (auto a = ngraph::as_type<ngraph::AttributeAdapter<
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>>>>(&adapter)) {
a->set(m_values.get<std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>>>(name));
} else { } else {
NGRAPH_CHECK(false, "Attribute \"", name, "\" cannot be unmarshalled"); NGRAPH_CHECK(false, "Attribute \"", name, "\" cannot be unmarshalled");
} }
@ -247,12 +273,22 @@ class SerializeAttributeVisitor : public AttributeVisitor {
public: public:
SerializeAttributeVisitor(ValueMap& value_map) : m_values(value_map) {} SerializeAttributeVisitor(ValueMap& value_map) : m_values(value_map) {}
void on_adapter(const std::string& name, ValueAccessor<std::shared_ptr<ov::Function>>& adapter) override {
m_values.insert(name, adapter.get());
}
void on_adapter(const std::string& name, ValueAccessor<void>& adapter) override { void on_adapter(const std::string& name, ValueAccessor<void>& adapter) override {
if (auto a = ::ngraph::as_type<::ngraph::AttributeAdapter<std::shared_ptr<ngraph::runtime::AlignedBuffer>>>( if (auto a = ::ngraph::as_type<::ngraph::AttributeAdapter<std::shared_ptr<ngraph::runtime::AlignedBuffer>>>(
&adapter)) { &adapter)) {
HostTensorPtr data = std::make_shared<HostTensor>(element::u8, Shape{a->get()->size()}); HostTensorPtr data = std::make_shared<HostTensor>(element::u8, Shape{a->get()->size()});
data->write(a->get()->get_ptr(), a->get()->size()); data->write(a->get()->get_ptr(), a->get()->size());
m_values.insert(name, data); m_values.insert(name, data);
} else if (auto a = ngraph::as_type<ngraph::AttributeAdapter<
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::OutputDescription>>>>(&adapter)) {
m_values.insert_vector(name, a->get());
} else if (auto a = ngraph::as_type<ngraph::AttributeAdapter<
std::vector<std::shared_ptr<ngraph::op::util::SubGraphOp::InputDescription>>>>(&adapter)) {
m_values.insert_vector(name, a->get());
} else { } else {
NGRAPH_CHECK(false, "Attribute \"", name, "\" cannot be marshalled"); NGRAPH_CHECK(false, "Attribute \"", name, "\" cannot be marshalled");
} }

View File

@ -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<If>();
auto X = make_shared<Parameter>(element::f32, Shape{1, 2, 2});
auto Y = make_shared<Parameter>(element::f32, Shape{1, 2, 2});
auto cond = std::make_shared<Constant>(element::boolean, Shape{1}, true);
auto cond2 = std::make_shared<Constant>(element::boolean, Shape{1}, false);
auto Xt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto Yt = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto Xe = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto Ye = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto then_op = std::make_shared<op::v1::Multiply>(Xt, Yt);
auto res0 = make_shared<op::Result>(then_op);
auto res1 = make_shared<op::Result>(Xe);
auto then_body = make_shared<ngraph::Function>(OutputVector{res0}, ParameterVector{Xt, Yt});
auto else_body = make_shared<ngraph::Function>(OutputVector{res1}, ParameterVector{Xe});
auto if_op = make_shared<If>(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<If>(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);
}