From 77c0a2f8c1b688eb813247f4f740ff9580dbba16 Mon Sep 17 00:00:00 2001 From: Hao Jin Date: Wed, 20 Nov 2019 01:33:07 +0000 Subject: [PATCH] support bitwise_and --- python/mxnet/ndarray/numpy/_op.py | 49 +++++++++++++++++-- python/mxnet/numpy/multiarray.py | 48 ++++++++++++++++-- python/mxnet/numpy_dispatch_protocol.py | 1 + python/mxnet/symbol/numpy/_symbol.py | 33 +++++++++++-- src/operator/mshadow_op.h | 2 + .../np_elemwise_broadcast_op_extended.cc | 35 +++++++++++++ .../np_elemwise_broadcast_op_extended.cu | 6 +++ src/operator/operator_tune.cc | 1 + .../unittest/test_numpy_interoperability.py | 13 +++++ tests/python/unittest/test_numpy_op.py | 1 + 10 files changed, 178 insertions(+), 11 deletions(-) diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index 7efaefebe71d..b95e0529f202 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -38,10 +38,11 @@ 'stack', 'vstack', 'row_stack', 'column_stack', 'hstack', 'dstack', 'average', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'indices', 'copysign', 'ravel', 'unravel_index', - 'hanning', 'hamming', 'blackman', 'flip', 'flipud', 'fliplr', 'around', 'round', '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', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', + 'hanning', 'hamming', 'blackman', 'flip', 'flipud', 'fliplr', 'around', 'round', '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', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', 'resize', 'nan_to_num', 'where', 'bincount'] @@ -5254,6 +5255,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 ae3e5066500f..c768d1e6d8c1 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -60,7 +60,8 @@ 'split', 'vsplit', 'concatenate', 'stack', 'vstack', 'row_stack', 'column_stack', 'hstack', 'dstack', 'average', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'indices', 'copysign', 'ravel', 'unravel_index', 'hanning', 'hamming', 'blackman', 'flip', 'flipud', - 'fliplr', 'around', 'round', 'arctan2', 'hypot', 'bitwise_xor', 'bitwise_or', 'rad2deg', 'deg2rad', + 'fliplr', 'around', 'round', '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', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', 'resize', 'nan_to_num', 'where', @@ -68,6 +69,7 @@ __all__ += fallback.__all__ + # Return code for dispatching indexing function call _NDARRAY_UNSUPPORTED_INDEXING = -1 _NDARRAY_BASIC_INDEXING = 0 @@ -7094,6 +7096,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): @@ -7122,10 +7164,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 3fb788bcd050..7a7376b65309 100644 --- a/python/mxnet/numpy_dispatch_protocol.py +++ b/python/mxnet/numpy_dispatch_protocol.py @@ -257,6 +257,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 9b22f3acf7e7..29e22d3ba97b 100644 --- a/python/mxnet/symbol/numpy/_symbol.py +++ b/python/mxnet/symbol/numpy/_symbol.py @@ -46,10 +46,11 @@ 'stack', 'vstack', 'row_stack', 'column_stack', 'hstack', 'dstack', 'average', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'indices', 'copysign', 'ravel', 'unravel_index', - 'hanning', 'hamming', 'blackman', 'flip', 'flipud', 'fliplr', 'around', 'round', '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', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', + 'hanning', 'hamming', 'blackman', 'flip', 'flipud', 'fliplr', 'around', 'round', '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', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', 'resize', 'nan_to_num', 'where', 'bincount'] @@ -4896,6 +4897,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 4176d3a68792..fb5dc789cf3e 100644 --- a/src/operator/mshadow_op.h +++ b/src/operator/mshadow_op.h @@ -618,6 +618,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 9ca291391c5c..745c3d5fe86d 100644 --- a/src/operator/operator_tune.cc +++ b/src/operator/operator_tune.cc @@ -397,6 +397,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 24a4da984414..fb0b82fbcdd7 100644 --- a/tests/python/unittest/test_numpy_interoperability.py +++ b/tests/python/unittest/test_numpy_interoperability.py @@ -1229,6 +1229,18 @@ def _add_workload_bitwise_or(): 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)) @@ -1816,6 +1828,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 f8d6817ea444..dc17c0f25d35 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -2063,6 +2063,7 @@ def hybrid_forward(self, F, a, b, *args, **kwargs): 'power': (1.0, 3.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)],