From 79c08d3c463910ff98118caf6801211ea7d6d74d Mon Sep 17 00:00:00 2001 From: alicia <32725332+Alicia1529@users.noreply.github.com> Date: Sat, 19 Oct 2019 13:16:12 +0800 Subject: [PATCH 1/2] numpy op doc: max, min, prod (#16506) --- python/mxnet/_numpy_op_doc.py | 213 +++++++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 2 deletions(-) diff --git a/python/mxnet/_numpy_op_doc.py b/python/mxnet/_numpy_op_doc.py index f08d19a66549..ce8be5988247 100644 --- a/python/mxnet/_numpy_op_doc.py +++ b/python/mxnet/_numpy_op_doc.py @@ -130,8 +130,8 @@ def _npx_nonzero(a): Notes ----- - This function differs from the original numpy.prod in the following aspects: - - Do not support python numeric. + This function differs from the original numpy.nonzero in the following aspects: + - Does not support python numeric. - The return value is same as numpy.transpose(numpy.nonzero(a)). Examples @@ -706,6 +706,214 @@ def _np_squeeze(a, axis=None, out=None): pass +def _np_max(a, axis=None, out=None, keepdims=False): + """ + Return the maximum of an array or maximum along an axis. + + Parameters + ---------- + a : ndarray + Input data. + axis : int, optional + Axis along which to operate. By default, flattened input is used. + out : ndarray, optional + Alternative output array in which to place the result. Must + be of the same shape and buffer length as the expected output. + See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `arr`. + + Returns + ------- + max : ndarray + Maximum of `a`. If `axis` is None, the result is an array of dimension 1. + If `axis` is given, the result is an array of dimension + ``a.ndim - 1``. + + See Also + -------- + min : + The minimum value of an array along a given axis, ignoring any nan. + maximum : + Element-wise maximum of two arrays, ignoring any nan. + argmax : + Return the indices of the maximum values. + + Notes + ----- + NaN in the orginal `numpy` is denoted as nan and will be ignored. + + Don't use `max` for element-wise comparison of 2 arrays; when + ``a.shape[0]`` is 2, ``maximum(a[0], a[1])`` is faster than + ``max(a, axis=0)``. + + Examples + -------- + >>> a = np.arange(4).reshape((2,2)) + >>> a + array([[0., 1.], + [2., 3.]]) + >>> np.max(a) # Maximum of the flattened array + array(3.) + >>> np.max(a, axis=0) # Maxima along the first axis + array([2., 3.]) + >>> np.max(a, axis=1) # Maxima along the second axis + array([1., 3.]) + + >>> b = np.arange(5, dtype=np.float32) + >>> b[2] = np.nan + >>> np.max(b) + array(4.) + """ + pass + + +def _np_min(a, axis=None, out=None, keepdims=False): + """ + Return the minimum of an array or minimum along an axis. + + Parameters + ---------- + a : ndarray + Input data. + axis : int, optional + Axis along which to operate. By default, flattened input is used. + out : ndarray, optional + Alternative output array in which to place the result. Must + be of the same shape and buffer length as the expected output. + See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `arr`. + + Returns + ------- + min : ndarray + Minimum of `a`. If `axis` is None, the result is an array of dimension 1. + If `axis` is given, the result is an array of dimension + ``a.ndim - 1``. + + See Also + -------- + max : + The maximum value of an array along a given axis, ignoring any nan. + minimum : + Element-wise minimum of two arrays, ignoring any nan. + + Notes + ----- + NaN in the orginal `numpy` is denoted as nan and will be ignored. + + Don't use `min` for element-wise comparison of 2 arrays; when + ``a.shape[0]`` is 2, ``minimum(a[0], a[1])`` is faster than + ``min(a, axis=0)``. + + Examples + -------- + >>> a = np.arange(4).reshape((2,2)) + >>> a + array([[0., 1.], + [2., 3.]]) + >>> np.min(a) # Minimum of the flattened array + array(0.) + >>> np.min(a, axis=0) # Minima along the first axis + array([0., 1.]) + >>> np.min(a, axis=1) # Minima along the second axis + array([0., 2.]) + >>> b = np.arange(5, dtype=np.float32) + >>> b[2] = np.nan + >>> np.min(b) + array(0.) # nan will be ignored + """ + pass + + +def _np_prod(a, axis=None, dtype=None, out=None, keepdims=False): + """ + Return the product of array elements over a given axis. + + Parameters + ---------- + a : ndarray + Input data. + axis : None or int or tuple of ints, optional + Axis or axes along which a product is performed. + The default (`axis` = `None`) is perform a product over all + the dimensions of the input array. `axis` may be negative, in + which case it counts from the last to the first axis. + If this is a tuple of ints, a product is performed on multiple + axes, instead of a single axis or all the axes as before. + dtype : data-type, optional + The data-type of the returned array, as well as of the accumulator + in which the elements are multiplied. By default, if `a` is of + integer type, `dtype` is the default platform integer. (Note: if + the type of `a` is unsigned, then so is `dtype`.) Otherwise, + the dtype is the same as that of `a`. + out : ndarray, optional + Alternative output array in which to place the result. It must have + the same shape as the expected output, but the type of the + output values will be cast if necessary. + keepdims : bool, optional + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `arr`. + + Returns + ------- + product_along_axis : ndarray, see `dtype` parameter above. + An array shaped as `a` but with the specified axis removed. + Returns a reference to `out` if specified. + + See Also + -------- + ndarray.prod : equivalent method + + Notes + ----- + Arithmetic is modular when using integer types, and no error is + raised on overflow. That means that, on a 32-bit platform: + + >>> x = np.array([536870910, 536870910, 536870910, 536870910]) + >>> np.prod(x) #random + array(8.307675e+34) + + Examples + -------- + By default, calculate the product of all elements: + + >>> np.prod(np.array([1.,2.])) + array(2.) + + Even when the input array is two-dimensional: + + >>> np.prod(np.array([1.,2.,3.,4.]).reshape((2,2))) + array(24.) + + But we can also specify the axis over which to multiply: + + >>> np.prod(np.array([1.,2.,3.,4.]).reshape((2,2)), axis=1) + array([ 2., 12.]) + + If the type of `x` is unsigned, then the output type is + the unsigned platform integer: + + >>> x = np.array([1, 2, 3], dtype=np.uint8) + >>> np.prod(x).dtype == np.uint8 + True + + If `x` is of a signed integer type, then the output type + is the default platform integer: + + >>> x = np.array([1, 2, 3], dtype=np.int8) + >>> np.prod(x).dtype == np.int8 + True + """ + pass + + def _np_moveaxis(a, source, destination): """Move axes of an array to new positions. Other axes remain in their original order. @@ -784,6 +992,7 @@ def _np__random_shuffle(x): """ pass + def _np_broadcast_to(array, shape, out=None): """ Broadcast an array to a new shape. From c2cf2a3010b36a00defb1ba5c0c6953fb8a3522e Mon Sep 17 00:00:00 2001 From: kshitij12345 Date: Thu, 15 Aug 2019 17:27:08 +0530 Subject: [PATCH 2/2] add interface for rand add relevant tests address comments. * fix document string -> Returns description. Fix --- python/mxnet/ndarray/numpy/random.py | 29 ++++++++++++- python/mxnet/numpy/random.py | 30 ++++++++++++- python/mxnet/symbol/numpy/random.py | 30 ++++++++++++- tests/python/unittest/test_numpy_op.py | 58 +++++++++++++++++++++++--- 4 files changed, 137 insertions(+), 10 deletions(-) diff --git a/python/mxnet/ndarray/numpy/random.py b/python/mxnet/ndarray/numpy/random.py index 9e401695709d..583f56e046f3 100644 --- a/python/mxnet/ndarray/numpy/random.py +++ b/python/mxnet/ndarray/numpy/random.py @@ -23,7 +23,7 @@ from ..ndarray import NDArray -__all__ = ['randint', 'uniform', 'normal', "choice"] +__all__ = ['randint', 'uniform', 'normal', "choice", "rand"] def randint(low, high=None, size=None, dtype=None, ctx=None, out=None): @@ -317,3 +317,30 @@ def choice(a, size=None, replace=True, p=None, ctx=None, out=None): return _npi.choice(a=a, size=size, replace=replace, ctx=ctx, weighted=False, out=out) else: return _npi.choice(p, a=a, size=size, replace=replace, ctx=ctx, weighted=True, out=out) + + +def rand(*size, **kwargs): + r"""Random values in a given shape. + + Create an array of the given shape and populate it with random + samples from a uniform distribution over [0, 1). + Parameters + ---------- + d0, d1, ..., dn : int, optional + The dimensions of the returned array, should be all positive. + If no argument is given a single Python float is returned. + Returns + ------- + out : ndarray + Random values. + Examples + -------- + >>> np.random.rand(3,2) + array([[ 0.14022471, 0.96360618], #random + [ 0.37601032, 0.25528411], #random + [ 0.49313049, 0.94909878]]) #random + """ + output_shape = () + for s in size: + output_shape += (s,) + return uniform(0, 1, size=output_shape, **kwargs) diff --git a/python/mxnet/numpy/random.py b/python/mxnet/numpy/random.py index 746ce99c6d9c..d0ae237a5b92 100644 --- a/python/mxnet/numpy/random.py +++ b/python/mxnet/numpy/random.py @@ -20,8 +20,7 @@ from __future__ import absolute_import from ..ndarray import numpy as _mx_nd_np - -__all__ = ["randint", "uniform", "normal", "choice"] +__all__ = ["randint", "uniform", "normal", "choice", "rand"] def randint(low, high=None, size=None, dtype=None, ctx=None, out=None): @@ -231,3 +230,30 @@ def choice(a, size=None, replace=True, p=None, ctx=None, out=None): array([2, 3, 0]) """ return _mx_nd_np.random.choice(a, size, replace, p, ctx, out) + + +def rand(*size, **kwargs): + r"""Random values in a given shape. + + Create an array of the given shape and populate it with random + samples from a uniform distribution over [0, 1). + Parameters + ---------- + d0, d1, ..., dn : int, optional + The dimensions of the returned array, should be all positive. + If no argument is given a single Python float is returned. + Returns + ------- + out : ndarray + Random values. + Examples + -------- + >>> np.random.rand(3,2) + array([[ 0.14022471, 0.96360618], #random + [ 0.37601032, 0.25528411], #random + [ 0.49313049, 0.94909878]]) #random + """ + output_shape = () + for s in size: + output_shape += (s,) + return _mx_nd_np.random.uniform(0, 1, size=output_shape, **kwargs) diff --git a/python/mxnet/symbol/numpy/random.py b/python/mxnet/symbol/numpy/random.py index 84cc5704b138..d891ea0c21a0 100644 --- a/python/mxnet/symbol/numpy/random.py +++ b/python/mxnet/symbol/numpy/random.py @@ -21,8 +21,7 @@ from ...context import current_context from . import _internal as _npi - -__all__ = ['randint', 'uniform', 'normal'] +__all__ = ['randint', 'uniform', 'normal', 'rand'] def randint(low, high=None, size=None, dtype=None, ctx=None, out=None): @@ -86,6 +85,33 @@ def randint(low, high=None, size=None, dtype=None, ctx=None, out=None): return _npi.random_randint(low, high, shape=size, dtype=dtype, ctx=ctx, out=out) +def rand(*size, **kwargs): + r"""Random values in a given shape. + + Create an array of the given shape and populate it with random + samples from a uniform distribution over [0, 1). + Parameters + ---------- + d0, d1, ..., dn : int, optional + The dimensions of the returned array, should be all positive. + If no argument is given a single Python float is returned. + Returns + ------- + out : ndarray + Random values. + Examples + -------- + >>> np.random.rand(3,2) + array([[ 0.14022471, 0.96360618], #random + [ 0.37601032, 0.25528411], #random + [ 0.49313049, 0.94909878]]) #random + """ + output_shape = () + for s in size: + output_shape += (s,) + return uniform(0, 1, size=output_shape, **kwargs) + + def uniform(low=0.0, high=1.0, size=None, dtype=None, ctx=None, out=None): """Draw samples from a uniform distribution. diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 5676ff8ac919..99833d11b087 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -20,7 +20,9 @@ import sys import unittest import numpy as _np +import platform import mxnet as mx +import scipy.stats as ss from mxnet import np, npx from mxnet.gluon import HybridBlock from mxnet.base import MXNetError @@ -28,13 +30,9 @@ from mxnet.test_utils import check_numeric_gradient, use_np, collapse_sum_like from common import assertRaises, with_seed import random -import scipy.stats as ss -from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf, retry -from mxnet.runtime import Features +from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf from mxnet.numpy_op_signature import _get_builtin_op -from mxnet.test_utils import current_context, verify_generator, gen_buckets_probs_with_ppf from mxnet.test_utils import is_op_runnable, has_tvm_ops -import platform @with_seed() @@ -3439,6 +3437,56 @@ def dbg(name, data): assert_almost_equal(grad[0][iop], grad[1][iop], rtol=rtol, atol=atol) +@with_seed() +@use_np +def test_np_rand(): + # Test shapes. + shapes = [ + (3, 3), + (3, 4), + (0, 0), + (3, 3, 3), + (0, 0, 0), + (2, 2, 4, 3), + (2, 2, 4, 3), + (2, 0, 3, 0), + (2, 0, 2, 3) + ] + dtypes = ['float16', 'float32', 'float64'] + for dtype in dtypes: + for shape in shapes: + data_mx = np.random.rand(*shape, dtype=dtype) + assert data_mx.shape == shape + + # Test random generator. + ctx = mx.context.current_context() + samples = 1000000 + trials = 8 + num_buckets = 10 + lower = 0.0 + upper = 1.0 + for dtype in ['float16', 'float32', 'float64']: + buckets, probs = gen_buckets_probs_with_ppf( + lambda x: ss.uniform.ppf(x, lower, upper), num_buckets) + # Quantize bucket boundaries to reflect the actual dtype + # and adjust probs accordingly + buckets = np.array(buckets, dtype=dtype).tolist() + probs = [(ss.uniform.cdf(buckets[i][1], lower, upper) - + ss.uniform.cdf(buckets[i][0], lower, upper)) + for i in range(num_buckets)] + + def generator_mx(x): return np.random.rand( + samples, ctx=ctx, dtype=dtype).asnumpy() + verify_generator(generator=generator_mx, buckets=buckets, + probs=probs, nsamples=samples, nrepeat=trials) + generator_mx_same_seed =\ + lambda x: _np.concatenate( + [np.random.rand(x // 10, ctx=ctx, dtype=dtype).asnumpy() + for _ in range(10)]) + verify_generator(generator=generator_mx_same_seed, buckets=buckets, + probs=probs, nsamples=samples, nrepeat=trials) + + if __name__ == '__main__': import nose nose.runmodule()