Skip to content

Commit

Permalink
[numpy] Misc fix for other chapters (apache#15332)
Browse files Browse the repository at this point in the history
* Add np.prod

* Fix ndarray.reshape accepting positional integers as arguments

* Rebase

* Fix rebase error

* Add np.ndarray.flatten

* Fix

* Add broadcast_to

* Add meshgrid and broadcast_arrays

* Fix sin, cos, sinh, cosh not supporting scalars

* Add more unary ops supporting python scalars

* Fix

* Fix

* Fix ci

* Fix sanity
  • Loading branch information
reminisce authored and haojin2 committed Aug 6, 2019
1 parent e7743f1 commit c2aa908
Show file tree
Hide file tree
Showing 25 changed files with 1,351 additions and 248 deletions.
34 changes: 34 additions & 0 deletions python/mxnet/_numpy_op_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,37 @@ def _npi_multinomial(a):
In other words, each entry ``out[i,j,...,:]`` is an N-dimensional value drawn from the distribution.
"""
pass


def _np_cumsum(a, axis=None, dtype=None, out=None):
"""
Return the cumulative sum of the elements along a given axis.
Parameters
----------
a : array_like
Input array.
axis : int, optional
Axis along which the cumulative sum is computed. The default
(None) is to compute the cumsum over the flattened array.
dtype : dtype, optional
Type of the returned array and of the accumulator in which the
elements are summed. If `dtype` is not specified, it defaults
to the dtype of `a`, unless `a` has an integer dtype with a
precision less than that of the default platform integer. In
that case, the default platform integer is used.
out : ndarray, optional
Alternative output array in which to place the result. It must
have the same shape and buffer length as the expected output
but the type will be cast if necessary. See `doc.ufuncs`
(Section "Output arguments") for more details.
Returns
-------
cumsum_along_axis : ndarray.
A new array holding the result is returned unless `out` is
specified, in which case a reference to `out` is returned. The
result has the same size as `a`, and the same shape as `a` if
`axis` is not None or `a` is a 1-d array.
"""
pass
13 changes: 6 additions & 7 deletions python/mxnet/gluon/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from .utils import _indent, _brief_print_list, HookHandle
from .utils import _check_same_symbol_type, _check_all_np_ndarrays
from .. import numpy_extension as _mx_npx
from .. import numpy as _mx_np
from .. import numpy as _mx_np, numpy_extension as _mx_npx
from .. util import is_np_array


Expand Down Expand Up @@ -336,10 +336,8 @@ def save_parameters(self, filename):
"""
params = self._collect_params_with_prefix()
arg_dict = {key : val._reduce() for key, val in params.items()}
if is_np_array():
_mx_np.save(filename, arg_dict)
else:
ndarray.save(filename, arg_dict)
save_fn = _mx_npx.save if is_np_array() else ndarray.save
save_fn(filename, arg_dict)

def save_params(self, filename):
"""[Deprecated] Please use save_parameters. Note that if you want load
Expand Down Expand Up @@ -389,7 +387,7 @@ def load_parameters(self, filename, ctx=None, allow_missing=False,
<https://mxnet.incubator.apache.org/tutorials/gluon/save_load_params.html>`_
"""
if is_np_array():
loaded = _mx_np.load(filename)
loaded = _mx_npx.load(filename)
else:
loaded = ndarray.load(filename)
params = self._collect_params_with_prefix()
Expand Down Expand Up @@ -920,7 +918,8 @@ def export(self, path, epoch=0, remove_amp_cast=True):
else:
assert name in aux_names
arg_dict['aux:%s'%name] = param._reduce()
ndarray.save('%s-%04d.params'%(path, epoch), arg_dict)
save_fn = _mx_npx.save if is_np_array() else ndarray.save
save_fn('%s-%04d.params'%(path, epoch), arg_dict)

def forward(self, x, *args):
"""Defines the forward computation. Arguments can be either
Expand Down
2 changes: 2 additions & 0 deletions python/mxnet/gluon/data/vision/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ def _get_data(self):
with gzip.open(label_file, 'rb') as fin:
struct.unpack(">II", fin.read(8))
label = np.frombuffer(fin.read(), dtype=np.uint8).astype(np.int32)
if is_np_array():
label = _mx_np.array(label, dtype=label.dtype)

with gzip.open(data_file, 'rb') as fin:
struct.unpack(">IIII", fin.read(16))
Expand Down
2 changes: 1 addition & 1 deletion python/mxnet/ndarray/ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -2510,7 +2510,7 @@ def _get_broadcast_shape(shape1, shape2):
for a, b in zip(shape1[::-1], shape2[::-1]):
if a != 1 and b != 1 and a != b:
raise ValueError('shape1=%s is not broadcastable to shape2=%s' % (shape1, shape2))
shape[i] = max(a, b)
shape[i] = b if a == 1 else a
i -= 1
return tuple(shape)

Expand Down
220 changes: 206 additions & 14 deletions python/mxnet/ndarray/numpy/_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@

__all__ = ['zeros', 'ones', 'maximum', 'minimum', 'stack', 'arange', 'argmax',
'add', 'subtract', 'multiply', 'divide', 'mod', 'power', 'concatenate',
'clip', 'split', 'swapaxes', 'expand_dims', 'tile', 'linspace']
'clip', 'split', 'swapaxes', 'expand_dims', 'tile', 'linspace',
'sin', 'cos', 'sinh', 'cosh', 'log10', 'sqrt']


@set_module('mxnet.ndarray.numpy')
Expand Down Expand Up @@ -99,29 +100,29 @@ def _ufunc_helper(lhs, rhs, fn_array, fn_scalar, lfn_scalar, rfn_scalar=None, ou
Parameters
--------
lhs : NDArray or numeric value
lhs : ndarray or numeric value
Left-hand side operand.
rhs : NDArray or numeric value
rhs : ndarray or numeric value
Right-hand operand,
fn_array : function
Function to be called if both lhs and rhs are of ``NDArray`` type.
Function to be called if both lhs and rhs are of ``ndarray`` type.
fn_scalar : function
Function to be called if both lhs and rhs are numeric values.
lfn_scalar : function
Function to be called if lhs is ``NDArray`` while rhs is numeric value
Function to be called if lhs is ``ndarray`` while rhs is numeric value
rfn_scalar : function
Function to be called if lhs is numeric value while rhs is ``NDArray``;
Function to be called if lhs is numeric value while rhs is ``ndarray``;
if none is provided, then the function is commutative, so rfn_scalar is equal to lfn_scalar
Returns
--------
mxnet.numpy.ndarray
result array
mxnet.numpy.ndarray or scalar
result array or scalar
"""
from ...numpy import ndarray
if isinstance(lhs, numeric_types):
Expand All @@ -138,7 +139,7 @@ def _ufunc_helper(lhs, rhs, fn_array, fn_scalar, lfn_scalar, rfn_scalar=None, ou
elif isinstance(rhs, ndarray):
return fn_array(lhs, rhs, out=out)
else:
raise TypeError('type %s not supported' % str(type(rhs)))
raise TypeError('type {} not supported'.format(str(type(rhs))))
#pylint: enable= too-many-arguments, no-member, protected-access


Expand Down Expand Up @@ -633,7 +634,7 @@ def tile(A, reps):


@set_module('mxnet.ndarray.numpy')
def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0, **kwargs): #pylint: disable=too-many-arguments
def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0, **kwargs): # pylint: disable=too-many-arguments
"""Return evenly spaced numbers over a specified interval.
Returns num evenly spaced samples, calculated over the interval [start, stop].
Expand All @@ -653,15 +654,16 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis
endpoint : bool, optional
If True, stop is the last sample. Otherwise, it is not included.
Default is True.
retstep: bool, optional
retstep : bool, optional
If True, return (samples, step), where step is the spacing between samples.
dtype: dtype, optional
dtype : dtype, optional
The type of the output array. If dtype is not given, infer the data
type from the other input arguments.
axis : int, optional
The axis in the result to store the samples. Relevant only if start or
stop are array-like. By default (0), the samples will be along a new
axis inserted at the beginning. Use -1 to get an axis at the end.
Returns
-------
samples : ndarray
Expand All @@ -678,7 +680,7 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis
axis could only be 0 now.
"""
if isinstance(start, (list, _np.ndarray, NDArray)) or \
isinstance(stop, (list, _np.ndarray, NDArray)):
isinstance(stop, (list, _np.ndarray, NDArray)):
raise NotImplementedError('start and stop only support int')
if axis != 0:
raise NotImplementedError("the function only support axis 0")
Expand All @@ -687,6 +689,196 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis
ctx = current_context()
if retstep:
step = (stop - start) / (num - 1)
return (_npi.linspace(start=start, stop=stop, num=num, endpoint=endpoint, ctx=ctx, dtype=dtype), step)
return _npi.linspace(start=start, stop=stop, num=num, endpoint=endpoint, ctx=ctx, dtype=dtype), step
else:
return _npi.linspace(start=start, stop=stop, num=num, endpoint=endpoint, ctx=ctx, dtype=dtype)


def _unary_func_helper(x, fn_array, fn_scalar, out=None, **kwargs):
"""Helper function for unary operators.
Parameters
----------
x : ndarray or scalar
Input of the unary operator.
fn_array : function
Function to be called if x is of ``ndarray`` type.
fn_scalar : function
Function to be called if x is a Python scalar.
out : ndarray
The buffer ndarray for storing the result of the unary function.
Returns
-------
out : mxnet.numpy.ndarray or scalar
Result array or scalar.
"""
if isinstance(x, numeric_types):
return fn_scalar(x, **kwargs)
elif isinstance(x, NDArray):
return fn_array(x, out=out, **kwargs)
else:
raise TypeError('type {} not supported'.format(str(type(x))))


@set_module('mxnet.ndarray.numpy')
def sin(x, out=None, **kwargs):
r"""Trigonometric sine, element-wise.
Parameters
----------
x : ndarray or scalar
Angle, in radians (:math:`2 \pi` rad equals 360 degrees).
out : ndarray or None
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. 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.
"""
return _unary_func_helper(x, _npi.sin, _np.sin, out=out, **kwargs)


@set_module('mxnet.ndarray.numpy')
def cos(x, out=None, **kwargs):
r"""Cosine, element-wise.
Parameters
----------
x : ndarray or scalar
Angle, in radians (:math:`2 \pi` rad equals 360 degrees).
out : ndarray or None
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. 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.
"""
return _unary_func_helper(x, _npi.cos, _np.cos, out=out, **kwargs)


@set_module('mxnet.ndarray.numpy')
def sinh(x, out=None, **kwargs):
"""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
Input array or scalar.
out : ndarray or None
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. 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.
"""
return _unary_func_helper(x, _npi.sinh, _np.sinh, out=out, **kwargs)


@set_module('mxnet.ndarray.numpy')
def cosh(x, out=None, **kwargs):
"""Hyperbolic cosine, element-wise.
Equivalent to ``1/2 * (np.exp(x) + np.exp(-x))`` and ``np.cos(1j*x)``.
Parameters
----------
x : ndarray or scalar
Input array or scalar.
out : ndarray or None
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. 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.
"""
return _unary_func_helper(x, _npi.cosh, _np.cosh, out=out, **kwargs)


@set_module('mxnet.ndarray.numpy')
def log10(x, out=None, **kwargs):
"""Return the base 10 logarithm of the input array, element-wise.
Parameters
----------
x : ndarray or scalar
Input array or scalar.
out : ndarray or None
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. 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.
"""
return _unary_func_helper(x, _npi.log10, _np.log10, out=out, **kwargs)


@set_module('mxnet.ndarray.numpy')
def sqrt(x, out=None, **kwargs):
"""
Return the non-negative square-root of an array, element-wise.
Parameters
----------
x : ndarray or scalar
The values whose square-roots are required.
out : ndarray, or None, optional
A location into which the result is stored. If provided, it must have
a shape that the inputs broadcast to. If not provided or `None`,
a freshly-allocated array is returned.
Returns
-------
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.
"""
return _unary_func_helper(x, _npi.sqrt, _np.sqrt, out=out, **kwargs)
Loading

0 comments on commit c2aa908

Please sign in to comment.