diff options
author | mattip <matti.picus@gmail.com> | 2019-11-21 13:01:39 -0800 |
---|---|---|
committer | mattip <matti.picus@gmail.com> | 2019-11-21 13:01:39 -0800 |
commit | 29b9d3bc0f49d7eaff6a9649c26c18b7a156b902 (patch) | |
tree | ba66edaac76bc9bbefbf739a6b66e540e4e5ad1d | |
parent | d428127183d46b2fbd99afefa4670642addf8d6e (diff) | |
download | numpy-29b9d3bc0f49d7eaff6a9649c26c18b7a156b902.tar.gz |
TST. API: test using distributions.h via cffi
-rw-r--r-- | numpy/random/_examples/cffi/extending.py | 72 | ||||
-rw-r--r-- | numpy/random/tests/test_extending.py | 12 | ||||
-rw-r--r-- | test_requirements.txt | 2 |
3 files changed, 84 insertions, 2 deletions
diff --git a/numpy/random/_examples/cffi/extending.py b/numpy/random/_examples/cffi/extending.py new file mode 100644 index 000000000..732cbbb1d --- /dev/null +++ b/numpy/random/_examples/cffi/extending.py @@ -0,0 +1,72 @@ +""" +Use cffi to access the underlying C functions from distributions.h +""" +import os +import numpy as np +import cffi +ffi = cffi.FFI() + +inc_dir = os.path.join(np.get_include(), 'numpy') + +# Basic numpy types +ffi.cdef(''' + typedef intptr_t npy_intp; + typedef unsigned char npy_bool; + +''') + +with open(os.path.join(inc_dir, 'random', 'bitgen.h')) as fid: + s = [] + for line in fid: + # massage the include file + if line.strip().startswith('#'): + continue + s.append(line) + ffi.cdef('\n'.join(s)) + +with open(os.path.join(inc_dir, 'random', 'distributions.h')) as fid: + s = [] + in_skip = 0 + for line in fid: + # massage the include file + if line.strip().startswith('#'): + continue + + # skip any inlined function definition + # which starts with 'static NPY_INLINE xxx(...) {' + # and ends with a closing '}' + if line.strip().startswith('static NPY_INLINE'): + in_skip += line.count('{') + continue + elif in_skip > 0: + in_skip += line.count('{') + in_skip -= line.count('}') + continue + + # replace defines with their value or remove them + line = line.replace('DECLDIR', '') + line = line.replace('NPY_INLINE', '') + line = line.replace('RAND_INT_TYPE', 'int64_t') + s.append(line) + ffi.cdef('\n'.join(s)) + +lib = ffi.dlopen(np.random._generator.__file__) + +# Compare the distributions.h random_standard_normal_fill to +# Generator.standard_random +bit_gen = np.random.PCG64() +rng = np.random.Generator(bit_gen) +state = bit_gen.state + +interface = rng.bit_generator.cffi +n = 100 +vals_cffi = ffi.new('double[%d]' % n) +lib.random_standard_normal_fill(interface.bit_generator, n, vals_cffi) + +# reset the state +bit_gen.state = state + +vals = rng.standard_normal(n) + +for i in range(n): + assert vals[i] == vals_cffi[i] diff --git a/numpy/random/tests/test_extending.py b/numpy/random/tests/test_extending.py index 6f0f7a462..607853663 100644 --- a/numpy/random/tests/test_extending.py +++ b/numpy/random/tests/test_extending.py @@ -3,11 +3,15 @@ import pytest import warnings try: + import cffi +except ImportError: + cffi = None + +try: with warnings.catch_warnings(record=True) as w: # numba issue gh-4733 warnings.filterwarnings('always', '', DeprecationWarning) import numba - import cffi except ImportError: numba = None @@ -26,7 +30,11 @@ def test_cython(): sys.argv = argv os.chdir(curdir) -@pytest.mark.skipif(numba is None, reason="requires numba") +@pytest.mark.skipif(numba is None or cffi is None, + reason="requires numba and cffi") def test_numba(): from numpy.random._examples.numba import extending +@pytest.mark.skipif(cffi is None, reason="requires cffi") +def test_cffi(): + from numpy.random._examples.cffi import extending diff --git a/test_requirements.txt b/test_requirements.txt index 8b4807607..7752858c8 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -5,3 +5,5 @@ pytest-cov==2.8.1 pickle5; python_version == '3.7' pickle5; python_version == '3.6' and platform_python_implementation != 'PyPy' nose +# for numpy.random.test.test_extending +cffi |