[CPU][DS] Dynamic shapes support for Range (#7929)

This commit is contained in:
Yury Gaydaychuk 2021-10-28 12:54:01 +03:00 committed by GitHub
parent c1a352a136
commit ab6f9c670e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 242 additions and 42 deletions

View File

@ -3,7 +3,6 @@
//
#include <string>
#include <ngraph/opsets/opset1.hpp>
#include "ie_parallel.hpp"
#include "mkldnn_range_node.h"
@ -14,20 +13,10 @@ using namespace InferenceEngine;
bool MKLDNNRangeNode::isSupportedOperation(const std::shared_ptr<const ngraph::Node>& 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<const ngraph::opset1::Constant>(op->get_input_node_shared_ptr(RANGE_START)) == nullptr ||
std::dynamic_pointer_cast<const ngraph::opset1::Constant>(op->get_input_node_shared_ptr(RANGE_LIMIT)) == nullptr ||
std::dynamic_pointer_cast<const ngraph::opset1::Constant>(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<ngraph::Node>& 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 <typename data_t>
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 = &delta;
*startPtr = reinterpret_cast<const data_t *>(getParentEdgeAt(RANGE_START)->getMemoryPtr()->GetPtr())[0];
*stopPtr = reinterpret_cast<const data_t *>(getParentEdgeAt(RANGE_LIMIT)->getMemoryPtr()->GetPtr())[0];
*stepPtr = reinterpret_cast<const data_t *>(getParentEdgeAt(RANGE_DELTA)->getMemoryPtr()->GetPtr())[0];
const data_t span = *stopPtr - *startPtr;
const data_t step = *stepPtr;
if (std::is_same<data_t, int>::value) {
int iSpan = static_cast<int>(span);
int iStep = static_cast<int>(step);
return static_cast<size_t>(div_up(iSpan < 0 ? -iSpan : iSpan, iStep < 0 ? -iStep : iStep));
} else {
return static_cast<size_t>(std::ceil(std::fabs(span) / std::fabs(step)));
}
}
template <typename data_t>
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<data_t>(&start, nullptr, &delta);
if (isDynamicNode()) {
VectorDims newOutputShape {work_amount_dst};
getChildEdgeAt(0)->getMemoryPtr()->redefineDesc(getBaseMemDescAtOutputPort(0)->cloneWithNewDims(newOutputShape));
}
data_t* dst_data = reinterpret_cast<data_t *>(getChildEdgesAtPort(0)[0]->getMemoryPtr()->GetPtr());
data_t start = reinterpret_cast<const data_t *>(getParentEdgeAt(RANGE_START)->getMemoryPtr()->GetPtr())[0];
data_t limit = reinterpret_cast<const data_t *>(getParentEdgeAt(RANGE_LIMIT)->getMemoryPtr()->GetPtr())[0];
data_t delta = reinterpret_cast<const data_t *>(getParentEdgeAt(RANGE_DELTA)->getMemoryPtr()->GetPtr())[0];
size_t work_amount_dst = static_cast<size_t>(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;
}

View File

@ -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<const ngraph::Node>& op, std::string& errorMessage) noexcept;
template <typename data_t>
InferenceEngine::StatusCode rangeKernel() noexcept;
template <typename data_t>
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;

View File

@ -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<ngraph::PartialShape>, std::vector<std::vector<ngraph::Shape>>>, // input shape
std::tuple<float, float, float>, // 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<RangeLayerCPUTestParamsSet>,
virtual public LayerTestsUtils::LayerTestsCommon, public CPUTestsBase {
float start = 0;
float stop = 0;
float step = 0;
public:
static std::string getTestCaseName(testing::TestParamInfo<RangeLayerCPUTestParamsSet> obj) {
CPULayerTestsDefinitions::RangeLayerTestParams basicParamsSet;
CPUSpecificParams cpuParams;
std::tie(basicParamsSet, cpuParams) = obj.param;
std::string td;
Precision netPrc = Precision::FP32;
std::pair<std::vector<ngraph::PartialShape>, std::vector<std::vector<ngraph::Shape>>> shapes;
RangeSpecificParams rangePar;
std::tie(rangePar, netPrc, td) = basicParamsSet;
std::tuple<float, float, float> 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<ngraph::PartialShape>, std::vector<std::vector<ngraph::Shape>>> shapes;
std::tie(rangeParams, inPrc, targetDevice) = basicParamsSet;
std::tuple<float, float, float> 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<ngraph::opset5::Parameter>(ngNetPrc, ngraph::Shape{});
auto stopPar = std::make_shared<ngraph::opset5::Parameter>(ngNetPrc, ngraph::Shape{});
auto stepPar = std::make_shared<ngraph::opset5::Parameter>(ngNetPrc, ngraph::Shape{});
auto range = std::make_shared<ngraph::opset4::Range>(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<ngraph::opset3::Result>(range)};
function = std::make_shared<ngraph::Function>(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<CPUSpecificParams> filterCPUInfoForDevice() {
return std::vector<CPUSpecificParams> {CPUSpecificParams{{}, {x}, {}, {}}};
}
const std::vector<InferenceEngine::Precision> netPrecisions = {
InferenceEngine::Precision::FP32,
InferenceEngine::Precision::I32
};
const std::vector<InferenceEngine::Precision> outputType = {
InferenceEngine::Precision::FP32,
InferenceEngine::Precision::I32
};
std::vector<std::pair<std::vector<ngraph::PartialShape>, std::vector<std::vector<ngraph::Shape>>>> inShapesDynamic = {
{{ngraph::PartialShape(), ngraph::PartialShape(), ngraph::PartialShape()},
{{ngraph::Shape{}, ngraph::Shape{}, ngraph::Shape{}}, {ngraph::Shape{}, ngraph::Shape{}, ngraph::Shape{}}}}
};
std::vector<std::pair<std::vector<ngraph::PartialShape>, std::vector<std::vector<ngraph::Shape>>>> inShapesPseudoStatic = {
{{}, {{ngraph::Shape{}, ngraph::Shape{}, ngraph::Shape{}}}}
};
const std::vector<std::tuple<float, float, float>> rangeInputValues = {
std::tuple<float, float, float> {1.0, -5.0, -1.0},
std::tuple<float, float, float> {1.0, 10.0, 1.2},
std::tuple<float, float, float> {1.1, 12.2, 1.1},
std::tuple<float, float, float> {1.1, -5.1, -1.1},
std::tuple<float, float, float> {1.0, 5.0, 2.0},
std::tuple<float, float, float> {10.0, 6.0, -3.0},
std::tuple<float, float, float> {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

View File

@ -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",

View File

@ -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

View File

@ -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",