done
This commit is contained in:
		| @ -0,0 +1,86 @@ | ||||
| from timeit import timeit | ||||
|  | ||||
| import numba as nb | ||||
|  | ||||
| import numpy as np | ||||
| from numpy.random import PCG64 | ||||
|  | ||||
| bit_gen = PCG64() | ||||
| next_d = bit_gen.cffi.next_double | ||||
| state_addr = bit_gen.cffi.state_address | ||||
|  | ||||
| def normals(n, state): | ||||
|     out = np.empty(n) | ||||
|     for i in range((n + 1) // 2): | ||||
|         x1 = 2.0 * next_d(state) - 1.0 | ||||
|         x2 = 2.0 * next_d(state) - 1.0 | ||||
|         r2 = x1 * x1 + x2 * x2 | ||||
|         while r2 >= 1.0 or r2 == 0.0: | ||||
|             x1 = 2.0 * next_d(state) - 1.0 | ||||
|             x2 = 2.0 * next_d(state) - 1.0 | ||||
|             r2 = x1 * x1 + x2 * x2 | ||||
|         f = np.sqrt(-2.0 * np.log(r2) / r2) | ||||
|         out[2 * i] = f * x1 | ||||
|         if 2 * i + 1 < n: | ||||
|             out[2 * i + 1] = f * x2 | ||||
|     return out | ||||
|  | ||||
|  | ||||
| # Compile using Numba | ||||
| normalsj = nb.jit(normals, nopython=True) | ||||
| # Must use state address not state with numba | ||||
| n = 10000 | ||||
|  | ||||
| def numbacall(): | ||||
|     return normalsj(n, state_addr) | ||||
|  | ||||
|  | ||||
| rg = np.random.Generator(PCG64()) | ||||
|  | ||||
| def numpycall(): | ||||
|     return rg.normal(size=n) | ||||
|  | ||||
|  | ||||
| # Check that the functions work | ||||
| r1 = numbacall() | ||||
| r2 = numpycall() | ||||
| assert r1.shape == (n,) | ||||
| assert r1.shape == r2.shape | ||||
|  | ||||
| t1 = timeit(numbacall, number=1000) | ||||
| print(f'{t1:.2f} secs for {n} PCG64 (Numba/PCG64) gaussian randoms') | ||||
| t2 = timeit(numpycall, number=1000) | ||||
| print(f'{t2:.2f} secs for {n} PCG64 (NumPy/PCG64) gaussian randoms') | ||||
|  | ||||
| # example 2 | ||||
|  | ||||
| next_u32 = bit_gen.ctypes.next_uint32 | ||||
| ctypes_state = bit_gen.ctypes.state | ||||
|  | ||||
| @nb.jit(nopython=True) | ||||
| def bounded_uint(lb, ub, state): | ||||
|     mask = delta = ub - lb | ||||
|     mask |= mask >> 1 | ||||
|     mask |= mask >> 2 | ||||
|     mask |= mask >> 4 | ||||
|     mask |= mask >> 8 | ||||
|     mask |= mask >> 16 | ||||
|  | ||||
|     val = next_u32(state) & mask | ||||
|     while val > delta: | ||||
|         val = next_u32(state) & mask | ||||
|  | ||||
|     return lb + val | ||||
|  | ||||
|  | ||||
| print(bounded_uint(323, 2394691, ctypes_state.value)) | ||||
|  | ||||
|  | ||||
| @nb.jit(nopython=True) | ||||
| def bounded_uints(lb, ub, n, state): | ||||
|     out = np.empty(n, dtype=np.uint32) | ||||
|     for i in range(n): | ||||
|         out[i] = bounded_uint(lb, ub, state) | ||||
|  | ||||
|  | ||||
| bounded_uints(323, 2394691, 10000000, ctypes_state.value) | ||||
| @ -0,0 +1,67 @@ | ||||
| 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]) | ||||
		Reference in New Issue
	
	Block a user