From cf15cbd301cabe4ab640efa7dee40b2863084fc3 Mon Sep 17 00:00:00 2001 From: Vitaliy Urusovskij Date: Tue, 21 Dec 2021 06:45:14 +0300 Subject: [PATCH] Save Parameters order during IR deserialization (#9286) * Save Parameters order during IR deserialization * Add test to check inputs/outputs in IR --- src/frontends/ir/src/ir_deserializer.cpp | 23 +++--- .../ir_serialization/deterministicity.cpp | 77 +++++++++++++++++++ 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/frontends/ir/src/ir_deserializer.cpp b/src/frontends/ir/src/ir_deserializer.cpp index 166c9a92808..d319a5690aa 100644 --- a/src/frontends/ir/src/ir_deserializer.cpp +++ b/src/frontends/ir/src/ir_deserializer.cpp @@ -423,6 +423,9 @@ std::shared_ptr XmlDeserializer::parse_function( std::vector outputs; std::unordered_set opName; + std::vector order; + std::set dfs_used_nodes; + std::map> edges; // Read all layers and store their parameters in params map FOREACH_CHILD (node, root.child("layers"), "layer") { auto node_param = parseGenericParams(node); @@ -433,11 +436,15 @@ std::shared_ptr XmlDeserializer::parse_function( if (node_param.type == "Result" || node_param.type == "Assign") { outputs.push_back(node_param.layerId); } + if (node_param.type == "Parameter") { + // Save Parameters order according to order in XML. + // To do so, handle nodes manually and ignore during DFS + dfs_used_nodes.insert(node_param.layerId); + order.push_back(node_param.layerId); + edges[node_param.layerId] = {}; + } } - std::map> edges; - std::map> id_to_node; - // Read all edges and store them for further usage FOREACH_CHILD (_ec, root.child("edges"), "edge") { size_t fromLayer = XMLParseUtils::GetUIntAttr(_ec, "from-layer"); @@ -448,12 +455,10 @@ std::shared_ptr XmlDeserializer::parse_function( } // Run DFS starting from outputs to get nodes topological order - std::set used; - std::vector order; - std::function dfs = [&edges, &order, &used, &dfs](const size_t id) { - if (used.count(id)) + std::function dfs = [&edges, &order, &dfs_used_nodes, &dfs](const size_t id) { + if (dfs_used_nodes.count(id)) return; - used.insert(id); + dfs_used_nodes.insert(id); for (auto& edge : edges[id]) { dfs(edge.fromLayerId); } @@ -464,7 +469,7 @@ std::shared_ptr XmlDeserializer::parse_function( // OV_ITT_SCOPE_NEXT(FIRST_INFERENCE, taskChain, "ConstructNgraphNodes"); FunctionNodes func_nodes; - + std::map> id_to_node; std::map> variable_id_to_read_value; // Following topological order create nGraph operations diff --git a/src/tests/functional/inference_engine/ir_serialization/deterministicity.cpp b/src/tests/functional/inference_engine/ir_serialization/deterministicity.cpp index c22afcac38d..d97ef097677 100644 --- a/src/tests/functional/inference_engine/ir_serialization/deterministicity.cpp +++ b/src/tests/functional/inference_engine/ir_serialization/deterministicity.cpp @@ -9,6 +9,12 @@ #include "gtest/gtest.h" #include "ie_core.hpp" +#include +#include +#include + +using namespace ngraph; + #ifndef IR_SERIALIZATION_MODELS_PATH // should be already defined by cmake # error "IR_SERIALIZATION_MODELS_PATH is not defined" #endif @@ -92,3 +98,74 @@ TEST_F(SerializationDeterministicityTest, SerializeToBlob) { ASSERT_TRUE(expected.layerCount() == result.layerCount()); ASSERT_TRUE(expected.getInputShapes() == result.getInputShapes()); } + +class DeterministicityTest : public ::testing::Test { +protected: + std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name(); + std::string m_out_xml_path = test_name + ".xml"; + std::string m_out_bin_path = test_name + ".bin"; + + void TearDown() override { + std::remove(m_out_xml_path.c_str()); + std::remove(m_out_bin_path.c_str()); + } +}; + +TEST_F(DeterministicityTest, IRInputsOrder) { + const std::vector friend_names = {"A", "B", "C"}; + + auto a = std::make_shared(element::f32, Shape{2, 2}); + auto b = std::make_shared(element::f32, Shape{2, 2}); + auto add = std::make_shared(a, b); + auto c = std::make_shared(element::f32, Shape{2, 2}); + auto relu = std::make_shared(c); + auto add2 = std::make_shared(add, relu); + auto res = std::make_shared(add2); + + a->set_friendly_name(friend_names[0]); + b->set_friendly_name(friend_names[1]); + c->set_friendly_name(friend_names[2]); + + auto model = std::make_shared(NodeVector{res}, ParameterVector{a, b, c}); + + ov::runtime::Core core; + auto serialize = ov::pass::Serialize(m_out_xml_path, m_out_bin_path); + serialize.run_on_function(model); + auto serialized_func = core.read_model(m_out_xml_path); + + for (size_t i = 0; i < friend_names.size(); i++) { + auto param = serialized_func->get_parameters()[i]; + ASSERT_EQ(i, serialized_func->get_parameter_index(param)); + ASSERT_STREQ(friend_names[i].c_str(), param->get_friendly_name().c_str()); + } +} + +TEST_F(DeterministicityTest, IROutputsOrder) { + const std::vector friend_names = {"D", "E", "F"}; + + auto a = std::make_shared(element::f32, Shape{4, 4}); + auto axis_1 = ngraph::opset5::Constant::create(element::i64, Shape{}, {1}); + auto split1 = std::make_shared(a, axis_1, 2); + auto res1 = std::make_shared(split1->output(0)); + auto relu = std::make_shared(split1->output(1)); + auto split2 = std::make_shared(relu, axis_1, 2); + auto res2 = std::make_shared(split2->output(0)); + auto res3 = std::make_shared(split2->output(1)); + + res1->set_friendly_name(friend_names[0]); + res2->set_friendly_name(friend_names[1]); + res3->set_friendly_name(friend_names[2]); + + auto model = std::make_shared(NodeVector{res1, res2, res3}, ParameterVector{a}); + + ov::runtime::Core core; + auto serialize = ov::pass::Serialize(m_out_xml_path, m_out_bin_path); + serialize.run_on_function(model); + auto serialized_func = core.read_model(m_out_xml_path); + + for (size_t i = 0; i < friend_names.size(); i++) { + auto out = serialized_func->get_results()[i]; + ASSERT_EQ(i, serialized_func->get_result_index(out)); + ASSERT_STREQ(friend_names[i].c_str(), out->get_friendly_name().c_str()); + } +} \ No newline at end of file