Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IR] Changed RT Info nodes naming (compatible with an old) #25159

Merged
merged 9 commits into from
Jul 8, 2024
29 changes: 28 additions & 1 deletion src/core/src/pass/serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,35 @@ class PaddingsFixer {
}
};

bool is_correct_tag_name(const std::string& name) {
if (name.length() == 0) {
return false;
}
if (!std::all_of(name.begin(), name.end(), [](const int c) {
return std::isalnum(c) || (c == '_') || (c == '-') || (c == '.');
})) {
return false;
}
Comment on lines +889 to +896
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (name.length() == 0) {
return false;
}
if (!std::all_of(name.begin(), name.end(), [](const int c) {
return std::isalnum(c) || (c == '_') || (c == '-') || (c == '.');
})) {
return false;
}
if (!std::all_of(name.begin(), name.end(), [](const int c) {
return std::isalnum(c) || (c == '_') || (c == '-') || (c == '.');
})) {
return false;
}

For empty string std::all_of should return false also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I just prefer explicit check :) I think I'll move it near name[0] access, just to prevent issues in case of reordering in the future. If this build fails - I'll apply, if ok - change as a separate pr

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, std::all_of for an empty sequence returns true.

if (std::isalpha(name[0]) == false && name[0] != '_') {
return false;
}
if (name.length() >= 3 && (name[0] == 'X' || name[0] == 'x') && (name[1] == 'M' || name[1] == 'm') &&
(name[2] == 'l' || name[2] == 'L')) {
return false;
}
return true;
}

void serialize_rt_info(pugi::xml_node& root, const std::string& name, const ov::Any& data) {
auto child = root.append_child(name.c_str());
pugi::xml_node child;
if (is_correct_tag_name(name)) {
child = root.append_child(name.c_str());
} else {
// Name may brake XML-naming specification, so better to store it as an attribute of typical
// node
child = root.append_child("info");
child.append_attribute("name").set_value(name.c_str());
}
if (data.is<std::shared_ptr<ov::Meta>>()) {
std::shared_ptr<ov::Meta> meta = data.as<std::shared_ptr<ov::Meta>>();
ov::AnyMap& map = *meta;
Expand Down
44 changes: 44 additions & 0 deletions src/core/tests/pass/serialization/rt_info_serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,47 @@ TEST_F(RTInfoSerializationTest, all_attributes_v10) {
check_info(add->output(0).get_rt_info());
EXPECT_EQ(f->get_parameters()[0]->get_layout(), "");
}

TEST_F(RTInfoSerializationTest, tag_names_verification) {
std::map<std::string, std::string> test_cases = {
{"0", "bad"},
{"0a", "bad"},
{"-a", "bad"},
{"a 0", "bad"},
{"a0", "good"},
{"a.0", "good"},
{".a0", "bad"},
{"a_0", "good"},
{"_0a", "bad"},
{"aXmL", "good"},
{"xMLa", "bad"},
{"XML", "bad"},
};
auto init_info = [&test_cases](ov::RTMap& info) {
for (const auto& item : test_cases) {
info[item.first] = item.second;
}
};

std::shared_ptr<ov::Model> model;
{
auto data = std::make_shared<ov::opset8::Parameter>(ov::element::Type_t::f32, ov::Shape{1, 3, 10, 10});
model = std::make_shared<ov::Model>(ov::OutputVector{data}, ov::ParameterVector{data});
init_info(model->get_rt_info());
}

ov::pass::Manager pass_manager;
pass_manager.register_pass<ov::pass::Serialize>(m_out_xml_path, m_out_bin_path);
pass_manager.run_passes(model);

auto ir_model = getWithIRFrontend(m_out_xml_path, m_out_bin_path);
ASSERT_NE(nullptr, ir_model);

auto model_rt_info = ir_model->get_rt_info();
std::for_each(test_cases.begin(),
test_cases.end(),
[&model_rt_info](const std::pair<std::string, std::string>& item) {
ASSERT_TRUE(model_rt_info.count(item.first));
ASSERT_EQ(model_rt_info[item.first], item.second);
});
}
18 changes: 14 additions & 4 deletions src/frontends/ir/src/ir_deserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,14 @@ class MetaDataParser : public ov::Meta {

ov::AnyMap parse_node(const pugi::xml_node& node) const {
ov::AnyMap result;
const std::string node_name = node.name();
// Old version may produce nodes like <name value="..."/>, but it may brake xml-naming convention
// Now it should look like <info name="..." value="..."/>.
// Also we keep an option to read an old XMLs where it doesn't have name attribute
const auto name_attr = node.attribute("name");
const std::string node_name = name_attr.empty() ? node.name() : name_attr.value();
for (const auto& data : node.children()) {
const std::string data_name = data.name();
const auto name_attr = data.attribute("name");
const std::string data_name = name_attr.empty() ? data.name() : name_attr.value();
// WA for legacy POT config
if (data_name == "config" && node_name == "quantization_parameters") {
// Read legacy pot config
Expand Down Expand Up @@ -658,12 +663,17 @@ void ov::XmlDeserializer::read_meta_data(const std::shared_ptr<ov::Model>& model
for (const auto& data : meta_section.children()) {
if (data.empty())
continue;
// Old version may produce nodes like <name value="..."/>, but it may brake xml-naming convention
// Now it should look like <info name="..." value="..."/>.
// Also we keep an option to read an old XMLs where it doesn't have name attribute
gkrivor marked this conversation as resolved.
Show resolved Hide resolved
const auto name_attr = data.attribute("name");
const auto node_name = name_attr.empty() ? data.name() : name_attr.value();
if (!data.attribute("value").empty()) {
rt_info[data.name()] = pugixml::get_str_attr(data, "value");
rt_info[node_name] = pugixml::get_str_attr(data, "value");
} else {
// Use meta data for set of parameters
std::shared_ptr<ov::Meta> meta = std::make_shared<MetaDataParser>(data.name(), data);
rt_info[data.name()] = meta;
rt_info[node_name] = meta;
}
}
}
Expand Down
66 changes: 66 additions & 0 deletions src/frontends/ir/tests/rt_info_deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -819,3 +819,69 @@ TEST_F(RTInfoDeserialization, indexes_input_and_output_v11) {
ASSERT_EQ(f->get_results()[0]->get_friendly_name(), "output2");
ASSERT_EQ(f->get_results()[1]->get_friendly_name(), "output1");
}

TEST_F(RTInfoDeserialization, node_naming_v11) {
std::string model = R"V0G0N(
<net name="Network" version="11">
<layers>
<layer id="0" name="input" type="Parameter" version="opset1">
<data shape="1,3,224,224" element_type="f32" />
<output>
<port id="0" precision="FP32" names="input">
<dim>1</dim>
<dim>3</dim>
<dim>224</dim>
<dim>224</dim>
</port>
</output>
</layer>
<layer id="1" name="output" type="Result" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>3</dim>
<dim>224</dim>
<dim>224</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="0" />
</edges>
<rt_info>
<framework>
<item0 value="0" /> <!-- Old-style notation, may cause an issue if starts with an unexpected character. Compatibility. -->
<info name="item1" value="1" /> <!-- New-style notation, name can contain any characters -->
</framework>
<info name="conversion_parameters"> <!-- New-style whole block -->
<info name="input_model" value="DIR\model.onnx" />
<info name="is_python_api_used" value="True" />
</info>
</rt_info>
</net>
)V0G0N";
auto f = getWithIRFrontend(model);
ASSERT_NE(nullptr, f);

auto check_version = [](const std::shared_ptr<ov::Model>& f, int ref_version) {
auto& rt_info = f->get_rt_info();
ASSERT_TRUE(rt_info.count("version"));
ASSERT_TRUE(rt_info.at("version").is<int64_t>());
ASSERT_EQ(rt_info.at("version").as<int64_t>(), ref_version);
};
check_version(f, 11);

auto& rt_info = f->get_rt_info();
ASSERT_TRUE(rt_info.count("framework"));
ASSERT_TRUE(rt_info.count("conversion_parameters"));

auto& item0 = f->get_rt_info<std::string>("framework", "item0");
ASSERT_EQ(item0, "0");

auto& item1 = f->get_rt_info<std::string>("framework", "item1");
ASSERT_EQ(item1, "1");

auto& is_python_api_used = f->get_rt_info<std::string>("conversion_parameters", "is_python_api_used");
ASSERT_EQ(is_python_api_used, "True");
}
4 changes: 2 additions & 2 deletions tools/mo/openvino/tools/mo/utils/ir_engine/ir_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def read_rt_info_attr(elem):
val_dict = {}
for child in elem:
child_val = read_rt_info_attr(child)
val_dict[child.tag] = child_val
val_dict[child.attrib.get('name', child.tag)] = child_val
return val_dict


Expand Down Expand Up @@ -104,7 +104,7 @@ def __load_xml(self):
statistics[layer.find('name').text] = {'min': layer.find('min').text, 'max': layer.find('max').text}
elif child.tag == 'rt_info':
for elem in child:
self.meta_data[elem.tag] = read_rt_info_attr(elem)
self.meta_data[elem.attrib.get('name', elem.tag)] = read_rt_info_attr(elem)

# TODO: Remove this part when POT updates to using of rt_info
elif child.tag == 'quantization_parameters':
Expand Down
Loading