diff --git a/benchmark/python/ffi/benchmark_ffi.py b/benchmark/python/ffi/benchmark_ffi.py index 96d8e1d6658f..99f6ac95dc9e 100644 --- a/benchmark/python/ffi/benchmark_ffi.py +++ b/benchmark/python/ffi/benchmark_ffi.py @@ -62,6 +62,9 @@ def prepare_workloads(): OpArgMngr.add_workload("split", pool['3x3'], (0, 1, 2), axis=1) OpArgMngr.add_workload("argmax", pool['3x2'], axis=-1) OpArgMngr.add_workload("argmin", pool['3x2'], axis=-1) + OpArgMngr.add_workload("atleast_1d", pool['2'], pool['2x2']) + OpArgMngr.add_workload("atleast_2d", pool['2'], pool['2x2']) + OpArgMngr.add_workload("atleast_3d", pool['2'], pool['2x2']) OpArgMngr.add_workload("indices", dimensions=(1, 2, 3)) OpArgMngr.add_workload("subtract", pool['2x2'], pool['2x2']) OpArgMngr.add_workload("multiply", pool['2x2'], pool['2x2']) diff --git a/python/mxnet/_numpy_op_doc.py b/python/mxnet/_numpy_op_doc.py index 3d80ce03b44e..8341d43608ce 100644 --- a/python/mxnet/_numpy_op_doc.py +++ b/python/mxnet/_numpy_op_doc.py @@ -370,113 +370,6 @@ def _np_copy(a, out=None): pass -def _np_atleast_1d(*arys): - """ - Convert inputs to arrays with at least one dimension. - - Scalar inputs are converted to 1-dimensional arrays, whilst higher-dimensional inputs are preserved. - - Parameters - ---------- - arys1, arys2, ... : ndarray - One or more input arrays. - - Returns - ------- - ret : ndarray - An array, or list of arrays, each with a.ndim >= 1. Copies are made only if necessary. - - See also - -------- - atleast_2d, atleast_3d - - Examples - -------- - >>> np.atleast_1d(1.0) - array([1.]) - >>> x = np.arange(9.0).reshape(3,3) - >>> np.atleast_1d(x) - array([[0., 1., 2.], - [3., 4., 5.], - [6., 7., 8.]]) - >>> np.atleast_1d(np.array(1), np.array([3, 4])) - [array([1.]), array([3., 4.])] - """ - pass - - -def _np_atleast_2d(*arys): - """ - Convert inputs to arrays with at least two dimensions. - - Parameters - ---------- - arys1, arys2, ... : ndarray - One or more input arrays. - - Returns - ------- - ret : ndarray - An array, or list of arrays, each with a.ndim >= 2. Copies are made only if necessary. - - See also - -------- - atleast_1d, atleast_3d - - Examples - -------- - >>> np.atleast_2d(3.0) - array([[3.]]) - >>> x = np.arange(3.0) - >>> np.atleast_2d(x) - array([[0., 1., 2.]]) - >>> np.atleast_2d(np.array(1), np.array([1, 2]), np.array([[1, 2]])) - [array([[1.]]), array([[1., 2.]]), array([[1., 2.]])] - """ - pass - -def _np_atleast_3d(*arys): - """ - Convert inputs to arrays with at least three dimension. - - Parameters - ---------- - arys1, arys2, ... : ndarray - One or more input arrays. - - Returns - ------- - ret : ndarray - An array, or list of arrays, each with a.ndim >= 3. - For example, a 1-D array of shape (N,) becomes a view of shape (1, N, 1), - and a 2-D array of shape (M, N) becomes a view of shape (M, N, 1). - - See also - -------- - atleast_1d, atleast_2d - - Examples - -------- - >>> np.atleast_3d(3.0) - array([[[3.]]]) - >>> x = np.arange(3.0) - >>> np.atleast_3d(x).shape - (1, 3, 1) - >>> x = np.arange(12.0).reshape(4,3) - >>> np.atleast_3d(x).shape - (4, 3, 1) - >>> for arr in np.atleast_3d(np.array([1, 2]), np.array([[1, 2]]), np.array([[[1, 2]]])): - ... print(arr, arr.shape) - ... - [[[1.] - [2.]]] (1, 2, 1) - [[[1.] - [2.]]] (1, 2, 1) - [[[1. 2.]]] (1, 1, 2) - """ - pass - - def _np_reshape(a, newshape, order='C', out=None): """ Gives a new shape to an array without changing its data. diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index ff0e48d47664..d633c5ed4b97 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -47,6 +47,7 @@ 'equal', 'not_equal', 'greater', 'less', 'greater_equal', 'less_equal', 'roll', 'rot90', 'einsum', 'true_divide', 'nonzero', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', 'ediff1d', 'resize', 'polyval', 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', + 'atleast_1d', 'atleast_2d', 'atleast_3d', 'where', 'bincount', 'pad', 'cumsum', 'diag', 'diagonal'] @@ -7564,6 +7565,123 @@ def isfinite(x, out=None, **kwargs): return _unary_func_helper(x, _npi.isfinite, _np.isfinite, out=out, **kwargs) +@set_module('mxnet.ndarray.numpy') +def atleast_1d(*arys): + """ + Convert inputs to arrays with at least one dimension. + + Scalar inputs are converted to 1-dimensional arrays, whilst higher-dimensional inputs are preserved. + + Parameters + ---------- + arys1, arys2, ... : ndarray + One or more input arrays. + + Returns + ------- + ret : ndarray + An array, or list of arrays, each with a.ndim >= 1. Copies are made only if necessary. + + See also + -------- + atleast_2d, atleast_3d + + Examples + -------- + >>> np.atleast_1d(1.0) + array([1.]) + >>> x = np.arange(9.0).reshape(3,3) + >>> np.atleast_1d(x) + array([[0., 1., 2.], + [3., 4., 5.], + [6., 7., 8.]]) + >>> np.atleast_1d(np.array(1), np.array([3, 4])) + [array([1.]), array([3., 4.])] + """ + if len(arys) == 1: + return _api_internal.atleast_1d(*arys)[0] + return list(_api_internal.atleast_1d(*arys)) + + +@set_module('mxnet.ndarray.numpy') +def atleast_2d(*arys): + """ + Convert inputs to arrays with at least two dimensions. + + Parameters + ---------- + arys1, arys2, ... : ndarray + One or more input arrays. + + Returns + ------- + ret : ndarray + An array, or list of arrays, each with a.ndim >= 2. Copies are made only if necessary. + + See also + -------- + atleast_1d, atleast_3d + + Examples + -------- + >>> np.atleast_2d(3.0) + array([[3.]]) + >>> x = np.arange(3.0) + >>> np.atleast_2d(x) + array([[0., 1., 2.]]) + >>> np.atleast_2d(np.array(1), np.array([1, 2]), np.array([[1, 2]])) + [array([[1.]]), array([[1., 2.]]), array([[1., 2.]])] + """ + if len(arys) == 1: + return _api_internal.atleast_2d(*arys)[0] + return list(_api_internal.atleast_2d(*arys)) + + +@set_module('mxnet.ndarray.numpy') +def atleast_3d(*arys): + """ + Convert inputs to arrays with at least three dimension. + + Parameters + ---------- + arys1, arys2, ... : ndarray + One or more input arrays. + + Returns + ------- + ret : ndarray + An array, or list of arrays, each with a.ndim >= 3. + For example, a 1-D array of shape (N,) becomes a view of shape (1, N, 1), + and a 2-D array of shape (M, N) becomes a view of shape (M, N, 1). + + See also + -------- + atleast_1d, atleast_2d + + Examples + -------- + >>> np.atleast_3d(3.0) + array([[[3.]]]) + >>> x = np.arange(3.0) + >>> np.atleast_3d(x).shape + (1, 3, 1) + >>> x = np.arange(12.0).reshape(4,3) + >>> np.atleast_3d(x).shape + (4, 3, 1) + >>> for arr in np.atleast_3d(np.array([1, 2]), np.array([[1, 2]]), np.array([[[1, 2]]])): + ... print(arr, arr.shape) + ... + [[[1.] + [2.]]] (1, 2, 1) + [[[1.] + [2.]]] (1, 2, 1) + [[[1. 2.]]] (1, 1, 2) + """ + if len(arys) == 1: + return _api_internal.atleast_3d(*arys)[0] + return list(_api_internal.atleast_3d(*arys)) + + @set_module('mxnet.ndarray.numpy') def where(condition, x=None, y=None): # pylint: disable=too-many-return-statements """where(condition, [x, y]) diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index 281a6f7cc3fc..255a09cafc3c 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -73,6 +73,7 @@ 'greater', 'less', 'greater_equal', 'less_equal', 'roll', 'rot90', 'einsum', 'true_divide', 'nonzero', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', 'ediff1d', 'resize', 'matmul', 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', 'polyval', 'where', 'bincount', + 'atleast_1d', 'atleast_2d', 'atleast_3d', 'pad', 'cumsum', 'diag', 'diagonal'] __all__ += fallback.__all__ @@ -9951,6 +9952,117 @@ def bincount(x, weights=None, minlength=0): return _mx_nd_np.bincount(x, weights=weights, minlength=minlength) +@set_module('mxnet.numpy') +def atleast_1d(*arys): + """ + Convert inputs to arrays with at least one dimension. + + Scalar inputs are converted to 1-dimensional arrays, whilst higher-dimensional inputs are preserved. + + Parameters + ---------- + arys1, arys2, ... : ndarray + One or more input arrays. + + Returns + ------- + ret : ndarray + An array, or list of arrays, each with a.ndim >= 1. Copies are made only if necessary. + + See also + -------- + atleast_2d, atleast_3d + + Examples + -------- + >>> np.atleast_1d(1.0) + array([1.]) + >>> x = np.arange(9.0).reshape(3,3) + >>> np.atleast_1d(x) + array([[0., 1., 2.], + [3., 4., 5.], + [6., 7., 8.]]) + >>> np.atleast_1d(np.array(1), np.array([3, 4])) + [array([1.]), array([3., 4.])] + """ + return _mx_nd_np.atleast_1d(*arys) + + +@set_module('mxnet.numpy') +def atleast_2d(*arys): + """ + Convert inputs to arrays with at least two dimensions. + + Parameters + ---------- + arys1, arys2, ... : ndarray + One or more input arrays. + + Returns + ------- + ret : ndarray + An array, or list of arrays, each with a.ndim >= 2. Copies are made only if necessary. + + See also + -------- + atleast_1d, atleast_3d + + Examples + -------- + >>> np.atleast_2d(3.0) + array([[3.]]) + >>> x = np.arange(3.0) + >>> np.atleast_2d(x) + array([[0., 1., 2.]]) + >>> np.atleast_2d(np.array(1), np.array([1, 2]), np.array([[1, 2]])) + [array([[1.]]), array([[1., 2.]]), array([[1., 2.]])] + """ + return _mx_nd_np.atleast_2d(*arys) + + +@set_module('mxnet.numpy') +def atleast_3d(*arys): + """ + Convert inputs to arrays with at least three dimension. + + Parameters + ---------- + arys1, arys2, ... : ndarray + One or more input arrays. + + Returns + ------- + ret : ndarray + An array, or list of arrays, each with a.ndim >= 3. + For example, a 1-D array of shape (N,) becomes a view of shape (1, N, 1), + and a 2-D array of shape (M, N) becomes a view of shape (M, N, 1). + + See also + -------- + atleast_1d, atleast_2d + + Examples + -------- + >>> np.atleast_3d(3.0) + array([[[3.]]]) + >>> x = np.arange(3.0) + >>> np.atleast_3d(x).shape + (1, 3, 1) + >>> x = np.arange(12.0).reshape(4,3) + >>> np.atleast_3d(x).shape + (4, 3, 1) + >>> for arr in np.atleast_3d(np.array([1, 2]), np.array([[1, 2]]), np.array([[[1, 2]]])): + ... print(arr, arr.shape) + ... + [[[1.] + [2.]]] (1, 2, 1) + [[[1.] + [2.]]] (1, 2, 1) + [[[1. 2.]]] (1, 1, 2) + """ + return _mx_nd_np.atleast_3d(*arys) + + @set_module('mxnet.numpy') def pad(x, pad_width=None, mode="constant", **kwargs): # pylint: disable=too-many-arguments """ diff --git a/python/mxnet/symbol/numpy/_symbol.py b/python/mxnet/symbol/numpy/_symbol.py index a2a4cd9d3584..5293d6c17a98 100644 --- a/python/mxnet/symbol/numpy/_symbol.py +++ b/python/mxnet/symbol/numpy/_symbol.py @@ -53,6 +53,7 @@ 'equal', 'not_equal', 'greater', 'less', 'greater_equal', 'less_equal', 'roll', 'rot90', 'einsum', 'true_divide', 'quantile', 'percentile', 'shares_memory', 'may_share_memory', 'diff', 'ediff1d', 'resize', 'polyval', 'nan_to_num', 'isnan', 'isinf', 'isposinf', 'isneginf', 'isfinite', + 'atleast_1d', 'atleast_2d', 'atleast_3d', 'where', 'bincount', 'pad', 'cumsum', 'diag', 'diagonal'] @@ -6632,6 +6633,76 @@ def isfinite(x, out=None, **kwargs): return _unary_func_helper(x, _npi.isfinite, _np.isfinite, out=out, **kwargs) +@set_module('mxnet.symbol.numpy') +def atleast_1d(*arys): + """ + Convert inputs to arrays with at least one dimension. + + Scalar inputs are converted to 1-dimensional arrays, whilst higher-dimensional inputs are preserved. + + Parameters + ---------- + arys1, arys2, ... : _Symbol + One or more input arrays. + + Returns + ------- + ret : _Symbol + An array, or list of arrays, each with a.ndim >= 1. Copies are made only if necessary. + + See also + -------- + atleast_2d, atleast_3d + """ + return _npi.atleast_1d(*arys) + + +@set_module('mxnet.symbol.numpy') +def atleast_2d(*arys): + """ + Convert inputs to arrays with at least two dimensions. + + Parameters + ---------- + arys1, arys2, ... : _Symbol + One or more input arrays. + + Returns + ------- + ret : _Symbol + An array, or list of arrays, each with a.ndim >= 2. Copies are made only if necessary. + + See also + -------- + atleast_1d, atleast_3d + """ + return _npi.atleast_2d(*arys) + + +@set_module('mxnet.symbol.numpy') +def atleast_3d(*arys): + """ + Convert inputs to arrays with at least three dimension. + + Parameters + ---------- + arys1, arys2, ... : _Symbol + One or more input arrays. + + Returns + ------- + ret : _Symbol + An array, or list of arrays, each with a.ndim >= 3. + For example, a 1-D array of shape (N,) becomes a view of shape (1, N, 1), + and a 2-D array of shape (M, N) becomes a view of shape (M, N, 1). + + See also + -------- + atleast_1d, atleast_2d + """ + return _npi.atleast_3d(*arys) + + @set_module('mxnet.symbol.numpy') def where(condition, x, y): """ diff --git a/src/api/operator/numpy/np_init_op.cc b/src/api/operator/numpy/np_init_op.cc index c339fb505029..147f8a6b2022 100644 --- a/src/api/operator/numpy/np_init_op.cc +++ b/src/api/operator/numpy/np_init_op.cc @@ -120,4 +120,85 @@ MXNET_REGISTER_API("_npi.indices") *ret = ndoutputs[0]; }); +MXNET_REGISTER_API("_npi.atleast_1d") +.set_body([](runtime::MXNetArgs args, runtime::MXNetRetValue* ret) { + using namespace runtime; + const nnvm::Op* op = Op::Get("_npi_atleast_1d"); + nnvm::NodeAttrs attrs; + op::AtleastNDParam param; + int args_size = args.size(); + param.num_args = args_size; + attrs.parsed = param; + attrs.op = op; + SetAttrDict(&attrs); + int num_inputs = args_size; + std::vector inputs_vec(args_size, nullptr); + for (int i = 0; i < args_size; ++i) { + inputs_vec[i] = args[i].operator mxnet::NDArray*(); + } + NDArray** inputs = inputs_vec.data(); + int num_outputs = 0; + auto ndoutputs = Invoke(op, &attrs, num_inputs, inputs, &num_outputs, nullptr); + std::vector ndarray_handles; + ndarray_handles.reserve(num_outputs); + for (int i = 0; i < num_outputs; ++i) { + ndarray_handles.emplace_back(ndoutputs[i]); + } + *ret = ADT(0, ndarray_handles.begin(), ndarray_handles.end()); +}); + +MXNET_REGISTER_API("_npi.atleast_2d") +.set_body([](runtime::MXNetArgs args, runtime::MXNetRetValue* ret) { + using namespace runtime; + const nnvm::Op* op = Op::Get("_npi_atleast_2d"); + nnvm::NodeAttrs attrs; + op::AtleastNDParam param; + int args_size = args.size(); + param.num_args = args_size; + attrs.parsed = param; + attrs.op = op; + SetAttrDict(&attrs); + int num_inputs = args_size; + std::vector inputs_vec(args_size, nullptr); + for (int i = 0; i < args_size; ++i) { + inputs_vec[i] = args[i].operator mxnet::NDArray*(); + } + NDArray** inputs = inputs_vec.data(); + int num_outputs = 0; + auto ndoutputs = Invoke(op, &attrs, num_inputs, inputs, &num_outputs, nullptr); + std::vector ndarray_handles; + ndarray_handles.reserve(num_outputs); + for (int i = 0; i < num_outputs; ++i) { + ndarray_handles.emplace_back(ndoutputs[i]); + } + *ret = ADT(0, ndarray_handles.begin(), ndarray_handles.end()); +}); + +MXNET_REGISTER_API("_npi.atleast_3d") +.set_body([](runtime::MXNetArgs args, runtime::MXNetRetValue* ret) { + using namespace runtime; + const nnvm::Op* op = Op::Get("_npi_atleast_3d"); + nnvm::NodeAttrs attrs; + op::AtleastNDParam param; + int args_size = args.size(); + param.num_args = args_size; + attrs.parsed = param; + attrs.op = op; + SetAttrDict(&attrs); + int num_inputs = args_size; + std::vector inputs_vec(args_size, nullptr); + for (int i = 0; i < args_size; ++i) { + inputs_vec[i] = args[i].operator mxnet::NDArray*(); + } + NDArray** inputs = inputs_vec.data(); + int num_outputs = 0; + auto ndoutputs = Invoke(op, &attrs, num_inputs, inputs, &num_outputs, nullptr); + std::vector ndarray_handles; + ndarray_handles.reserve(num_outputs); + for (int i = 0; i < num_outputs; ++i) { + ndarray_handles.emplace_back(ndoutputs[i]); + } + *ret = ADT(0, ndarray_handles.begin(), ndarray_handles.end()); +}); + } // namespace mxnet diff --git a/src/operator/numpy/np_init_op.cc b/src/operator/numpy/np_init_op.cc index e6073bd2a22d..cffca8f10ba4 100644 --- a/src/operator/numpy/np_init_op.cc +++ b/src/operator/numpy/np_init_op.cc @@ -134,7 +134,7 @@ inline bool AtleastNDShape(const nnvm::NodeAttrs& attrs, } #define NNVM_REGISTER_ATLEAST_ND(N) \ -NNVM_REGISTER_OP(_np_atleast_##N##d) \ +NNVM_REGISTER_OP(_npi_atleast_##N##d) \ .set_attr_parser(ParamParser) \ .set_num_inputs( \ [](const NodeAttrs& attrs) { \ diff --git a/src/operator/numpy/np_init_op.cu b/src/operator/numpy/np_init_op.cu index 886bed61ec66..95e4322f31e7 100644 --- a/src/operator/numpy/np_init_op.cu +++ b/src/operator/numpy/np_init_op.cu @@ -41,13 +41,13 @@ NNVM_REGISTER_OP(_npi_identity) NNVM_REGISTER_OP(_npi_full_like) .set_attr("FCompute", FullLikeOpCompute); -NNVM_REGISTER_OP(_np_atleast_1d) +NNVM_REGISTER_OP(_npi_atleast_1d) .set_attr("FCompute", AtleastNDCompute); -NNVM_REGISTER_OP(_np_atleast_2d) +NNVM_REGISTER_OP(_npi_atleast_2d) .set_attr("FCompute", AtleastNDCompute); -NNVM_REGISTER_OP(_np_atleast_3d) +NNVM_REGISTER_OP(_npi_atleast_3d) .set_attr("FCompute", AtleastNDCompute); NNVM_REGISTER_OP(_npi_arange) diff --git a/src/operator/numpy/np_init_op.h b/src/operator/numpy/np_init_op.h index e92af5fc4544..021989eb5fce 100644 --- a/src/operator/numpy/np_init_op.h +++ b/src/operator/numpy/np_init_op.h @@ -287,6 +287,11 @@ struct AtleastNDParam : dmlc::Parameter { .set_lower_bound(1) .describe("Number of input arrays."); } + void SetAttrDict(std::unordered_map* dict) { + std::ostringstream num_args_s; + num_args_s << num_args; + (*dict)["num_args"] = num_args_s.str(); + } }; template