From ab6f9c670e4b7a767a64bc2b83fd881e019c1535 Mon Sep 17 00:00:00 2001 From: Yury Gaydaychuk Date: Thu, 28 Oct 2021 12:54:01 +0300 Subject: [PATCH] [CPU][DS] Dynamic shapes support for Range (#7929) --- .../mkldnn_plugin/nodes/mkldnn_range_node.cpp | 56 ++--- .../mkldnn_plugin/nodes/mkldnn_range_node.h | 12 +- .../plugin/cpu/single_layer_tests/range.cpp | 200 ++++++++++++++++++ .../python/tests/test_onnx/test_backend.py | 6 - .../test_ngraph/test_sequence_processing.py | 4 +- .../test_onnx/test_backend.py | 6 - 6 files changed, 242 insertions(+), 42 deletions(-) create mode 100644 inference-engine/tests/functional/plugin/cpu/single_layer_tests/range.cpp diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.cpp b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.cpp index 41945d4c614..74eca2779fb 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.cpp +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.cpp @@ -3,7 +3,6 @@ // #include - #include #include "ie_parallel.hpp" #include "mkldnn_range_node.h" @@ -14,20 +13,10 @@ using namespace InferenceEngine; bool MKLDNNRangeNode::isSupportedOperation(const std::shared_ptr& op, std::string& errorMessage) noexcept { try { - if (isDynamicNgraphNode(op)) { - errorMessage = "Doesn't support op with dynamic shapes"; - return false; - } if (!MKLDNNPlugin::one_of(op->get_type_info(), ngraph::op::v0::Range::get_type_info_static(), ngraph::op::v4::Range::get_type_info_static())) { errorMessage = "Only opset1 and opset4 Range operation is supported"; return false; } - if (std::dynamic_pointer_cast(op->get_input_node_shared_ptr(RANGE_START)) == nullptr || - std::dynamic_pointer_cast(op->get_input_node_shared_ptr(RANGE_LIMIT)) == nullptr || - std::dynamic_pointer_cast(op->get_input_node_shared_ptr(RANGE_DELTA)) == nullptr) { - errorMessage = "Only const inputs for Range operation is supported"; - return false; - } } catch (...) { return false; } @@ -58,9 +47,9 @@ MKLDNNRangeNode::MKLDNNRangeNode(const std::shared_ptr& op, const if (ngraph::shape_size(delta_dims) != 1) IE_THROW() << errorPrefix << " has delta scalar with more than 1 value"; - SizeVector dst_dims = op->get_output_shape(0); - if (dst_dims.size() > 1) - IE_THROW() << errorPrefix << " has unsupported rank for output: " << dst_dims.size(); + size_t dstRank = op->get_output_partial_shape(0).size(); + if (dstRank > 1) + IE_THROW() << errorPrefix << " has unsupported rank for output: " << dstRank; } void MKLDNNRangeNode::initSupportedPrimitiveDescriptors() { @@ -111,30 +100,47 @@ void MKLDNNRangeNode::execute(mkldnn::stream strm) { IE_THROW() << errorMsg; } } - +template +size_t MKLDNNRangeNode::getWorkAmount(data_t *startPtr, data_t *stopPtr, data_t *stepPtr) const noexcept { + data_t start = 0, limit = 0, delta = 0; + if (startPtr == nullptr) + startPtr = &start; + if (stopPtr == nullptr) + stopPtr = &limit; + if (stepPtr == nullptr) + stepPtr = δ + *startPtr = reinterpret_cast(getParentEdgeAt(RANGE_START)->getMemoryPtr()->GetPtr())[0]; + *stopPtr = reinterpret_cast(getParentEdgeAt(RANGE_LIMIT)->getMemoryPtr()->GetPtr())[0]; + *stepPtr = reinterpret_cast(getParentEdgeAt(RANGE_DELTA)->getMemoryPtr()->GetPtr())[0]; + const data_t span = *stopPtr - *startPtr; + const data_t step = *stepPtr; + if (std::is_same::value) { + int iSpan = static_cast(span); + int iStep = static_cast(step); + return static_cast(div_up(iSpan < 0 ? -iSpan : iSpan, iStep < 0 ? -iStep : iStep)); + } else { + return static_cast(std::ceil(std::fabs(span) / std::fabs(step))); + } +} template InferenceEngine::StatusCode MKLDNNRangeNode::rangeKernel() noexcept { - size_t dst_size = getChildEdgesAtPort(0)[0]->getMemory().getStaticDims()[0]; + data_t start = 0, delta = 0; + size_t work_amount_dst = getWorkAmount(&start, nullptr, &delta); + if (isDynamicNode()) { + VectorDims newOutputShape {work_amount_dst}; + getChildEdgeAt(0)->getMemoryPtr()->redefineDesc(getBaseMemDescAtOutputPort(0)->cloneWithNewDims(newOutputShape)); + } data_t* dst_data = reinterpret_cast(getChildEdgesAtPort(0)[0]->getMemoryPtr()->GetPtr()); - data_t start = reinterpret_cast(getParentEdgeAt(RANGE_START)->getMemoryPtr()->GetPtr())[0]; - data_t limit = reinterpret_cast(getParentEdgeAt(RANGE_LIMIT)->getMemoryPtr()->GetPtr())[0]; - data_t delta = reinterpret_cast(getParentEdgeAt(RANGE_DELTA)->getMemoryPtr()->GetPtr())[0]; - size_t work_amount_dst = static_cast(std::floor(std::abs((limit - start) / delta))); - if (work_amount_dst != dst_size) - return PARAMETER_MISMATCH; - parallel_nt(0, [&](const int ithr, const int nthr) { size_t iwork = 0, end = 0; splitter(work_amount_dst, nthr, ithr, iwork, end); data_t dst_value = start + iwork * delta; - for (; iwork < end; ++iwork, dst_value += delta) { dst_data[iwork] = dst_value; } }); return OK; } - bool MKLDNNRangeNode::created() const { return getType() == Range; } diff --git a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.h b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.h index 36b3f4aaeef..3ee5e400221 100644 --- a/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.h +++ b/inference-engine/src/mkldnn_plugin/nodes/mkldnn_range_node.h @@ -15,14 +15,22 @@ public: void getSupportedDescriptors() override {}; void initSupportedPrimitiveDescriptors() override; - void createPrimitive() override {}; + void createPrimitive() override { + if (inputShapesDefined()) + updateLastInputDims(); + }; void execute(mkldnn::stream strm) override; bool created() const override; - + bool needPrepareParams() const override {return false;}; + bool needShapeInfer() const override {return false;}; + void executeDynamicImpl(mkldnn::stream strm) override { execute(strm); } static bool isSupportedOperation(const std::shared_ptr& op, std::string& errorMessage) noexcept; template InferenceEngine::StatusCode rangeKernel() noexcept; + template + size_t getWorkAmount(data_t *startPtr = nullptr, data_t *stopPtr = nullptr, data_t *stepPtr = nullptr) const noexcept; + private: static const size_t RANGE_START = 0; static const size_t RANGE_LIMIT = 1; diff --git a/inference-engine/tests/functional/plugin/cpu/single_layer_tests/range.cpp b/inference-engine/tests/functional/plugin/cpu/single_layer_tests/range.cpp new file mode 100644 index 00000000000..8ba49f16978 --- /dev/null +++ b/inference-engine/tests/functional/plugin/cpu/single_layer_tests/range.cpp @@ -0,0 +1,200 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "test_utils/cpu_test_utils.hpp" + +#include "ngraph_functions/builders.hpp" +#include "ngraph_functions/utils/ngraph_helpers.hpp" + +using namespace InferenceEngine; +using namespace CPUTestUtils; + +namespace CPULayerTestsDefinitions { +typedef std::tuple< + std::pair, std::vector>>, // input shape + std::tuple, // start, limit, delta + Precision // output type +> RangeSpecificParams; + +typedef std::tuple< + RangeSpecificParams, + InferenceEngine::Precision, // Net precision + LayerTestsUtils::TargetDevice // Device name +> RangeLayerTestParams; + +typedef std::tuple< + CPULayerTestsDefinitions::RangeLayerTestParams, + CPUSpecificParams> RangeLayerCPUTestParamsSet; + +class RangeLayerCPUTest : public testing::WithParamInterface, + virtual public LayerTestsUtils::LayerTestsCommon, public CPUTestsBase { + float start = 0; + float stop = 0; + float step = 0; +public: + static std::string getTestCaseName(testing::TestParamInfo obj) { + CPULayerTestsDefinitions::RangeLayerTestParams basicParamsSet; + CPUSpecificParams cpuParams; + std::tie(basicParamsSet, cpuParams) = obj.param; + std::string td; + Precision netPrc = Precision::FP32; + std::pair, std::vector>> shapes; + + RangeSpecificParams rangePar; + std::tie(rangePar, netPrc, td) = basicParamsSet; + std::tuple rangeInputs; + InferenceEngine::Precision outPrc = Precision::FP32; + std::tie(shapes, rangeInputs, outPrc) = rangePar; + float start = std::get<0>(rangeInputs); + float stop = std::get<1>(rangeInputs); + float step = std::get<2>(rangeInputs); + + std::ostringstream result; + result << "RangeTest_" << std::to_string(obj.index) << "_"; + result << "NetPr_" << netPrc.name() << "_"; + result << "OutPr_" << outPrc.name() << "_"; + result << "Start_" << start << "_"; + result << "Stop_" << stop << "_"; + result << "Step_" << step << "_"; + result << CPUTestsBase::getTestCaseName(cpuParams); + result << CommonTestUtils::vec2str(shapes.second[0]) << "_"; + return result.str(); + } +protected: + void GenerateInputs() override { + // for correct work of fill_data_random() method + size_t blobFillingRange = (inPrc == Precision::FP32 ? 0 : 1); + inputs.clear(); + const auto& inputsInfo = executableNetwork.GetInputsInfo(); + const auto& functionParams = function->get_parameters(); + for (int i = 0; i < functionParams.size(); ++i) { + const float scalarVal = (i == 0 ? start : (i == 1 ? stop : step)); + const auto& param = functionParams[i]; + const auto infoIt = inputsInfo.find(param->get_friendly_name()); + GTEST_ASSERT_NE(infoIt, inputsInfo.cend()); + InferenceEngine::InputInfo::CPtr info = infoIt->second; + InferenceEngine::Blob::Ptr blob = nullptr; + if (!inputDynamicShapes.empty()) { + if (inputDynamicShapes[i].rank() != 0) { + InferenceEngine::DataPtr dataNew( + new InferenceEngine::Data(infoIt->first, info->getTensorDesc().getPrecision(), + targetStaticShapes[index][i], + info->getTensorDesc().getLayout())); + InferenceEngine::InputInfo infoNew; + infoNew.setInputData(dataNew); + blob = FuncTestUtils::createAndFillBlob(infoNew.getTensorDesc(), blobFillingRange, scalarVal); + } + } + if (blob == nullptr) { + blob = FuncTestUtils::createAndFillBlob((*info).getTensorDesc(), blobFillingRange, scalarVal); + } + inputs.push_back(blob); + } + } + + void SetUp() override { + CPULayerTestsDefinitions::RangeLayerTestParams basicParamsSet; + CPUSpecificParams cpuParams; + std::tie(basicParamsSet, cpuParams) = this->GetParam(); + std::tie(inFmts, outFmts, priority, selectedType) = cpuParams; + CPULayerTestsDefinitions::RangeSpecificParams rangeParams; + std::pair, std::vector>> shapes; + std::tie(rangeParams, inPrc, targetDevice) = basicParamsSet; + std::tuple rangeInputs; + + std::tie(shapes, rangeInputs, outPrc) = rangeParams; + targetStaticShapes = shapes.second; + inputDynamicShapes = shapes.first; + + start = std::get<0>(rangeInputs); + stop = std::get<1>(rangeInputs); + step = std::get<2>(rangeInputs); + auto ngOutPr = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(outPrc); + auto ngNetPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(inPrc); + auto startPar = std::make_shared(ngNetPrc, ngraph::Shape{}); + auto stopPar = std::make_shared(ngNetPrc, ngraph::Shape{}); + auto stepPar = std::make_shared(ngNetPrc, ngraph::Shape{}); + auto range = std::make_shared(startPar, stopPar, stepPar, ngOutPr); + range->get_rt_info() = getCPUInfo(); + selectedType = std::string("ref_any_") + (inPrc == outPrc ? inPrc.name() : "FP32"); + startPar->set_friendly_name("start"); + stopPar->set_friendly_name("stop"); + stepPar->set_friendly_name("step"); + + const ngraph::ResultVector results{std::make_shared(range)}; + function = std::make_shared(results, ngraph::ParameterVector { + startPar, stopPar, stepPar}, "Range"); + functionRefs = ngraph::clone_function(*function); + } +}; + +TEST_P(RangeLayerCPUTest, CompareWithRefs) { + SKIP_IF_CURRENT_TEST_IS_DISABLED() + Run(); + CheckPluginRelatedResults(executableNetwork, "Range"); +} + +namespace { + +/* CPU PARAMS */ +std::vector filterCPUInfoForDevice() { + return std::vector {CPUSpecificParams{{}, {x}, {}, {}}}; +} + +const std::vector netPrecisions = { + InferenceEngine::Precision::FP32, + InferenceEngine::Precision::I32 +}; +const std::vector outputType = { + InferenceEngine::Precision::FP32, + InferenceEngine::Precision::I32 +}; + +std::vector, std::vector>>> inShapesDynamic = { + {{ngraph::PartialShape(), ngraph::PartialShape(), ngraph::PartialShape()}, + {{ngraph::Shape{}, ngraph::Shape{}, ngraph::Shape{}}, {ngraph::Shape{}, ngraph::Shape{}, ngraph::Shape{}}}} +}; +std::vector, std::vector>>> inShapesPseudoStatic = { + {{}, {{ngraph::Shape{}, ngraph::Shape{}, ngraph::Shape{}}}} +}; + +const std::vector> rangeInputValues = { + std::tuple {1.0, -5.0, -1.0}, + std::tuple {1.0, 10.0, 1.2}, + std::tuple {1.1, 12.2, 1.1}, + std::tuple {1.1, -5.1, -1.1}, + std::tuple {1.0, 5.0, 2.0}, + std::tuple {10.0, 6.0, -3.0}, + std::tuple {5, 35, 5} +}; +const auto rangeParDynamic = ::testing::Combine( + ::testing::ValuesIn(inShapesDynamic), + ::testing::ValuesIn(rangeInputValues), + ::testing::ValuesIn(outputType) +); +const auto rangeParStatic = ::testing::Combine( + ::testing::ValuesIn(inShapesPseudoStatic), + ::testing::ValuesIn(rangeInputValues), + ::testing::ValuesIn(outputType) +); +const auto params3dDynamic = ::testing::Combine( + ::testing::Combine( + rangeParDynamic, + ::testing::ValuesIn(netPrecisions), + ::testing::Values(CommonTestUtils::DEVICE_CPU)), + ::testing::ValuesIn(filterCPUInfoForDevice())); +const auto params3dPseudoStatic = ::testing::Combine( + ::testing::Combine( + rangeParStatic, + ::testing::ValuesIn(netPrecisions), + ::testing::Values(CommonTestUtils::DEVICE_CPU)), + ::testing::ValuesIn(filterCPUInfoForDevice())); +// We don't check static case, because of constant folding, but we can use static shape for test infrastructure, +// however Range node will be dynamic, since inputs are parameters, not a constants +INSTANTIATE_TEST_SUITE_P(smoke_RangePseudoStaticLayoutTest, RangeLayerCPUTest, + params3dPseudoStatic, RangeLayerCPUTest::getTestCaseName); +INSTANTIATE_TEST_SUITE_P(smoke_RangeDynamicLayoutTest, RangeLayerCPUTest, + params3dDynamic, RangeLayerCPUTest::getTestCaseName); +} // namespace +} // namespace CPULayerTestsDefinitions diff --git a/runtime/bindings/python/tests/test_onnx/test_backend.py b/runtime/bindings/python/tests/test_onnx/test_backend.py index 89ba8a0cbb8..b2c2a4e2b6d 100644 --- a/runtime/bindings/python/tests/test_onnx/test_backend.py +++ b/runtime/bindings/python/tests/test_onnx/test_backend.py @@ -31,7 +31,6 @@ from tests import ( xfail_issue_39658, xfail_issue_39659, xfail_issue_39662, - xfail_issue_44848, xfail_issue_44851, xfail_issue_44854, xfail_issue_44858, @@ -390,11 +389,6 @@ tests_expected_to_fail = [ "OnnxBackendNodeModelTest.test_reduce_sum_keepdims_random_cpu", "OnnxBackendNodeModelTest.test_reduce_sum_negative_axes_keepdims_example_cpu", ), - ( - xfail_issue_44848, - "OnnxBackendNodeModelTest.test_range_float_type_positive_delta_cpu", - "OnnxBackendNodeModelTest.test_range_int32_type_negative_delta_cpu", - ), ( xfail_issue_44851, "OnnxBackendNodeModelTest.test_expand_dim_changed_cpu", diff --git a/runtime/bindings/python/tests_compatibility/test_ngraph/test_sequence_processing.py b/runtime/bindings/python/tests_compatibility/test_ngraph/test_sequence_processing.py index 6b96724569a..7bd9b767622 100644 --- a/runtime/bindings/python/tests_compatibility/test_ngraph/test_sequence_processing.py +++ b/runtime/bindings/python/tests_compatibility/test_ngraph/test_sequence_processing.py @@ -6,8 +6,7 @@ import numpy as np import ngraph as ng from tests_compatibility.runtime import get_runtime from tests_compatibility.test_ngraph.util import run_op_node -from tests_compatibility import (xfail_issue_47337, - xfail_issue_44848) +from tests_compatibility import (xfail_issue_47337) def test_onehot(): @@ -35,7 +34,6 @@ def test_one_hot(): assert np.allclose(result, excepted) -@xfail_issue_44848 def test_range(): start = 5 stop = 35 diff --git a/runtime/bindings/python/tests_compatibility/test_onnx/test_backend.py b/runtime/bindings/python/tests_compatibility/test_onnx/test_backend.py index 4143a0f4f43..782b3b1349f 100644 --- a/runtime/bindings/python/tests_compatibility/test_onnx/test_backend.py +++ b/runtime/bindings/python/tests_compatibility/test_onnx/test_backend.py @@ -30,7 +30,6 @@ from tests_compatibility import ( xfail_issue_39658, xfail_issue_39659, xfail_issue_39662, - xfail_issue_44848, xfail_issue_44851, xfail_issue_44854, xfail_issue_44858, @@ -349,11 +348,6 @@ tests_expected_to_fail = [ "OnnxBackendNodeModelTest.test_reduce_sum_keepdims_random_cpu", "OnnxBackendNodeModelTest.test_reduce_sum_negative_axes_keepdims_example_cpu", ), - ( - xfail_issue_44848, - "OnnxBackendNodeModelTest.test_range_float_type_positive_delta_cpu", - "OnnxBackendNodeModelTest.test_range_int32_type_negative_delta_cpu", - ), ( xfail_issue_44851, "OnnxBackendNodeModelTest.test_expand_dim_changed_cpu",