import os import sys from ctypes import POINTER, c_double, c_float, c_int, c_longlong, cast, pointer from os import path import pytest import numpy as np from numpy._core._multiarray_umath import __cpu_features__ from numpy.testing import assert_array_max_ulp from numpy.testing._private.utils import _glibc_older_than UNARY_UFUNCS = [obj for obj in np._core.umath.__dict__.values() if isinstance(obj, np.ufunc)] UNARY_OBJECT_UFUNCS = [uf for uf in UNARY_UFUNCS if "O->O" in uf.types] # Remove functions that do not support `floats` UNARY_OBJECT_UFUNCS.remove(np.invert) UNARY_OBJECT_UFUNCS.remove(np.bitwise_count) IS_AVX = __cpu_features__.get('AVX512F', False) or \ (__cpu_features__.get('FMA3', False) and __cpu_features__.get('AVX2', False)) IS_AVX512FP16 = __cpu_features__.get('AVX512FP16', False) # only run on linux with AVX, also avoid old glibc (numpy/numpy#20448). runtest = (sys.platform.startswith('linux') and IS_AVX and not _glibc_older_than("2.17")) platform_skip = pytest.mark.skipif(not runtest, reason="avoid testing inconsistent platform " "library implementations") # convert string to hex function taken from: # https://stackoverflow.com/questions/1592158/convert-hex-to-float # def convert(s, datatype="np.float32"): i = int(s, 16) # convert from hex to a Python int if (datatype == "np.float64"): cp = pointer(c_longlong(i)) # make this into a c long long integer fp = cast(cp, POINTER(c_double)) # cast the int pointer to a double pointer else: cp = pointer(c_int(i)) # make this into a c integer fp = cast(cp, POINTER(c_float)) # cast the int pointer to a float pointer return fp.contents.value # dereference the pointer, get the float str_to_float = np.vectorize(convert) class TestAccuracy: @platform_skip def test_validate_transcendentals(self): with np.errstate(all='ignore'): data_dir = path.join(path.dirname(__file__), 'data') files = os.listdir(data_dir) files = list(filter(lambda f: f.endswith('.csv'), files)) for filename in files: filepath = path.join(data_dir, filename) with open(filepath) as fid: file_without_comments = ( r for r in fid if r[0] not in ('$', '#') ) data = np.genfromtxt(file_without_comments, dtype=('|S39', '|S39', '|S39', int), names=('type', 'input', 'output', 'ulperr'), delimiter=',', skip_header=1) npname = path.splitext(filename)[0].split('-')[3] npfunc = getattr(np, npname) for datatype in np.unique(data['type']): data_subset = data[data['type'] == datatype] data_input_str = data_subset['input'].astype(str) data_output_str = data_subset['output'].astype(str) data_type_str = data_subset['type'].astype(str) inval = np.array(str_to_float(data_input_str, data_type_str), dtype=eval(datatype)) outval = np.array(str_to_float(data_output_str, data_type_str), dtype=eval(datatype)) perm = np.random.permutation(len(inval)) inval = inval[perm] outval = outval[perm] maxulperr = data_subset['ulperr'].max() assert_array_max_ulp(npfunc(inval), outval, maxulperr) @pytest.mark.skipif(IS_AVX512FP16, reason="SVML FP16 have slightly higher ULP errors") @pytest.mark.parametrize("ufunc", UNARY_OBJECT_UFUNCS) def test_validate_fp16_transcendentals(self, ufunc): with np.errstate(all='ignore'): arr = np.arange(65536, dtype=np.int16) datafp16 = np.frombuffer(arr.tobytes(), dtype=np.float16) datafp32 = datafp16.astype(np.float32) assert_array_max_ulp(ufunc(datafp16), ufunc(datafp32), maxulp=1, dtype=np.float16) @pytest.mark.skipif(not IS_AVX512FP16, reason="lower ULP only apply for SVML FP16") def test_validate_svml_fp16(self): max_ulp_err = { "arccos": 2.54, "arccosh": 2.09, "arcsin": 3.06, "arcsinh": 1.51, "arctan": 2.61, "arctanh": 1.88, "cbrt": 1.57, "cos": 1.43, "cosh": 1.33, "exp2": 1.33, "exp": 1.27, "expm1": 0.53, "log": 1.80, "log10": 1.27, "log1p": 1.88, "log2": 1.80, "sin": 1.88, "sinh": 2.05, "tan": 2.26, "tanh": 3.00, } with np.errstate(all='ignore'): arr = np.arange(65536, dtype=np.int16) datafp16 = np.frombuffer(arr.tobytes(), dtype=np.float16) datafp32 = datafp16.astype(np.float32) for func in max_ulp_err: ufunc = getattr(np, func) ulp = np.ceil(max_ulp_err[func]) assert_array_max_ulp(ufunc(datafp16), ufunc(datafp32), maxulp=ulp, dtype=np.float16)