diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index 69baa06b96de..109954361477 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', 'nan_to_num', 'isnan', 'isinf', 'where', 'bincount'] + 'diff', 'resize', 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', + 'where', 'bincount'] @set_module('mxnet.ndarray.numpy') @@ -6612,9 +6613,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 @@ -6688,6 +6688,153 @@ def isinf(x, out=None, **kwargs): return _unary_func_helper(x, _npi.isinf, _np.isinf, 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 _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): """where(condition, [x, y]) diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index 7002c7bc8401..6b788f9b9dd3 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', - 'nan_to_num', 'isnan', 'isinf', 'where', 'bincount'] + 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', 'where', 'bincount'] __all__ += fallback.__all__ @@ -8691,9 +8691,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 @@ -8767,6 +8766,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 d3fa0d67c08e..18b5c28b7b6c 100644 --- a/python/mxnet/numpy_dispatch_protocol.py +++ b/python/mxnet/numpy_dispatch_protocol.py @@ -173,6 +173,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 1ed34a0660aa..af0b622d9059 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', 'nan_to_num', 'isnan', 'isinf', 'where', 'bincount'] + 'resize', 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', + 'where', 'bincount'] @set_module('mxnet.symbol.numpy') @@ -5962,7 +5963,7 @@ def isinf(x, out=None, **kwargs): Parameters ---------- - x : _Symbol + x : ndarray Input array. out : ndarray or None, optional A location into which the result is stored. @@ -5971,16 +5972,15 @@ def isinf(x, out=None, **kwargs): Returns ------- - y : _Symbol or bool + y : ndarray or bool True where x is positive or 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. - 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 @@ -5992,6 +5992,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 + 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 + 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 + 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..158483b6dd9c 100644 --- a/src/operator/numpy/np_elemwise_unary_op_basic.cc +++ b/src/operator/numpy/np_elemwise_unary_op_basic.cc @@ -92,6 +92,21 @@ bool NumpyUnaryLogicOpType(const nnvm::NodeAttrs& attrs, return true; } +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; + 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$) \ NNVM_REGISTER_OP(__name$) \ .set_num_inputs(1) \ @@ -109,6 +124,23 @@ bool NumpyUnaryLogicOpType(const nnvm::NodeAttrs& attrs, .set_attr("FCompute", UnaryOp::ComputeLogic) \ .add_argument(__input_name$, "NDArray-or-Symbol", "The input array.") +#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", NumpyUnaryBoolOpType) \ + .set_attr("FInplaceOption", \ + [](const NodeAttrs& attrs){ \ + return std::vector >{{0, 0}}; \ + }) \ + .set_attr("FListInputNames", \ + [](const NodeAttrs& attrs) { \ + return std::vector{__input_name$}; \ + }) \ + .set_attr("FCompute", UnaryOp::ComputeLogic) \ + .add_argument(__input_name$, "NDArray-or-Symbol", "The input array.") + // negative MXNET_OPERATOR_REGISTER_NUMPY_UNARY(_npi_negative, "x", mshadow_op::negation) .describe(R"code(Numerical negative, element-wise. @@ -288,11 +320,23 @@ MXNET_OPERATOR_REGISTER_NUMPY_UNARY_LOGIC(_npi_logical_not, "x", mshadow_op::np_ .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 935f46e469ec..7c90b529099b 100644 --- a/tests/python/unittest/test_numpy_interoperability.py +++ b/tests/python/unittest/test_numpy_interoperability.py @@ -1764,22 +1764,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_linalg_cond(): @@ -1814,6 +1816,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([[[]]]) } @@ -1963,8 +1967,11 @@ 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_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 41b77019da87..8f8901b1441d 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -7163,7 +7163,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): @@ -7177,18 +7177,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]] ] @@ -7211,8 +7220,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) @@ -7223,8 +7242,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()