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

[RtInfo/IR] Serialize non RuntimeAttribute rt_info entries of Node and Tensor #27358

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
23b7a71
Add custom info (de)serialization
t-jankowski Aug 27, 2024
5d986c1
Fix RTInfoDeserialization.node_v11 test
t-jankowski Oct 29, 2024
00a2649
Deserialize nested maps
t-jankowski Oct 31, 2024
05210a0
Merge remote-tracking branch 'upstream/master' into tj/rt_info/serial…
t-jankowski Nov 4, 2024
52c6d26
Fix MO IR reader
t-jankowski Nov 4, 2024
3ff33d0
Merge remote-tracking branch 'upstream/master' into tj/rt_info/serial…
t-jankowski Nov 8, 2024
5627553
Merge branch 'master' into tj/rt_info/serialize-rt_map
t-jankowski Nov 12, 2024
b6986e5
Merge remote-tracking branch 'upstream/master' into tj/rt_info/serial…
t-jankowski Nov 19, 2024
cc000fc
Use namepaces for test code
t-jankowski Nov 19, 2024
44f4a13
Merge remote-tracking branch 'upstream/master' into tj/rt_info/serial…
t-jankowski Nov 25, 2024
5f76938
Merge remote-tracking branch 'upstream/master' into tj/rt_info/serial…
t-jankowski Nov 29, 2024
937d0e7
Remove const_cast on name
t-jankowski Nov 29, 2024
cd48083
Merge branch 'master' into tj/rt_info/serialize-rt_map
mlukasze Dec 13, 2024
00bdf12
Merge branch 'master' into tj/rt_info/serialize-rt_map
mlukasze Dec 16, 2024
e1d7343
Merge branch 'master' into tj/rt_info/serialize-rt_map
mlukasze Dec 18, 2024
66e6d44
Merge branch 'master' into tj/rt_info/serialize-rt_map
mlukasze Dec 19, 2024
d467d97
Merge branch 'master' into tj/rt_info/serialize-rt_map
mlukasze Dec 20, 2024
7676ad5
Merge remote-tracking branch 'upstream/master' into tj/rt_info/serial…
t-jankowski Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions src/core/src/pass/serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,7 @@ void serialize_rt_info(pugi::xml_node& root, const std::string& name, const ov::
child.append_attribute("name").set_value(name.c_str());
}
if (data.is<std::shared_ptr<ov::Meta>>()) {
auto meta = data.as<std::shared_ptr<ov::Meta>>();
const auto& meta = data.as<std::shared_ptr<ov::Meta>>();
do {
if (auto meta_with_pugixml_node = std::dynamic_pointer_cast<ov::MetaDataWithPugixml>(meta)) {
if (auto pugi_node = meta_with_pugixml_node->get_pugi_node()) {
Expand All @@ -944,11 +944,32 @@ void serialize_rt_info(pugi::xml_node& root, const std::string& name, const ov::
serialize_rt_info(child, it.first, it.second);
}
} else {
std::string value = data.as<std::string>();
const auto& value = data.as<std::string>();
child.append_attribute("value").set_value(value.c_str());
}
}

bool append_custom_rt_info(pugi::xml_node& node, const std::string& name, const ov::Any& data) {
auto custom_node = node.append_child("custom");
custom_node.append_attribute("name").set_value(name.c_str());
bool appended = false;

if (data.is<ov::AnyMap>()) {
const auto& any_map = data.as<ov::AnyMap>();
for (const auto& it : any_map)
appended |= append_custom_rt_info(custom_node, it.first, it.second);

} else {
const auto& value = data.as<std::string>();
custom_node.append_attribute("value").set_value(value.c_str());
appended = true;
}

if (!appended)
node.remove_child(custom_node);
return appended;
}

void ngfunction_2_ir(pugi::xml_node& netXml,
const ov::Model& model,
ConstantWriter& constant_node_write_handler,
Expand Down Expand Up @@ -1012,10 +1033,10 @@ void ngfunction_2_ir(pugi::xml_node& netXml,
// <layers/data> general attributes
pugi::xml_node data = layer.append_child("data");

auto append_runtime_info = [](pugi::xml_node& node, ov::RTMap& attributes) {
auto append_runtime_info = [](pugi::xml_node& node, const ov::RTMap& attributes) {
pugi::xml_node rt_node = node.append_child("rt_info");
bool has_attrs = false;
for (auto& item : attributes) {
for (const auto& item : attributes) {
if (item.second.is<ov::RuntimeAttribute>()) {
auto attribute_node = rt_node.append_child("attribute");
auto& rt_attribute = item.second.as<ov::RuntimeAttribute>();
Expand All @@ -1030,9 +1051,13 @@ void ngfunction_2_ir(pugi::xml_node& netXml,
}
}
}
if (!has_attrs) {

for (const auto& item : attributes)
if (!item.second.is<ov::RuntimeAttribute>())
has_attrs |= append_custom_rt_info(rt_node, item.first, item.second);

if (!has_attrs)
node.remove_child(rt_node);
}
};

if (version >= 11) {
Expand Down
180 changes: 180 additions & 0 deletions src/core/tests/pass/serialization/rt_info_serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

#include <gtest/gtest.h>

#include <iostream>
#include <sstream>

#include "common_test_utils/common_utils.hpp"
#include "common_test_utils/file_utils.hpp"
#include "common_test_utils/test_common.hpp"
Expand Down Expand Up @@ -299,3 +302,180 @@ TEST(OvSerializationTests, SerializeRawMeta) {
EXPECT_EQ(0, serialized_model.compare(ir_with_rt_info));
}
}

namespace ov {
namespace test {

TEST(RTInfoSerialization, custom_info) {
std::string ref_ir_xml = R"V0G0N(<?xml version="1.0"?>
<net name="CustomRTI" version="11">
<layers>
<layer id="0" name="node_A" type="Parameter" version="opset1">
<data shape="10,10" element_type="f32" />
<rt_info>
<custom name="node_info_A" value="v_A" />
</rt_info>
<output>
<port id="0" precision="FP32">
<dim>10</dim>
<dim>10</dim>
<rt_info>
<custom name="output_info_A" value="o_A" />
</rt_info>
</port>
</output>
</layer>
<layer id="1" name="node_B" type="Const" version="opset1">
<data element_type="f32" shape="1" offset="0" size="4" />
<rt_info>
<custom name="node_info_B" value="v_B" />
</rt_info>
<output>
<port id="0" precision="FP32">
<dim>1</dim>
<rt_info>
<custom name="output_info_B" value="o_B" />
</rt_info>
</port>
</output>
</layer>
<layer id="2" name="node_C" type="Add" version="opset1">
<data auto_broadcast="numpy" />
<rt_info>
<custom name="node_info_C" value="v_C" />
</rt_info>
<input>
<port id="0" precision="FP32">
<dim>10</dim>
<dim>10</dim>
</port>
<port id="1" precision="FP32">
<dim>1</dim>
</port>
</input>
<output>
<port id="2" precision="FP32">
<dim>10</dim>
<dim>10</dim>
<rt_info>
<custom name="output_info_C" value="o_C" />
<custom name="output_info_D" value="o_D" />
</rt_info>
</port>
</output>
</layer>
<layer id="3" name="node_D" type="Result" version="opset1">
<rt_info>
<custom name="node_info_D" value="v_D" />
</rt_info>
<input>
<port id="0" precision="FP32">
<dim>10</dim>
<dim>10</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" />
</edges>
<rt_info />
</net>
)V0G0N";

const auto data = std::make_shared<op::v0::Parameter>(element::Type_t::f32, Shape{10, 10});
const auto one = std::make_shared<op::v0::Constant>(element::f32, Shape{1}, std::vector<float>{1.f});
const auto add = std::make_shared<op::v1::Add>(data, one);
const auto result = std::make_shared<op::v0::Result>(add);

const auto add_info = [](const std::shared_ptr<Node>& node, const std::string& value) {
node->set_friendly_name("node_" + value);
node->get_rt_info()["node_info_" + value] = "v_" + value;
node->output(0).get_rt_info()["output_info_" + value] = "o_" + value;
};
add_info(data, "A");
add_info(one, "B");
add_info(add, "C");
add_info(result, "D");

const auto model = std::make_shared<Model>(ResultVector{result}, ParameterVector{data});
model->set_friendly_name("CustomRTI");

std::stringstream model_ss, weights_ss;
EXPECT_NO_THROW((ov::pass::Serialize{model_ss, weights_ss}.run_on_model(model)));
EXPECT_EQ(ref_ir_xml.compare(model_ss.str()), 0);
}

TEST(RTInfoSerialization, AnyMap_info) {
std::string ref_ir_xml = R"V0G0N(<?xml version="1.0"?>
<net name="CustomRTI" version="11">
<layers>
<layer id="0" name="data" type="Parameter" version="opset1">
<data shape="111" element_type="f64" />
<output>
<port id="0" precision="FP64">
<dim>111</dim>
</port>
</output>
</layer>
<layer id="1" name="abs" type="Abs" version="opset1">
<rt_info>
<custom name="AnyMap">
<custom name="a" value="b" />
<custom name="i" value="7" />
<custom name="nested">
<custom name="c" value="d" />
</custom>
<custom name="x" value="3.14" />
</custom>
</rt_info>
<input>
<port id="0" precision="FP64">
<dim>111</dim>
</port>
</input>
<output>
<port id="1" precision="FP64">
<dim>111</dim>
</port>
</output>
</layer>
<layer id="2" name="result" type="Result" version="opset1">
<input>
<port id="0" precision="FP64">
<dim>111</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="1" to-port="0" />
<edge from-layer="1" from-port="1" to-layer="2" to-port="0" />
</edges>
<rt_info />
</net>
)V0G0N";

const auto data = std::make_shared<op::v0::Parameter>(element::Type_t::f64, Shape{111});
const auto abs = std::make_shared<op::v0::Abs>(data);
const auto result = std::make_shared<op::v0::Result>(abs);

data->set_friendly_name("data");
abs->set_friendly_name("abs");
result->set_friendly_name("result");

const auto empty = AnyMap{};
const auto nested = AnyMap{{"c", "d"}};
abs->get_rt_info()["AnyMap"] = AnyMap{{"a", "b"}, {"empty", empty}, {"i", 7}, {"x", 3.14}, {"nested", nested}};

const auto model = std::make_shared<Model>(ResultVector{result}, ParameterVector{data});
model->set_friendly_name("CustomRTI");

std::stringstream model_ss, weights_ss;
EXPECT_NO_THROW((ov::pass::Serialize{model_ss, weights_ss}.run_on_model(model)));
EXPECT_EQ(ref_ir_xml.compare(model_ss.str()), 0);
}
} // namespace test
} // namespace ov
62 changes: 42 additions & 20 deletions src/frontends/ir/src/ir_deserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,25 @@ static const std::string& translate_type_name(const std::string& name) {
return name;
}

namespace {
void set_custom_rt_info(const pugi::xml_node& rt_attrs, ov::AnyMap& rt_info) {
std::string custom_name, custom_value;
for (const auto& item : rt_attrs) {
if (std::strcmp(item.name(), "custom") == 0) {
if (ov::getStrAttribute(item, "name", custom_name)) {
if (ov::getStrAttribute(item, "value", custom_value)) {
rt_info.emplace(custom_name, custom_value);
} else {
rt_info.emplace(custom_name, ov::AnyMap{});
auto& nested = rt_info[custom_name].as<ov::AnyMap>();
set_custom_rt_info(item, nested);
}
}
}
}
}
} // namespace

std::shared_ptr<ov::Node> ov::XmlDeserializer::create_node(const std::vector<ov::Output<ov::Node>>& inputs,
const pugi::xml_node& node,
const std::shared_ptr<ov::AlignedBuffer>& weights,
Expand Down Expand Up @@ -972,33 +991,36 @@ std::shared_ptr<ov::Node> ov::XmlDeserializer::create_node(const std::vector<ov:
if (!rt_attrs)
return;
for (const auto& item : rt_attrs) {
std::string attribute_name, attribute_version;
// For view:
// <attribute name="old_api_map_order" version="0" value="0,3,1,2"/>
if (!getStrAttribute(item, "name", attribute_name) || !getStrAttribute(item, "version", attribute_version))
continue;

const auto& type_info = ov::DiscreteTypeInfo(attribute_name.c_str(), attribute_version.c_str());
auto attr = attrs_factory.create_by_type_info(type_info);
if (!attr.empty()) {
if (attr.is<ov::RuntimeAttribute>()) {
RTInfoDeserializer attribute_visitor(item);
if (attr.as<ov::RuntimeAttribute>().visit_attributes(attribute_visitor)) {
auto res = rt_info.emplace(type_info, attr);
if (!res.second) {
OPENVINO_THROW("multiple rt_info attributes are detected: ", attribute_name);
if (std::strcmp(item.name(), "attribute") == 0) {
std::string attribute_name, attribute_version;
if (!getStrAttribute(item, "name", attribute_name) ||
!getStrAttribute(item, "version", attribute_version))
continue;

const auto& type_info = ov::DiscreteTypeInfo(attribute_name.c_str(), attribute_version.c_str());
auto attr = attrs_factory.create_by_type_info(type_info);
if (!attr.empty()) {
if (attr.is<ov::RuntimeAttribute>()) {
RTInfoDeserializer attribute_visitor(item);
if (attr.as<ov::RuntimeAttribute>().visit_attributes(attribute_visitor)) {
auto res = rt_info.emplace(type_info, attr);
if (!res.second) {
OPENVINO_THROW("multiple rt_info attributes are detected: ", attribute_name);
}
} else {
OPENVINO_THROW("VisitAttributes is not supported for: ", item.name(), " attribute");
}
} else {
OPENVINO_THROW("VisitAttributes is not supported for: ", item.name(), " attribute");
OPENVINO_THROW("Attribute: ", item.name(), " is not recognized as runtime attribute");
}
} else {
OPENVINO_THROW("Attribute: ", item.name(), " is not recognized as runtime attribute");
// As runtime attributes are optional, so we skip attribute if it is unknown to avoid exception
// when loading new IR with new attribute in old OV version.
}
} else {
// As runtime attributes are optional, so we skip attribute if it is unknown to avoid exception
// when loading new IR with new attribute in old OV version.
}
}

set_custom_rt_info(rt_attrs, rt_info);
};

// read runtime info only for IR v11+
Expand Down
Loading
Loading