From 6039853e04fbafb46555b09d2c5d7f7cdc426ad3 Mon Sep 17 00:00:00 2001 From: Alicia1529 Date: Wed, 13 Nov 2019 21:28:36 +0800 Subject: [PATCH] add numpy op full_like, c++ impl, fix zeros_like, ones_like type inference --- python/mxnet/_numpy_op_doc.py | 71 ------- python/mxnet/ndarray/numpy/_op.py | 173 +++++++++++++++++- python/mxnet/numpy/multiarray.py | 173 +++++++++++++++++- python/mxnet/numpy_dispatch_protocol.py | 4 +- python/mxnet/symbol/numpy/_symbol.py | 132 ++++++++++++- src/operator/numpy/np_init_op.cc | 28 +-- src/operator/numpy/np_init_op.cu | 7 +- src/operator/tensor/init_op.h | 46 +++++ .../unittest/test_numpy_interoperability.py | 14 +- tests/python/unittest/test_numpy_op.py | 48 +++++ 10 files changed, 590 insertions(+), 106 deletions(-) diff --git a/python/mxnet/_numpy_op_doc.py b/python/mxnet/_numpy_op_doc.py index 33158baf10a5..9a75ba425785 100644 --- a/python/mxnet/_numpy_op_doc.py +++ b/python/mxnet/_numpy_op_doc.py @@ -20,77 +20,6 @@ """Doc placeholder for numpy ops with prefix _np.""" -def _np_ones_like(a): - """ - Return an array of ones with the same shape and type as a given array. - - Parameters - ---------- - a : ndarray - The shape and data-type of `a` define these same attributes of - the returned array. - - Returns - ------- - out : ndarray - Array of ones with the same shape and type as `a`. - - Examples - -------- - >>> x = np.arange(6) - >>> x = x.reshape((2, 3)) - >>> x - array([[0., 1., 2.], - [3., 4., 5.]]) - >>> np.ones_like(x) - array([[1., 1., 1.], - [1., 1., 1.]]) - - >>> y = np.arange(3, dtype=float) - >>> y - array([0., 1., 2.], dtype=float64) - >>> - >>> np.ones_like(y) - array([1., 1., 1.], dtype=float64) - """ - pass - - -def _np_zeros_like(a): - """ - Return an array of zeros with the same shape and type as a given array. - - Parameters - ---------- - a : ndarray - The shape and data-type of `a` define these same attributes of - the returned array. - - Returns - ------- - out : ndarray - Array of zeros with the same shape and type as `a`. - - Examples - -------- - >>> x = np.arange(6) - >>> x = x.reshape((2, 3)) - >>> x - array([[0., 1., 2.], - [3., 4., 5.]]) - >>> np.zeros_like(x) - array([[0., 0., 0.], - [0., 0., 0.]]) - >>> y = np.arange(3, dtype=float) - >>> y - array([0., 1., 2.], dtype=float64) - >>> - >>> np.zeros_like(y) - array([0., 0., 0.], dtype=float64) - """ - pass - - def _np_cumsum(a, axis=None, dtype=None, out=None): """ Return the cumulative sum of the elements along a given axis. diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index a8179c2b70cf..971256a2529f 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -28,7 +28,8 @@ from . import _internal as _npi from ..ndarray import NDArray -__all__ = ['zeros', 'ones', 'full', 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', +__all__ = ['zeros', 'zeros_like', 'ones', 'ones_like', 'full', 'full_like', + 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', 'arctan2', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'log10', 'sqrt', 'cbrt', 'abs', 'absolute', 'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor', @@ -110,6 +111,118 @@ def ones(shape, dtype=_np.float32, order='C', ctx=None): return _npi.ones(shape=shape, ctx=ctx, dtype=dtype) +@set_module('mxnet.ndarray.numpy') +def zeros_like(a, dtype=None, order='C', ctx=None, out=None): + """ + Return an array of zeros with the same shape and type as a given array. + + Parameters + ---------- + a : ndarray + The shape and data-type of `a` define these same attributes of + the returned array. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : ndarray + Array of zeros with the same shape and type as a. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full : Return a new array of given shape filled with value. + + Examples + -------- + >>> x = np.arange(6) + >>> x = x.reshape((2, 3)) + >>> x + array([[0., 1., 2.], + [3., 4., 5.]]) + >>> np.zeros_like(x) + array([[0., 0., 0.], + [0., 0., 0.]]) + >>> np.zeros_like(x, int) + array([[0, 0, 0], + [0, 0, 0]], dtype=int64) + >>> y = np.arange(3, dtype=float) + >>> y + array([0., 1., 2.], dtype=float64) + >>> np.zeros_like(y) + array([0., 0., 0.], dtype=float64) + """ + return _npi.full_like(a, fill_value=0, dtype=dtype, ctx=None, out=None) + + +@set_module('mxnet.ndarray.numpy') +def ones_like(a, dtype=None, order='C', ctx=None, out=None): + """ + Return an array of ones with the same shape and type as a given array. + + Parameters + ---------- + a : ndarray + The shape and data-type of `a` define these same attributes of + the returned array. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : ndarray + Array of ones with the same shape and type as a. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full_like : Return a new array with shape of input filled with value. + ones : Return a new array setting values to one. + + Examples + -------- + >>> x = np.arange(6) + >>> x = x.reshape((2, 3)) + >>> x + array([[0., 1., 2.], + [3., 4., 5.]]) + >>> np.ones_like(x) + array([[1., 1., 1.], + [1., 1., 1.]]) + >>> np.ones_like(x, int) + array([[1, 1, 1], + [1, 1, 1]], dtype=int64) + >>> y = np.arange(3, dtype=float) + >>> y + array([0., 1., 2.], dtype=float64) + >>> np.ones_like(y) + array([1., 1., 1.], dtype=float64) + """ + return _npi.full_like(a, fill_value=1, dtype=dtype, ctx=None, out=None) + + @set_module('mxnet.ndarray.numpy') def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): # pylint: disable=too-many-arguments """ @@ -165,6 +278,64 @@ def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): # pylin return _npi.full(shape=shape, value=fill_value, ctx=ctx, dtype=dtype, out=out) +@set_module('mxnet.ndarray.numpy') +def full_like(a, fill_value, dtype=None, order='C', ctx=None, out=None): # pylint: disable=too-many-arguments + """ + Return a full array with the same shape and type as a given array. + + Parameters + ---------- + a : ndarray + The shape and data-type of `a` define these same attributes of + the returned array. + fill_value : scalar + Fill value. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : ndarray + Array of `fill_value` with the same shape and type as `a`. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full : Return a new array of given shape filled with value. + + Examples + -------- + >>> x = np.arange(6, dtype=int) + >>> np.full_like(x, 1) + array([1, 1, 1, 1, 1, 1], dtype=int64) + >>> np.full_like(x, 0.1) + array([0, 0, 0, 0, 0, 0], dtype=int64) + >>> np.full_like(x, 0.1, dtype=np.float64) + array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1], dtype=float64) + >>> np.full_like(x, np.nan, dtype=np.double) + array([nan, nan, nan, nan, nan, nan], dtype=float64) + >>> y = np.arange(6, dtype=np.float32) + >>> np.full_like(y, 0.1) + array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) + """ + if order != 'C': + raise NotImplementedError + if ctx is None: + ctx = current_context() + return _npi.full_like(a, fill_value=fill_value, dtype=dtype, ctx=ctx, out=out) + + @set_module('mxnet.ndarray.numpy') def arange(start, stop=None, step=1, dtype=None, ctx=None): """Return evenly spaced values within a given interval. diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index bbbb0fd2fd6f..53455e834a30 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -46,8 +46,9 @@ from ..ndarray.numpy import _internal as _npi from ..ndarray.ndarray import _storage_type -__all__ = ['ndarray', 'empty', 'array', 'zeros', 'ones', 'full', 'add', 'subtract', 'multiply', 'divide', - 'mod', 'remainder', 'power', 'arctan2', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'log10', +__all__ = ['ndarray', 'empty', 'array', 'zeros', 'zeros_like', 'ones', 'ones_like', 'full', 'full_like', + 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', + 'arctan2', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'log10', 'sqrt', 'cbrt', 'abs', 'absolute', 'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor', 'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'append', @@ -946,7 +947,7 @@ def attach_grad(self, grad_req='write'): # pylint: disable=arguments-differ - 'add': gradient will be added to existing value on every backward. - 'null': do not compute gradient for this NDArray. """ - grad = _mx_np_op.zeros_like(self) # pylint: disable=undefined-variable + grad = _mx_nd_np.zeros_like(self) # pylint: disable=undefined-variable grad_req = _GRAD_REQ_MAP[grad_req] check_call(_LIB.MXAutogradMarkVariables( 1, ctypes.pointer(self.handle), @@ -7243,6 +7244,172 @@ def resize(a, new_shape): return _mx_nd_np.resize(a, new_shape) +@set_module('mxnet.numpy') +def full_like(a, fill_value, dtype=None, order='C', ctx=None, out=None): + """ + Return a full array with the same shape and type as a given array. + + Parameters + ---------- + a : ndarray + The shape and data-type of `a` define these same attributes of + the returned array. + fill_value : scalar + Fill value. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : ndarray + Array of `fill_value` with the same shape and type as `a`. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full : Return a new array of given shape filled with value. + + Examples + -------- + >>> x = np.arange(6, dtype=int) + >>> np.full_like(x, 1) + array([1, 1, 1, 1, 1, 1], dtype=int64) + >>> np.full_like(x, 0.1) + array([0, 0, 0, 0, 0, 0], dtype=int64) + >>> np.full_like(x, 0.1, dtype=np.float64) + array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1], dtype=float64) + >>> np.full_like(x, np.nan, dtype=np.float64) + array([nan, nan, nan, nan, nan, nan], dtype=float64) + >>> y = np.arange(6, dtype=np.float32) + >>> np.full_like(y, 0.1) + array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) + """ + return _mx_nd_np.full_like(a, fill_value=fill_value, dtype=dtype, order=order, ctx=None, out=None) + + +@set_module('mxnet.numpy') +def zeros_like(a, dtype=None, order='C', ctx=None, out=None): + """ + Return an array of zeros with the same shape and type as a given array. + + Parameters + ---------- + a : ndarray + The shape and data-type of `a` define these same attributes of + the returned array. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : ndarray + Array of zeros with the same shape and type as a. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full : Return a new array of given shape filled with value. + + Examples + -------- + >>> x = np.arange(6) + >>> x = x.reshape((2, 3)) + >>> x + array([[0., 1., 2.], + [3., 4., 5.]]) + >>> np.zeros_like(x) + array([[0., 0., 0.], + [0., 0., 0.]]) + >>> np.zeros_like(x, int) + array([[0, 0, 0], + [0, 0, 0]], dtype=int64) + >>> y = np.arange(3, dtype=float) + >>> y + array([0., 1., 2.], dtype=float64) + >>> np.zeros_like(y) + array([0., 0., 0.], dtype=float64) + """ + return _mx_nd_np.full_like(a, fill_value=0, dtype=dtype, order=order, ctx=None, out=None) + + +@set_module('mxnet.numpy') +def ones_like(a, dtype=None, order='C', ctx=None, out=None): + """ + Return an array of ones with the same shape and type as a given array. + + Parameters + ---------- + a : ndarray + The shape and data-type of `a` define these same attributes of + the returned array. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : ndarray + Array of ones with the same shape and type as a. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full_like : Return a new array with shape of input filled with value. + ones : Return a new array setting values to one. + + Examples + -------- + >>> x = np.arange(6) + >>> x = x.reshape((2, 3)) + >>> x + array([[0., 1., 2.], + [3., 4., 5.]]) + >>> np.ones_like(x) + array([[1., 1., 1.], + [1., 1., 1.]]) + >>> np.ones_like(x, int) + array([[1, 1, 1], + [1, 1, 1]], dtype=int64) + >>> y = np.arange(3, dtype=float) + >>> y + array([0., 1., 2.], dtype=float64) + >>> np.ones_like(y) + array([1., 1., 1.], dtype=float64) + """ + return _mx_nd_np.full_like(a, fill_value=1, dtype=dtype, order=order, ctx=None, out=None) + + @set_module('mxnet.numpy') def nan_to_num(x, copy=True, nan=0.0, posinf=None, neginf=None, **kwargs): """ diff --git a/python/mxnet/numpy_dispatch_protocol.py b/python/mxnet/numpy_dispatch_protocol.py index ef9ed6cc363f..1f7afc9032e9 100644 --- a/python/mxnet/numpy_dispatch_protocol.py +++ b/python/mxnet/numpy_dispatch_protocol.py @@ -137,6 +137,7 @@ def _run_with_array_ufunc_proto(*args, **kwargs): 'diff', 'resize', 'where', + 'full_like', ] @@ -233,7 +234,8 @@ def _register_array_function(): 'less', 'less_equal', 'greater', - 'greater_equal' + 'greater_equal', + 'full_like', ] diff --git a/python/mxnet/symbol/numpy/_symbol.py b/python/mxnet/symbol/numpy/_symbol.py index 7da771966f1f..effab2e88207 100644 --- a/python/mxnet/symbol/numpy/_symbol.py +++ b/python/mxnet/symbol/numpy/_symbol.py @@ -30,7 +30,8 @@ from .._internal import _set_np_symbol_class from . import _internal as _npi -__all__ = ['zeros', 'ones', 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', 'arctan2', +__all__ = ['zeros', 'zeros_like', 'ones', 'ones_like', 'full_like', + 'add', 'subtract', 'multiply', 'divide', 'mod', 'remainder', 'power', 'arctan2', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'log10', 'sqrt', 'cbrt', 'abs', 'absolute', 'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor', @@ -980,6 +981,135 @@ def full(shape, fill_value, dtype=None, order='C', ctx=None, out=None): # pylin return _npi.full(shape=shape, value=fill_value, ctx=ctx, dtype=dtype, out=out) +@set_module('mxnet.symbol.numpy') +def full_like(a, fill_value, dtype=None, order='C', ctx=None, out=None): # pylint: disable=too-many-arguments + """ + Return a full array with the same shape and type as a given array. + + Parameters + ---------- + a : _Symbol + The shape and data-type of `a` define these same attributes of + the returned array. + fill_value : scalar + Fill value. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : _Symbol + Array `fill_value` with the same shape and type as `a`. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + full : Return a new array of given shape filled with value. + """ + if order != 'C': + raise NotImplementedError + if ctx is None: + ctx = current_context() + return _npi.full_like(a, fill_value=fill_value, ctx=ctx, dtype=dtype, out=out) + + +@set_module('mxnet.symbol.numpy') +def zeros_like(a, dtype=None, order='C', ctx=None, out=None): # pylint: disable=too-many-arguments + """ + Return an array of zeros with the same shape and type as a given array. + + Parameters + ---------- + a : _Symbol + The shape and data-type of `a` define these same attributes of + the returned array. + fill_value : scalar + Fill value. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : _Symbol + Array of zeros with the same shape and type as `a`. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + zeros : Return a new array of given shape filled with zeros. + """ + if order != 'C': + raise NotImplementedError + if ctx is None: + ctx = current_context() + return _npi.full_like(a, fill_value=0, ctx=ctx, dtype=dtype, out=out) + + +@set_module('mxnet.symbol.numpy') +def ones_like(a, dtype=None, order='C', ctx=None, out=None): # pylint: disable=too-many-arguments + """ + Return an array of ones with the same shape and type as a given array. + + Parameters + ---------- + a : _Symbol + The shape and data-type of `a` define these same attributes of + the returned array. + fill_value : scalar + Fill value. + dtype : data-type, optional + Overrides the data type of the result. + Temporarily do not support boolean type. + order : {'C'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. Currently only supports C order. + ctx: to specify the device, e.g. the i-th GPU. + out : ndarray or None, optional + 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 + ------- + out : _Symbol + Array of ones with the same shape and type as `a`. + + See Also + -------- + empty_like : Return an empty array with shape and type of input. + ones_like : Return an array of ones with shape and type of input. + zeros_like : Return an array of zeros with shape and type of input. + zeros : Return a new array of given shape filled with zeros. + """ + if order != 'C': + raise NotImplementedError + if ctx is None: + ctx = current_context() + return _npi.full_like(a, fill_value=1, ctx=ctx, dtype=dtype, out=out) + + @set_module('mxnet.symbol.numpy') def identity(n, dtype=None, ctx=None): """ diff --git a/src/operator/numpy/np_init_op.cc b/src/operator/numpy/np_init_op.cc index f2446c85b182..669f0a6c4399 100644 --- a/src/operator/numpy/np_init_op.cc +++ b/src/operator/numpy/np_init_op.cc @@ -34,6 +34,7 @@ namespace op { DMLC_REGISTER_PARAMETER(NumpyEyeParam); DMLC_REGISTER_PARAMETER(IndicesOpParam); DMLC_REGISTER_PARAMETER(LogspaceParam); +DMLC_REGISTER_PARAMETER(FullLikeOpParam); inline bool NumpyIndicesShape(const nnvm::NodeAttrs& attrs, mxnet::ShapeVector* in_shapes, @@ -97,29 +98,13 @@ NNVM_REGISTER_OP(_npi_identity) .set_attr("FCompute", IdentityCompute) .add_arguments(InitOpParam::__FIELDS__()); -NNVM_REGISTER_OP(_np_zeros_like) -.set_num_inputs(1) -.set_num_outputs(1) -.set_attr("FInferShape", ElemwiseShape<1, 1>) -.set_attr("FInferType", ElemwiseType<1, 1>) -.set_attr("FIgnoreInputs", - [](const NodeAttrs& attrs) { - return std::vector(1, 0); - }) -.set_attr("FListInputNames", - [](const NodeAttrs& attrs) { - return std::vector{"a"}; - }) -.set_attr("FCompute", FillCompute) -.set_attr("FGradient", MakeZeroGradNodes) -.add_argument("a", "NDArray-or-Symbol", - "The shape and data-type of a define these same attributes of the returned array."); -NNVM_REGISTER_OP(_np_ones_like) +NNVM_REGISTER_OP(_npi_full_like) .set_num_inputs(1) .set_num_outputs(1) +.set_attr_parser(ParamParser) .set_attr("FInferShape", ElemwiseShape<1, 1>) -.set_attr("FInferType", ElemwiseType<1, 1>) +.set_attr("FInferType", FullLikeOpType) .set_attr("FIgnoreInputs", [](const NodeAttrs& attrs) { return std::vector(1, 0); @@ -128,10 +113,11 @@ NNVM_REGISTER_OP(_np_ones_like) [](const NodeAttrs& attrs) { return std::vector{"a"}; }) -.set_attr("FCompute", FillCompute) +.set_attr("FCompute", FullLikeOpCompute) .set_attr("FGradient", MakeZeroGradNodes) .add_argument("a", "NDArray-or-Symbol", - "The shape and data-type of a define these same attributes of the returned array."); + "The shape and data-type of a define these same attributes of the returned array.") +.add_arguments(FullLikeOpParam::__FIELDS__()); NNVM_REGISTER_OP(_npi_arange) .set_num_inputs(0) diff --git a/src/operator/numpy/np_init_op.cu b/src/operator/numpy/np_init_op.cu index b6e2f93e8d3c..5095fe60bdef 100644 --- a/src/operator/numpy/np_init_op.cu +++ b/src/operator/numpy/np_init_op.cu @@ -38,11 +38,8 @@ NNVM_REGISTER_OP(_npi_ones) NNVM_REGISTER_OP(_npi_identity) .set_attr("FCompute", IdentityCompute); -NNVM_REGISTER_OP(_np_zeros_like) -.set_attr("FCompute", FillCompute); - -NNVM_REGISTER_OP(_np_ones_like) -.set_attr("FCompute", FillCompute); +NNVM_REGISTER_OP(_npi_full_like) +.set_attr("FCompute", FullLikeOpCompute); NNVM_REGISTER_OP(_npi_arange) .set_attr("FCompute", RangeCompute); diff --git a/src/operator/tensor/init_op.h b/src/operator/tensor/init_op.h index a0139f7fde2d..c7afdc6baaa3 100644 --- a/src/operator/tensor/init_op.h +++ b/src/operator/tensor/init_op.h @@ -80,6 +80,39 @@ struct InitOpWithoutDTypeParam : public dmlc::Parameter } }; +struct FullLikeOpParam : public dmlc::Parameter { + double fill_value; + std::string ctx; + dmlc::optional dtype; + DMLC_DECLARE_PARAMETER(FullLikeOpParam) { + DMLC_DECLARE_FIELD(fill_value) + .describe("Value with which to fill newly created tensor"); + DMLC_DECLARE_FIELD(ctx) + .set_default("") + .describe("Context of output, in format [cpu|gpu|cpu_pinned](n)." + "Only used for imperative calls."); + DMLC_DECLARE_FIELD(dtype).set_default(dmlc::optional()) + MXNET_ADD_ALL_TYPES + .describe("Target data type."); + } +}; + +/*! \brief Infer type of FullLikeOpCompute*/ +template +inline bool FullLikeOpType(const nnvm::NodeAttrs& attrs, + std::vector *in_attrs, + std::vector *out_attrs) { + const ParamType& param = nnvm::get(attrs.parsed); + CHECK_EQ(in_attrs->size(), 1U); + CHECK_EQ(out_attrs->size(), 1U); + if (param.dtype.has_value()) { + TYPE_ASSIGN_CHECK(*out_attrs, 0, param.dtype.value()); + } else { + TYPE_ASSIGN_CHECK(*out_attrs, 0, in_attrs->at(0)); + } + return out_attrs->at(0) != -1;; +} + struct EyeParam : public dmlc::Parameter { nnvm::dim_t N; nnvm::dim_t M; @@ -402,6 +435,19 @@ void FillCompute(const nnvm::NodeAttrs& attrs, Fill(ctx.get_stream(), outputs[0], req[0], value); } +/*! \brief Fill output with a scalar integer value */ +template +void FullLikeOpCompute(const nnvm::NodeAttrs& attrs, + const OpContext& ctx, + const std::vector& inputs, + const std::vector& req, + const std::vector& outputs) { + CHECK_EQ(inputs.size(), 1U); + CHECK_EQ(outputs.size(), 1U); + const auto& param = nnvm::get(attrs.parsed); + Fill(ctx.get_stream(), outputs[0], req[0], param.fill_value); +} + /*! \brief Fill output with an arbitrary value */ template void InitFillWithScalarCompute(const nnvm::NodeAttrs &attrs, diff --git a/tests/python/unittest/test_numpy_interoperability.py b/tests/python/unittest/test_numpy_interoperability.py index 79a478679aaf..025ebf77dac3 100644 --- a/tests/python/unittest/test_numpy_interoperability.py +++ b/tests/python/unittest/test_numpy_interoperability.py @@ -716,9 +716,16 @@ def _add_workload_var(array_pool): def _add_workload_zeros_like(array_pool): OpArgMngr.add_workload('zeros_like', array_pool['4x1']) - OpArgMngr.add_workload('zeros_like', np.random.uniform(size=(3, 3)).astype(np.float64)) - OpArgMngr.add_workload('zeros_like', np.random.uniform(size=(3, 3)).astype(np.float32)) - OpArgMngr.add_workload('zeros_like', np.random.randint(2, size = (3, 3))) + OpArgMngr.add_workload('zeros_like', np.random.uniform(size=(3, 3)).astype(np.float64), np.int64) + OpArgMngr.add_workload('zeros_like', np.random.uniform(size=(3, 3)).astype(np.float32), np.float64) + OpArgMngr.add_workload('zeros_like', np.random.randint(2, size = (3, 3)), int) + + +def _add_workload_full_like(): + OpArgMngr.add_workload('full_like', np.random.uniform(low=0, high=100, size=(1,3,4), dtype='float64'), 1) + OpArgMngr.add_workload('full_like', np.random.uniform(low=0, high=100, size=(9,3,1)), 2, np.int64) + OpArgMngr.add_workload('full_like', np.random.uniform(low=0, high=100, size=(9,3)), np.nan) + OpArgMngr.add_workload('full_like', np.random.uniform(low=0, high=100, size=(2,0)), 0, np.float32) def _add_workload_outer(): @@ -1320,6 +1327,7 @@ def _prepare_workloads(): _add_workload_where() _add_workload_diff() _add_workload_resize() + _add_workload_full_like() _prepare_workloads() diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 76b38eb45205..9ada9a3c231b 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -17,6 +17,7 @@ # pylint: skip-file from __future__ import absolute_import +from distutils.version import StrictVersion import sys import unittest import itertools @@ -3404,6 +3405,53 @@ def g(data): assert_almost_equal(mx_out.asnumpy(), expected_np, rtol=rtol, atol=atol) +@with_seed() +@use_np +def test_np_full_like(): + class TestFullLike(HybridBlock): + def __init__(self, fill_value, dtype, ctx): + super(TestFullLike, self).__init__() + self._fill_value = fill_value + self._dtype = dtype + self._ctx = ctx + + def hybrid_forward(self, F, x, *args, **kwargs): + return F.np.full_like(x, self._fill_value, dtype=self._dtype, ctx=self._ctx) + + if StrictVersion(platform.python_version()) < StrictVersion('3.0.0'): + return + + dtypes = ['float64', 'float32', 'float16', 'int64', 'int32', 'int8'] + shapes = [ + (), + (1,), + (4, 3), + (4, 5), + (2, 1), + (6, 5, 6), + (4, 2, 1, 2), + (5, 1, 3, 3), + (3, 3, 1, 0), + ] + # numpy.full_like operator in py2 cannot handle shape like (5, 0, 3) properly + fill_values = [0, 1, 2, 3, 4, 5, 6] + flags = [True, False] + for fill_value, dtype, shape, hybridize in itertools.product( + fill_values, dtypes, shapes, flags): + param_dtype= _np.random.choice(dtypes) + a = np.random.uniform(low=0, high=100, size=shape, dtype='float64').astype(dtype) + test = TestFullLike(fill_value, param_dtype, npx.current_context()) + expected_ret = _np.full_like(a.asnumpy(), fill_value=fill_value, dtype=param_dtype) + if hybridize: + test.hybridize() + ret = test(a) + assert_almost_equal(ret.asnumpy(), expected_ret, rtol=1e-3, atol=1e-5) + + # check imperative again + ret = np.full_like(a, fill_value, param_dtype) + assert_almost_equal(ret.asnumpy(), expected_ret, rtol=1e-3, atol=1e-5) + + @with_seed() @use_np def test_np_roll():