From a31f5685c3ae397cd2de5d0e31bd3dab513694d7 Mon Sep 17 00:00:00 2001 From: Alicia1529 Date: Mon, 10 Feb 2020 15:53:59 +0000 Subject: [PATCH] add isposinf isneginf isfinite --- python/mxnet/ndarray/numpy/_op.py | 152 +++++++++++++++++- python/mxnet/numpy/multiarray.py | 152 +++++++++++++++++- python/mxnet/numpy_dispatch_protocol.py | 3 + python/mxnet/symbol/numpy/_symbol.py | 104 +++++++++++- src/operator/mshadow_op.h | 24 +++ .../numpy/np_elemwise_unary_op_basic.cc | 35 ++-- .../numpy/np_elemwise_unary_op_basic.cu | 9 ++ src/operator/operator_tune.cc | 3 + .../unittest/test_numpy_interoperability.py | 39 +++-- tests/python/unittest/test_numpy_op.py | 49 +++++- 10 files changed, 529 insertions(+), 41 deletions(-) diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index f560248f5a0e..40d20ed7ac16 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -44,7 +44,8 @@ 'tril', 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', 'equal', 'not_equal', 'greater', 'less', 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', 'true_divide', 'nonzero', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', - 'diff', 'resize', 'polyval', 'nan_to_num', 'isnan', 'isinf', 'where', 'bincount'] + 'diff', 'resize', 'polyval', 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', + 'where', 'bincount'] @set_module('mxnet.ndarray.numpy') @@ -6759,9 +6760,8 @@ def isnan(x, out=None, **kwargs): Notes ----- NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). - This means that Not a Number is not equivalent to infinity. - This function differs from the original `numpy.isnan + This function differs from the original `numpy.isinf `_ in the following aspects: - Does not support complex number for now @@ -6835,6 +6835,152 @@ def isinf(x, out=None, **kwargs): return _unary_func_helper(x, _npi.isinf, _np.isinf, out=out, **kwargs) +@wrap_np_unary_func +def isposinf(x, out=None, **kwargs): + """ + Test element-wise for positive infinity, return result as bool array. + + Parameters + ---------- + x : ndarray + Input array. + out : ndarray or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : ndarray or bool + True where x is positive infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + + Examples + -------- + >>> np.isposinf(np.inf) + True + >>> np.isposinf(-np.inf) + False + >>> np.isposinf(np.nan) + False + >>> np.isposinf(np.array([-np.inf, 0., np.inf])) + array([False, False, True]) + >>> x = np.array([-np.inf, 0., np.inf]) + >>> y = np.array([True, True, True], dtype=np.bool) + >>> np.isposinf(x, y) + array([False, False, True]) + >>> y + array([False, False, True]) + """ + return _unary_func_helper(x, _npi.isposinf, _np.isposinf, out=out, **kwargs) + + +@set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func +def isneginf(x, out=None, **kwargs): + """ + Test element-wise for negative infinity, return result as bool array. + + Parameters + ---------- + x : ndarray + Input array. + out : ndarray or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : ndarray or bool + True where x is negative infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + + Examples + -------- + >>> np.isneginf(-np.inf) + True + >>> np.isneginf(np.inf) + False + >>> np.isneginf(float('-inf')) + True + >>> np.isneginf(np.array([-np.inf, 0., np.inf])) + array([ True, False, False]) + >>> x = np.array([-np.inf, 0., np.inf]) + >>> y = np.array([True, True, True], dtype=np.bool) + >>> np.isneginf(x, y) + array([ True, False, False]) + >>> y + array([ True, False, False]) + """ + return _unary_func_helper(x, _npi.isneginf, _np.isneginf, out=out, **kwargs) + + +@set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func +def isfinite(x, out=None, **kwargs): + """ + Test element-wise for finiteness (not infinity or not Not a Number). + + Parameters + ---------- + x : ndarray + Input array. + out : ndarray or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : ndarray or bool + True where x is negative infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + Not a Number, positive infinity and negative infinity are considered to be non-finite. + + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + Also that positive infinity is not equivalent to negative infinity. + But infinity is equivalent to positive infinity. Errors result if the second argument + is also supplied when x is a scalar input, or if first and second arguments have different shapes. + + Examples + -------- + >>> np.isfinite(1) + True + >>> np.isfinite(0) + True + >>> np.isfinite(np.nan) + False + >>> np.isfinite(np.inf) + False + >>> np.isfinite(-np.inf) + False + >>> np.isfinite(np.array([np.log(-1.),1.,np.log(0)])) + array([False, True, False]) + >>> x = np.array([-np.inf, 0., np.inf]) + >>> y = np.array([True, True, True], dtype=np.bool) + >>> np.isfinite(x, y) + array([False, True, False]) + >>> y + array([False, True, False]) + """ + return _unary_func_helper(x, _npi.isfinite, _np.isfinite, out=out, **kwargs) + + @set_module('mxnet.ndarray.numpy') def where(condition, x=None, y=None): # pylint: disable=too-many-return-statements """where(condition, [x, y]) diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index 4a7a85ad6b31..090c99ec7da2 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -65,7 +65,7 @@ 'unique', 'lcm', 'tril', 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', 'equal', 'not_equal', 'greater', 'less', 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', 'true_divide', 'nonzero', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', 'resize', 'matmul', - 'nan_to_num', 'isnan', 'isinf', 'polyval', 'where', 'bincount'] + 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', 'polyval', 'where', 'bincount'] __all__ += fallback.__all__ @@ -8827,9 +8827,8 @@ def isnan(x, out=None, **kwargs): Notes ----- NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). - This means that Not a Number is not equivalent to infinity. - This function differs from the original `numpy.isnan + This function differs from the original `numpy.isinf `_ in the following aspects: - Does not support complex number for now @@ -8903,6 +8902,153 @@ def isinf(x, out=None, **kwargs): return _mx_nd_np.isinf(x, out=out, **kwargs) +@set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func +def isposinf(x, out=None, **kwargs): + """ + Test element-wise for positive infinity, return result as bool array. + + Parameters + ---------- + x : ndarray + Input array. + out : ndarray or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : ndarray or bool + True where x is positive infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + + Examples + -------- + >>> np.isposinf(np.inf) + True + >>> np.isposinf(-np.inf) + False + >>> np.isposinf(np.nan) + False + >>> np.isposinf(np.array([-np.inf, 0., np.inf])) + array([False, False, True]) + >>> x = np.array([-np.inf, 0., np.inf]) + >>> y = np.array([True, True, True], dtype=np.bool) + >>> np.isposinf(x, y) + array([False, False, True]) + >>> y + array([False, False, True]) + """ + return _mx_nd_np.isposinf(x, out=out, **kwargs) + + +@set_module('mxnet.numpy') +@wrap_np_unary_func +def isneginf(x, out=None, **kwargs): + """ + Test element-wise for negative infinity, return result as bool array. + + Parameters + ---------- + x : ndarray + Input array. + out : ndarray or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : ndarray or bool + True where x is negative infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + + Examples + -------- + >>> np.isneginf(-np.inf) + True + >>> np.isneginf(np.inf) + False + >>> np.isneginf(float('-inf')) + True + >>> np.isneginf(np.array([-np.inf, 0., np.inf])) + array([ True, False, False]) + >>> x = np.array([-np.inf, 0., np.inf]) + >>> y = np.array([True, True, True], dtype=np.bool) + >>> np.isneginf(x, y) + array([ True, False, False]) + >>> y + array([ True, False, False]) + """ + return _mx_nd_np.isneginf(x, out=out, **kwargs) + + +@set_module('mxnet.numpy') +@wrap_np_unary_func +def isfinite(x, out=None, **kwargs): + """ + Test element-wise for finiteness (not infinity or not Not a Number). + + Parameters + ---------- + x : ndarray + Input array. + out : ndarray or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : ndarray or bool + True where x is negative infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + Not a Number, positive infinity and negative infinity are considered to be non-finite. + + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + Also that positive infinity is not equivalent to negative infinity. + But infinity is equivalent to positive infinity. Errors result if the second argument + is also supplied when x is a scalar input, or if first and second arguments have different shapes. + + Examples + -------- + >>> np.isfinite(1) + True + >>> np.isfinite(0) + True + >>> np.isfinite(np.nan) + False + >>> np.isfinite(np.inf) + False + >>> np.isfinite(-np.inf) + False + >>> np.isfinite(np.array([np.log(-1.),1.,np.log(0)])) + array([False, True, False]) + >>> x = np.array([-np.inf, 0., np.inf]) + >>> y = np.array([True, True, True], dtype=np.bool) + >>> np.isfinite(x, y) + array([False, True, False]) + >>> y + array([False, True, False]) + """ + return _mx_nd_np.isfinite(x, out=out, **kwargs) + + @set_module('mxnet.numpy') def where(condition, x=None, y=None): """where(condition, [x, y]) diff --git a/python/mxnet/numpy_dispatch_protocol.py b/python/mxnet/numpy_dispatch_protocol.py index bf4341fff21d..72c4e3af0e50 100644 --- a/python/mxnet/numpy_dispatch_protocol.py +++ b/python/mxnet/numpy_dispatch_protocol.py @@ -175,6 +175,9 @@ def _run_with_array_ufunc_proto(*args, **kwargs): 'empty_like', 'nan_to_num', 'isnan', + 'isfinite', + 'isposinf', + 'isneginf', 'isinf', ] diff --git a/python/mxnet/symbol/numpy/_symbol.py b/python/mxnet/symbol/numpy/_symbol.py index 6c3b5234bd90..e9af2574bc5f 100644 --- a/python/mxnet/symbol/numpy/_symbol.py +++ b/python/mxnet/symbol/numpy/_symbol.py @@ -51,7 +51,8 @@ 'tril', 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', 'equal', 'not_equal', 'greater', 'less', 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', 'true_divide', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', - 'resize', 'polyval', 'nan_to_num', 'isnan', 'isinf', 'where', 'bincount'] + 'resize', 'polyval', 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', + 'where', 'bincount'] @set_module('mxnet.symbol.numpy') @@ -6019,7 +6020,7 @@ def isnan(x, out=None, **kwargs): Parameters ---------- - x : _Symbol + x : _Symbol or scalar Input array. out : _Symbol or None, optional A location into which the result is stored. @@ -6057,9 +6058,9 @@ def isinf(x, out=None, **kwargs): Parameters ---------- - x : _Symbol + x : _Symbol or scalar Input array. - out : ndarray or None, optional + out : _Symbol or None, optional A location into which the result is stored. If provided, it must have the same shape and dtype as input ndarray. If not provided or `None`, a freshly-allocated array is returned. @@ -6073,9 +6074,8 @@ def isinf(x, out=None, **kwargs): Notes ----- NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). - This means that Not a Number is not equivalent to infinity. - This function differs from the original `numpy.isnan + This function differs from the original `numpy.isinf `_ in the following aspects: - Does not support complex number for now @@ -6087,6 +6087,98 @@ def isinf(x, out=None, **kwargs): return _unary_func_helper(x, _npi.isinf, _np.isinf, out=out, **kwargs) +@set_module('mxnet.symbol.numpy') +@wrap_np_unary_func +def isposinf(x, out=None, **kwargs): + """ + Test element-wise for positive infinity, return result as bool array. + + Parameters + ---------- + x : _Symbol or scalar + Input array. + out : _Symbol or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : _Symbol or bool + True where x is positive infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + """ + return _unary_func_helper(x, _npi.isposinf, _np.isposinf, out=out, **kwargs) + + +@set_module('mxnet.symbol.numpy') +@wrap_np_unary_func +def isneginf(x, out=None, **kwargs): + """ + Test element-wise for negative infinity, return result as bool array. + + Parameters + ---------- + x : _Symbol or scalar + Input array. + out : _Symbol or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : _Symbol or bool + True where x is negative infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + """ + return _unary_func_helper(x, _npi.isneginf, _np.isneginf, out=out, **kwargs) + + +@set_module('mxnet.symbol.numpy') +@wrap_np_unary_func +def isfinite(x, out=None, **kwargs): + """ + Test element-wise for finiteness (not infinity or not Not a Number). + + Parameters + ---------- + x : _Symbol or scalar + Input array. + out : _Symbol or None, optional + A location into which the result is stored. + If provided, it must have the same shape and dtype as input ndarray. + If not provided or `None`, a freshly-allocated array is returned. + + Returns + ------- + y : _Symbol or bool + True where x is negative infinity, false otherwise. + This is a scalar if x is a scalar. + + Notes + ----- + Not a Number, positive infinity and negative infinity are considered to be non-finite. + + NumPy uses the IEEE Standard for Binary Floating-Point for Arithmetic (IEEE 754). + This means that Not a Number is not equivalent to infinity. + Also that positive infinity is not equivalent to negative infinity. + But infinity is equivalent to positive infinity. Errors result if the second argument + is also supplied when x is a scalar input, or if first and second arguments have different shapes. + """ + return _unary_func_helper(x, _npi.isfinite, _np.isfinite, out=out, **kwargs) + + @set_module('mxnet.symbol.numpy') def where(condition, x, y): """ diff --git a/src/operator/mshadow_op.h b/src/operator/mshadow_op.h index fa424ad6d0fc..2d2a0dea575d 100644 --- a/src/operator/mshadow_op.h +++ b/src/operator/mshadow_op.h @@ -699,6 +699,30 @@ struct isinf : public mxnet_op::tunable { } }; +/*! \brief used to determine whether a number is finite*/ +struct isfinite : public mxnet_op::tunable { + template + MSHADOW_XINLINE static bool Map(DType a) { + return !IsNan(a) && !IsInf(a); + } +}; + +/*! \brief used to determine whether a number is positive infinity*/ +struct isposinf : public mxnet_op::tunable { + template + MSHADOW_XINLINE static bool Map(DType a) { + return IsInf(a) && a > 0; + } +}; + +/*! \brief used to determine whether a number is negative infinity*/ +struct isneginf : public mxnet_op::tunable { + template + MSHADOW_XINLINE static bool Map(DType a) { + return IsInf(a) && a < 0; + } +}; + /*! \brief used for generate gradient of MAE loss*/ MXNET_BINARY_MATH_OP_NC(minus_sign, a - b > DType(0) ? DType(1) : -DType(1)); diff --git a/src/operator/numpy/np_elemwise_unary_op_basic.cc b/src/operator/numpy/np_elemwise_unary_op_basic.cc index d13cb86030a7..c4678a0c6fd6 100644 --- a/src/operator/numpy/np_elemwise_unary_op_basic.cc +++ b/src/operator/numpy/np_elemwise_unary_op_basic.cc @@ -82,22 +82,27 @@ NNVM_REGISTER_OP(_np_copy) .set_attr("FCompute", UnaryOp::Compute) \ .add_argument(__input_name$, "NDArray-or-Symbol", "The input array.") -bool NumpyUnaryLogicOpType(const nnvm::NodeAttrs& attrs, - std::vector* in_attrs, - std::vector* out_attrs) { +bool NumpyUnaryBoolOpType(const nnvm::NodeAttrs& attrs, + std::vector* in_attrs, + std::vector* out_attrs) { CHECK_EQ(in_attrs->size(), 1U); CHECK_EQ(out_attrs->size(), 1U); + if (in_attrs->at(0) == -1) return false; - TYPE_ASSIGN_CHECK(*out_attrs, 0, mshadow::kBool); + if (out_attrs->at(0) == -1) { + out_attrs->at(0) = mshadow::kBool; + } else if (out_attrs->at(0) != mshadow::kBool) { + LOG(FATAL) << "TypeError: the `out` parameter should be a boolean array"; + } return true; } -#define MXNET_OPERATOR_REGISTER_NUMPY_UNARY_LOGIC(__name$, __input_name$, __kernel$) \ +#define MXNET_OPERATOR_REGISTER_NUMPY_UNARY_BOOL(__name$, __input_name$, __kernel$) \ NNVM_REGISTER_OP(__name$) \ .set_num_inputs(1) \ .set_num_outputs(1) \ .set_attr("FInferShape", ElemwiseShape<1, 1>) \ - .set_attr("FInferType", NumpyUnaryLogicOpType) \ + .set_attr("FInferType", NumpyUnaryBoolOpType) \ .set_attr("FInplaceOption", \ [](const NodeAttrs& attrs){ \ return std::vector >{{0, 0}}; \ @@ -284,15 +289,27 @@ MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_npi_expm1, "x", mshadow_op::expm1) // logical_not -MXNET_OPERATOR_REGISTER_NUMPY_UNARY_LOGIC(_npi_logical_not, "x", mshadow_op::np_logical_not) +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_BOOL(_npi_logical_not, "x", mshadow_op::np_logical_not) .set_attr("FGradient", MakeZeroGradNodes); // isnan -MXNET_OPERATOR_REGISTER_NUMPY_UNARY_LOGIC(_npi_isnan, "x", mshadow_op::isnan) +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_BOOL(_npi_isnan, "x", mshadow_op::isnan) .set_attr("FGradient", MakeZeroGradNodes); // isinf -MXNET_OPERATOR_REGISTER_NUMPY_UNARY_LOGIC(_npi_isinf, "x", mshadow_op::isinf) +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_BOOL(_npi_isinf, "x", mshadow_op::isinf) +.set_attr("FGradient", MakeZeroGradNodes); + +// isposinf +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_BOOL(_npi_isposinf, "x", mshadow_op::isposinf) +.set_attr("FGradient", MakeZeroGradNodes); + +// isneginf +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_BOOL(_npi_isneginf, "x", mshadow_op::isneginf) +.set_attr("FGradient", MakeZeroGradNodes); + +// isfinite +MXNET_OPERATOR_REGISTER_NUMPY_UNARY_BOOL(_npi_isfinite, "x", mshadow_op::isfinite) .set_attr("FGradient", MakeZeroGradNodes); // sin diff --git a/src/operator/numpy/np_elemwise_unary_op_basic.cu b/src/operator/numpy/np_elemwise_unary_op_basic.cu index 9724f2d71b83..643c4c4d6958 100644 --- a/src/operator/numpy/np_elemwise_unary_op_basic.cu +++ b/src/operator/numpy/np_elemwise_unary_op_basic.cu @@ -88,6 +88,15 @@ NNVM_REGISTER_OP(_npi_isnan) NNVM_REGISTER_OP(_npi_isinf) .set_attr("FCompute", UnaryOp::ComputeLogic); +NNVM_REGISTER_OP(_npi_isposinf) +.set_attr("FCompute", UnaryOp::ComputeLogic); + +NNVM_REGISTER_OP(_npi_isneginf) +.set_attr("FCompute", UnaryOp::ComputeLogic); + +NNVM_REGISTER_OP(_npi_isfinite) +.set_attr("FCompute", UnaryOp::ComputeLogic); + MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_npi_sin, mshadow_op::sin); MXNET_OPERATOR_REGISTER_NUMPY_UNARY_GPU(_npi_cos, mshadow_op::cos); diff --git a/src/operator/operator_tune.cc b/src/operator/operator_tune.cc index c0e9a63af892..3f24b4942363 100644 --- a/src/operator/operator_tune.cc +++ b/src/operator/operator_tune.cc @@ -314,6 +314,9 @@ IMPLEMENT_UNARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::np_logical_not); IMPLEMENT_UNARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::bitwise_not); // NOLINT() IMPLEMENT_UNARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::isnan); // NOLINT() IMPLEMENT_UNARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::isinf); // NOLINT() +IMPLEMENT_UNARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::isposinf); // NOLINT() +IMPLEMENT_UNARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::isneginf); // NOLINT() +IMPLEMENT_UNARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::isfinite); // NOLINT() IMPLEMENT_UNARY_WORKLOAD_BWD(mxnet::op::mshadow_op::nt); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_FWD(mxnet::op::mshadow_op::clip); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_BWD(mxnet::op::mshadow_op::clip); // NOLINT() diff --git a/tests/python/unittest/test_numpy_interoperability.py b/tests/python/unittest/test_numpy_interoperability.py index 610b4caa69d5..63c923afb997 100644 --- a/tests/python/unittest/test_numpy_interoperability.py +++ b/tests/python/unittest/test_numpy_interoperability.py @@ -1866,22 +1866,24 @@ def _add_workload_nan_to_num(): OpArgMngr.add_workload('nan_to_num', array3, True) -def _add_workload_isnan(): - array1 = np.array([[-_np.nan, 0, 456, _np.inf], [-1, -_np.inf, 0, _np.nan]]) - array2 = np.array([_np.inf/_np.inf, _np.inf, _np.nan, -574, 0, 23425, _np.nan,-5]) - array3 = np.array(_np.nan) - OpArgMngr.add_workload('isnan', array1,) - OpArgMngr.add_workload('isnan', array2) - OpArgMngr.add_workload('isnan', array3) +def _add_workload_isnan(array_pool): + OpArgMngr.add_workload('isnan', array_pool['2x4']) -def _add_workload_isinf(): - array1 = np.array([[-433, float('inf'), 456, _np.inf], [-1, -_np.inf, 0, 1]]) - array2 = np.array([_np.inf/_np.inf, _np.inf, -_np.inf, -574, 0, 23425, _np.inf,-5]) - array3 = np.array(_np.inf) - OpArgMngr.add_workload('isinf', array1) - OpArgMngr.add_workload('isinf', array2) - OpArgMngr.add_workload('isinf', array3) +def _add_workload_isinf(array_pool): + OpArgMngr.add_workload('isinf', array_pool['2x4']) + + +def _add_workload_isposinf(array_pool): + OpArgMngr.add_workload('isposinf', array_pool['2x4']) + + +def _add_workload_isneginf(array_pool): + OpArgMngr.add_workload('isneginf', array_pool['2x4']) + + +def _add_workload_isfinite(array_pool): + OpArgMngr.add_workload('isfinite', array_pool['2x4']) def _add_workload_polyval(): @@ -1930,6 +1932,8 @@ def _add_workload_spacing(): def _prepare_workloads(): array_pool = { '4x1': np.random.uniform(size=(4, 1)) + 2, + '2x4': np.array([[ -433, float('inf'), 456, _np.inf, _np.nan], + [-_np.inf, float("nan"), -1, 0, _np.inf]]), '1x2': np.random.uniform(size=(1, 2)) + 2, '1x1x0': np.array([[[]]]) } @@ -2081,9 +2085,12 @@ def _prepare_workloads(): _add_workload_full_like(array_pool) _add_workload_empty_like() _add_workload_nan_to_num() - _add_workload_isnan() - _add_workload_isinf() _add_workload_polyval() + _add_workload_isnan(array_pool) + _add_workload_isinf(array_pool) + _add_workload_isposinf(array_pool) + _add_workload_isneginf(array_pool) + _add_workload_isfinite(array_pool) _add_workload_heaviside() _add_workload_spacing() diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 213b7b908daf..3d4fd1c6db9f 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -7361,7 +7361,7 @@ def hybrid_forward(self, F, a): @with_seed() @use_np -def test_np_isnan_isinf(): +def test_np_unary_bool_funcs(): def check_unary_func(func): class TestUnary(HybridBlock): def __init__(self, func): @@ -7375,18 +7375,27 @@ def hybrid_forward(self, F, a): _np.nan, _np.inf, -_np.inf, - _np.array(0)/0, - _np.inf/_np.inf, + float('inf'), + float('-inf'), + float("nan"), + _np.array(0)/0, # nan + 0.0 * _np.inf, # nan + _np.inf/_np.inf, # nan + _np.inf - _np.inf, # nan + _np.array(1)/0, # inf + 0 + np.inf, # inf 1, [_np.nan], [_np.inf], [-_np.inf], [_np.array(0)/0], + [-_np.array(0)/0], + [_np.inf - _np.inf], # nan [1], [1,2,3,4,-1,-2,-3,-4,0], [_np.nan, _np.inf, -_np.inf], [_np.nan, _np.inf, -_np.inf, -574, 0, 23425, 24234,-5], - [_np.nan, -1, 0, 1], + [_np.nan, -1, 0, 1, float('inf'), float('-inf'), float('nan')], [[-433, 0, 456, _np.inf], [-1, -_np.inf, 0, 1]] ] @@ -7409,8 +7418,18 @@ def hybrid_forward(self, F, a): np_out = np_func(np_data) assert_almost_equal(mx_out.asnumpy(), np_out, rtol, atol) + # test imperative mx_out_imperative = getattr(mx.np, func)(mx_data) + assert_almost_equal(mx_out_imperative.asnumpy(), np_out, rtol, atol) + # if `out` is given and dtype == np.bool + mx_x = np.empty_like(mx_data).astype(np.bool) + np_x = mx_x.asnumpy() + getattr(mx.np, func)(mx_data, mx_x) + np_func(np_data, np_x) assert_almost_equal(mx_out_imperative .asnumpy(), np_out, rtol, atol) + # if `out` is given but dtype mismatches + mx_y = np.empty_like(mx_data) + assertRaises(TypeError, getattr(np, func), mx_data, out=mx_y) assertRaises(NotImplementedError, getattr(np, func), mx_data, where=False) assertRaises(NotImplementedError, getattr(np, func), mx_data, subok=False) @@ -7421,8 +7440,30 @@ def hybrid_forward(self, F, a): assertRaises(NotImplementedError, getattr(np, func), mx_data, order='C') assertRaises(NotImplementedError, getattr(np, func), mx_data, order='mxnet') + # test special shape and dtype + shape_list = [(), (1,), (2, 3), (4, 0, 5), 6, (7, 8), None] + dtype_list = ['int32', 'int64', 'float16', 'float32', 'float64'] + for [hybridize, dtype, shape] in itertools.product(hybridize_list, dtype_list, shape_list): + mx_data = mx.np.random.randint(low=-1, high=1, size=shape).astype(dtype) + np_data = mx_data.asnumpy() + + if hybridize: + mx_func.hybridize() + with mx.autograd.record(): + mx_out= mx_func(mx_data) + + assert mx_out.dtype == np.bool_ + + np_out = np_func(np_data) + assert_almost_equal(mx_out.asnumpy(), np_out, rtol, atol) + mx_out_imperative = getattr(mx.np, func)(mx_data) + assert_almost_equal(mx_out_imperative .asnumpy(), np_out, rtol, atol) + check_unary_func("isnan") check_unary_func("isinf") + check_unary_func("isposinf") + check_unary_func("isneginf") + check_unary_func("isfinite") @with_seed()