Skip to main content
`Ctrl`+`K`

# Extending via Numba and CFFI

    r"""
    Building the required library in this example requires a source distribution
    of NumPy or clone of the NumPy git repository since distributions.c is not
    included in binary distributions.

    On *nix, execute in numpy/random/src/distributions

    export ${PYTHON_VERSION}=3.8 # Python version
    export PYTHON_INCLUDE=#path to Python's include folder, usually \
        ${PYTHON_HOME}/include/python${PYTHON_VERSION}m
    export NUMPY_INCLUDE=#path to numpy's include folder, usually \
        ${PYTHON_HOME}/lib/python${PYTHON_VERSION}/site-packages/numpy/_core/include
    gcc -shared -o libdistributions.so -fPIC distributions.c \
        -I${NUMPY_INCLUDE} -I${PYTHON_INCLUDE}
    mv libdistributions.so ../../_examples/numba/

    On Windows

    rem PYTHON_HOME and PYTHON_VERSION are setup dependent, this is an example
    set PYTHON_HOME=c:\Anaconda
    set PYTHON_VERSION=38
    cl.exe /LD .\distributions.c -DDLL_EXPORT \
        -I%PYTHON_HOME%\lib\site-packages\numpy\_core\include \
        -I%PYTHON_HOME%\include %PYTHON_HOME%\libs\python%PYTHON_VERSION%.lib
    move distributions.dll ../../_examples/numba/
    """
    import os

    import numba as nb
    from cffi import FFI

    import numpy as np
    from numpy.random import PCG64

    ffi = FFI()
    if os.path.exists('./distributions.dll'):
        lib = ffi.dlopen('./distributions.dll')
    elif os.path.exists('./libdistributions.so'):
        lib = ffi.dlopen('./libdistributions.so')
    else:
        raise RuntimeError('Required DLL/so file was not found.')

    ffi.cdef("""
    double random_standard_normal(void *bitgen_state);
    """)
    x = PCG64()
    xffi = x.cffi
    bit_generator = xffi.bit_generator

    random_standard_normal = lib.random_standard_normal

    def normals(n, bit_generator):
        out = np.empty(n)
        for i in range(n):
            out[i] = random_standard_normal(bit_generator)
        return out

    normalsj = nb.jit(normals, nopython=True)

    # Numba requires a memory address for void *
    # Can also get address from x.ctypes.bit_generator.value
    bit_generator_address = int(ffi.cast('uintptr_t', bit_generator))

    norm = normalsj(1000, bit_generator_address)
    print(norm[:12])