From d646dc73e45aa0fa36db679e92197a298198e627 Mon Sep 17 00:00:00 2001 From: ybrnathan <7902510+ybrnathan@users.noreply.github.com> Date: Mon, 16 Sep 2019 21:36:15 -0700 Subject: [PATCH] OpSet 11 Operator Update: Supporting Neg Axis (#1835) * OpSet 11 Update: Neg Axis handling for 5 ops: ArgMax, ArgMin, HardMax, LogSoftmax, Softmax. * Add unit test for ArgMax, ArgMin and HardMax * Add lp_norm and exclude nGraph from hardmax test. * Exclude nGraph from Neg Axis test. * Exclude nGraph from NegAxis test * fix excluded ep. * Fix nGraph test failure * add test.run --- .../providers/cpu/cpu_execution_provider.cc | 42 +++++++++----- .../core/providers/cpu/math/hardmax.cc | 16 ++++- onnxruntime/core/providers/cpu/math/hardmax.h | 3 - .../core/providers/cpu/math/logsoftmax.cc | 9 ++- .../core/providers/cpu/math/softmax.cc | 10 +++- onnxruntime/core/providers/cpu/nn/lp_norm.cc | 3 +- .../providers/cpu/reduction/reduction_ops.cc | 23 +++++++- .../test/providers/cpu/math/hardmax_test.cc | 38 ++++++++---- .../test/providers/cpu/nn/lp_norm_op_test.cc | 27 +++++++++ .../cpu/reduction/reduction_ops_test.cc | 58 ++++++++++++++++++- 10 files changed, 191 insertions(+), 38 deletions(-) diff --git a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc index 0df2802234..8c563084a3 100644 --- a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc +++ b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc @@ -102,15 +102,15 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, Asi class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, Acos); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, Atan); class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, Gemm); -class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Hardmax); -class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, LogSoftmax); +class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 10, Hardmax); +class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 10, LogSoftmax); class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 9, float, MatMul); class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 9, double, MatMul); class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, 9, int32_t, MatMul); class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, 9, uint32_t, MatMul); class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, 9, int64_t, MatMul); class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 9, 9, uint64_t, MatMul); -class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Softmax); +class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 10, Softmax); class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 9, TopK); class ONNX_OPERATOR_VERSIONED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, 9, BatchNormalization); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, Conv); @@ -153,10 +153,10 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, float, ReduceSumSquare); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, double, ReduceSumSquare); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, int32_t, ReduceSumSquare); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, float, ArgMax); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, int32_t, ArgMax); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, float, ArgMin); -class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, int32_t, ArgMin); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 10, float, ArgMax); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 10, int32_t, ArgMax); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 10, float, ArgMin); +class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 1, 10, int32_t, ArgMin); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, GRU); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, LSTM); class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 7, RNN); @@ -307,6 +307,13 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 10, Re // opset 11 class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Clip); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, uint8_t, DynamicQuantizeLinear); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, float, ArgMax); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, int32_t, ArgMax); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, float, ArgMin); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, int32_t, ArgMin); +class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Hardmax); +class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, LogSoftmax); +class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 11, Softmax); void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) { static const BuildKernelCreateInfoFn function_table[] = { @@ -394,15 +401,15 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, - BuildKernelCreateInfo, - BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, - BuildKernelCreateInfo, + BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, @@ -445,10 +452,10 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, - BuildKernelCreateInfo, - BuildKernelCreateInfo, - BuildKernelCreateInfo, - BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, @@ -599,6 +606,13 @@ void RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) { //opset 11 BuildKernelCreateInfo, BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, + BuildKernelCreateInfo, }; for (auto& function_table_entry : function_table) { diff --git a/onnxruntime/core/providers/cpu/math/hardmax.cc b/onnxruntime/core/providers/cpu/math/hardmax.cc index 67fc4ed241..8a3c0b0262 100644 --- a/onnxruntime/core/providers/cpu/math/hardmax.cc +++ b/onnxruntime/core/providers/cpu/math/hardmax.cc @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "core/providers/cpu/math/hardmax.h" +#include "core/providers/common.h" #include "core/util/math_cpuonly.h" #include "core/util/math.h" @@ -13,8 +14,9 @@ Status Hardmax::Compute(OpKernelContext* ctx) const { const TensorShape& input_shape = X->Shape(); const auto* Xdata = X->template Data(); - size_t tmpN = input_shape.SizeToDimension(axis_); - size_t tmpD = input_shape.SizeFromDimension(axis_); + auto axis = HandleNegativeAxis(axis_, input_shape.NumDimensions()); // handle negative and enforce axis is valid + size_t tmpN = input_shape.SizeToDimension(axis); + size_t tmpD = input_shape.SizeFromDimension(axis); // Math::RowwiseMax expects int N and D. if (tmpN * tmpD > INT32_MAX || tmpN > INT32_MAX || tmpD > INT32_MAX) { @@ -48,9 +50,17 @@ Status Hardmax::Compute(OpKernelContext* ctx) const { return Status::OK(); } -ONNX_CPU_OPERATOR_KERNEL( +ONNX_CPU_OPERATOR_VERSIONED_KERNEL( Hardmax, 1, + 10, + KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), + Hardmax); + +// Opset 11 starts to support Neg Axis. +ONNX_CPU_OPERATOR_KERNEL( + Hardmax, + 11, KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), Hardmax); diff --git a/onnxruntime/core/providers/cpu/math/hardmax.h b/onnxruntime/core/providers/cpu/math/hardmax.h index 745973b8db..2c3600bc6f 100644 --- a/onnxruntime/core/providers/cpu/math/hardmax.h +++ b/onnxruntime/core/providers/cpu/math/hardmax.h @@ -19,9 +19,6 @@ class Hardmax final : public OpKernel { if (status.IsOK()) { axis_ = gsl::narrow_cast(axis); } - - // if value was provided, make sure it was valid - ORT_ENFORCE(axis_ >= 0, "Invalid axis provided."); } Status Compute(OpKernelContext* context) const override; diff --git a/onnxruntime/core/providers/cpu/math/logsoftmax.cc b/onnxruntime/core/providers/cpu/math/logsoftmax.cc index 19fbb9897c..cbf2cf4083 100644 --- a/onnxruntime/core/providers/cpu/math/logsoftmax.cc +++ b/onnxruntime/core/providers/cpu/math/logsoftmax.cc @@ -42,10 +42,17 @@ Status LogSoftmax::Compute(OpKernelContext* ctx) const { return status; } -ONNX_CPU_OPERATOR_KERNEL( +ONNX_CPU_OPERATOR_VERSIONED_KERNEL( LogSoftmax, 1, + 10, KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), LogSoftmax); +// Opset 11 starts to support Neg Axis. +ONNX_CPU_OPERATOR_KERNEL( + LogSoftmax, + 11, + KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), + LogSoftmax); } // namespace onnxruntime diff --git a/onnxruntime/core/providers/cpu/math/softmax.cc b/onnxruntime/core/providers/cpu/math/softmax.cc index 542e20e79f..f3946c7c71 100644 --- a/onnxruntime/core/providers/cpu/math/softmax.cc +++ b/onnxruntime/core/providers/cpu/math/softmax.cc @@ -43,9 +43,17 @@ Status Softmax::Compute(OpKernelContext* ctx) const { return status; } -ONNX_CPU_OPERATOR_KERNEL( +ONNX_CPU_OPERATOR_VERSIONED_KERNEL( Softmax, 1, + 10, + KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), + Softmax); + +// Opset 11 starts to support Neg Axis. +ONNX_CPU_OPERATOR_KERNEL( + Softmax, + 11, KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), Softmax); diff --git a/onnxruntime/core/providers/cpu/nn/lp_norm.cc b/onnxruntime/core/providers/cpu/nn/lp_norm.cc index dc2c4c766a..27c1a4319e 100644 --- a/onnxruntime/core/providers/cpu/nn/lp_norm.cc +++ b/onnxruntime/core/providers/cpu/nn/lp_norm.cc @@ -3,6 +3,7 @@ #include "core/providers/cpu/nn/lp_norm.h" #include "core/util/math_cpuonly.h" +#include "core/providers/common.h" namespace onnxruntime { ONNX_CPU_OPERATOR_KERNEL( @@ -55,7 +56,7 @@ Status LpNorm::Compute(OpKernelContext* p_op_kernel_context) const { const TensorShape& input_shape = input->Shape(); Tensor* output = p_op_kernel_context->Output(0, input_shape); - const auto canonical_axis = axis_ != -1 ? axis_ : (input_shape.NumDimensions() - 1); + const auto canonical_axis = HandleNegativeAxis(axis_, static_cast(input_shape.NumDimensions())); const int64_t m = input_shape.GetDims()[canonical_axis]; const int64_t n = input_shape.Size() / m; const int64_t sf = input_shape.SizeFromDimension(canonical_axis + 1); diff --git a/onnxruntime/core/providers/cpu/reduction/reduction_ops.cc b/onnxruntime/core/providers/cpu/reduction/reduction_ops.cc index 648eba8b2e..1bbdb7473b 100644 --- a/onnxruntime/core/providers/cpu/reduction/reduction_ops.cc +++ b/onnxruntime/core/providers/cpu/reduction/reduction_ops.cc @@ -22,6 +22,23 @@ namespace onnxruntime { KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), \ x); +#define REGISTER_UNARY_ELEMENTWISE_VERSIONED_KERNEL(x, startVer, endVer) \ + ONNX_CPU_OPERATOR_VERSIONED_TYPED_KERNEL( \ + x, \ + startVer, \ + endVer, \ + float, \ + KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), \ + x); \ + \ + ONNX_CPU_OPERATOR_VERSIONED_TYPED_KERNEL( \ + x, \ + startVer, \ + endVer, \ + int32_t, \ + KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), \ + x); + #define REGISTER_UNARY_ELEMENTWISE_KERNEL_DOUBLE_ONLY(x, sinceVersion) \ ONNX_CPU_OPERATOR_TYPED_KERNEL( \ x, \ @@ -53,8 +70,10 @@ REGISTER_UNARY_ELEMENTWISE_KERNEL_INT64_ONLY(ReduceSum, 1); REGISTER_UNARY_ELEMENTWISE_KERNEL_DOUBLE_ONLY(ReduceSum, 1); REGISTER_UNARY_ELEMENTWISE_KERNEL(ReduceSumSquare, 1); REGISTER_UNARY_ELEMENTWISE_KERNEL_DOUBLE_ONLY(ReduceSumSquare, 1); -REGISTER_UNARY_ELEMENTWISE_KERNEL(ArgMax, 1); -REGISTER_UNARY_ELEMENTWISE_KERNEL(ArgMin, 1); +REGISTER_UNARY_ELEMENTWISE_VERSIONED_KERNEL(ArgMax, 1, 10); +REGISTER_UNARY_ELEMENTWISE_KERNEL(ArgMax, 11); +REGISTER_UNARY_ELEMENTWISE_VERSIONED_KERNEL(ArgMin, 1, 10); +REGISTER_UNARY_ELEMENTWISE_KERNEL(ArgMin, 11); // When all reduce axises located at the tail of the dims, quite general cases, transpose and extra // copy could be skiped to improve performance, if required by check_no_transpose = true; diff --git a/onnxruntime/test/providers/cpu/math/hardmax_test.cc b/onnxruntime/test/providers/cpu/math/hardmax_test.cc index 1cab384c25..1a0dfbd51d 100644 --- a/onnxruntime/test/providers/cpu/math/hardmax_test.cc +++ b/onnxruntime/test/providers/cpu/math/hardmax_test.cc @@ -23,7 +23,13 @@ static void RunTest(const std::vector& x_vals, test.AddInput("X", dimensions, x_vals); test.AddOutput("Y", dimensions, expected_vals); - test.Run(expect_result, expected_err_str); + + std::unordered_set excluded_eps; + if (axis < 0) { + excluded_eps.insert(kNGraphExecutionProvider); // NGraph EP cannot handle negative axis values + } + + test.Run(expect_result, expected_err_str, excluded_eps); } TEST(HardmaxOperator, Simple) { @@ -135,17 +141,27 @@ TEST(HardmaxOperator, ThreeDimsAxis2) { RunTest(x_vals_3dims, expected_vals, three_dimensions, /*axis*/ 2); } -TEST(HardmaxOperator, InvalidAxis) { - std::vector x_vals = {-1.0f, 0.0f, 1.0f}; - std::vector expected_vals = {0.0f, 0.0f, 0.0f}; - std::vector dimensions = {1, 3}; +TEST(HardmaxOperator, ThreeDimsNegAxis2) { + // x = + // import cntk as C + // expected = C.hardmax(x.reshape(12,5)).eval().reshape(3, 4, 5) + std::vector expected_vals = { + 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, - RunTest(x_vals, - expected_vals, - dimensions, - /* invalid axis */ -1, - OpTester::ExpectResult::kExpectFailure, - "Invalid axis provided."); + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}; + + RunTest(x_vals_3dims, expected_vals, three_dimensions, /*axis*/ -1); } } // namespace test diff --git a/onnxruntime/test/providers/cpu/nn/lp_norm_op_test.cc b/onnxruntime/test/providers/cpu/nn/lp_norm_op_test.cc index d21f821fcc..43ba665df0 100644 --- a/onnxruntime/test/providers/cpu/nn/lp_norm_op_test.cc +++ b/onnxruntime/test/providers/cpu/nn/lp_norm_op_test.cc @@ -80,5 +80,32 @@ TEST(LpNormalizationTest, LpNormalizationDefaultAxisAndP) { test.AddOutput("Y", input_dims, expected_output); test.Run(); } + +TEST(LpNormalizationTest, L1NormalizationWithValidNegativeAxis) { + OpTester test("LpNormalization"); + test.AddAttribute("axis", static_cast(-2)); + test.AddAttribute("p", static_cast(1)); + + vector input = {5.93932154F, 7.4367043F, 6.42487038F, 5.90394865F, + 4.81289319F, 6.81304702F, 4.9382849F, 9.02595701F, + 9.67296484F, 4.45097367F, 8.12552534F, 5.76005428F, + + 6.11240105F, 9.33036974F, 1.63932452F, 1.7841637F, + 1.18196558F, 8.49357861F, 8.00341076F, 8.83010933F, + 9.80756508F, 8.19242708F, 5.15331426F, 8.02476259F}; + vector input_dims = {2, 3, 4}; + test.AddInput("input", input_dims, input); + + vector expected_output = {0.2907843F, 0.3976693F, 0.3296719F, 0.28535331F, + 0.23563529F, 0.36431994F, 0.25339247F, 0.43624816F, + 0.47358041F, 0.23801075F, 0.41693563F, 0.27839852F, + + 0.35740998F, 0.3586345F, 0.11079474F, 0.09572189F, + 0.06911299F, 0.32647048F, 0.54091538F, 0.47374282F, + 0.57347703F, 0.31489502F, 0.34828988F, 0.43053529F}; + test.AddOutput("Y", input_dims, expected_output); + test.Run(); +} + } // namespace test } // namespace onnxruntime diff --git a/onnxruntime/test/providers/cpu/reduction/reduction_ops_test.cc b/onnxruntime/test/providers/cpu/reduction/reduction_ops_test.cc index c8b4daefed..963df55292 100644 --- a/onnxruntime/test/providers/cpu/reduction/reduction_ops_test.cc +++ b/onnxruntime/test/providers/cpu/reduction/reduction_ops_test.cc @@ -22,16 +22,27 @@ void TestReduceOp(const std::string& op, { OpTester test(op.c_str()); + bool has_neg_axis = false; + if (!axes.empty()) { - if (op.compare("ArgMax") == 0 || op.compare("ArgMin") == 0) + if (op.compare("ArgMax") == 0 || op.compare("ArgMin") == 0) { test.AddAttribute("axis", axes[0]); + if (axes[0] < 0) + has_neg_axis = true; + } else test.AddAttribute("axes", axes); } test.AddAttribute("keepdims", keepdims); test.AddInput("data", input_dims, data); test.AddOutput("reduced", expected_dims, expected_data); - test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kCudaExecutionProvider, kTensorrtExecutionProvider}); //TensorRT: result differs + + std::unordered_set excluded_eps = {kCudaExecutionProvider, kTensorrtExecutionProvider}; //TensorRT: result differs + if (has_neg_axis) { + excluded_eps.insert(kNGraphExecutionProvider); // NGraph EP cannot handle negative axis values + } + + test.Run(OpTester::ExpectResult::kExpectSuccess, "", excluded_eps); } TEST(ReductionOpTest, ReductionVariationTest) { @@ -1106,6 +1117,28 @@ TEST(ReductionOpTest, ArgMax_int32) { test.Run(); } +TEST(ReductionOpTest, ArgMax_int32_neg_axis) { + OpTester test("ArgMax"); + test.AddAttribute("axis", (int64_t)(-2)); + test.AddAttribute("keepdims", (int64_t)1); + test.AddInput("data", {3, 2, 2}, + {1, 2, + 3, 4, + + 5, 6, + 7, 8, + + 9, 10, + 11, 12}); + test.AddOutput("reduced", {3, 1, 2}, + {1, 1, + 1, 1, + 1, 1}); + + std::unordered_set excluded_eps = {kNGraphExecutionProvider}; // NGraph EP cannot handle negative axis values + test.Run(OpTester::ExpectResult::kExpectSuccess, "", excluded_eps); +} + TEST(ReductionOpTest, ArgMax2D) { OpTester test("ArgMax"); test.AddAttribute("axis", (int64_t)1); @@ -1186,5 +1219,26 @@ TEST(ReductionOpTest, ArgMin_int32) { test.Run(); } +TEST(ReductionOpTest, ArgMin_int32_neg_axis) { + OpTester test("ArgMin"); + test.AddAttribute("axis", (int64_t)(-3)); + test.AddAttribute("keepdims", (int64_t)0); + test.AddInput("data", {3, 2, 2}, + {1, 2, + 3, 4, + + 5, 6, + 7, 8, + + 9, 10, + 11, 12}); + test.AddOutput("reduced", {2, 2}, + {0, 0, + 0, 0}); + + std::unordered_set excluded_eps = {kNGraphExecutionProvider}; // NGraph EP cannot handle negative axis values + test.Run(OpTester::ExpectResult::kExpectSuccess, "", excluded_eps); +} + } // namespace test } // namespace onnxruntime