# Copyright 2020-2021 Huawei Technologies Co., Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================ """array operations, the function docs are adapted from Numpy API.""" from ..common import dtype as mstype from ..ops import operations as P from ..ops import functional as F from ..ops.primitive import constexpr from ..nn import Cell from .utils import _convert_list_tensor_to_tuple_tensor, _expand, _broadcast_to, \ _is_empty from .utils_const import _check_is_int, _check_axes_range, _check_start_normalize, \ _check_is_tensor, _check_is_tuple, _check_is_list, _raise_type_error, _raise_value_error, \ _infer_out_shape, _empty, _promote, _check_same_type, _check_input_tensor # According to official numpy reference, the dimension of a numpy array must be less # than 32 MAX_NUMPY_DIMS = 32 @constexpr def _prepare_shape_for_expand_dims(shape, axes): """ Creates the expanded new shape based on the shape and given axes Args: shape (tuple): the shape of the tensor axes Union(int, tuple(int), list(int)): the axes with dimensions expanded. Returns: new_shape(tuple): the shape with dimensions expanded. """ new_shape = [] shape_idx = 0 new_shape_length = len(shape) # Convert to set if isinstance(axes, int): new_shape_length += 1 if axes >= new_shape_length or axes < -new_shape_length: raise ValueError(f"axis {axes} is out of bounds for tensor of dimension {new_shape_length}") axes = {axes} elif isinstance(axes, (list, tuple)): new_shape_length += len(axes) for axis in axes: if axis >= new_shape_length or axis < -new_shape_length: raise ValueError(f"axis {axis} is out of bounds for tensor of dimension {new_shape_length}") axes = set(axes) else: raise TypeError(f"only int, tuple and list are allowed for axes, but got {type(axes)}") for new_shape_idx in range(new_shape_length): if new_shape_idx in axes or new_shape_idx - new_shape_length in axes: new_shape.append(1) else: new_shape.append(shape[shape_idx]) shape_idx += 1 return tuple(new_shape) def expand_dims(a, axis): """ Expands the shape of a tensor. Inserts a new axis that will appear at the axis position in the expanded tensor shape. Args: a (Tensor): Input tensor array. axis Union[int, list(int), tuple(int)]: Position in the expanded axes where the new axis is placed, Returns: Tensor, view of a tensor with the number of dimensions increased. Raises: TypeError: If input arguments have types not specified above. ValueError: If axis exceeds a.ndim. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> x = np.ones((2,2)) >>> x = np.expand_dims(x,0) >>> print(x.shape) (1, 2, 2) """ if not _check_is_tensor(F.typeof(a)): _raise_type_error("Input is not Tensor.") shape = F.shape(a) # yield expanded shape based on the axes new_shape = _prepare_shape_for_expand_dims(shape, axis) return F.reshape(a, new_shape) def squeeze(a, axis=None): """ Removes single-dimensional entries from the shape of an tensor. Args: a (Tensor): Input tensor array. axis: Union[None, int, list(int), tuple(list)]. Default is None. Returns: Tensor, with all or a subset of the dimensions of length 1 removed. Raises: TypeError: If input arguments have types not specified above. ValueError: If specified axis has shape entry > 1. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> x = np.ones((1,2,2,1)) >>> x = np.squeeze(x) >>> print(x.shape) (2, 2) """ if not _check_is_tensor(F.typeof(a)): _raise_type_error("Input is not Tensor.") return a.squeeze(axis) def transpose(a, axes=None): """ Reverses or permutes the axes of a tensor; returns the modified tensor. Args: a (Tensor): a tensor to be transposed axes (Union[None, tuple, list]): the axes order, if axes is None, transpose the entire tensor. Default is None. Returns: Tensor, the transposed tensor array. Raises: TypeError: If input arguments have types not specified above. ValueError: If the number of axes is not euqal to a.ndim. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> x = np.ones((1,2,3)) >>> x = np.transpose(x) >>> print(x.shape) (3, 2, 1) """ if not _check_is_tensor(F.typeof(a)): _raise_type_error("Input is not Tensor.") return a.transpose(axes) def rollaxis(x, axis, start=0): """ Rolls the specified axis backwards, until it lies in the given position. The positions of the other axes do not change relative to one another. Args: x (Tensor): A Tensor to be transposed. axis (int): The axis to be rolled. start (int): - When start >= 0: - When start <= axis: the axis is rolled back until it lies in this position (start). - When start > axis: the axis is rolled until it lies before this position (start). - When start < 0: the start will be normalized as follows: start ........... Normalized start -(x.ndim+1) raise ValueError -x.ndim 0 ... ... -1 x.ndim-1 0 0 ... ... x.ndim x.ndim x.ndim+1 raise ValueError Returns: Transposed Tensor. Has the same data type as the original tensor x. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Raises: TypeError: If axis or start is not integer, or x is not tensor. ValueError: If axis is not in the range from -ndim to ndim-1 or start is not in the range from -ndim to ndim. Examples: >>> import mindspore.numpy as np >>> x = np.ones((2,3,4)) >>> output = np.rollaxis(x, 0, 2) >>> print(output.shape) (3, 2, 4) """ if not _check_is_tensor(F.typeof(x)): _raise_type_error("Input is not Tensor.") if not _check_is_int(axis): _raise_type_error("integer argument expected, but got ", axis) if not _check_is_int(start): _raise_type_error("integer argument expected, but got ", start) shape = F.shape(x) ndim = F.tuple_len(shape) axis = _check_axes_range(axis, ndim) start = _check_start_normalize(start, ndim) if start - axis >= 0 and start - axis <= 1: return x perm = F.make_range(0, ndim) new_perm = None if start < axis: if axis + 1 < ndim: new_perm = perm[0:start] + perm[axis:axis+1] + \ perm[start:axis] + perm[axis+1:] else: new_perm = perm[0:start] + perm[axis:axis+1] + perm[start:axis] if start > axis: if start < ndim: new_perm = perm[0:axis] + perm[axis+1:start] + \ perm[axis:axis+1] + perm[start:] else: new_perm = perm[0:axis] + perm[axis+1:start] + \ perm[axis:axis+1] return F.transpose(x, new_perm) def swapaxes(x, axis1, axis2): """ Interchanges two axes of a tensor. Args: x (Tensor): A tensor to be transposed. axis1 (int): First axis. axis2 (int): Second axis. Returns: Transposed tensor, has the same data type as the original tensor x. Raises: TypeError: If axis1 or axis2 is not integer, or x is not tensor. ValueError: If axis1 or axis2 is not in the range from -ndim to ndim-1. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> x = np.ones((2,3,4)) >>> output = np.swapaxes(x, 0, 2) >>> print(output.shape) (4,3,2) """ if not _check_is_tensor(F.typeof(x)): _raise_type_error("Input is not Tensor.") return x.swapaxes(axis1, axis2) def reshape(x, new_shape): """ Reshapes a tensor without changing its data. Args: x (Tensor): A tensor to be reshaped. new_shape (Union[int, list(int), tuple(int)]): The new shape should be compatible with the original shape. If the tuple has only one element, the result will be a 1-D tensor of that length. One shape dimension can be -1. In this case, the value is inferred from the length of the tensor and remaining dimensions. Returns: Reshaped Tensor. Has the same data type as the original tensor x. Raises: TypeError: If new_shape is not integer, list or tuple, or x is not tensor. ValueError: If new_shape does not compatible with the original shape. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> x = np.asarray([[-0.1, 0.3, 3.6], [0.4, 0.5, -3.2]]) >>> output = np.reshape(x, (3, 2)) >>> print(output) [[-0.1 0.3] [ 3.6 0.4] [ 0.5 -3.2]] >>> output = np.reshape(x, (3, -1)) >>> print(output) [[-0.1 0.3] [ 3.6 0.4] [ 0.5 -3.2]] >>> output = np.reshape(x, (6, )) >>> print(output) [-0.1 0.3 3.6 0.4 0.5 -3.2] """ if not _check_is_tensor(F.typeof(x)): _raise_type_error("Input is not Tensor.") return x.reshape(new_shape) def ravel(x): """ Returns a contiguous flattened tensor. A 1-D tensor, containing the elements of the input, is returned. Args: x (Tensor): A tensor to be flattened. Returns: Flattened tensor, has the same data type as the original tensor x. Raises: TypeError: If x is not tensor. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> x = np.ones((2,3,4)) >>> output = np.ravel(x) >>> print(output.shape) (24,) """ if not _check_is_tensor(F.typeof(x)): _raise_type_error("Input is not Tensor.") return x.ravel() @constexpr def _move_axes_for_concatenate(arr_shape, axis): """ Moves axis 0 to the desiganated position, while keeps other axes' relative positions unchanged, only used if a single tensor is concatenated. """ original_axes = tuple(range(len(arr_shape))) new_axes = original_axes[1:axis+1] + (0,) + original_axes[axis+1:] new_shape = arr_shape[1:axis+1] + (arr_shape[0] * arr_shape[axis+1],) + \ arr_shape[axis+2:] return new_axes, new_shape def _promote_type_for_concatenate(tuple_of_tensors): """ Checks dtype for all tensors in the tuple. If dtypes are not the same, promote them to the `highest` dtype in the tuple, so that they are ready for the concat operator. Args: tuple_of_tensors(tuple(tensor)): A tuple of tensors Returns: tuple of tensors, with each tensor promoted to ths same dtype. """ need_cast = False final_type = tuple_of_tensors[0].dtype for tensor in tuple_of_tensors: if not _check_same_type(final_type, tensor.dtype): need_cast = True final_type = _promote(final_type, tensor.dtype) if not need_cast: return tuple_of_tensors tuple_of_casted_tensors = () for tensor in tuple_of_tensors: tuple_of_casted_tensors += (tensor.astype(final_type, copy=False),) return tuple_of_casted_tensors def concatenate(arrays, axis=0): """ Joins a sequence of tensors along an existing axis. Args: arrays: Union[Tensor, tuple(Tensor), list(Tensor)], a tensor or a list of tensors to be concatenated. axis (int, optional): The axis along which the tensors will be joined, if axis is None, tensors are flattened before use. Default is 0. Returns: Tensor, a tensor concatenated from a tensor or a list of tensors. Raises: TypeError: If input arguments have types not specified above. ValueError: If specified axis < 0, and exceeds tensor.ndim. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> x1 = np.ones((1,2,3)) >>> x2 = np.ones((1,2,1)) >>> x = np.concatenate((x1, x2), axis=-1) >>> print(x.shape) (1, 2, 4) """ array_type = F.typeof(arrays) if _check_is_tensor(array_type): # if the input is a single tensor # if only one tensor is provided, it is treated as a tuple along the # first dimension. For example, a tensor of shape (3,4,5) will be treated # as: tuple(tensor_1(4,5), tensor_2(4,5), tensor_3(4,5)) if axis is None or axis >= MAX_NUMPY_DIMS: return ravel(arrays) arr_shape = F.shape(arrays) _check_axes_range((axis,), len(arr_shape)) # move axis 0 to the disiganated position, while keep other axes' relative # positions unchanged new_axes, new_shape = _move_axes_for_concatenate(arr_shape, axis) arrays = transpose(arrays, new_axes) arrays = reshape(arrays, new_shape) return arrays flattened_arrays = () if axis is None or axis >= MAX_NUMPY_DIMS: for arr in arrays: flattened_arrays += (ravel(arr),) axis = -1 flattened_arrays = _promote_type_for_concatenate(flattened_arrays) return P.Concat(axis)(flattened_arrays) # convert a list of tensor to a tuple of tensor arrays = _convert_list_tensor_to_tuple_tensor(arrays) arr_shape = F.shape(arrays[0]) _check_axes_range((axis,), len(arr_shape)) # if only one tensor in the tuple/list, return the tensor itself if len(arrays) == 1: return arrays[0] arrays = _promote_type_for_concatenate(arrays) return P.Concat(axis)(arrays) def column_stack(tup): """ Stacks 1-D tensors as columns into a 2-D tensor. 2-D tensors are stacked as-is, like np.hstack. Args: tup (Union[Tensor, tuple, list]): A sequence of 1-D or 2-D tensors. All of them must have the same shape except the axis to be concatenated. Returns: 2-D Tensor, formed by stacking the given tensors. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Raises: TypeError: If tup is not Tensor, list or tuple. ValueError: If tup is empty. Examples: >>> import mindspore.numpy as mnp >>> import numpy as onp >>> from mindspore import Tensor >>> x1 = Tensor(onp.array([1, 2, 3]).astype('int32')) >>> x2 = Tensor(onp.array([4, 5, 6]).astype('int32')) >>> output = mnp.column_stack((x1, x2)) >>> print(output) [[1, 4], [2, 5], [3, 6]] """ if _check_is_tensor(F.typeof(tup)): return tup if not _check_is_list(tup) and not _check_is_tuple(tup): _raise_type_error("Tensor or, list or tuple of tensors are required, but got ", tup) trans_tup = () for tensor in tup: if tensor.ndim < 1: tensor = F.expand_dims(tensor, 0) if tensor.ndim == 1: tensor = F.expand_dims(tensor, 1) trans_tup += (tensor,) if not trans_tup: _raise_value_error("Need at least one tensor to concatenate.") return P.Concat(1)(trans_tup) def vstack(tup): """ Stacks tensors in sequence vertically. This is equivalent to concatenation along the first axis. 1-D tensors should firstly be reshaped to (1, N), and then be concatenated along the first axis. Args: tup (Union[Tensor, tuple, list]): A sequence of 1-D or 2-D tensors. The tensors must have the same shape along all but the first axis. 1-D tensors must have the same shape. Returns: Stacked Tensor, formed by stacking the given tensors. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Raises: TypeError: If tup is not Tensor, list or tuple. ValueError: If tup is empty. Examples: >>> import mindspore.numpy as mnp >>> import numpy as onp >>> from mindspore import Tensor >>> x1 = Tensor(onp.array([1, 2, 3]).astype('int32')) >>> x2 = Tensor(onp.array([4, 5, 6]).astype('int32')) >>> output = mnp.vstack((x1, x2)) >>> print(output) [[1, 2, 3], [4, 5, 6]] """ if _check_is_tensor(F.typeof(tup)): return tup if not _check_is_list(tup) and not _check_is_tuple(tup): _raise_type_error("Tensor or, list or tuple of tensors are required, but got", tup) trans_tup = () for tensor in tup: if tensor.ndim <= 1: tensor = _expand(tensor, 2, 0) trans_tup += (tensor,) if not trans_tup: _raise_value_error("Need at least one tensor to concatenate.") return P.Concat(0)(trans_tup) def hstack(tup): """ Stacks tensors in sequence horizontally. This is equivalent to concatenation along the second axis, except for 1-D tensors where it concatenates along the first axis. Args: tup (Union[Tensor, tuple, list]): A sequence of 1-D or 2-D tensors. The tensors must have the same shape along all but the second axis, except 1-D tensors which can be any length. Returns: Stacked Tensor, formed by stacking the given tensors. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Raises: TypeError: If tup is not Tensor, list or tuple. ValueError: If tup is empty. Examples: >>> import mindspore.numpy as mnp >>> import numpy as onp >>> from mindspore import Tensor >>> x1 = Tensor(onp.array([1, 2, 3]).astype('int32')) >>> x2 = Tensor(onp.array([4, 5, 6]).astype('int32')) >>> output = mnp.hstack((x1, x2)) >>> print(output) [1, 2, 3, 4, 5, 6] """ if _check_is_tensor(F.typeof(tup)): return tup if not _check_is_list(tup) and not _check_is_tuple(tup): _raise_type_error("Tensor or, list or tuple of tensors are required, but got", tup) tuple_of_tensor = () for tensor in tup: if tensor.ndim < 1: tensor = F.expand_dims(tensor, 0) tuple_of_tensor += (tensor,) if not tuple_of_tensor: _raise_value_error("Need at least one tensor to concatenate.") if tuple_of_tensor[0].ndim <= 1: return P.Concat(0)(tuple_of_tensor) return P.Concat(1)(tuple_of_tensor) def dstack(tup): """ Stacks tensors in sequence depth wise (along the third axis). This is equivalent to concatenation along the third axis. 1-D tensors (N,) should be reshaped to (1,N,1). 2-D tensors (M,N) should be reshaped to (M,N,1) before concatenation. Args: tup (Union[Tensor, tuple, list]): A sequence of tensors. The tensors must have the same shape along all but the third axis. 1-D or 2-D tensors must have the same shape. Returns: Stacked Tensor, formed by stacking the given tensors. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Raises: TypeError: If tup is not Tensor, list or tuple. ValueError: If tup is empty. Examples: >>> import mindspore.numpy as mnp >>> import numpy as onp >>> from mindspore import Tensor >>> x1 = Tensor(onp.array([1, 2, 3]).astype('int32')) >>> x2 = Tensor(onp.array([4, 5, 6]).astype('int32')) >>> output = mnp.dstack((x1, x2)) >>> print(output) [[[1, 4], [2, 5], [3, 6]]] """ if _check_is_tensor(F.typeof(tup)): return tup if not _check_is_list(tup) and not _check_is_tuple(tup): _raise_type_error("Tensor or, list or tuple of tensors are required, but got", tup) trans_tup = () for tensor in tup: if tensor.ndim <= 1: tensor = _expand(tensor, 2, 0) if tensor.ndim == 2: tensor = F.expand_dims(tensor, 2) trans_tup += (tensor,) if not trans_tup: _raise_value_error("Need at least one tensor to concatenate.") return P.Concat(2)(trans_tup) def where(condition, x=None, y=None): """ Returns elements chosen from x or y depending on condition. Note: As nonzero is not supported, neither x or y can be None. Args: condition (Tensor): where True, yield x, otherwise yield y. x, y (Tensor): Values from which to choose. x, y and condition need to be broadcastable to some shape. Returns: Tensor or scalar, with elements from x where condition is True, and elements from y elsewhere. Raises: ValueError: if operands cannot be broadcast. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> import mindspore.numpy as np >>> condition = np.full((1, 1, 2), [False, True]) >>> x = np.full((1, 3, 2), 5) >>> y = np.full((2, 1, 1), 7) >>> output = np.where(condition, x, y) >>> print(output) [[[7, 5], [7, 5], [7, 5]], [[7, 5], [7, 5], [7, 5]]] """ # type promotes input tensors dtype1 = F.dtype(x) dtype2 = F.dtype(y) dtype = _promote(dtype1, dtype2) if not _check_same_type(dtype1, dtype): x = F.cast(x, dtype) if not _check_same_type(dtype2, dtype): y = F.cast(y, dtype) is_bool = _check_same_type(dtype1, mstype.bool_) and _check_same_type( dtype2, mstype.bool_) if is_bool: # select does not support bool type for x or y x = F.cast(x, mstype.float32) y = F.cast(y, mstype.float32) # broadcasts input tensors shape_out = _infer_out_shape(F.shape(condition), F.shape(x), F.shape(y)) ndim_out = len(shape_out) if not _check_same_type(F.dtype(condition), mstype.float32): # tiling with bool is not supported on GPU condition = F.cast(condition, mstype.float32) condition = _expand(condition, ndim_out) x = _expand(x, ndim_out) y = _expand(y, ndim_out) condition = _broadcast_to( condition, F.shape(condition), shape_out, ndim_out) x = _broadcast_to(x, F.shape(x), shape_out, ndim_out) y = _broadcast_to(y, F.shape(y), shape_out, ndim_out) if not _check_same_type(F.dtype(condition), mstype.bool_): condition = F.cast(condition, mstype.bool_) res = F.select(condition, x, y) if is_bool: res = F.cast(res, mstype.bool_) return res def _atleast_xd(ndim, arys): """Returns arys with at least ndim.""" for arr in arys: _check_input_tensor(F.typeof(arr)) res = [] for arr in arys: arr = _expand(arr, ndim) res.append(arr) if len(res) == 1: return res[0] return res def atleast_1d(*arys): """ Converts inputs to arrays with at least one dimension. Scalar inputs are converted to 1-dimensional arrays, whilst higher-dimensional inputs are preserved. Note: In graph mode, returns a tuple of tensor instead of a list of tensors. Args: arys1, arys2, … (Tensor): one or more input tensors. Returns: Tensor, or list of tensors, each with a.ndim >= 1. Raises: TypeError: if the input is not a tensor. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> a = np.ones((2, 3)) >>> b = np.ones(()) >>> c = np.ones(5) >>> output = np.atleast_1d(a, b, c) >>> print(output) (Tensor(shape=[2, 3], dtype=Float32, value= [[1.00000000e+000, 1.00000000e+000, 1.00000000e+000], [1.00000000e+000, 1.00000000e+000, 1.00000000e+000]]), Tensor(shape=[1], dtype=Float32, value= [1.00000000e+000]), Tensor(shape=[5], dtype=Float32, value= [1.00000000e+000, 1.00000000e+000, 1.00000000e+000, 1.00000000e+000, 1.00000000e+000])) """ return _atleast_xd(1, arys) def atleast_2d(*arys): """ Views inputs as arrays with at least two dimensions. Note: In graph mode, returns a tuple of tensor instead of a list of tensors. Args: arys1, arys2, … (Tensor): one or more input tensors. Returns: Tensor, or list of tensors, each with a.ndim >= 2. Raises: TypeError: if the input is not a tensor. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> a = np.ones((2, 3)) >>> b = np.ones(()) >>> c = np.ones(5) >>> output = np.atleast_2d(a, b, c) >>> print(output) (Tensor(shape=[2, 3], dtype=Float32, value= [[1.00000000e+000, 1.00000000e+000, 1.00000000e+000], [1.00000000e+000, 1.00000000e+000, 1.00000000e+000]]), Tensor(shape=[1, 1], dtype=Float32, value= [[1.00000000e+000]]), Tensor(shape=[1, 5], dtype=Float32, value= [[1.00000000e+000, 1.00000000e+000, 1.00000000e+000, 1.00000000e+000, 1.00000000e+000]])) """ return _atleast_xd(2, arys) def atleast_3d(*arys): """ Views inputs as arrays with at least three dimensions. Note: In graph mode, returns a tuple of tensor instead of a list of tensors. Args: arys1, arys2, … (Tensor): one or more input tensors. Returns: Tensor, or list of tensors, 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). Raises: TypeError: if the input is not a tensor. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> a = np.ones((2, 3)) >>> b = np.ones(()) >>> c = np.ones(5) >>> output = np.atleast_3d(a, b, c) >>> print(output) (Tensor(shape=[2, 3, 1], dtype=Float32, value= [[[1.00000000e+000], [1.00000000e+000], [1.00000000e+000]], [[1.00000000e+000], [1.00000000e+000], [1.00000000e+000]]]), Tensor(shape=[1, 1, 1], dtype=Float32, value= [[[1.00000000e+000]]]), Tensor(shape=[1, 5, 1], dtype=Float32, value= [[[1.00000000e+000], [1.00000000e+000], [1.00000000e+000], [1.00000000e+000], [1.00000000e+000]]])) """ res = [] for arr in arys: ndim = F.rank(arr) if ndim == 0: arr = F.reshape(arr, (1, 1, 1)) elif ndim == 1: arr = F.reshape(arr, (1, F.size(arr), 1)) elif ndim == 2: arr = F.reshape(arr, F.shape(arr) + (1,)) res.append(arr) if len(res) == 1: return res[0] return res def stack(arrays, axis=0): """ Joins 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. Note: Numpy argument out is not supported. Args: arrays (sequence of Tensor): Each array must have the same shape. axis (int): optional. The axis in the result array along which the input arrays are stacked. Returns: Tensor, The stacked array has one more dimension than the input arrays. Raises: ValueError: if input is not Tensor, tuple, or list. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Examples: >>> arrays = [np.ones((3, 4)) for _ in range(10)] >>> output = np.stack(arrays, axis=0) >>> print(output.shape) (10, 3, 4) >>> output = np.stack(arrays, axis=1) >>> print(output.shape) (3, 10, 4) >>> output = np.stack(arrays, axis=2) >>> print(output.shape) (3, 4, 10) """ arr_type = F.typeof(arrays) if _check_is_tensor(arr_type): shape = F.shape(arrays) ndim = F.rank(arrays) axis = axis % ndim axes = F.make_range(ndim) perm = axes[1:axis+1] + (0,) + axes[axis+1:] if _is_empty(shape): return _empty(mstype.float32, shape[1:axis+1] + (shape[0],) + shape[axis+1:]) return transpose(arrays, perm) if _check_is_tuple(arr_type) or _check_is_list(arr_type): shape = (len(arrays),) + F.shape(arrays[0]) ndim = len(shape) axis = axis % ndim if _is_empty(shape): return _empty(mstype.float32, shape[1:axis+1] + (shape[0],) + shape[axis+1:]) seq = () for arr in arrays: seq += (F.expand_dims(arr, axis),) return concatenate(seq, axis) return _raise_value_error('input arrays must be Tensor, tuple, or list') class UniqueNet(Cell): """The operation `mindspore.ops.Unique` must be wrapped inside a model and executed in graph mode. """ def __init__(self): super(UniqueNet, self).__init__() self.unique = P.Unique() def construct(self, x): return self.unique(x) def unique(x, return_inverse=False): """ Finds the unique elements of a tensor. The input tensor will be flattened first when it has more than one dimension. Note: Numpy arguments `axis`, `return_index` and `return_counts` are not supported. This operator must be executed in graph mode. Args: x (Tensor): The input tensor to be processed. return_inverse (bool): If True, also return the indices of the unique tensor. Default: False. Returns: Tensor or tuple of Tensors. - If `return_inverse` is False, just return the unique tensor. - If `return_inverse` is True, return tuple of tensors. Supported Platforms: ``Ascend`` ``GPU`` ``CPU`` Raises: TypeError: If x is not tensor. Examples: >>> import mindspore.numpy as mnp >>> import numpy as onp >>> from mindspore import context >>> context.set_context(mode=context.GRAPH_MODE) >>> input_x = mnp.asarray(onp.array([1, 2, 2, 2, 3, 4, 5]).astype('int32')) >>> output_x = mnp.unique(input_x) >>> print(output_x) [1, 2, 3, 4, 5] >>> output_x = mnp.unique(input_x, return_inverse=True) >>> print(output_x) (Tensor(shape=[5], dtype=Int32, value= [ 1, 2, 3, 4, 5]), Tensor(shape=[7], dtype=Int32, value= [0, 1, 1, 1, 2, 3, 4])) """ if not _check_is_tensor(F.typeof(x)): _raise_type_error("Tensor is expected, but got", x) if F.tuple_len(F.shape(x)) > 1: x = ravel(x) uniq = UniqueNet() res = uniq(x) if not return_inverse: return res[0] return res