diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index 295c83ef47a0..220555aa288e 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -36,10 +36,10 @@ 'linspace', 'logspace', 'expand_dims', 'tile', 'arange', 'split', 'vsplit', 'concatenate', 'append', 'stack', 'vstack', 'column_stack', 'dstack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'indices', 'copysign', 'ravel', 'unravel_index', 'hanning', 'hamming', 'blackman', - 'flip', 'around', 'hypot', 'bitwise_xor', 'bitwise_or', 'rad2deg', 'deg2rad', 'unique', 'lcm', 'tril', - 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', 'equal', 'not_equal', 'greater', 'less', - 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', 'true_divide', 'nonzero', 'shares_memory', - 'may_share_memory', 'diff', 'resize', 'nan_to_num', 'where'] + 'flip', 'around', 'hypot', 'bitwise_and', 'bitwise_xor', 'bitwise_or', 'rad2deg', 'deg2rad', 'unique', + 'lcm', 'tril', 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', 'equal', 'not_equal', 'greater', + 'less', 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', 'true_divide', 'nonzero', + 'shares_memory', 'may_share_memory', 'diff', 'resize', 'nan_to_num', 'where'] @set_module('mxnet.ndarray.numpy') def shape(a): @@ -4364,6 +4364,46 @@ def hypot(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.hypot, _np.hypot, _npi.hypot_scalar, None, out) +@set_module('mxnet.ndarray.numpy') +@wrap_np_binary_func +def bitwise_and(x1, x2, out=None, **kwargs): + r""" + Compute the bit-wise XOR of two arrays element-wise. + + Parameters + ---------- + x1, x2 : ndarray or scalar + Only integer and boolean types are handled. If x1.shape != x2.shape, + they must be broadcastable to a common shape (which becomes the shape of the output). + out : ndarray, optional + A location into which the result is stored. If provided, it must have a shape that the + inputs broadcast to. If not provided or None, a freshly-allocated array is returned. + + Returns + ------- + out : ndarray + Result. + + Examples + -------- + >>> np.bitwise_and(13, 17) + 1 + + >>> np.bitwise_and(14, 13) + 12 + >>> np.bitwise_and(np.array([14,3], dtype='int32'), 13) + array([12, 1], dtype=int32) + + >>> np.bitwise_and(np.array([11,7], dtype='int32'), np.array([4,25], dtype='int32')) + array([0, 1], dtype=int32) + >>> np.bitwise_and(np.array([2,5,255], dtype='int32'), np.array([3,14,16], dtype='int32')) + array([ 2, 4, 16], dtype=int32) + >>> np.bitwise_and(np.array([True, True], dtype='bool'), np.array([False, True], dtype='bool')) + array([False, True]) + """ + return _ufunc_helper(x1, x2, _npi.bitwise_and, _np.bitwise_and, _npi.bitwise_and_scalar, None, out) + + @set_module('mxnet.ndarray.numpy') @wrap_np_binary_func def bitwise_xor(x1, x2, out=None, **kwargs): diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index 1c6873d342d1..69840ef0134a 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -54,10 +54,11 @@ 'tensordot', 'histogram', 'eye', 'linspace', 'logspace', 'expand_dims', 'tile', 'arange', 'split', 'vsplit', 'concatenate', 'stack', 'vstack', 'column_stack', 'dstack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'indices', 'copysign', 'ravel', 'unravel_index', - 'hanning', 'hamming', 'blackman', 'flip', 'around', 'arctan2', 'hypot', 'bitwise_xor', 'bitwise_or', - 'rad2deg', 'deg2rad', 'unique', 'lcm', 'tril', 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', - 'equal', 'not_equal', 'greater', 'less', 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', - 'true_divide', 'nonzero', 'shares_memory', 'may_share_memory', 'diff', 'resize', 'nan_to_num', 'where'] + 'hanning', 'hamming', 'blackman', 'flip', 'around', 'arctan2', 'hypot', 'bitwise_and', 'bitwise_xor', + 'bitwise_or', 'rad2deg', 'deg2rad', 'unique', 'lcm', 'tril', 'identity', 'take', 'ldexp', 'vdot', + 'inner', 'outer', 'equal', 'not_equal', 'greater', 'less', 'greater_equal', 'less_equal', 'hsplit', 'rot90', + 'einsum', 'true_divide', 'nonzero', 'shares_memory', 'may_share_memory', 'diff', 'resize', 'nan_to_num', + 'where'] # Return code for dispatching indexing function call _NDARRAY_UNSUPPORTED_INDEXING = -1 @@ -6269,6 +6270,46 @@ def hypot(x1, x2, out=None, **kwargs): return _mx_nd_np.hypot(x1, x2, out=out) +@set_module('mxnet.numpy') +@wrap_np_binary_func +def bitwise_and(x1, x2, out=None, **kwargs): + r""" + Compute the bit-wise XOR of two arrays element-wise. + + Parameters + ---------- + x1, x2 : ndarray or scalar + Only integer and boolean types are handled. If x1.shape != x2.shape, + they must be broadcastable to a common shape (which becomes the shape of the output). + out : ndarray, optional + A location into which the result is stored. If provided, it must have a shape that the + inputs broadcast to. If not provided or None, a freshly-allocated array is returned. + + Returns + ------- + out : ndarray + Result. + + Examples + -------- + >>> np.bitwise_and(13, 17) + 1 + + >>> np.bitwise_and(14, 13) + 12 + >>> np.bitwise_and(np.array([14,3], dtype='int32'), 13) + array([26, 5], dtype=int32) + + >>> np.bitwise_and(np.array([11,7], dtype='int32'), np.array([4,25], dtype='int32')) + array([0, 1], dtype=int32) + >>> np.bitwise_and(np.array([2,5,255], dtype='int32'), np.array([3,14,16], dtype='int32')) + array([ 2, 4, 16], dtype=int32) + >>> np.bitwise_and(np.array([True, True], dtype='bool'), np.array([False, True], dtype='bool')) + array([False, True]) + """ + return _mx_nd_np.bitwise_and(x1, x2, out=out) + + @set_module('mxnet.numpy') @wrap_np_binary_func def bitwise_xor(x1, x2, out=None, **kwargs): @@ -6297,10 +6338,10 @@ def bitwise_xor(x1, x2, out=None, **kwargs): >>> np.bitwise_xor(31, 5) 26 >>> np.bitwise_xor(np.array([31,3], dtype=np.int32), 5) - array([26, 6]) + array([26, 6], dtype=int32) >>> np.bitwise_xor(np.array([31,3], dtype='int32'), np.array([5,6], dtype='int32')) - array([26, 5]) + array([26, 5], dtype=int32) >>> np.bitwise_xor(np.array([True, True], dtype='bool'), np.array([False, True], dtype='bool')) array([ True, False]) """ diff --git a/python/mxnet/numpy_dispatch_protocol.py b/python/mxnet/numpy_dispatch_protocol.py index 1f68ca3c522a..8468f8b070db 100644 --- a/python/mxnet/numpy_dispatch_protocol.py +++ b/python/mxnet/numpy_dispatch_protocol.py @@ -230,6 +230,7 @@ def _register_array_function(): 'ceil', 'trunc', 'floor', + 'bitwise_and', 'bitwise_xor', 'bitwise_or', 'logical_not', diff --git a/python/mxnet/symbol/numpy/_symbol.py b/python/mxnet/symbol/numpy/_symbol.py index 3790078bd397..e160d0a56f16 100644 --- a/python/mxnet/symbol/numpy/_symbol.py +++ b/python/mxnet/symbol/numpy/_symbol.py @@ -38,9 +38,9 @@ 'linspace', 'logspace', 'expand_dims', 'tile', 'arange', 'split', 'vsplit', 'concatenate', 'append', 'stack', 'vstack', 'column_stack', 'dstack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'indices', 'copysign', 'ravel', 'unravel_index', 'hanning', 'hamming', 'blackman', - 'flip', 'around', 'hypot', 'bitwise_xor', 'bitwise_or', 'rad2deg', 'deg2rad', 'unique', 'lcm', 'tril', - 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', 'equal', 'not_equal', 'greater', 'less', - 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', 'true_divide', 'shares_memory', + 'flip', 'around', 'hypot', 'bitwise_and', 'bitwise_xor', 'bitwise_or', 'rad2deg', 'deg2rad', 'unique', + 'lcm', 'tril', 'identity', 'take', 'ldexp', 'vdot', 'inner', 'outer', 'equal', 'not_equal', 'greater', + 'less', 'greater_equal', 'less_equal', 'hsplit', 'rot90', 'einsum', 'true_divide', 'shares_memory', 'may_share_memory', 'diff', 'resize', 'nan_to_num', 'where'] def _num_outputs(sym): @@ -4110,6 +4110,30 @@ def hypot(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.hypot, _np.hypot, _npi.hypot_scalar, None, out) +@set_module('mxnet.symbol.numpy') +@wrap_np_binary_func +def bitwise_and(x1, x2, out=None, **kwargs): + r""" + Compute the bit-wise XOR of two arrays element-wise. + + Parameters + ---------- + x1, x2 : _Symbol or scalar + Only integer and boolean types are handled. If x1.shape != x2.shape, + they must be broadcastable to a common shape (which becomes the shape of the output). + out : _Symbol or None, optional + A location into which the result is stored. If provided, it must have + a shape that the inputs broadcast to. If not provided or `None`, + a freshly-allocated array is returned. + + Returns + ------- + out : _Symbol or scalar + Result. + """ + return _ufunc_helper(x1, x2, _npi.bitwise_and, _np.bitwise_and, _npi.bitwise_and_scalar, None, out) + + @set_module('mxnet.symbol.numpy') @wrap_np_binary_func def bitwise_xor(x1, x2, out=None, **kwargs): diff --git a/src/operator/mshadow_op.h b/src/operator/mshadow_op.h index a0a424b6209b..685c9c4b636f 100644 --- a/src/operator/mshadow_op.h +++ b/src/operator/mshadow_op.h @@ -560,6 +560,8 @@ MXNET_BINARY_MATH_OP(logical_or, a || b ? DType(1) : DType(0)); MXNET_BINARY_MATH_OP(logical_xor, (a || b) && !(a && b) ? DType(1) : DType(0)); +MXNET_BINARY_MATH_OP(bitwise_and, static_cast(a) & static_cast(b)); + MXNET_BINARY_MATH_OP(bitwise_xor, static_cast(a) ^ static_cast(b)); MXNET_BINARY_MATH_OP(bitwise_or, static_cast(a) | static_cast(b)); diff --git a/src/operator/numpy/np_elemwise_broadcast_op_extended.cc b/src/operator/numpy/np_elemwise_broadcast_op_extended.cc index 34ff20e733e5..70233a596dc7 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op_extended.cc +++ b/src/operator/numpy/np_elemwise_broadcast_op_extended.cc @@ -100,6 +100,41 @@ NNVM_REGISTER_OP(_npi_lcm_scalar) .add_argument("scalar", "int", "scalar input") .set_attr("FCompute", BinaryScalarOp::Compute); +NNVM_REGISTER_OP(_npi_bitwise_and) +.set_num_inputs(2) +.set_num_outputs(1) +.set_attr("FListInputNames", +[](const NodeAttrs& attrs) { + return std::vector{"lhs", "rhs"}; +}) +.set_attr("FInferShape", BinaryBroadcastShape) +.set_attr("FInferType", ElemwiseIntType<2, 1>) +.set_attr("FInplaceOption", +[](const NodeAttrs& attrs){ + return std::vector >{{0, 0}, {1, 0}}; +}) +.set_attr("FGradient", MakeZeroGradNodes) +.set_attr("FCompute", BinaryBroadcastIntCompute) +.add_argument("lhs", "NDArray-or-Symbol", "First input to the function") +.add_argument("rhs", "NDArray-or-Symbol", "Second input to the function"); + +NNVM_REGISTER_OP(_npi_bitwise_and_scalar) +.set_num_inputs(1) +.set_num_outputs(1) +.set_attr_parser([](NodeAttrs* attrs) { + attrs->parsed = std::stod(attrs->dict["scalar"]); + }) +.set_attr("FInferShape", ElemwiseShape<1, 1>) +.set_attr("FInferType", ElemwiseIntType<1, 1>) +.set_attr("FInplaceOption", + [](const NodeAttrs& attrs){ + return std::vector >{{0, 0}}; + }) +.set_attr("FGradient", MakeZeroGradNodes) +.add_argument("data", "NDArray-or-Symbol", "source input") +.add_argument("scalar", "int", "scalar input") +.set_attr("FCompute", BinaryScalarOp::ComputeInt); + NNVM_REGISTER_OP(_npi_bitwise_xor) .set_num_inputs(2) .set_num_outputs(1) diff --git a/src/operator/numpy/np_elemwise_broadcast_op_extended.cu b/src/operator/numpy/np_elemwise_broadcast_op_extended.cu index 90f11b1cd93a..8f135b3efd03 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op_extended.cu +++ b/src/operator/numpy/np_elemwise_broadcast_op_extended.cu @@ -34,6 +34,9 @@ NNVM_REGISTER_OP(_npi_copysign) NNVM_REGISTER_OP(_npi_lcm) .set_attr("FCompute", BinaryBroadcastIntCompute); +NNVM_REGISTER_OP(_npi_bitwise_and) +.set_attr("FCompute", BinaryBroadcastIntCompute); + NNVM_REGISTER_OP(_npi_bitwise_xor) .set_attr("FCompute", BinaryBroadcastIntCompute); @@ -85,6 +88,9 @@ NNVM_REGISTER_OP(_backward_npi_rarctan2_scalar) NNVM_REGISTER_OP(_npi_lcm_scalar) .set_attr("FCompute", BinaryScalarOp::ComputeInt); +NNVM_REGISTER_OP(_npi_bitwise_and_scalar) +.set_attr("FCompute", BinaryScalarOp::ComputeInt); + NNVM_REGISTER_OP(_npi_bitwise_xor_scalar) .set_attr("FCompute", BinaryScalarOp::ComputeInt); diff --git a/src/operator/operator_tune.cc b/src/operator/operator_tune.cc index 249da3d049c1..a88854f0c1a2 100644 --- a/src/operator/operator_tune.cc +++ b/src/operator/operator_tune.cc @@ -396,6 +396,7 @@ IMPLEMENT_BINARY_WORKLOAD_FWD(mxnet::op::mshadow_op::logical_or); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_BWD(mxnet::op::mshadow_op::logical_or); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_FWD(mxnet::op::mshadow_op::logical_xor); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_BWD(mxnet::op::mshadow_op::logical_xor); // NOLINT() +IMPLEMENT_BINARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::bitwise_and); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::bitwise_xor); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_FWD_WITH_BOOL(mxnet::op::mshadow_op::bitwise_or); // NOLINT() IMPLEMENT_BINARY_WORKLOAD_FWD(mxnet::op::mshadow_op::smooth_l1_loss); // NOLINT() diff --git a/tests/python/unittest/test_numpy_interoperability.py b/tests/python/unittest/test_numpy_interoperability.py index 930ad5260430..dd30e60b45e9 100644 --- a/tests/python/unittest/test_numpy_interoperability.py +++ b/tests/python/unittest/test_numpy_interoperability.py @@ -875,6 +875,18 @@ def _add_workload_bitwise_or(): OpArgMngr.add_workload('bitwise_or', zeros, ones) OpArgMngr.add_workload('bitwise_or', ones, ones) +def _add_workload_bitwise_and(): + OpArgMngr.add_workload('bitwise_and', np.array([False, False, True, True], dtype=np.bool), + np.array([False, True, False, True], dtype=np.bool)) + for dtype in [np.int8, np.int32, np.int64]: + zeros = np.array([0], dtype=dtype) + ones = np.array([-1], dtype=dtype) + OpArgMngr.add_workload('bitwise_and', zeros, zeros) + OpArgMngr.add_workload('bitwise_and', ones, zeros) + OpArgMngr.add_workload('bitwise_and', zeros, ones) + OpArgMngr.add_workload('bitwise_and', ones, ones) + + def _add_workload_bitwise_xor(): OpArgMngr.add_workload('bitwise_xor', np.array([False, False, True, True], dtype=np.bool), np.array([False, True, False, True], dtype=np.bool)) @@ -1368,6 +1380,7 @@ def _prepare_workloads(): _add_workload_inner() _add_workload_hypot() _add_workload_lcm() + _add_workload_bitwise_and() _add_workload_bitwise_xor() _add_workload_bitwise_or() _add_workload_ldexp() diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 9b7f7036bcda..7b28408de054 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -1670,6 +1670,7 @@ def hybrid_forward(self, F, a, b, *args, **kwargs): 'power': (1.0, 2.0, [lambda y, x1, x2: _np.power(x1, x2 - 1.0) * x2], [lambda y, x1, x2: _np.power(x1, x2) * _np.log(x1)]), 'lcm': (-100, 100, [None], None, [[_np.int32]]), + 'bitwise_and': (-100, 100, [None], None, [[_np.int32]]), 'bitwise_xor': (-100, 100, [None], None, [[_np.int32]]), 'bitwise_or': (-100, 100, [None], None, [[_np.int32]]), 'maximum': (-1, 1, [lambda y, x1, x2: _np.ones(y.shape) * (x1 >= x2)],