From 10df5907b5c245a603187c60cea385060fc00bf3 Mon Sep 17 00:00:00 2001 From: "Gladilov, Gleb" Date: Thu, 24 Sep 2020 12:40:10 +0300 Subject: [PATCH] [IE][VPU]: Fixes addCopyForOutputsInsideNetwork (#2393) * [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 * [IE][VPU]: Introduces tests on addCopyForOutputsInsideNetwork Signed-off-by: Gladilov, Gleb --- .../passes/add_copy_for_outputs_inside_network.cpp | 4 - .../unit/vpu/base/graph_transformer_tests.cpp | 9 + .../unit/vpu/base/graph_transformer_tests.hpp | 5 +- .../add_copy_for_outputs_inside_network.cpp | 217 +++++++++++++++++++++ 4 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 inference-engine/tests/unit/vpu/middleend_tests/passes_tests/add_copy_for_outputs_inside_network.cpp diff --git a/inference-engine/src/vpu/graph_transformer/src/middleend/passes/add_copy_for_outputs_inside_network.cpp b/inference-engine/src/vpu/graph_transformer/src/middleend/passes/add_copy_for_outputs_inside_network.cpp index 859b267..a81a93f 100644 --- a/inference-engine/src/vpu/graph_transformer/src/middleend/passes/add_copy_for_outputs_inside_network.cpp +++ b/inference-engine/src/vpu/graph_transformer/src/middleend/passes/add_copy_for_outputs_inside_network.cpp @@ -44,10 +44,6 @@ public: newIntermediateData, outputData, "addCopyForOutputsInsideNetwork"); - - if (const auto& parentShapeEdge = outputData->parentDataToShapeEdge()) { - model->connectDataWithShape(parentShapeEdge->parent(), newIntermediateData); - } } } diff --git a/inference-engine/tests/unit/vpu/base/graph_transformer_tests.cpp b/inference-engine/tests/unit/vpu/base/graph_transformer_tests.cpp index f58816c..efe885d 100644 --- a/inference-engine/tests/unit/vpu/base/graph_transformer_tests.cpp +++ b/inference-engine/tests/unit/vpu/base/graph_transformer_tests.cpp @@ -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& 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)); } diff --git a/inference-engine/tests/unit/vpu/base/graph_transformer_tests.hpp b/inference-engine/tests/unit/vpu/base/graph_transformer_tests.hpp index 5c5557b..e8e7d45 100644 --- a/inference-engine/tests/unit/vpu/base/graph_transformer_tests.hpp +++ b/inference-engine/tests/unit/vpu/base/graph_transformer_tests.hpp @@ -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 index 0000000..dd97d71 --- /dev/null +++ b/inference-engine/tests/unit/vpu/middleend_tests/passes_tests/add_copy_for_outputs_inside_network.cpp @@ -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& parentShapes = {}, const vpu::DataMap& 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 -- 2.7.4