[IE][VPU]: Fixes addCopyForOutputsInsideNetwork (#2393)
authorGladilov, Gleb <gleb.gladilov@intel.com>
Thu, 24 Sep 2020 09:40:10 +0000 (12:40 +0300)
committerGitHub <noreply@github.com>
Thu, 24 Sep 2020 09:40:10 +0000 (12:40 +0300)
* [IE][VPU]: Fixes addCopyForOutputsInsideNetwork

In case of dynamic output with consumer pass tries
to connect output's shape with new intermediate data
twice: one at the moment of duplicateData call (successful)
and once more at the end of the pass manually. The second
try leads to error since child data is already connected.

Signed-off-by: Gladilov, Gleb <gleb.gladilov@intel.com>
* [IE][VPU]: Introduces tests on addCopyForOutputsInsideNetwork

Signed-off-by: Gladilov, Gleb <gleb.gladilov@intel.com>
inference-engine/src/vpu/graph_transformer/src/middleend/passes/add_copy_for_outputs_inside_network.cpp
inference-engine/tests/unit/vpu/base/graph_transformer_tests.cpp
inference-engine/tests/unit/vpu/base/graph_transformer_tests.hpp
inference-engine/tests/unit/vpu/middleend_tests/passes_tests/add_copy_for_outputs_inside_network.cpp [new file with mode: 0644]

index 859b267..a81a93f 100644 (file)
@@ -44,10 +44,6 @@ public:
                 newIntermediateData,
                 outputData,
                 "addCopyForOutputsInsideNetwork");
-
-            if (const auto& parentShapeEdge = outputData->parentDataToShapeEdge()) {
-                model->connectDataWithShape(parentShapeEdge->parent(), newIntermediateData);
-            }
         }
     }
 
index f58816c..efe885d 100644 (file)
@@ -72,6 +72,13 @@ OutputInfo OutputInfo::fromNetwork(int ind) {
     return info;
 }
 
+InputInfo InputInfo::constant(const DataDesc& desc) {
+    InputInfo info;
+    info.type = InputType::Constant;
+    info.desc = desc;
+    return info;
+}
+
 OutputInfo OutputInfo::intermediate(const DataDesc& desc) {
     OutputInfo info;
     info.type = OutputType::Intermediate;
@@ -161,6 +168,8 @@ Stage TestModel::addStage(const std::vector<InputInfo>& curInputInfos, const std
     for (const auto& info : curInputInfos) {
         if (info.type == InputType::Original) {
             curInputs.push_back(_inputs.at(info.originalInputInd));
+        } else if (info.type == InputType::Constant) {
+            curInputs.push_back(_model->addConstData(formatString("Const {} / {}", _stages.size(), curInputs.size()), info.desc));
         } else {
             curInputs.push_back(_stages.at(info.prevStageInd)->output(info.prevStageOutputInd));
         }
index 5c5557b..e8e7d45 100644 (file)
@@ -46,7 +46,8 @@ private:
 enum class InputType {
     Original,
     PrevStageOutput,
-    Intermediate
+    Intermediate,
+    Constant
 };
 
 struct InputInfo final {
@@ -54,10 +55,12 @@ struct InputInfo final {
     int originalInputInd = -1;
     int prevStageInd = -1;
     int prevStageOutputInd = -1;
+    DataDesc desc = DataDesc();
 
     static InputInfo fromNetwork(int ind = 0);
 
     static InputInfo fromPrevStage(int ind, int outputInd = 0);
+    static InputInfo constant(const DataDesc& desc);
 
     InputInfo& output(int ind);
 };
diff --git a/inference-engine/tests/unit/vpu/middleend_tests/passes_tests/add_copy_for_outputs_inside_network.cpp b/inference-engine/tests/unit/vpu/middleend_tests/passes_tests/add_copy_for_outputs_inside_network.cpp
new file mode 100644 (file)
index 0000000..dd97d71
--- /dev/null
@@ -0,0 +1,217 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "graph_transformer_tests.hpp"
+
+namespace {
+
+class AddCopyForOutputsInsideNetwork : public vpu::GraphTransformerTest {
+protected:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(GraphTransformerTest::SetUp());
+        ASSERT_NO_FATAL_FAILURE(InitCompileEnv());
+        ASSERT_NO_FATAL_FAILURE(InitPipeline());
+
+        m_testModel = CreateTestModel();
+    }
+
+    void CheckOutputs(const vpu::DataMap<vpu::Data>& parentShapes = {}, const vpu::DataMap<vpu::DataVector>& childShapes = {}) {
+        for (const auto& output : m_testModel.getOutputs()) {
+            ASSERT_TRUE(output->consumerEdges().empty());
+            ASSERT_TRUE((parentShapes.count(output) == 0 && output->parentDataToShapeEdge() == nullptr) ||
+                (parentShapes.count(output) == 1 && output->parentDataToShapeEdge()->parent() == parentShapes.at(output)));
+            ASSERT_FALSE((childShapes.count(output) == 0) ^ output->childDataToShapeEdges().empty());
+            if (output->childDataToShapeEdges().empty()) {
+                continue;
+            }
+
+            vpu::DataVector actualChildDataObjects;
+            const auto& actualChildShapesEdges = output->childDataToShapeEdges();
+            std::transform(actualChildShapesEdges.begin(), actualChildShapesEdges.end(), std::back_inserter(actualChildDataObjects),
+                [](const vpu::DataToShapeAllocation& edge) { return edge->child(); });
+            ASSERT_EQ(actualChildDataObjects, childShapes.at(output));
+        }
+    }
+
+    void Compile() {
+        m_pipeline.run(m_testModel.getBaseModel());
+    }
+
+    void InitPipeline() {
+        m_pipeline = vpu::PassSet();
+        m_pipeline.addPass(passManager->dumpModel("before-addCopyForOutputsInsideNetwork"));
+        m_pipeline.addPass(passManager->addCopyForOutputsInsideNetwork());
+        m_pipeline.addPass(passManager->dumpModel("after-addCopyForOutputsInsideNetwork"));
+    }
+
+protected:
+    vpu::PassSet m_pipeline;
+    vpu::TestModel m_testModel;
+
+    const vpu::DataDesc m_defaultDescriptor = {1};
+};
+
+TEST_F(AddCopyForOutputsInsideNetwork, DynamicOutputWithoutConsumer) {
+    m_testModel.createInputs({m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor});
+    m_testModel.createOutputs({m_defaultDescriptor, m_defaultDescriptor});
+    m_testModel.addStage({vpu::InputInfo::fromNetwork(0), vpu::InputInfo::fromNetwork(1)}, {vpu::OutputInfo::fromNetwork(0)});
+
+    auto shapeProducer = m_testModel.addStage(
+        {vpu::InputInfo::fromPrevStage(0, 0), vpu::InputInfo::constant(m_defaultDescriptor)},
+        {vpu::OutputInfo::intermediate(m_defaultDescriptor)});
+
+    m_testModel.addStage(
+        {
+            vpu::InputInfo::fromPrevStage(1, 0),
+            vpu::InputInfo::fromNetwork(2),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::fromNetwork(3),
+        },
+        {vpu::OutputInfo::fromNetwork(1)});
+
+    ASSERT_NO_THROW(m_testModel.getBaseModel()->connectDataWithShape(shapeProducer->output(0), m_testModel.getOutputs().back()));
+    ASSERT_NO_THROW(Compile());
+    CheckOutputs({{m_testModel.getOutputs().back(), shapeProducer->output(0)}});
+}
+
+TEST_F(AddCopyForOutputsInsideNetwork, StaticOutputWithConsumer) {
+    m_testModel.createInputs({m_defaultDescriptor, m_defaultDescriptor});
+    m_testModel.createOutputs({m_defaultDescriptor, m_defaultDescriptor});
+
+    m_testModel.addStage(
+        {
+            vpu::InputInfo::fromNetwork(0),
+            vpu::InputInfo::fromNetwork(1),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor)
+        },
+        {
+            vpu::OutputInfo::fromNetwork(0),
+            vpu::OutputInfo::fromNetwork(1)});
+
+    const auto shapeProducer = m_testModel.addStage(
+        {
+            vpu::InputInfo::fromPrevStage(0, 0),
+            vpu::InputInfo::constant(m_defaultDescriptor)
+        },
+        {
+            vpu::OutputInfo::intermediate(m_defaultDescriptor)
+        });
+
+    m_testModel.getBaseModel()->connectDataWithShape(shapeProducer->output(0), m_testModel.getOutputs().back());
+
+    ASSERT_NO_THROW(Compile());
+    CheckOutputs({{m_testModel.getOutputs().back(), shapeProducer->output(0)}});
+}
+
+TEST_F(AddCopyForOutputsInsideNetwork, DynamicOutputWithConsumer) {
+    m_testModel.createInputs({m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor});
+    m_testModel.createOutputs({m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor});
+
+    m_testModel.addStage(
+        {
+            vpu::InputInfo::fromNetwork(0),
+            vpu::InputInfo::fromNetwork(1),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor)
+        },
+        {
+            vpu::OutputInfo::fromNetwork(0),
+            vpu::OutputInfo::fromNetwork(1)});
+
+    const auto shapeProducer = m_testModel.addStage(
+        {
+            vpu::InputInfo::fromPrevStage(0, 0),
+            vpu::InputInfo::constant(m_defaultDescriptor)
+        },
+        {
+            vpu::OutputInfo::intermediate(m_defaultDescriptor)
+        });
+
+    m_testModel.getBaseModel()->connectDataWithShape(shapeProducer->output(0), m_testModel.getOutputs()[1]);
+
+    m_testModel.addStage(
+        {
+            vpu::InputInfo::fromNetwork(2),
+            vpu::InputInfo::fromPrevStage(1, 0),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::fromPrevStage(0, 1),
+        },
+        {vpu::OutputInfo::fromNetwork(2)});
+
+    ASSERT_NO_THROW(Compile());
+    CheckOutputs({{m_testModel.getOutputs()[1], shapeProducer->output(0)}});
+}
+
+TEST_F(AddCopyForOutputsInsideNetwork, StaticOutputWithConsumerAndDynamicOutputWithConsumer) {
+    m_testModel.createInputs({m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor});
+    m_testModel.createOutputs({m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor, m_defaultDescriptor});
+
+    m_testModel.addStage(
+        {
+            vpu::InputInfo::fromNetwork(0),
+            vpu::InputInfo::fromNetwork(1),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor)
+        },
+        {
+            vpu::OutputInfo::fromNetwork(0),
+            vpu::OutputInfo::fromNetwork(1)});
+
+    const auto shapeProducer_0 = m_testModel.addStage(
+        {
+            vpu::InputInfo::fromPrevStage(0, 0),
+            vpu::InputInfo::constant(m_defaultDescriptor)
+        },
+        {
+            vpu::OutputInfo::intermediate(m_defaultDescriptor)
+        });
+
+    m_testModel.getBaseModel()->connectDataWithShape(shapeProducer_0->output(0), m_testModel.getOutputs()[1]);
+
+    m_testModel.addStage(
+        {
+            vpu::InputInfo::fromNetwork(2),
+            vpu::InputInfo::fromNetwork(3),
+        },
+        {
+            vpu::OutputInfo::fromNetwork(3)
+        });
+
+    const auto shapeProducer_1 = m_testModel.addStage(
+        {
+            vpu::InputInfo::fromPrevStage(2, 0),
+            vpu::InputInfo::constant(m_defaultDescriptor)
+        },
+        {
+            vpu::OutputInfo::intermediate(m_defaultDescriptor)
+        });
+
+    m_testModel.addStage(
+        {
+            vpu::InputInfo::fromPrevStage(3, 0),
+            vpu::InputInfo::fromPrevStage(1, 0),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::constant(m_defaultDescriptor),
+            vpu::InputInfo::fromPrevStage(0, 1),
+        },
+        {vpu::OutputInfo::fromNetwork(2)});
+
+    m_testModel.getBaseModel()->connectDataWithShape(shapeProducer_1->output(0), m_testModel.getOutputs()[2]);
+
+    ASSERT_NO_THROW(Compile());
+    CheckOutputs({
+        {m_testModel.getOutputs()[1], shapeProducer_0->output(0)},
+        {m_testModel.getOutputs()[2], shapeProducer_1->output(0)}});
+}
+
+} // namespace