Support opset-11 Range CPU kernel (#1980)

* Initial commit

* Update

* Update

* Update

* Add tests to MKLDNN exclsuion

* Update

* PR feedback

* Revert
This commit is contained in:
Hariharan Seshadri 2019-10-04 09:34:14 -07:00 committed by GitHub
parent a7414287a9
commit 534660bf2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 198 additions and 229 deletions

View file

@ -1,90 +0,0 @@
#include "range.h"
#include "onnx/defs/schema.h"
#include <cmath>
namespace onnxruntime {
namespace contrib {
template <typename T>
static Status ComputeRange(OpKernelContext* ctx) {
auto& start_tensor = *ctx->Input<Tensor>(0);
auto& limit_tensor = *ctx->Input<Tensor>(1);
auto delta_tensor_ptr = ctx->Input<Tensor>(2);
if (!start_tensor.Shape().IsScalar()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"start in Range operator should be scalar like tensor, yet got shape:",
start_tensor.Shape());
}
if (!limit_tensor.Shape().IsScalar()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"limit in Range operator should be scalar like tensor, yet got shape:",
limit_tensor.Shape());
}
if (delta_tensor_ptr != nullptr && !delta_tensor_ptr->Shape().IsScalar()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"delta in Range operator should be scalar like tensor, yet got shape:",
delta_tensor_ptr->Shape());
}
T start = *start_tensor.template Data<T>();
T limit = *limit_tensor.template Data<T>();
T delta = (delta_tensor_ptr == nullptr) ? T{1} : *(delta_tensor_ptr->template Data<T>());
if (delta == T{0}) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "delta in Range operator can not be zero!");
}
int64_t n = static_cast<int64_t>(ceil((1.0 * (limit - start)) / delta));
if (n <= 0) n = 0;
TensorShape shape = {n};
T* y = ctx->Output(0, shape)->template MutableData<T>();
for (int64_t i = 0; i < n; ++i) {
*y++ = start;
start += delta;
}
return Status::OK();
}
Status Range::Compute(OpKernelContext* ctx) const {
auto input_tensor = ctx->Input<Tensor>(0);
if (input_tensor == nullptr) return Status(common::ONNXRUNTIME, common::FAIL, "input count mismatch");
auto data_type = input_tensor->DataType();
if (data_type == DataTypeImpl::GetType<int32_t>()) {
return ComputeRange<int32_t>(ctx);
}
else if (data_type == DataTypeImpl::GetType<int16_t>()) {
return ComputeRange<int16_t>(ctx);
}
else if (data_type == DataTypeImpl::GetType<int64_t>()) {
return ComputeRange<int64_t>(ctx);
}
else if (data_type == DataTypeImpl::GetType<float>()) {
return ComputeRange<float>(ctx);
}
else if (data_type == DataTypeImpl::GetType<double>()) {
return ComputeRange<double>(ctx);
}
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Unsupportted tensor data type:",
data_type);
}
/* Range operator */
ONNX_OPERATOR_KERNEL_EX(
Range, //name
kMSDomain,
1,
kCpuExecutionProvider,
KernelDefBuilder().TypeConstraint("T", {
DataTypeImpl::GetTensorType<float>(),
DataTypeImpl::GetTensorType<double>(),
DataTypeImpl::GetTensorType<int16_t>(),
DataTypeImpl::GetTensorType<int32_t>(),
DataTypeImpl::GetTensorType<int64_t>()}),
Range);
} // namespace contrib
} // namespace onnxruntime

View file

@ -394,6 +394,7 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Ge
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, GatherElements);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Pad);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, GatherND);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Range);
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Unique);
void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) {
@ -1011,6 +1012,7 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) {
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, GatherElements)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Pad)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, GatherND)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Range)>,
BuildKernelCreateInfo<ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Unique)>};
for (auto& function_table_entry : function_table) {

View file

@ -0,0 +1,103 @@
#include "range.h"
#include <cmath>
namespace onnxruntime {
// Register a kernel for kMsDomain (contrib op) Range
#ifndef DISABLE_CONTRIB_OPS
namespace contrib {
// TODO: Remove this contrib kernel registration and the schema from the appropriate places
// once Keras Mask RCNN is shipped with all ONNX domain ops
// Currently this kernel is required to support Keras Mask-RCNN
ONNX_OPERATOR_KERNEL_EX(
Range, //name
kMSDomain,
1,
kCpuExecutionProvider,
KernelDefBuilder().TypeConstraint("T", {DataTypeImpl::GetTensorType<float>(),
DataTypeImpl::GetTensorType<double>(),
DataTypeImpl::GetTensorType<int16_t>(),
DataTypeImpl::GetTensorType<int32_t>(),
DataTypeImpl::GetTensorType<int64_t>()}),
Range);
} // namespace contrib
#endif
ONNX_CPU_OPERATOR_KERNEL(
Range,
11,
KernelDefBuilder().TypeConstraint("T", {DataTypeImpl::GetTensorType<float>(),
DataTypeImpl::GetTensorType<double>(),
DataTypeImpl::GetTensorType<int16_t>(),
DataTypeImpl::GetTensorType<int32_t>(),
DataTypeImpl::GetTensorType<int64_t>()}),
Range);
template <typename T>
static Status ComputeRange(OpKernelContext* ctx) {
const auto& start_tensor = *ctx->Input<Tensor>(0);
const auto& limit_tensor = *ctx->Input<Tensor>(1);
const auto* delta_tensor_ptr = ctx->Input<Tensor>(2);
if (!start_tensor.Shape().IsScalar()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"start in Range operator should be scalar like tensor, yet got shape:",
start_tensor.Shape());
}
if (!limit_tensor.Shape().IsScalar()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"limit in Range operator should be scalar like tensor, yet got shape:",
limit_tensor.Shape());
}
if (delta_tensor_ptr != nullptr && !delta_tensor_ptr->Shape().IsScalar()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"delta in Range operator should be scalar like tensor, yet got shape:",
delta_tensor_ptr->Shape());
}
T start = *start_tensor.template Data<T>();
T limit = *limit_tensor.template Data<T>();
T delta = (delta_tensor_ptr == nullptr) ? T{1} : *(delta_tensor_ptr->template Data<T>());
if (delta == T{0}) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "delta in Range operator can not be zero!");
}
int64_t n = static_cast<int64_t>(ceil((1.0 * (limit - start)) / delta));
if (n <= 0)
n = 0;
TensorShape shape = {n};
T* y = ctx->Output(0, shape)->template MutableData<T>();
for (int64_t i = 0; i < n; ++i) {
*y++ = start;
start += delta;
}
return Status::OK();
}
Status Range::Compute(OpKernelContext* ctx) const {
const auto* input_tensor = ctx->Input<Tensor>(0);
if (input_tensor == nullptr) return Status(common::ONNXRUNTIME, common::FAIL, "input count mismatch");
auto data_type = input_tensor->DataType();
if (data_type == DataTypeImpl::GetType<int32_t>()) {
return ComputeRange<int32_t>(ctx);
} else if (data_type == DataTypeImpl::GetType<float>()) {
return ComputeRange<float>(ctx);
} else if (data_type == DataTypeImpl::GetType<int64_t>()) {
return ComputeRange<int64_t>(ctx);
} else if (data_type == DataTypeImpl::GetType<double>()) {
return ComputeRange<double>(ctx);
} else if (data_type == DataTypeImpl::GetType<int16_t>()) {
return ComputeRange<int16_t>(ctx);
}
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Range op: Unsupported tensor data type:", data_type);
}
} // namespace onnxruntime

View file

@ -8,7 +8,6 @@
#include "core/framework/tensor.h"
namespace onnxruntime {
namespace contrib {
class Range : public OpKernel {
public:
@ -17,5 +16,4 @@ class Range : public OpKernel {
Status Compute(OpKernelContext* context) const override;
};
} // namespace contrib
} // namespace onnxruntime

View file

@ -1,133 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "gtest/gtest.h"
#include "test/providers/provider_test_utils.h"
namespace onnxruntime {
namespace test {
TEST(RangeTest, PositiveInt32DeltaDefault) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<int32_t> start = {0};
std::vector<int32_t> limit = {5};
std::vector<int32_t> expected_output = {0, 1, 2, 3, 4};
test.AddInput<int32_t>("start", {1}, start);
test.AddInput<int32_t>("limit", {1}, limit);
test.AddOutput<int32_t>("Y", {5LL}, expected_output);
test.Run();
}
TEST(RangeTest, PositiveInt32Delta_0) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<int32_t> start = {0};
std::vector<int32_t> limit = {10};
std::vector<int32_t> delta = {2};
std::vector<int32_t> expected_output = {0, 2, 4, 6, 8};
test.AddInput<int32_t>("start", {1}, start);
test.AddInput<int32_t>("limit", {1}, limit);
test.AddInput<int32_t>("delta", {1}, delta);
test.AddOutput<int32_t>("Y", {5LL}, expected_output);
test.Run();
}
TEST(RangeTest, PositiveInt32Delta_1) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<int32_t> start = {0};
std::vector<int32_t> limit = {9};
std::vector<int32_t> delta = {2};
std::vector<int32_t> expected_output = {0, 2, 4, 6, 8};
test.AddInput<int32_t>("start", {1}, start);
test.AddInput<int32_t>("limit", {1}, limit);
test.AddInput<int32_t>("delta", {1}, delta);
test.AddOutput<int32_t>("Y", {5LL}, expected_output);
test.Run();
}
TEST(RangeTest, PositiveInt32Delta_2) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<int32_t> start = {1};
std::vector<int32_t> limit = {2};
std::vector<int32_t> delta = {2};
std::vector<int32_t> expected_output = {1};
test.AddInput<int32_t>("start", {1}, start);
test.AddInput<int32_t>("limit", {1}, limit);
test.AddInput<int32_t>("delta", {1}, delta);
test.AddOutput<int32_t>("Y", {1LL}, expected_output);
test.Run();
}
TEST(RangeTest, Int32ScalarNegativeDelta_0) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<int32_t> start = {2};
std::vector<int32_t> limit = {-9};
std::vector<int32_t> delta = {-2};
std::vector<int32_t> expected_output = {2, 0, -2, -4, -6, -8};
test.AddInput<int32_t>("start", {}, start);
test.AddInput<int32_t>("limit", {}, limit);
test.AddInput<int32_t>("delta", {}, delta);
test.AddOutput<int32_t>("Y", {6LL}, expected_output);
test.Run();
}
TEST(RangeTest, Int32ScalarNegativeDelta_1) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<int32_t> start = {2};
std::vector<int32_t> limit = {9};
std::vector<int32_t> delta = {-2};
std::vector<int32_t> expected_output = {};
test.AddInput<int32_t>("start", {}, start);
test.AddInput<int32_t>("limit", {}, limit);
test.AddInput<int32_t>("delta", {}, delta);
test.AddOutput<int32_t>("Y", {0}, expected_output);
test.Run();
}
TEST(RangeTest, ScalarFloatNegativeDelta) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<float> start = {2.0f};
std::vector<float> limit = {-8.1f};
std::vector<float> delta = {-2.0f};
std::vector<float> expected_output = {2.0f, 0.0f, -2.0f, -4.0f, -6.0f, -8.0f};
test.AddInput<float>("start", {}, start);
test.AddInput<float>("limit", {}, limit);
test.AddInput<float>("delta", {}, delta);
test.AddOutput<float>("Y", {6LL}, expected_output);
test.Run();
}
TEST(RangeTest, SameStartAndLimit) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<float> start = {2.0f};
std::vector<float> limit = {2.0f};
std::vector<float> expected_output = {};
test.AddInput<float>("start", {}, start);
test.AddInput<float>("limit", {}, limit);
test.AddOutput<float>("Y", {0}, expected_output);
test.Run();
}
TEST(RangeTest, AlmostSameStartAndLimitHighDelta) {
OpTester test("Range", 1, onnxruntime::kMSDomain);
std::vector<float> start = {2.0f};
std::vector<float> limit = {2.01f};
std::vector<float> delta = {1000000.0f};
std::vector<float> expected_output = {2.0f};
test.AddInput<float>("start", {}, start);
test.AddInput<float>("limit", {}, limit);
test.AddInput<float>("delta", {}, delta);
test.AddOutput<float>("Y", {1}, expected_output);
test.Run();
}
} // namespace test
} // namespace onnxruntime

View file

@ -394,10 +394,6 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
{"top_k_negative_axis", "TopK(11) not implemented yet"},
{"unique_not_sorted_without_axis", "Expected data for 'Y' is incorrect and in sorted order."},
{"cumsum_1d_reverse_exclusive", "only failing linux GPU CI. Likely build error."},
{"range_float_type_positive_delta", "not implemented yet"},
{"range_float_type_positive_delta_expanded", "not implemented yet"},
{"range_int32_type_negative_delta", "not implemented yet"},
{"range_int32_type_negative_delta_expanded", "not implemented yet"},
{"det_2d", "not implemented yet"},
{"det_nd", "not implemented yet"},
{"resize_downsample_scales_cubic_A_n0p5_exclude_outside", "not implemented yet"},
@ -500,6 +496,8 @@ int real_main(int argc, char* argv[], Ort::Env& env) {
broken_tests.insert({"tf_mobilenet_v1_1.0_224", "result mismatch"});
broken_tests.insert({"mobilenetv2-1.0", "result mismatch"});
broken_tests.insert({"candy", "result mismatch"});
broken_tests.insert({"range_float_type_positive_delta_expanded", "get unknown exception from MKLDNN EP"});
broken_tests.insert({"range_int32_type_negative_delta_expanded", "get unknown exception from MKLDNN EP"});
#endif
#ifdef USE_OPENVINO

View file

@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "gtest/gtest.h"
#include "test/providers/provider_test_utils.h"
namespace onnxruntime {
namespace test {
template <typename T>
static void RunTest(
T start,
T limit,
T delta,
const std::vector<int64_t>& output_dims,
const std::vector<T>& output) {
// ONNX domain opset-11
OpTester test1("Range", 11);
test1.AddInput<T>("start", {}, {start});
test1.AddInput<T>("limit", {}, {limit});
test1.AddInput<T>("delta", {}, {delta});
test1.AddOutput<T>("output", output_dims, output);
// NGraph does not yet support opset-11 and builds break on this test, hence exclude the EP
test1.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider});
#ifndef DISABLE_CONTRIB_OPS
// MSFT domain opset-1 (contrib op)
OpTester test2("Range", 1, kMSDomain);
test2.AddInput<T>("start", {}, {start});
test2.AddInput<T>("limit", {}, {limit});
if (delta != T{1}) // only contrib schema allows optional 'delta' input
test2.AddInput<T>("delta", {}, {delta});
test2.AddOutput<T>("output", output_dims, output);
test2.Run();
#endif
} // namespace test
TEST(RangeTest, Int32_DeltaDefault) {
RunTest<int32_t>(0, 5, 1, {5}, {0, 1, 2, 3, 4});
}
TEST(RangeTest, Int64_DeltaDefault) {
RunTest<int64_t>(0, 5, 1, {5}, {0, 1, 2, 3, 4});
}
TEST(RangeTest, Float_DeltaDefault) {
RunTest<float>(0.f, 5.f, 1.f, {5}, {0.f, 1.f, 2.f, 3.f, 4.f});
}
TEST(RangeTest, Double_DeltaDefault) {
RunTest<double>(0., 5., 1., {5}, {0., 1., 2., 3., 4.});
}
TEST(RangeTest, Int32_Delta_NonDefault) {
RunTest<int32_t>(0, 10, 2, {5}, {0, 2, 4, 6, 8});
}
TEST(RangeTest, Int64_Delta_NonDefault_0) {
RunTest<int64_t>(0, 9, 2, {5}, {0, 2, 4, 6, 8});
}
TEST(RangeTest, Int64_Delta_NonDefault_1) {
RunTest<int64_t>(1, 2, 2, {1}, {1});
}
TEST(RangeTest, Int32_NegativeDelta_0) {
RunTest<int32_t>(2, -9, -2, {6}, {2, 0, -2, -4, -6, -8});
}
TEST(RangeTest, Int32_NegativeDelta_1) {
RunTest<int32_t>(2, 9, -2, {0}, {});
}
TEST(RangeTest, Float_NegativeDelta_0) {
RunTest<float>(2.0f, -8.1f, -2.0f, {6}, {2.0f, 0.0f, -2.0f, -4.0f, -6.0f, -8.0f});
}
TEST(RangeTest, Float_SameStartAndLimit) {
RunTest<float>(2.0f, 2.0f, 1, {0}, {});
}
TEST(RangeTest, AlmostSameStartAndLimitHighDelta) {
RunTest<float>(2.0f, 2.01f, 1000000.0f, {1}, {2.0f});
}
} // namespace test
} // namespace onnxruntime