Serialization of experimental and custom ops (#2862)
authorJozef Daniecki <jozef.daniecki@intel.com>
Fri, 30 Oct 2020 04:31:27 +0000 (05:31 +0100)
committerGitHub <noreply@github.com>
Fri, 30 Oct 2020 04:31:27 +0000 (07:31 +0300)
* Add IEGeneric node type handling.

* Remove dependency on plugin_api library.

IEGeneric type name is passed via Visiotr API as new syntetic atribute.

* Add custom operations support

* Fix charachter literals comparison.

* Pass custom opsets to CNNNetwork:serialize().

IE extensions are stored in ngraph CNNNetwork and later used to pass
custom opsets to serialization transformation.

* Refactor custom ops tests to use template_extension library.

* Add comment on __generic_ie_type__ purpose.

16 files changed:
inference-engine/include/cpp/ie_cnn_network.h
inference-engine/src/inference_engine/cnn_network_ngraph_impl.cpp
inference-engine/src/inference_engine/cnn_network_ngraph_impl.hpp
inference-engine/src/inference_engine/generic_ie.cpp
inference-engine/src/plugin_api/generic_ie.hpp
inference-engine/src/readers/ir_reader/ie_ir_parser.cpp
inference-engine/src/readers/ir_reader/ie_ir_parser.hpp
inference-engine/src/readers/onnx_reader/ie_onnx_reader.cpp
inference-engine/src/transformations/include/transformations/serialize.hpp
inference-engine/src/transformations/src/transformations/serialize.cpp
inference-engine/tests/functional/inference_engine/ir_serialization/custom_ops.cpp [new file with mode: 0644]
inference-engine/tests/functional/inference_engine/ir_serialization/models/custom_op.prototxt [new file with mode: 0644]
inference-engine/tests/functional/inference_engine/ir_serialization/models/custom_op.xml [new file with mode: 0644]
inference-engine/tests/functional/inference_engine/ir_serialization/models/experimental_detectron_detection_output.xml [new file with mode: 0644]
inference-engine/tests/functional/inference_engine/ir_serialization/models/experimental_detectron_roi_feature_extractor.xml [new file with mode: 0644]
inference-engine/tests/functional/inference_engine/ir_serialization/serialize.cpp

index 8cd2358..9f3ea19 100644 (file)
@@ -20,6 +20,7 @@
 #include "ie_common.h"
 #include "ie_data.h"
 #include "details/ie_exception_conversion.hpp"
+#include "ie_extension.h"
 
 namespace ngraph {
 
@@ -54,8 +55,10 @@ public:
      * This constructor wraps existing ngraph::Function
      * If you want to avoid modification of original Function, please create a copy
      * @param network Pointer to the ngraph::Function object
+     * @param exts Vector of pointers to IE extension objects
      */
-    explicit CNNNetwork(const std::shared_ptr<ngraph::Function>& network);
+    explicit CNNNetwork(const std::shared_ptr<ngraph::Function>& network,
+                        const std::vector<IExtensionPtr>& exts = {});
 
     /**
      * @brief A destructor
index 379c260..dea2821 100644 (file)
@@ -53,7 +53,8 @@ static std::shared_ptr<ngraph::Function> copyFunction(const std::shared_ptr<cons
     return specialized_function;
 }
 
-CNNNetwork::CNNNetwork(const std::shared_ptr<ngraph::Function>& graph) {
+CNNNetwork::CNNNetwork(const std::shared_ptr<ngraph::Function>& graph,
+                       const std::vector<IExtensionPtr>& exts) {
     OV_ITT_SCOPED_TASK(itt::domains::IE, "CNNNetwork::CNNNetwork");
 
     if (graph == nullptr) {
@@ -61,7 +62,7 @@ CNNNetwork::CNNNetwork(const std::shared_ptr<ngraph::Function>& graph) {
     }
 
     // Create CNNNetworkNGraphImpl
-    network = std::make_shared<CNNNetworkNGraphImpl>(graph);
+    network = std::make_shared<CNNNetworkNGraphImpl>(graph, exts);
     actual = network.get();
     if (actual == nullptr) {
         THROW_IE_EXCEPTION << "CNNNetwork was not initialized.";
@@ -111,8 +112,10 @@ void CNNNetworkNGraphImpl::createDataForResult(const ::ngraph::Output<::ngraph::
     }
 }
 
-CNNNetworkNGraphImpl::CNNNetworkNGraphImpl(const std::shared_ptr<Function>& nGraph)
-    : _ngraph_function(nGraph) {
+CNNNetworkNGraphImpl::CNNNetworkNGraphImpl(
+    const std::shared_ptr<Function>& nGraph,
+    const std::vector<IExtensionPtr>& exts)
+    : _ngraph_function(nGraph), _ie_extensions(exts) {
     // Restore usual attributes for ICNNNetwork
     auto keep_input_info = [](CNNNetworkNGraphImpl& network, const DataPtr& inData) {
         InputInfo::Ptr info(new InputInfo());
@@ -402,8 +405,15 @@ StatusCode CNNNetworkNGraphImpl::serialize(const std::string& xmlPath,
                                            ResponseDesc* resp) const noexcept {
     try {
         if (getFunction()) {
+            std::map<std::string, ngraph::OpSet> custom_opsets;
+            for (auto extension : _ie_extensions) {
+                auto opset = extension->getOpSets();
+                custom_opsets.insert(begin(opset), end(opset));
+            }
             ngraph::pass::Manager manager;
-            manager.register_pass<ngraph::pass::Serialize>(xmlPath, binPath);
+            manager.register_pass<ngraph::pass::Serialize>(
+                xmlPath, binPath, ngraph::pass::Serialize::Version::IR_V10,
+                custom_opsets);
             manager.run_passes(_ngraph_function);
         } else {
 #ifdef ENABLE_V7_SERIALIZE
index 7eb5c5d..ed6c3c7 100644 (file)
@@ -27,6 +27,7 @@
 #include "ie_common.h"
 #include "ie_data.h"
 #include "ie_input_info.hpp"
+#include "ie_extension.h"
 
 namespace InferenceEngine {
 namespace details {
@@ -36,7 +37,8 @@ namespace details {
  */
 class INFERENCE_ENGINE_API_CLASS(CNNNetworkNGraphImpl): public ICNNNetwork {
 public:
-    CNNNetworkNGraphImpl(const std::shared_ptr<::ngraph::Function>& nGraph);
+    CNNNetworkNGraphImpl(const std::shared_ptr<::ngraph::Function>& nGraph,
+                         const std::vector<IExtensionPtr>& exts = {});
     CNNNetworkNGraphImpl(const ICNNNetwork& nGraph);
     ~CNNNetworkNGraphImpl() override = default;
 
@@ -88,6 +90,7 @@ protected:
 private:
     InferenceEngine::InputsDataMap _inputData;
     std::map<std::string, DataPtr> _outputData;
+    const std::vector<IExtensionPtr> _ie_extensions;
 
     /**
      * @brief Create DataPtr for nGraph operation
index 4dd9ae4..e20a4eb 100644 (file)
@@ -168,3 +168,17 @@ void ngraph::op::GenericIE::validate_and_infer_types() {
                            << " with type " << type;
     }
 }
+
+bool ngraph::op::GenericIE::visit_attributes(ngraph::AttributeVisitor& visitor) {
+    for (const auto& p : params) {
+        std::string name = p.first;
+        std::string value = p.second;
+        visitor.on_attribute(name, value);
+    }
+    // This is a way to pass type name to transformations::Serialize() without
+    // adding plugin_api dependency on transformation library
+    std::string name = "__generic_ie_type__";
+    std::string value = getType();
+    visitor.on_attribute(name, value);
+    return true;
+}
index f921143..a7e352a 100644 (file)
@@ -104,6 +104,8 @@ public:
 
     std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
 
+    bool visit_attributes(ngraph::AttributeVisitor& visitor) override;
+
     static void addExtension(std::shared_ptr<const ngraph::Function> func, const InferenceEngine::IShapeInferExtensionPtr& ext);
     static std::vector<InferenceEngine::IShapeInferExtensionPtr> getExtensions(std::shared_ptr<const ngraph::Function> func);
 
index 157984e..fd43522 100644 (file)
@@ -63,7 +63,7 @@ public:
         originBlob(weights) { }
 };
 
-V10Parser::V10Parser(const std::vector<IExtensionPtr>& exts) {
+V10Parser::V10Parser(const std::vector<IExtensionPtr>& exts) : _exts(exts) {
     // Load default opsets
     opsets["opset1"] = ngraph::get_opset1();
     opsets["opset2"] = ngraph::get_opset2();
@@ -196,7 +196,7 @@ std::shared_ptr<ICNNNetwork> V10Parser::parse(const pugi::xml_node& root, std::i
             result_nodes[0]->add_control_dependency(assign);
         }
     }
-    CNNNetwork net(function);
+    CNNNetwork net(function, _exts);
     parsePreProcess(net, root, binStream);
     return net;
 }
index 78b64b3..c515cc6 100644 (file)
@@ -59,6 +59,7 @@ public:
 
 private:
     std::map<std::string, ngraph::OpSet> opsets;
+    const std::vector<IExtensionPtr> _exts;
 
     struct GenericLayerParams {
         struct LayerPortData {
index 8668061..6c3f5bb 100644 (file)
@@ -62,7 +62,7 @@ bool ONNXReader::supportModel(std::istream& model) const {
 }
 
 CNNNetwork ONNXReader::read(std::istream& model, const std::vector<IExtensionPtr>& exts) const {
-    return CNNNetwork(ngraph::onnx_import::import_onnx_model(model, readPathFromStream(model)));
+    return CNNNetwork(ngraph::onnx_import::import_onnx_model(model, readPathFromStream(model)), exts);
 }
 
 INFERENCE_PLUGIN_API(StatusCode) InferenceEngine::CreateReader(IReader*& reader, ResponseDesc *resp) noexcept {
index ca7fc5a..7a26024 100644 (file)
@@ -33,11 +33,12 @@ public:
     bool run_on_function(std::shared_ptr<ngraph::Function> f) override;
 
     Serialize(const std::string& xmlPath, const std::string& binPath,
-              Version version = Version::IR_V10)
-        : m_xmlPath{xmlPath}, m_binPath{binPath}, m_version{version} {}
+              Version version = Version::IR_V10, std::map<std::string, ngraph::OpSet> custom_opsets = {})
+        : m_xmlPath{xmlPath}, m_binPath{binPath}, m_version{version}, m_custom_opsets{custom_opsets} {}
 
 private:
     const std::string m_xmlPath;
     const std::string m_binPath;
     const Version m_version;
+    const std::map<std::string, ngraph::OpSet> m_custom_opsets;
 };
index 19dc17a..0b0a468 100644 (file)
@@ -18,8 +18,8 @@ NGRAPH_RTTI_DEFINITION(ngraph::pass::Serialize, "Serialize", 0);
 
 namespace {  // helpers
 template <typename T, typename A>
-std::string joinVec(std::vector<T, A> const& vec,
-                    std::string const& glue = std::string(",")) {
+std::string joinVec(const std::vector<T, A>& vec,
+                    const std::string& glue = std::string(",")) {
     if (vec.empty()) return "";
     std::stringstream oss;
     oss << vec[0];
@@ -51,6 +51,8 @@ class XmlVisitor : public ngraph::AttributeVisitor {
     }
 
 public:
+    std::string ie_generic_type_name = "";
+
     XmlVisitor(pugi::xml_node& data) : m_data(data) {}
 
     void on_adapter(const std::string& name,
@@ -65,7 +67,15 @@ public:
     }
     void on_adapter(const std::string& name,
                     ngraph::ValueAccessor<std::string>& adapter) override {
-        m_data.append_attribute(name.c_str()).set_value(adapter.get().c_str());
+        // __generic_ie_type__ should not be serialized as a <data> attribute
+        // it is a WA to retrieve layer type name without introducing dependency on
+        // plugi_api library on transformations library
+        if (name == "__generic_ie_type__") {
+            ie_generic_type_name = adapter.get();
+        } else {
+            m_data.append_attribute(name.c_str())
+                .set_value(adapter.get().c_str());
+        }
     }
     void on_adapter(const std::string& name,
                     ngraph::ValueAccessor<int64_t>& adapter) override {
@@ -160,7 +170,9 @@ ConstantAtributes dump_constant_data(std::vector<uint8_t>& bin,
     return attr;
 }
 
-std::string get_opset_name(const ngraph::Node* n) {
+std::string get_opset_name(
+    const ngraph::Node* n,
+    const std::map<std::string, ngraph::OpSet>& custom_opsets) {
     auto opsets = std::array<std::reference_wrapper<const ngraph::OpSet>, 5>{
         ngraph::get_opset1(), ngraph::get_opset2(), ngraph::get_opset3(),
         ngraph::get_opset4(), ngraph::get_opset5()};
@@ -171,6 +183,15 @@ std::string get_opset_name(const ngraph::Node* n) {
             return "opset" + std::to_string(idx + 1);
         }
     }
+
+    for (const auto& custom_opset : custom_opsets) {
+        std::string name = custom_opset.first;
+        ngraph::OpSet opset = custom_opset.second;
+        if (opset.contains_op_type(n)) {
+            return name;
+        }
+    }
+
     return "experimental";
 }
 
@@ -180,8 +201,6 @@ std::string get_opset_name(const ngraph::Node* n) {
 // discrepancies discoverd, translations needs to be added here.
 std::string get_type_name(const ngraph::Node* n) {
     std::string name = n->get_type_name();
-    NGRAPH_CHECK(name != "GenericIE", "Unsupported type in ", n);
-
     const std::unordered_map<std::string, std::string> translator = {
         {"Constant", "Const"}};
     if (translator.count(name) > 0) {
@@ -250,8 +269,10 @@ std::string get_node_unique_name(std::unordered_set<std::string>& unique_names,
     return name;
 }
 
-void ngfunction_2_irv10(pugi::xml_document& doc, std::vector<uint8_t>& bin,
-                        const ngraph::Function& f) {
+void ngfunction_2_irv10(
+    pugi::xml_document& doc, std::vector<uint8_t>& bin,
+    const ngraph::Function& f,
+    const std::map<std::string, ngraph::OpSet>& custom_opsets) {
     pugi::xml_node netXml = doc.append_child("net");
     netXml.append_attribute("name").set_value(f.get_friendly_name().c_str());
     netXml.append_attribute("version").set_value("10");
@@ -270,9 +291,9 @@ void ngfunction_2_irv10(pugi::xml_document& doc, std::vector<uint8_t>& bin,
         layer.append_attribute("id").set_value(layer_ids.find(node)->second);
         layer.append_attribute("name").set_value(
             get_node_unique_name(unique_names, node).c_str());
-        layer.append_attribute("type").set_value(get_type_name(node).c_str());
+        auto layer_type_attribute = layer.append_attribute("type");
         layer.append_attribute("version").set_value(
-            get_opset_name(node).c_str());
+            get_opset_name(node, custom_opsets).c_str());
 
         // <layers/data>
         pugi::xml_node data = layer.append_child("data");
@@ -281,7 +302,13 @@ void ngfunction_2_irv10(pugi::xml_document& doc, std::vector<uint8_t>& bin,
         XmlVisitor visitor{data};
         NGRAPH_CHECK(node->visit_attributes(visitor),
                      "Visitor API is not supported in ", node);
-
+        std::string node_type_name {node->get_type_name()};
+        if (node_type_name == "GenericIE") {
+            layer_type_attribute.set_value(
+                visitor.ie_generic_type_name.c_str());
+        } else {
+            layer_type_attribute.set_value(get_type_name(node).c_str());
+        }
         // <layers/data> constant atributes (special case)
         if (auto constant = dynamic_cast<ngraph::op::Constant*>(node)) {
             ConstantAtributes attr = dump_constant_data(bin, *constant);
@@ -347,7 +374,7 @@ bool pass::Serialize::run_on_function(std::shared_ptr<ngraph::Function> f) {
     std::vector<uint8_t> constants;
     switch (m_version) {
     case Version::IR_V10:
-        ngfunction_2_irv10(xml_doc, constants, *f);
+        ngfunction_2_irv10(xml_doc, constants, *f, m_custom_opsets);
         break;
     default:
         NGRAPH_UNREACHABLE("Unsupported version");
diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/custom_ops.cpp b/inference-engine/tests/functional/inference_engine/ir_serialization/custom_ops.cpp
new file mode 100644 (file)
index 0000000..60ab91c
--- /dev/null
@@ -0,0 +1,103 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <gtest/gtest.h>
+
+#include <file_utils.h>
+#include <ie_api.h>
+#include <ie_iextension.h>
+#include "common_test_utils/ngraph_test_utils.hpp"
+#include "ie_core.hpp"
+#include "ngraph/ngraph.hpp"
+#include "transformations/serialize.hpp"
+
+#ifndef IR_SERIALIZATION_MODELS_PATH  // should be already defined by cmake
+#define IR_SERIALIZATION_MODELS_PATH ""
+#endif
+
+#ifndef IE_BUILD_POSTFIX  // should be already defined by cmake
+#define IE_BUILD_POSTFIX ""
+#endif
+
+static std::string get_extension_path() {
+    return FileUtils::makeSharedLibraryName<char>(
+        {}, std::string("template_extension") + IE_BUILD_POSTFIX);
+}
+
+class CustomOpsSerializationTest : 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(CustomOpsSerializationTest, CustomOpUser_MO) {
+    const std::string model = IR_SERIALIZATION_MODELS_PATH "custom_op.xml";
+
+    InferenceEngine::Core ie;
+    ie.AddExtension(
+        InferenceEngine::make_so_pointer<InferenceEngine::IExtension>(
+            get_extension_path()));
+
+    auto expected = ie.ReadNetwork(model);
+    expected.serialize(m_out_xml_path, m_out_bin_path);
+    auto result = ie.ReadNetwork(m_out_xml_path, m_out_bin_path);
+
+    bool success;
+    std::string message;
+    std::tie(success, message) =
+        compare_functions(result.getFunction(), expected.getFunction());
+
+    ASSERT_TRUE(success) << message;
+}
+
+TEST_F(CustomOpsSerializationTest, CustomOpUser_ONNXImporter) {
+    const std::string model = IR_SERIALIZATION_MODELS_PATH "custom_op.prototxt";
+
+    InferenceEngine::Core ie;
+    ie.AddExtension(
+        InferenceEngine::make_so_pointer<InferenceEngine::IExtension>(
+            get_extension_path()));
+
+    auto expected = ie.ReadNetwork(model);
+    expected.serialize(m_out_xml_path, m_out_bin_path);
+    auto result = ie.ReadNetwork(m_out_xml_path, m_out_bin_path);
+
+    bool success;
+    std::string message;
+    std::tie(success, message) =
+        compare_functions(result.getFunction(), expected.getFunction());
+
+    ASSERT_TRUE(success) << message;
+}
+
+TEST_F(CustomOpsSerializationTest, CustomOpTransformation) {
+    const std::string model = IR_SERIALIZATION_MODELS_PATH "custom_op.xml";
+
+    InferenceEngine::Core ie;
+    auto extension =
+        InferenceEngine::make_so_pointer<InferenceEngine::IExtension>(
+            get_extension_path());
+    ie.AddExtension(extension);
+    auto expected = ie.ReadNetwork(model);
+    ngraph::pass::Manager manager;
+    manager.register_pass<ngraph::pass::Serialize>(
+        m_out_xml_path, m_out_bin_path,
+        ngraph::pass::Serialize::Version::IR_V10, extension->getOpSets());
+    manager.run_passes(expected.getFunction());
+    auto result = ie.ReadNetwork(m_out_xml_path, m_out_bin_path);
+
+    bool success;
+    std::string message;
+    std::tie(success, message) =
+        compare_functions(result.getFunction(), expected.getFunction());
+
+    ASSERT_TRUE(success) << message;
+}
diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/models/custom_op.prototxt b/inference-engine/tests/functional/inference_engine/ir_serialization/models/custom_op.prototxt
new file mode 100644 (file)
index 0000000..3d643ab
--- /dev/null
@@ -0,0 +1,66 @@
+# This is syntetic model created by hand desined only for white-box unit testing
+ir_version: 3
+producer_name: "nGraph ONNX Importer"
+graph {
+  node {
+    input: "A"
+    output: "Y"
+    name: "operation"
+    op_type: "Template"
+    domain: "custom_domain"
+    attribute {
+        name: "add"
+        type: INT
+        i: 11
+    }
+  }
+  name: "test_graph"
+  input {
+    name: "A"
+    type {
+      tensor_type {
+        elem_type: 1
+        shape {
+          dim {
+            dim_value: 2
+          }
+          dim {
+            dim_value: 2
+          }
+          dim {
+            dim_value: 2
+          }
+          dim {
+            dim_value: 1
+          }
+        }
+      }
+    }
+  }
+  output {
+    name: "Y"
+    type {
+      tensor_type {
+        elem_type: 1
+        shape {
+          dim {
+            dim_value: 2
+          }
+          dim {
+            dim_value: 2
+          }
+          dim {
+            dim_value: 2
+          }
+          dim {
+            dim_value: 1
+          }
+        }
+      }
+    }
+  }
+}
+opset_import {
+  version: 1
+  domain: "com.example"
+}
\ No newline at end of file
diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/models/custom_op.xml b/inference-engine/tests/functional/inference_engine/ir_serialization/models/custom_op.xml
new file mode 100644 (file)
index 0000000..a7f9ef8
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" ?>
+<!--This is syntetic model created by hand desined only for white-box unit testing-->
+<net name="Network" version="10">
+    <layers>
+        <layer name="in1" type="Parameter" id="0" version="opset1">
+            <data element_type="f32" shape="2,2,2,1"/>
+            <output>
+                <port id="0" precision="FP32">
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>1</dim>
+                </port>
+            </output>
+        </layer>
+        <layer name="operation" id="1" type="Template" version="custom_opset">
+            <data  add="11"/>
+            <input>
+                <port id="1" precision="FP32">
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>1</dim>
+                </port>
+            </input>
+            <output>
+                <port id="2" precision="FP32">
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>1</dim>
+                </port>
+            </output>
+        </layer>
+        <layer name="output" type="Result" id="2" version="opset1">
+            <input>
+                <port id="0" precision="FP32">
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>2</dim>
+                    <dim>1</dim>
+                </port>
+            </input>
+        </layer>
+    </layers>
+    <edges>
+        <edge from-layer="0" from-port="0" to-layer="1" to-port="1"/>
+        <edge from-layer="1" from-port="2" to-layer="2" to-port="0"/>
+    </edges>
+</net>
\ No newline at end of file
diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/models/experimental_detectron_detection_output.xml b/inference-engine/tests/functional/inference_engine/ir_serialization/models/experimental_detectron_detection_output.xml
new file mode 100644 (file)
index 0000000..d00f042
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" ?>
+<!--This is syntetic model created by hand desined only for white-box unit testing-->
+<net name="add_abc" version="10">
+       <layers>
+               <layer id="0" name="A" type="Parameter" version="opset1">
+                       <data element_type="f32" shape="1"/>
+                       <output>
+                               <port id="0" precision="FP32">
+                                       <dim>1</dim>
+                               </port>
+                       </output>
+               </layer>
+               <layer id="1" name="DetectionOutput" type="ExperimentalDetectronDetectionOutput" version="experimental">
+                       <data class_agnostic_box_regression="0" deltas_weights="10.0,10.0,5.0,5.0" max_delta_log_wh="4.135166645050049" max_detections_per_image="100" nms_threshold="0.5" num_classes="81" post_nms_count="2000" score_threshold="0.05"/>
+                       <input>
+                               <port id="0">
+                                       <dim>1</dim>
+                               </port>
+                       </input>
+                       <output>
+                               <port id="1" precision="FP32">
+                                       <dim>1</dim>
+                               </port>
+                       </output>
+               </layer>
+               <layer id="2" name="Y" type="Result" version="opset1">
+                       <input>
+                               <port id="0">
+                                       <dim>1</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>
+</net>
diff --git a/inference-engine/tests/functional/inference_engine/ir_serialization/models/experimental_detectron_roi_feature_extractor.xml b/inference-engine/tests/functional/inference_engine/ir_serialization/models/experimental_detectron_roi_feature_extractor.xml
new file mode 100644 (file)
index 0000000..56eb115
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" ?>
+<!--This is syntetic model created by hand desined only for white-box unit testing-->
+<net name="add_abc" version="10">
+       <layers>
+               <layer id="0" name="A" type="Parameter" version="opset1">
+                       <data element_type="f32" shape="1"/>
+                       <output>
+                               <port id="0" precision="FP32">
+                                       <dim>1</dim>
+                               </port>
+                       </output>
+               </layer>
+               <layer id="1" name="ROIFeatureExtractor_1" type="ExperimentalDetectronROIFeatureExtractor" version="experimental">
+                       <data distribute_rois_between_levels="1" image_id="0" output_size="14" preserve_rois_order="1" pyramid_scales="4,8,16,32,64" sampling_ratio="2"/>
+                       <input>
+                               <port id="0">
+                                       <dim>1</dim>
+                               </port>
+                       </input>
+                       <output>
+                               <port id="1" precision="FP32">
+                                       <dim>1</dim>
+                               </port>
+                       </output>
+               </layer>
+               <layer id="2" name="Y" type="Result" version="opset1">
+                       <input>
+                               <port id="0">
+                                       <dim>1</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>
+</net>
index f41a4a0..a901309 100644 (file)
@@ -165,3 +165,37 @@ TEST_F(SerializationTest, ModelWithConstants_ONNXImporter) {
 
     ASSERT_TRUE(success) << message;
 }
+
+TEST_F(SerializationTest, ExperimentalDetectronROIFeatureExtractor_MO) {
+    const std::string model = IR_SERIALIZATION_MODELS_PATH
+        "experimental_detectron_roi_feature_extractor.xml";
+
+    InferenceEngine::Core ie;
+    auto expected = ie.ReadNetwork(model);
+    expected.serialize(m_out_xml_path, m_out_bin_path);
+    auto result = ie.ReadNetwork(m_out_xml_path, m_out_bin_path);
+
+    bool success;
+    std::string message;
+    std::tie(success, message) =
+        compare_functions(result.getFunction(), expected.getFunction());
+
+    ASSERT_TRUE(success) << message;
+}
+
+TEST_F(SerializationTest, ExperimentalDetectronDetectionOutput_MO) {
+    const std::string model = IR_SERIALIZATION_MODELS_PATH
+        "experimental_detectron_detection_output.xml";
+
+    InferenceEngine::Core ie;
+    auto expected = ie.ReadNetwork(model);
+    expected.serialize(m_out_xml_path, m_out_bin_path);
+    auto result = ie.ReadNetwork(m_out_xml_path, m_out_bin_path);
+
+    bool success;
+    std::string message;
+    std::tie(success, message) =
+        compare_functions(result.getFunction(), expected.getFunction());
+
+    ASSERT_TRUE(success) << message;
+}