| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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. |
| |
| """ Hexagon pytest utility functions """ |
| |
| from typing import List, Optional, Union |
| import collections |
| import numpy as np |
| |
| |
| def get_test_id(*test_params, test_param_descs: List[Optional[str]] = None) -> str: |
| """ |
| An opinionated alternative to pytest's default algorithm for generating a |
| test's ID string. Intended to make it easier for human readers to |
| interpret the test IDs. |
| |
| 'test_params': The sequence of pytest parameter values supplied to some unit |
| test. |
| |
| 'test_param_descs': An (optional) means to provide additional text for some/all of the |
| paramuments in 'test_params'. |
| |
| If provided, then len(test_params) must equal len(test_param_descs). |
| Each element test_param_descs that is a non-empty string will be used |
| in some sensible way in this function's returned string. |
| """ |
| |
| assert len(test_params) > 0 |
| |
| if test_param_descs is None: |
| test_param_descs = [None] * len(test_params) |
| else: |
| assert len(test_param_descs) == len(test_params) |
| |
| def get_single_param_chunk(param_val, param_desc: Optional[str]): |
| if isinstance(param_val, list): |
| # Like str(list), but avoid the whitespace padding. |
| val_str = "[" + ",".join(str(x) for x in param_val) + "]" |
| need_prefix_separator = False |
| |
| elif isinstance(param_val, bool): |
| if param_val: |
| val_str = "T" |
| else: |
| val_str = "F" |
| need_prefix_separator = True |
| |
| elif isinstance(param_val, TensorContentConstant): |
| val_str = f"const[{param_val.elem_value}]" |
| need_prefix_separator = True |
| |
| elif isinstance(param_val, TensorContentDtypeMin): |
| val_str = "min" |
| need_prefix_separator = True |
| |
| elif isinstance(param_val, TensorContentDtypeMax): |
| val_str = "max" |
| need_prefix_separator = True |
| |
| elif isinstance(param_val, TensorContentRandom): |
| val_str = "random" |
| need_prefix_separator = True |
| |
| elif isinstance(param_val, TensorContentSequentialCOrder): |
| val_str = f"seqC[start:{param_val.start_value},inc:{param_val.increment}]" |
| need_prefix_separator = True |
| |
| else: |
| val_str = str(param_val) |
| need_prefix_separator = True |
| |
| if param_desc and need_prefix_separator: |
| return f"{param_desc}:{val_str}" |
| elif param_desc and not need_prefix_separator: |
| return f"{param_desc}{val_str}" |
| else: |
| return val_str |
| |
| chunks = [ |
| get_single_param_chunk(param_val, param_desc) |
| for param_val, param_desc in zip(test_params, test_param_descs) |
| ] |
| return "-".join(chunks) |
| |
| |
| def get_multitest_ids( |
| multitest_params_list: List[List], param_descs: Optional[List[Optional[str]]] |
| ) -> List[str]: |
| """ |
| A convenience function for classes that use both 'tvm.testing.parameters' and 'get_test_id'. |
| |
| This function provides a workaround for a specific quirk in Python, where list-comprehension |
| can't necessarily access the value of another class-variable, discused here: |
| https://stackoverflow.com/q/13905741 |
| """ |
| return [ |
| get_test_id(*single_test_param_list, test_param_descs=param_descs) |
| for single_test_param_list in multitest_params_list |
| ] |
| |
| |
| def get_numpy_dtype_info(dtype) -> Union[np.finfo, np.iinfo]: |
| """ |
| Return an appropriate 'np.iinfo' or 'np.finfo' object corresponding to |
| the specified Numpy dtype. |
| |
| 'dtype' must be a value that 'numpy.dtype(...)' can handle. |
| """ |
| np_dtype = np.dtype(dtype) |
| kind = np_dtype.kind |
| |
| if kind == "f": |
| return np.finfo(np_dtype) |
| elif kind == "i": |
| return np.iinfo(np_dtype) |
| else: |
| raise TypeError(f"dtype ({dtype}) must indicate some floating-point or integral data type") |
| |
| |
| TensorContentConstant = collections.namedtuple("TensorContentConstant", ["elem_value"]) |
| TensorContentSequentialCOrder = collections.namedtuple( |
| "TensorContentSequentialCOrder", ["start_value", "increment"] |
| ) |
| TensorContentRandom = collections.namedtuple("TensorContentRandom", []) |
| TensorContentDtypeMin = collections.namedtuple("TensorContentDtypeMin", []) |
| TensorContentDtypeMax = collections.namedtuple("TensorContentDtypeMax", []) |
| |
| |
| def create_populated_numpy_tensor( |
| input_shape: Union[list, tuple], dtype: str, input_tensor_populator |
| ) -> np.ndarray: |
| """ |
| Create a numpy tensor with the specified shape, dtype, and content. |
| """ |
| itp = input_tensor_populator # just for brevity |
| |
| if isinstance(itp, TensorContentConstant): |
| return np.full(tuple(input_shape), itp.elem_value, dtype=dtype) |
| |
| elif isinstance(itp, TensorContentDtypeMin): |
| info = get_numpy_dtype_info(dtype) |
| return np.full(tuple(input_shape), info.min, dtype=dtype) |
| |
| elif isinstance(itp, TensorContentDtypeMax): |
| info = get_numpy_dtype_info(dtype) |
| return np.full(tuple(input_shape), info.max, dtype=dtype) |
| |
| elif isinstance(itp, TensorContentRandom): |
| return np.random.random(input_shape).astype(dtype) |
| |
| elif isinstance(itp, TensorContentSequentialCOrder): |
| a = np.empty(tuple(input_shape), dtype) |
| |
| with np.nditer(a, op_flags=["writeonly"], order="C") as iterator: |
| next_elem_val = itp.start_value |
| for elem in iterator: |
| elem[...] = next_elem_val |
| next_elem_val += itp.increment |
| return a |
| |
| else: |
| raise ValueError(f"Unexpected input_tensor_populator type: {type(itp)}") |