62fbbf9e创建于 2021年3月20日历史提交
# 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.
# ============================================================================
"""Packaged operations based on MindSpore."""

__all__ = [
    'absolute',
    'arange',
    'argmax',
    'argmin',
    'argsort',
    'assign',
    'intersection',
    'matmul',
    'maximum',
    'minimum',
    'mean',
    'mul',
    'sort',
    'sqrt',
    'squeeze',
    'tile',
    'reshape',
    'zeros',
    'zeros_like',
    'softmax',
    'Tensor',
    'summation'
]

from typing import List, Tuple, Union, Callable

import numpy as np

import mindspore
from mindspore import nn
import mindspore.ops.operations as op

_Axis = Union[int, Tuple[int, ...], List[int]]
_Idx = Union[int, mindspore.Tensor, Tuple[int, ...], Tuple[mindspore.Tensor, ...]]
_Number = Union[int, float, np.int, np.float]
_Shape = Union[int, Tuple[int, ...]]
Tensor = mindspore.Tensor


def absolute(inputs: Tensor) -> Tensor:
    """Get the absolute value of a tensor value."""
    abs_op = op.Abs()
    outputs = abs_op(inputs)
    return outputs


def arange(
        start: _Number,
        end: _Number,
        step: _Number = 1,
        dtype: mindspore.dtype = None) -> Tensor:
    """Get the arange value of tensor."""
    nums = np.arange(start=start, stop=end, step=step, dtype=np.int32)
    nums = mindspore.Tensor(nums, dtype=dtype)
    return nums


def argmax(inputs: Tensor, axis: int = -1, keep_dims: bool = False) -> Tensor:
    """Returns the indices of the maximum values along an axis."""
    inputs_np = inputs.asnumpy()
    outputs = np.argmax(inputs_np, axis=axis)

    if keep_dims:
        outputs = np.expand_dims(outputs, axis=axis)

    return mindspore.Tensor(outputs, mindspore.int32)


def argmin(inputs: Tensor, axis: int = -1, keep_dims: bool = False) -> Tensor:
    """Returns the indices of the minimum values along an axis."""
    inputs_np = inputs.asnumpy()
    outputs = np.argmin(inputs_np, axis=axis)

    if keep_dims:
        outputs = np.expand_dims(outputs, axis=axis)

    return mindspore.Tensor(outputs, mindspore.int32)


def argsort(inputs: Tensor, axis: int = -1, descending: bool = False) -> Tensor:
    """Returns the indices that would sort an array."""
    inputs_np = inputs.asnumpy()
    factor = -1 if descending else 1
    indices_np = np.argsort(factor * inputs_np, axis=axis)
    indices = mindspore.Tensor(indices_np, dtype=mindspore.int32)
    return indices


def assign(inputs: Tensor, idx: _Idx, value: Tensor) -> Tensor:
    """Assign a tensor value to the given tensor and index."""
    inputs_np = inputs.asnumpy()
    if isinstance(idx, Tensor):
        idx = idx.asnumpy()
    value_np = value.asnumpy()
    inputs_np[idx] = value_np
    outputs = mindspore.Tensor(inputs_np)
    return outputs


def intersection(*inputs: Tensor) -> Tensor:
    """Get the intersection value by the given tensor list."""
    outputs_np = np.ones_like(inputs[0])
    for inp in inputs:
        outputs_np &= inp.asnumpy()
    outputs = mindspore.Tensor(outputs_np)
    return outputs


def matmul(inputs_x: Tensor, inputs_y: Tensor) -> Tensor:
    """Multiplies matrix `inputs_x` and matrix `inputs_y`."""
    matmul_op = op.MatMul()
    outputs = matmul_op(inputs_x, inputs_y)
    return outputs


def maximum(inputs: Tensor, axis: _Axis = (), keep_dims: bool = False) -> Tensor:
    """Reduces a dimension of a tensor by the maximum value in this dimension."""
    max_op = op.ReduceMax(keep_dims)
    outputs = max_op(inputs, axis)
    return outputs


def minimum(inputs: Tensor, axis: _Axis = (), keep_dims: bool = False) -> Tensor:
    """Reduces a dimension of a tensor by the minimum value in the dimension."""
    max_op = op.ReduceMin(keep_dims)
    outputs = max_op(inputs, axis)
    return outputs


def mean(inputs: Tensor, axis: _Axis = (), keep_dims: bool = False) -> Tensor:
    """Reduces a dimension of a tensor by averaging all elements in the dimension."""
    mean_op = op.ReduceMean(keep_dims)
    outputs = mean_op(inputs, axis)
    return outputs


def mul(inputs_x: Tensor, inputs_y: Tensor) -> Tensor:
    """
    Multiplies two tensors element-wise.

    Inputs of `input_x` and `input_y` comply with the implicit type conversion rules to make the data types consistent.
    The inputs must be two tensors or one tensor and one scalar.
    When the inputs are two tensors,
    dtypes of them cannot be both bool, and the shapes of them could be broadcast.
    When the inputs are one tensor and one scalar,
    the scalar could only be a constant.

    Inputs:
        - **input_x** (Union[Tensor, Number, bool]) - The first input is a number or
          a bool or a tensor whose data type is number or bool.
        - **input_y** (Union[Tensor, Number, bool]) - The second input is a number or
          a bool when the first input is a tensor or a tensor whose data type is number or bool.

    Outputs:
        Tensor, the shape is the same as the one after broadcasting,
        and the data type is the one with higher precision or higher digits among the two inputs.
    """
    mul_op = op.Mul()
    outputs = mul_op(inputs_x, inputs_y)
    return outputs


def sort(inputs: Tensor, axis: _Axis = -1, descending: bool = False) -> Tensor:
    """Return a sorted copy of an array."""
    inputs_np = inputs.asnumpy()
    outputs_np = np.sort(inputs_np, axis=axis)
    if descending:
        outputs_np = np.flip(outputs_np, axis=axis)
    outputs = mindspore.Tensor(outputs_np)
    return outputs


def squeeze(inputs: Tensor, axis: _Axis = ()):
    """Returns a tensor with the same type but dimensions of 1 are removed based on `axis`."""
    squeeze_op = op.Squeeze(axis)
    outputs = squeeze_op(inputs)
    return outputs


def tile(inputs: Tensor, shape: Tuple[int, ...]) -> Tensor:
    """Replicates a tensor with given multiples times."""
    tile_op = op.Tile()
    outputs = tile_op(inputs, shape)
    return outputs


def reshape(inputs: Tensor, shape: _Shape) -> Tensor:
    """Reshapes input tensor with the same values based on a given shape tuple."""
    if isinstance(shape, int):
        shape = (shape,)
    return op.Reshape()(inputs, shape)


def zeros(shape: _Shape, dtype: mindspore.dtype = None) -> Tensor:
    """Return a new array of given shape and type, filled with zeros."""
    outputs = np.zeros(shape)
    return mindspore.Tensor(outputs, dtype=dtype)


def zeros_like(inputs: Tensor, dtype: mindspore.dtype = None) -> Tensor:
    """Return an array of zeros with the same shape and type as a given array."""
    inputs_np = inputs.asnumpy()
    outputs_np = np.zeros_like(inputs_np)
    outputs = mindspore.Tensor(outputs_np, dtype)
    return outputs


def random(shape: _Shape, dtype: mindspore.dtype = None) -> Tensor:
    """Return random floats in the half-open interval [0.0, 1.0)."""
    outputs_np = np.random.random(shape)
    outputs = mindspore.Tensor(outputs_np, dtype)
    return outputs


def randint(low: int, high: int, shape: _Shape, dtype: mindspore.dtype = mindspore.int8) -> Tensor:
    """Return random integers from `low` (inclusive) to `high` (exclusive)."""
    outputs_np = np.random.randint(low, high, size=shape)
    outputs = mindspore.Tensor(outputs_np, dtype=dtype)
    return outputs


def softmax(axis: int = -1) -> Callable:
    """Softmax activation function."""
    func = nn.Softmax(axis=axis)
    return func


def summation(inputs: Tensor, axis: _Axis = (), keep_dims: bool = False) -> Tensor:
    """Reduces a dimension of a tensor by summing all elements in the dimension."""
    sum_op = op.ReduceSum(keep_dims)
    outputs = sum_op(inputs, axis)
    return outputs


def stack(inputs: List[Tensor], axis: int) -> Tensor:
    """Stacks a list of tensors in specified axis."""
    stack_op = op.Stack(axis)
    outputs = stack_op(inputs)
    return outputs


def sqrt(inputs: Tensor) -> Tensor:
    """Returns square root of a tensor element-wise."""
    sqrt_op = op.Sqrt()
    return sqrt_op(inputs)