1r"""
2Building the required library in this example requires a source distribution
3of NumPy or clone of the NumPy git repository since distributions.c is not
4included in binary distributions.
5
6On *nix, execute in numpy/random/src/distributions
7
8export ${PYTHON_VERSION}=3.8 # Python version
9export PYTHON_INCLUDE=#path to Python's include folder, usually \
10    ${PYTHON_HOME}/include/python${PYTHON_VERSION}m
11export NUMPY_INCLUDE=#path to numpy's include folder, usually \
12    ${PYTHON_HOME}/lib/python${PYTHON_VERSION}/site-packages/numpy/core/include
13gcc -shared -o libdistributions.so -fPIC distributions.c \
14    -I${NUMPY_INCLUDE} -I${PYTHON_INCLUDE}
15mv libdistributions.so ../../_examples/numba/
16
17On Windows
18
19rem PYTHON_HOME and PYTHON_VERSION are setup dependent, this is an example
20set PYTHON_HOME=c:\Anaconda
21set PYTHON_VERSION=38
22cl.exe /LD .\distributions.c -DDLL_EXPORT \
23    -I%PYTHON_HOME%\lib\site-packages\numpy\core\include \
24    -I%PYTHON_HOME%\include %PYTHON_HOME%\libs\python%PYTHON_VERSION%.lib
25move distributions.dll ../../_examples/numba/
26"""
27import os
28
29import numba as nb
30import numpy as np
31from cffi import FFI
32
33from numpy.random import PCG64
34
35ffi = FFI()
36if os.path.exists('./distributions.dll'):
37    lib = ffi.dlopen('./distributions.dll')
38elif os.path.exists('./libdistributions.so'):
39    lib = ffi.dlopen('./libdistributions.so')
40else:
41    raise RuntimeError('Required DLL/so file was not found.')
42
43ffi.cdef("""
44double random_standard_normal(void *bitgen_state);
45""")
46x = PCG64()
47xffi = x.cffi
48bit_generator = xffi.bit_generator
49
50random_standard_normal = lib.random_standard_normal
51
52
53def normals(n, bit_generator):
54    out = np.empty(n)
55    for i in range(n):
56        out[i] = random_standard_normal(bit_generator)
57    return out
58
59
60normalsj = nb.jit(normals, nopython=True)
61
62# Numba requires a memory address for void *
63# Can also get address from x.ctypes.bit_generator.value
64bit_generator_address = int(ffi.cast('uintptr_t', bit_generator))
65
66norm = normalsj(1000, bit_generator_address)
67print(norm[:12])
68