|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- #!/usr/bin/env python3
- # coding: utf-8
- # Copyright 2019 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.
-
- """dsl create helping function"""
- import logging
- import math
- import akg
- from akg.utils import format_transform as ft_util
- from akg.utils import validation_check as vc_util
-
- class TensorUtils:
- """Class for creating tensor."""
- CREATE_SCH_ONLY = 'create_sch_only'
-
- @classmethod
- def get_tensor_attrs(cls, tensor):
- """get tensor attrs."""
- tensor_attrs = dict()
- if "attrs" in dir(tensor.op):
- tensor_attrs = dict(tensor.op.attrs.items())
- return tensor_attrs
-
- @classmethod
- def update_tensor_attrs(cls, tensor, attrs):
- """update tensor attrs."""
- tensor_attrs = cls.get_tensor_attrs(tensor)
- tensor_attrs.update(attrs)
- tensor = akg.tvm.compute(tensor.shape,
- lambda *indice: tensor[indice],
- name=tensor.op.name,
- tag=tensor.op.tag,
- attrs=tensor_attrs)
- return tensor
-
- @classmethod
- def is_create_sch_only(cls, tensor):
- tensor_attrs = cls.get_tensor_attrs(tensor)
- if cls.CREATE_SCH_ONLY in tensor_attrs.keys():
- return True
- return False
-
- @classmethod
- def is_output_value(cls, tensor):
- """check output value."""
- return not cls.is_create_sch_only(tensor)
-
- @classmethod
- def inplace_set(cls, input_tensor, output_tensor, buffer_name="data_buf"):
- """inplace set."""
- input_tensor_shape = ft_util.get_shape(input_tensor)
- output_tensor_shape = ft_util.get_shape(output_tensor)
- if not input_tensor_shape == output_tensor_shape:
- raise RuntimeError("Shape of the input_tensor and the output_tensor should be equal, "
- "but got %s and %s" % (input_tensor_shape, output_tensor_shape))
- output_tensor = cls.update_tensor_attrs(output_tensor, {cls.CREATE_SCH_ONLY: 1})
- data_buf = akg.tvm.decl_buffer(input_tensor.shape, input_tensor.dtype, name=buffer_name)
- binds_info = {input_tensor: data_buf, output_tensor: data_buf}
- return output_tensor, binds_info
-
- @classmethod
- def inplace_set_tensors(cls, input_tensors, output_tensors, buffer_names=None):
- """
- inplace set for tensors
-
- Args:
- in_tensors (Union[list, tuple]): Origin input tensors.
- out_tensors (Union[list, tuple]): Origin output tensors.
- buffer_names (Union[list, tuple] or None): Buffer names used to bind.
-
- Return:
- inplace_tensors (list): Output tensors with the inplace info.
- binds_infos (dict): Dictionary that maps the input tensor and the output
- tensor to buffer.
- """
- if not buffer_names:
- buffer_names = ["data_buf_%s" % i for i in range(len(input_tensors))]
- for arg in (input_tensors, output_tensors, buffer_names):
- if not isinstance(arg, (tuple, list)):
- raise RuntimeError("arg must be tuple or list!")
- if len(input_tensors) != len(output_tensors) or len(input_tensors) != len(buffer_names):
- raise RuntimeError("length of the input_tensors, output_tensors and buffer_names must be equal!")
-
- inplace_tensors = []
- binds_infos = dict()
- for input_tensor, output_tensor, buffer_name in zip(input_tensors, output_tensors, buffer_names):
- inplace_tensor, binds_info = cls.inplace_set(input_tensor, output_tensor, buffer_name)
- inplace_tensors.append(inplace_tensor)
- binds_infos.update(binds_info)
- return inplace_tensors, binds_infos
-
-
- def produce_shapes(shape1, shape2):
- """two input shapes produce three output shape."""
- shape1 = list(shape1)
- shape2 = list(shape2)
- flag = 0
- if len(shape1) < len(shape2):
- shape1, shape2 = shape2, shape1
- flag = 1
-
- output_shape_len = len(shape1)
- dec = output_shape_len - len(shape2)
- for i in range(dec):
- shape2 = [1] + shape2
-
- out_shape = []
- for i in range(output_shape_len):
- if (shape1[i] != shape2[i]) and (shape1[i] != 1) and (shape2[i] != 1):
- raise RuntimeError("input shapes not match!")
- if isinstance(shape1[i], int) and isinstance(shape2[i], int) and shape1[i] > shape2[i]:
- out_shape.append(shape1[i])
- else:
- out_shape.append(shape2[i])
-
- if flag == 1:
- shape1, shape2 = shape2, shape1
-
- return shape1, shape2, out_shape
-
-
- def get_reduce_out_shape(in_shape, axis=None, keepdims=False):
- """
- Computes ouput shape in reduction operators.
-
- Args:
- in_shape : input shape
- axis (Union[int, list, tuple]): The reduction axis. Default value is None, in this case,
- all dimensions will be reduced.
- keepdims (bool): If True, retains reduced dimensions with length 1, default value is False.
-
- Returns:
- output shape.
- """
-
- dims = len(in_shape)
- if axis is None:
- axis = list(range(dims))
- if not isinstance(axis, (int, list, tuple)):
- raise ValueError("axis must be of the following type: int, list, tuple.")
- if isinstance(axis, int):
- axis = [axis]
- axis = list(axis)
- for i, axis_val in enumerate(axis):
- if axis_val < 0:
- axis[i] = axis_val + dims
- if axis_val >= dims:
- raise ValueError("axis[{}] is {}, which exceeds max dimension {}".format(i, axis[i], dims))
- remaining_axis = []
- for i in range(dims):
- if i not in axis:
- remaining_axis.append(i)
- out_shape = []
- for i in range(dims):
- if i in remaining_axis:
- out_shape.append(in_shape[i])
- else:
- if keepdims:
- out_shape.append(1)
- if not out_shape:
- out_shape.append(1)
- return out_shape
-
-
- def get_input_pad_shape(shape, dtype):
- """Function for getting input pad shape."""
- pad_unit = ft_util.get_bytes(dtype, allow_none=True)
-
- if pad_unit is None:
- logging.warning("%s is not support in TensorAddPad, the result is not undefined.", dtype)
- return shape
-
- lastdim = int(math.ceil(shape[-1] / pad_unit) * pad_unit)
- pad_shape = [*shape[:-1], '{},{}'.format(shape[-1], lastdim)] if lastdim != shape[-1] else shape
-
- return pad_shape
-
-
- def mul_axis_sum(data, axes, keepdims, name=None, attrs=None):
- """calculate sum one by one."""
- if name is None and attrs is None:
- for axis in axes:
- data = akg.topi.sum(data, axis=axis, keepdims=keepdims)
- else:
- shape = [x.value for x in data.shape]
- for axis in axes[:-1]:
- data = akg.topi.sum(data, axis=axis, keepdims=keepdims)
- l_axis = shape[axes[-1]]
- k = akg.tvm.reduce_axis((0, l_axis), name="k")
- res_shape = [1 if i in axes else shape[i] for i in range(len(shape))]
-
- def sumfunc(*i):
- new_i = list(i)
- new_i[axes[-1]] = k
- return akg.tvm.sum(data(*tuple(new_i)), axis=k)
- if name is None:
- data = akg.tvm.compute(res_shape, sumfunc, attrs=attrs)
- elif attrs is None:
- data = akg.tvm.compute(res_shape, sumfunc, name=name)
- else:
- data = akg.tvm.compute(res_shape, sumfunc, name=name, attrs=attrs)
- return data
-
-
- def update_by_moving_average(hat_z, z, momentum):
- r"""
- Update value with moving average.
-
- Note:
- :math:`\hat{z_{new}} = momentum * \hat{z} + (1-momentum) * z`
- where \f$ \hat{z} \f$ is the estimated statistic and \f$ z \f$ is the new observed value.
-
- Args:
- hat_z (tvm.tensor.Tensor): Tensor of type float16, float32.
- z (tvm.tensor.Tensor): Tensor of type float16, float32.
- momentum (float): must meet '0.0 < momentum < 1.0'.
-
- Returns:
- tvm.tensor.Tensor, updated value.
- """
- run = akg.lang.cce.vmuls(hat_z, momentum)
- now = akg.lang.cce.vmuls(z, (1 - momentum))
- return akg.lang.cce.vadd(run, now)
-
- def cal_pad_shapes_by_strategy(shape, kernel, stride, strategy):
- """
- Calculate the pad size and output shape by padding strategy.
-
- Args:
- shape (Union[list, tuple]): Input shape, a list or tuple of 5 int numbers.
- kernel (Union[list, tuple]): List or tuple of two int numbers for pooling window's size.
- stride (Union[list, tuple]): List or tuple of two int numbers for window's stride.
- strategy (Union[str, list]): A string or list for padding strategy, should be 'VALID',
- 'SAME' or instance of list(including four int numbers, as 'CONSTANTS' strategy).
-
- Returns:
- pad_sizes: Padding sizes(a list of four int numbers: [H_head_pad, H_tail_pad, W_head_pad, W_tail_pad]).
- out_shape: Output tensor's shape(a list of two int numbers: [output_H, output_W]).
- """
-
- pool_shapes = [shape[2], shape[3]]
- out_shape = []
- pad_sizes = []
- contrain_var = False
- for sh in [shape, kernel, stride]:
- for s in sh:
- if not isinstance(s, (int, akg.tvm.expr.IntImm)):
- contrain_var = True
- if isinstance(strategy, str) and strategy.upper() == "VALID":
- for i in range(2):
- out_shape.append(math.ceil((pool_shapes[i] - (kernel[i] - 1)) / stride[i]))
- if out_shape[i] <= 0:
- raise ValueError("With pad mode {0}, the value of the kernel "
- "(or window) size should be less than or "
- "equal to that of the corresponding input "
- "shape!".format(strategy))
-
- pad_sizes += [0, 0] # for h
- pad_sizes += [0, 0] # for w
-
- elif isinstance(strategy, str) and strategy.upper() == "SAME":
- for i in range(2):
- out_shape.append(math.ceil(pool_shapes[i] / stride[i]))
- diff_shape = ((out_shape[i] - 1) * stride[i] + kernel[i]) - pool_shapes[i]
- diff_shape = diff_shape if diff_shape > 0 else 0
- pad_shape = [math.floor(diff_shape / 2), math.ceil(diff_shape / 2)]
- pad_sizes += pad_shape
-
- elif isinstance(strategy, (list, tuple)):
- if len(strategy) != 4:
- raise RuntimeError(
- "When with strateg 'CONSTANTS', strategy should be list or tuple of 4 int numbers but get {}".
- format(strategy))
- vc_util.check_pad('pad', strategy, 4)
- for i in range(2):
- pad_shape = [strategy[i * 2], strategy[i * 2 + 1]]
- if contrain_var:
- out_shape.append(akg.tvm.floordiv((pool_shapes[i] +
- (pad_shape[0] + pad_shape[1]) - kernel[i]), (stride[i])) + 1)
- else:
- out_shape.append(math.floor((pool_shapes[i] +
- (pad_shape[0] + pad_shape[1]) - kernel[i]) / float(stride[i])) + 1)
-
- pad_sizes += pad_shape
- height, width = out_shape
- if (isinstance(height, int) and height <= 0) or (isinstance(width, int) and width <= 0):
- raise ValueError("The height and witdth of calculated output"
- " shape [{}, {}] are invalid. Please check the "
- "input parameters!".format(height, width))
- else:
- raise RuntimeError("Padding strategies only support 'VALID', 'CONSTANTS' or 'SAME', but get {}".
- format(strategy))
-
- return pad_sizes, out_shape
-
- def broadcast_gradient_args(x, y):
- """
- Return the reduction indices for computing gradients of x op y with broadcast.
-
- Args:
- x (Union[list, tuple]): the shape of data input
- y (Union[list, tuple]): the shape of data input
-
- Returns:
- rx (list): the reduction indices for computing gradients of x
- ry (list): the reduction indices for computing gradients of y
- """
- rx = []
- ry = []
- for i, item in enumerate(x):
- if item < y[i]:
- rx.append(i)
- elif item > y[i]:
- ry.append(i)
-
- return rx, ry
-
- def zero_const(dtype):
- return akg.tvm.const(0, dtype)
-
- def one_const(dtype):
- return akg.tvm.const(1, dtype)
-
- def neg_one_const(dtype):
- return akg.tvm.const(-1, dtype)
-
- def half_const(dtype):
- return akg.tvm.const(0.5, dtype)
-
- def pi_const(dtype):
- return akg.tvm.const(3.1415926535897932384626433832795, dtype)
-
- def get_value(val, type):
- if isinstance(val, type) and type in [akg.tvm.expr.IntImm, akg.tvm.expr.FloatImm]:
- return val.value
- return val
|