From e8e46ff1a792a3bb8a662392db10ade82eae470a Mon Sep 17 00:00:00 2001 From: Hao Jin Date: Tue, 26 Nov 2019 09:06:15 +0000 Subject: [PATCH] port shape op to 1.6.x --- python/mxnet/ndarray/numpy/_op.py | 40 +++++++++-- python/mxnet/numpy/multiarray.py | 68 ++++++++++++++----- python/mxnet/numpy_dispatch_protocol.py | 1 + .../unittest/test_numpy_interoperability.py | 15 ++-- tests/python/unittest/test_numpy_op.py | 19 ++++++ 5 files changed, 119 insertions(+), 24 deletions(-) diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index ff404a7a2df7..e48b95bb29a3 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -28,7 +28,7 @@ from . import _internal as _npi from ..ndarray import NDArray -__all__ = ['zeros', 'ones', 'full', 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', +__all__ = ['shape', 'zeros', 'ones', 'full', 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', 'arctan2', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'log10', 'sqrt', 'cbrt', 'abs', 'absolute', 'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor', @@ -41,7 +41,37 @@ 'hsplit', 'rot90', 'einsum', 'true_divide', 'nonzero', 'shares_memory', 'may_share_memory', 'diff'] @set_module('mxnet.ndarray.numpy') -def zeros(shape, dtype=_np.float32, order='C', ctx=None): +def shape(a): + """ + Return the shape of an array. + Parameters + ---------- + a : array_like + Input array. + Returns + ------- + shape : tuple of ints + The elements of the shape tuple give the lengths of the + corresponding array dimensions. + See Also + -------- + ndarray.shape : Equivalent array method. + Examples + -------- + >>> np.shape(np.eye(3)) + (3, 3) + >>> np.shape([[1, 2]]) + (1, 2) + >>> np.shape([0]) + (1,) + >>> np.shape(0) + () + """ + return a.shape + + +@set_module('mxnet.ndarray.numpy') +def zeros(shape, dtype=_np.float32, order='C', ctx=None): # pylint: disable=redefined-outer-name """Return a new array of given shape and type, filled with zeros. This function currently only supports storing multi-dimensional data in row-major (C-style). @@ -75,7 +105,7 @@ def zeros(shape, dtype=_np.float32, order='C', ctx=None): @set_module('mxnet.ndarray.numpy') -def ones(shape, dtype=_np.float32, order='C', ctx=None): +def ones(shape, dtype=_np.float32, order='C', ctx=None): # pylint: disable=redefined-outer-name """Return a new array of given shape and type, filled with ones. This function currently only supports storing multi-dimensional data in row-major (C-style). @@ -108,8 +138,9 @@ def ones(shape, dtype=_np.float32, order='C', ctx=None): return _npi.ones(shape=shape, ctx=ctx, dtype=dtype) +# pylint: disable=too-many-arguments, redefined-outer-name @set_module('mxnet.ndarray.numpy') -def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): # pylint: disable=too-many-arguments +def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): """ Return a new array of given shape and type, filled with `fill_value`. Parameters @@ -161,6 +192,7 @@ def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): # pylin ctx = current_context() dtype = _np.float32 if dtype is None else dtype return _npi.full(shape=shape, value=fill_value, ctx=ctx, dtype=dtype, out=out) +# pylint: enable=too-many-arguments, redefined-outer-name @set_module('mxnet.ndarray.numpy') diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index c623f67967ba..f2cea05d4ea7 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -45,7 +45,7 @@ from ..ndarray import numpy as _mx_nd_np from ..ndarray.numpy import _internal as _npi -__all__ = ['ndarray', 'empty', 'array', 'zeros', 'ones', 'full', 'add', 'subtract', 'multiply', 'divide', +__all__ = ['ndarray', 'empty', 'array', 'shape', 'zeros', 'ones', 'full', 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', 'arctan2', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'log10', 'sqrt', 'cbrt', 'abs', 'absolute', 'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', @@ -66,7 +66,7 @@ # This function is copied from ndarray.py since pylint # keeps giving false alarm error of undefined-all-variable -def _new_alloc_handle(shape, ctx, delay_alloc, dtype=mx_real_t): +def _new_alloc_handle(shape, ctx, delay_alloc, dtype=mx_real_t): # pylint: disable=redefined-outer-name """Return a new handle with specified shape and context. Empty handle is only used to hold results. @@ -88,7 +88,7 @@ def _new_alloc_handle(shape, ctx, delay_alloc, dtype=mx_real_t): return hdl -def _reshape_view(a, *shape): +def _reshape_view(a, *shape): # pylint: disable=redefined-outer-name """Returns a **view** of this array with a new shape without altering any data. Parameters @@ -459,7 +459,7 @@ def __getitem__(self, key): """ # handling possible boolean indexing first ndim = self.ndim - shape = self.shape + shape = self.shape # pylint: disable=redefined-outer-name if isinstance(key, list): try: @@ -801,7 +801,7 @@ def __int__(self): def __len__(self): """Number of elements along the first axis.""" - shape = self.shape + shape = self.shape # pylint: disable=redefined-outer-name if len(shape) == 0: raise TypeError('len() of unsized object') return self.shape[0] @@ -1161,7 +1161,7 @@ def reshape_like(self, *args, **kwargs): """ raise AttributeError('mxnet.numpy.ndarray object has no attribute reshape_like') - def reshape_view(self, *shape, **kwargs): + def reshape_view(self, *shape, **kwargs): # pylint: disable=redefined-outer-name """Returns a **view** of this array with a new shape without altering any data. Inheritated from NDArray.reshape. """ @@ -1515,13 +1515,15 @@ def mean(self, axis=None, dtype=None, out=None, keepdims=False): # pylint: disa """Returns the average of the array elements along given axis.""" return mean(self, axis=axis, dtype=dtype, out=out, keepdims=keepdims) - def std(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): # pylint: disable=arguments-differ + # pylint: disable=too-many-arguments, arguments-differ + def std(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): """Returns the standard deviation of the array elements along given axis.""" return std(self, axis=axis, dtype=dtype, ddof=ddof, keepdims=keepdims, out=out) - def var(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): # pylint: disable=arguments-differ + def var(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): """Returns the variance of the array elements, along given axis.""" return var(self, axis=axis, dtype=dtype, out=out, ddof=ddof, keepdims=keepdims) + # pylint: enable=too-many-arguments, arguments-differ def cumsum(self, axis=None, dtype=None, out=None): """Return the cumulative sum of the elements along the given axis.""" @@ -1850,7 +1852,7 @@ def squeeze(self, axis=None): # pylint: disable=arguments-differ """Remove single-dimensional entries from the shape of a.""" return _mx_np_op.squeeze(self, axis=axis) - def broadcast_to(self, shape): + def broadcast_to(self, shape): # pylint: disable=redefined-outer-name return _mx_np_op.broadcast_to(self, shape) def broadcast_like(self, other): @@ -1912,7 +1914,7 @@ def tostype(self, stype): @set_module('mxnet.numpy') -def empty(shape, dtype=_np.float32, order='C', ctx=None): +def empty(shape, dtype=_np.float32, order='C', ctx=None): # pylint: disable=redefined-outer-name """Return a new array of given shape and type, without initializing entries. Parameters @@ -2016,7 +2018,37 @@ def array(object, dtype=None, ctx=None): @set_module('mxnet.numpy') -def zeros(shape, dtype=_np.float32, order='C', ctx=None): +def shape(a): + """ + Return the shape of an array. + Parameters + ---------- + a : array_like + Input array. + Returns + ------- + shape : tuple of ints + The elements of the shape tuple give the lengths of the + corresponding array dimensions. + See Also + -------- + ndarray.shape : Equivalent array method. + Examples + -------- + >>> np.shape(np.eye(3)) + (3, 3) + >>> np.shape([[1, 2]]) + (1, 2) + >>> np.shape([0]) + (1,) + >>> np.shape(0) + () + """ + return _mx_nd_np.shape(a) + + +@set_module('mxnet.numpy') +def zeros(shape, dtype=_np.float32, order='C', ctx=None): # pylint: disable=redefined-outer-name """Return a new array of given shape and type, filled with zeros. This function currently only supports storing multi-dimensional data in row-major (C-style). @@ -2057,7 +2089,7 @@ def zeros(shape, dtype=_np.float32, order='C', ctx=None): @set_module('mxnet.numpy') -def ones(shape, dtype=_np.float32, order='C', ctx=None): +def ones(shape, dtype=_np.float32, order='C', ctx=None): # pylint: disable=redefined-outer-name """Return a new array of given shape and type, filled with ones. This function currently only supports storing multi-dimensional data in row-major (C-style). @@ -2102,8 +2134,9 @@ def ones(shape, dtype=_np.float32, order='C', ctx=None): return _mx_nd_np.ones(shape, dtype, order, ctx) +# pylint: disable=too-many-arguments, redefined-outer-name @set_module('mxnet.numpy') -def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): # pylint: disable=too-many-arguments +def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): """ Return a new array of given shape and type, filled with `fill_value`. @@ -2156,6 +2189,7 @@ def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): # pylin [2, 2]], dtype=int32) """ return _mx_nd_np.full(shape, fill_value, order=order, ctx=ctx, dtype=dtype, out=out) +# pylint: enable=too-many-arguments, redefined-outer-name @set_module('mxnet.numpy') @@ -4220,7 +4254,7 @@ def tensordot(a, b, axes=2): @set_module('mxnet.numpy') -def histogram(a, bins=10, range=None, normed=None, weights=None, density=None): # pylint-disable=too-many-arguments +def histogram(a, bins=10, range=None, normed=None, weights=None, density=None): # pylint: disable=too-many-arguments """ Compute the histogram of a set of data. @@ -4379,6 +4413,7 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis return _mx_nd_np.linspace(start, stop, num, endpoint, retstep, dtype, axis, ctx) +# pylint: disable=too-many-arguments @set_module('mxnet.numpy') def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0, ctx=None): r"""Return numbers spaced evenly on a log scale. @@ -4453,6 +4488,7 @@ def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0, array([ 100. , 215.44347, 464.15887, 1000. ], ctx=gpu(0)) """ return _mx_nd_np.logspace(start, stop, num, endpoint, base, dtype, axis, ctx=ctx) +# pylint: enable=too-many-arguments @set_module('mxnet.numpy') @@ -5418,7 +5454,7 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=False): # pylint: disable @set_module('mxnet.numpy') -def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): # pylint: disable=too-many-arguments """ Compute the standard deviation along the specified axis. Returns the standard deviation, a measure of the spread of a distribution, @@ -5485,7 +5521,7 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): @set_module('mxnet.numpy') -def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): # pylint: disable=too-many-arguments """ Compute the variance along the specified axis. Returns the variance of the array elements, a measure of the spread of a diff --git a/python/mxnet/numpy_dispatch_protocol.py b/python/mxnet/numpy_dispatch_protocol.py index cdd21af829de..47a71ec89a63 100644 --- a/python/mxnet/numpy_dispatch_protocol.py +++ b/python/mxnet/numpy_dispatch_protocol.py @@ -125,6 +125,7 @@ def _run_with_array_ufunc_proto(*args, **kwargs): 'column_stack', 'zeros_like', 'linalg.norm', + 'shape', 'trace', 'tril', 'meshgrid', diff --git a/tests/python/unittest/test_numpy_interoperability.py b/tests/python/unittest/test_numpy_interoperability.py index 8416b1a9099f..1544db2353a3 100644 --- a/tests/python/unittest/test_numpy_interoperability.py +++ b/tests/python/unittest/test_numpy_interoperability.py @@ -1110,6 +1110,12 @@ def _add_workload_nonzero(): OpArgMngr.add_workload('nonzero', np.array([True, False, False], dtype=np.bool_)) +def _add_workload_shape(): + OpArgMngr.add_workload('shape', np.random.uniform(size=())) + OpArgMngr.add_workload('shape', np.random.uniform(size=(0, 1))) + OpArgMngr.add_workload('shape', np.random.uniform(size=(2, 3))) + + def _add_workload_diff(): x = np.array([1, 4, 6, 7, 12]) OpArgMngr.add_workload('diff', x) @@ -1240,6 +1246,7 @@ def _prepare_workloads(): _add_workload_greater_equal(array_pool) _add_workload_less(array_pool) _add_workload_less_equal(array_pool) + _add_workload_shape() _add_workload_diff() @@ -1271,11 +1278,11 @@ def _check_interoperability_helper(op_name, *args, **kwargs): expected_out = _get_numpy_op_output(onp_op, *args, **kwargs) if isinstance(out, (tuple, list)): assert type(out) == type(expected_out) - for arr in out: - assert isinstance(arr, np.ndarray) for arr, expected_arr in zip(out, expected_out): - assert isinstance(arr, np.ndarray) - assert_almost_equal(arr.asnumpy(), expected_arr, rtol=1e-3, atol=1e-4, use_broadcast=False, equal_nan=True) + if isinstance(arr, np.ndarray): + assert_almost_equal(arr.asnumpy(), expected_arr, rtol=1e-3, atol=1e-4, use_broadcast=False, equal_nan=True) + else: + _np.testing.assert_equal(arr, expected_arr) else: assert isinstance(out, np.ndarray) assert_almost_equal(out.asnumpy(), expected_out, rtol=1e-3, atol=1e-4, use_broadcast=False, equal_nan=True) diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 643b9c1e9ba0..64a07e7d672a 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -756,6 +756,25 @@ def legalize_shape(shape): np_out = getattr(_np, name)(x.asnumpy(), axis=axis, dtype=acc_type[itype], keepdims=keepdims, ddof=ddof).astype(dtype) assert_almost_equal(mx_out.asnumpy(), np_out, rtol=rtol, atol=atol, use_broadcast=False, equal_nan=True) +@with_seed() +@use_np +def test_np_shape(): + shapes = [ + (), + (0, 1), + (2, 3), + (2, 3, 4), + ] + + for shape in shapes: + mx_a = np.random.uniform(size=shape) + np_a = _np.random.uniform(size=shape) + + mx_shape = np.shape(mx_a) + np_shape = _np.shape(np_a) + + assert mx_shape == np_shape + @with_seed() @use_np