From f6cfbdf92f30eb607b6d4e4a52376a7b0bc4e20e Mon Sep 17 00:00:00 2001 From: Hao Jin Date: Thu, 17 Oct 2019 18:23:36 -0700 Subject: [PATCH] improve unary and binary operator handling and refactor tests (#16423) --- python/mxnet/ndarray/numpy/_op.py | 358 ++++++++++--- python/mxnet/numpy/multiarray.py | 347 +++++++++--- python/mxnet/symbol/numpy/_symbol.py | 233 ++++++-- python/mxnet/util.py | 106 ++++ .../numpy/np_elemwise_broadcast_op.cc | 2 + .../elemwise_binary_broadcast_op-inl.cuh | 18 +- tests/python/unittest/test_numpy_op.py | 506 +++++------------- 7 files changed, 979 insertions(+), 591 deletions(-) diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index fbdb8e98a15f..08bcdb7833fb 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -23,6 +23,7 @@ import numpy as _np from ...base import numeric_types from ...util import _sanity_check_params, set_module +from ...util import wrap_np_unary_func, wrap_np_binary_func from ...context import current_context from . import _internal as _npi from ..ndarray import NDArray @@ -498,8 +499,10 @@ def unique(ar, return_index=False, return_inverse=False, return_counts=False, ax @set_module('mxnet.ndarray.numpy') -def add(x1, x2, out=None): - """Add arguments element-wise. +@wrap_np_binary_func +def add(x1, x2, out=None, **kwargs): + """ + Add arguments element-wise. Parameters ---------- @@ -521,8 +524,10 @@ def add(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def subtract(x1, x2, out=None): - """Subtract arguments element-wise. +@wrap_np_binary_func +def subtract(x1, x2, out=None, **kwargs): + """ + Subtract arguments element-wise. Parameters ---------- @@ -546,8 +551,10 @@ def subtract(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def multiply(x1, x2, out=None): - """Multiply arguments element-wise. +@wrap_np_binary_func +def multiply(x1, x2, out=None, **kwargs): + """ + Multiply arguments element-wise. Parameters ---------- @@ -570,8 +577,10 @@ def multiply(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def divide(x1, x2, out=None): - """Returns a true division of the inputs, element-wise. +@wrap_np_binary_func +def divide(x1, x2, out=None, **kwargs): + """ + Returns a true division of the inputs, element-wise. Parameters ---------- @@ -596,8 +605,10 @@ def divide(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def mod(x1, x2, out=None): - """Return element-wise remainder of division. +@wrap_np_binary_func +def mod(x1, x2, out=None, **kwargs): + """ + Return element-wise remainder of division. Parameters ---------- @@ -621,8 +632,10 @@ def mod(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') +@wrap_np_binary_func def remainder(x1, x2, out=None): - """Return element-wise remainder of division. + """ + Return element-wise remainder of division. Parameters ---------- @@ -646,8 +659,10 @@ def remainder(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def power(x1, x2, out=None): - """First array elements raised to powers from second array, element-wise. +@wrap_np_binary_func +def power(x1, x2, out=None, **kwargs): + """ + First array elements raised to powers from second array, element-wise. Parameters ---------- @@ -1023,7 +1038,8 @@ def expand_dims(a, axis): @set_module('mxnet.ndarray.numpy') -def lcm(x1, x2, out=None): +@wrap_np_binary_func +def lcm(x1, x2, out=None, **kwargs): """ Returns the lowest common multiple of ``|x1|`` and ``|x2|`` @@ -1123,8 +1139,11 @@ def _unary_func_helper(x, fn_array, fn_scalar, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def sin(x, out=None, **kwargs): - r"""Trigonometric sine, element-wise. + r""" + Trigonometric sine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -1134,19 +1153,32 @@ def sin(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The sine of each element of x. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.sin(np.pi/2.) + 1.0 + >>> np.sin(np.array((0., 30., 45., 60., 90.)) * np.pi / 180.) + array([0. , 0.5 , 0.70710677, 0.86602545, 1. ]) """ return _unary_func_helper(x, _npi.sin, _np.sin, out=out, **kwargs) + @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def cos(x, out=None, **kwargs): - r"""Cosine, element-wise. + r""" + Cosine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -1156,21 +1188,36 @@ def cos(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The corresponding cosine values. This is a scalar if x is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.cos(np.array([0, np.pi/2, np.pi])) + array([ 1.000000e+00, -4.371139e-08, -1.000000e+00]) + >>> # Example of providing the optional output parameter + >>> out1 = np.array([0], dtype='f') + >>> out2 = np.cos(np.array([0.1]), out1) + >>> out2 is out1 + True """ return _unary_func_helper(x, _npi.cos, _np.cos, out=out, **kwargs) @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def sinh(x, out=None, **kwargs): - """Hyperbolic sine, element-wise. + """ + Hyperbolic sine, element-wise. Equivalent to ``1/2 * (np.exp(x) - np.exp(-x))`` or ``-1j * np.sin(1j*x)``. + Parameters ---------- x : ndarray or scalar @@ -1180,21 +1227,36 @@ def sinh(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The corresponding hyperbolic sine values. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.sinh(0) + 0.0 + >>> # Example of providing the optional output parameter + >>> out1 = np.array([0], dtype='f') + >>> out2 = np.sinh(np.array([0.1]), out1) + >>> out2 is out1 + True """ return _unary_func_helper(x, _npi.sinh, _np.sinh, out=out, **kwargs) @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def cosh(x, out=None, **kwargs): - """Hyperbolic cosine, element-wise. + """ + Hyperbolic cosine, element-wise. Equivalent to ``1/2 * (np.exp(x) + np.exp(-x))`` and ``np.cos(1j*x)``. + Parameters ---------- x : ndarray or scalar @@ -1204,22 +1266,31 @@ def cosh(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The corresponding hyperbolic cosine values. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.cosh(0) + 1.0 """ return _unary_func_helper(x, _npi.cosh, _np.cosh, out=out, **kwargs) @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def tanh(x, out=None, **kwargs): """ Compute hyperbolic tangent element-wise. Equivalent to ``np.sinh(x)/np.cosh(x)``. + Parameters ---------- x : ndarray or scalar. @@ -1229,10 +1300,12 @@ def tanh(x, out=None, **kwargs): must have a shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ------- y : ndarray or scalar The corresponding hyperbolic tangent values. + Notes ----- If `out` is provided, the function writes the result into it, @@ -1240,6 +1313,7 @@ def tanh(x, out=None, **kwargs): - input x does not support complex computation (like imaginary number) >>> np.tanh(np.pi*1j) TypeError: type not supported + Examples -------- >>> np.tanh(np.array[0, np.pi])) @@ -1252,19 +1326,16 @@ def tanh(x, out=None, **kwargs): >>> out2 = np.tanh(np.array(0.1), out1) >>> out2 is out1 True - >>> # Example of ValueError due to provision of shape mis-matched `out` - >>> np.tanh(np.zeros((3,3)),np.zeros((2,2))) - mxnet.base.MXNetError: - [07:17:36] ../src/ndarray/./../operator/tensor/../elemwise_op_common.h:135: - Check failed: assign(&dattr, vec.at(i)): Incompatible attr in node - at 0-th output: expected [3,3], got [2,2] """ return _unary_func_helper(x, _npi.tanh, _np.tanh, out=out, **kwargs) @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def log10(x, out=None, **kwargs): - """Return the base 10 logarithm of the input array, element-wise. + """ + Return the base 10 logarithm of the input array, element-wise. + Parameters ---------- x : ndarray or scalar @@ -1274,22 +1345,31 @@ def log10(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The logarithm to the base 10 of `x`, element-wise. NaNs are returned where x is negative. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.log10(np.array([1e-15, -3.])) + array([-15., nan]) """ return _unary_func_helper(x, _npi.log10, _np.log10, out=out, **kwargs) @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def sqrt(x, out=None, **kwargs): """ Return the non-negative square-root of an array, element-wise. + Parameters ---------- x : ndarray or scalar @@ -1298,22 +1378,33 @@ def sqrt(x, out=None, **kwargs): 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 ------- y : ndarray or scalar An array of the same shape as `x`, containing the positive square-root of each element in `x`. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.sqrt(np.array([1,4,9])) + array([1., 2., 3.]) + >>> np.sqrt(np.array([4, -1, _np.inf])) + array([ 2., nan, inf]) """ return _unary_func_helper(x, _npi.sqrt, _np.sqrt, out=out, **kwargs) @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def cbrt(x, out=None, **kwargs): r""" Return the cube-root of an array, element-wise. + Parameters ---------- x : ndarray @@ -1322,11 +1413,13 @@ def cbrt(x, out=None, **kwargs): 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. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. + Returns ---------- y : ndarray An array of the same shape as x, containing the cube cube-root of each element in x. If out was provided, y is a reference to it. This is a scalar if x is a scalar. + Examples ---------- >>> np.cbrt([1,8,27]) @@ -1336,9 +1429,11 @@ def cbrt(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def abs(x, out=None, **kwargs): - r"""abs(x, out=None, **kwargs) + r""" Calculate the absolute value element-wise. + Parameters ---------- x : ndarray or scalar @@ -1347,11 +1442,13 @@ def abs(x, out=None, **kwargs): 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 ------- absolute : ndarray An ndarray containing the absolute value of each element in `x`. This is a scalar if `x` is a scalar. + Examples -------- >>> x = np.array([-1.2, 1.2]) @@ -1362,10 +1459,12 @@ def abs(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def absolute(x, out=None, **kwargs): r""" Calculate the absolute value element-wise. np.abs is a shorthand for this function. + Parameters ---------- x : ndarray @@ -1374,10 +1473,12 @@ def absolute(x, out=None, **kwargs): 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. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. + Returns ---------- absolute : ndarray An ndarray containing the absolute value of each element in x. + Examples ---------- >>> x = np.array([-1.2, 1.2]) @@ -1388,11 +1489,12 @@ def absolute(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def sign(x, out=None, **kwargs): r""" - sign(x, out=None) Returns an element-wise indication of the sign of a number. The `sign` function returns ``-1 if x < 0, 0 if x==0, 1 if x > 0``. Only supports real number. + Parameters ---------- x : ndarray or a scalar @@ -1401,11 +1503,13 @@ def sign(x, out=None, **kwargs): 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 The sign of `x`. This is a scalar if `x` is a scalar. + Note ------- - Only supports real number as input elements. @@ -1413,17 +1517,18 @@ def sign(x, out=None, **kwargs): - ``out`` param: cannot perform auto broadcasting. ``out`` ndarray's shape must be the same as the expected output. - ``out`` param: cannot perform auto type cast. ``out`` ndarray's dtype must be the same as the expected output. - ``out`` param does not support scalar input case. + Examples -------- >>> a = np.array([-5., 4.5]) >>> np.sign(a) array([-1., 1.]) - Scalars as input: + >>> # Use scalars as inputs: >>> np.sign(4.0) 1.0 >>> np.sign(0) 0 - Use ``out`` parameter: + >>> # Use ``out`` parameter: >>> b = np.zeros((2, )) >>> np.sign(a, out=b) array([-1., 1.]) @@ -1434,9 +1539,11 @@ def sign(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def exp(x, out=None, **kwargs): - r"""exp(x, out=None, **kwargs) + r""" Calculate the exponential of all elements in the input array. + Parameters ---------- x : ndarray or scalar @@ -1445,11 +1552,13 @@ def exp(x, out=None, **kwargs): 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 or scalar Output array, element-wise exponential of `x`. This is a scalar if `x` is a scalar. + Examples -------- >>> np.exp(1) @@ -1462,9 +1571,11 @@ def exp(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def expm1(x, out=None, **kwargs): - r"""expm1(x, out=None, **kwargs) + r""" Calculate `exp(x) - 1` of all elements in the input array. + Parameters ---------- x : ndarray or scalar @@ -1473,11 +1584,13 @@ def expm1(x, out=None, **kwargs): 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 or scalar Output array, element-wise exponential minus one: `out = exp(x) - 1`. This is a scalar if `x` is a scalar. + Examples -------- >>> np.expm1(1) @@ -1490,10 +1603,11 @@ def expm1(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def arcsin(x, out=None, **kwargs): r""" - arcsin(x, out=None) Inverse sine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -1502,12 +1616,14 @@ def arcsin(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- angle : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. The inverse sine of each element in `x`, in radians and in the closed interval ``[-pi/2, pi/2]``. + Examples -------- >>> np.arcsin(1) # pi/2 @@ -1516,6 +1632,7 @@ def arcsin(x, out=None, **kwargs): -1.5707963267948966 >>> np.arcsin(0) 0.0 + Notes ----- `arcsin` is a multivalued function: for each `x` there are infinitely @@ -1532,6 +1649,7 @@ def arcsin(x, out=None, **kwargs): - Only support ndarray or scalar now. - `where` argument is not supported. - Complex input is not supported. + References ---------- Abramowitz, M. and Stegun, I. A., *Handbook of Mathematical Functions*, @@ -1542,10 +1660,12 @@ def arcsin(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def arccos(x, out=None, **kwargs): r""" Trigonometric inverse cosine, element-wise. The inverse of cos so that, if y = cos(x), then x = arccos(y). + Parameters ---------- x : ndarray @@ -1554,14 +1674,17 @@ def arccos(x, out=None, **kwargs): 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. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. + Returns ---------- angle : ndarray The angle of the ray intersecting the unit circle at the given x-coordinate in radians [0, pi]. This is a scalar if x is a scalar. + See also ---------- cos, arctan, arcsin + Notes ---------- arccos is a multivalued function: for each x there are infinitely many numbers z such that @@ -1570,9 +1693,9 @@ def arccos(x, out=None, **kwargs): For each value that cannot be expressed as a real number or infinity, it yields nan and sets the invalid floating point error flag. The inverse cos is also known as acos or cos^-1. + Examples ---------- - We expect the arccos of 1 to be 0, and of -1 to be pi: >>> np.arccos([1, -1]) array([ 0. , 3.14159265]) """ @@ -1580,10 +1703,12 @@ def arccos(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def arctan(x, out=None, **kwargs): - r"""arctan(x, out=None, **kwargs) + r""" Trigonometric inverse tangent, element-wise. The inverse of tan, so that if ``y = tan(x)`` then ``x = arctan(y)``. + Parameters ---------- x : ndarray or scalar @@ -1592,12 +1717,14 @@ def arctan(x, out=None, **kwargs): 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 or scalar Out has the same shape as `x`. It lies is in ``[-pi/2, pi/2]`` (``arctan(+/-inf)`` returns ``+/-pi/2``). This is a scalar if `x` is a scalar. + Notes ----- `arctan` is a multi-valued function: for each `x` there are infinitely @@ -1608,9 +1735,9 @@ def arctan(x, out=None, **kwargs): it yields ``nan`` and sets the `invalid` floating point error flag. For complex-valued input, we do not have support for them yet. The inverse tangent is also known as `atan` or tan^{-1}. + Examples -------- - We expect the arctan of 0 to be 0, and of 1 to be pi/4: >>> x = np.array([0, 1]) >>> np.arctan(x) array([0. , 0.7853982]) @@ -1621,13 +1748,14 @@ def arctan(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def log(x, out=None, **kwargs): """ - log(x, out=None) Natural logarithm, element-wise. The natural logarithm `log` is the inverse of the exponential function, so that `log(exp(x)) = x`. The natural logarithm is logarithm in base `e`. + Parameters ---------- x : ndarray @@ -1636,11 +1764,13 @@ def log(x, out=None, **kwargs): 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 The natural logarithm of `x`, element-wise. This is a scalar if `x` is a scalar. + Notes ----- Currently only supports data of real values and ``inf`` as input. Returns data of real value, ``inf``, ``-inf`` and @@ -1653,16 +1783,16 @@ def log(x, out=None, **kwargs): - ``out`` param: cannot perform auto broadcasting. ``out`` ndarray's shape must be the same as the expected output. - ``out`` param: cannot perform auto type cast. ``out`` ndarray's dtype must be the same as the expected output. - ``out`` param does not support scalar input case. + Examples -------- >>> a = np.array([1, np.exp(1), np.exp(2), 0], dtype=np.float64) >>> np.log(a) array([ 0., 1., 2., -inf], dtype=float64) - Due to internal calculation mechanism, using default float32 dtype may cause some special behavior: + >>> # Using default float32 dtype may lead to slightly different behavior: >>> a = np.array([1, np.exp(1), np.exp(2), 0], dtype=np.float32) >>> np.log(a) array([ 0., 0.99999994, 2., -inf]) - Scalar calculation: >>> np.log(1) 0.0 """ @@ -1670,10 +1800,11 @@ def log(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def degrees(x, out=None, **kwargs): """ - degrees(x, out=None) Convert angles from radians to degrees. + Parameters ---------- x : ndarray @@ -1682,12 +1813,14 @@ def degrees(x, out=None, **kwargs): 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 The corresponding degree values; if `out` was supplied this is a reference to it. This is a scalar if `x` is a scalar. + Notes ------- This function differs from the original `numpy.degrees @@ -1697,13 +1830,13 @@ def degrees(x, out=None, **kwargs): - ``out`` param: cannot perform auto broadcasting. ``out`` ndarray's shape must be the same as the expected output. - ``out`` param: cannot perform auto type cast. ``out`` ndarray's dtype must be the same as the expected output. - ``out`` param does not support scalar input case. + Examples -------- - Convert a radian array to degrees >>> rad = np.arange(12.) * np.pi / 6 >>> np.degrees(rad) array([ 0., 30., 60., 90., 120., 150., 180., 210., 240., 270., 300., 330.]) - Use specified ``out`` ndarray: + >>> # Use specified ``out`` ndarray: >>> out = np.zeros((rad.shape)) >>> np.degrees(rad, out) array([ 0., 30., 60., 90., 120., 150., 180., 210., 240., 270., 300., 330.]) @@ -1714,11 +1847,11 @@ def degrees(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') -def rad2deg(x, out=None): +@wrap_np_unary_func +def rad2deg(x, out=None, **kwargs): r""" - rad2deg(x, out=None) - Convert angles from radians to degrees. + Parameters ---------- x : ndarray or scalar @@ -1750,9 +1883,11 @@ def rad2deg(x, out=None): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def rint(x, out=None, **kwargs): """ Round elements of the array to the nearest integer. + Parameters ---------- x : ndarray or scalar @@ -1761,10 +1896,12 @@ def rint(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape and type as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- out : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Notes ----- This function differs from the original `numpy.rint @@ -1773,6 +1910,7 @@ def rint(x, out=None, **kwargs): - only ndarray or scalar is accpted as valid input, tuple of ndarray is not supported - broadcasting to `out` of different shape is currently not supported - when input is plain python numerics, the result will not be stored in the `out` param + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) @@ -1783,9 +1921,11 @@ def rint(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def log2(x, out=None, **kwargs): """ Base-2 logarithm of x. + Parameters ---------- x : ndarray or scalar @@ -1794,11 +1934,13 @@ def log2(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape and type as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray The logarithm base two of `x`, element-wise. This is a scalar if `x` is a scalar. + Notes ----- This function differs from the original `numpy.log2 @@ -1807,6 +1949,7 @@ def log2(x, out=None, **kwargs): - only ndarray or scalar is accpted as valid input, tuple of ndarray is not supported - broadcasting to `out` of different shape is currently not supported - when input is plain python numerics, the result will not be stored in the `out` param + Examples -------- >>> x = np.array([0, 1, 2, 2**4]) @@ -1817,10 +1960,12 @@ def log2(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def log1p(x, out=None, **kwargs): """ Return the natural logarithm of one plus the input array, element-wise. Calculates ``log(1 + x)``. + Parameters ---------- x : ndarray or scalar @@ -1830,11 +1975,13 @@ def log1p(x, out=None, **kwargs): must have a shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ------- y : ndarray or scalar Natural logarithm of 1 + x, element-wise. This is a scalar if x is a scalar. + Notes ----- For real-valued input, `log1p` is accurate also for `x` so small @@ -1846,6 +1993,7 @@ def log1p(x, out=None, **kwargs): For each value that cannot be expressed as a real number or infinity, it yields ``nan`` and sets the `invalid` floating point error flag. cannot support complex-valued input. + Examples -------- >>> np.log1p(1e-99) @@ -1858,9 +2006,11 @@ def log1p(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def radians(x, out=None, **kwargs): """ Convert angles from degrees to radians. + Parameters ---------- x : ndarray or scalar @@ -1869,10 +2019,12 @@ def radians(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape and type as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray The corresponding radian values. This is a scalar if x is a scalar. + Notes ----- This function differs from the original `numpy.radians @@ -1881,6 +2033,7 @@ def radians(x, out=None, **kwargs): - only ndarray or scalar is accpted as valid input, tuple of ndarray is not supported - broadcasting to `out` of different shape is currently not supported - when input is plain python numerics, the result will not be stored in the `out` param + Examples -------- >>> deg = np.arange(12.) * 30. @@ -1893,11 +2046,11 @@ def radians(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') -def deg2rad(x, out=None): +@wrap_np_unary_func +def deg2rad(x, out=None, **kwargs): r""" - deg2rad(x, out=None) - Convert angles from degrees to radians. + Parameters ---------- x : ndarray or scalar @@ -1929,11 +2082,12 @@ def deg2rad(x, out=None): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def reciprocal(x, out=None, **kwargs): r""" - reciprocal(x, out=None) Return the reciprocal of the argument, element-wise. Calculates ``1/x``. + Parameters ---------- x : ndarray or scalar @@ -1942,10 +2096,12 @@ def reciprocal(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Examples -------- >>> np.reciprocal(2.) @@ -1953,6 +2109,7 @@ def reciprocal(x, out=None, **kwargs): >>> x = np.array([1, 2., 3.33]) >>> np.reciprocal(x) array([1. , 0.5 , 0.3003003]) + Notes ----- .. note:: @@ -1971,10 +2128,11 @@ def reciprocal(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def square(x, out=None, **kwargs): r""" - square(x, out=None) Return the element-wise square of the input. + Parameters ---------- x : ndarray or scalar @@ -1983,10 +2141,12 @@ def square(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Examples -------- >>> np.square(2.) @@ -1994,6 +2154,7 @@ def square(x, out=None, **kwargs): >>> x = np.array([1, 2., -1]) >>> np.square(x) array([1., 4., 1.]) + Notes ----- The output `ndarray` has the same `ctx` as the input `ndarray`. @@ -2008,23 +2169,23 @@ def square(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') -def negative(x, out=None, where=True, **kwargs): +@wrap_np_unary_func +def negative(x, out=None, **kwargs): r""" - negative(x, out=None, where=True) Numerical negative, element-wise. + Parameters: ------------ x : ndarray or scalar Input array. out : ndarray, None, or tuple of ndarray and None, optional A location into which the result is stored. - where : ndarray, optional - Values of True indicate to calculate the ufunc at that position, - values of False indicate to leave the value in the output alone. + Returns: --------- y : ndarray or scalar Returned array or scalar: y = -x. This is a scalar if x is a scalar. + Examples: --------- >>> np.negative(1) @@ -2034,7 +2195,8 @@ def negative(x, out=None, where=True, **kwargs): @set_module('mxnet.ndarray.numpy') -def fix(x, out=None): +@wrap_np_unary_func +def fix(x, out=None, **kwargs): r""" Round an array of floats element-wise to nearest integer towards zero. The rounded values are returned as floats. @@ -2045,9 +2207,11 @@ def fix(x, out=None): An array of floats to be rounded out : ndarray, optional Output array + Returns: ------- y : ndarray of floats + Examples --------- >>> np.fix(3.14) @@ -2057,9 +2221,9 @@ def fix(x, out=None): @set_module('mxnet.ndarray.numpy') -def tan(x, out=None, where=True, **kwargs): +@wrap_np_unary_func +def tan(x, out=None, **kwargs): r""" - tan(x, out=None, where=True) Compute tangent element-wise. Equivalent to np.sin(x)/np.cos(x) element-wise. @@ -2075,11 +2239,14 @@ def tan(x, out=None, where=True, **kwargs): where : ndarray, optional Values of True indicate to calculate the ufunc at that position, values of False indicate to leave the value in the output alone. + Returns: ------- y : ndarray The corresponding tangent values. This is a scalar if x is a scalar. + Examples: + --------- >>> np.tan(0.5) 0.5463024898437905 """ @@ -2088,11 +2255,13 @@ def tan(x, out=None, where=True, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def ceil(x, out=None, **kwargs): r""" Return the ceiling of the input, element-wise. The ceil of the ndarray `x` is the smallest integer `i`, such that `i >= x`. It is often denoted as :math:`\lceil x \rceil`. + Parameters ---------- x : ndarray or scalar @@ -2102,17 +2271,19 @@ def ceil(x, out=None, **kwargs): must have a same shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ------- y : ndarray or scalar The ceiling of each element in `x`, with `float` dtype. This is a scalar if `x` is a scalar. + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) >>> np.ceil(a) array([-1., -1., -0., 1., 2., 2., 2.]) - >>> #if you use parameter out, x and out must be ndarray. if not, you will get an error! + >>> #if you use parameter out, x and out must be ndarray. >>> a = np.array(1) >>> np.ceil(np.array(3.5), a) array(4.) @@ -2123,11 +2294,13 @@ def ceil(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def floor(x, out=None, **kwargs): r""" Return the floor of the input, element-wise. The floor of the ndarray `x` is the largest integer `i`, such that `i <= x`. It is often denoted as :math:`\lfloor x \rfloor`. + Parameters ---------- x : ndarray or scalar @@ -2137,17 +2310,19 @@ def floor(x, out=None, **kwargs): must have a same shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ------- y : ndarray or scalar The floor of each element in `x`, with `float` dtype. This is a scalar if `x` is a scalar. + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) >>> np.floor(a) array([-2., -2., -1., 0., 1., 1., 2.]) - >>> #if you use parameter out, x and out must be ndarray. if not, you will get an error! + >>> #if you use parameter out, x and out must be ndarray. >>> a = np.array(1) >>> np.floor(np.array(3.5), a) array(3.) @@ -2158,9 +2333,9 @@ def floor(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def trunc(x, out=None, **kwargs): r""" - trunc(x, out=None) Return the truncated value of the input, element-wise. The truncated value of the scalar `x` is the nearest integer `i` which is closer to zero than `x` is. In short, the fractional part of the @@ -2178,6 +2353,7 @@ def trunc(x, out=None, **kwargs): y : ndarray or scalar The truncated value of each element in `x`. This is a scalar if `x` is a scalar. + Notes ----- This function differs from the original numpy.trunc in the following aspects: @@ -2196,10 +2372,11 @@ def trunc(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def logical_not(x, out=None, **kwargs): r""" - logical_not(x, out=None) Compute the truth value of NOT x element-wise. + Parameters ---------- x : ndarray or scalar @@ -2213,6 +2390,7 @@ def logical_not(x, out=None, **kwargs): Boolean result with the same shape as `x` of the NOT operation on elements of `x`. This is a scalar if `x` is a scalar. + Notes ----- This function differs from the original numpy.logical_not in the following aspects: @@ -2220,6 +2398,7 @@ def logical_not(x, out=None, **kwargs): - Cannot cast type automatically. Dtype of `out` must be same as the expected one. - Cannot broadcast automatically. Shape of `out` must be same as the expected one. - If `x` is plain python numeric, the result won't be stored in out. + Examples -------- >>> x= np.array([True, False, 0, 1]) @@ -2234,10 +2413,11 @@ def logical_not(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def arcsinh(x, out=None, **kwargs): r""" - arcsinh(x, out=None) Inverse hyperbolic sine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2250,6 +2430,7 @@ def arcsinh(x, out=None, **kwargs): arcsinh : ndarray Array of the same shape as `x`. This is a scalar if `x` is a scalar. + Notes ----- `arcsinh` is a multivalued function: for each `x` there are infinitely @@ -2265,6 +2446,7 @@ def arcsinh(x, out=None, **kwargs): - Cannot cast type automatically. DType of `out` must be same as the expected one. - Cannot broadcast automatically. Shape of `out` must be same as the expected one. - If `x` is plain python numeric, the result won't be stored in out. + Examples -------- >>> a = np.array([3.2, 5.0]) @@ -2277,10 +2459,11 @@ def arcsinh(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def arccosh(x, out=None, **kwargs): r""" - arccosh(x, out=None) Inverse hyperbolic cosine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2293,6 +2476,7 @@ def arccosh(x, out=None, **kwargs): arccosh : ndarray Array of the same shape as `x`. This is a scalar if `x` is a scalar. + Notes ----- `arccosh` is a multivalued function: for each `x` there are infinitely @@ -2308,6 +2492,7 @@ def arccosh(x, out=None, **kwargs): - Cannot cast type automatically. Dtype of `out` must be same as the expected one. - Cannot broadcast automatically. Shape of `out` must be same as the expected one. - If `x` is plain python numeric, the result won't be stored in out. + Examples -------- >>> a = np.array([3.2, 5.0]) @@ -2320,10 +2505,11 @@ def arccosh(x, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') +@wrap_np_unary_func def arctanh(x, out=None, **kwargs): r""" - arctanh(x, out=None) Inverse hyperbolic tangent, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2336,6 +2522,7 @@ def arctanh(x, out=None, **kwargs): arctanh : ndarray Array of the same shape as `x`. This is a scalar if `x` is a scalar. + Notes ----- `arctanh` is a multivalued function: for each `x` there are infinitely @@ -2351,6 +2538,7 @@ def arctanh(x, out=None, **kwargs): - Cannot cast type automatically. Dtype of `out` must be same as the expected one. - Cannot broadcast automatically. Shape of `out` must be same as the expected one. - If `x` is plain python numeric, the result won't be stored in out. + Examples -------- >>> a = np.array([0.0, -0.5]) @@ -2432,7 +2620,9 @@ def tile(A, reps): # pylint: disable=redefined-outer-name @set_module('mxnet.ndarray.numpy') def split(ary, indices_or_sections, axis=0): - """Split an array into multiple sub-arrays. + """ + Split an array into multiple sub-arrays. + Parameters ---------- ary : ndarray @@ -2451,10 +2641,12 @@ def split(ary, indices_or_sections, axis=0): an empty sub-array is returned correspondingly. axis : int, optional The axis along which to split, default is 0. + Returns ------- sub-arrays : list of ndarrays A list of sub-arrays. + Raises ------ ValueError @@ -2674,7 +2866,9 @@ def vsplit(ary, indices_or_sections): @set_module('mxnet.ndarray.numpy') def concatenate(seq, axis=0, out=None): - """Join a sequence of arrays along an existing axis. + """ + Join a sequence of arrays along an existing axis. + Parameters ---------- a1, a2, ... : sequence of ndarray @@ -2687,6 +2881,7 @@ def concatenate(seq, axis=0, out=None): If provided, the destination to place the result. The shape must be correct, matching that of what concatenate would have returned if no out argument were specified. + Returns ------- res : ndarray @@ -2700,6 +2895,7 @@ def stack(arrays, axis=0, out=None): """Join a sequence of arrays along a new axis. The axis parameter specifies the index of the new axis in the dimensions of the result. For example, if `axis=0` it will be the first dimension and if `axis=-1` it will be the last dimension. + Parameters ---------- arrays : sequence of ndarray @@ -2709,6 +2905,7 @@ def stack(arrays, axis=0, out=None): out : ndarray, optional If provided, the destination to place the result. The shape must be correct, matching that of what stack would have returned if no out argument were specified. + Returns ------- stacked : ndarray @@ -2816,8 +3013,10 @@ def dstack(arrays): @set_module('mxnet.ndarray.numpy') -def maximum(x1, x2, out=None): - """Returns element-wise maximum of the input arrays with broadcasting. +@wrap_np_binary_func +def maximum(x1, x2, out=None, **kwargs): + """ + Returns element-wise maximum of the input arrays with broadcasting. Parameters ---------- @@ -2833,8 +3032,10 @@ def maximum(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def minimum(x1, x2, out=None): - """Returns element-wise minimum of the input arrays with broadcasting. +@wrap_np_binary_func +def minimum(x1, x2, out=None, **kwargs): + """ + Returns element-wise minimum of the input arrays with broadcasting. Parameters ---------- @@ -3261,9 +3462,9 @@ def indices(dimensions, dtype=_np.int32, ctx=None): @set_module('mxnet.ndarray.numpy') -def copysign(x1, x2, out=None): - r"""copysign(x1, x2, out=None) - +@wrap_np_binary_func +def copysign(x1, x2, out=None, **kwargs): + r""" Change the sign of x1 to that of x2, element-wise. If `x2` is a scalar, its sign will be copied to all elements of `x1`. @@ -3730,10 +3931,9 @@ def around(x, decimals=0, out=None, **kwargs): @set_module('mxnet.ndarray.numpy') -def arctan2(x1, x2, out=None): +@wrap_np_binary_func +def arctan2(x1, x2, out=None, **kwargs): r""" - arctan2(x1, x2, out=None) - Element-wise arc tangent of ``x1/x2`` choosing the quadrant correctly. The quadrant (i.e., branch) is chosen so that ``arctan2(x1, x2)`` is @@ -3816,7 +4016,8 @@ def arctan2(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def hypot(x1, x2, out=None): +@wrap_np_binary_func +def hypot(x1, x2, out=None, **kwargs): r""" Given the "legs" of a right triangle, return its hypotenuse. @@ -3863,7 +4064,8 @@ def hypot(x1, x2, out=None): @set_module('mxnet.ndarray.numpy') -def ldexp(x1, x2, out=None): +@wrap_np_binary_func +def ldexp(x1, x2, out=None, **kwargs): """ Returns x1 * 2**x2, element-wise. The mantissas `x1` and twos exponents `x2` are used to construct diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index 70977c0b7f2d..aec4f9ef4617 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -40,7 +40,7 @@ from ..base import check_call, _LIB, NDArrayHandle, c_array from ..base import mx_real_t, c_array_buf, mx_uint, numeric_types, integer_types from ..context import Context -from ..util import _sanity_check_params, set_module +from ..util import _sanity_check_params, set_module, wrap_np_unary_func, wrap_np_binary_func from ..context import current_context from ..ndarray import numpy as _mx_nd_np from ..ndarray.numpy import _internal as _npi @@ -2114,8 +2114,10 @@ def unique(ar, return_index=False, return_inverse=False, return_counts=False, ax @set_module('mxnet.numpy') -def add(x1, x2, out=None): - """Add arguments element-wise. +@wrap_np_binary_func +def add(x1, x2, out=None, **kwargs): + """ + Add arguments element-wise. Parameters ---------- @@ -2137,8 +2139,10 @@ def add(x1, x2, out=None): @set_module('mxnet.numpy') -def subtract(x1, x2, out=None): - """Subtract arguments element-wise. +@wrap_np_binary_func +def subtract(x1, x2, out=None, **kwargs): + """ + Subtract arguments element-wise. Parameters ---------- @@ -2161,8 +2165,10 @@ def subtract(x1, x2, out=None): @set_module('mxnet.numpy') -def multiply(x1, x2, out=None): - """Multiply arguments element-wise. +@wrap_np_binary_func +def multiply(x1, x2, out=None, **kwargs): + """ + Multiply arguments element-wise. Parameters ---------- @@ -2184,8 +2190,10 @@ def multiply(x1, x2, out=None): @set_module('mxnet.numpy') -def divide(x1, x2, out=None): - """Returns a true division of the inputs, element-wise. +@wrap_np_binary_func +def divide(x1, x2, out=None, **kwargs): + """ + Returns a true division of the inputs, element-wise. Parameters ---------- @@ -2209,8 +2217,10 @@ def divide(x1, x2, out=None): @set_module('mxnet.numpy') -def mod(x1, x2, out=None): - """Return element-wise remainder of division. +@wrap_np_binary_func +def mod(x1, x2, out=None, **kwargs): + """ + Return element-wise remainder of division. Parameters ---------- @@ -2234,8 +2244,10 @@ def mod(x1, x2, out=None): @set_module('mxnet.numpy') -def remainder(x1, x2, out=None): - """Return element-wise remainder of division. +@wrap_np_binary_func +def remainder(x1, x2, out=None, **kwargs): + """ + Return element-wise remainder of division. Parameters ---------- @@ -2259,8 +2271,10 @@ def remainder(x1, x2, out=None): @set_module('mxnet.numpy') -def power(x1, x2, out=None): - """First array elements raised to powers from second array, element-wise. +@wrap_np_binary_func +def power(x1, x2, out=None, **kwargs): + """ + First array elements raised to powers from second array, element-wise. Parameters ---------- @@ -2285,7 +2299,8 @@ def power(x1, x2, out=None): @set_module('mxnet.numpy') -def lcm(x1, x2, out=None): +@wrap_np_binary_func +def lcm(x1, x2, out=None, **kwargs): """ Returns the lowest common multiple of ``|x1|`` and ``|x2|`` @@ -2322,8 +2337,11 @@ def lcm(x1, x2, out=None): @set_module('mxnet.numpy') +@wrap_np_unary_func def sin(x, out=None, **kwargs): - r"""Trigonometric sine, element-wise. + r""" + Trigonometric sine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2333,20 +2351,32 @@ def sin(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The sine of each element of x. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.sin(np.pi/2.) + 1.0 + >>> np.sin(np.array((0., 30., 45., 60., 90.)) * np.pi / 180.) + array([0. , 0.5 , 0.70710677, 0.86602545, 1. ]) """ return _mx_nd_np.sin(x, out=out, **kwargs) @set_module('mxnet.numpy') +@wrap_np_unary_func def cos(x, out=None, **kwargs): - r"""Cosine, element-wise. + r""" + Cosine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2356,20 +2386,36 @@ def cos(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The corresponding cosine values. This is a scalar if x is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.cos(np.array([0, np.pi/2, np.pi])) + array([ 1.000000e+00, -4.371139e-08, -1.000000e+00]) + >>> # Example of providing the optional output parameter + >>> out1 = np.array([0], dtype='f') + >>> out2 = np.cos(np.array([0.1]), out1) + >>> out2 is out1 + True """ return _mx_nd_np.cos(x, out=out, **kwargs) +@set_module('mxnet.numpy') +@wrap_np_unary_func def sinh(x, out=None, **kwargs): - """Hyperbolic sine, element-wise. + """ + Hyperbolic sine, element-wise. Equivalent to ``1/2 * (np.exp(x) - np.exp(-x))`` or ``-1j * np.sin(1j*x)``. + Parameters ---------- x : ndarray or scalar @@ -2379,21 +2425,36 @@ def sinh(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The corresponding hyperbolic sine values. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.sinh(0) + 0.0 + >>> # Example of providing the optional output parameter + >>> out1 = np.array([0], dtype='f') + >>> out2 = np.sinh(np.array([0.1]), out1) + >>> out2 is out1 + True """ return _mx_nd_np.sinh(x, out=out, **kwargs) @set_module('mxnet.numpy') +@wrap_np_unary_func def cosh(x, out=None, **kwargs): - """Hyperbolic cosine, element-wise. + """ + Hyperbolic cosine, element-wise. Equivalent to ``1/2 * (np.exp(x) + np.exp(-x))`` and ``np.cos(1j*x)``. + Parameters ---------- x : ndarray or scalar @@ -2403,22 +2464,31 @@ def cosh(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The corresponding hyperbolic cosine values. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.cosh(0) + 1.0 """ return _mx_nd_np.cosh(x, out=out, **kwargs) @set_module('mxnet.numpy') +@wrap_np_unary_func def tanh(x, out=None, **kwargs): """ Compute hyperbolic tangent element-wise. Equivalent to ``np.sinh(x)/np.cosh(x)``. + Parameters ---------- x : ndarray or scalar. @@ -2428,10 +2498,12 @@ def tanh(x, out=None, **kwargs): must have a shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ---------- y : ndarray or scalar The corresponding hyperbolic tangent values. + Notes ----- If `out` is provided, the function writes the result into it, @@ -2439,6 +2511,7 @@ def tanh(x, out=None, **kwargs): - input x does not support complex computation (like imaginary number) >>> np.tanh(np.pi*1j) TypeError: type not supported + Examples -------- >>> np.tanh(np.array[0, np.pi])) @@ -2451,19 +2524,16 @@ def tanh(x, out=None, **kwargs): >>> out2 = np.tanh(np.array(0.1), out1) >>> out2 is out1 True - >>> # Example of ValueError due to provision of shape mis-matched `out` - >>> np.tanh(np.zeros((3,3)),np.zeros((2,2))) - mxnet.base.MXNetError: - [07:17:36] ../src/ndarray/./../operator/tensor/../elemwise_op_common.h:135: - Check failed: assign(&dattr, vec.at(i)): Incompatible attr in node - at 0-th output: expected [3,3], got [2,2] """ return _mx_nd_np.tanh(x, out=out, **kwargs) @set_module('mxnet.numpy') +@wrap_np_unary_func def log10(x, out=None, **kwargs): - """Return the base 10 logarithm of the input array, element-wise. + """ + Return the base 10 logarithm of the input array, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2473,22 +2543,31 @@ def log10(x, out=None, **kwargs): must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. The dtype of the output is the same as that of the input if the input is an ndarray. + Returns ------- y : ndarray or scalar The logarithm to the base 10 of `x`, element-wise. NaNs are returned where x is negative. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.log10(np.array([1e-15, -3.])) + array([-15., nan]) """ return _mx_nd_np.log10(x, out=out, **kwargs) @set_module('mxnet.numpy') +@wrap_np_unary_func def sqrt(x, out=None, **kwargs): """ Return the non-negative square-root of an array, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2497,22 +2576,33 @@ def sqrt(x, out=None, **kwargs): 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 ------- y : ndarray or scalar An array of the same shape as `x`, containing the positive square-root of each element in `x`. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. + + Examples + -------- + >>> np.sqrt(np.array([1,4,9])) + array([1., 2., 3.]) + >>> np.sqrt(np.array([4, -1, _np.inf])) + array([ 2., nan, inf]) """ return _mx_nd_np.sqrt(x, out=out, **kwargs) @set_module('mxnet.numpy') +@wrap_np_unary_func def cbrt(x, out=None, **kwargs): """ Return the cube-root of an array, element-wise. + Parameters ---------- x : ndarray @@ -2521,11 +2611,13 @@ def cbrt(x, out=None, **kwargs): 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. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. + Returns ---------- y : ndarray An array of the same shape as x, containing the cube cube-root of each element in x. If out was provided, y is a reference to it. This is a scalar if x is a scalar. + Examples ---------- >>> np.cbrt([1,8,27]) @@ -2535,9 +2627,11 @@ def cbrt(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def abs(x, out=None, **kwargs): - r"""abs(x, out=None, **kwargs) + r""" Calculate the absolute value element-wise. + Parameters ---------- x : ndarray or scalar @@ -2546,11 +2640,13 @@ def abs(x, out=None, **kwargs): 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 ------- absolute : ndarray An ndarray containing the absolute value of each element in `x`. This is a scalar if `x` is a scalar. + Examples -------- >>> x = np.array([-1.2, 1.2]) @@ -2561,10 +2657,12 @@ def abs(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def absolute(x, out=None, **kwargs): """ Calculate the absolute value element-wise. np.abs is a shorthand for this function. + Parameters ---------- x : ndarray @@ -2573,10 +2671,12 @@ def absolute(x, out=None, **kwargs): 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. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. + Returns ---------- absolute : ndarray An ndarray containing the absolute value of each element in x. + Examples ---------- >>> x = np.array([-1.2, 1.2]) @@ -2587,9 +2687,11 @@ def absolute(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def exp(x, out=None, **kwargs): - r"""exp(x, out=None, **kwargs) + r""" Calculate the exponential of all elements in the input array. + Parameters ---------- x : ndarray or scalar @@ -2598,11 +2700,13 @@ def exp(x, out=None, **kwargs): 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 or scalar Output array, element-wise exponential of `x`. This is a scalar if `x` is a scalar. + Examples -------- >>> np.exp(1) @@ -2615,9 +2719,11 @@ def exp(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def expm1(x, out=None, **kwargs): - r"""expm1(x, out=None, **kwargs) + r""" Calculate `exp(x) - 1` for all elements in the array. + Parameters ---------- x : ndarray or scalar @@ -2626,11 +2732,13 @@ def expm1(x, out=None, **kwargs): 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 or scalar Output array, element-wise exponential minus one: `out = exp(x) - 1`. This is a scalar if `x` is a scalar. + Examples -------- >>> np.expm1(1) @@ -2643,10 +2751,11 @@ def expm1(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def arcsin(x, out=None, **kwargs): r""" - arcsin(x, out=None) Inverse sine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -2655,12 +2764,14 @@ def arcsin(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- angle : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. The inverse sine of each element in `x`, in radians and in the closed interval ``[-pi/2, pi/2]``. + Examples -------- >>> np.arcsin(1) # pi/2 @@ -2669,6 +2780,7 @@ def arcsin(x, out=None, **kwargs): -1.5707963267948966 >>> np.arcsin(0) 0.0 + Notes ----- `arcsin` is a multivalued function: for each `x` there are infinitely @@ -2685,6 +2797,7 @@ def arcsin(x, out=None, **kwargs): - Only support ndarray or scalar now. - `where` argument is not supported. - Complex input is not supported. + References ---------- Abramowitz, M. and Stegun, I. A., *Handbook of Mathematical Functions*, @@ -2695,10 +2808,12 @@ def arcsin(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def arccos(x, out=None, **kwargs): """ Trigonometric inverse cosine, element-wise. The inverse of cos so that, if y = cos(x), then x = arccos(y). + Parameters ---------- x : ndarray @@ -2707,14 +2822,13 @@ def arccos(x, out=None, **kwargs): 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. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. + Returns ---------- angle : ndarray The angle of the ray intersecting the unit circle at the given x-coordinate in radians [0, pi]. This is a scalar if x is a scalar. - See also - ---------- - cos, arctan, arcsin + Notes ---------- arccos is a multivalued function: for each x there are infinitely many numbers z such that @@ -2723,9 +2837,9 @@ def arccos(x, out=None, **kwargs): For each value that cannot be expressed as a real number or infinity, it yields nan and sets the invalid floating point error flag. The inverse cos is also known as acos or cos^-1. + Examples ---------- - We expect the arccos of 1 to be 0, and of -1 to be pi: >>> np.arccos([1, -1]) array([ 0. , 3.14159265]) """ @@ -2733,10 +2847,12 @@ def arccos(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def arctan(x, out=None, **kwargs): - r"""arctan(x, out=None, **kwargs) + r""" Trigonometric inverse tangent, element-wise. The inverse of tan, so that if ``y = tan(x)`` then ``x = arctan(y)``. + Parameters ---------- x : ndarray or scalar @@ -2745,12 +2861,14 @@ def arctan(x, out=None, **kwargs): 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 or scalar Out has the same shape as `x`. It lies is in ``[-pi/2, pi/2]`` (``arctan(+/-inf)`` returns ``+/-pi/2``). This is a scalar if `x` is a scalar. + Notes ----- `arctan` is a multi-valued function: for each `x` there are infinitely @@ -2761,9 +2879,9 @@ def arctan(x, out=None, **kwargs): it yields ``nan`` and sets the `invalid` floating point error flag. For complex-valued input, we do not have support for them yet. The inverse tangent is also known as `atan` or tan^{-1}. + Examples -------- - We expect the arctan of 0 to be 0, and of 1 to be pi/4: >>> x = np.array([0, 1]) >>> np.arctan(x) array([0. , 0.7853982]) @@ -2774,11 +2892,12 @@ def arctan(x, out=None, **kwargs): @set_module('mxnet.numpy') -def sign(x, out=None): +@wrap_np_unary_func +def sign(x, out=None, **kwargs): """ - sign(x, out=None) Returns an element-wise indication of the sign of a number. The `sign` function returns ``-1 if x < 0, 0 if x==0, 1 if x > 0``. Only supports real number. + Parameters ---------- x : ndarray or a scalar @@ -2787,11 +2906,13 @@ def sign(x, out=None): 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 The sign of `x`. This is a scalar if `x` is a scalar. + Note ------- - Only supports real number as input elements. @@ -2799,6 +2920,7 @@ def sign(x, out=None): - ``out`` param: cannot perform auto broadcasting. ``out`` ndarray's shape must be the same as the expected output. - ``out`` param: cannot perform auto type cast. ``out`` ndarray's dtype must be the same as the expected output. - ``out`` param does not support scalar input case. + Examples -------- >>> a = np.array([-5., 4.5]) @@ -2820,13 +2942,14 @@ def sign(x, out=None): @set_module('mxnet.numpy') +@wrap_np_unary_func def log(x, out=None, **kwargs): """ - log(x, out=None) Natural logarithm, element-wise. The natural logarithm `log` is the inverse of the exponential function, so that `log(exp(x)) = x`. The natural logarithm is logarithm in base `e`. + Parameters ---------- x : ndarray @@ -2835,11 +2958,13 @@ def log(x, out=None, **kwargs): 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 The natural logarithm of `x`, element-wise. This is a scalar if `x` is a scalar. + Notes ----- Currently only supports data of real values and ``inf`` as input. Returns data of real value, ``inf``, ``-inf`` and @@ -2852,16 +2977,16 @@ def log(x, out=None, **kwargs): - ``out`` param: cannot perform auto broadcasting. ``out`` ndarray's shape must be the same as the expected output. - ``out`` param: cannot perform auto type cast. ``out`` ndarray's dtype must be the same as the expected output. - ``out`` param does not support scalar input case. + Examples -------- >>> a = np.array([1, np.exp(1), np.exp(2), 0], dtype=np.float64) >>> np.log(a) array([ 0., 1., 2., -inf], dtype=float64) - Due to internal calculation mechanism, using default float32 dtype may cause some special behavior: + >>> # Using the default float32 dtype leads to slightly different behavior >>> a = np.array([1, np.exp(1), np.exp(2), 0]) >>> np.log(a) array([ 0., 0.99999994, 2., -inf]) - Scalar calculation: >>> np.log(1) 0.0 """ @@ -2869,9 +2994,11 @@ def log(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def rint(x, out=None, **kwargs): """ Round elements of the array to the nearest integer. + Parameters ---------- x : ndarray or scalar @@ -2880,10 +3007,12 @@ def rint(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape and type as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- out : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Notes ----- This function differs from the original `numpy.rint @@ -2892,6 +3021,7 @@ def rint(x, out=None, **kwargs): - only ndarray or scalar is accpted as valid input, tuple of ndarray is not supported - broadcasting to `out` of different shape is currently not supported - when input is plain python numerics, the result will not be stored in the `out` param + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) @@ -2902,9 +3032,11 @@ def rint(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def log2(x, out=None, **kwargs): """ Base-2 logarithm of x. + Parameters ---------- x : ndarray or scalar @@ -2913,11 +3045,13 @@ def log2(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape and type as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray The logarithm base two of `x`, element-wise. This is a scalar if `x` is a scalar. + Notes ----- This function differs from the original `numpy.log2 @@ -2926,6 +3060,7 @@ def log2(x, out=None, **kwargs): - only ndarray or scalar is accpted as valid input, tuple of ndarray is not supported - broadcasting to `out` of different shape is currently not supported - when input is plain python numerics, the result will not be stored in the `out` param + Examples -------- >>> x = np.array([0, 1, 2, 2**4]) @@ -2936,10 +3071,12 @@ def log2(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def log1p(x, out=None, **kwargs): """ Return the natural logarithm of one plus the input array, element-wise. Calculates ``log(1 + x)``. + Parameters ---------- x : ndarray or scalar @@ -2949,11 +3086,13 @@ def log1p(x, out=None, **kwargs): must have a shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ------- y : ndarray or scalar Natural logarithm of 1 + x, element-wise. This is a scalar if x is a scalar. + Notes ----- For real-valued input, `log1p` is accurate also for `x` so small @@ -2965,6 +3104,7 @@ def log1p(x, out=None, **kwargs): For each value that cannot be expressed as a real number or infinity, it yields ``nan`` and sets the `invalid` floating point error flag. cannot support complex-valued input. + Examples -------- >>> np.log1p(1e-99) @@ -2977,10 +3117,11 @@ def log1p(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def degrees(x, out=None, **kwargs): """ - degrees(x, out=None) Convert angles from radians to degrees. + Parameters ---------- x : ndarray @@ -2989,12 +3130,14 @@ def degrees(x, out=None, **kwargs): 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 The corresponding degree values; if `out` was supplied this is a reference to it. This is a scalar if `x` is a scalar. + Notes ------- This function differs from the original `numpy.degrees @@ -3004,13 +3147,13 @@ def degrees(x, out=None, **kwargs): - ``out`` param: cannot perform auto broadcasting. ``out`` ndarray's shape must be the same as the expected output. - ``out`` param: cannot perform auto type cast. ``out`` ndarray's dtype must be the same as the expected output. - ``out`` param does not support scalar input case. + Examples -------- - Convert a radian array to degrees >>> rad = np.arange(12.) * np.pi / 6 >>> np.degrees(rad) array([ 0., 30., 60., 90., 120., 150., 180., 210., 240., 270., 300., 330.]) - Use specified ``out`` ndarray: + >>> # Use specified ``out`` ndarray: >>> out = np.zeros((rad.shape)) >>> np.degrees(rad, out) array([ 0., 30., 60., 90., 120., 150., 180., 210., 240., 270., 300., 330.]) @@ -3021,10 +3164,9 @@ def degrees(x, out=None, **kwargs): @set_module('mxnet.numpy') -def rad2deg(x, out=None): +@wrap_np_unary_func +def rad2deg(x, out=None, **kwargs): r""" - rad2deg(x, out=None) - Convert angles from radians to degrees. Parameters ---------- @@ -3057,9 +3199,11 @@ def rad2deg(x, out=None): @set_module('mxnet.numpy') +@wrap_np_unary_func def radians(x, out=None, **kwargs): """ Convert angles from degrees to radians. + Parameters ---------- x : ndarray or scalar @@ -3068,10 +3212,12 @@ def radians(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape and type as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray The corresponding radian values. This is a scalar if x is a scalar. + Notes ----- This function differs from the original `numpy.radians @@ -3080,6 +3226,7 @@ def radians(x, out=None, **kwargs): - only ndarray or scalar is accpted as valid input, tuple of ndarray is not supported - broadcasting to `out` of different shape is currently not supported - when input is plain python numerics, the result will not be stored in the `out` param + Examples -------- >>> deg = np.arange(12.) * 30. @@ -3092,11 +3239,11 @@ def radians(x, out=None, **kwargs): @set_module('mxnet.numpy') -def deg2rad(x, out=None): +@wrap_np_unary_func +def deg2rad(x, out=None, **kwargs): r""" - deg2rad(x, out=None) - Convert angles from degrees to radians. + Parameters ---------- x : ndarray or scalar @@ -3128,11 +3275,12 @@ def deg2rad(x, out=None): @set_module('mxnet.numpy') +@wrap_np_unary_func def reciprocal(x, out=None, **kwargs): r""" - reciprocal(x, out=None) Return the reciprocal of the argument, element-wise. Calculates ``1/x``. + Parameters ---------- x : ndarray or scalar @@ -3141,10 +3289,12 @@ def reciprocal(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Examples -------- >>> np.reciprocal(2.) @@ -3152,6 +3302,7 @@ def reciprocal(x, out=None, **kwargs): >>> x = np.array([1, 2., 3.33]) >>> np.reciprocal(x) array([1. , 0.5 , 0.3003003]) + Notes ----- .. note:: @@ -3170,10 +3321,11 @@ def reciprocal(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def square(x, out=None, **kwargs): r""" - square(x, out=None) Return the element-wise square of the input. + Parameters ---------- x : ndarray or scalar @@ -3182,10 +3334,12 @@ def square(x, out=None, **kwargs): A location into which the result is stored. If provided, it must have the same shape as the input. If not provided or None, a freshly-allocated array is returned. + Returns ------- y : ndarray or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Examples -------- >>> np.square(2.) @@ -3193,6 +3347,7 @@ def square(x, out=None, **kwargs): >>> x = np.array([1, 2., -1]) >>> np.square(x) array([1., 4., 1.]) + Notes ----- The output `ndarray` has the same `ctx` as the input `ndarray`. @@ -3207,10 +3362,11 @@ def square(x, out=None, **kwargs): @set_module('mxnet.numpy') -def negative(x, out=None, where=True, **kwargs): +@wrap_np_unary_func +def negative(x, out=None, **kwargs): r""" - negative(x, out=None, where=True) Numerical negative, element-wise. + Parameters: ------------ x : ndarray or scalar @@ -3221,13 +3377,12 @@ def negative(x, out=None, where=True, **kwargs): If not provided or None, a freshly-allocated array is returned. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. - where : ndarray, optional - Values of True indicate to calculate the ufunc at that position, - values of False indicate to leave the value in the output alone. + Returns: ------- y : ndarray or scalar Returned array or scalar: y = -x. This is a scalar if x is a scalar. + Examples: -------- >>> np.negative(1) @@ -3237,7 +3392,8 @@ def negative(x, out=None, where=True, **kwargs): @set_module('mxnet.numpy') -def fix(x, out=None): +@wrap_np_unary_func +def fix(x, out=None, **kwargs): """ Round an array of floats element-wise to nearest integer towards zero. The rounded values are returned as floats. @@ -3248,10 +3404,12 @@ def fix(x, out=None): An array of floats to be rounded out : ndarray, optional Output array + Returns: ------- y : ndarray or scalar Returned array or scalar: y = -x. This is a scalar if x is a scalar.ndarray of floats + Examples: --------- >>> np.fix(3.14) @@ -3261,9 +3419,9 @@ def fix(x, out=None): @set_module('mxnet.numpy') -def tan(x, out=None, where=True, **kwargs): +@wrap_np_unary_func +def tan(x, out=None, **kwargs): r""" - tan(x, out=None, where=True) Compute tangent element-wise. Equivalent to np.sin(x)/np.cos(x) element-wise. @@ -3276,13 +3434,12 @@ def tan(x, out=None, where=True, **kwargs): it must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. - where : ndarray, optional - Values of True indicate to calculate the ufunc at that position, - values of False indicate to leave the value in the output alone. + Returns: ------- y : ndarray The corresponding tangent values. This is a scalar if x is a scalar. + Examples: --------- >>> np.tan(0.5) @@ -3293,11 +3450,13 @@ def tan(x, out=None, where=True, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def ceil(x, out=None, **kwargs): r""" Return the ceiling of the input, element-wise. The ceil of the ndarray `x` is the smallest integer `i`, such that `i >= x`. It is often denoted as :math:`\lceil x \rceil`. + Parameters ---------- x : ndarray or scalar @@ -3307,17 +3466,19 @@ def ceil(x, out=None, **kwargs): must have a shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ------- y : ndarray or scalar The ceiling of each element in `x`, with `float` dtype. This is a scalar if `x` is a scalar. + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) >>> np.ceil(a) array([-1., -1., -0., 1., 2., 2., 2.]) - >>> #if you use parameter out, x and out must be ndarray. if not, you will get an error! + >>> # if you use parameter out, x and out must be ndarray. >>> a = np.array(1) >>> np.ceil(np.array(3.5), a) array(4.) @@ -3328,11 +3489,13 @@ def ceil(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def floor(x, out=None, **kwargs): r""" Return the floor of the input, element-wise. The ceil of the ndarray `x` is the largest integer `i`, such that `i <= x`. It is often denoted as :math:`\lfloor x \rfloor`. + Parameters ---------- x : ndarray or scalar @@ -3342,17 +3505,19 @@ def floor(x, out=None, **kwargs): must have a shape that the inputs fill into. If not provided or None, a freshly-allocated array is returned. The dtype of the output and input must be the same. + Returns ------- y : ndarray or scalar The floor of each element in `x`, with `float` dtype. This is a scalar if `x` is a scalar. + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) >>> np.floor(a) array([-2., -2., -1., 0., 1., 1., 2.]) - >>> #if you use parameter out, x and out must be ndarray. if not, you will get an error! + >>> # if you use parameter out, x and out must be ndarray. >>> a = np.array(1) >>> np.floor(np.array(3.5), a) array(3.) @@ -3363,9 +3528,9 @@ def floor(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def trunc(x, out=None, **kwargs): r""" - trunc(x, out=None) Return the truncated value of the input, element-wise. The truncated value of the scalar `x` is the nearest integer `i` which is closer to zero than `x` is. In short, the fractional part of the @@ -3401,10 +3566,11 @@ def trunc(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def logical_not(x, out=None, **kwargs): r""" - logical_not(x, out=None) Compute the truth value of NOT x element-wise. + Parameters ---------- x : ndarray or scalar @@ -3418,6 +3584,7 @@ def logical_not(x, out=None, **kwargs): Boolean result with the same shape as `x` of the NOT operation on elements of `x`. This is a scalar if `x` is a scalar. + Notes ----- This function differs from the original numpy.logical_not in the following aspects: @@ -3440,10 +3607,11 @@ def logical_not(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def arcsinh(x, out=None, **kwargs): r""" - arcsinh(x, out=None) Inverse hyperbolic cosine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -3486,10 +3654,11 @@ def arcsinh(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def arccosh(x, out=None, **kwargs): r""" - arccosh(x, out=None) Inverse hyperbolic cosine, element-wise. + Parameters ---------- x : ndarray or scalar @@ -3532,10 +3701,11 @@ def arccosh(x, out=None, **kwargs): @set_module('mxnet.numpy') +@wrap_np_unary_func def arctanh(x, out=None, **kwargs): r""" - arctanh(x, out=None) Inverse hyperbolic tangent, element-wise. + Parameters ---------- x : ndarray or scalar @@ -3590,6 +3760,7 @@ def tensordot(a, b, axes=2): integer_like scalar, ``N``; if it is such, then the last ``N`` dimensions of `a` and the first ``N`` dimensions of `b` are summed over. + Parameters ---------- a, b : ndarray, len(shape) >= 1 @@ -3601,9 +3772,11 @@ def tensordot(a, b, axes=2): * (2,) ndarray Or, a list of axes to be summed over, first sequence applying to `a`, second to `b`. Both elements ndarray must be of the same length. + See Also -------- dot, einsum + Notes ----- Three common use cases are: @@ -3617,6 +3790,7 @@ def tensordot(a, b, axes=2): (first) axes of `a` (`b`) - the argument `axes` should consist of two sequences of the same length, with the first axis to sum over given first in both sequences, the second axis second, and so forth. + Examples -------- >>> a = np.arange(60.).reshape(3,4,5) @@ -4263,8 +4437,10 @@ def dstack(arrays): @set_module('mxnet.numpy') -def maximum(x1, x2, out=None): - """Returns element-wise maximum of the input arrays with broadcasting. +@wrap_np_binary_func +def maximum(x1, x2, out=None, **kwargs): + """ + Returns element-wise maximum of the input arrays with broadcasting. Parameters ---------- @@ -4280,8 +4456,10 @@ def maximum(x1, x2, out=None): @set_module('mxnet.numpy') -def minimum(x1, x2, out=None): - """Returns element-wise minimum of the input arrays with broadcasting. +@wrap_np_binary_func +def minimum(x1, x2, out=None, **kwargs): + """ + Returns element-wise minimum of the input arrays with broadcasting. Parameters ---------- @@ -4696,9 +4874,9 @@ def indices(dimensions, dtype=_np.int32, ctx=None): @set_module('mxnet.numpy') -def copysign(x1, x2, out=None): - r"""copysign(x1, x2, out=None) - +@wrap_np_binary_func +def copysign(x1, x2, out=None, **kwargs): + r""" Change the sign of x1 to that of x2, element-wise. If `x2` is a scalar, its sign will be copied to all elements of `x1`. @@ -5141,10 +5319,9 @@ def around(x, decimals=0, out=None, **kwargs): @set_module('mxnet.numpy') -def arctan2(x1, x2, out=None): +@wrap_np_binary_func +def arctan2(x1, x2, out=None, **kwargs): r""" - arctan2(x1, x2, out=None) - Element-wise arc tangent of ``x1/x2`` choosing the quadrant correctly. The quadrant (i.e., branch) is chosen so that ``arctan2(x1, x2)`` is @@ -5226,10 +5403,9 @@ def arctan2(x1, x2, out=None): @set_module('mxnet.numpy') -def hypot(x1, x2, out=None): +@wrap_np_binary_func +def hypot(x1, x2, out=None, **kwargs): r""" - hypot(x1, x2, out=None) - Given the "legs" of a right triangle, return its hypotenuse. Equivalent to ``sqrt(x1**2 + x2**2)``, element-wise. If `x1` or @@ -5275,7 +5451,8 @@ def hypot(x1, x2, out=None): @set_module('mxnet.numpy') -def ldexp(x1, x2, out=None): +@wrap_np_binary_func +def ldexp(x1, x2, out=None, **kwargs): """ Returns x1 * 2**x2, element-wise. The mantissas `x1` and twos exponents `x2` are used to construct diff --git a/python/mxnet/symbol/numpy/_symbol.py b/python/mxnet/symbol/numpy/_symbol.py index 66b2aa2ac3fb..5e535ef3ef84 100644 --- a/python/mxnet/symbol/numpy/_symbol.py +++ b/python/mxnet/symbol/numpy/_symbol.py @@ -24,6 +24,7 @@ from . import _op as _mx_np_op from ...base import _LIB, SymbolHandle, numeric_types, mx_uint from ...util import check_call, set_module, _sanity_check_params +from ...util import wrap_np_unary_func, wrap_np_binary_func from ...context import current_context from ..symbol import Symbol from .._internal import _set_np_symbol_class @@ -1138,44 +1139,52 @@ def _ufunc_helper(lhs, rhs, fn_array, fn_scalar, lfn_scalar, rfn_scalar=None, ou @set_module('mxnet.symbol.numpy') -def add(x1, x2, out=None): +@wrap_np_binary_func +def add(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.add, _np.add, _npi.add_scalar, None, out) @set_module('mxnet.symbol.numpy') -def subtract(x1, x2, out=None): +@wrap_np_binary_func +def subtract(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.subtract, _np.subtract, _npi.subtract_scalar, _npi.rsubtract_scalar, out) @set_module('mxnet.symbol.numpy') -def multiply(x1, x2, out=None): +@wrap_np_binary_func +def multiply(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.multiply, _np.multiply, _npi.multiply_scalar, None, out) @set_module('mxnet.symbol.numpy') -def divide(x1, x2, out=None): +@wrap_np_binary_func +def divide(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.true_divide, _np.divide, _npi.true_divide_scalar, _npi.rtrue_divide_scalar, out) @set_module('mxnet.symbol.numpy') -def mod(x1, x2, out=None): +@wrap_np_binary_func +def mod(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.mod, _np.mod, _npi.mod_scalar, _npi.rmod_scalar, out) @set_module('mxnet.symbol.numpy') -def remainder(x1, x2, out=None): +@wrap_np_binary_func +def remainder(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.mod, _np.mod, _npi.mod_scalar, _npi.rmod_scalar, out) @set_module('mxnet.symbol.numpy') -def power(x1, x2, out=None): +@wrap_np_binary_func +def power(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.power, _np.power, _npi.power_scalar, _npi.rpower_scalar, out) @set_module('mxnet.symbol.numpy') -def lcm(x1, x2, out=None): +@wrap_np_binary_func +def lcm(x1, x2, out=None, **kwargs): """ Returns the lowest common multiple of ``|x1|`` and ``|x2|`` @@ -1570,19 +1579,24 @@ def _unary_func_helper(x, fn_array, fn_scalar, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def sin(x, out=None, **kwargs): - r"""Trigonometric sine, element-wise. + r""" + Trigonometric sine, element-wise. + Parameters ---------- x : _Symbol or scalar Angle, in radians (:math:`2 \pi` rad equals 360 degrees). out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol The sine of each element of x. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. @@ -1591,18 +1605,23 @@ def sin(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def cos(x, out=None, **kwargs): - r"""Cosine, element-wise. + r""" + Cosine, element-wise. + Parameters ---------- x : _Symbol or scalar Angle, in radians (:math:`2 \pi` rad equals 360 degrees). out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol The corresponding cosine values. This is a scalar if x is a scalar. + Notes ---- This function only supports input type of float. @@ -1611,19 +1630,24 @@ def cos(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def sinh(x, out=None, **kwargs): - """Hyperbolic sine, element-wise. + """ + Hyperbolic sine, element-wise. Equivalent to ``1/2 * (np.exp(x) - np.exp(-x))`` or ``-1j * np.sin(1j*x)``. + Parameters ---------- x : _Symbol or scalar Input array or scalar. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol or scalar The corresponding hyperbolic sine values. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. @@ -1632,19 +1656,24 @@ def sinh(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def cosh(x, out=None, **kwargs): - """Hyperbolic cosine, element-wise. + """ + Hyperbolic cosine, element-wise. Equivalent to ``1/2 * (np.exp(x) + np.exp(-x))`` and ``np.cos(1j*x)``. + Parameters ---------- x : _Symbol or scalar Input array or scalar. out : ndarray or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol or scalar The corresponding hyperbolic cosine values. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. @@ -1653,20 +1682,24 @@ def cosh(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def tanh(x, out=None, **kwargs): """ Compute hyperbolic tangent element-wise. Equivalent to ``np.sinh(x)/np.cosh(x)``. + Parameters ---------- x : _Symbol Input array. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol The corresponding hyperbolic tangent values. + Notes ----- If `out` is provided, the function writes the result into it, @@ -1674,6 +1707,7 @@ def tanh(x, out=None, **kwargs): - input x does not support complex computation (like imaginary number) >>> np.tanh(np.pi*1j) TypeError: type not supported + Examples -------- >>> np.tanh(np.array[0, np.pi])) @@ -1697,19 +1731,24 @@ def tanh(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def log10(x, out=None, **kwargs): - """Return the base 10 logarithm of the input array, element-wise. + """ + Return the base 10 logarithm of the input array, element-wise. + Parameters ---------- x : _Symbol or scalar Input array or scalar. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol or scalar The logarithm to the base 10 of `x`, element-wise. NaNs are returned where x is negative. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. @@ -1718,20 +1757,24 @@ def log10(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def sqrt(x, out=None, **kwargs): """ Return the non-negative square-root of an array, element-wise. + Parameters ---------- x : _Symbol or scalar The values whose square-roots are required. out : _Symbol, or None, optional Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol or scalar An array of the same shape as `x`, containing the positive square-root of each element in `x`. This is a scalar if `x` is a scalar. + Notes ---- This function only supports input type of float. @@ -1740,15 +1783,18 @@ def sqrt(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def cbrt(x, out=None, **kwargs): r""" Return the cube-root of an array, element-wise. + Parameters ---------- x : _Symbol The values whose cube-roots are required. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ---------- y : _Symbol @@ -1759,15 +1805,18 @@ def cbrt(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def abs(x, out=None, **kwargs): - r"""abs(x, out=None, **kwargs) + r""" Calculate the absolute value element-wise. + Parameters ---------- x : _Symbol or scalar Input array. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- absolute : _Symbol @@ -1778,6 +1827,7 @@ def abs(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def absolute(x, out=None, **kwargs): r""" Calculate the absolute value element-wise. @@ -1789,6 +1839,7 @@ def absolute(x, out=None, **kwargs): Input array. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ---------- absolute : _Symbol @@ -1798,22 +1849,25 @@ def absolute(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def sign(x, out=None, **kwargs): r""" - sign(x, out=None) Returns an element-wise indication of the sign of a number. The `sign` function returns ``-1 if x < 0, 0 if x==0, 1 if x > 0``. Only supports real number. + Parameters ---------- x : _Symbol or a scalar Input values. out : _Symbol or None, optional Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol The sign of `x`. This is a scalar if `x` is a scalar. + Note ------- - Only supports real number as input elements. @@ -1826,15 +1880,18 @@ def sign(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def exp(x, out=None, **kwargs): - r"""exp(x, out=None, **kwargs) + r""" Calculate the exponential of all elements in the input array. + Parameters ---------- x : _Symbol or scalar Input values. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- out : _Symbol @@ -1845,15 +1902,18 @@ def exp(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def expm1(x, out=None, **kwargs): - r"""expm1(x, out=None, **kwargs) + r""" Calculate `exp(x) - 1` for all elements in the array. + Parameters ---------- x : _Symbol or scalar Input values. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- out : _Symbol @@ -1864,20 +1924,23 @@ def expm1(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def arcsin(x, out=None, **kwargs): r""" - arcsin(x, out=None) Inverse sine, element-wise. + Parameters ---------- x : _Symbol or scalar The values whose reciprocals are required. out : _Symbol, or None, optional Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- angle : _Symbol or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Notes ----- `arcsin` is a multivalued function: for each `x` there are infinitely @@ -1894,6 +1957,7 @@ def arcsin(x, out=None, **kwargs): - Only support _Symbol or scalar now. - `where` argument is not supported. - Complex input is not supported. + References ---------- Abramowitz, M. and Stegun, I. A., *Handbook of Mathematical Functions*, @@ -1904,24 +1968,29 @@ def arcsin(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def arccos(x, out=None, **kwargs): r""" Trigonometric inverse cosine, element-wise. The inverse of cos so that, if y = cos(x), then x = arccos(y). + Parameters ---------- x : _Symbol x-coordinate on the unit circle. For real arguments, the domain is [-1, 1]. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ---------- angle : _Symbol The angle of the ray intersecting the unit circle at the given x-coordinate in radians [0, pi]. This is a scalar if x is a scalar. + See also ---------- cos, arctan, arcsin + Notes ---------- arccos is a multivalued function: for each x there are infinitely many numbers z such that @@ -1935,22 +2004,26 @@ def arccos(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def arctan(x, out=None, **kwargs): - r"""arctan(x, out=None, **kwargs) + r""" Trigonometric inverse tangent, element-wise. The inverse of tan, so that if ``y = tan(x)`` then ``x = arctan(y)``. + Parameters ---------- x : _Symbol or scalar Input values. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- out : _Symbol Out has the same shape as `x`. It lies is in ``[-pi/2, pi/2]`` (``arctan(+/-inf)`` returns ``+/-pi/2``). This is a scalar if `x` is a scalar. + Notes ----- `arctan` is a multi-valued function: for each `x` there are infinitely @@ -1966,24 +2039,27 @@ def arctan(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def log(x, out=None, **kwargs): """ - log(x, out=None) Natural logarithm, element-wise. The natural logarithm `log` is the inverse of the exponential function, so that `log(exp(x)) = x`. The natural logarithm is logarithm in base `e`. + Parameters ---------- x : _Symbol Input value. Elements must be of real value. out : _Symbol or None, optional Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol The natural logarithm of `x`, element-wise. This is a scalar if `x` is a scalar. + Notes ----- Currently only supports data of real values and ``inf`` as input. Returns data of real value, ``inf``, ``-inf`` and @@ -2001,22 +2077,25 @@ def log(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def degrees(x, out=None, **kwargs): """ - degrees(x, out=None) Convert angles from radians to degrees. + Parameters ---------- x : _Symbol Input value. Elements must be of real value. out : _Symbol or None, optional Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol of floats The corresponding degree values; if `out` was supplied this is a reference to it. This is a scalar if `x` is a scalar. + Notes ------- This function differs from the original `numpy.degrees @@ -2031,11 +2110,11 @@ def degrees(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') -def rad2deg(x, out=None): +@wrap_np_unary_func +def rad2deg(x, out=None, **kwargs): r""" - rad2deg(x, out=None) - Convert angles from radians to degrees. + Parameters ---------- x : _Symbol or scalar @@ -2060,19 +2139,24 @@ def rad2deg(x, out=None): return _unary_func_helper(x, _npi.rad2deg, _np.rad2deg, out=out) +@set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def rint(x, out=None, **kwargs): """ Round elements of the array to the nearest integer. + Parameters ---------- x : _Symbol or scalar Input array. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- out : _Symbol or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Notes ----- This function differs from the original `numpy.rint @@ -2086,6 +2170,7 @@ def rint(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def log2(x, out=None, **kwargs): """ Base-2 logarithm of x. @@ -2115,6 +2200,7 @@ def log2(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def log1p(x, out=None, **kwargs): """ Return the natural logarithm of one plus the input array, element-wise. @@ -2153,6 +2239,7 @@ def log1p(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def radians(x, out=None, **kwargs): """ Convert angles from degrees to radians. @@ -2186,11 +2273,13 @@ def radians(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') -def deg2rad(x, out=None): +@wrap_np_unary_func +def deg2rad(x, out=None, **kwargs): r""" deg2rad(x, out=None) Convert angles from degrees to radians. + Parameters ---------- x : _Symbol or scalar @@ -2216,21 +2305,24 @@ def deg2rad(x, out=None): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def reciprocal(x, out=None, **kwargs): r""" - reciprocal(x, out=None) Return the reciprocal of the argument, element-wise. Calculates ``1/x``. + Parameters ---------- x : _Symbol or scalar The values whose reciprocals are required. out : _Symbol, or None, optional Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Notes ----- .. note:: @@ -2249,20 +2341,23 @@ def reciprocal(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def square(x, out=None, **kwargs): r""" - square(x, out=None) Return the element-wise square of the input. + Parameters ---------- x : _Symbol or scalar The values whose reciprocals are required. out : _Symbol, or None, optional Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- y : _Symbol or scalar Output array is same shape and type as x. This is a scalar if x is a scalar. + Notes ----- The output `symbol` has the same `ctx` as the input `symbol`. @@ -2276,10 +2371,11 @@ def square(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') -def negative(x, out=None, where=True, **kwargs): +@wrap_np_unary_func +def negative(x, out=None, **kwargs): r""" - negative(x, out=None, where=True) Numerical negative, element-wise. + Parameters: ------------ x : _Symbol or scalar @@ -2290,13 +2386,12 @@ def negative(x, out=None, where=True, **kwargs): If not provided or None, a freshly-allocated array is returned. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. - where : _Symbol or scalar, optional - Values of True indicate to calculate the ufunc at that position, - values of False indicate to leave the value in the output alone. + Returns: ------- y : _Symbol or scalar Returned array or scalar: y = -x. This is a scalar if x is a scalar. + Examples: --------- >>> np.negative(1) @@ -2306,7 +2401,8 @@ def negative(x, out=None, where=True, **kwargs): @set_module('mxnet.symbol.numpy') -def fix(x, out=None): +@wrap_np_unary_func +def fix(x, out=None, **kwargs): """ Round to nearest integer towards zero. @@ -2318,9 +2414,11 @@ def fix(x, out=None): An array of floats to be rounded out : _Symbol or scalar, optional Output array + Returns: --------- y : _Symbol or scalar + Examples: ---------- >>> np.fix(3.14) @@ -2330,9 +2428,9 @@ def fix(x, out=None): @set_module('mxnet.symbol.numpy') -def tan(x, out=None, where=True, **kwargs): +@wrap_np_unary_func +def tan(x, out=None, **kwargs): r""" - tan(x, out=None, where=True) Compute tangent element-wise. Equivalent to np.sin(x)/np.cos(x) element-wise. @@ -2345,9 +2443,7 @@ def tan(x, out=None, where=True, **kwargs): it must have a shape that the inputs broadcast to. If not provided or None, a freshly-allocated array is returned. A tuple (possible only as a keyword argument) must have length equal to the number of outputs. - where : array_like, optional - Values of True indicate to calculate the ufunc at that position, - values of False indicate to leave the value in the output alone. + Returns: ------- y : _Symbol or scalar @@ -2358,23 +2454,26 @@ def tan(x, out=None, where=True, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def ceil(x, out=None, **kwargs): r""" Return the ceiling of the input, element-wise. The ceil of the ndarray `x` is the smallest integer `i`, such that `i >= x`. It is often denoted as :math:`\lceil x \rceil`. + Parameters ---------- x : _Symbol or scalar Input array. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- - y : - _Symbol or scalar + y : _Symbol or scalar The ceiling of each element in `x`, with `float` dtype. This is a scalar if `x` is a scalar. + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) @@ -2391,29 +2490,32 @@ def ceil(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def floor(x, out=None, **kwargs): r""" Return the floor of the input, element-wise. The floor of the ndarray `x` is the largest integer `i`, such that `i <= x`. It is often denoted as :math:`\lfloor x \rfloor`. + Parameters ---------- x : _Symbol or scalar Input array. out : _Symbol or None Dummy parameter to keep the consistency with the ndarray counterpart. + Returns ------- - y : - _Symbol or scalar + y : _Symbol or scalar The floor of each element in `x`, with `float` dtype. This is a scalar if `x` is a scalar. + Examples -------- >>> a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0]) >>> np.floor(a) array([-2., -2., -1., 0., 1., 1., 2.]) - >>> #if you use parameter out, x and out must be ndarray. if not, you will get an error! + >>> # if you use parameter out, x and out must be ndarray. if not, you will get an error! >>> a = np.array(1) >>> np.floor(np.array(3.5), a) array(3.) @@ -2424,9 +2526,9 @@ def floor(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def trunc(x, out=None, **kwargs): r""" - trunc(x, out=None) Return the truncated value of the input, element-wise. The truncated value of the scalar `x` is the nearest integer `i` which is closer to zero than `x` is. In short, the fractional part of the @@ -2444,6 +2546,7 @@ def trunc(x, out=None, **kwargs): y : _Symbol or scalar The truncated value of each element in `x`. This is a scalar if `x` is a scalar. + Notes ----- This function differs from the original numpy.trunc in the following aspects: @@ -2456,10 +2559,11 @@ def trunc(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def logical_not(x, out=None, **kwargs): r""" - logical_not(x, out=None) Compute the truth value of NOT x element-wise. + Parameters ---------- x : _Symbol or scalar @@ -2473,6 +2577,7 @@ def logical_not(x, out=None, **kwargs): Boolean result with the same shape as `x` of the NOT operation on elements of `x`. This is a scalar if `x` is a scalar. + Notes ----- This function differs from the original numpy.logical_not in the following aspects: @@ -2485,10 +2590,11 @@ def logical_not(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def arcsinh(x, out=None, **kwargs): r""" - arcsinh(x, out=None) Inverse hyperbolic sine, element-wise. + Parameters ---------- x : _Symbol or scalar @@ -2501,6 +2607,7 @@ def arcsinh(x, out=None, **kwargs): arcsinh : _Symbol Array of the same shape as `x`. This is a scalar if `x` is a scalar. + Notes ----- `arcsinh` is a multivalued function: for each `x` there are infinitely @@ -2521,10 +2628,11 @@ def arcsinh(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def arccosh(x, out=None, **kwargs): r""" - arccosh(x, out=None) Inverse hyperbolic cosine, element-wise. + Parameters ---------- x : _Symbol or scalar @@ -2537,6 +2645,7 @@ def arccosh(x, out=None, **kwargs): arccosh : _Symbol Array of the same shape as `x`. This is a scalar if `x` is a scalar. + Notes ----- `arccosh` is a multivalued function: for each `x` there are infinitely @@ -2557,10 +2666,11 @@ def arccosh(x, out=None, **kwargs): @set_module('mxnet.symbol.numpy') +@wrap_np_unary_func def arctanh(x, out=None, **kwargs): r""" - arctanh(x, out=None) Inverse hyperbolic tangent, element-wise. + Parameters ---------- x : _Symbol or scalar @@ -2573,6 +2683,7 @@ def arctanh(x, out=None, **kwargs): arctanh : _Symbol Array of the same shape as `x`. This is a scalar if `x` is a scalar. + Notes ----- `arctanh` is a multivalued function: for each `x` there are infinitely @@ -2681,6 +2792,7 @@ def arange(start, stop=None, step=1, dtype=None, ctx=None): @set_module('mxnet.symbol.numpy') def split(ary, indices_or_sections, axis=0): """Split an array into multiple sub-arrays. + Parameters ---------- ary : ndarray @@ -2699,10 +2811,12 @@ def split(ary, indices_or_sections, axis=0): an empty sub-array is returned correspondingly. axis : int, optional The axis along which to split, default is 0. + Returns ------- sub-arrays : list of ndarrays A list of sub-arrays. + Raises ------ ValueError @@ -2991,12 +3105,14 @@ def dstack(arrays): @set_module('mxnet.symbol.numpy') -def maximum(x1, x2, out=None): +@wrap_np_binary_func +def maximum(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.maximum, _np.maximum, _npi.maximum_scalar, None, out) @set_module('mxnet.symbol.numpy') -def minimum(x1, x2, out=None): +@wrap_np_binary_func +def minimum(x1, x2, out=None, **kwargs): return _ufunc_helper(x1, x2, _npi.minimum, _np.minimum, _npi.minimum_scalar, None, out) @@ -3340,9 +3456,9 @@ def indices(dimensions, dtype=_np.int32, ctx=None): @set_module('mxnet.symbol.numpy') -def copysign(x1, x2, out=None): - r"""copysign(x1, x2, out=None) - +@wrap_np_binary_func +def copysign(x1, x2, out=None, **kwargs): + r""" Change the sign of x1 to that of x2, element-wise. If `x2` is a scalar, its sign will be copied to all elements of `x1`. @@ -3734,10 +3850,9 @@ def around(x, decimals=0, out=None, **kwargs): @set_module('mxnet.symbol.numpy') -def arctan2(x1, x2, out=None): +@wrap_np_binary_func +def arctan2(x1, x2, out=None, **kwargs): r""" - arctan2(x1, x2, out=None) - Element-wise arc tangent of ``x1/x2`` choosing the quadrant correctly. The quadrant (i.e., branch) is chosen so that ``arctan2(x1, x2)`` is @@ -3802,7 +3917,8 @@ def arctan2(x1, x2, out=None): @set_module('mxnet.symbol.numpy') -def hypot(x1, x2, out=None): +@wrap_np_binary_func +def hypot(x1, x2, out=None, **kwargs): r""" Given the "legs" of a right triangle, return its hypotenuse. @@ -3895,7 +4011,8 @@ def unique(ar, return_index=False, return_inverse=False, return_counts=False, ax @set_module('mxnet.symbol.numpy') -def ldexp(x1, x2, out=None): +@wrap_np_binary_func +def ldexp(x1, x2, out=None, **kwargs): """ Returns x1 * 2**x2, element-wise. The mantissas `x1` and twos exponents `x2` are used to construct diff --git a/python/mxnet/util.py b/python/mxnet/util.py index 1050fb2a481d..c2dfd85dcfac 100644 --- a/python/mxnet/util.py +++ b/python/mxnet/util.py @@ -27,6 +27,15 @@ from .base import _LIB, check_call +_np_ufunc_default_kwargs = { + 'where': True, + 'casting': 'same_kind', + 'order': 'K', + 'dtype': None, + 'subok': True, +} + + def makedirs(d): """Create directories recursively if they don't exist. os.makedirs(exist_ok=True) is not available in Python2""" @@ -558,6 +567,103 @@ def hybrid_forward(self, F, x, w): return use_np_shape(use_np_array(func)) +def np_ufunc_legal_option(key, value): + """ + Checking if ufunc arguments are legal inputs + + Parameters + ---------- + key : string + the key of the ufunc argument. + value : string + the value of the ufunc argument. + + Returns + ------- + legal : boolean + Whether or not the argument is a legal one. True when the key is one of the ufunc + arguments and value is an allowed value. False when the key is not one of the ufunc + arugments or the value is not an allowed value even when the key is a legal one. + """ + if key == 'where': + return True + elif key == 'casting': + return (value in set(['no', 'equiv', 'safe', 'same_kind', 'unsafe'])) + elif key == 'order': + if isinstance(value, str): + return True + elif key == 'dtype': + import numpy as _np + return (value in set([_np.int8, _np.uint8, _np.int32, _np.int64, + _np.float16, _np.float32, _np.float64, + 'int8', 'uint8', 'int32', 'int64', + 'float16', 'float32', 'float64'])) + elif key == 'subok': + return isinstance(value, bool) + return False + + +def wrap_np_unary_func(func): + """A convenience decorator for wrapping numpy-compatible unary ufuncs to provide uniform + error handling. + + Parameters + ---------- + func : a numpy-compatible unary function to be wrapped for better error handling. + + Returns + ------- + Function + A function wrapped with proper error handling. + """ + @wraps_safely(func) + def _wrap_np_unary_func(x, out=None, **kwargs): + if len(kwargs) != 0: + for key, value in kwargs.items(): + # if argument is not in the set of ufunc arguments + if key not in _np_ufunc_default_kwargs: + raise TypeError("{} is an invalid keyword to function \'{}\'".format(key, func.__name__)) + # if argument is one of the ufunc arguments, but not with the default value + if value != _np_ufunc_default_kwargs[key]: + # if the provided value of the argument is a legal option, raise NotImplementedError + if np_ufunc_legal_option(key, value): + raise NotImplementedError("{}={} is not implemented yet".format(key, str(value))) + # otherwise raise TypeError with not understood error message + raise TypeError("{} {} not understood".format(key, value)) + return func(x, out=out) + return _wrap_np_unary_func + + +def wrap_np_binary_func(func): + """A convenience decorator for wrapping numpy-compatible binary ufuncs to provide uniform + error handling. + + Parameters + ---------- + func : a numpy-compatible binary function to be wrapped for better error handling. + + Returns + ------- + Function + A function wrapped with proper error handling. + """ + @wraps_safely(func) + def _wrap_np_binary_func(x1, x2, out=None, **kwargs): + if len(kwargs) != 0: + for key, value in kwargs.items(): + # if argument is not in the set of ufunc arguments + if key not in _np_ufunc_default_kwargs: + raise TypeError("{} is an invalid keyword to function \'{}\'".format(key, func.__name__)) + # if argument is one of the ufunc arguments, but not with the default value + if value != _np_ufunc_default_kwargs[key]: + # if the provided value of the argument is a legal option, raise NotImplementedError + if np_ufunc_legal_option(key, value): + raise NotImplementedError("{}={} is not implemented yet".format(key, str(value))) + # otherwise raise TypeError with not understood error message + raise TypeError("{} {} not understood".format(key, value)) + return func(x1, x2, out=out) + return _wrap_np_binary_func + def _set_np_array(active): """Turns on/off NumPy array semantics for the current thread in which `mxnet.numpy.ndarray` is expected to be created, instead of the legacy `mx.nd.NDArray`. diff --git a/src/operator/numpy/np_elemwise_broadcast_op.cc b/src/operator/numpy/np_elemwise_broadcast_op.cc index 7e07e47b4a8f..c206ad453ba6 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op.cc +++ b/src/operator/numpy/np_elemwise_broadcast_op.cc @@ -109,6 +109,7 @@ NNVM_REGISTER_OP(_npi_lcm) [](const NodeAttrs& attrs){ return std::vector >{{0, 0}, {1, 0}}; }) +.set_attr("FGradient", MakeZeroGradNodes) .set_attr("FCompute", BinaryBroadcastCompute) .add_argument("lhs", "NDArray-or-Symbol", "First input to the function") .add_argument("rhs", "NDArray-or-Symbol", "Second input to the function"); @@ -125,6 +126,7 @@ NNVM_REGISTER_OP(_npi_lcm_scalar) [](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::Compute); diff --git a/src/operator/tensor/elemwise_binary_broadcast_op-inl.cuh b/src/operator/tensor/elemwise_binary_broadcast_op-inl.cuh index 8469f59e2e9c..d65e12aef86e 100644 --- a/src/operator/tensor/elemwise_binary_broadcast_op-inl.cuh +++ b/src/operator/tensor/elemwise_binary_broadcast_op-inl.cuh @@ -57,8 +57,22 @@ BinaryBroadcastBackwardUseNone(const nnvm::NodeAttrs& attrs, Tensor workspace = ctx.requested[0].get_space_typed( Shape1(workspace_size * sizeof(index_t)), s); - Reduce(s, lhs, req[0], workspace, out); - Reduce(s, rhs, req[1], workspace, out); + if (out.shape_.Size() != 0) { + Reduce(s, lhs, req[0], workspace, out); + Reduce(s, rhs, req[1], workspace, out); + } else { + using namespace mxnet_op; + if (lhs.shape_.Size() != 0) { + MSHADOW_TYPE_SWITCH(lhs.type_flag_, LType, { + Kernel::Launch(s, lhs.shape_.Size(), lhs.dptr()); + }); + } + if (rhs.shape_.Size() != 0) { + MSHADOW_TYPE_SWITCH(rhs.type_flag_, RType, { + Kernel::Launch(s, rhs.shape_.Size(), rhs.dptr()); + }); + } + } }); }); } diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 28edc2e05306..2956f99f4eb7 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -219,67 +219,6 @@ def test_np_dot(): assert False -@with_seed() -@use_np -def test_np_ldexp(): - class TestLdexp(HybridBlock): - def __init__(self): - super(TestLdexp, self).__init__() - - def hybrid_forward(self, F, x1, x2): - return F.np.ldexp(x1, x2) - - def _np_ldexp(x1, x2): - return x1 * _np.power(2.0, x2) - - def dldx(x1, x2): - grad_a = _np.power(2.0, x2) - grad_b = _np_ldexp(x1, x2) * _np.log(2.0) - if len(x1) == 1: - grad_a = _np.sum(grad_a) - if len(x2) == 1: - grad_b = _np.sum(grad_b) - return [grad_a, grad_b] - - shapes = [ - ((3, 1), (3, 1)), - ((3, 1, 2), (3, 1, 2)), - ((1, ),(1, )), - ((1, ), (2, )), - ((3, ), (1, )), - ((3, 0), (3, 0)), # zero-size shape - ((0, 1), (0, 1)), # zero-size shape - ((2, 0, 2), (2, 0, 2)), # zero-size shape - ] - - for hybridize in [True, False]: - for shape1, shape2 in shapes: - for dtype in [_np.float16, _np.float32, _np.float64]: - test_ldexp = TestLdexp() - if hybridize: - test_ldexp.hybridize() - x1 = rand_ndarray(shape=shape1, dtype=dtype).as_np_ndarray() - x1.attach_grad() - x2 = rand_ndarray(shape=shape2, dtype=dtype).as_np_ndarray() - x2.attach_grad() - - np_out = _np_ldexp(x1.asnumpy(), x2.asnumpy()) - with mx.autograd.record(): - mx_out = test_ldexp(x1, x2) - assert mx_out.shape == np_out.shape - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=1e-1, atol=1e-1) - - mx_out.backward() - np_backward = dldx(x1.asnumpy(), x2.asnumpy()) - assert_almost_equal(x1.grad.asnumpy(), np_backward[0], atol=1e-1, rtol=1e-1) - assert_almost_equal(x2.grad.asnumpy(), np_backward[1], atol=1e-1, rtol=1e-1) - - # Test imperative once again - mx_out = np.ldexp(x1, x2) - np_out = _np_ldexp(x1.asnumpy(), x2.asnumpy()) - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=1e-1, atol=1e-1) - - @with_seed() @use_np def test_np_vdot(): @@ -1405,6 +1344,21 @@ def hybrid_forward(self, F, a, *args, **kwargs): y.backward() assert_almost_equal(mx_test_data.grad.asnumpy(), ref_grad(np_test_data), rtol=1e-1, atol=1e-2, equal_nan=True) + np_out = getattr(_np, func)(np_test_data) + mx_out = getattr(mx.np, func)(mx_test_data) + assert mx_out.shape == np_out.shape + assert_almost_equal(mx_out.asnumpy(), np_out, rtol=1e-3, atol=1e-5) + + + assertRaises(NotImplementedError, getattr(np, func), mx_test_data, where=False) + assertRaises(NotImplementedError, getattr(np, func), mx_test_data, subok=False) + assertRaises(NotImplementedError, getattr(np, func), mx_test_data, dtype=_np.int8) + assertRaises(TypeError, getattr(np, func), mx_test_data, dtype="abcdefg") + assertRaises(NotImplementedError, getattr(np, func), mx_test_data, casting='safe') + assertRaises(TypeError, getattr(np, func), mx_test_data, casting='mxnet') + assertRaises(NotImplementedError, getattr(np, func), mx_test_data, order='C') + assertRaises(NotImplementedError, getattr(np, func), mx_test_data, order='mxnet') + funcs = { 'absolute' : (lambda x: -1. * (x < 0) + (x > 0), -1.0, 1.0), 'cbrt' : (lambda x: 1. / (3. * _np.cbrt(x) ** 2), -1.0, 1.0), @@ -1451,6 +1405,129 @@ def hybrid_forward(self, F, a, *args, **kwargs): check_unary_func(func, ref_grad, shape, low, high) +@with_seed() +@use_np +def test_np_binary_funcs(): + def check_binary_func(func, lshape, rshape, low, high, lgrads, rgrads=None, alltypes=None): + class TestBinary(HybridBlock): + def __init__(self, func): + super(TestBinary, self).__init__() + self._func = func + + def hybrid_forward(self, F, a, b, *args, **kwargs): + return getattr(F.np, self._func)(a, b) + + np_func = getattr(_np, func) + mx_func = TestBinary(func) + alltypes = alltypes if alltypes else [[_np.float16, _np.float32, _np.float64]] + for dtypes, lgrad, rgrad in zip(alltypes, lgrads, rgrads if rgrads else lgrads): + for dtype in dtypes: + ldtype = rdtype = dtype + if isinstance(dtype, tuple): + assert len(dtype) == 2 + ldtype, rdtype = dtype + np_test_x1 = _np.random.uniform(low, high, lshape).astype(ldtype) + np_test_x2 = _np.random.uniform(low, high, rshape).astype(rdtype) + mx_test_x1 = mx.numpy.array(np_test_x1, dtype=ldtype) + mx_test_x2 = mx.numpy.array(np_test_x2, dtype=rdtype) + for hybridize in [True, False]: + if hybridize: + mx_func.hybridize() + if lgrad: + mx_test_x1.attach_grad() + mx_test_x2.attach_grad() + np_out = np_func(np_test_x1, np_test_x2) + with mx.autograd.record(): + y = mx_func(mx_test_x1, mx_test_x2) + assert y.shape == np_out.shape + assert_almost_equal(y.asnumpy(), np_out.astype(y.dtype), rtol=1e-3, atol=1e-5, + use_broadcast=False, equal_nan=True) + + if lgrad: + y.backward() + assert_almost_equal(mx_test_x1.grad.asnumpy(), + collapse_sum_like(lgrad(y.asnumpy(), np_test_x1, np_test_x2), mx_test_x1.shape), + rtol=1e-1, atol=1e-2, equal_nan=True, use_broadcast=False) + if rgrads is None: + assert_almost_equal(mx_test_x2.grad.asnumpy(), + collapse_sum_like(rgrad(y.asnumpy(), np_test_x2, np_test_x1), mx_test_x2.shape), + rtol=1e-1, atol=1e-2, equal_nan=True, use_broadcast=False) + else: + assert_almost_equal(mx_test_x2.grad.asnumpy(), + collapse_sum_like(rgrad(y.asnumpy(), np_test_x1, np_test_x2), mx_test_x2.shape), + rtol=1e-1, atol=1e-2, equal_nan=True, use_broadcast=False) + + np_out = getattr(_np, func)(np_test_x1, np_test_x2) + mx_out = getattr(mx.np, func)(mx_test_x1, mx_test_x2) + assert mx_out.shape == np_out.shape + assert_almost_equal(mx_out.asnumpy(), np_out.astype(mx_out.dtype), rtol=1e-3, atol=1e-5, + use_broadcast=False, equal_nan=True) + + assertRaises(NotImplementedError, getattr(np, func), mx_test_x1, mx_test_x2, where=False) + assertRaises(NotImplementedError, getattr(np, func), mx_test_x1, mx_test_x2, subok=False) + assertRaises(NotImplementedError, getattr(np, func), mx_test_x1, mx_test_x2, dtype=_np.int8) + assertRaises(TypeError, getattr(np, func), mx_test_x1, mx_test_x2, dtype="abcdefg") + assertRaises(NotImplementedError, getattr(np, func), mx_test_x1, mx_test_x2, casting='safe') + assertRaises(TypeError, getattr(np, func), mx_test_x1, mx_test_x2, casting='mxnet') + assertRaises(NotImplementedError, getattr(np, func), mx_test_x1, mx_test_x2, order='C') + assertRaises(NotImplementedError, getattr(np, func), mx_test_x1, mx_test_x2, order='mxnet') + + + funcs = { + 'add': (-1.0, 1.0, [lambda y, x1, x2: _np.ones(y.shape)], None), + 'subtract': + (-1.0, 1.0, [lambda y, x1, x2: _np.ones(y.shape)], + [lambda y, x1, x2: -_np.ones(y.shape)]), + 'multiply': (-1.0, 1.0, [lambda y, x1, x2: _np.broadcast_to(x2, y.shape)], + [lambda y, x1, x2: _np.broadcast_to(x1, y.shape)]), + 'divide': (0.1, 1.0, [lambda y, x1, x2: _np.ones(y.shape) / x2], + [lambda y, x1, x2: -x1 / (x2 * x2)]), + 'mod': (1.0, 10.0, + [lambda y, x1, x2: _np.ones(y.shape), + lambda y, x1, x2: _np.zeros(y.shape)], + [lambda y, x1, x2: -_np.floor(x1 / x2), + lambda y, x1, x2: _np.zeros(y.shape)], + [[_np.float16, _np.float32, _np.float64], [_np.int32]]), + 'remainder': (1.0, 10.0, + [lambda y, x1, x2: _np.ones(y.shape), + lambda y, x1, x2: _np.zeros(y.shape)], + [lambda y, x1, x2: -_np.floor(x1 / x2), + lambda y, x1, x2: _np.zeros(y.shape)], + [[_np.float16, _np.float32, _np.float64], [_np.int32]]), + '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]]), + 'maximum': (-1, 1, [lambda y, x1, x2: _np.ones(y.shape) * (x1 >= x2)], + [lambda y, x1, x2: _np.ones(y.shape) * (x1 < x2)]), + 'minimum': (-1, 1, [lambda y, x1, x2: _np.ones(y.shape) * (x1 <= x2)], + [lambda y, x1, x2: _np.ones(y.shape) * (x1 > x2)]), + 'copysign': (-1, 1, + [lambda y, x1, x2: _np.ones(y.shape) * (((x1 * x2) >= 0).astype(_np.float32) - ((x1 * x2) < 0).astype(_np.float32))], + [lambda y, x1, x2: _np.zeros(y.shape)]), + 'arctan2': (-1, 1, [lambda y, x1, x2: x2 / (_np.square(x1) + _np.square(x2))], + [lambda y, x1, x2: -x1 / (_np.square(x1) + _np.square(x2))]), + 'hypot': (-1, 1, [lambda y, x1, x2: x1 / y], + [lambda y, x1, x2: x2 / y]), + 'ldexp': (-3, 3, [None], None, [[_np.int32]]), + } + shape_pairs = [((3, 2), (3, 2)), + ((3, 2), (3, 1)), + ((3, 1), (3, 0)), + ((0, 2), (1, 2)), + ((2, 3, 4), (3, 1)), + ((2, 3), ()), + ((), (2, 3))] + for lshape, rshape in shape_pairs: + for func, func_data in funcs.items(): + dtypes = None + assert (len(func_data) == 4 or len(func_data) == 5) + if len(func_data) is 4: + low, high, lgrads, rgrads = func_data + else: + low, high, lgrads, rgrads, dtypes = func_data + check_binary_func(func, lshape, rshape, low, high, lgrads, rgrads, dtypes) + + @with_seed() @use_np def test_npx_relu(): @@ -1938,32 +2015,6 @@ def test_np_randint(): verify_generator(generator=generator_mx_same_seed, buckets=buckets, probs=probs, nrepeat=100) -@with_seed() -@use_np -def test_np_minimum_maximum(): - def check_symbol_output_type(op_name): - x1, x2 = mx.sym.var('x1').as_np_ndarray(), mx.sym.var('x2').as_np_ndarray() - ret = getattr(mx.sym.np, op_name)(x1, x2) - assert type(ret) == mx.sym.np._Symbol - - def check_comp_op(op_name, x1, x2): - mx_out = getattr(np, op_name)(x1, x2) - if isinstance(x1, np.ndarray) or isinstance(x2, np.ndarray): - assert type(mx_out) == np.ndarray - np_out = getattr(_np, op_name)(x1.asnumpy() if isinstance(x1, np.ndarray) else x1, - x2.asnumpy() if isinstance(x2, np.ndarray) else x2) - assert same(mx_out.asnumpy() if isinstance(mx_out, np.ndarray) else mx_out, np_out) - - op_names = ['minimum', 'maximum'] - for op_name in op_names: - check_symbol_output_type(op_name) - check_comp_op(op_name, np.random.uniform(size=(2, 1)), np.random.uniform(size=(5, 1, 4))) - check_comp_op(op_name, np.random.uniform(size=(2, 0)), np.random.uniform(size=(5, 1, 1))) - check_comp_op(op_name, np.random.uniform(), np.random.uniform(size=(5, 1, 4))) - check_comp_op(op_name, _np.random.uniform(), np.random.uniform(size=(2, 3))) - check_comp_op(op_name, np.random.uniform(size=(2, 3)), _np.random.uniform()) - - @with_seed() @use_np def test_np_swapaxes(): @@ -2507,111 +2558,6 @@ def hybrid_forward(self, F, x): assert_almost_equal(mx_ret.asnumpy(), np_ret, atol=1e-5, rtol=1e-4) -@with_seed() -@use_np -def test_np_copysign(): - class TestCopysign(HybridBlock): - def __init__(self): - super(TestCopysign, self).__init__() - - def hybrid_forward(self, F, a1, a2): - return F.np.copysign(a1, a2) - - def get_grad(a1, a2): - sign = _np.logical_or(_np.logical_and(a1 < 0, a2 < 0), - _np.logical_and(a1 >= 0, a2 >= 0)) - sign = 2 * sign.astype(int) - 1 - sign = sign.reshape(-1, *a1.shape) - sign = _np.sum(sign, axis=0) - return sign, _np.zeros_like(a2) - - def get_grad_left(a1, a2): - sign = _np.logical_or(_np.logical_and(a1 < 0, a2 < 0), - _np.logical_and(a1 >= 0, a2 >= 0)) - sign = 2 * sign.astype(int) - 1 - sign = sign.reshape(a1.shape) - return sign - - def get_grad_right(a1, a2): - return _np.zeros_like(a2) - - shapes = [ - (), - (1), - (2, 1), - (3, 2, 1), - (4, 3, 2, 1), - (2, 4, 3, 2, 1) - ] - types = ['float16', 'float32', 'float64', 'int8', 'int32', 'int64'] - for a1shape in shapes: - for a2shape in shapes: - for hybridize in [True, False]: - for dtype in types: - test_copysign = TestCopysign() - if hybridize: - test_copysign.hybridize() - rtol = 1e-3 - atol = 1e-5 - a1_np = _np.array(_np.random.uniform(-1.0, 1.0, a1shape), dtype=dtype) - a2_np = _np.array(_np.random.uniform(-1.0, 1.0, a2shape), dtype=dtype) - a1 = np.array(a1_np, dtype=dtype) - a2 = np.array(a2_np, dtype=dtype) - a1.attach_grad() - a2.attach_grad() - expected_np = _np.copysign(a1_np, a2_np) - with mx.autograd.record(): - mx_out = test_copysign(a1, a2) - assert mx_out.shape == expected_np.shape - assert_almost_equal(mx_out.asnumpy(), expected_np, rtol=rtol, atol=atol) - - # Test gradient - mx_out.backward() - a1_grad, a2_grad = get_grad(a1_np, a2_np) - assert_almost_equal(a1.grad.asnumpy(), a1_grad, rtol=rtol, atol=atol) - assert_almost_equal(a2.grad.asnumpy(), a2_grad, rtol=rtol, atol=atol) - - # Test imperative once again - mx_out = np.copysign(a1, a2) - expected_np = _np.copysign(a1_np, a2_np) - assert_almost_equal(mx_out.asnumpy(), expected_np, rtol=rtol, atol=atol) - - types = ['float16', 'float32', 'float64'] - for x_shape in shapes: - for dtype in types: - # Test left - x_np = _np.array(_np.random.uniform(-2.0, 2.0, x_shape), dtype=dtype) - scalar = _np.random.uniform(-2.0, 2.0) - x = np.array(x_np, dtype=dtype) - x.attach_grad() - expected_np = _np.copysign(x_np, scalar) - with mx.autograd.record(): - mx_out = np.copysign(x, scalar) - assert mx_out.shape == expected_np.shape - assert_almost_equal(mx_out.asnumpy(), expected_np, rtol=rtol, atol=atol) - - # Test gradient - mx_out.backward() - x_grad = get_grad_left(x_np, scalar) - assert_almost_equal(x.grad.asnumpy(), x_grad, rtol=rtol, atol=atol) - - # Test right - x_np = _np.array(_np.random.uniform(-2.0, 2.0, x_shape), dtype=dtype) - scalar = _np.random.uniform(-2.0, 2.0) - x = np.array(x_np, dtype=dtype) - x.attach_grad() - expected_np = _np.copysign(scalar, x_np) - with mx.autograd.record(): - mx_out = np.copysign(scalar, x) - assert mx_out.shape == expected_np.shape - assert_almost_equal(mx_out.asnumpy(), expected_np, rtol=rtol, atol=atol) - - # Test gradient - mx_out.backward() - x_grad = get_grad_right(scalar, x_np) - assert_almost_equal(x.grad.asnumpy(), x_grad, rtol=rtol, atol=atol) - - @with_seed() @use_np def test_np_svd(): @@ -3011,73 +2957,6 @@ def hybrid_forward(self, F, x): assert_almost_equal(mx_out.asnumpy(), np_out, rtol=rtol, atol=atol) -@with_seed() -@use_np -def test_np_arctan2(): - class TestArctan2(HybridBlock): - def __init__(self): - super(TestArctan2, self).__init__() - - def hybrid_forward(self, F, x1, x2): - return F.np.arctan2(x1, x2) - - # Reduce dimension of src to dimension of des. - def dimReduce(src, des): - srcShape = src.shape - desShape = des.shape - if len(desShape) == 0: - return src.sum() - redu = [] - for i, j in zip(range(len(srcShape)-1, -1, -1), range(len(desShape)-1, -1, -1)): - if srcShape[i] != desShape[j] and desShape[j] == 1: - redu.append(i) - if j == 0: - for k in range(0, i): - redu.append(k) - break - if len(redu) > 0: - src = _np.reshape(src.sum(axis=tuple(redu)), desShape) - return src - - types = ['float64', 'float32', 'float16'] - for hybridize in [True, False]: - for shape1, shape2 in [[(3, 2), (3, 2)], # tall matrices - [(), ()], # scalar only - [(3, 0, 2), (3, 0, 2)], # zero-dim - [(3, 4, 5), (4, 1)], # trailing dim broadcasting - [(3, 4, 5), ()], # scalar broadcasting - [(), (1, 2, 3)], # scalar broadcasting - ]: - for oneType in types: - rtol = 1e-2 if oneType == 'float16' else 1e-3 - atol = 1e-2 if oneType == 'float16' else 1e-5 - test_arctan2 = TestArctan2() - if hybridize: - test_arctan2.hybridize() - x1 = rand_ndarray(shape1, dtype=oneType).as_np_ndarray() - x2 = rand_ndarray(shape2, dtype=oneType).as_np_ndarray() - x11 = x1.asnumpy() - x21 = x2.asnumpy() - x1.attach_grad() - x2.attach_grad() - np_out = _np.arctan2(x1.asnumpy(), x2.asnumpy()) - with mx.autograd.record(): - mx_out = test_arctan2(x1, x2) - assert mx_out.shape == np_out.shape - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=rtol, atol=atol) - mx_out.backward() - np_backward_1 = x21 / (x11 * x11 + x21 * x21) - np_backward_2 = -1 * x11 / (x11 * x11 + x21 * x21) - np_backward_1 = dimReduce(np_backward_1, x11) - np_backward_2 = dimReduce(np_backward_2, x21) - assert_almost_equal(x1.grad.asnumpy(), np_backward_1, rtol=rtol, atol=atol) - assert_almost_equal(x2.grad.asnumpy(), np_backward_2, rtol=rtol, atol=atol) - - mx_out = np.arctan2(x1, x2) - np_out = _np.arctan2(x1.asnumpy(), x2.asnumpy()) - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=rtol, atol=atol) - - @with_seed() @use_np def test_np_nonzero(): @@ -3110,72 +2989,6 @@ def hybrid_forward(self, F, x): assert_almost_equal(mx_out.asnumpy(), np_out, rtol, atol) -@with_seed() -@use_np -def test_np_hypot(): - class TestHypot(HybridBlock): - def __init__(self): - super(TestHypot, self).__init__() - - def hybrid_forward(self, F, x1, x2): - return F.np.hypot(x1, x2) - - def dimReduce(src, des): - srcShape = src.shape - desShape = des.shape - if len(desShape) == 0: - return src.sum() - redu = [] - for i, j in zip(range(len(srcShape)-1, -1, -1), range(len(desShape)-1, -1, -1)): - if srcShape[i] != desShape[j] and desShape[j] == 1: - redu.append(i) - if j == 0: - for k in range(0, i): - redu.append(k) - break - if len(redu) > 0: - src = _np.reshape(src.sum(axis=tuple(redu)), desShape) - return src - - types = ['float64', 'float32', 'float16'] - for hybridize in [True, False]: - for shape1, shape2 in [[(3, 2), (3, 2)], # tall matrices - [(), ()], # scalar only - [(3, 0, 2), (3, 0, 2)], # zero-dim - [(3, 4, 5), (4, 1)], # trailing dim broadcasting - [(3, 4, 5), ()], # scalar broadcasting - [(), (1, 2, 3)], # scalar broadcasting - ]: - for oneType in types: - rtol = 1e-2 if oneType == 'float16' else 1e-3 - atol = 1e-2 if oneType == 'float16' else 1e-5 - test_hypot = TestHypot() - if hybridize: - test_hypot.hybridize() - x1 = rand_ndarray(shape1, dtype=oneType).as_np_ndarray() - x2 = rand_ndarray(shape2, dtype=oneType).as_np_ndarray() - x11 = x1.asnumpy() - x21 = x2.asnumpy() - x1.attach_grad() - x2.attach_grad() - np_out = _np.hypot(x1.asnumpy(), x2.asnumpy()) - with mx.autograd.record(): - mx_out = test_hypot(x1, x2) - assert mx_out.shape == np_out.shape - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=rtol, atol=atol) - mx_out.backward() - np_backward_1 = x11 / np_out - np_backward_2 = x21 / np_out - np_backward_1 = dimReduce(np_backward_1, x11) - np_backward_2 = dimReduce(np_backward_2, x21) - assert_almost_equal(x1.grad.asnumpy(), np_backward_1, rtol=rtol, atol=atol) - assert_almost_equal(x2.grad.asnumpy(), np_backward_2, rtol=rtol, atol=atol) - - mx_out = np.hypot(x1, x2) - np_out = _np.hypot(x1.asnumpy(), x2.asnumpy()) - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=rtol, atol=atol) - - @with_seed() @use_np def test_np_unique(): @@ -3224,49 +3037,6 @@ def hybrid_forward(self, F, a): assert_almost_equal(mx_out[i].asnumpy(), np_out[i], rtol=1e-3, atol=1e-5) -@with_seed() -@use_np -def test_np_lcm(): - shapes = [ - ((3, 1), (3,)), - ((3, 1), (3, 5)), - ((1, 4), (3, 1)), - ((), ()), - ((4, 0), ()), - ((3, 4, 5), ()), - ((), (3, 4, 5)), - ((3, 4, 5), (3, 1, 5)), - ((5, 1), (5, 2)) - ] - - class TestLcm(HybridBlock): - def __init__(self): - super(TestLcm, self).__init__() - - def hybrid_forward(self, F, x1, x2): - return F.np.lcm(x1, x2) - - for hybridize in [False]: - for shape in shapes: - test_lcm = TestLcm() - if hybridize: - test_lcm.hybridize() - - x1 = rand_ndarray(shape[0]).astype(_np.int32).as_np_ndarray() - x2 = rand_ndarray(shape[1]).astype(_np.int32).as_np_ndarray() - - np_out = _np.lcm(x1.asnumpy(), x2.asnumpy()) - mx_out = test_lcm(x1, x2) - - assert mx_out.shape == np_out.shape - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=1e-3, atol=1e-5) - - # Test imperative once again - mx_out = np.lcm(x1, x2) - np_out = _np.lcm(x1.asnumpy(), x2.asnumpy()) - assert_almost_equal(mx_out.asnumpy(), np_out, rtol=1e-3, atol=1e-5) - - @with_seed() @use_np def test_np_take():