This is an automated email from the ASF dual-hosted git repository. haoj pushed a commit to branch numpy in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
commit aaa39fc0515d5fa0e280bd6dbfe55f35ea9b7a31 Author: Hao Jin <hjjn.a...@gmail.com> AuthorDate: Sat Jun 1 21:33:53 2019 -0700 Numpy Unary Ops (#15010) * Unary Ops * new version of unit tests --- src/operator/numpy/np_elemwise_unary_op_basic.cc | 297 +++++++++++++++++++++++ src/operator/numpy/np_elemwise_unary_op_basic.cu | 71 ++++++ src/operator/tensor/elemwise_binary_op.h | 16 +- src/operator/tensor/elemwise_unary_op.h | 6 +- tests/python/unittest/test_numpy_op.py | 78 ++++++ 5 files changed, 460 insertions(+), 8 deletions(-) diff --git a/src/operator/numpy/np_elemwise_unary_op_basic.cc b/src/operator/numpy/np_elemwise_unary_op_basic.cc index a64356e..87a765e 100644 --- a/src/operator/numpy/np_elemwise_unary_op_basic.cc +++ b/src/operator/numpy/np_elemwise_unary_op_basic.cc @@ -69,5 +69,302 @@ NNVM_REGISTER_OP(_np_copy) }) .add_argument("a", "NDArray-or-Symbol", "The input"); +#define MXNET_OPERATOR_REGISTER_NUMPY_UNARY(__name$, __input_name$, __kernel$) \ +NNVM_REGISTER_OP(__name$) \ +.set_num_inputs(1) \ +.set_num_outputs(1) \ +.set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>) \ +.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<1, 1>) \ +.set_attr<nnvm::FInplaceOption>("FInplaceOption", \ + [](const NodeAttrs& attrs){ \ + return std::vector<std::pair<int, int> >{{0, 0}}; \ + }) \ +.set_attr<nnvm::FListInputNames>("FListInputNames", \ + [](const NodeAttrs& attrs) { \ + return std::vector<std::string>{__input_name$}; \ + }) \ +.set_attr<FCompute>("FCompute<cpu>", UnaryOp::Compute<cpu, __kernel$>) \ +.add_argument(__input_name$, "NDArray-or-Symbol", "The input array.") + +// negative +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_negative, "x", mshadow_op::negation) +.describe(R"code(Numerical negative, element-wise. +Example:: + negative([1., -1.]) = [-1., 1.] +)code") +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseNone{"negative"}); + +// reciprocal +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_reciprocal, "x", mshadow_op::reciprocal) +.describe(R"code(Return the reciprocal of the argument, element-wise. +Example:: + reciprocal([-2, 1, 3, 1.6, 0.2]) = [-0.5, 1.0, 0.33333334, 0.625, 5.0] +)code") +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_reciprocal"}); + +// abs +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_absolute, "x", mshadow_op::abs) +.add_alias("_np_abs") +.describe(R"code(Returns element-wise absolute value of the input. +Example:: + absolute([-2, 0, 3]) = [2, 0, 3] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_abs"}); + +// sign +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_sign, "x", mshadow_op::sign) +.describe(R"code(Returns an element-wise indication of the sign of a number. +The sign function returns -1 if x < 0, 0 if x==0, 1 if x > 0. +Example:: + sign([-2, 0, 3]) = [-1, 0, 1] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_sign"}); + +// rint +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_rint, "x", mshadow_op::rint) +.describe(R"code(Round elements of the array to the nearest integer. +Example:: + rint([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) = [-2., -2., -0., 0., 2., 2., 2.] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes); + +// ceil +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_ceil, "x", mshadow_op::ceil) +.describe(R"code(Return the ceiling of the input, element-wise. +The ceil of the scalar x is the smallest integer i, such that i >= x. +Example:: + ceil([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) = [-1., -1., -0., 1., 2., 2., 2.] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes); + +// floor +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_floor, "x", mshadow_op::floor) +.describe(R"code(Return the floor of the input, element-wise. +The floor of the scalar x is the largest integer i, such that i <= x. +Example:: + floor([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) = [-2., -2., -1., 0., 1., 1., 2.] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes); + +// trunc +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_trunc, "x", mshadow_op::trunc) +.describe(R"code(Return the truncated value of the input, element-wise. +The truncated value of the scalar x is the nearest integer i which is closer to +zero than x is. In short, the fractional part of the signed number x is discarded. +Example:: + trunc([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) = [-1., -1., -0., 0., 1., 1., 2.] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes); + +// fix +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_fix, "x", mshadow_op::fix) +.describe(R"code(Round to nearest integer towards zero. +Round an array of floats element-wise to nearest integer towards zero. +The rounded values are returned as floats. +Example:: + fix([-2.1, -1.9, 1.9, 2.1]) = [-2., -1., 1., 2.] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes); + +// square +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_square, "x", mshadow_op::square) +.describe(R"code(Return the element-wise square of the input. +Example:: + square([2, 3, 4]) = [4, 9, 16] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_square"}); + +// sqrt +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_sqrt, "x", mshadow_op::square_root) +.describe(R"code(Return the non-negative square-root of an array, element-wise. +Example:: + sqrt([4, 9, 16]) = [2, 3, 4] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseOut{"_backward_sqrt"}); + +// cbrt +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_cbrt, "x", mshadow_op::cube_root) +.describe(R"code(Return the cube-root of an array, element-wise. +Example:: + cbrt([1, 8, -125]) = [1, 2, -5] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseOut{"_backward_cbrt"}); + +// exp +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_exp, "x", mshadow_op::exp) +.describe(R"code(Calculate the exponential of all elements in the input array. +Example:: + exp([0, 1, 2]) = [1., 2.71828175, 7.38905621] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseOut{"_mul"}); + +// log +NNVM_REGISTER_OP(_np_log) +.describe(R"code(Returns element-wise Natural logarithmic value of the input. +The natural logarithm is logarithm in base *e*, so that ``log(exp(x)) = x`` +)code" ADD_FILELINE) +.set_num_inputs(1) +.set_num_outputs(1) +.set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>) +.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<1, 1>) +.set_attr<nnvm::FInplaceOption>("FInplaceOption", + [](const NodeAttrs& attrs){ + return std::vector<std::pair<int, int> >{{0, 0}}; + }) +.set_attr<nnvm::FListInputNames>("FListInputNames", + [](const NodeAttrs& attrs) { + return std::vector<std::string>{"x"}; + }) +.set_attr<FCompute>("FCompute<cpu>", UnaryOp::Compute<cpu, mshadow_op::log>) +.add_argument("x", "NDArray-or-Symbol", "The input array.") +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_log"}); + +// log10 +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_log10, "x", mshadow_op::log10) +.describe(R"code(Returns element-wise Base-10 logarithmic value of the input. +``10**log10(x) = x`` +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_log10"}); + +// log2 +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_log2, "x", mshadow_op::log2) +.describe(R"code(Returns element-wise Base-2 logarithmic value of the input. +``2**log2(x) = x`` +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_log2"}); + +// log1p +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_log1p, "x", mshadow_op::log1p) +.describe(R"code(Return the natural logarithm of one plus the input array, element-wise. +Calculates ``log(1 + x)``. +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_log1p"}); + +// expm1 +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_expm1, "x", mshadow_op::expm1) +.describe(R"code(Calculate ``exp(x) - 1`` for all elements in the array.)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_expm1"}); + + +// logical_not +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_logical_not, "x", mshadow_op::nt) +.describe(R"code(Compute the truth value of NOT x element-wise. +Example:: + logical_not([-2., 0., 1.]) = [0., 1., 0.] +)code") +.set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes); + +// sin +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_sin, "x", mshadow_op::sin) +.describe(R"code(Trigonometric sine, element-wise. +.. math:: + sin([0, \pi/4, \pi/2]) = [0, 0.707, 1] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_sin" }); + +// cos +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_cos, "x", mshadow_op::cos) +.describe(R"code(Computes the element-wise cosine of the input array. +.. math:: + cos([0, \pi/4, \pi/2]) = [1, 0.707, 0] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_cos"}); + +// tan +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_tan, "x", mshadow_op::tan) +.describe(R"code(Computes the element-wise tangent of the input array. +.. math:: + tan([0, \pi/4, \pi/2]) = [0, 1, -inf] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseOut{ "_backward_tan" }); + +// arcsin +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_arcsin, "x", mshadow_op::arcsin) +.describe(R"code(Returns element-wise inverse sine of the input array. +.. math:: + arcsin([-1, -.707, 0, .707, 1]) = [-\pi/2, -\pi/4, 0, \pi/4, \pi/2] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_arcsin" }); + +// arccos +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_arccos, "x", mshadow_op::arccos) +.describe(R"code(Returns element-wise inverse cosine of the input array. +The input should be in range `[-1, 1]`. +The output is in the closed interval :math:`[0, \pi]` +.. math:: + arccos([-1, -.707, 0, .707, 1]) = [\pi, 3\pi/4, \pi/2, \pi/4, 0] +The storage type of ``arccos`` output is always dense +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_arccos" }); + +// arctan +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_arctan, "x", mshadow_op::arctan) +.describe(R"code(Returns element-wise inverse tangent of the input array. +.. math:: + arctan([-1, 0, 1]) = [-\pi/4, 0, \pi/4] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_arctan" }); + +// degrees +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_degrees, "x", mshadow_op::degrees) +.describe(R"code(Converts each element of the input array from radians to degrees. +.. math:: + degrees([0, \pi/2, \pi, 3\pi/2, 2\pi]) = [0, 90, 180, 270, 360] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_degrees" }); + +// radians +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_radians, "x", mshadow_op::radians) +.describe(R"code(Converts each element of the input array from degrees to radians. +.. math:: + radians([0, 90, 180, 270, 360]) = [0, \pi/2, \pi, 3\pi/2, 2\pi] +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_radians" }); + +// sinh +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_sinh, "x", mshadow_op::sinh) +.describe(R"code(Returns the hyperbolic sine of the input array, computed element-wise. +.. math:: + sinh(x) = 0.5\times(exp(x) - exp(-x)) +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_sinh" }); + +// cosh +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_cosh, "x", mshadow_op::cosh) +.describe(R"code(Returns the hyperbolic cosine of the input array, computed element-wise. +.. math:: + cosh(x) = 0.5\times(exp(x) + exp(-x)) +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_cosh" }); + +// tanh +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_tanh, "x", mshadow_op::tanh) +.describe(R"code(Returns the hyperbolic tangent of the input array, computed element-wise. +.. math:: + tanh(x) = sinh(x) / cosh(x) +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseOut{ "_backward_tanh" }); + +// arcsinh +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_arcsinh, "x", mshadow_op::arcsinh) +.describe(R"code(Returns the element-wise inverse hyperbolic sine of the input array, \ +computed element-wise. +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_arcsinh" }); + +// arccosh +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_arccosh, "x", mshadow_op::arccosh) +.describe(R"code(Returns the element-wise inverse hyperbolic cosine of the input array, \ +computed element-wise. +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_arccosh" }); + +// arctanh +MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_np_arctanh, "x", mshadow_op::arctanh) +.describe(R"code(Returns the element-wise inverse hyperbolic tangent of the input array, \ +computed element-wise. +)code" ADD_FILELINE) +.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{ "_backward_arctanh" }); + } // namespace op } // namespace mxnet diff --git a/src/operator/numpy/np_elemwise_unary_op_basic.cu b/src/operator/numpy/np_elemwise_unary_op_basic.cu index 600f198..a3cdff9 100644 --- a/src/operator/numpy/np_elemwise_unary_op_basic.cu +++ b/src/operator/numpy/np_elemwise_unary_op_basic.cu @@ -35,5 +35,76 @@ NNVM_REGISTER_OP(_npe_sigmoid) NNVM_REGISTER_OP(_np_copy) .set_attr<FCompute>("FCompute<gpu>", UnaryOp::IdentityCompute<gpu>); +#define MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(__name$, __kernel$) \ +NNVM_REGISTER_OP(__name$) \ +.set_attr<FCompute>("FCompute<gpu>", UnaryOp::Compute<gpu, __kernel$>) \ + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_negative, mshadow_op::negation); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_reciprocal, mshadow_op::reciprocal); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_absolute, mshadow_op::abs); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_sign, mshadow_op::sign); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_rint, mshadow_op::rint); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_ceil, mshadow_op::ceil); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_floor, mshadow_op::floor); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_trunc, mshadow_op::trunc); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_fix, mshadow_op::fix); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_square, mshadow_op::square); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_sqrt, mshadow_op::square_root); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_cbrt, mshadow_op::cube_root); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_exp, mshadow_op::exp); + +NNVM_REGISTER_OP(_np_log) +.set_attr<FCompute>("FCompute<gpu>", UnaryOp::Compute<gpu, mshadow_op::log>); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_log10, mshadow_op::log10); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_log2, mshadow_op::log2); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_log1p, mshadow_op::log1p); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_expm1, mshadow_op::expm1); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_logical_not, mshadow_op::nt); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_sin, mshadow_op::sin); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_cos, mshadow_op::cos); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_tan, mshadow_op::tan); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_arcsin, mshadow_op::arcsin); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_arccos, mshadow_op::arccos); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_arctan, mshadow_op::arctan); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_degrees, mshadow_op::degrees); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_radians, mshadow_op::radians); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_sinh, mshadow_op::sinh); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_cosh, mshadow_op::cosh); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_tanh, mshadow_op::tanh); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_arcsinh, mshadow_op::arcsinh); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_arccosh, mshadow_op::arccosh); + +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_np_arctanh, mshadow_op::arctanh); + } // namespace op } // namespace mxnet diff --git a/src/operator/tensor/elemwise_binary_op.h b/src/operator/tensor/elemwise_binary_op.h index 2fe3fd9..9c1d8b1 100644 --- a/src/operator/tensor/elemwise_binary_op.h +++ b/src/operator/tensor/elemwise_binary_op.h @@ -487,9 +487,11 @@ class ElemwiseBinaryOp : public OpBase { MSHADOW_TYPE_SWITCH(outputs[0].type_flag_, DType, { const size_t size = (minthree(outputs[0].Size(), inputs[0].Size(), inputs[1].Size()) + DataType<DType>::kLanes - 1) / DataType<DType>::kLanes; - Kernel<mxnet_op::op_with_req<OP, Req>, xpu>::Launch(s, size, - outputs[0].dptr<DType>(), - inputs[0].dptr<DType>(), inputs[1].dptr<DType>()); + if (size != 0) { + Kernel<mxnet_op::op_with_req<OP, Req>, xpu>::Launch(s, size, + outputs[0].dptr<DType>(), + inputs[0].dptr<DType>(), inputs[1].dptr<DType>()); + } }); }); } @@ -510,9 +512,11 @@ class ElemwiseBinaryOp : public OpBase { MSHADOW_TYPE_SWITCH_WITH_HALF2(outputs[0].type_flag_, DType, { const size_t size = (minthree(outputs[0].Size(), inputs[0].Size(), inputs[1].Size()) + DataType<DType>::kLanes - 1) / DataType<DType>::kLanes; - Kernel<mxnet_op::op_with_req<OP, Req>, xpu>::Launch(s, size, - outputs[0].dptr<DType>(), - inputs[0].dptr<DType>(), inputs[1].dptr<DType>()); + if (size != 0) { + Kernel<mxnet_op::op_with_req<OP, Req>, xpu>::Launch(s, size, + outputs[0].dptr<DType>(), + inputs[0].dptr<DType>(), inputs[1].dptr<DType>()); + } }); }); } diff --git a/src/operator/tensor/elemwise_unary_op.h b/src/operator/tensor/elemwise_unary_op.h index 458106e..87964ac 100644 --- a/src/operator/tensor/elemwise_unary_op.h +++ b/src/operator/tensor/elemwise_unary_op.h @@ -243,8 +243,10 @@ class UnaryOp : public OpBase { mshadow::Stream<xpu> *s = ctx.get_stream<xpu>(); MSHADOW_TYPE_SWITCH(outputs[0].type_flag_, DType, { MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { - mxnet_op::Kernel<mxnet_op::op_with_req<OP, Req>, xpu>::Launch( - s, inputs[0].Size(), outputs[0].dptr<DType>(), inputs[0].dptr<DType>()); + if (inputs[0].Size() != 0) { + mxnet_op::Kernel<mxnet_op::op_with_req<OP, Req>, xpu>::Launch( + s, inputs[0].Size(), outputs[0].dptr<DType>(), inputs[0].dptr<DType>()); + } }); }); } diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 853cb50..3608690 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -315,6 +315,83 @@ def test_np_minimum(): @with_seed() @mx.use_np_shape +def test_np_unary_funcs(): + def check_unary_func(func, ref_grad, shape, low, high): + class TestUnary(HybridBlock): + def __init__(self, func): + super(TestUnary, self).__init__() + self._func = func + + def hybrid_forward(self, F, a, *args, **kwargs): + return getattr(F.np, self._func)(a) + + print(func) + np_func = getattr(_np, func) + mx_func = TestUnary(func) + np_test_data = _np.random.uniform(low, high, shape).astype(_np.float32) + mx_test_data = mx.numpy.array(np_test_data) + for hybridize in [True, False]: + if hybridize: + mx_func.hybridize() + if ref_grad: + mx_test_data.attach_grad() + np_out = np_func(np_test_data) + with mx.autograd.record(): + y = mx_func(mx_test_data) + assert y.shape == np_out.shape + assert_almost_equal(y.asnumpy(), np_out, rtol=1e-3, atol=1e-5) + + if ref_grad: + y.backward() + print(mx_test_data.grad.asnumpy()) + print(ref_grad(np_test_data)) + assert_almost_equal(mx_test_data.grad.asnumpy(), ref_grad(np_test_data), rtol=1e-5, atol=1e-6, equal_nan=True) + + funcs = { + 'absolute' : (lambda x: -1. * (x < 0) + (x > 0), -1.0, 1.0), + 'cbrt' : (lambda x: 1. / (3. * _np.cbrt(x) ** 2), -1.0, 1.0), + 'ceil' : (None, -10.0, 10.0), + 'exp' : (lambda x: _np.exp(x), -1.0, 1.0), + 'expm1' : (lambda x: _np.exp(x), -1.0, 1.0), + 'fix' : (None, -10.0, 10.0), + 'floor' : (None, -10.0, 10.0), + 'log' : (lambda x: 1.0 / x, 0.1, 5.0), + 'log10' : (lambda x: 1.0 / (x * _np.log(10)), 0.1, 10.0), + 'log1p' : (lambda x: 1.0 / (1.0 + x), -0.9, 5.0), + 'log2' : (lambda x: 1.0 / (x * _np.log(2)), 0.1, 2.0), + 'logical_not' : (None, -1.0, 1.0), + 'negative' : (lambda x: -1. * _np.ones(x.shape), -1.0, 1.0), + 'reciprocal' : (lambda x: -1. / (x ** 2), 0.01, 1.0), + 'rint' : (None, -5.0, 5.0), + 'sign' : (None, -1.0, 1.0), + 'sqrt' : (lambda x: 0.5 / _np.sqrt(x), 0.001, 10.0), + 'square' : (lambda x: 2.0 * x, -1.0, 1.0), + 'trunc' : (None, -5.0, 5.0), + 'sin' : (lambda x: _np.cos(x), -1.0, 1.0), + 'cos' : (lambda x: -_np.sin(x), -1.0, 1.0), + 'tan' : (lambda x: _np.tan(x) ** 2 + 1.0, -1.0, 1.0), + 'arcsin' : (lambda x: 1. / (1. - x ** 2) ** (1. / 2.), -1.0, 1.0), + 'arccos' : (lambda x: -1. / (1. - x ** 2.) ** (1. / 2.), -1.0, 1.0), + 'arctan' : (lambda x: 1. / (x ** 2. + 1.), -1.0, 1.0), + 'degrees' : (lambda x: 180. / _np.pi * _np.ones(x.shape), -1.0, 1.0), + 'radians' : (lambda x: _np.pi / 180. * _np.ones(x.shape), -1.0, 1.0), + 'sinh' : (lambda x: _np.cosh(x), -1.0, 1.0), + 'cosh' : (lambda x: _np.sinh(x), -1.0, 1.0), + 'tanh' : (lambda x: 1. - _np.tanh(x) ** 2, -1.0, 1.0), + 'arcsinh' : (lambda x: 1./(x**2 + 1.)**(1./2.), -1.0, 1.0), + 'arccosh' : (lambda x: 1./(x**2 - 1.)**(1./2.), 2.0, 5.0), + 'arctanh' : (lambda x: -1./(x**2 - 1.), -0.99, 0.99) + } + ndim = random.choice([2, 3, 4]) + shape = random.choice([rand_shape_nd(ndim, dim=3), (1, 0, 2)]) + for shape in [rand_shape_nd(ndim, dim=3), (1, 0, 2)]: + for func, func_data in funcs.items(): + ref_grad, low, high = func_data + check_unary_func(func, ref_grad, shape, low, high) + + +@with_seed() +@mx.use_np_shape def test_np_stack(): class TestStack(HybridBlock): def __init__(self, axis=None): @@ -364,6 +441,7 @@ def test_np_stack(): mx_out = np.stack([mx_a, mx_b, mx_c, mx_d], axis=axis) assert same(mx_out.asnumpy(), np_out) + if __name__ == '__main__': import nose nose.runmodule()