├── xtension ├── __init__.py └── foo.pyx ├── rng ├── rng │ ├── tests │ │ ├── __init__.py │ │ ├── data │ │ │ └── __init__.py │ │ ├── test_numpy_mt19937_regressions.py │ │ └── test_direct.py │ ├── rk_state_len.pxi │ ├── .coveragerc │ ├── __init__.py │ ├── aligned_malloc.pxi │ ├── aligned_malloc.c │ ├── cython_overrides.pxd │ ├── src │ │ ├── common │ │ │ ├── binomial.h │ │ │ ├── binomial.pxi │ │ │ ├── entropy.h │ │ │ ├── entropy.c │ │ │ ├── stdint.h │ │ │ └── inttypes.h │ │ ├── xoroshiro128plus │ │ │ ├── xoroshiro128plus.h │ │ │ ├── xoroshiro128plus-test.gen.c │ │ │ ├── xoroshiro128plus.c │ │ │ └── xoroshiro128plus-original.c │ │ └── random-kit │ │ │ ├── random-kit-test-gen.c │ │ │ ├── random-kit.h │ │ │ └── random-kit.c │ ├── binomial.pxd │ ├── interface │ │ └── random-kit │ │ │ ├── random-kit-shim.c │ │ │ └── random-kit-shim.h │ ├── mt19937.pxd │ ├── distributions.pxd │ ├── aligned_malloc.h │ ├── array_fillers.pxi.in │ ├── array_fillers.pxi │ ├── bounded_integers.pxi.in │ ├── performance.py │ └── distributions.h ├── setup.cfg ├── MANIFEST.in ├── licenses │ ├── NUMPY-LICENSE.rst │ ├── RANDOMKIT-LICENSE.txt │ └── PCG-LICENSE.txt ├── LICENSE.md ├── setup.py ├── README.md └── README.rst ├── cython_mcmc ├── cython_mcmc │ ├── __init__.py │ └── mcmc.pyx └── setup.py ├── shell-into-container.sh ├── docker-test-xtension.sh ├── docker-test-xtension.bat ├── setup_test_xtension.py ├── launch-container.bat ├── .gitignore ├── launch-container.sh ├── test-xtension.sh ├── test-setup.ipynb ├── requirements_conda.txt ├── 05-profiling-and-performance.ipynb ├── README.md ├── 11-cython-mcmc-taking-it-to-11.ipynb ├── 07-cythonize-pandas-groupby.ipynb ├── 04-functions.ipynb ├── 00-preliminaries.ipynb ├── 02-cython-comparison.ipynb ├── 01-python-slow.ipynb ├── 03-cython-types.ipynb └── 06-numpy-buffers-fused-types.ipynb /xtension/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rng/rng/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rng/rng/tests/data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cython_mcmc/cython_mcmc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rng/rng/rk_state_len.pxi: -------------------------------------------------------------------------------- 1 | DEF RK_STATE_LEN = 624 2 | -------------------------------------------------------------------------------- /rng/rng/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | plugins = Cython.Coverage 3 | -------------------------------------------------------------------------------- /shell-into-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker exec -it cython-tutorial bash 4 | -------------------------------------------------------------------------------- /xtension/foo.pyx: -------------------------------------------------------------------------------- 1 | 2 | 3 | def multiply_by_pi(int n): 4 | return n * 3.1415926 5 | -------------------------------------------------------------------------------- /docker-test-xtension.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker exec \ 4 | -it \ 5 | cython-tutorial \ 6 | /bin/sh -c "./test-xtension.sh" 7 | -------------------------------------------------------------------------------- /docker-test-xtension.bat: -------------------------------------------------------------------------------- 1 | REM 2017-06-26 Untested; please provide feedback! 2 | 3 | docker exec -it cython-tutorial /bin/sh -c "./test-xtension.sh" 4 | -------------------------------------------------------------------------------- /setup_test_xtension.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from Cython.Build import cythonize 3 | 4 | setup(name='xtension', 5 | ext_modules=cythonize('xtension/*.pyx')) 6 | -------------------------------------------------------------------------------- /launch-container.bat: -------------------------------------------------------------------------------- 1 | REM 2017-06-26 Untested; please provide feedback! 2 | 3 | 4 | docker run -it --rm -p 8888:8888 -v %cd%:/home/jovyan/ --name cython-tutorial jupyter/scipy-notebook 5 | -------------------------------------------------------------------------------- /rng/setup.cfg: -------------------------------------------------------------------------------- 1 | [versioneer] 2 | VCS = git 3 | style = pep440 4 | versionfile_source = srs/_version.py 5 | versionfile_build = srs/_version.py 6 | tag_prefix = 7 | parentdir_prefix = srs- 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info/ 2 | .bash_history 3 | *.so 4 | .cache/ 5 | .ipynb_checkpoints/ 6 | .ipython/ 7 | .local/ 8 | __pycache__/ 9 | build/ 10 | mt19937.c 11 | ng-numpy-randomstate/ 12 | xtension/foo.c 13 | -------------------------------------------------------------------------------- /launch-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker run \ 4 | -it \ 5 | --rm \ 6 | -p 8888:8888 \ 7 | -v `pwd`:/home/jovyan/ \ 8 | --name cython-tutorial \ 9 | jupyter/scipy-notebook:8e15d329f1e9 10 | -------------------------------------------------------------------------------- /rng/rng/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, absolute_import, print_function 2 | 3 | from rng.mt19937 import * 4 | 5 | from ._version import get_versions 6 | __version__ = get_versions()['version'] 7 | del get_versions 8 | -------------------------------------------------------------------------------- /rng/rng/aligned_malloc.pxi: -------------------------------------------------------------------------------- 1 | cdef extern from "aligned_malloc.h": 2 | cdef void *PyArray_realloc_aligned(void *p, size_t n); 3 | cdef void *PyArray_malloc_aligned(size_t n); 4 | cdef void *PyArray_calloc_aligned(size_t n, size_t s); 5 | cdef void PyArray_free_aligned(void *p); 6 | -------------------------------------------------------------------------------- /rng/rng/aligned_malloc.c: -------------------------------------------------------------------------------- 1 | #include "aligned_malloc.h" 2 | 3 | static NPY_INLINE void *PyArray_realloc_aligned(void *p, size_t n); 4 | 5 | static NPY_INLINE void *PyArray_malloc_aligned(size_t n); 6 | 7 | static NPY_INLINE void *PyArray_calloc_aligned(size_t n, size_t s); 8 | 9 | static NPY_INLINE void PyArray_free_aligned(void *p); -------------------------------------------------------------------------------- /rng/rng/cython_overrides.pxd: -------------------------------------------------------------------------------- 1 | cdef extern from "Python.h": 2 | double PyFloat_AsDouble(object ob) except? -1.0 3 | double PyComplex_RealAsDouble(object ob) except? -1.0 4 | double PyComplex_ImagAsDouble(object ob) except? -1.0 5 | long PyInt_AsLong(object ob) except? -1 6 | int PyErr_Occurred() 7 | void PyErr_Clear() 8 | -------------------------------------------------------------------------------- /rng/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include srs *.c *.h *.pxi *.pxd *.pyx *.in 2 | recursive-include srs/tests *.csv 3 | include *.md 4 | exclude srs/entropy.c 5 | exclude srs/dsfmt* 6 | exclude srs/mt19937* 7 | exclude srs/mlfg_1279_861* 8 | exclude srs/pcg32* 9 | exclude srs/pcg64* 10 | exclude srs/xorshift128* 11 | exclude srs/xorshift1024* 12 | 13 | include versioneer.py 14 | include srs/_version.py 15 | -------------------------------------------------------------------------------- /cython_mcmc/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools.extension import Extension 2 | from setuptools import setup 3 | from Cython.Build import cythonize 4 | import numpy 5 | 6 | ext = Extension('cython_mcmc.mcmc', 7 | sources=['cython_mcmc/mcmc.pyx'], 8 | include_dirs=[numpy.get_include(), '../rng/rng']) 9 | 10 | setup(name='cython_mcmc', ext_modules=cythonize([ext], include_path=['../rng'])) 11 | -------------------------------------------------------------------------------- /test-xtension.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | python setup_test_xtension.py build_ext -if 4 | echo "***********************************************************" 5 | python -c "import sys; print('sys.executable:', sys.executable)" 6 | python -c "import cython; print('cython version:', cython.__version__)" 7 | python -c "from xtension import foo; print('xtension module test (31.415926):', foo.multiply_by_pi(10))" 8 | echo "***********************************************************" 9 | -------------------------------------------------------------------------------- /rng/rng/src/common/binomial.h: -------------------------------------------------------------------------------- 1 | typedef struct s_binomial_t 2 | { 3 | int has_binomial; /* !=0: following parameters initialized for binomial */ 4 | double psave; 5 | long nsave; 6 | double r; 7 | double q; 8 | double fm; 9 | long m; 10 | double p1; 11 | double xm; 12 | double xl; 13 | double xr; 14 | double c; 15 | double laml; 16 | double lamr; 17 | double p2; 18 | double p3; 19 | double p4; 20 | } binomial_t; 21 | -------------------------------------------------------------------------------- /rng/rng/binomial.pxd: -------------------------------------------------------------------------------- 1 | cdef extern from "distributions.h": 2 | 3 | cdef struct s_binomial_t: 4 | int has_binomial 5 | double psave 6 | long nsave 7 | double r 8 | double q 9 | double fm 10 | long m 11 | double p1 12 | double xm 13 | double xl 14 | double xr 15 | double c 16 | double laml 17 | double lamr 18 | double p2 19 | double p3 20 | double p4 21 | 22 | ctypedef s_binomial_t binomial_t 23 | -------------------------------------------------------------------------------- /rng/rng/src/common/binomial.pxi: -------------------------------------------------------------------------------- 1 | cdef extern from "distributions.h": 2 | 3 | cdef struct s_binomial_t: 4 | int has_binomial 5 | double psave 6 | long nsave 7 | double r 8 | double q 9 | double fm 10 | long m 11 | double p1 12 | double xm 13 | double xl 14 | double xr 15 | double c 16 | double laml 17 | double lamr 18 | double p2 19 | double p3 20 | double p4 21 | 22 | ctypedef s_binomial_t binomial_t 23 | -------------------------------------------------------------------------------- /rng/rng/interface/random-kit/random-kit-shim.c: -------------------------------------------------------------------------------- 1 | #include "random-kit-shim.h" 2 | 3 | extern inline uint32_t random_uint32(aug_state* state); 4 | 5 | extern inline uint64_t random_uint64(aug_state* state); 6 | 7 | extern inline uint64_t random_raw_values(aug_state* state); 8 | 9 | extern inline double random_double(aug_state* state); 10 | 11 | void set_seed_by_array(aug_state* state, uint32_t *init_key, int key_length) 12 | { 13 | randomkit_init_by_array(state->rng, init_key, key_length); 14 | } 15 | 16 | void set_seed(aug_state* state, uint32_t seed) 17 | { 18 | randomkit_seed(state->rng, seed); 19 | } 20 | 21 | // void entropy_init(aug_state* state) 22 | // { 23 | // uint32_t seeds[1]; 24 | // entropy_fill((void*) seeds, sizeof(seeds)); 25 | // set_seed(state, seeds[0]); 26 | // } 27 | -------------------------------------------------------------------------------- /rng/rng/mt19937.pxd: -------------------------------------------------------------------------------- 1 | from binomial cimport binomial_t 2 | cimport numpy as np 3 | cimport cython 4 | from libc cimport string 5 | from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t, 6 | int8_t, int16_t, int32_t, int64_t, intptr_t) 7 | from libc.stdlib cimport malloc, free 8 | from libc.math cimport sqrt 9 | from cpython cimport Py_INCREF, PyComplex_FromDoubles 10 | from cython_overrides cimport PyFloat_AsDouble, PyInt_AsLong, PyComplex_RealAsDouble, PyComplex_ImagAsDouble 11 | from distributions cimport aug_state 12 | 13 | cdef class RandomState: 14 | 15 | cdef void *rng_loc 16 | cdef binomial_t binomial_info 17 | cdef aug_state rng_state 18 | cdef object lock 19 | cdef object __seed 20 | cdef object __stream 21 | cdef object __version 22 | 23 | cdef double c_standard_normal(self) 24 | cdef double c_random_sample(self) 25 | -------------------------------------------------------------------------------- /rng/rng/distributions.pxd: -------------------------------------------------------------------------------- 1 | from binomial cimport binomial_t 2 | from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t, 3 | int8_t, int16_t, int32_t, int64_t, intptr_t) 4 | include "rk_state_len.pxi" 5 | 6 | cdef extern from "distributions.h": 7 | 8 | cdef struct s_randomkit_state: 9 | uint32_t key[RK_STATE_LEN] 10 | int pos 11 | 12 | ctypedef s_randomkit_state randomkit_state 13 | 14 | cdef struct s_aug_state: 15 | randomkit_state *rng 16 | binomial_t *binomial 17 | 18 | int has_gauss, shift_zig_random_int, has_uint32, has_gauss_float 19 | float gauss_float 20 | double gauss 21 | uint64_t zig_random_int 22 | uint32_t uinteger 23 | 24 | ctypedef s_aug_state aug_state 25 | cdef void set_seed(aug_state* state, uint32_t seed) 26 | cdef void set_seed_by_array(aug_state* state, uint32_t *init_key, int key_length) 27 | 28 | ctypedef randomkit_state rng_t 29 | 30 | -------------------------------------------------------------------------------- /rng/rng/src/xoroshiro128plus/xoroshiro128plus.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef _WIN32 4 | #include "../common/stdint.h" 5 | #define inline __forceinline 6 | #else 7 | #include 8 | #endif 9 | 10 | typedef struct s_xoroshiro128plus_state 11 | { 12 | uint64_t s[2]; 13 | } xoroshiro128plus_state; 14 | 15 | void xoroshiro128plus_jump(xoroshiro128plus_state* state); 16 | 17 | void xoroshiro128plus_seed(xoroshiro128plus_state* state, uint64_t seed); 18 | 19 | void xoroshiro128plus_seed_by_array(xoroshiro128plus_state* state, uint64_t *seed_array, int count); 20 | 21 | void xoroshiro128plus_init_state(xoroshiro128plus_state* state, uint64_t seed, uint64_t inc); 22 | 23 | static inline uint64_t rotl(const uint64_t x, int k) { 24 | return (x << k) | (x >> (64 - k)); 25 | } 26 | 27 | inline uint64_t xoroshiro128plus_next(xoroshiro128plus_state* state) { 28 | const uint64_t s0 = state->s[0]; 29 | uint64_t s1 = state->s[1]; 30 | const uint64_t result = s0 + s1; 31 | 32 | s1 ^= s0; 33 | state->s[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b 34 | state->s[1] = rotl(s1, 36); // c 35 | 36 | return result; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /rng/rng/src/random-kit/random-kit-test-gen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "randomkit.h" 4 | 5 | int main(void) 6 | { 7 | int i; 8 | uint32_t temp, seed = 1UL; 9 | randomkit_state state = {{ 0 }}; 10 | randomkit_seed(&state, seed); 11 | 12 | FILE *fp; 13 | fp = fopen("randomkit-testset-1.csv", "w"); 14 | if(fp == NULL){ 15 | printf("Couldn't open file\n"); 16 | return -1; 17 | } 18 | fprintf(fp, "seed, %" PRIu32 "\n", seed); 19 | for (i=0; i < 1000; i++) 20 | { 21 | temp = randomkit_random(&state); 22 | fprintf(fp, "%d, %" PRIu32 "\n", i, temp); 23 | printf("%d, %" PRIu32 "\n", i, temp); 24 | } 25 | fclose(fp); 26 | 27 | seed = 123456789UL; 28 | randomkit_seed(&state, seed); 29 | fp = fopen("randomkit-testset-2.csv", "w"); 30 | if(fp == NULL){ 31 | printf("Couldn't open file\n"); 32 | return -1; 33 | } 34 | fprintf(fp, "seed, %" PRIu32 "\n", seed); 35 | for (i=0; i < 1000; i++) 36 | { 37 | temp = randomkit_random(&state); 38 | fprintf(fp, "%d, %" PRIu32 "\n", i, temp); 39 | printf("%d, %" PRIu32 "\n", i, temp); 40 | } 41 | fclose(fp); 42 | } -------------------------------------------------------------------------------- /rng/rng/src/xoroshiro128plus/xoroshiro128plus-test.gen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "xoroshiro128plus.h" 4 | 5 | int main(void) 6 | { 7 | int i; 8 | uint64_t temp, seed = 1ULL; 9 | xoroshiro128plus_state state = {{ 0 }}; 10 | xoroshiro128plus_seed(&state, seed); 11 | 12 | FILE *fp; 13 | fp = fopen("xoroshiro128plus-testset-1.csv", "w"); 14 | if(fp == NULL){ 15 | printf("Couldn't open file\n"); 16 | return -1; 17 | } 18 | fprintf(fp, "seed, %" PRIu64 "\n", seed); 19 | for (i=0; i < 1000; i++) 20 | { 21 | temp = xoroshiro128plus_next(&state); 22 | fprintf(fp, "%d, %" PRIu64 "\n", i, temp); 23 | printf("%d, %" PRIu64 "\n", i, temp); 24 | } 25 | fclose(fp); 26 | 27 | seed = 12345678910111ULL; 28 | xoroshiro128plus_seed(&state, seed); 29 | fp = fopen("xoroshiro128plus-testset-2.csv", "w"); 30 | if(fp == NULL){ 31 | printf("Couldn't open file\n"); 32 | return -1; 33 | } 34 | fprintf(fp, "seed, %" PRIu64 "\n", seed); 35 | for (i=0; i < 1000; i++) 36 | { 37 | temp = xoroshiro128plus_next(&state); 38 | fprintf(fp, "%d, %" PRIu64 "\n", i, temp); 39 | printf("%d, %" PRIu64 "\n", i, temp); 40 | } 41 | fclose(fp); 42 | } -------------------------------------------------------------------------------- /rng/rng/src/common/entropy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PCG Random Number Generation for C. 3 | * 4 | * Copyright 2014 Melissa O'Neill 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * For additional information about the PCG random number generation scheme, 19 | * including its license and other licensing options, visit 20 | * 21 | * http://www.pcg-random.org 22 | */ 23 | #include 24 | #ifdef _WIN32 25 | #include "../common/stdint.h" 26 | typedef int bool; 27 | #define false 0 28 | #define true 1 29 | #else 30 | #include 31 | #include 32 | #endif 33 | 34 | extern void entropy_fill(void *dest, size_t size); 35 | 36 | extern bool entropy_getbytes(void* dest, size_t size); 37 | 38 | extern bool entropy_fallback_getbytes(void *dest, size_t size); -------------------------------------------------------------------------------- /rng/rng/src/random-kit/random-kit.h: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_KIT_H 2 | #define RANDOM_KIT_H 3 | 4 | #include 5 | #ifdef _WIN32 6 | #include "../common/stdint.h" 7 | #else 8 | #include 9 | #endif 10 | 11 | #ifdef _WIN32 12 | #define inline __forceinline 13 | #endif 14 | 15 | 16 | #define RK_STATE_LEN 624 17 | 18 | #define N 624 19 | #define M 397 20 | #define MATRIX_A 0x9908b0dfUL 21 | #define UPPER_MASK 0x80000000UL 22 | #define LOWER_MASK 0x7fffffffUL 23 | 24 | typedef struct s_randomkit_state 25 | { 26 | uint32_t key[RK_STATE_LEN]; 27 | int pos; 28 | } 29 | randomkit_state; 30 | 31 | extern void randomkit_seed(randomkit_state *state, uint32_t seed); 32 | 33 | extern void randomkit_gen(randomkit_state *state); 34 | 35 | /* Slightly optimized reference implementation of the Mersenne Twister */ 36 | static inline uint32_t randomkit_random(randomkit_state *state) 37 | { 38 | uint32_t y; 39 | 40 | if (state->pos == RK_STATE_LEN) { 41 | // Move to function to help inlining 42 | randomkit_gen(state); 43 | } 44 | y = state->key[state->pos++]; 45 | 46 | /* Tempering */ 47 | y ^= (y >> 11); 48 | y ^= (y << 7) & 0x9d2c5680UL; 49 | y ^= (y << 15) & 0xefc60000UL; 50 | y ^= (y >> 18); 51 | 52 | return y; 53 | } 54 | 55 | extern void randomkit_init_by_array(randomkit_state *state, uint32_t *init_key, int key_length); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /rng/rng/aligned_malloc.h: -------------------------------------------------------------------------------- 1 | #include "Python.h" 2 | #include "numpy/npy_common.h" 3 | 4 | #define NPY_MEMALIGN 16 /* 16 for SSE2, 32 for AVX, 64 for Xeon Phi */ 5 | 6 | static NPY_INLINE 7 | void *PyArray_realloc_aligned(void *p, size_t n) 8 | { 9 | void *p1, **p2, *base; 10 | size_t old_offs, offs = NPY_MEMALIGN - 1 + sizeof(void*); 11 | if (NPY_UNLIKELY(p != NULL)) { 12 | base = *(((void**)p)-1); 13 | if (NPY_UNLIKELY((p1 = PyMem_Realloc(base,n+offs)) == NULL)) return NULL; 14 | if (NPY_LIKELY(p1 == base)) return p; 15 | p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1)); 16 | old_offs = (size_t)((Py_uintptr_t)p - (Py_uintptr_t)base); 17 | memmove((void*)p2,((char*)p1)+old_offs,n); 18 | } else { 19 | if (NPY_UNLIKELY((p1 = PyMem_Malloc(n + offs)) == NULL)) return NULL; 20 | p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1)); 21 | } 22 | *(p2-1) = p1; 23 | return (void*)p2; 24 | } 25 | 26 | static NPY_INLINE 27 | void *PyArray_malloc_aligned(size_t n) 28 | { 29 | return PyArray_realloc_aligned(NULL, n); 30 | } 31 | 32 | static NPY_INLINE 33 | void *PyArray_calloc_aligned(size_t n, size_t s) 34 | { 35 | void *p; 36 | if (NPY_UNLIKELY((p = PyArray_realloc_aligned(NULL,n*s)) == NULL)) return NULL; 37 | memset(p, 0, n*s); 38 | return p; 39 | } 40 | 41 | static NPY_INLINE 42 | void PyArray_free_aligned(void *p) 43 | { 44 | void *base = *(((void**)p)-1); 45 | PyMem_Free(base); 46 | } -------------------------------------------------------------------------------- /rng/rng/interface/random-kit/random-kit-shim.h: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_KIT_SHIM_H 2 | #define RANDOM_KIT_SHIM_H 3 | 4 | #ifdef _WIN32 5 | #include "../../src/common/stdint.h" 6 | #define inline __forceinline 7 | #else 8 | #include 9 | #endif 10 | 11 | #include "../../src/common/binomial.h" 12 | #include "../../src/common/entropy.h" 13 | #include "../../src/random-kit/random-kit.h" 14 | 15 | typedef struct s_aug_state { 16 | randomkit_state *rng; 17 | binomial_t *binomial; 18 | 19 | int has_gauss, has_gauss_float, shift_zig_random_int, has_uint32; 20 | float gauss_float; 21 | double gauss; 22 | uint32_t uinteger; 23 | uint64_t zig_random_int; 24 | } aug_state; 25 | 26 | static inline uint32_t random_uint32(aug_state* state) 27 | { 28 | return randomkit_random(state->rng); 29 | } 30 | 31 | static inline uint64_t random_uint64(aug_state* state) 32 | { 33 | return (((uint64_t) randomkit_random(state->rng)) << 32) | randomkit_random(state->rng); 34 | } 35 | 36 | static inline double random_double(aug_state* state) 37 | { 38 | int32_t a = random_uint32(state) >> 5, b = random_uint32(state) >> 6; 39 | return (a * 67108864.0 + b) / 9007199254740992.0; 40 | } 41 | 42 | static inline uint64_t random_raw_values(aug_state* state) 43 | { 44 | return (uint64_t)random_uint32(state); 45 | } 46 | 47 | extern void entropy_init(aug_state* state); 48 | 49 | extern void set_seed_by_array(aug_state* state, uint32_t *init_key, int key_length); 50 | 51 | extern void set_seed(aug_state* state, uint32_t seed); 52 | 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /rng/licenses/NUMPY-LICENSE.rst: -------------------------------------------------------------------------------- 1 | ************* 2 | NumPy License 3 | ************* 4 | 5 | Copyright (c) 2005, NumPy Developers 6 | 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are 11 | met: 12 | 13 | * Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | 16 | * Redistributions in binary form must reproduce the above 17 | copyright notice, this list of conditions and the following 18 | disclaimer in the documentation and/or other materials provided 19 | with the distribution. 20 | 21 | * Neither the name of the NumPy Developers nor the names of any 22 | contributors may be used to endorse or promote products derived 23 | from this software without specific prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | -------------------------------------------------------------------------------- /rng/LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (c) 2016 Kevin Sheppard 4 | All rights reserved. 5 | 6 | Developed by: Kevin Sheppard 7 | https://github.com/bashtage 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a 10 | copy of this software and associated documentation files (the "Software"), to 11 | deal with the Software without restriction, including without limitation the 12 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | sell copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimers. 18 | 19 | Redistributions in binary form must reproduce the above copyright notice, 20 | this list of conditions and the following disclaimers in the documentation 21 | and/or other materials provided with the distribution. 22 | Neither the names of Kevin Sheppard, 23 | nor the names of its contributors may be used to endorse or promote 24 | products derived from this Software without specific prior written 25 | permission. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH 33 | THE SOFTWARE. -------------------------------------------------------------------------------- /rng/rng/src/xoroshiro128plus/xoroshiro128plus.c: -------------------------------------------------------------------------------- 1 | #include "xoroshiro128plus.h" 2 | #include "../splitmix64/splitmix64.h" 3 | 4 | extern inline uint64_t xoroshiro128plus_next(xoroshiro128plus_state* state); 5 | 6 | static inline uint64_t rotl(const uint64_t x, int k); 7 | 8 | void xoroshiro128plus_jump(xoroshiro128plus_state* state) { 9 | static const uint64_t JUMP[] = { 0xbeac0467eba5facb, 0xd86b048b86aa9922 }; 10 | 11 | int i, b; 12 | uint64_t s0 = 0; 13 | uint64_t s1 = 0; 14 | for(i = 0; i < sizeof JUMP / sizeof *JUMP; i++) 15 | for(b = 0; b < 64; b++) { 16 | if (JUMP[i] & 1ULL << b) { 17 | s0 ^= state->s[0]; 18 | s1 ^= state->s[1]; 19 | } 20 | xoroshiro128plus_next(state); 21 | } 22 | 23 | state->s[0] = s0; 24 | state->s[1] = s1; 25 | } 26 | 27 | void xoroshiro128plus_seed(xoroshiro128plus_state* state, uint64_t seed) 28 | { 29 | 30 | uint64_t seed_copy = seed; 31 | uint64_t state1 = splitmix64_next(&seed_copy); 32 | uint64_t state2 = splitmix64_next(&seed_copy); 33 | xoroshiro128plus_init_state(state, state1, state2); 34 | } 35 | 36 | void xoroshiro128plus_seed_by_array(xoroshiro128plus_state* state, uint64_t *seed_array, int count) 37 | { 38 | uint64_t initial_state[2] = {0}; 39 | uint64_t seed_copy = 0; 40 | int iter_bound = 2>=count ? 2 : count; 41 | int i, loc = 0; 42 | for (i = 0; i < iter_bound; i++) 43 | { 44 | if (i < count) 45 | seed_copy ^= seed_array[i]; 46 | initial_state[loc] = splitmix64_next(&seed_copy); 47 | loc ++; 48 | if (loc == 2) 49 | loc = 0; 50 | } 51 | xoroshiro128plus_init_state(state, initial_state[0], initial_state[1]); 52 | } 53 | 54 | void xoroshiro128plus_init_state(xoroshiro128plus_state* state, uint64_t seed, uint64_t inc) 55 | { 56 | state->s[0] = seed; 57 | state->s[1] = inc; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /test-setup.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%load_ext Cython" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "def pure_python():\n", 23 | " # yes, this is cheating a little. Thank Gauss.\n", 24 | " n = 10**6\n", 25 | " return (n - 1) * n / 2" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "%%cython\n", 35 | "\n", 36 | "from cython.parallel cimport prange\n", 37 | "\n", 38 | "def try_prange():\n", 39 | " cdef int i, n=10**6\n", 40 | " cdef long ret = 0\n", 41 | " for i in prange(n, nogil=True):\n", 42 | " ret += i\n", 43 | " return ret\n", 44 | "\n", 45 | "def try_range():\n", 46 | " cdef int i, n=10**6\n", 47 | " cdef long ret = 0\n", 48 | " for i in range(n):\n", 49 | " ret += i\n", 50 | " return ret\n", 51 | "\n", 52 | "def try_untyped():\n", 53 | " n = 10**6\n", 54 | " ret = 0\n", 55 | " for i in range(n):\n", 56 | " ret += i\n", 57 | " return ret" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "assert try_prange() == try_range() == try_untyped() == pure_python()" 67 | ] 68 | } 69 | ], 70 | "metadata": { 71 | "kernelspec": { 72 | "display_name": "Python 3", 73 | "language": "python", 74 | "name": "python3" 75 | }, 76 | "language_info": { 77 | "codemirror_mode": { 78 | "name": "ipython", 79 | "version": 3 80 | }, 81 | "file_extension": ".py", 82 | "mimetype": "text/x-python", 83 | "name": "python", 84 | "nbconvert_exporter": "python", 85 | "pygments_lexer": "ipython3", 86 | "version": "3.5.3" 87 | } 88 | }, 89 | "nbformat": 4, 90 | "nbformat_minor": 2 91 | } 92 | -------------------------------------------------------------------------------- /rng/rng/array_fillers.pxi.in: -------------------------------------------------------------------------------- 1 | ctypedef void (* random_float_fill)(aug_state* state, np.npy_intp count, float *out) nogil 2 | ctypedef void (* random_double_fill)(aug_state* state, np.npy_intp count, double *out) nogil 3 | 4 | {{ 5 | py: 6 | ctypes = (('float64', 'double'), 7 | ('float32', 'float')) 8 | }} 9 | {{for nptype, ctype in ctypes}} 10 | 11 | 12 | cdef object {{ctype}}_fill(aug_state* state, void *func, object size, object lock, object out): 13 | cdef random_{{ctype}}_fill f = func 14 | cdef {{ctype}} fout 15 | cdef {{ctype}} *out_array_data 16 | cdef np.ndarray out_array 17 | cdef np.npy_intp n 18 | 19 | if size is None and out is None: 20 | with lock: 21 | f(state, 1, &fout) 22 | return fout 23 | 24 | if out is not None: 25 | check_output(out, np.{{nptype}}, size) 26 | out_array = out 27 | else: 28 | out_array = np.empty(size, np.{{nptype}}) 29 | 30 | n = np.PyArray_SIZE(out_array) 31 | out_array_data = <{{ctype}} *>np.PyArray_DATA(out_array) 32 | with lock, nogil: 33 | f(state, n, out_array_data) 34 | return out_array 35 | 36 | {{endfor}} 37 | 38 | 39 | cdef object float_fill_from_double(aug_state* state, void *func, object size, object lock): 40 | cdef random_double_fill f = func 41 | cdef double out 42 | cdef double* buffer 43 | cdef float *out_array_data 44 | cdef np.ndarray out_array 45 | cdef np.npy_intp i, n, buffer_size, remaining, total, fetch 46 | 47 | if size is None: 48 | with lock: 49 | f(state, 1, &out) 50 | return out 51 | else: 52 | out_array = np.empty(size, np.float32) 53 | n = np.PyArray_SIZE(out_array) 54 | buffer_size = 1000 if n > 1000 else n 55 | total = 0 56 | remaining = n 57 | buffer = malloc(buffer_size * sizeof(double)) 58 | 59 | out_array_data = np.PyArray_DATA(out_array) 60 | while remaining > 0: 61 | fetch = 1000 if remaining > 1000 else remaining 62 | with lock, nogil: 63 | f(state, fetch, buffer) 64 | for i in range(fetch): 65 | out_array_data[i + total] = buffer[i] 66 | total += fetch 67 | remaining -= fetch 68 | 69 | free(buffer) 70 | 71 | return out_array 72 | -------------------------------------------------------------------------------- /rng/rng/src/xoroshiro128plus/xoroshiro128plus-original.c: -------------------------------------------------------------------------------- 1 | /* Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org) 2 | 3 | To the extent possible under law, the author has dedicated all copyright 4 | and related and neighboring rights to this software to the public domain 5 | worldwide. This software is distributed without any warranty. 6 | 7 | See . */ 8 | 9 | #include 10 | 11 | /* This is the successor to xorshift128+. It is the fastest full-period 12 | generator passing BigCrush without systematic failures, but due to the 13 | relatively short period it is acceptable only for applications with a 14 | mild amount of parallelism; otherwise, use a xorshift1024* generator. 15 | 16 | Beside passing BigCrush, this generator passes the PractRand test suite 17 | up to (and included) 16TB, with the exception of binary rank tests, 18 | which fail due to the lowest bit being an LFSR; all other bits pass all 19 | tests. We suggest to use a sign test to extract a random Boolean value. 20 | 21 | Note that the generator uses a simulated rotate operation, which most C 22 | compilers will turn into a single instruction. In Java, you can use 23 | Long.rotateLeft(). In languages that do not make low-level rotation 24 | instructions accessible xorshift128+ could be faster. 25 | 26 | The state must be seeded so that it is not everywhere zero. If you have 27 | a 64-bit seed, we suggest to seed a splitmix64 generator and use its 28 | output to fill s. */ 29 | 30 | uint64_t s[2]; 31 | 32 | static inline uint64_t rotl(const uint64_t x, int k) { 33 | return (x << k) | (x >> (64 - k)); 34 | } 35 | 36 | uint64_t next(void) { 37 | const uint64_t s0 = s[0]; 38 | uint64_t s1 = s[1]; 39 | const uint64_t result = s0 + s1; 40 | 41 | s1 ^= s0; 42 | s[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b 43 | s[1] = rotl(s1, 36); // c 44 | 45 | return result; 46 | } 47 | 48 | 49 | /* This is the jump function for the generator. It is equivalent 50 | to 2^64 calls to next(); it can be used to generate 2^64 51 | non-overlapping subsequences for parallel computations. */ 52 | 53 | void jump(void) { 54 | static const uint64_t JUMP[] = { 0xbeac0467eba5facb, 0xd86b048b86aa9922 }; 55 | 56 | uint64_t s0 = 0; 57 | uint64_t s1 = 0; 58 | for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++) 59 | for(int b = 0; b < 64; b++) { 60 | if (JUMP[i] & 1ULL << b) { 61 | s0 ^= s[0]; 62 | s1 ^= s[1]; 63 | } 64 | next(); 65 | } 66 | 67 | s[0] = s0; 68 | s[1] = s1; 69 | } 70 | -------------------------------------------------------------------------------- /rng/rng/array_fillers.pxi: -------------------------------------------------------------------------------- 1 | ctypedef void (* random_float_fill)(aug_state* state, np.npy_intp count, float *out) nogil 2 | ctypedef void (* random_double_fill)(aug_state* state, np.npy_intp count, double *out) nogil 3 | 4 | 5 | 6 | 7 | cdef object double_fill(aug_state* state, void *func, object size, object lock, object out): 8 | cdef random_double_fill f = func 9 | cdef double fout 10 | cdef double *out_array_data 11 | cdef np.ndarray out_array 12 | cdef np.npy_intp n 13 | 14 | if size is None and out is None: 15 | with lock: 16 | f(state, 1, &fout) 17 | return fout 18 | 19 | if out is not None: 20 | check_output(out, np.float64, size) 21 | out_array = out 22 | else: 23 | out_array = np.empty(size, np.float64) 24 | 25 | n = np.PyArray_SIZE(out_array) 26 | out_array_data = np.PyArray_DATA(out_array) 27 | with lock, nogil: 28 | f(state, n, out_array_data) 29 | return out_array 30 | 31 | 32 | cdef object float_fill(aug_state* state, void *func, object size, object lock, object out): 33 | cdef random_float_fill f = func 34 | cdef float fout 35 | cdef float *out_array_data 36 | cdef np.ndarray out_array 37 | cdef np.npy_intp n 38 | 39 | if size is None and out is None: 40 | with lock: 41 | f(state, 1, &fout) 42 | return fout 43 | 44 | if out is not None: 45 | check_output(out, np.float32, size) 46 | out_array = out 47 | else: 48 | out_array = np.empty(size, np.float32) 49 | 50 | n = np.PyArray_SIZE(out_array) 51 | out_array_data = np.PyArray_DATA(out_array) 52 | with lock, nogil: 53 | f(state, n, out_array_data) 54 | return out_array 55 | 56 | 57 | cdef object float_fill_from_double(aug_state* state, void *func, object size, object lock): 58 | cdef random_double_fill f = func 59 | cdef double out 60 | cdef double* buffer 61 | cdef float *out_array_data 62 | cdef np.ndarray out_array 63 | cdef np.npy_intp i, n, buffer_size, remaining, total, fetch 64 | 65 | if size is None: 66 | with lock: 67 | f(state, 1, &out) 68 | return out 69 | else: 70 | out_array = np.empty(size, np.float32) 71 | n = np.PyArray_SIZE(out_array) 72 | buffer_size = 1000 if n > 1000 else n 73 | total = 0 74 | remaining = n 75 | buffer = malloc(buffer_size * sizeof(double)) 76 | 77 | out_array_data = np.PyArray_DATA(out_array) 78 | while remaining > 0: 79 | fetch = 1000 if remaining > 1000 else remaining 80 | with lock, nogil: 81 | f(state, fetch, buffer) 82 | for i in range(fetch): 83 | out_array_data[i + total] = buffer[i] 84 | total += fetch 85 | remaining -= fetch 86 | 87 | free(buffer) 88 | 89 | return out_array 90 | -------------------------------------------------------------------------------- /requirements_conda.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: osx-64 4 | appnope=0.1.0=py35_0 5 | beautifulsoup4=4.5.3=py35_0 6 | bkcharts=0.2=py35_0 7 | blas=1.1=openblas 8 | bleach=1.5.0=py35_0 9 | bokeh=0.12.6=py35_0 10 | ca-certificates=2017.4.17=0 11 | certifi=2017.4.17=py35_0 12 | chardet=3.0.2=py35_1 13 | click=6.7=py35_0 14 | cloudpickle=0.2.2=py35_4 15 | cycler=0.10.0=py35_0 16 | cython=0.25.2=py35_1 17 | dask=0.15.0=py35_0 18 | decorator=4.0.11=py35_0 19 | dill=0.2.6=py35_0 20 | distributed=1.17.1=py35_0 21 | entrypoints=0.2.3=py35_1 22 | fastcache=1.0.2=py35_0 23 | freetype=2.7=1 24 | h5py=2.6.0=np112py35_7 25 | hdf5=1.8.17=11 26 | heapdict=1.0.0=py35_0 27 | html5lib=0.999=py35_0 28 | idna=2.5=py35_0 29 | ipykernel=4.6.1=py35_0 30 | ipython=6.1.0=py35_0 31 | ipython_genutils=0.2.0=py35_0 32 | ipywidgets=6.0.0=py35_0 33 | jedi=0.10.0=py35_0 34 | jinja2=2.9.5=py35_0 35 | jpeg=9b=0 36 | jsonschema=2.5.1=py35_0 37 | jupyter_client=5.1.0=py35_0 38 | jupyter_core=4.3.0=py35_0 39 | libgfortran=3.0.0=0 40 | libpng=1.6.28=0 41 | libsodium=1.0.10=0 42 | libtiff=4.0.6=7 43 | llvmlite=0.16.0=py35_0 44 | locket=0.2.0=py35_1 45 | markupsafe=1.0=py35_0 46 | matplotlib=2.0.2=np112py35_0 47 | mistune=0.7.4=py35_0 48 | mpmath=0.19=py35_2 49 | msgpack-python=0.4.8=py35_0 50 | nbconvert=5.2.1=py35_1 51 | nbformat=4.3.0=py35_0 52 | ncurses=5.9=10 53 | networkx=1.11=py35_0 54 | nomkl=1.0=0 55 | notebook=5.0.0=py35_0 56 | numba=0.31.0=np112py35_0 57 | numexpr=2.6.2=np112py35_0 58 | numpy=1.12.1=py35_blas_openblas_200 59 | olefile=0.44=py35_0 60 | openblas=0.2.19=2 61 | openssl=1.0.2k=0 62 | pandas=0.19.2=np112py35_1 63 | pandoc=1.19.2=0 64 | pandocfilters=1.4.1=py35_0 65 | partd=0.3.8=py35_0 66 | patsy=0.4.1=py35_0 67 | pexpect=4.2.1=py35_0 68 | pickleshare=0.7.3=py35_0 69 | pillow=4.1.1=py35_0 70 | pip=9.0.1=py35_0 71 | prompt_toolkit=1.0.14=py35_0 72 | psutil=5.2.1=py35_0 73 | ptyprocess=0.5.2=py35_0 74 | pygments=2.2.0=py35_0 75 | pyparsing=2.2.0=py35_0 76 | python=3.5.3=3 77 | python-dateutil=2.6.0=py35_0 78 | pytz=2017.2=py35_0 79 | pyyaml=3.12=py35_1 80 | pyzmq=16.0.2=py35_2 81 | readline=6.2=0 82 | requests=2.18.1=py35_0 83 | scikit-image=0.12.3=np112py35_1 84 | scikit-learn=0.18.2=np112py35_blas_openblas_200 85 | scipy=0.19.1=np112py35_blas_openblas_200 86 | seaborn=0.7.1=py35_0 87 | setuptools=33.1.1=py35_0 88 | simplegeneric=0.8.1=py35_0 89 | six=1.10.0=py35_1 90 | sortedcontainers=1.5.3=py35_0 91 | sqlalchemy=1.1.5=py35_0 92 | sqlite=3.13.0=1 93 | statsmodels=0.8.0=np112py35_0 94 | sympy=1.0=py35_0 95 | tblib=1.3.2=py35_0 96 | terminado=0.6=py35_0 97 | testpath=0.3=py35_0 98 | tk=8.5.19=1 99 | toolz=0.8.2=py35_0 100 | tornado=4.5.1=py35_0 101 | traitlets=4.3.2=py35_0 102 | urllib3=1.21.1=py35_0 103 | vincent=0.4.4=py35_0 104 | wcwidth=0.1.7=py35_0 105 | webencodings=0.5=py35_0 106 | wheel=0.29.0=py35_0 107 | widgetsnbextension=2.0.0=py35_0 108 | xlrd=1.0.0=py35_1 109 | xz=5.2.2=0 110 | yaml=0.1.6=0 111 | zeromq=4.2.1=1 112 | zict=0.1.2=py35_0 113 | zlib=1.2.11=0 114 | -------------------------------------------------------------------------------- /cython_mcmc/cython_mcmc/mcmc.pyx: -------------------------------------------------------------------------------- 1 | # cython: profile=True 2 | 3 | cimport cython 4 | from rng.mt19937 cimport RandomState 5 | from rng.mt19937 import RandomState 6 | from libc.math cimport log as clog, pi as cpi, exp as cexp 7 | import numpy as np 8 | cimport numpy as cnp 9 | 10 | DEF LOG_2_PI = 1.8378770664093453 11 | 12 | @cython.boundscheck(False) 13 | @cython.wraparound(False) 14 | @cython.cdivision(True) 15 | cdef double norm_logpdf(const double mu, const double sigma, const double* const x, const int n): 16 | cdef double s = 0.0 17 | cdef double ONE_OVER_SIG = 1.0 / sigma 18 | cdef int i 19 | for i in range(n): 20 | s += (x[i] - mu) * (x[i] - mu) 21 | return - 0.5 * n * LOG_2_PI - n * clog(sigma) - 0.5 * ONE_OVER_SIG * ONE_OVER_SIG * s 22 | 23 | cdef RandomState _rs = RandomState() 24 | 25 | cdef double sample_norm(double mu, double sigma): 26 | return _rs.c_standard_normal() * sigma + mu 27 | 28 | cdef bint accept_p(double log_p_accept): 29 | return _rs.c_random_sample() < cexp(log_p_accept) 30 | 31 | @cython.boundscheck(False) 32 | @cython.wraparound(False) 33 | def log_sampler(cnp.ndarray[double] data, 34 | int samples, 35 | double mu_init=.5, 36 | double proposal_width=.5, 37 | double mu_prior_mu=0, 38 | double mu_prior_sd=1.): 39 | 40 | cdef: 41 | double mu_proposal 42 | double log_likelihood_current, log_likelihood_proposal 43 | double log_prior_current, log_prior_proposal 44 | double log_p_current, log_p_proposal 45 | double log_p_accept 46 | bint accept 47 | double mu_current = mu_init 48 | list posterior = [mu_current] 49 | double *cdata = &data[0] 50 | int ndata = data.shape[0] 51 | cnp.ndarray[double] np_buf = np.empty((1,), dtype='f8') 52 | double *buf1 = &np_buf[0] 53 | int i 54 | 55 | for i in range(samples): 56 | # suggest new position 57 | # mu_proposal = sample_norm(_rs, mu_current, proposal_width) 58 | mu_proposal = sample_norm(mu_current, proposal_width) 59 | 60 | # Compute likelihood by adding log probabilities of each data point 61 | log_likelihood_current = norm_logpdf(mu_current, 1, cdata, ndata) 62 | log_likelihood_proposal = norm_logpdf(mu_proposal, 1, cdata, ndata) 63 | 64 | # Compute prior log probability of current and proposed mu 65 | buf1[0] = mu_current 66 | log_prior_current = norm_logpdf(mu_prior_mu, mu_prior_sd, buf1, 1) 67 | buf1[0] = mu_proposal 68 | log_prior_proposal = norm_logpdf(mu_prior_mu, mu_prior_sd, buf1, 1) 69 | 70 | log_p_current = log_likelihood_current + log_prior_current 71 | log_p_proposal = log_likelihood_proposal + log_prior_proposal 72 | 73 | # Accept proposal? 74 | log_p_accept = log_p_proposal - log_p_current 75 | 76 | # if accept_p(_rs, log_p_accept): 77 | if accept_p(log_p_accept): 78 | mu_current = mu_proposal 79 | 80 | posterior.append(mu_current) 81 | 82 | return posterior 83 | -------------------------------------------------------------------------------- /rng/licenses/RANDOMKIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2005, Jean-Sebastien Roy (js@jeannot.org) 2 | 3 | The rk_random and rk_seed functions algorithms and the original design of 4 | the Mersenne Twister RNG: 5 | 6 | Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions 11 | are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | 3. The names of its contributors may not be used to endorse or promote 21 | products derived from this software without specific prior written 22 | permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 28 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | Original algorithm for the implementation of rk_interval function from 37 | Richard J. Wagner's implementation of the Mersenne Twister RNG, optimised by 38 | Magnus Jonsson. 39 | 40 | Constants used in the rk_double implementation by Isaku Wada. 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a 43 | copy of this software and associated documentation files (the 44 | "Software"), to deal in the Software without restriction, including 45 | without limitation the rights to use, copy, modify, merge, publish, 46 | distribute, sublicense, and/or sell copies of the Software, and to 47 | permit persons to whom the Software is furnished to do so, subject to 48 | the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be included 51 | in all copies or substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 54 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 55 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 56 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 57 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 58 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 59 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 60 | -------------------------------------------------------------------------------- /rng/rng/bounded_integers.pxi.in: -------------------------------------------------------------------------------- 1 | _randint_type = {'bool': (0, 2), 2 | 'int8': (-2**7, 2**7), 3 | 'int16': (-2**15, 2**15), 4 | 'int32': (-2**31, 2**31), 5 | 'int64': (-2**63, 2**63), 6 | 'uint8': (0, 2**8), 7 | 'uint16': (0, 2**16), 8 | 'uint32': (0, 2**32), 9 | 'uint64': (0, 2**64) 10 | } 11 | 12 | ctypedef np.npy_bool bool_t 13 | 14 | {{ 15 | py: 16 | ctypes = (('int64', 'uint64'), 17 | ('int32', 'uint32'), 18 | ('int16', 'uint16'), 19 | ('int8', 'uint8'), 20 | ('uint64', 'uint64'), 21 | ('uint32', 'uint32'), 22 | ('uint16', 'uint16'), 23 | ('uint8', 'uint8'), 24 | ('bool','bool'), 25 | )}} 26 | 27 | {{for nptype, utype in ctypes}} 28 | 29 | {{ py: otype = nptype + '_' if nptype == 'bool' else nptype }} 30 | 31 | cdef object _rand_{{nptype}}(low, high, size, aug_state *state, lock): 32 | """ 33 | _rand_{{nptype}}(self, low, high, size, rngstate) 34 | 35 | Return random np.{{nptype}} integers between `low` and `high`, inclusive. 36 | 37 | Return random integers from the "discrete uniform" distribution in the 38 | closed interval [`low`, `high`). If `high` is None (the default), 39 | then results are from [0, `low`). On entry the arguments are presumed 40 | to have been validated for size and order for the np.{{nptype}} type. 41 | 42 | Parameters 43 | ---------- 44 | low : int 45 | Lowest (signed) integer to be drawn from the distribution (unless 46 | ``high=None``, in which case this parameter is the *highest* such 47 | integer). 48 | high : int 49 | If provided, the largest (signed) integer to be drawn from the 50 | distribution (see above for behavior if ``high=None``). 51 | size : int or tuple of ints 52 | Output shape. If the given shape is, e.g., ``(m, n, k)``, then 53 | ``m * n * k`` samples are drawn. Default is None, in which case a 54 | single value is returned. 55 | rngstate : encapsulated pointer to rk_state 56 | The specific type depends on the python version. In Python 2 it is 57 | a PyCObject, in Python 3 a PyCapsule object. 58 | 59 | Returns 60 | ------- 61 | out : python scalar or ndarray of np.{{nptype}} 62 | `size`-shaped array of random integers from the appropriate 63 | distribution, or a single such random int if `size` not provided. 64 | 65 | """ 66 | cdef {{utype}}_t off, rng, buf 67 | cdef {{utype}}_t *out 68 | cdef np.ndarray array 69 | cdef np.npy_intp cnt 70 | 71 | rng = <{{utype}}_t>(high - low) 72 | off = <{{utype}}_t>(<{{nptype}}_t>low) 73 | if size is None: 74 | with lock: 75 | random_bounded_{{utype}}_fill(state, off, rng, 1, &buf) 76 | return np.{{otype}}(<{{nptype}}_t>buf) 77 | else: 78 | array = np.empty(size, np.{{nptype}}) 79 | cnt = np.PyArray_SIZE(array) 80 | out = <{{utype}}_t *>np.PyArray_DATA(array) 81 | with lock, nogil: 82 | random_bounded_{{utype}}_fill(state, off, rng, cnt, out) 83 | return array 84 | 85 | {{endfor}} 86 | 87 | -------------------------------------------------------------------------------- /rng/rng/src/random-kit/random-kit.c: -------------------------------------------------------------------------------- 1 | #include "random-kit.h" 2 | 3 | extern inline uint32_t randomkit_random(randomkit_state *state); 4 | 5 | void randomkit_seed(randomkit_state *state, uint32_t seed) 6 | { 7 | int pos; 8 | seed &= 0xffffffffUL; 9 | 10 | /* Knuth's PRNG as used in the Mersenne Twister reference implementation */ 11 | for (pos = 0; pos < RK_STATE_LEN; pos++) { 12 | state->key[pos] = seed; 13 | seed = (1812433253UL * (seed ^ (seed >> 30)) + pos + 1) & 0xffffffffUL; 14 | } 15 | state->pos = RK_STATE_LEN; 16 | } 17 | 18 | 19 | /* initializes mt[RK_STATE_LEN] with a seed */ 20 | static void init_genrand(randomkit_state *state, uint32_t s) 21 | { 22 | int mti; 23 | uint32_t *mt = state->key; 24 | 25 | mt[0] = s & 0xffffffffUL; 26 | for (mti = 1; mti < RK_STATE_LEN; mti++) { 27 | /* 28 | * See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. 29 | * In the previous versions, MSBs of the seed affect 30 | * only MSBs of the array mt[]. 31 | * 2002/01/09 modified by Makoto Matsumoto 32 | */ 33 | mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); 34 | /* for > 32 bit machines */ 35 | mt[mti] &= 0xffffffffUL; 36 | } 37 | state->pos = mti; 38 | return; 39 | } 40 | 41 | 42 | /* 43 | * initialize by an array with array-length 44 | * init_key is the array for initializing keys 45 | * key_length is its length 46 | */ 47 | void randomkit_init_by_array(randomkit_state *state, uint32_t *init_key, int key_length) 48 | { 49 | /* was signed in the original code. RDH 12/16/2002 */ 50 | int i = 1; 51 | int j = 0; 52 | uint32_t *mt = state->key; 53 | int k; 54 | 55 | init_genrand(state, 19650218UL); 56 | k = (RK_STATE_LEN > key_length ? RK_STATE_LEN : key_length); 57 | for (; k; k--) { 58 | /* non linear */ 59 | mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL)) 60 | + init_key[j] + j; 61 | /* for > 32 bit machines */ 62 | mt[i] &= 0xffffffffUL; 63 | i++; 64 | j++; 65 | if (i >= RK_STATE_LEN) { 66 | mt[0] = mt[RK_STATE_LEN - 1]; 67 | i = 1; 68 | } 69 | if (j >= key_length) { 70 | j = 0; 71 | } 72 | } 73 | for (k = RK_STATE_LEN - 1; k; k--) { 74 | mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) 75 | - i; /* non linear */ 76 | mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ 77 | i++; 78 | if (i >= RK_STATE_LEN) { 79 | mt[0] = mt[RK_STATE_LEN - 1]; 80 | i = 1; 81 | } 82 | } 83 | 84 | mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ 85 | } 86 | 87 | void randomkit_gen(randomkit_state *state) 88 | { 89 | uint32_t y; 90 | int i; 91 | 92 | for (i = 0; i < N - M; i++) { 93 | y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK); 94 | state->key[i] = state->key[i+M] ^ (y>>1) ^ (-(y & 1) & MATRIX_A); 95 | } 96 | for (; i < N - 1; i++) { 97 | y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK); 98 | state->key[i] = state->key[i+(M-N)] ^ (y>>1) ^ (-(y & 1) & MATRIX_A); 99 | } 100 | y = (state->key[N - 1] & UPPER_MASK) | (state->key[0] & LOWER_MASK); 101 | state->key[N - 1] = state->key[M - 1] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A); 102 | 103 | state->pos = 0; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /rng/rng/performance.py: -------------------------------------------------------------------------------- 1 | import os 2 | import struct 3 | import timeit 4 | 5 | import numpy as np 6 | import pandas as pd 7 | from numpy.random import RandomState 8 | 9 | rs = RandomState() 10 | 11 | SETUP = ''' 12 | import numpy as np 13 | import {mod}.{rng} 14 | rs = {mod}.{rng}.RandomState() 15 | rs.random_sample() 16 | ''' 17 | 18 | scale_32 = scale_64 = 1 19 | if struct.calcsize('P') == 8 and os.name != 'nt': 20 | # 64 bit 21 | scale_32 = 0.5 22 | else: 23 | scale_64 = 2 24 | 25 | # RNGS = ['mlfg_1279_861', 'mrg32k3a', 'pcg64', 'pcg32', 'mt19937', 'xorshift128', 'xorshift1024', 26 | # 'xoroshiro128plus', 'dsfmt', 'random'] 27 | RNGS = ['mt19937'] 28 | 29 | 30 | def timer(code, setup): 31 | return 1000 * min(timeit.Timer(code, setup=setup).repeat(10, 10)) / 10.0 32 | 33 | 34 | def print_legend(legend): 35 | print('\n' + legend + '\n' + '*' * max(60, len(legend))) 36 | 37 | 38 | def run_timer(dist, command, numpy_command=None, setup='', random_type=''): 39 | print('-' * 80) 40 | if numpy_command is None: 41 | numpy_command = command 42 | 43 | res = {} 44 | for rng in RNGS: 45 | mod = 'srs' if rng != 'random' else 'numpy' 46 | key = '-'.join((mod, rng, dist)).replace('"', '') 47 | command = numpy_command if 'numpy' in mod else command 48 | res[key] = timer(command, setup=setup.format(mod=mod, rng=rng)) 49 | 50 | # import pdb; pdb.set_trace() 51 | s = pd.Series(res) 52 | t = s.apply(lambda x: '{0:0.2f} ms'.format(x)) 53 | print_legend('Time to produce 1,000,000 ' + random_type) 54 | print(t.sort_index()) 55 | 56 | p = 1000.0 / s 57 | p = p.apply(lambda x: '{0:0.2f} million'.format(x)) 58 | print_legend(random_type + ' per second') 59 | print(p.sort_index()) 60 | 61 | if 0: 62 | baseline = [k for k in p.index if 'numpy' in k][0] 63 | p = 1000.0 / s 64 | p = p / p[baseline] * 100 - 100 65 | p = p.drop(baseline, 0) 66 | p = p.apply(lambda x: '{0:0.1f}%'.format(x)) 67 | print_legend('Speed-up relative to NumPy') 68 | print(p.sort_index()) 69 | print('-' * 80) 70 | 71 | 72 | def timer_uniform(): 73 | dist = 'random_sample' 74 | command = 'rs.random_sample(1000000)' 75 | run_timer(dist, command, None, SETUP, 'Uniforms') 76 | 77 | 78 | def timer_32bit(): 79 | info = np.iinfo(np.uint32) 80 | min, max = info.min, info.max 81 | dist = 'random_uintegers' 82 | command = 'rs.random_uintegers(1000000, 32)' 83 | command_numpy = 'rs.randint({min}, {max}+1, 1000000, dtype=np.uint32)' 84 | command_numpy = command_numpy.format(min=min, max=max) 85 | run_timer(dist, command, command_numpy, SETUP, '32-bit unsigned integers') 86 | 87 | 88 | def timer_64bit(): 89 | info = np.iinfo(np.uint64) 90 | min, max = info.min, info.max 91 | dist = 'random_uintegers' 92 | command = 'rs.random_uintegers(1000000)' 93 | command_numpy = 'rs.randint({min}, {max}+1, 1000000, dtype=np.uint64)' 94 | command_numpy = command_numpy.format(min=min, max=max) 95 | run_timer(dist, command, command_numpy, SETUP, '64-bit unsigned integers') 96 | 97 | 98 | def timer_normal(): 99 | dist = 'standard_normal' 100 | command = 'rs.standard_normal(1000000, method="bm")' 101 | command_numpy = 'rs.standard_normal(1000000)' 102 | run_timer(dist, command, command_numpy, SETUP, 'Box-Muller normals') 103 | 104 | 105 | def timer_normal_zig(): 106 | dist = 'standard_normal' 107 | command = 'rs.standard_normal(1000000, method="zig")' 108 | command_numpy = 'rs.standard_normal(1000000)' 109 | run_timer(dist, command, command_numpy, SETUP, 'Standard normals (Ziggurat)') 110 | 111 | 112 | if __name__ == '__main__': 113 | timer_uniform() 114 | timer_32bit() 115 | timer_64bit() 116 | timer_normal() 117 | timer_normal_zig() 118 | -------------------------------------------------------------------------------- /05-profiling-and-performance.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Profiling and Performance" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Step one: get runtime performance numbers" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "%load_ext Cython" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "%%cython\n", 37 | "# cython: profile=True\n", 38 | "\n", 39 | "def outer(int n):\n", 40 | " # Contrived, I admit...\n", 41 | " for _ in range(n):\n", 42 | " inner(n)\n", 43 | " return inner(n)\n", 44 | "\n", 45 | "cdef int inner(int n):\n", 46 | " res = 0\n", 47 | " for i in range(n):\n", 48 | " res += i\n", 49 | " return res\n", 50 | "\n", 51 | "def reverse(l):\n", 52 | " if len(l) <= 1:\n", 53 | " return l\n", 54 | " return _reverse_helper(l, [])\n", 55 | "\n", 56 | "cdef _reverse_helper(l, a):\n", 57 | " if not l:\n", 58 | " return a\n", 59 | " return _reverse_helper(l[:-1], a + [l[-1]])" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": { 66 | "collapsed": true 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "reverse(list(range(10)))" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": { 77 | "collapsed": true 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "%%prun\n", 82 | "reverse(list(range(10000)))" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": { 89 | "collapsed": true 90 | }, 91 | "outputs": [], 92 | "source": [ 93 | "%%prun\n", 94 | "outer(10000)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## Step two: line-level annotations" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": { 108 | "collapsed": true 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "%%cython -a\n", 113 | "\n", 114 | "def outer(int n):\n", 115 | " for _ in range(n):\n", 116 | " inner(n)\n", 117 | " return inner(n)\n", 118 | "\n", 119 | "cdef int inner(int n):\n", 120 | " res = 0\n", 121 | " for i in range(n):\n", 122 | " res += i\n", 123 | " return res\n", 124 | "\n", 125 | "def reverse(l):\n", 126 | " if len(l) <= 1:\n", 127 | " return l\n", 128 | " return _reverse_helper(l, [])\n", 129 | "\n", 130 | "cdef list _reverse_helper(list l, list a):\n", 131 | " if not l:\n", 132 | " return a\n", 133 | " return _reverse_helper(l[:-1], a + [l[-1]])" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": { 139 | "collapsed": true 140 | }, 141 | "source": [ 142 | "## Exercises\n", 143 | "\n", 144 | "* With the help of profiling and annotations, modify the implementations of `outer` and `inner` and see what difference it makes in runtime and in code generated.\n", 145 | "* Can you add static C typing to make the `for` loops and loop bodies efficient?" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": { 152 | "collapsed": true 153 | }, 154 | "outputs": [], 155 | "source": [] 156 | } 157 | ], 158 | "metadata": { 159 | "kernelspec": { 160 | "display_name": "Python 3", 161 | "language": "python", 162 | "name": "python3" 163 | }, 164 | "language_info": { 165 | "codemirror_mode": { 166 | "name": "ipython", 167 | "version": 3 168 | }, 169 | "file_extension": ".py", 170 | "mimetype": "text/x-python", 171 | "name": "python", 172 | "nbconvert_exporter": "python", 173 | "pygments_lexer": "ipython3", 174 | "version": "3.6.1" 175 | } 176 | }, 177 | "nbformat": 4, 178 | "nbformat_minor": 2 179 | } 180 | -------------------------------------------------------------------------------- /rng/setup.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import sys 4 | from os.path import join 5 | import subprocess 6 | import struct 7 | 8 | import numpy 9 | from Cython.Build import cythonize 10 | from setuptools import setup, find_packages 11 | from setuptools.extension import Extension 12 | from setuptools.dist import Distribution 13 | import versioneer 14 | 15 | DEVELOP = False 16 | 17 | try: 18 | import Cython.Tempita as tempita 19 | except ImportError: 20 | try: 21 | import tempita 22 | except ImportError: 23 | raise ImportError('tempita required to install, ' 24 | 'use pip install tempita') 25 | 26 | FORCE_EMULATION = False 27 | USE_SSE2 = True if '--no-sse2' not in sys.argv else False 28 | 29 | mod_dir = './rng' 30 | configs = [] 31 | 32 | rngs = ['RNG_MT19937'] 33 | 34 | compile_rngs = rngs[:] 35 | 36 | extra_defs = [('_CRT_SECURE_NO_WARNINGS', '1')] if os.name == 'nt' else [] 37 | extra_link_args = ['/LTCG', 'Advapi32.lib', 'Kernel32.lib'] if os.name == 'nt' else [] 38 | base_extra_compile_args = [] if os.name == 'nt' else ['-std=c99'] 39 | if USE_SSE2: 40 | if os.name == 'nt': 41 | base_extra_compile_args += ['/wd4146', '/GL'] 42 | if struct.calcsize('P') < 8: 43 | base_extra_compile_args += ['/arch:SSE2'] 44 | else: 45 | base_extra_compile_args += ['-msse2'] 46 | 47 | 48 | base_include_dirs = [mod_dir] + [numpy.get_include()] 49 | if os.name == 'nt' and sys.version_info < (3, 5): 50 | base_include_dirs += [join(mod_dir, 'src', 'common')] 51 | 52 | for rng in rngs: 53 | if rng not in compile_rngs: 54 | continue 55 | 56 | file_name = rng.lower().replace('rng_', '') 57 | flags = {'RS_RNG_MOD_NAME': file_name} 58 | sources = [join(mod_dir, file_name + '.pyx'), 59 | join(mod_dir, 'distributions.c'), 60 | join(mod_dir, 'aligned_malloc.c')] 61 | include_dirs = base_include_dirs[:] 62 | extra_compile_args = base_extra_compile_args[:] 63 | 64 | if rng == 'RNG_MT19937': 65 | sources += [join(mod_dir, 'src', 'random-kit', p) for p in ('random-kit.c',)] 66 | sources += [join(mod_dir, 'interface', 'random-kit', 'random-kit-shim.c')] 67 | 68 | defs = [('RS_RANDOMKIT', '1')] 69 | 70 | include_dirs += [join(mod_dir, 'src', 'random-kit')] 71 | 72 | else: 73 | raise ValueError("what you talkin' bout, Willis?") 74 | 75 | config = {'file_name': file_name, 76 | 'sources': sources, 77 | 'include_dirs': include_dirs, 78 | 'defs': defs, 79 | 'flags': dict([(k, v) for k, v in flags.items()]), 80 | 'compile_args': extra_compile_args 81 | } 82 | 83 | configs.append(config) 84 | 85 | 86 | class BinaryDistribution(Distribution): 87 | def is_pure(self): 88 | return False 89 | 90 | 91 | try: 92 | import os 93 | readme_orig_time = os.path.getmtime('README.md') 94 | readme_mod_time = os.path.getmtime('README.rst') 95 | if readme_orig_time > readme_mod_time: 96 | subprocess.call(['pandoc', '--from=markdown', '--to=rst', '--output=README.rst', 'README.md']) 97 | except: 98 | pass 99 | # Generate files and extensions 100 | extensions = [] 101 | 102 | for config in configs: 103 | config_file_name = mod_dir + '/' + config['file_name'] + '-config.pxi' 104 | ext = Extension('rng.' + config['file_name'], 105 | sources=config['sources'], 106 | include_dirs=config['include_dirs'], 107 | define_macros=config['defs'] + extra_defs, 108 | extra_compile_args=config['compile_args'], 109 | extra_link_args=extra_link_args) 110 | extensions += [ext] 111 | 112 | # Do not run cythonize if clean 113 | if 'clean' in sys.argv: 114 | def cythonize(e, *args, **kwargs): 115 | return e 116 | else: 117 | files = glob.glob('./rng/*.in') 118 | for templated_file in files: 119 | output_file_name = os.path.splitext(templated_file)[0] 120 | if (DEVELOP and os.path.exists(output_file_name) and 121 | (os.path.getmtime(templated_file) < os.path.getmtime(output_file_name))): 122 | continue 123 | with open(templated_file, 'r') as source_file: 124 | template = tempita.Template(source_file.read()) 125 | with open(output_file_name, 'w') as output_file: 126 | output_file.write(template.substitute()) 127 | 128 | ext_modules = cythonize(extensions, force=not DEVELOP) 129 | 130 | setup(name='rng', 131 | version=versioneer.get_version(), 132 | cmdclass=versioneer.get_cmdclass(), 133 | packages=find_packages(), 134 | package_dir={'rng': './rng'}, 135 | package_data={'': ['*.c', '*.h', '*.pxi', '*.pyx', '*.pxd'], 136 | 'rng.tests.data': ['*.csv']}, 137 | include_package_data=True, 138 | license='NSCA', 139 | ext_modules=ext_modules) 140 | -------------------------------------------------------------------------------- /rng/rng/distributions.h: -------------------------------------------------------------------------------- 1 | #ifndef DISTRIBUTIONS_H 2 | #define DISTRIBUTIONS_H 3 | 4 | #include 5 | #include 6 | 7 | #include "Python.h" 8 | #include "numpy/npy_common.h" 9 | 10 | #ifdef _WIN32 11 | #include "src/common/stdint.h" 12 | typedef int bool; 13 | #define false 0 14 | #define true 1 15 | #else 16 | #include 17 | #endif 18 | 19 | #ifndef min 20 | #define min(x,y) ((xy)?x:y) 22 | #endif 23 | 24 | #ifndef M_PI 25 | #define M_PI 3.14159265358979323846264338328 26 | #endif 27 | 28 | #include "interface/random-kit/random-kit-shim.h" 29 | 30 | extern int64_t random_positive_int64(aug_state* state); 31 | 32 | extern int32_t random_positive_int32(aug_state* state); 33 | 34 | extern float random_standard_uniform_float(aug_state* state); 35 | 36 | extern double random_standard_uniform_double(aug_state* state); 37 | 38 | extern double random_standard_exponential(aug_state* state); 39 | 40 | extern double random_gauss(aug_state* state); 41 | 42 | extern double random_standard_gamma(aug_state* state, double shape); 43 | 44 | extern float random_standard_gamma_float(aug_state* state, float shape); 45 | 46 | extern double random_normal(aug_state *state, double loc, double scale); 47 | 48 | extern double random_normal_zig(aug_state *state, double loc, double scale); 49 | 50 | extern double random_exponential(aug_state *state, double scale); 51 | 52 | extern double random_uniform(aug_state *state, double loc, double scale); 53 | 54 | extern double random_gamma(aug_state *state, double shape, double scale); 55 | 56 | extern float random_gamma_float(aug_state *state, float shape, float scale); 57 | 58 | extern double random_beta(aug_state *state, double a, double b); 59 | 60 | extern double random_chisquare(aug_state *state, double df); 61 | 62 | extern double random_f(aug_state *state, double dfnum, double dfden); 63 | 64 | extern long random_negative_binomial(aug_state *state, double n, double p); 65 | 66 | extern double random_standard_cauchy(aug_state *state); 67 | 68 | extern double random_standard_t(aug_state *state, double df); 69 | 70 | extern double random_pareto(aug_state *state, double a); 71 | 72 | extern double random_weibull(aug_state *state, double a); 73 | 74 | extern double random_power(aug_state *state, double a); 75 | 76 | extern double random_laplace(aug_state *state, double loc, double scale); 77 | 78 | extern double random_gumbel(aug_state *state, double loc, double scale); 79 | 80 | extern double random_logistic(aug_state *state, double loc, double scale); 81 | 82 | extern double random_lognormal(aug_state *state, double mean, double sigma); 83 | 84 | extern double random_rayleigh(aug_state *state, double mode); 85 | 86 | extern double random_gauss_zig(aug_state* state); 87 | 88 | extern double random_gauss_zig_julia(aug_state* state); 89 | 90 | extern double random_noncentral_chisquare(aug_state *state, double df, double nonc); 91 | 92 | extern double random_noncentral_f(aug_state *state, double dfnum, double dfden, double nonc); 93 | 94 | extern double random_wald(aug_state *state, double mean, double scale); 95 | 96 | extern double random_vonmises(aug_state *state, double mu, double kappa); 97 | 98 | extern double random_triangular(aug_state *state, double left, double mode, double right); 99 | 100 | extern long random_poisson(aug_state *state, double lam); 101 | 102 | extern long random_negative_binomial(aug_state *state, double n, double p); 103 | 104 | extern long random_binomial(aug_state *state, double p, long n); 105 | 106 | extern long random_logseries(aug_state *state, double p); 107 | 108 | extern long random_geometric(aug_state *state, double p); 109 | 110 | extern long random_zipf(aug_state *state, double a); 111 | 112 | extern long random_hypergeometric(aug_state *state, long good, long bad, long sample); 113 | 114 | extern long random_positive_int(aug_state* state); 115 | 116 | extern unsigned long random_uint(aug_state* state); 117 | 118 | extern unsigned long random_interval(aug_state* state, unsigned long max); 119 | 120 | extern void random_bounded_uint64_fill(aug_state *state, uint64_t off, uint64_t rng, npy_intp cnt, uint64_t *out); 121 | 122 | extern void random_bounded_uint32_fill(aug_state *state, uint32_t off, uint32_t rng, npy_intp cnt, uint32_t *out); 123 | 124 | extern void random_bounded_uint16_fill(aug_state *state, uint16_t off, uint16_t rng, npy_intp cnt, uint16_t *out); 125 | 126 | extern void random_bounded_uint8_fill(aug_state *state, uint8_t off, uint8_t rng, npy_intp cnt, uint8_t *out); 127 | 128 | extern void random_bounded_bool_fill(aug_state *state, npy_bool off, npy_bool rng, npy_intp cnt, npy_bool *out); 129 | 130 | extern void random_uniform_fill_float(aug_state* state, npy_intp count, float *out); 131 | 132 | extern void random_uniform_fill_double(aug_state* state, npy_intp count, double *out); 133 | 134 | extern void random_standard_exponential_fill_double(aug_state* state, npy_intp count, double *out); 135 | 136 | extern void random_standard_exponential_fill_float(aug_state* state, npy_intp count, float *out); 137 | 138 | extern void random_gauss_fill(aug_state* state, npy_intp count, double *out); 139 | 140 | extern void random_gauss_fill_float(aug_state* state, npy_intp count, float *out); 141 | 142 | extern void random_gauss_zig_julia_fill(aug_state *state, npy_intp count, double *out); 143 | 144 | extern void random_gauss_zig_float_fill(aug_state *state, npy_intp count, float *out); 145 | 146 | extern void random_gauss_zig_double_fill(aug_state *state, npy_intp count, double *out); 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /rng/rng/src/common/entropy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PCG Random Number Generation for C. 3 | * 4 | * Copyright 2014 Melissa O'Neill 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * For additional information about the PCG random number generation scheme, 19 | * including its license and other licensing options, visit 20 | * 21 | * http://www.pcg-random.org 22 | */ 23 | 24 | /* This code provides a mechanism for getting external randomness for 25 | * seeding purposes. Usually, it's just a wrapper around reading 26 | * /dev/random. 27 | * 28 | * Alas, because not every system provides /dev/random, we need a fallback. 29 | * We also need to try to test whether or not to use the fallback. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "entropy.h" 38 | #ifdef _WIN32 39 | /* Windows */ 40 | #include 41 | #include 42 | #include 43 | #include 44 | #else 45 | /* Unix */ 46 | #include 47 | #include 48 | #include 49 | #endif 50 | 51 | 52 | #ifndef IS_UNIX 53 | #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) \ 54 | || (defined(__APPLE__) && defined(__MACH__))) 55 | #define IS_UNIX 1 56 | #else 57 | #define IS_UNIX 0 58 | #endif 59 | #endif 60 | 61 | // If HAVE_DEV_RANDOM is set, we use that value, otherwise we guess 62 | #ifndef HAVE_DEV_RANDOM 63 | #define HAVE_DEV_RANDOM IS_UNIX 64 | #endif 65 | 66 | #if HAVE_DEV_RANDOM 67 | #include 68 | #include 69 | #endif 70 | 71 | #if HAVE_DEV_RANDOM 72 | /* entropy_getbytes(dest, size): 73 | * Use /dev/random to get some external entropy for seeding purposes. 74 | * 75 | * Note: 76 | * If reading /dev/random fails (which ought to never happen), it returns 77 | * false, otherwise it returns true. If it fails, you could instead call 78 | * fallback_entropy_getbytes which always succeeds. 79 | */ 80 | 81 | bool entropy_getbytes(void* dest, size_t size) 82 | { 83 | int fd = open("/dev/urandom", O_RDONLY); 84 | if (fd < 0) 85 | return false; 86 | ssize_t sz = read(fd, dest, size); 87 | if ((sz < 0) || ((size_t)sz < size)) 88 | return false; 89 | return close(fd) == 0; 90 | } 91 | #endif 92 | 93 | #ifdef _WIN32 94 | bool entropy_getbytes(void *dest, size_t size) 95 | { 96 | HCRYPTPROV hCryptProv; 97 | BOOL done; 98 | 99 | if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 100 | CRYPT_VERIFYCONTEXT) || !hCryptProv) { 101 | return true; 102 | } 103 | done = CryptGenRandom(hCryptProv, (DWORD)size, (unsigned char *)dest); 104 | CryptReleaseContext(hCryptProv, 0); 105 | if (!done) { 106 | return false; 107 | } 108 | 109 | return true; 110 | } 111 | #endif 112 | 113 | 114 | 115 | /* Thomas Wang 32/64 bits integer hash function */ 116 | uint32_t entropy_hash_32(uint32_t key) { 117 | key += ~(key << 15); 118 | key ^= (key >> 10); 119 | key += (key << 3); 120 | key ^= (key >> 6); 121 | key += ~(key << 11); 122 | key ^= (key >> 16); 123 | return key; 124 | } 125 | 126 | uint64_t entropy_hash_64(uint64_t key) { 127 | key = (~key) + (key << 21); // key = (key << 21) - key - 1; 128 | key = key ^ (key >> 24); 129 | key = (key + (key << 3)) + (key << 8); // key * 265 130 | key = key ^ (key >> 14); 131 | key = (key + (key << 2)) + (key << 4); // key * 21 132 | key = key ^ (key >> 28); 133 | key = key + (key << 31); 134 | return key; 135 | } 136 | 137 | 138 | uint32_t entropy_randombytes(void) { 139 | 140 | #ifndef _WIN32 141 | struct timeval tv; 142 | gettimeofday(&tv, NULL); 143 | return entropy_hash_32(getpid()) ^ entropy_hash_32(tv.tv_sec) ^ entropy_hash_32(tv.tv_usec) ^ entropy_hash_32(clock()); 144 | #else 145 | uint32_t out = 0; 146 | int64_t counter; 147 | struct _timeb tv; 148 | _ftime(&tv); 149 | out = entropy_hash_32(GetCurrentProcessId()) ^ entropy_hash_32((uint32_t)tv.time) ^ entropy_hash_32(tv.millitm) ^ entropy_hash_32(clock()); 150 | if (QueryPerformanceCounter((LARGE_INTEGER *)&counter) != 0) 151 | out ^= entropy_hash_32((uint32_t)(counter & 0xffffffff)); 152 | return out; 153 | #endif 154 | } 155 | 156 | bool entropy_fallback_getbytes(void *dest, size_t size) 157 | { 158 | int hashes = (int)size; 159 | uint32_t *hash = malloc(hashes * sizeof(uint32_t)); 160 | // uint32_t hash[hashes]; 161 | int i; 162 | for (i=0; i < hashes; i++) 163 | { 164 | hash[i] = entropy_randombytes(); 165 | } 166 | memcpy(dest, (void *) hash, size); 167 | free(hash); 168 | return true; 169 | } 170 | 171 | void entropy_fill(void *dest, size_t size) 172 | { 173 | bool success; 174 | success = entropy_getbytes(dest, size); 175 | if (!success) 176 | { 177 | entropy_fallback_getbytes(dest, size); 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /rng/rng/tests/test_numpy_mt19937_regressions.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, absolute_import, print_function 2 | 3 | import sys 4 | from numpy.testing import (TestCase, run_module_suite, assert_, 5 | assert_array_equal, assert_raises) 6 | from numpy.compat import long 7 | import numpy as np 8 | from srs import mt19937 9 | 10 | 11 | class TestRegression(TestCase): 12 | 13 | def test_VonMises_range(self): 14 | # Make sure generated random variables are in [-pi, pi]. 15 | # Regression test for ticket #986. 16 | for mu in np.linspace(-7., 7., 5): 17 | r = mt19937.vonmises(mu, 1, 50) 18 | assert_(np.all(r > -np.pi) and np.all(r <= np.pi)) 19 | 20 | def test_hypergeometric_range(self): 21 | # Test for ticket #921 22 | assert_(np.all(mt19937.hypergeometric(3, 18, 11, size=10) < 4)) 23 | assert_(np.all(mt19937.hypergeometric(18, 3, 11, size=10) > 0)) 24 | 25 | # Test for ticket #5623 26 | args = [ 27 | (2**20 - 2, 2**20 - 2, 2**20 - 2), # Check for 32-bit systems 28 | ] 29 | is_64bits = sys.maxsize > 2**32 30 | if is_64bits and sys.platform != 'win32': 31 | args.append((2**40 - 2, 2**40 - 2, 2**40 - 2)) # Check for 64-bit systems 32 | for arg in args: 33 | assert_(mt19937.hypergeometric(*arg) > 0) 34 | 35 | def test_logseries_convergence(self): 36 | # Test for ticket #923 37 | N = 1000 38 | mt19937.seed(0) 39 | rvsn = mt19937.logseries(0.8, size=N) 40 | # these two frequency counts should be close to theoretical 41 | # numbers with this large sample 42 | # theoretical large N result is 0.49706795 43 | freq = np.sum(rvsn == 1) / float(N) 44 | msg = "Frequency was %f, should be > 0.45" % freq 45 | assert_(freq > 0.45, msg) 46 | # theoretical large N result is 0.19882718 47 | freq = np.sum(rvsn == 2) / float(N) 48 | msg = "Frequency was %f, should be < 0.23" % freq 49 | assert_(freq < 0.23, msg) 50 | 51 | def test_permutation_longs(self): 52 | mt19937.seed(1234) 53 | a = mt19937.permutation(12) 54 | mt19937.seed(1234) 55 | b = mt19937.permutation(long(12)) 56 | assert_array_equal(a, b) 57 | 58 | def test_shuffle_mixed_dimension(self): 59 | # Test for trac ticket #2074 60 | for t in [[1, 2, 3, None], 61 | [(1, 1), (2, 2), (3, 3), None], 62 | [1, (2, 2), (3, 3), None], 63 | [(1, 1), 2, 3, None]]: 64 | mt19937.seed(12345) 65 | shuffled = list(t) 66 | mt19937.shuffle(shuffled) 67 | assert_array_equal(shuffled, [t[0], t[3], t[1], t[2]]) 68 | 69 | def test_call_within_srs(self): 70 | # Check that custom RandomState does not call into global state 71 | m = mt19937.RandomState() 72 | res = np.array([0, 8, 7, 2, 1, 9, 4, 7, 0, 3]) 73 | for i in range(3): 74 | mt19937.seed(i) 75 | m.seed(4321) 76 | # If m.state is not honored, the result will change 77 | assert_array_equal(m.choice(10, size=10, p=np.ones(10)/10.), res) 78 | 79 | def test_multivariate_normal_size_types(self): 80 | # Test for multivariate_normal issue with 'size' argument. 81 | # Check that the multivariate_normal size argument can be a 82 | # numpy integer. 83 | mt19937.multivariate_normal([0], [[0]], size=1) 84 | mt19937.multivariate_normal([0], [[0]], size=np.int_(1)) 85 | mt19937.multivariate_normal([0], [[0]], size=np.int64(1)) 86 | 87 | def test_beta_small_parameters(self): 88 | # Test that beta with small a and b parameters does not produce 89 | # NaNs due to roundoff errors causing 0 / 0, gh-5851 90 | mt19937.seed(1234567890) 91 | x = mt19937.beta(0.0001, 0.0001, size=100) 92 | assert_(not np.any(np.isnan(x)), 'Nans in mt19937.beta') 93 | 94 | def test_choice_sum_of_probs_tolerance(self): 95 | # The sum of probs should be 1.0 with some tolerance. 96 | # For low precision dtypes the tolerance was too tight. 97 | # See numpy github issue 6123. 98 | mt19937.seed(1234) 99 | a = [1, 2, 3] 100 | counts = [4, 4, 2] 101 | for dt in np.float16, np.float32, np.float64: 102 | probs = np.array(counts, dtype=dt) / sum(counts) 103 | c = mt19937.choice(a, p=probs) 104 | assert_(c in a) 105 | assert_raises(ValueError, mt19937.choice, a, p=probs*0.9) 106 | 107 | 108 | def test_shuffle_of_array_of_different_length_strings(self): 109 | # Test that permuting an array of different length strings 110 | # will not cause a segfault on garbage collection 111 | # Tests gh-7710 112 | mt19937.seed(1234) 113 | 114 | a = np.array(['a', 'a' * 1000]) 115 | 116 | for _ in range(100): 117 | mt19937.shuffle(a) 118 | 119 | # Force Garbage Collection - should not segfault. 120 | import gc 121 | gc.collect() 122 | 123 | def test_shuffle_of_array_of_objects(self): 124 | # Test that permuting an array of objects will not cause 125 | # a segfault on garbage collection. 126 | # See gh-7719 127 | mt19937.seed(1234) 128 | a = np.array([np.arange(1), np.arange(4)]) 129 | 130 | for _ in range(1000): 131 | mt19937.shuffle(a) 132 | 133 | # Force Garbage Collection - should not segfault. 134 | import gc 135 | gc.collect() 136 | 137 | if __name__ == "__main__": 138 | run_module_suite() 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SciPy 2017 Cython Tutorial 2 | 3 | 4 | 5 | 2017-06-26 NOTE: the Windows-specific instructions below are untested. If you 6 | are a Windows-using tutorial-ee, please provide feedback (pull-request, 7 | email the author, tutorial slack channel) and corrections, thank you! 8 | 9 | # End goal 10 | 11 | We need just a few things set up for this tutorial: 12 | * The contents of this repository. 13 | * A CPython interpreter (Python 3). 14 | * The Cython package (version 0.25) and a few other dependencies (see 15 | `requirements_conda.txt`). 16 | * A working C / C++ compiler. 17 | 18 | It's the last requirement that can be a challenge, depending on your platform / 19 | OS. The standard GCC / clang compiler that is available on Linux / Mac 20 | (respectively) will work fine. Windows can be more of a challenge. 21 | 22 | In an effort to make things more uniform, we are using a docker container that 23 | bundles everything together except for the contents of this repository. 24 | 25 | ## Setup Instructions 26 | 27 | * Clone this repository and `cd` into it: 28 | 29 | ```bash 30 | $ git clone git@github.com:kwmsmith/scipy-2017-cython-tutorial.git 31 | $ cd scipy-2017-cython-tutorial 32 | ``` 33 | 34 | * If you've cloned earlier, `git pull` on the `master` branch to ensure you 35 | have the latest updates: 36 | 37 | ```bash 38 | $ git pull 39 | ``` 40 | 41 | The tutorial has a few one-time requirements which we describe below. 42 | 43 | ## One-stop-shop via Jupyter docker containers 44 | 45 | The `jupyter` project has convenient self-contained docker containers with 46 | everything we need for this tutorial. We recommend this method provided you 47 | can install and use a recent version of docker on your platform. This path 48 | will ensure you have a functional environment that matches the one used by the 49 | instructor. 50 | 51 | * [Install Docker](https://www.docker.com/community-edition) for your OS. 52 | * Mac / Linux: Open up a terminal and execute 53 | 54 | ```bash 55 | $ ./launch-container.sh 56 | ``` 57 | 58 | Leave this terminal as-is and do not exit the running docker session. 59 | 60 | Verify that your container is running by opening a separate terminal and running 61 | 62 | ```bash 63 | $ docker ps 64 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 65 | deadbeef jupyter/scipy-notebook "tini -- start-not..." 7 minutes ago Up 7 minutes 0.0.0.0:8888->8888/tcp cython-tutorial 66 | ``` 67 | 68 | You should see output like the above, with a different `CONTAINER ID`. 69 | Importantly, you should see `cython-tutorial` under the `NAMES` column. You 70 | will see more than one row if you have other docker containers running. 71 | 72 | * Windows: Open up a powershell and run 73 | 74 | ```bash 75 | $ launch-container.bat 76 | ``` 77 | 78 | This will download an run the [Jupyter scipy 79 | notebook](https://hub.docker.com/r/jupyter/scipy-notebook/) docker image, and 80 | launch a notebook server. 81 | 82 | * If successful, jupyter will publish a URI like 83 | 84 | ``` 85 | http://localhost:8888/?token=5f369cf87f26b0a3e4756e4b28bbd9deadbeef 86 | ``` 87 | 88 | * Visit this URI in your browser, you should see the home page of a Jupyter 89 | notebook server, with a list of all files in this repository. 90 | 91 | * Open the `test-setup` ipython notebook, and execute all cells. All should 92 | execute without error or exception. 93 | 94 | * In a separate terminal (Mac / Linux) or powershell (Windows) window, navigate 95 | to this directory and run the `docker-test-xtension.sh` command (Mac / Linux) 96 | or the `docker-test-xtension.bat` command (Windows). 97 | 98 | * You should see output like: 99 | 100 | ```bash 101 | $ ./docker-test-xtension.sh 102 | running build_ext 103 | building 'xtension.foo' extension 104 | gcc -pthread -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/opt/conda/include/python3.5m -c xtension/foo.c -o build/temp.linux-x86_64-3.5/xtension/foo.o 105 | gcc -pthread -shared -L/opt/conda/lib -Wl,-rpath=/opt/conda/lib,--no-as-needed build/temp.linux-x86_64-3.5/xtension/foo.o -L/opt/conda/lib -lpython3.5m -o build/lib.linux-x86_64-3.5/xtension/foo.cpython-35m-x86_64-linux-gnu.so 106 | copying build/lib.linux-x86_64-3.5/xtension/foo.cpython-35m-x86_64-linux-gnu.so -> xtension 107 | *********************************************************** 108 | sys.executable: /opt/conda/bin/python 109 | cython version: 0.25.2 110 | xtension module test (31.415926): 31.415926 111 | *********************************************************** 112 | ``` 113 | 114 | * If you see an error like `Error: No such container: cython-tutorial`, then 115 | you likely shut down the docker container before running the test. Re-launch 116 | the container (`./launch-container.sh`) and in a separate terminal run the 117 | `docker-test-xtension.sh` script again. 118 | 119 | ## Platform-specific (non-docker) setup instructions 120 | 121 | These instructions are for those who can't or don't want to use the recommended 122 | docker-based installation above. 123 | 124 | ### Mac-specific setup (non-docker) 125 | 126 | * Ensure you already have XCode / Mac OS developer tools / command line tools 127 | installed; if not, do so for your version of Mac OS. Check your install by 128 | running the following from the commandline: 129 | 130 | ```bash 131 | $ gcc --version 132 | Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 133 | Apple LLVM version 8.0.0 (clang-800.0.42.1) 134 | Target: x86_64-apple-darwin15.6.0 135 | Thread model: posix 136 | InstalledDir: /Library/Developer/CommandLineTools/usr/bin 137 | ``` 138 | 139 | ### Linux-specific setup (non-docker) 140 | 141 | * Ensure you have the packages necessary for `gcc` and related headers for your 142 | distribution. Check that you have a recent version of `gcc` installed: 143 | 144 | ```bash 145 | $ gcc --version 146 | ``` 147 | 148 | ### Windows-specific setup (non-docker) 149 | 150 | NOTE: untested -- please provide feedback! 151 | 152 | * [Install Visual Studio 2017](https://blogs.msdn.microsoft.com/pythonengineering/2016/04/11/unable-to-find-vcvarsall-bat/), select the *Python development workload* and the Native development tools option. 153 | 154 | ### General setup after compiler / dev tools are installed (non-docker) 155 | 156 | * If you haven't already, [install Miniconda](https://conda.io/miniconda.html). 157 | 158 | * Create a `cython-tutorial` environment for this tutorial, using the 159 | `requirements_conda.txt` file provided: 160 | 161 | ```bash 162 | $ conda create --yes -n cython-tutorial --file ./requirements_conda.txt 163 | $ source activate cython-tutorial 164 | ``` 165 | 166 | * Launch a jupyter notebook server 167 | 168 | ```bash 169 | $ jupyter notebook 170 | ``` 171 | 172 | * Open the `test-setup` notebook and run all cells. All should execute without 173 | error or exception. 174 | -------------------------------------------------------------------------------- /11-cython-mcmc-taking-it-to-11.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Optimized Cython MCMC implementation\n", 8 | "\n", 9 | "The implementation in the previous section still used `numpy.random` calls, which go through Python.\n", 10 | "\n", 11 | "In this notebook, we'll demonstrate creating a separate Cython package called `cython_mcmc` that uses another Cython package named `mt19937` for faster random number generation.\n", 12 | "\n", 13 | "We will see a significant speedup for the MCMC sampler as a result of our efforts." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## First step -- compile external packages and run perfomance tests" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": { 27 | "collapsed": true 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "%%bash\n", 32 | "cd ./rng\n", 33 | "python ./setup.py develop\n", 34 | "cython -a ./rng/mt19937.pyx" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": { 41 | "collapsed": true 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "%%bash\n", 46 | "cd cython_mcmc\n", 47 | "python ./setup.py develop\n", 48 | "cython -a ./cython_mcmc/mcmc.pyx" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": { 55 | "collapsed": true 56 | }, 57 | "outputs": [], 58 | "source": [ 59 | "%matplotlib inline\n", 60 | "import matplotlib.pyplot as plt\n", 61 | "import seaborn as sns\n", 62 | "\n", 63 | "import numpy as np\n", 64 | "from scipy.stats import norm\n", 65 | "\n", 66 | "from cython_mcmc import mcmc\n", 67 | "\n", 68 | "np.random.seed(123)\n", 69 | "data = np.random.randn(20)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "%timeit mcmc.log_sampler(data, samples=15000, mu_init=1.0)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "posterior = mcmc.log_sampler(data, samples=15000, mu_init=1.0)\n", 88 | "plt.plot(posterior);" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": { 95 | "collapsed": true 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "def calc_posterior_analytical(data, x, mu_0, sigma_0):\n", 100 | " sigma = 1.\n", 101 | " n = len(data)\n", 102 | " mu_post = (mu_0 / sigma_0**2 + data.sum() / sigma**2) / (1. / sigma_0**2 + n / sigma**2)\n", 103 | " sigma_post = (1. / sigma_0**2 + n / sigma**2)**-1\n", 104 | " return norm(mu_post, np.sqrt(sigma_post)).pdf(x)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "ax = plt.subplot()\n", 114 | "\n", 115 | "sns.distplot(posterior[500:], ax=ax, label='estimated posterior')\n", 116 | "x = np.linspace(-.7, .9, 500)\n", 117 | "post = calc_posterior_analytical(data, x, 0, 1)\n", 118 | "ax.plot(x, post, 'g', label='analytic posterior')\n", 119 | "_ = ax.set(xlabel='mu', ylabel='belief');\n", 120 | "ax.legend();" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "## Standard error in $\\mu$ shrinks as more data is collected" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "%%time\n", 137 | "data_2000 = np.random.randn(2000)\n", 138 | "posterior_2000 = mcmc.log_sampler(data_2000, samples=150000, mu_init=1.0)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "ax = plt.subplot()\n", 148 | "\n", 149 | "sns.distplot(posterior_2000[500::5], ax=ax, label='estimated posterior')\n", 150 | "x = np.linspace(-.1, .1, 500)\n", 151 | "post = calc_posterior_analytical(data_2000, x, 0, 1)\n", 152 | "ax.plot(x, post, 'g', label='analytic posterior')\n", 153 | "_ = ax.set(xlabel='mu', ylabel='belief');\n", 154 | "ax.legend();" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "## `log_sampler()` using external library" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "%%html\n", 171 | "./cython_mcmc/cython_mcmc/mcmc.html" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "### Worth noting\n", 179 | "\n", 180 | "* `from [...] cimport RandomState`\n", 181 | "* `from [...] import RandomState`\n", 182 | "* `sample_norm()` and `accept_p()` implementations.\n", 183 | "* `norm_logpdf()` implementation uses raw C buffers." 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "## `RandomState` extension type\n", 191 | "\n", 192 | "Cython-level compile-time interface defined in `mt19937.pxd`:" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "!cat ./rng/rng/mt19937.pxd | nl" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": null, 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "%%html\n", 211 | "./rng/rng/mt19937.html" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "## Putting it all together in `setup.py` with `cythonize()`\n", 219 | "\n", 220 | "* We use a `setup.py` script in the `cython_mcmc` package to compile everything together.\n", 221 | "* We specify all the `mcmc.pyx` external Cython and C dependencies in an `Extension()` object.\n", 222 | "* We use the `Cython.Build.cythonize()` command to pull everything together and compile things for us.\n", 223 | "* The `python setup.py develop` command is what kicks things off for us, and produces the `mcmc.so` shared object file." 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": { 230 | "collapsed": true 231 | }, 232 | "outputs": [], 233 | "source": [ 234 | "%cat ./cython_mcmc/setup.py" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": { 241 | "collapsed": true 242 | }, 243 | "outputs": [], 244 | "source": [] 245 | } 246 | ], 247 | "metadata": { 248 | "kernelspec": { 249 | "display_name": "Python 3", 250 | "language": "python", 251 | "name": "python3" 252 | }, 253 | "language_info": { 254 | "codemirror_mode": { 255 | "name": "ipython", 256 | "version": 3 257 | }, 258 | "file_extension": ".py", 259 | "mimetype": "text/x-python", 260 | "name": "python", 261 | "nbconvert_exporter": "python", 262 | "pygments_lexer": "ipython3", 263 | "version": "3.6.1" 264 | } 265 | }, 266 | "nbformat": 4, 267 | "nbformat_minor": 2 268 | } 269 | -------------------------------------------------------------------------------- /07-cythonize-pandas-groupby.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Speed up Pandas' GroupBy with Cython\n", 8 | "\n", 9 | "Scenario: we have a large dataframe [comprising](https://en.wikipedia.org/wiki/User:Giraffedata/comprised_of) several smaller tables concatenated together.\n", 10 | "\n", 11 | "We've done all our data cleaning and munging on the columns of the large dataframe, and we need to break it up into small dataframes. This scenario may, hypothetically, come up when doing sequence modeling with recurrent neural networks, and we need to prepare mini-batches of sequences for our model to train against.\n", 12 | "\n", 13 | "A straightforward way to do it is `list(df.groupby(level=0, sort=False))`. Unfortunately, Pandas can be really slow in this case -- making lots of little dataframes is an expensive operation.\n", 14 | "\n", 15 | "Since we know certain constraints are satisfied, can we speed this up with NumPy and Cython?" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": { 22 | "collapsed": true 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "import pandas as pd\n", 27 | "import numpy as np\n", 28 | "from uuid import uuid4\n", 29 | "import toolz" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": { 36 | "collapsed": true 37 | }, 38 | "outputs": [], 39 | "source": [ 40 | "%load_ext Cython" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "nblocks = 10000\n", 50 | "rowsperblock = np.random.randint(1, 7, size=nblocks)\n", 51 | "ncols = 3\n", 52 | "\n", 53 | "idx = list(toolz.concat([[uuid4().hex] * rpb for rpb in rowsperblock]))\n", 54 | "df = pd.DataFrame(np.random.standard_normal(size=(rowsperblock.sum(), ncols)), index=idx)\n", 55 | "print(df.info())\n", 56 | "df.head(15)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "df.index.is_monotonic" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "list(df.groupby(level=[0], sort=False))[:2]" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "%timeit list(df.groupby(level=[0], sort=False))" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "%prun list(df.groupby(level=[0], sort=False))" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "### Creating lots of little dataframes is expensive\n", 100 | "* Pandas doesn't have a fast path for DataFrame creation with data that's already clean." 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "## NumPy version\n", 108 | "\n", 109 | "* For this problem, we're willing to adjust the output and generate a sequence of NumPy arrays rather than a sequence of dataframes.\n", 110 | "* With this adjustment, we'll see we can get a substantial speedup using NumPy operations.\n", 111 | "* Once it's rewritten to use NumPy, then we can get *further* speedups with Cython." 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": { 118 | "collapsed": true 119 | }, 120 | "outputs": [], 121 | "source": [ 122 | "def splitby(df):\n", 123 | " idx = df.index\n", 124 | " # NumPy array of \"posts\" that delineate the row indices\n", 125 | " # with which to split the dataframe.\n", 126 | " posts = np.where(idx[1:] != idx[:-1])[0] + 1\n", 127 | " split_labels = idx[np.concatenate([[0], posts, [-1]])]\n", 128 | " split_data = np.split(df.values, posts, 0)\n", 129 | " return list(zip(split_labels, split_data))" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "splitby(df)[:2]" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "%timeit splitby(df)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "### Questions\n", 155 | "* Why is this faster?\n", 156 | "* What about `DataFrame.iloc`? Can it give us fast slicing without the overhead? Why or why not?\n", 157 | "* If we double the number of columns, how do you expect the two versions to scale?" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "## First Cython version\n", 165 | "\n", 166 | "* With room for improvement..." 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "%%cython -a\n", 176 | "\n", 177 | "cimport cython\n", 178 | "cimport numpy as cnp\n", 179 | "import numpy as np\n", 180 | "\n", 181 | "def splitby_cython(df):\n", 182 | " cols = df.values\n", 183 | " idx = df.index.values\n", 184 | " n = idx.shape[0]\n", 185 | " result = []\n", 186 | " thispost = 0\n", 187 | " \n", 188 | " for i in range(1, n):\n", 189 | " if idx[i] != idx[i-1]:\n", 190 | " result.append((idx[i-1], cols[thispost:i]))\n", 191 | " thispost = i\n", 192 | " \n", 193 | " result.append((idx[i-1], cols[thispost:]))\n", 194 | " return result" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "%timeit splitby_cython(df)" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "### Exercise\n", 211 | "\n", 212 | "* Use constructs and patterns from previous notebooks to improve this result.\n", 213 | "\n", 214 | "Some pointers:\n", 215 | "* Think about how you can give Cython more type information to convert Python objects into C equivalents.\n", 216 | "* Good candidates:\n", 217 | " * Loop indexing vars, arguments to `range()`.\n", 218 | " * NumPy arrays with `cdef cnp.ndarray[double] xyz = ...`.\n", 219 | " * Statically declaring `list` and other Python types.\n", 220 | "* Remember the `@cython.boundcheck(False)` and `@cython.wraparound(False)` decorators." 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": null, 226 | "metadata": { 227 | "collapsed": true 228 | }, 229 | "outputs": [], 230 | "source": [] 231 | } 232 | ], 233 | "metadata": { 234 | "kernelspec": { 235 | "display_name": "Python 3", 236 | "language": "python", 237 | "name": "python3" 238 | }, 239 | "language_info": { 240 | "codemirror_mode": { 241 | "name": "ipython", 242 | "version": 3 243 | }, 244 | "file_extension": ".py", 245 | "mimetype": "text/x-python", 246 | "name": "python", 247 | "nbconvert_exporter": "python", 248 | "pygments_lexer": "ipython3", 249 | "version": "3.6.1" 250 | } 251 | }, 252 | "nbformat": 4, 253 | "nbformat_minor": 2 254 | } 255 | -------------------------------------------------------------------------------- /04-functions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Cython functions\n", 8 | "* Cython supports 3 kinds of functions:\n", 9 | " * Python `def` functions -- compiled Python functions that work with Python types.\n", 10 | " * C-level `cdef` functions -- low-overhead C-level functions that support C-only types.\n", 11 | " * Hybrid `cpdef` functions -- C-level function with auto-generated Python compatibility wrappers." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "%load_ext Cython" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "## `def` functions in Cython: identical to `builtin_function_or_method`\n", 30 | "\n", 31 | "* Expects Python objects as inputs; always returns a Python object." 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "def pyfunc(a, b):\n", 41 | " \"\"\"Adds its arguments polymorphically, in pure Python.\"\"\"\n", 42 | " return a + b\n", 43 | "\n", 44 | "print(type(pyfunc))\n", 45 | "print(dir(pyfunc))" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "%%cython\n", 55 | "\n", 56 | "def cyfunc(a, b):\n", 57 | " \"\"\"Adds its arguments polymorphically, in Cython!\"\"\"\n", 58 | " return a + b\n", 59 | "\n", 60 | "print(type(cyfunc))\n", 61 | "print(dir(cyfunc))" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "### Cython functions don't have dynamic attributes that Python functions have" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "set(dir(pyfunc)) - set(dir(cyfunc))" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "assert pyfunc(1, 2) == cyfunc(1, 2)\n", 87 | "assert pyfunc('a', 'b') == cyfunc('a', 'b')\n", 88 | "\n", 89 | "class klass:\n", 90 | " def __add__(self, other):\n", 91 | " print(\"Salut!\")\n", 92 | " return 42\n", 93 | "\n", 94 | "print(cyfunc(klass(), klass()))" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## `cdef` functions: C-functions with Python-like syntax" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "%%cython\n", 111 | "\n", 112 | "cdef untyped(a, b):\n", 113 | " return a + b\n", 114 | "\n", 115 | "print(untyped(1, 2), untyped('a', 'b'))\n", 116 | "\n", 117 | "cdef int typed(double a, double b):\n", 118 | " return (a + b) # Type casting in Cython.\n", 119 | "\n", 120 | "print(typed(1, 2), typed(3.14, 2.72))\n", 121 | "\n", 122 | "# print(typed('a', 'b')) <<<<<<<<<<< Compilation Error, not ValueError / RuntimeError." 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "### `cdef` variables / functions not visible to Python outside defining scope" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "untyped(1, 2), typed(1, 2)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "### Pop quiz:\n", 146 | "\n", 147 | "* Conjecture what Cython does when executing `print(typed(1, 2))` to make the `print()` call work." 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "## `cpdef` functions: two functions in one!" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "%%cython -a\n", 164 | " \n", 165 | "\n", 166 | "# cpdef functions are just like cdef functions with\n", 167 | "# an implicitly defined Python wrapper for free.\n", 168 | "cpdef int cpdef_func(int y, int z):\n", 169 | " return y + z\n", 170 | "\n", 171 | "# Call directly from other Cython code:\n", 172 | "print(cpdef_func(1, 2))" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "# Call from Python (via Python wrapper)\n", 182 | "cpdef_func(1, 2)" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "### Pop quiz:\n", 190 | "\n", 191 | "* given that a `cpdef` function is like a `cdef` function with the constraint of using only Python-convertible types in its signature, what C types can we not use with a `cpdef` function?" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "## `cdef` and `cpdef` functions and error propogation" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "%%cython\n", 208 | "\n", 209 | "cpdef int unchecked_div(int a, int b):\n", 210 | " return (a / b)\n", 211 | "\n", 212 | "# This result is concerning...\n", 213 | "print(unchecked_div(1, 0))" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "%%cython\n", 223 | "\n", 224 | "cpdef int checked_div(int a, int b) except *:\n", 225 | " return (a / b)\n", 226 | "\n", 227 | "# This is better...\n", 228 | "print(checked_div(1, 0)) # ZeroDivisionError: float division" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "### Raising exceptions inside cpdef / cdef functions with C return types" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": { 242 | "collapsed": true 243 | }, 244 | "outputs": [], 245 | "source": [ 246 | "%%cython\n", 247 | "\n", 248 | "cpdef int func() except -1: # we guarantee that -1 is never a valid return value, \n", 249 | " # ... # so Cython can use it as a sentinel to flag that an \n", 250 | " raise ValueError(\"...\") # exception has occurred." 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "metadata": {}, 257 | "outputs": [], 258 | "source": [ 259 | "func()" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": { 266 | "collapsed": true 267 | }, 268 | "outputs": [], 269 | "source": [] 270 | } 271 | ], 272 | "metadata": { 273 | "kernelspec": { 274 | "display_name": "Python 3", 275 | "language": "python", 276 | "name": "python3" 277 | }, 278 | "language_info": { 279 | "codemirror_mode": { 280 | "name": "ipython", 281 | "version": 3 282 | }, 283 | "file_extension": ".py", 284 | "mimetype": "text/x-python", 285 | "name": "python", 286 | "nbconvert_exporter": "python", 287 | "pygments_lexer": "ipython3", 288 | "version": "3.6.1" 289 | } 290 | }, 291 | "nbformat": 4, 292 | "nbformat_minor": 2 293 | } 294 | -------------------------------------------------------------------------------- /00-preliminaries.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introductions, Scope, Goals" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## About me\n", 15 | "\n", 16 | "* Data Scientist at [HomeAway](https://www.homeaway.com/)\n", 17 | "* Author of [_Cython: A Guide for Python Programmers_](http://shop.oreilly.com/product/0636920033431.do) \n", 18 | "* Have taught Cython & interfacing with Python a lot:\n", 19 | " * [SciPy 2013](https://youtu.be/gMvkiQ-gOW8), [SciPy 2015](https://youtu.be/JKCjsRDffXo) (notice a trend?)\n", 20 | " * [Enthought's training material](https://training.enthought.com/course/INTERFACING)\n", 21 | "* Physicist -> Scientific Software Developer -> Solution Architect / Data Engineer -> Data Scientist.\n", 22 | "* Usual grab-bag of Big Data and Machine Learning components and languages:\n", 23 | " * Currently: Python / Scala / R, Spark, PyData / SciPy stacks, TensorFlow, Theano, Keras, PyMC3.\n", 24 | " * In past jobs: C, C++, Cython, SWIG.\n", 25 | "* Industries: Oil & Gas, Finance, National Labs, Travel." 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Scope & Goals\n", 33 | "\n", 34 | "* Goal: learn a bit more how CPython works under the hood.\n", 35 | "* Goal: understand why CPython can be slow, and how Cython can make it fast(er).\n", 36 | "* Goal: gain enough exposure to Cython to tackle your own project, with some templates.\n", 37 | "* Non-goal: pointers, dynamic memory allocation / reference counting, Python C-API.\n", 38 | " * Cython abstracts all of this away for us, unless we're wrapping C / C++ code.\n", 39 | "* Non-goal: wrapping C / C++ codebases with Cython.\n", 40 | " * Gets into the weeds pretty quickly, requires more C / C++ expertise than assumed." 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "## What makes this tutorial different?\n", 48 | "\n", 49 | "* Thanks to cython integration with jupyter notebooks, much more hands-on.\n", 50 | "* Cover basic material more quickly\n", 51 | " * Cut out interfacing with C / C++, remove some extraneous details.\n", 52 | " * There's a book if you want to go in depth :)\n", 53 | "* More time on data-science focused verticals." 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "## Tools\n", 61 | "\n", 62 | "* We will be using the `Cython` IPython notebook extension.\n", 63 | "* Enables the `%%cython` cell magic command." 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": { 70 | "collapsed": true 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "%load_ext Cython" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": { 81 | "collapsed": true 82 | }, 83 | "outputs": [], 84 | "source": [ 85 | "%%cython\n", 86 | "\n", 87 | "'''\n", 88 | "Inside this cell is Cython code only. \n", 89 | "\n", 90 | "Behind the scenes, Jupyter and Cython write out a `.pyx` file that is dynamically compiled on cell execution. \n", 91 | "\n", 92 | "All the Python-level constructs are then imported into the current kernel namespace.\n", 93 | "'''\n", 94 | "\n", 95 | "def first_cython_func(int i):\n", 96 | " return i * 3.1415926" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "# We *use* the function in a separate (Python) cell.\n", 106 | "first_cython_func(10)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": null, 112 | "metadata": { 113 | "collapsed": true 114 | }, 115 | "outputs": [], 116 | "source": [ 117 | "%%cython?" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "```\n", 125 | " %cython [-3] [-2] [-c COMPILE_ARGS] [--link-args LINK_ARGS] [-l LIB]\n", 126 | " [-n NAME] [-L dir] [-I INCLUDE] [-+] [-f] [-a]\n", 127 | "\n", 128 | "Compile and import everything from a Cython code cell.\n", 129 | "\n", 130 | "The contents of the cell are written to a `.pyx` file in the\n", 131 | "directory `IPYTHONDIR/cython` using a filename with the hash of the\n", 132 | "code. This file is then cythonized and compiled. The resulting module\n", 133 | "is imported and all of its symbols are injected into the user's\n", 134 | "namespace. The usage is similar to that of `%%cython_pyximport` but\n", 135 | "you don't have to pass a module name::\n", 136 | "\n", 137 | " %%cython\n", 138 | " def f(x):\n", 139 | " return 2.0*x\n", 140 | "\n", 141 | "To compile OpenMP codes, pass the required `--compile-args`\n", 142 | "and `--link-args`. For example with gcc::\n", 143 | "\n", 144 | " %%cython --compile-args=-fopenmp --link-args=-fopenmp\n", 145 | " ...\n", 146 | "\n", 147 | "optional arguments:\n", 148 | " -3 Select Python 3 syntax.\n", 149 | " -2 Select Python 2 syntax.\n", 150 | " -c COMPILE_ARGS, --compile-args COMPILE_ARGS\n", 151 | " Extra flags to pass to compiler via the\n", 152 | " `extra_compile_args` Extension flag (can be specified\n", 153 | " multiple times).\n", 154 | " --link-args LINK_ARGS\n", 155 | " Extra flags to pass to linker via the\n", 156 | " `extra_link_args` Extension flag (can be specified\n", 157 | " multiple times).\n", 158 | " -l LIB, --lib LIB Add a library to link the extension against (can be\n", 159 | " specified multiple times).\n", 160 | " -n NAME, --name NAME Specify a name for the Cython module.\n", 161 | " -L dir Add a path to the list of library directories (can be\n", 162 | " specified multiple times).\n", 163 | " -I INCLUDE, --include INCLUDE\n", 164 | " Add a path to the list of include directories (can be\n", 165 | " specified multiple times).\n", 166 | " -+, --cplus Output a C++ rather than C file.\n", 167 | " -f, --force Force the compilation of a new module, even if the\n", 168 | " source has been previously compiled.\n", 169 | " -a, --annotate Produce a colorized HTML version of the source.\n", 170 | "```" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "!ls .ipython/cython/cyfoo*" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "Next notebook: [01-python-slow.ipynb](./01-python-slow.ipynb)" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": { 193 | "collapsed": true 194 | }, 195 | "outputs": [], 196 | "source": [] 197 | } 198 | ], 199 | "metadata": { 200 | "kernelspec": { 201 | "display_name": "Python 3", 202 | "language": "python", 203 | "name": "python3" 204 | }, 205 | "language_info": { 206 | "codemirror_mode": { 207 | "name": "ipython", 208 | "version": 3 209 | }, 210 | "file_extension": ".py", 211 | "mimetype": "text/x-python", 212 | "name": "python", 213 | "nbconvert_exporter": "python", 214 | "pygments_lexer": "ipython3", 215 | "version": "3.6.1" 216 | } 217 | }, 218 | "nbformat": 4, 219 | "nbformat_minor": 2 220 | } 221 | -------------------------------------------------------------------------------- /rng/rng/src/common/stdint.h: -------------------------------------------------------------------------------- 1 | // ISO C9x compliant stdint.h for Microsoft Visual Studio 2 | // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 3 | // 4 | // Copyright (c) 2006-2013 Alexander Chemeris 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // 1. Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // 12 | // 2. Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // 16 | // 3. Neither the name of the product nor the names of its contributors may 17 | // be used to endorse or promote products derived from this software 18 | // without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 22 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 23 | // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 | // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | // 31 | /////////////////////////////////////////////////////////////////////////////// 32 | 33 | #ifndef _MSC_VER // [ 34 | #error "Use this header only with Microsoft Visual C++ compilers!" 35 | #endif // _MSC_VER ] 36 | 37 | #ifndef _MSC_STDINT_H_ // [ 38 | #define _MSC_STDINT_H_ 39 | 40 | #if _MSC_VER > 1000 41 | #pragma once 42 | #endif 43 | 44 | #if _MSC_VER >= 1600 // [ 45 | #include 46 | #else // ] _MSC_VER >= 1600 [ 47 | 48 | #include 49 | 50 | // For Visual Studio 6 in C++ mode and for many Visual Studio versions when 51 | // compiling for ARM we should wrap include with 'extern "C++" {}' 52 | // or compiler give many errors like this: 53 | // error C2733: second C linkage of overloaded function 'wmemchr' not allowed 54 | #ifdef __cplusplus 55 | extern "C" { 56 | #endif 57 | # include 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | // Define _W64 macros to mark types changing their size, like intptr_t. 63 | #ifndef _W64 64 | # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 65 | # define _W64 __w64 66 | # else 67 | # define _W64 68 | # endif 69 | #endif 70 | 71 | 72 | // 7.18.1 Integer types 73 | 74 | // 7.18.1.1 Exact-width integer types 75 | 76 | // Visual Studio 6 and Embedded Visual C++ 4 doesn't 77 | // realize that, e.g. char has the same size as __int8 78 | // so we give up on __intX for them. 79 | #if (_MSC_VER < 1300) 80 | typedef signed char int8_t; 81 | typedef signed short int16_t; 82 | typedef signed int int32_t; 83 | typedef unsigned char uint8_t; 84 | typedef unsigned short uint16_t; 85 | typedef unsigned int uint32_t; 86 | #else 87 | typedef signed __int8 int8_t; 88 | typedef signed __int16 int16_t; 89 | typedef signed __int32 int32_t; 90 | typedef unsigned __int8 uint8_t; 91 | typedef unsigned __int16 uint16_t; 92 | typedef unsigned __int32 uint32_t; 93 | #endif 94 | typedef signed __int64 int64_t; 95 | typedef unsigned __int64 uint64_t; 96 | 97 | 98 | // 7.18.1.2 Minimum-width integer types 99 | typedef int8_t int_least8_t; 100 | typedef int16_t int_least16_t; 101 | typedef int32_t int_least32_t; 102 | typedef int64_t int_least64_t; 103 | typedef uint8_t uint_least8_t; 104 | typedef uint16_t uint_least16_t; 105 | typedef uint32_t uint_least32_t; 106 | typedef uint64_t uint_least64_t; 107 | 108 | // 7.18.1.3 Fastest minimum-width integer types 109 | typedef int8_t int_fast8_t; 110 | typedef int16_t int_fast16_t; 111 | typedef int32_t int_fast32_t; 112 | typedef int64_t int_fast64_t; 113 | typedef uint8_t uint_fast8_t; 114 | typedef uint16_t uint_fast16_t; 115 | typedef uint32_t uint_fast32_t; 116 | typedef uint64_t uint_fast64_t; 117 | 118 | // 7.18.1.4 Integer types capable of holding object pointers 119 | #ifdef _WIN64 // [ 120 | typedef signed __int64 intptr_t; 121 | typedef unsigned __int64 uintptr_t; 122 | #else // _WIN64 ][ 123 | typedef _W64 signed int intptr_t; 124 | typedef _W64 unsigned int uintptr_t; 125 | #endif // _WIN64 ] 126 | 127 | // 7.18.1.5 Greatest-width integer types 128 | typedef int64_t intmax_t; 129 | typedef uint64_t uintmax_t; 130 | 131 | 132 | // 7.18.2 Limits of specified-width integer types 133 | 134 | #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 135 | 136 | // 7.18.2.1 Limits of exact-width integer types 137 | #define INT8_MIN ((int8_t)_I8_MIN) 138 | #define INT8_MAX _I8_MAX 139 | #define INT16_MIN ((int16_t)_I16_MIN) 140 | #define INT16_MAX _I16_MAX 141 | #define INT32_MIN ((int32_t)_I32_MIN) 142 | #define INT32_MAX _I32_MAX 143 | #define INT64_MIN ((int64_t)_I64_MIN) 144 | #define INT64_MAX _I64_MAX 145 | #define UINT8_MAX _UI8_MAX 146 | #define UINT16_MAX _UI16_MAX 147 | #define UINT32_MAX _UI32_MAX 148 | #define UINT64_MAX _UI64_MAX 149 | 150 | // 7.18.2.2 Limits of minimum-width integer types 151 | #define INT_LEAST8_MIN INT8_MIN 152 | #define INT_LEAST8_MAX INT8_MAX 153 | #define INT_LEAST16_MIN INT16_MIN 154 | #define INT_LEAST16_MAX INT16_MAX 155 | #define INT_LEAST32_MIN INT32_MIN 156 | #define INT_LEAST32_MAX INT32_MAX 157 | #define INT_LEAST64_MIN INT64_MIN 158 | #define INT_LEAST64_MAX INT64_MAX 159 | #define UINT_LEAST8_MAX UINT8_MAX 160 | #define UINT_LEAST16_MAX UINT16_MAX 161 | #define UINT_LEAST32_MAX UINT32_MAX 162 | #define UINT_LEAST64_MAX UINT64_MAX 163 | 164 | // 7.18.2.3 Limits of fastest minimum-width integer types 165 | #define INT_FAST8_MIN INT8_MIN 166 | #define INT_FAST8_MAX INT8_MAX 167 | #define INT_FAST16_MIN INT16_MIN 168 | #define INT_FAST16_MAX INT16_MAX 169 | #define INT_FAST32_MIN INT32_MIN 170 | #define INT_FAST32_MAX INT32_MAX 171 | #define INT_FAST64_MIN INT64_MIN 172 | #define INT_FAST64_MAX INT64_MAX 173 | #define UINT_FAST8_MAX UINT8_MAX 174 | #define UINT_FAST16_MAX UINT16_MAX 175 | #define UINT_FAST32_MAX UINT32_MAX 176 | #define UINT_FAST64_MAX UINT64_MAX 177 | 178 | // 7.18.2.4 Limits of integer types capable of holding object pointers 179 | #ifdef _WIN64 // [ 180 | # define INTPTR_MIN INT64_MIN 181 | # define INTPTR_MAX INT64_MAX 182 | # define UINTPTR_MAX UINT64_MAX 183 | #else // _WIN64 ][ 184 | # define INTPTR_MIN INT32_MIN 185 | # define INTPTR_MAX INT32_MAX 186 | # define UINTPTR_MAX UINT32_MAX 187 | #endif // _WIN64 ] 188 | 189 | // 7.18.2.5 Limits of greatest-width integer types 190 | #define INTMAX_MIN INT64_MIN 191 | #define INTMAX_MAX INT64_MAX 192 | #define UINTMAX_MAX UINT64_MAX 193 | 194 | // 7.18.3 Limits of other integer types 195 | 196 | #ifdef _WIN64 // [ 197 | # define PTRDIFF_MIN _I64_MIN 198 | # define PTRDIFF_MAX _I64_MAX 199 | #else // _WIN64 ][ 200 | # define PTRDIFF_MIN _I32_MIN 201 | # define PTRDIFF_MAX _I32_MAX 202 | #endif // _WIN64 ] 203 | 204 | #define SIG_ATOMIC_MIN INT_MIN 205 | #define SIG_ATOMIC_MAX INT_MAX 206 | 207 | #ifndef SIZE_MAX // [ 208 | # ifdef _WIN64 // [ 209 | # define SIZE_MAX _UI64_MAX 210 | # else // _WIN64 ][ 211 | # define SIZE_MAX _UI32_MAX 212 | # endif // _WIN64 ] 213 | #endif // SIZE_MAX ] 214 | 215 | // WCHAR_MIN and WCHAR_MAX are also defined in 216 | #ifndef WCHAR_MIN // [ 217 | # define WCHAR_MIN 0 218 | #endif // WCHAR_MIN ] 219 | #ifndef WCHAR_MAX // [ 220 | # define WCHAR_MAX _UI16_MAX 221 | #endif // WCHAR_MAX ] 222 | 223 | #define WINT_MIN 0 224 | #define WINT_MAX _UI16_MAX 225 | 226 | #endif // __STDC_LIMIT_MACROS ] 227 | 228 | 229 | // 7.18.4 Limits of other integer types 230 | 231 | #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 232 | 233 | // 7.18.4.1 Macros for minimum-width integer constants 234 | 235 | #define INT8_C(val) val##i8 236 | #define INT16_C(val) val##i16 237 | #define INT32_C(val) val##i32 238 | #define INT64_C(val) val##i64 239 | 240 | #define UINT8_C(val) val##ui8 241 | #define UINT16_C(val) val##ui16 242 | #define UINT32_C(val) val##ui32 243 | #define UINT64_C(val) val##ui64 244 | 245 | // 7.18.4.2 Macros for greatest-width integer constants 246 | // These #ifndef's are needed to prevent collisions with . 247 | // Check out Issue 9 for the details. 248 | #ifndef INTMAX_C // [ 249 | # define INTMAX_C INT64_C 250 | #endif // INTMAX_C ] 251 | #ifndef UINTMAX_C // [ 252 | # define UINTMAX_C UINT64_C 253 | #endif // UINTMAX_C ] 254 | 255 | #endif // __STDC_CONSTANT_MACROS ] 256 | 257 | #endif // _MSC_VER >= 1600 ] 258 | 259 | #endif // _MSC_STDINT_H_ ] -------------------------------------------------------------------------------- /rng/rng/tests/test_direct.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from os.path import join 4 | from unittest import TestCase 5 | from nose import SkipTest 6 | 7 | import numpy as np 8 | from srs import mt19937 9 | from numpy.testing import assert_equal, assert_allclose, assert_array_equal, \ 10 | assert_raises 11 | 12 | if (sys.version_info > (3, 0)): 13 | long = int 14 | 15 | pwd = os.path.dirname(os.path.abspath(__file__)) 16 | 17 | 18 | def uniform32_from_uint64(x): 19 | x = np.uint64(x) 20 | upper = np.array(x >> np.uint64(32), dtype=np.uint32) 21 | lower = np.uint64(0xffffffff) 22 | lower = np.array(x & lower, dtype=np.uint32) 23 | joined = np.column_stack([lower, upper]).ravel() 24 | out = (joined >> np.uint32(9)) * (1.0 / 2 ** 23) 25 | return out.astype(np.float32) 26 | 27 | 28 | def uniform32_from_uint63(x): 29 | x = np.uint64(x) 30 | x = np.uint32(x >> np.uint64(32)) 31 | out = (x >> np.uint32(9)) * (1.0 / 2 ** 23) 32 | return out.astype(np.float32) 33 | 34 | 35 | def uniform32_from_uint53(x): 36 | x = np.uint64(x) 37 | x = np.uint32(x & np.uint64(0xffffffff)) 38 | out = (x >> np.uint32(9)) * (1.0 / 2 ** 23) 39 | return out.astype(np.float32) 40 | 41 | 42 | def uniform32_from_uint32(x): 43 | return (x >> np.uint32(9)) * (1.0 / 2 ** 23) 44 | 45 | 46 | def uniform32_from_uint(x, bits): 47 | if bits == 64: 48 | return uniform32_from_uint64(x) 49 | elif bits == 63: 50 | return uniform32_from_uint63(x) 51 | elif bits == 53: 52 | return uniform32_from_uint53(x) 53 | elif bits == 32: 54 | return uniform32_from_uint32(x) 55 | else: 56 | raise NotImplementedError 57 | 58 | 59 | def uniform_from_uint(x, bits): 60 | if bits in (64, 63, 53): 61 | return uniform_from_uint64(x) 62 | elif bits == 32: 63 | return uniform_from_uint32(x) 64 | 65 | 66 | def uniform_from_uint64(x): 67 | return (x >> np.uint64(11)) * (1.0 / 9007199254740992.0) 68 | 69 | 70 | def uniform_from_uint32(x): 71 | out = np.empty(len(x) // 2) 72 | for i in range(0, len(x), 2): 73 | a = x[i] >> 5 74 | b = x[i + 1] >> 6 75 | out[i // 2] = (a * 67108864.0 + b) / 9007199254740992.0 76 | return out 77 | 78 | 79 | def uint64_from_uint63(x): 80 | out = np.empty(len(x) // 2, dtype=np.uint64) 81 | for i in range(0, len(x), 2): 82 | a = x[i] & np.uint64(0xffffffff00000000) 83 | b = x[i + 1] >> np.uint64(32) 84 | out[i // 2] = a | b 85 | return out 86 | 87 | 88 | def uniform_from_dsfmt(x): 89 | return x.view(np.double) - 1.0 90 | 91 | 92 | def gauss_from_uint(x, n, bits): 93 | if bits in (64, 63): 94 | doubles = uniform_from_uint64(x) 95 | elif bits == 32: 96 | doubles = uniform_from_uint32(x) 97 | elif bits == 'dsfmt': 98 | doubles = uniform_from_dsfmt(x) 99 | gauss = [] 100 | loc = 0 101 | x1 = x2 = 0.0 102 | while len(gauss) < n: 103 | r2 = 2 104 | while r2 >= 1.0 or r2 == 0.0: 105 | x1 = 2.0 * doubles[loc] - 1.0 106 | x2 = 2.0 * doubles[loc + 1] - 1.0 107 | r2 = x1 * x1 + x2 * x2 108 | loc += 2 109 | 110 | f = np.sqrt(-2.0 * np.log(r2) / r2) 111 | gauss.append(f * x2) 112 | gauss.append(f * x1) 113 | 114 | return gauss[:n] 115 | 116 | 117 | class Base(object): 118 | dtype = np.uint64 119 | data2 = data1 = {} 120 | 121 | @classmethod 122 | def setup_class(cls): 123 | cls.RandomState = xorshift128.RandomState 124 | cls.bits = 64 125 | cls.dtype = np.uint64 126 | cls.seed_error_type = TypeError 127 | 128 | @classmethod 129 | def _read_csv(cls, filename): 130 | with open(filename) as csv: 131 | seed = csv.readline() 132 | seed = seed.split(',') 133 | seed = [long(s) for s in seed[1:]] 134 | data = [] 135 | for line in csv: 136 | data.append(long(line.split(',')[-1])) 137 | return {'seed': seed, 'data': np.array(data, dtype=cls.dtype)} 138 | 139 | def test_raw(self): 140 | rs = self.RandomState(*self.data1['seed']) 141 | uints = rs.random_raw(1000) 142 | assert_equal(uints, self.data1['data']) 143 | 144 | rs = self.RandomState(*self.data2['seed']) 145 | uints = rs.random_raw(1000) 146 | assert_equal(uints, self.data2['data']) 147 | 148 | def test_gauss_inv(self): 149 | n = 25 150 | rs = self.RandomState(*self.data1['seed']) 151 | gauss = rs.standard_normal(n, method='bm') 152 | assert_allclose(gauss, 153 | gauss_from_uint(self.data1['data'], n, self.bits)) 154 | 155 | rs = self.RandomState(*self.data2['seed']) 156 | gauss = rs.standard_normal(25, method='bm') 157 | assert_allclose(gauss, 158 | gauss_from_uint(self.data2['data'], n, self.bits)) 159 | 160 | def test_uniform_double(self): 161 | rs = self.RandomState(*self.data1['seed']) 162 | vals = uniform_from_uint(self.data1['data'], self.bits) 163 | uniforms = rs.random_sample(len(vals)) 164 | assert_allclose(uniforms, vals) 165 | assert_equal(uniforms.dtype, np.float64) 166 | 167 | rs = self.RandomState(*self.data2['seed']) 168 | vals = uniform_from_uint(self.data2['data'], self.bits) 169 | uniforms = rs.random_sample(len(vals)) 170 | assert_allclose(uniforms, vals) 171 | assert_equal(uniforms.dtype, np.float64) 172 | 173 | def test_uniform_float(self): 174 | rs = self.RandomState(*self.data1['seed']) 175 | vals = uniform32_from_uint(self.data1['data'], self.bits) 176 | uniforms = rs.random_sample(len(vals), dtype=np.float32) 177 | assert_allclose(uniforms, vals) 178 | assert_equal(uniforms.dtype, np.float32) 179 | 180 | rs = self.RandomState(*self.data2['seed']) 181 | vals = uniform32_from_uint(self.data2['data'], self.bits) 182 | uniforms = rs.random_sample(len(vals), dtype=np.float32) 183 | assert_allclose(uniforms, vals) 184 | assert_equal(uniforms.dtype, np.float32) 185 | 186 | def test_seed_float(self): 187 | # GH #82 188 | rs = self.RandomState(*self.data1['seed']) 189 | assert_raises(self.seed_error_type, rs.seed, np.pi) 190 | assert_raises(self.seed_error_type, rs.seed, -np.pi) 191 | 192 | def test_seed_float_array(self): 193 | # GH #82 194 | rs = self.RandomState(*self.data1['seed']) 195 | assert_raises(self.seed_error_type, rs.seed, np.array([np.pi])) 196 | assert_raises(self.seed_error_type, rs.seed, np.array([-np.pi])) 197 | assert_raises(self.seed_error_type, rs.seed, np.array([np.pi, -np.pi])) 198 | assert_raises(self.seed_error_type, rs.seed, np.array([0, np.pi])) 199 | assert_raises(self.seed_error_type, rs.seed, [np.pi]) 200 | assert_raises(self.seed_error_type, rs.seed, [0, np.pi]) 201 | 202 | def test_seed_out_of_range(self): 203 | # GH #82 204 | rs = self.RandomState(*self.data1['seed']) 205 | assert_raises(ValueError, rs.seed, 2 ** (2 * self.bits+1)) 206 | assert_raises(ValueError, rs.seed, -1) 207 | 208 | def test_seed_out_of_range_array(self): 209 | # GH #82 210 | rs = self.RandomState(*self.data1['seed']) 211 | assert_raises(ValueError, rs.seed, [2 ** (2 * self.bits+1)]) 212 | assert_raises(ValueError, rs.seed, [-1]) 213 | 214 | class TestMT19937(Base, TestCase): 215 | @classmethod 216 | def setup_class(cls): 217 | cls.RandomState = mt19937.RandomState 218 | cls.bits = 32 219 | cls.dtype = np.uint32 220 | cls.data1 = cls._read_csv(join(pwd, './data/randomkit-testset-1.csv')) 221 | cls.data2 = cls._read_csv(join(pwd, './data/randomkit-testset-2.csv')) 222 | cls.seed_error_type = ValueError 223 | 224 | def test_seed_out_of_range(self): 225 | # GH #82 226 | rs = self.RandomState(*self.data1['seed']) 227 | assert_raises(ValueError, rs.seed, 2 ** (self.bits + 1)) 228 | assert_raises(ValueError, rs.seed, -1) 229 | assert_raises(ValueError, rs.seed, 2 ** (2 * self.bits+1)) 230 | 231 | def test_seed_out_of_range_array(self): 232 | # GH #82 233 | rs = self.RandomState(*self.data1['seed']) 234 | assert_raises(ValueError, rs.seed, [2 ** (self.bits + 1)]) 235 | assert_raises(ValueError, rs.seed, [-1]) 236 | assert_raises(TypeError, rs.seed, [2 ** (2 * self.bits+1)]) 237 | 238 | def test_seed_float(self): 239 | # GH #82 240 | rs = self.RandomState(*self.data1['seed']) 241 | assert_raises(TypeError, rs.seed, np.pi) 242 | assert_raises(TypeError, rs.seed, -np.pi) 243 | 244 | def test_seed_float_array(self): 245 | # GH #82 246 | rs = self.RandomState(*self.data1['seed']) 247 | assert_raises(TypeError, rs.seed, np.array([np.pi])) 248 | assert_raises(TypeError, rs.seed, np.array([-np.pi])) 249 | assert_raises(TypeError, rs.seed, np.array([np.pi, -np.pi])) 250 | assert_raises(TypeError, rs.seed, np.array([0, np.pi])) 251 | assert_raises(TypeError, rs.seed, [np.pi]) 252 | assert_raises(TypeError, rs.seed, [0, np.pi]) 253 | -------------------------------------------------------------------------------- /rng/rng/src/common/inttypes.h: -------------------------------------------------------------------------------- 1 | // ISO C9x compliant inttypes.h for Microsoft Visual Studio 2 | // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 3 | // 4 | // Copyright (c) 2006-2013 Alexander Chemeris 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // 1. Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // 12 | // 2. Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // 16 | // 3. Neither the name of the product nor the names of its contributors may 17 | // be used to endorse or promote products derived from this software 18 | // without specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 22 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 23 | // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 | // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | // 31 | /////////////////////////////////////////////////////////////////////////////// 32 | 33 | #ifndef _MSC_VER // [ 34 | #error "Use this header only with Microsoft Visual C++ compilers!" 35 | #endif // _MSC_VER ] 36 | 37 | #ifndef _MSC_INTTYPES_H_ // [ 38 | #define _MSC_INTTYPES_H_ 39 | 40 | #if _MSC_VER > 1000 41 | #pragma once 42 | #endif 43 | 44 | #include "stdint.h" 45 | 46 | // 7.8 Format conversion of integer types 47 | 48 | typedef struct { 49 | intmax_t quot; 50 | intmax_t rem; 51 | } imaxdiv_t; 52 | 53 | // 7.8.1 Macros for format specifiers 54 | 55 | #if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 56 | 57 | // The fprintf macros for signed integers are: 58 | #define PRId8 "d" 59 | #define PRIi8 "i" 60 | #define PRIdLEAST8 "d" 61 | #define PRIiLEAST8 "i" 62 | #define PRIdFAST8 "d" 63 | #define PRIiFAST8 "i" 64 | 65 | #define PRId16 "hd" 66 | #define PRIi16 "hi" 67 | #define PRIdLEAST16 "hd" 68 | #define PRIiLEAST16 "hi" 69 | #define PRIdFAST16 "hd" 70 | #define PRIiFAST16 "hi" 71 | 72 | #define PRId32 "I32d" 73 | #define PRIi32 "I32i" 74 | #define PRIdLEAST32 "I32d" 75 | #define PRIiLEAST32 "I32i" 76 | #define PRIdFAST32 "I32d" 77 | #define PRIiFAST32 "I32i" 78 | 79 | #define PRId64 "I64d" 80 | #define PRIi64 "I64i" 81 | #define PRIdLEAST64 "I64d" 82 | #define PRIiLEAST64 "I64i" 83 | #define PRIdFAST64 "I64d" 84 | #define PRIiFAST64 "I64i" 85 | 86 | #define PRIdMAX "I64d" 87 | #define PRIiMAX "I64i" 88 | 89 | #define PRIdPTR "Id" 90 | #define PRIiPTR "Ii" 91 | 92 | // The fprintf macros for unsigned integers are: 93 | #define PRIo8 "o" 94 | #define PRIu8 "u" 95 | #define PRIx8 "x" 96 | #define PRIX8 "X" 97 | #define PRIoLEAST8 "o" 98 | #define PRIuLEAST8 "u" 99 | #define PRIxLEAST8 "x" 100 | #define PRIXLEAST8 "X" 101 | #define PRIoFAST8 "o" 102 | #define PRIuFAST8 "u" 103 | #define PRIxFAST8 "x" 104 | #define PRIXFAST8 "X" 105 | 106 | #define PRIo16 "ho" 107 | #define PRIu16 "hu" 108 | #define PRIx16 "hx" 109 | #define PRIX16 "hX" 110 | #define PRIoLEAST16 "ho" 111 | #define PRIuLEAST16 "hu" 112 | #define PRIxLEAST16 "hx" 113 | #define PRIXLEAST16 "hX" 114 | #define PRIoFAST16 "ho" 115 | #define PRIuFAST16 "hu" 116 | #define PRIxFAST16 "hx" 117 | #define PRIXFAST16 "hX" 118 | 119 | #define PRIo32 "I32o" 120 | #define PRIu32 "I32u" 121 | #define PRIx32 "I32x" 122 | #define PRIX32 "I32X" 123 | #define PRIoLEAST32 "I32o" 124 | #define PRIuLEAST32 "I32u" 125 | #define PRIxLEAST32 "I32x" 126 | #define PRIXLEAST32 "I32X" 127 | #define PRIoFAST32 "I32o" 128 | #define PRIuFAST32 "I32u" 129 | #define PRIxFAST32 "I32x" 130 | #define PRIXFAST32 "I32X" 131 | 132 | #define PRIo64 "I64o" 133 | #define PRIu64 "I64u" 134 | #define PRIx64 "I64x" 135 | #define PRIX64 "I64X" 136 | #define PRIoLEAST64 "I64o" 137 | #define PRIuLEAST64 "I64u" 138 | #define PRIxLEAST64 "I64x" 139 | #define PRIXLEAST64 "I64X" 140 | #define PRIoFAST64 "I64o" 141 | #define PRIuFAST64 "I64u" 142 | #define PRIxFAST64 "I64x" 143 | #define PRIXFAST64 "I64X" 144 | 145 | #define PRIoMAX "I64o" 146 | #define PRIuMAX "I64u" 147 | #define PRIxMAX "I64x" 148 | #define PRIXMAX "I64X" 149 | 150 | #define PRIoPTR "Io" 151 | #define PRIuPTR "Iu" 152 | #define PRIxPTR "Ix" 153 | #define PRIXPTR "IX" 154 | 155 | // The fscanf macros for signed integers are: 156 | #define SCNd8 "d" 157 | #define SCNi8 "i" 158 | #define SCNdLEAST8 "d" 159 | #define SCNiLEAST8 "i" 160 | #define SCNdFAST8 "d" 161 | #define SCNiFAST8 "i" 162 | 163 | #define SCNd16 "hd" 164 | #define SCNi16 "hi" 165 | #define SCNdLEAST16 "hd" 166 | #define SCNiLEAST16 "hi" 167 | #define SCNdFAST16 "hd" 168 | #define SCNiFAST16 "hi" 169 | 170 | #define SCNd32 "ld" 171 | #define SCNi32 "li" 172 | #define SCNdLEAST32 "ld" 173 | #define SCNiLEAST32 "li" 174 | #define SCNdFAST32 "ld" 175 | #define SCNiFAST32 "li" 176 | 177 | #define SCNd64 "I64d" 178 | #define SCNi64 "I64i" 179 | #define SCNdLEAST64 "I64d" 180 | #define SCNiLEAST64 "I64i" 181 | #define SCNdFAST64 "I64d" 182 | #define SCNiFAST64 "I64i" 183 | 184 | #define SCNdMAX "I64d" 185 | #define SCNiMAX "I64i" 186 | 187 | #ifdef _WIN64 // [ 188 | # define SCNdPTR "I64d" 189 | # define SCNiPTR "I64i" 190 | #else // _WIN64 ][ 191 | # define SCNdPTR "ld" 192 | # define SCNiPTR "li" 193 | #endif // _WIN64 ] 194 | 195 | // The fscanf macros for unsigned integers are: 196 | #define SCNo8 "o" 197 | #define SCNu8 "u" 198 | #define SCNx8 "x" 199 | #define SCNX8 "X" 200 | #define SCNoLEAST8 "o" 201 | #define SCNuLEAST8 "u" 202 | #define SCNxLEAST8 "x" 203 | #define SCNXLEAST8 "X" 204 | #define SCNoFAST8 "o" 205 | #define SCNuFAST8 "u" 206 | #define SCNxFAST8 "x" 207 | #define SCNXFAST8 "X" 208 | 209 | #define SCNo16 "ho" 210 | #define SCNu16 "hu" 211 | #define SCNx16 "hx" 212 | #define SCNX16 "hX" 213 | #define SCNoLEAST16 "ho" 214 | #define SCNuLEAST16 "hu" 215 | #define SCNxLEAST16 "hx" 216 | #define SCNXLEAST16 "hX" 217 | #define SCNoFAST16 "ho" 218 | #define SCNuFAST16 "hu" 219 | #define SCNxFAST16 "hx" 220 | #define SCNXFAST16 "hX" 221 | 222 | #define SCNo32 "lo" 223 | #define SCNu32 "lu" 224 | #define SCNx32 "lx" 225 | #define SCNX32 "lX" 226 | #define SCNoLEAST32 "lo" 227 | #define SCNuLEAST32 "lu" 228 | #define SCNxLEAST32 "lx" 229 | #define SCNXLEAST32 "lX" 230 | #define SCNoFAST32 "lo" 231 | #define SCNuFAST32 "lu" 232 | #define SCNxFAST32 "lx" 233 | #define SCNXFAST32 "lX" 234 | 235 | #define SCNo64 "I64o" 236 | #define SCNu64 "I64u" 237 | #define SCNx64 "I64x" 238 | #define SCNX64 "I64X" 239 | #define SCNoLEAST64 "I64o" 240 | #define SCNuLEAST64 "I64u" 241 | #define SCNxLEAST64 "I64x" 242 | #define SCNXLEAST64 "I64X" 243 | #define SCNoFAST64 "I64o" 244 | #define SCNuFAST64 "I64u" 245 | #define SCNxFAST64 "I64x" 246 | #define SCNXFAST64 "I64X" 247 | 248 | #define SCNoMAX "I64o" 249 | #define SCNuMAX "I64u" 250 | #define SCNxMAX "I64x" 251 | #define SCNXMAX "I64X" 252 | 253 | #ifdef _WIN64 // [ 254 | # define SCNoPTR "I64o" 255 | # define SCNuPTR "I64u" 256 | # define SCNxPTR "I64x" 257 | # define SCNXPTR "I64X" 258 | #else // _WIN64 ][ 259 | # define SCNoPTR "lo" 260 | # define SCNuPTR "lu" 261 | # define SCNxPTR "lx" 262 | # define SCNXPTR "lX" 263 | #endif // _WIN64 ] 264 | 265 | #endif // __STDC_FORMAT_MACROS ] 266 | 267 | // 7.8.2 Functions for greatest-width integer types 268 | 269 | // 7.8.2.1 The imaxabs function 270 | #define imaxabs _abs64 271 | 272 | // 7.8.2.2 The imaxdiv function 273 | 274 | // This is modified version of div() function from Microsoft's div.c found 275 | // in %MSVC.NET%\crt\src\div.c 276 | #ifdef STATIC_IMAXDIV // [ 277 | static 278 | #else // STATIC_IMAXDIV ][ 279 | _inline 280 | #endif // STATIC_IMAXDIV ] 281 | imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) 282 | { 283 | imaxdiv_t result; 284 | 285 | result.quot = numer / denom; 286 | result.rem = numer % denom; 287 | 288 | if (numer < 0 && result.rem > 0) { 289 | // did division wrong; must fix up 290 | ++result.quot; 291 | result.rem -= denom; 292 | } 293 | 294 | return result; 295 | } 296 | 297 | // 7.8.2.3 The strtoimax and strtoumax functions 298 | #define strtoimax _strtoi64 299 | #define strtoumax _strtoui64 300 | 301 | // 7.8.2.4 The wcstoimax and wcstoumax functions 302 | #define wcstoimax _wcstoi64 303 | #define wcstoumax _wcstoui64 304 | 305 | 306 | #endif // _MSC_INTTYPES_H_ ] -------------------------------------------------------------------------------- /02-cython-comparison.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# How does Cython speed up Python?" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Reason 1: Interpreted -> Compiled\n", 15 | "\n", 16 | "## Cython version of trivial function" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "%load_ext Cython" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "%%cython -n cyfoo\n", 37 | "\n", 38 | "def cyfoo(a, b):\n", 39 | " return a + b" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "## Profiling" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "%timeit cyfoo(1, 2)" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "import sys\n", 65 | "sys.modules['cyfoo']" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "print(\"Cython integer addition speedup: {:0.1f}%\".format((112. - 79.) / 112. * 100))" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "%timeit cyfoo('a', 'b')" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "print(\"Cython string addition speedup: {:0.1f}%\".format((159. - 133.) / 159. * 100))" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "### For simple addition, Cython version gives consistent speedup\n", 100 | "\n", 101 | "* With all the caveats for microbenchmarks..." 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "## We see the same `PyNumber_Add()` entry point as for interpreted Python" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "!cat /home/jovyan/.cache/ipython/cython/cyfoo.c | nl" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "\n", 125 | "\n", 126 | "```c\n", 127 | "static PyObject \n", 128 | "*__pyx_pf_5cyfoo_cyfoo(CYTHON_UNUSED PyObject *__pyx_self,\n", 129 | " PyObject *__pyx_v_a,\n", 130 | " PyObject *__pyx_v_b) {\n", 131 | "[...]\n", 132 | " /* \"cyfoo.pyx\":3\n", 133 | " * \n", 134 | " * def cyfoo(a, b):\n", 135 | " * return a + b # <<<<<<<<<<<<<<\n", 136 | " */\n", 137 | " __pyx_t_1 = PyNumber_Add(__pyx_v_a, __pyx_v_b);\n", 138 | " [...]\n", 139 | "}\n", 140 | "```" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "## We conclude: converting from interpreted to compiled code gives some speedup" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "## Reason 2: Dynamic -> Static Typing" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": { 161 | "collapsed": true 162 | }, 163 | "outputs": [], 164 | "source": [ 165 | "def pyfac(n):\n", 166 | " if n <= 1:\n", 167 | " return 1\n", 168 | " return n * pyfac(n - 1)" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": null, 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "%timeit pyfac(20.0)\n", 178 | "pyfac(20.0)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": { 185 | "collapsed": true 186 | }, 187 | "outputs": [], 188 | "source": [ 189 | "%%cython\n", 190 | "\n", 191 | "def cyfac(n):\n", 192 | " if n <= 1:\n", 193 | " return 1\n", 194 | " return n * cyfac(n - 1)\n", 195 | "\n", 196 | "def cyfac_double(double n):\n", 197 | " if n <= 1:\n", 198 | " return 1.0\n", 199 | " return n * cyfac_double(n - 1)" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": null, 205 | "metadata": {}, 206 | "outputs": [], 207 | "source": [ 208 | "%timeit cyfac(20.0)\n", 209 | "cyfac(20.0)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "%timeit cyfac_double(20.0)\n", 219 | "cyfac_double(20.0)" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "## Optimal Cython solution: up to 40x speedup\n", 227 | "\n", 228 | "* Optimal for *this* recursive implementation..." 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "metadata": { 235 | "collapsed": true 236 | }, 237 | "outputs": [], 238 | "source": [ 239 | "%%cython\n", 240 | "\n", 241 | "cpdef double cyfac_double_fast(double n):\n", 242 | " if n <= 1:\n", 243 | " return 1.0\n", 244 | " return n * cyfac_double_fast(n - 1)" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "%timeit cyfac_double_fast(20.0)\n", 254 | "cyfac_double_fast(20.0)" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "## For the record: what about a loop-based version?" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": null, 267 | "metadata": { 268 | "collapsed": true 269 | }, 270 | "outputs": [], 271 | "source": [ 272 | "def pyfac_loop(n):\n", 273 | " r = 1.0\n", 274 | " for i in range(1, n+1):\n", 275 | " r *= i\n", 276 | " return r" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": null, 282 | "metadata": {}, 283 | "outputs": [], 284 | "source": [ 285 | "%timeit pyfac_loop(20)\n", 286 | "pyfac_loop(20)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "%%cython\n", 296 | "\n", 297 | "cpdef double cyfac_loop(int n):\n", 298 | " cdef double r = 1.0\n", 299 | " cdef int i\n", 300 | " for i in range(1, n+1):\n", 301 | " r *= i\n", 302 | " return r" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [ 311 | "%timeit cyfac_loop(20)\n", 312 | "cyfac_loop(20)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "print(\"Cython speedup factor--loop-based version: {:0.1f}\".format((1.81 / 0.062)))" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "## Excercises / questions\n", 329 | "\n", 330 | "* Why are we using `double` here instead of `long`?\n", 331 | "* Why are the `pyfac_loop()` and `cyfac_loop()` versions *better* from a robustness pov?\n", 332 | "* Write a trivial no-op function in Python and measure its performance w/ `timeit`. Now, make a Cython no-op `def` function, and measure *it*. How do they compare? Conjecture why. What does this imply for function call overhead between pure Python and Cython code?" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": null, 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [ 341 | "def pynoop(): pass\n", 342 | "%timeit pynoop()" 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": null, 348 | "metadata": {}, 349 | "outputs": [], 350 | "source": [ 351 | "%%cython\n", 352 | "def cynoop(): pass" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": null, 358 | "metadata": {}, 359 | "outputs": [], 360 | "source": [ 361 | "%timeit cynoop()" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "metadata": { 368 | "collapsed": true 369 | }, 370 | "outputs": [], 371 | "source": [] 372 | } 373 | ], 374 | "metadata": { 375 | "kernelspec": { 376 | "display_name": "Python 3", 377 | "language": "python", 378 | "name": "python3" 379 | }, 380 | "language_info": { 381 | "codemirror_mode": { 382 | "name": "ipython", 383 | "version": 3 384 | }, 385 | "file_extension": ".py", 386 | "mimetype": "text/x-python", 387 | "name": "python", 388 | "nbconvert_exporter": "python", 389 | "pygments_lexer": "ipython3", 390 | "version": "3.6.1" 391 | } 392 | }, 393 | "nbformat": 4, 394 | "nbformat_minor": 2 395 | } 396 | -------------------------------------------------------------------------------- /rng/README.md: -------------------------------------------------------------------------------- 1 | # randomstate 2 | 3 | [![Travis Build Status](https://travis-ci.org/bashtage/ng-numpy-randomstate.svg?branch=master)](https://travis-ci.org/bashtage/ng-numpy-randomstate) 4 | [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/odc5c4ukhru5xicl/branch/master?svg=true)](https://ci.appveyor.com/project/bashtage/ng-numpy-randomstate/branch/master) 5 | [![PyPI version](https://badge.fury.io/py/randomstate.svg)](https://badge.fury.io/py/randomstate) 6 | 7 | This is a library and generic interface for alternative random 8 | generators in Python and NumPy. 9 | 10 | ## Features 11 | 12 | * Immediate drop in replacement for NumPy's RandomState 13 | 14 | ```python 15 | # import numpy.random as rnd 16 | import randomstate as rnd 17 | x = rnd.standard_normal(100) 18 | y = rnd.random_sample(100) 19 | z = rnd.randn(10,10) 20 | ``` 21 | 22 | * Default random generator is identical to NumPy's RandomState (i.e., 23 | same seed, same random numbers). 24 | * Support for random number generators that support independent streams 25 | and jumping ahead so that substreams can be generated 26 | * Faster random number generation, especially for Normals using the 27 | Ziggurat method 28 | 29 | ```python 30 | import randomstate as rnd 31 | w = rnd.standard_normal(10000, method='zig') 32 | ``` 33 | 34 | * Support for 32-bit floating randoms for core generators. 35 | Currently supported: 36 | 37 | * Uniforms (`random_sample`) 38 | * Exponentials (`standard_exponential`) 39 | * Normals (`standard_normal`, both Box-Muller and Ziggurat) 40 | * Standard Gammas (via `standard_gamma`) 41 | 42 | **WARNING**: The 32-bit generators are **experimental** and subject 43 | to change. 44 | 45 | **Note**: There are _no_ plans to extend the alternative precision 46 | generation to all random number types. 47 | 48 | * Support for filling existing arrays using `out` keyword argument. Currently 49 | supported in (both 32- and 64-bit outputs) 50 | 51 | * Uniforms (`random_sample`) 52 | * Exponentials (`standard_exponential`) 53 | * Normals (`standard_normal`) 54 | * Standard Gammas (via `standard_gamma`) 55 | 56 | ## Included Pseudo Random Number Generators 57 | 58 | This modules includes a number of alternative random 59 | number generators in addition to the MT19937 that is included in NumPy. 60 | The RNGs include: 61 | 62 | * [MT19937](https://github.com/numpy/numpy/blob/master/numpy/random/mtrand/), 63 | the NumPy rng 64 | * [dSFMT](http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/) a 65 | SSE2-aware version of the MT19937 generator that is especially fast at 66 | generating doubles 67 | * [xorshift128+](http://xorshift.di.unimi.it/), 68 | [xoroshiro128+](http://xoroshiro.di.unimi.it/) and 69 | [xorshift1024*](http://xorshift.di.unimi.it/) 70 | * [PCG32](http://www.pcg-random.org/) and 71 | [PCG64](http:w//www.pcg-random.org/) 72 | * [MRG32K3A](http://simul.iro.umontreal.ca/rng) 73 | * A multiplicative lagged fibonacci generator (LFG(63, 1279, 861, *)) 74 | 75 | ## Differences from `numpy.random.RandomState` 76 | 77 | ### New Features 78 | * `standard_normal`, `normal`, `randn` and `multivariate_normal` all 79 | support an additional `method` keyword argument which can be `bm` or 80 | `zig` where `bm` corresponds to the current method using the Box-Muller 81 | transformation and `zig` uses the much faster (100%+) Ziggurat method. 82 | * Core random number generators can produce either single precision 83 | (`np.float32`) or double precision (`np.float64`, the default) using 84 | an the optional keyword argument `dtype` 85 | * Core random number generators can fill existing arrays using the 86 | `out` keyword argument 87 | 88 | 89 | ### New Functions 90 | 91 | * `random_entropy` - Read from the system entropy provider, which is 92 | commonly used in cryptographic applications 93 | * `random_raw` - Direct access to the values produced by the underlying 94 | PRNG. The range of the values returned depends on the specifics of the 95 | PRNG implementation. 96 | * `random_uintegers` - unsigned integers, either 32- (`[0, 2**32-1]`) 97 | or 64-bit (`[0, 2**64-1]`) 98 | * `jump` - Jumps RNGs that support it. `jump` moves the state a great 99 | distance. _Only available if supported by the RNG._ 100 | * `advance` - Advanced the core RNG 'as-if' a number of draws were made, 101 | without actually drawing the numbers. _Only available if supported by 102 | the RNG._ 103 | 104 | ## Status 105 | 106 | * Complete drop-in replacement for `numpy.random.RandomState`. The 107 | `mt19937` generator is identical to `numpy.random.RandomState`, and 108 | will produce an identical sequence of random numbers for a given seed. 109 | * Builds and passes all tests on: 110 | * Linux 32/64 bit, Python 2.7, 3.4, 3.5, 3.6 (probably works on 2.6 and 3.3) 111 | * PC-BSD (FreeBSD) 64-bit, Python 2.7 112 | * OSX 64-bit, Python 2.7 113 | * Windows 32/64 bit (only tested on Python 2.7 and 3.5, but should 114 | work on 3.3/3.4) 115 | 116 | ## Version 117 | The version matched the latest version of NumPy where 118 | `randomstate.prng.mt19937` passes all NumPy test. 119 | 120 | ## Documentation 121 | 122 | An occasionally updated build of the documentation is available on 123 | [my github pages](http://bashtage.github.io/ng-numpy-randomstate/). 124 | 125 | ## Plans 126 | This module is essentially complete. There are a few rough edges that 127 | need to be smoothed. 128 | 129 | * Stream support for MLFG 130 | * Creation of additional streams from a RandomState where supported 131 | (i.e. a `next_stream()` method) 132 | 133 | ## Requirements 134 | Building requires: 135 | 136 | * Python (2.7, 3.4, 3.5, 3.6) 137 | * NumPy (1.9, 1.10, 1.11, 1.12) 138 | * Cython (0.22, 0.23, 0.24, 0.25) 139 | * tempita (0.5+), if not provided by Cython 140 | 141 | Testing requires nose (1.3+). 142 | 143 | **Note:** it might work with other versions but only tested with these 144 | versions. 145 | 146 | ## Development and Testing 147 | 148 | All development has been on 64-bit Linux, and it is regularly tested on 149 | Travis-CI. The library is occasionally tested on Linux 32-bit, 150 | OSX 10.10, PC-BSD 10.2 (should also work on Free BSD) and Windows 151 | (Python 2.7/3.5, both 32 and 64-bit). 152 | 153 | Basic tests are in place for all RNGs. The MT19937 is tested against 154 | NumPy's implementation for identical results. It also passes NumPy's 155 | test suite. 156 | 157 | ## Installing 158 | 159 | ```bash 160 | python setup.py install 161 | ``` 162 | 163 | ### SSE2 164 | `dSFTM` makes use of SSE2 by default. If you have a very old computer 165 | or are building on non-x86, you can install using: 166 | 167 | ```bash 168 | python setup.py install --no-sse2 169 | ``` 170 | 171 | ### Windows 172 | Either use a binary installer, or if building from scratch, use 173 | Python 3.5 with Visual Studio 2015 Community Edition. It can also be 174 | build using Microsoft Visual C++ Compiler for Python 2.7 and Python 2.7, 175 | although some modifications may be needed to `distutils` to find the 176 | compiler. 177 | 178 | ## Using 179 | 180 | The separate generators are importable from `randomstate.prng`. 181 | 182 | ```python 183 | import randomstate 184 | rs = randomstate.prng.xorshift128.RandomState() 185 | rs.random_sample(100) 186 | 187 | rs = randomstate.prng.pcg64.RandomState() 188 | rs.random_sample(100) 189 | 190 | # Identical to NumPy 191 | rs = randomstate.prng.mt19937.RandomState() 192 | rs.random_sample(100) 193 | ``` 194 | 195 | Like NumPy, `randomstate` also exposes a single instance of the 196 | `mt19937` generator directly at the module level so that commands like 197 | 198 | ```python 199 | import randomstate 200 | randomstate.standard_normal() 201 | randomstate.exponential(1.0, 1.0, size=10) 202 | ``` 203 | 204 | will work. 205 | 206 | ## License 207 | Standard NCSA, plus sub licenses for components. 208 | 209 | ## Performance 210 | Performance is promising, and even the mt19937 seems to be faster than 211 | NumPy's mt19937. 212 | 213 | ``` 214 | Speed-up relative to NumPy (Uniform Doubles) 215 | ************************************************************ 216 | randomstate.prng-dsfmt-random_sample 313.5% 217 | randomstate.prng-mlfg_1279_861-random_sample 459.4% 218 | randomstate.prng-mrg32k3a-random_sample -57.6% 219 | randomstate.prng-mt19937-random_sample 72.5% 220 | randomstate.prng-pcg32-random_sample 232.8% 221 | randomstate.prng-pcg64-random_sample 330.6% 222 | randomstate.prng-xoroshiro128plus-random_sample 609.9% 223 | randomstate.prng-xorshift1024-random_sample 348.8% 224 | randomstate.prng-xorshift128-random_sample 489.7% 225 | 226 | Speed-up relative to NumPy (Normals using Box-Muller) 227 | ************************************************************ 228 | randomstate.prng-dsfmt-standard_normal 26.8% 229 | randomstate.prng-mlfg_1279_861-standard_normal 30.9% 230 | randomstate.prng-mrg32k3a-standard_normal -14.8% 231 | randomstate.prng-mt19937-standard_normal 17.7% 232 | randomstate.prng-pcg32-standard_normal 24.5% 233 | randomstate.prng-pcg64-standard_normal 26.2% 234 | randomstate.prng-xoroshiro128plus-standard_normal 31.4% 235 | randomstate.prng-xorshift1024-standard_normal 27.4% 236 | randomstate.prng-xorshift128-standard_normal 30.3% 237 | 238 | Speed-up relative to NumPy (Normals using Ziggurat) 239 | ************************************************************ 240 | randomstate.prng-dsfmt-standard_normal 491.7% 241 | randomstate.prng-mlfg_1279_861-standard_normal 439.6% 242 | randomstate.prng-mrg32k3a-standard_normal 101.2% 243 | randomstate.prng-mt19937-standard_normal 354.4% 244 | randomstate.prng-pcg32-standard_normal 531.0% 245 | randomstate.prng-pcg64-standard_normal 517.9% 246 | randomstate.prng-xoroshiro128plus-standard_normal 674.0% 247 | randomstate.prng-xorshift1024-standard_normal 486.7% 248 | randomstate.prng-xorshift128-standard_normal 617.0% 249 | ``` -------------------------------------------------------------------------------- /01-python-slow.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Why is Python so slow\\*?\n", 8 | "\n", 9 | "\\* relative to other languages, for interpreted, dynamically typed code, etc, etc." 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Tracing CPython code execution" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": { 23 | "collapsed": true 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "def foo(a, b):\n", 28 | " return a + b" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "Let's disassemble it with the `dis` module:" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "from dis import dis\n", 45 | "dis(foo)" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "### What's `BINARY_ADD`?\n", 53 | "\n", 54 | "We crack open CPython's source code and take a look inside `Python/ceval.c`:\n", 55 | "\n", 56 | "```c\n", 57 | "/* Python/ceval.c */\n", 58 | "TARGET(BINARY_ADD) {\n", 59 | " PyObject *right = POP();\n", 60 | " PyObject *left = TOP();\n", 61 | " PyObject *sum;\n", 62 | " /* NOTE(haypo): Please don't try to micro-optimize int+int on\n", 63 | " CPython using bytecode, it is simply worthless.\n", 64 | " See http://bugs.python.org/issue21955 and\n", 65 | " http://bugs.python.org/issue10044 for the discussion. In short,\n", 66 | " no patch shown any impact on a realistic benchmark, only a minor\n", 67 | " speedup on microbenchmarks. */\n", 68 | " if (PyUnicode_CheckExact(left) &&\n", 69 | " PyUnicode_CheckExact(right)) {\n", 70 | " sum = unicode_concatenate(left, right, f, next_instr);\n", 71 | " /* unicode_concatenate consumed the ref to left */\n", 72 | " }\n", 73 | " else {\n", 74 | " sum = PyNumber_Add(left, right); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 75 | " Py_DECREF(left);\n", 76 | " }\n", 77 | " Py_DECREF(right);\n", 78 | " SET_TOP(sum);\n", 79 | " if (sum == NULL)\n", 80 | " goto error;\n", 81 | " DISPATCH();\n", 82 | "}\n", 83 | "```" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "### What's `PyNumber_Add(left, right)`?\n", 91 | "\n", 92 | "```c\n", 93 | "/* Objects/abstract.c */\n", 94 | "PyObject *\n", 95 | "PyNumber_Add(PyObject *v, PyObject *w)\n", 96 | "{\n", 97 | " PyObject *result = binary_op1(v, w, NB_SLOT(nb_add)); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 98 | " if (result == Py_NotImplemented) {\n", 99 | " PySequenceMethods *m = v->ob_type->tp_as_sequence;\n", 100 | " Py_DECREF(result);\n", 101 | " if (m && m->sq_concat) {\n", 102 | " return (*m->sq_concat)(v, w);\n", 103 | " }\n", 104 | " result = binop_type_error(v, w, \"+\");\n", 105 | " }\n", 106 | " return result;\n", 107 | "}\n", 108 | "```" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "### What's `binary_op1()`?\n", 116 | "\n", 117 | "```c\n", 118 | "static PyObject *\n", 119 | "binary_op1(PyObject *v, PyObject *w, const int op_slot)\n", 120 | "{\n", 121 | " PyObject *x;\n", 122 | " binaryfunc slotv = NULL;\n", 123 | " binaryfunc slotw = NULL;\n", 124 | "\n", 125 | " if (v->ob_type->tp_as_number != NULL)\n", 126 | " slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot);\n", 127 | " if (w->ob_type != v->ob_type &&\n", 128 | " w->ob_type->tp_as_number != NULL) {\n", 129 | " slotw = NB_BINOP(w->ob_type->tp_as_number, op_slot); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 130 | " if (slotw == slotv)\n", 131 | " slotw = NULL;\n", 132 | " }\n", 133 | " if (slotv) {\n", 134 | " if (slotw && PyType_IsSubtype(w->ob_type, v->ob_type)) {\n", 135 | " x = slotw(v, w);\n", 136 | " if (x != Py_NotImplemented)\n", 137 | " return x;\n", 138 | " Py_DECREF(x); /* can't do it */\n", 139 | " slotw = NULL;\n", 140 | " }\n", 141 | " x = slotv(v, w);\n", 142 | " if (x != Py_NotImplemented)\n", 143 | " return x;\n", 144 | " Py_DECREF(x); /* can't do it */\n", 145 | " }\n", 146 | " if (slotw) {\n", 147 | " x = slotw(v, w);\n", 148 | " if (x != Py_NotImplemented)\n", 149 | " return x;\n", 150 | " Py_DECREF(x); /* can't do it */\n", 151 | " }\n", 152 | " Py_RETURN_NOTIMPLEMENTED;\n", 153 | "}\n", 154 | "```" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "### What's `NB_BINOP()`?\n", 162 | "\n", 163 | "```c\n", 164 | "#define NB_BINOP(nb_methods, slot) \\\n", 165 | " (*(binaryfunc*)(& ((char*)nb_methods)[slot]))\n", 166 | "```" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "### Cut to the chase: where's the addition function for two ints (longs)?\n", 174 | "\n", 175 | "```c\n", 176 | "/* Objects/longobject.c */\n", 177 | "static PyObject *\n", 178 | "long_add(PyLongObject *a, PyLongObject *b)\n", 179 | "{\n", 180 | " PyLongObject *z;\n", 181 | "\n", 182 | " CHECK_BINOP(a, b);\n", 183 | "\n", 184 | " if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b)) <= 1) {\n", 185 | " return PyLong_FromLong(MEDIUM_VALUE(a) + MEDIUM_VALUE(b)); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 186 | " }\n", 187 | " if (Py_SIZE(a) < 0) {\n", 188 | " if (Py_SIZE(b) < 0) {\n", 189 | " z = x_add(a, b);\n", 190 | " if (z != NULL) {\n", 191 | " /* x_add received at least one multiple-digit int,\n", 192 | " and thus z must be a multiple-digit int.\n", 193 | " That also means z is not an element of\n", 194 | " small_ints, so negating it in-place is safe. */\n", 195 | " assert(Py_REFCNT(z) == 1);\n", 196 | " Py_SIZE(z) = -(Py_SIZE(z));\n", 197 | " }\n", 198 | " }\n", 199 | " else\n", 200 | " z = x_sub(b, a);\n", 201 | " }\n", 202 | " else {\n", 203 | " if (Py_SIZE(b) < 0)\n", 204 | " z = x_sub(a, b);\n", 205 | " else\n", 206 | " z = x_add(a, b);\n", 207 | " }\n", 208 | " return (PyObject *)z;\n", 209 | "}\n", 210 | "```" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "### What's `MEDIUM_VALUE()`?\n", 218 | "\n", 219 | "```c\n", 220 | "/* Objects/longobject.c */\n", 221 | "#define MEDIUM_VALUE(x) (assert(-1 <= Py_SIZE(x) && Py_SIZE(x) <= 1), \\\n", 222 | " Py_SIZE(x) < 0 ? -(sdigit)(x)->ob_digit[0] : \\\n", 223 | " (Py_SIZE(x) == 0 ? (sdigit)0 : \\\n", 224 | " (sdigit)(x)->ob_digit[0]))\n", 225 | "```" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "## Why so much code for such a simple operation?\n", 233 | "\n", 234 | "* C-level is interpreting bytecodes (`BINARY_ADD` in this case).\n", 235 | "* Polymorphism -- code can handle `foo('a', 'b')` or any types that support `+`.\n", 236 | "* Works for user-defined types, too, with an `__add__` or `__radd__` magic method.\n", 237 | "* Error checking everywhere...\n", 238 | "* For adding ints, does overflow checking and conversions, etc.\n", 239 | "\n", 240 | "All these features mean a lot of code at the C level." 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "## What is the performance?" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": {}, 254 | "outputs": [], 255 | "source": [ 256 | "%timeit foo(1, 2)" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "%timeit foo('a', 'b')" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "## Summary\n", 273 | "\n", 274 | "### CPython slowdown 1: interpreted bytecode execution\n", 275 | "\n", 276 | "* Fetching CPython bytecode ops, managing the stack machine, all ops in `ceval.c`.\n", 277 | "* Extensive error checking and handling.\n", 278 | "\n", 279 | "### CPython slowdown 2: dynamic type resolution\n", 280 | "\n", 281 | "* Type introspection, dynamic dispatch on every operation / method call, supporting generic operations.\n", 282 | "* And extensive error checking and handling, up-and-down the call stack." 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "How does Cython speed it up? [02-cython-comparison.ipynb](02-cython-comparison.ipynb)" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "metadata": { 296 | "collapsed": true 297 | }, 298 | "outputs": [], 299 | "source": [] 300 | } 301 | ], 302 | "metadata": { 303 | "kernelspec": { 304 | "display_name": "Python 3", 305 | "language": "python", 306 | "name": "python3" 307 | }, 308 | "language_info": { 309 | "codemirror_mode": { 310 | "name": "ipython", 311 | "version": 3 312 | }, 313 | "file_extension": ".py", 314 | "mimetype": "text/x-python", 315 | "name": "python", 316 | "nbconvert_exporter": "python", 317 | "pygments_lexer": "ipython3", 318 | "version": "3.6.1" 319 | } 320 | }, 321 | "nbformat": 4, 322 | "nbformat_minor": 2 323 | } 324 | -------------------------------------------------------------------------------- /03-cython-types.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## The Cython language\n", 8 | "\n", 9 | "* The Cython language is a superset of the Python language -- nearly any Python code is valid Cython code, with very few edge cases.\n", 10 | "\n", 11 | "* Cython is a [_creole programming language_](https://en.wikipedia.org/wiki/Creole_language) -- a stable (programming) language developed from a mixture of different languages. In this case, the component languages are Python, C, and C++, with a few cython-specific constructs.\n", 12 | "\n", 13 | "* It is designed to make it easy to write compiled CPython extension modules, and to fluidly mix in C and C++ constructs." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## Brief tour of Cython types\n", 21 | "\n", 22 | "* Regular python variables are dynamically typed, and work just fine in Cython.\n", 23 | "* Dynamically typed variables are simply Cython `object` types.\n", 24 | "* Cython introduces the `cdef` keyword to declare a C-level construct.\n", 25 | "* Cython supports the rest of the C type system--almost all beginning with `cdef` or a variant. We won't be covering all of these." 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Simple types" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": { 39 | "collapsed": true 40 | }, 41 | "outputs": [], 42 | "source": [ 43 | "%load_ext Cython" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "%%cython\n", 53 | "\n", 54 | "# untyped objects are simply Python declarations like we know and love...\n", 55 | "o = 1\n", 56 | "print(o)\n", 57 | "\n", 58 | "# and can be reassigned to a different type...\n", 59 | "o = 'a'\n", 60 | "\n", 61 | "# This print() function happens at extension module import time.\n", 62 | "print(o)" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "## Integral types" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "%%cython\n", 79 | "\n", 80 | "# The panoply of C integer types and their modifiers...\n", 81 | "# We typically get away with using just ints and longs...\n", 82 | "# https://en.wikipedia.org/wiki/C_data_types\n", 83 | "cdef:\n", 84 | " int i = 0\n", 85 | " unsigned long j = 1\n", 86 | " signed short k = -3\n", 87 | " long long ll = 1LL\n", 88 | " bint flag = True\n", 89 | "\n", 90 | "print(i, j, k, ll, flag)" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "### Cython enforces static typing (either compile time or runtime, depending)" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "%%cython\n", 107 | "\n", 108 | "cdef int i = 0\n", 109 | "\n", 110 | "# ...\n", 111 | "\n", 112 | "i = 'qwerty'" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "## Floating point types" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "%%cython\n", 129 | "\n", 130 | "# doubles are preferred for compatibility with Python `float` types\n", 131 | "# https://docs.python.org/3/c-api/float.html\n", 132 | "\n", 133 | "cdef:\n", 134 | " float a = 1.0\n", 135 | " double b = -1.0\n", 136 | " long double c = 1e100\n", 137 | "print(a, b, c)" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "## Complex types" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "%%cython\n", 154 | "\n", 155 | "# `double complex` is preferred for compatibility with Python's `complex` type:\n", 156 | "# https://docs.python.org/3/c-api/complex.html\n", 157 | "\n", 158 | "cdef:\n", 159 | " float complex fc = 1+1j\n", 160 | " double complex dc = 1+1j\n", 161 | " long double complex ldc = 1+1j\n", 162 | "\n", 163 | "print(fc, dc, ldc)\n", 164 | "print(fc.real, dc.imag, ldc.conjugate())" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "## String types\n", 172 | "\n", 173 | "* Note that string types in Python are complicated by Py2 / Py3 differences.\n", 174 | "* This tutorial is entirely in Python 3, so we do not have to go into the minutiae here.\n", 175 | "* See [the fine manual](http://cython.readthedocs.io/en/latest/src/tutorial/strings.html#python-string-types-in-cython-code) or [other documentation](http://shop.oreilly.com/product/0636920033431.do) for more details." 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "### Statically declaring Python string types in Cython" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "metadata": {}, 189 | "outputs": [], 190 | "source": [ 191 | "%%cython\n", 192 | "\n", 193 | "cdef str s = \"asdf\"\n", 194 | "cdef bytes b = b\"jkl;\"\n", 195 | "print(s, b)" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "##### Cython allows static declaration of Python types!" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "### C \"string\"s" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "%%cython\n", 219 | "\n", 220 | "bb = b\"asdf\"\n", 221 | "cdef char *buf = bb\n", 222 | "print(bb, buf)" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "#### Pop quiz:\n", 230 | "\n", 231 | "* What can we infer from the (runtime) warning generated here?\n", 232 | " ```\n", 233 | " warning: [...] Obtaining 'char *' from externally modifiable global Python value\n", 234 | " ```\n", 235 | "* When working with C-level strings, what must we keep in mind to maintain Python guarantees?\n", 236 | "\n", 237 | "**Strong recommendation**: Work with `str` and `bytes` objects only; avoid `char *` buffers unless you're interfacing with external C code." 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "%%cython\n", 247 | "\n", 248 | "# Why is this very, very bad?\n", 249 | "a = b'a'\n", 250 | "b = b'b'\n", 251 | "cdef char *c = a + b\n", 252 | "\n", 253 | "# ...and how can we address it?\n", 254 | "\n", 255 | "# Again, it's best to avoid using char *'s unless absolutely necessary -- keep your\n", 256 | "# Strings Python-level strings, and all will be copacetic." 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "## Other statically-declarable Python types" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "%%cython\n", 273 | "\n", 274 | "import datetime\n", 275 | "cimport cpython.datetime # We'll cover the `cimport` keyword later\n", 276 | "\n", 277 | "import array\n", 278 | "cimport cpython.array\n", 279 | "\n", 280 | "cdef:\n", 281 | " list lst = [1]\n", 282 | " dict dd = {'a': 'b'}\n", 283 | " set ss = set([1])\n", 284 | " frozenset fs = frozenset([1])\n", 285 | " cpython.datetime.datetime dt = datetime.datetime.now()\n", 286 | " cpython.array.array aa = array.array('i', [1, 2, 3])\n", 287 | " \n", 288 | "print(lst, dd, ss, fs, dt, aa)" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "### Nota Bene:\n", 296 | "\n", 297 | "* Everything on the LHS of the declaration is a C-level, statically typed entity.\n", 298 | "* Everything on the RHS of the declaration is a Python object of the corresponding type.\n", 299 | "* Cython enforces the static type at compile time -- a `list` variable can only ever be assigned to a `list` (or subclass)." 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "## Declaring and manipulating pointers\n", 307 | "\n", 308 | "**NOTE** Only do this if you have to -- fortunately we rarely have to drop to using or manipulating pointers in Cython unless we're interfacing with a C library." 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": null, 314 | "metadata": {}, 315 | "outputs": [], 316 | "source": [ 317 | "%%cython\n", 318 | "\n", 319 | "cdef:\n", 320 | " int *a = NULL\n", 321 | " int b = 42\n", 322 | "\n", 323 | "# Point `a` at `b`\n", 324 | "a = &b\n", 325 | "\n", 326 | "# Dereference `a` -- Note the use of `a[0]` -- we don't use `*a` to dereference!\n", 327 | "print(a[0], b)\n", 328 | "\n", 329 | "# Modify b via a:\n", 330 | "a[0] = 137\n", 331 | "\n", 332 | "# Confirm that both refer to same value:\n", 333 | "print(a[0], b)" 334 | ] 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": null, 339 | "metadata": { 340 | "collapsed": true 341 | }, 342 | "outputs": [], 343 | "source": [] 344 | } 345 | ], 346 | "metadata": { 347 | "kernelspec": { 348 | "display_name": "Python 3", 349 | "language": "python", 350 | "name": "python3" 351 | }, 352 | "language_info": { 353 | "codemirror_mode": { 354 | "name": "ipython", 355 | "version": 3 356 | }, 357 | "file_extension": ".py", 358 | "mimetype": "text/x-python", 359 | "name": "python", 360 | "nbconvert_exporter": "python", 361 | "pygments_lexer": "ipython3", 362 | "version": "3.6.1" 363 | } 364 | }, 365 | "nbformat": 4, 366 | "nbformat_minor": 2 367 | } 368 | -------------------------------------------------------------------------------- /rng/README.rst: -------------------------------------------------------------------------------- 1 | randomstate 2 | =========== 3 | 4 | |Travis Build Status| |Appveyor Build Status| |PyPI version| 5 | 6 | This is a library and generic interface for alternative random 7 | generators in Python and NumPy. 8 | 9 | Features 10 | -------- 11 | 12 | - Immediate drop in replacement for NumPy's RandomState 13 | 14 | .. code:: python 15 | 16 | # import numpy.random as rnd 17 | import randomstate as rnd 18 | x = rnd.standard_normal(100) 19 | y = rnd.random_sample(100) 20 | z = rnd.randn(10,10) 21 | 22 | - Default random generator is identical to NumPy's RandomState (i.e., 23 | same seed, same random numbers). 24 | - Support for random number generators that support independent streams 25 | and jumping ahead so that substreams can be generated 26 | - Faster random number generation, especially for Normals using the 27 | Ziggurat method 28 | 29 | .. code:: python 30 | 31 | import randomstate as rnd 32 | w = rnd.standard_normal(10000, method='zig') 33 | 34 | - Support for 32-bit floating randoms for core generators. Currently 35 | supported: 36 | 37 | - Uniforms (``random_sample``) 38 | - Exponentials (``standard_exponential``) 39 | - Normals (``standard_normal``, both Box-Muller and Ziggurat) 40 | - Standard Gammas (via ``standard_gamma``) 41 | 42 | **WARNING**: The 32-bit generators are **experimental** and subject to 43 | change. 44 | 45 | **Note**: There are *no* plans to extend the alternative precision 46 | generation to all random number types. 47 | 48 | - Support for filling existing arrays using ``out`` keyword argument. 49 | Currently supported in (both 32- and 64-bit outputs) 50 | 51 | - Uniforms (``random_sample``) 52 | - Exponentials (``standard_exponential``) 53 | - Normals (``standard_normal``) 54 | - Standard Gammas (via ``standard_gamma``) 55 | 56 | Included Pseudo Random Number Generators 57 | ---------------------------------------- 58 | 59 | This modules includes a number of alternative random number generators 60 | in addition to the MT19937 that is included in NumPy. The RNGs include: 61 | 62 | - `MT19937 `__, 63 | the NumPy rng 64 | - `dSFMT `__ a 65 | SSE2-aware version of the MT19937 generator that is especially fast 66 | at generating doubles 67 | - `xorshift128+ `__, 68 | `xoroshiro128+ `__ and 69 | `xorshift1024\* `__ 70 | - `PCG32 `__ and 71 | `PCG64 `__ 72 | - `MRG32K3A `__ 73 | - A multiplicative lagged fibonacci generator (LFG(63, 1279, 861, \*)) 74 | 75 | Differences from ``numpy.random.RandomState`` 76 | --------------------------------------------- 77 | 78 | New Features 79 | ~~~~~~~~~~~~ 80 | 81 | - ``standard_normal``, ``normal``, ``randn`` and 82 | ``multivariate_normal`` all support an additional ``method`` keyword 83 | argument which can be ``bm`` or ``zig`` where ``bm`` corresponds to 84 | the current method using the Box-Muller transformation and ``zig`` 85 | uses the much faster (100%+) Ziggurat method. 86 | - Core random number generators can produce either single precision 87 | (``np.float32``) or double precision (``np.float64``, the default) 88 | using an the optional keyword argument ``dtype`` 89 | - Core random number generators can fill existing arrays using the 90 | ``out`` keyword argument 91 | 92 | New Functions 93 | ~~~~~~~~~~~~~ 94 | 95 | - ``random_entropy`` - Read from the system entropy provider, which is 96 | commonly used in cryptographic applications 97 | - ``random_raw`` - Direct access to the values produced by the 98 | underlying PRNG. The range of the values returned depends on the 99 | specifics of the PRNG implementation. 100 | - ``random_uintegers`` - unsigned integers, either 32- 101 | (``[0, 2**32-1]``) or 64-bit (``[0, 2**64-1]``) 102 | - ``jump`` - Jumps RNGs that support it. ``jump`` moves the state a 103 | great distance. *Only available if supported by the RNG.* 104 | - ``advance`` - Advanced the core RNG 'as-if' a number of draws were 105 | made, without actually drawing the numbers. *Only available if 106 | supported by the RNG.* 107 | 108 | Status 109 | ------ 110 | 111 | - Complete drop-in replacement for ``numpy.random.RandomState``. The 112 | ``mt19937`` generator is identical to ``numpy.random.RandomState``, 113 | and will produce an identical sequence of random numbers for a given 114 | seed. 115 | - Builds and passes all tests on: 116 | - Linux 32/64 bit, Python 2.7, 3.4, 3.5, 3.6 (probably works on 2.6 and 117 | 3.3) 118 | - PC-BSD (FreeBSD) 64-bit, Python 2.7 119 | - OSX 64-bit, Python 2.7 120 | - Windows 32/64 bit (only tested on Python 2.7 and 3.5, but should work 121 | on 3.3/3.4) 122 | 123 | Version 124 | ------- 125 | 126 | The version matched the latest version of NumPy where 127 | ``randomstate.prng.mt19937`` passes all NumPy test. 128 | 129 | Documentation 130 | ------------- 131 | 132 | An occasionally updated build of the documentation is available on `my 133 | github pages `__. 134 | 135 | Plans 136 | ----- 137 | 138 | This module is essentially complete. There are a few rough edges that 139 | need to be smoothed. 140 | 141 | - Stream support for MLFG 142 | - Creation of additional streams from a RandomState where supported 143 | (i.e. a ``next_stream()`` method) 144 | 145 | Requirements 146 | ------------ 147 | 148 | Building requires: 149 | 150 | - Python (2.7, 3.4, 3.5, 3.6) 151 | - NumPy (1.9, 1.10, 1.11, 1.12) 152 | - Cython (0.22, 0.23, 0.24, 0.25) 153 | - tempita (0.5+), if not provided by Cython 154 | 155 | Testing requires nose (1.3+). 156 | 157 | **Note:** it might work with other versions but only tested with these 158 | versions. 159 | 160 | Development and Testing 161 | ----------------------- 162 | 163 | | All development has been on 64-bit Linux, and it is regularly tested 164 | on Travis-CI. The library is occasionally tested on Linux 32-bit, 165 | | OSX 10.10, PC-BSD 10.2 (should also work on Free BSD) and Windows 166 | (Python 2.7/3.5, both 32 and 64-bit). 167 | 168 | Basic tests are in place for all RNGs. The MT19937 is tested against 169 | NumPy's implementation for identical results. It also passes NumPy's 170 | test suite. 171 | 172 | Installing 173 | ---------- 174 | 175 | .. code:: bash 176 | 177 | python setup.py install 178 | 179 | SSE2 180 | ~~~~ 181 | 182 | ``dSFTM`` makes use of SSE2 by default. If you have a very old computer 183 | or are building on non-x86, you can install using: 184 | 185 | .. code:: bash 186 | 187 | python setup.py install --no-sse2 188 | 189 | Windows 190 | ~~~~~~~ 191 | 192 | Either use a binary installer, or if building from scratch, use Python 193 | 3.5 with Visual Studio 2015 Community Edition. It can also be build 194 | using Microsoft Visual C++ Compiler for Python 2.7 and Python 2.7, 195 | although some modifications may be needed to ``distutils`` to find the 196 | compiler. 197 | 198 | Using 199 | ----- 200 | 201 | The separate generators are importable from ``randomstate.prng``. 202 | 203 | .. code:: python 204 | 205 | import randomstate 206 | rs = randomstate.prng.xorshift128.RandomState() 207 | rs.random_sample(100) 208 | 209 | rs = randomstate.prng.pcg64.RandomState() 210 | rs.random_sample(100) 211 | 212 | # Identical to NumPy 213 | rs = randomstate.prng.mt19937.RandomState() 214 | rs.random_sample(100) 215 | 216 | Like NumPy, ``randomstate`` also exposes a single instance of the 217 | ``mt19937`` generator directly at the module level so that commands like 218 | 219 | .. code:: python 220 | 221 | import randomstate 222 | randomstate.standard_normal() 223 | randomstate.exponential(1.0, 1.0, size=10) 224 | 225 | will work. 226 | 227 | License 228 | ------- 229 | 230 | Standard NCSA, plus sub licenses for components. 231 | 232 | Performance 233 | ----------- 234 | 235 | Performance is promising, and even the mt19937 seems to be faster than 236 | NumPy's mt19937. 237 | 238 | :: 239 | 240 | Speed-up relative to NumPy (Uniform Doubles) 241 | ************************************************************ 242 | randomstate.prng-dsfmt-random_sample 313.5% 243 | randomstate.prng-mlfg_1279_861-random_sample 459.4% 244 | randomstate.prng-mrg32k3a-random_sample -57.6% 245 | randomstate.prng-mt19937-random_sample 72.5% 246 | randomstate.prng-pcg32-random_sample 232.8% 247 | randomstate.prng-pcg64-random_sample 330.6% 248 | randomstate.prng-xoroshiro128plus-random_sample 609.9% 249 | randomstate.prng-xorshift1024-random_sample 348.8% 250 | randomstate.prng-xorshift128-random_sample 489.7% 251 | 252 | Speed-up relative to NumPy (Normals using Box-Muller) 253 | ************************************************************ 254 | randomstate.prng-dsfmt-standard_normal 26.8% 255 | randomstate.prng-mlfg_1279_861-standard_normal 30.9% 256 | randomstate.prng-mrg32k3a-standard_normal -14.8% 257 | randomstate.prng-mt19937-standard_normal 17.7% 258 | randomstate.prng-pcg32-standard_normal 24.5% 259 | randomstate.prng-pcg64-standard_normal 26.2% 260 | randomstate.prng-xoroshiro128plus-standard_normal 31.4% 261 | randomstate.prng-xorshift1024-standard_normal 27.4% 262 | randomstate.prng-xorshift128-standard_normal 30.3% 263 | 264 | Speed-up relative to NumPy (Normals using Ziggurat) 265 | ************************************************************ 266 | randomstate.prng-dsfmt-standard_normal 491.7% 267 | randomstate.prng-mlfg_1279_861-standard_normal 439.6% 268 | randomstate.prng-mrg32k3a-standard_normal 101.2% 269 | randomstate.prng-mt19937-standard_normal 354.4% 270 | randomstate.prng-pcg32-standard_normal 531.0% 271 | randomstate.prng-pcg64-standard_normal 517.9% 272 | randomstate.prng-xoroshiro128plus-standard_normal 674.0% 273 | randomstate.prng-xorshift1024-standard_normal 486.7% 274 | randomstate.prng-xorshift128-standard_normal 617.0% 275 | 276 | .. |Travis Build Status| image:: https://travis-ci.org/bashtage/ng-numpy-randomstate.svg?branch=master 277 | :target: https://travis-ci.org/bashtage/ng-numpy-randomstate 278 | .. |Appveyor Build Status| image:: https://ci.appveyor.com/api/projects/status/odc5c4ukhru5xicl/branch/master?svg=true 279 | :target: https://ci.appveyor.com/project/bashtage/ng-numpy-randomstate/branch/master 280 | .. |PyPI version| image:: https://badge.fury.io/py/randomstate.svg 281 | :target: https://badge.fury.io/py/randomstate 282 | -------------------------------------------------------------------------------- /rng/licenses/PCG-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /06-numpy-buffers-fused-types.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Cython and NumPy, buffers, fused types\n", 8 | "\n", 9 | "We'll be re-implementing the `scipy.stats.poisson.entropy()` function / method using Cython and its NumPy support, and we'll see what sort of speedup we can get for our efforts.\n", 10 | "\n", 11 | "Obligatory XKCD:" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "%load_ext Cython\n", 28 | "import numpy as np" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "## Pure Python implementation" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "metadata": { 42 | "collapsed": true 43 | }, 44 | "outputs": [], 45 | "source": [ 46 | "# https://en.wiktionary.org/wiki/Shannon_entropy\n", 47 | "\n", 48 | "def shannon_entropy_py(p_x):\n", 49 | " return - np.sum(p_x * np.log(p_x))" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Cythonized version" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "%%cython -a\n", 66 | "\n", 67 | "import numpy as np\n", 68 | "cimport numpy as cnp\n", 69 | "\n", 70 | "def shannon_entropy_cy(cnp.ndarray p_x):\n", 71 | " return - np.sum(p_x * np.log(p_x))" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "## What's `cimport`?\n", 79 | "\n", 80 | "Cython introduces a new keyword, `cimport` that allows interfacing with other Cython code _at the C-level_ and _at compile time_.\n", 81 | "\n", 82 | "This is in distinction to Python's regular `import` statement, which interfaces with other _Python_ modules at _runtime_.\n", 83 | "\n", 84 | "The statement `cimport numpy as cnp` allows Cython to interface with NumPy arrays at the C level and at compile time for improved performance.\n", 85 | "\n", 86 | "The `cimport numpy` statement causes the Cython compiler to look for a `numpy.pxd` Cython declaration file at compile time. This is where we can find the C-level declarations of the NumPy C-API. Here we're using the `ndarray` object from `numpy.pxd` to declare the argument of `shannon_entropy_cy()`." 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## Scipy.stats comparison" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": { 100 | "collapsed": true 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "from scipy.stats import poisson\n", 105 | "poi = poisson(10.0)\n", 106 | "n = 100\n", 107 | "pmf = poi.pmf(np.arange(n))" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "print(poi.entropy())\n", 117 | "print(shannon_entropy_py(pmf))\n", 118 | "print(shannon_entropy_cy(pmf))" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "%%timeit\n", 128 | "poi.entropy()" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "metadata": {}, 135 | "outputs": [], 136 | "source": [ 137 | "%%timeit\n", 138 | "shannon_entropy_py(pmf)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "%%timeit\n", 148 | "shannon_entropy_cy(pmf)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": {}, 154 | "source": [ 155 | "## Explicit `for` loop" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "%%cython -a\n", 165 | "\n", 166 | "cimport numpy as cnp\n", 167 | "from libc.math cimport log as clog\n", 168 | "\n", 169 | "def shannon_entropy_v1(cnp.ndarray p_x):\n", 170 | " cdef double res = 0.0\n", 171 | " cdef int n = p_x.shape[0]\n", 172 | " cdef int i\n", 173 | " for i in range(n):\n", 174 | " res += p_x[i] * clog(p_x[i])\n", 175 | " return -res" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "%%timeit\n", 185 | "shannon_entropy_v1(pmf)" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": {}, 191 | "source": [ 192 | "## What's `from libc.math cimport log`?\n", 193 | "\n", 194 | "Cython allows us to `cimport` C (and C++) functions from the C and C++ standard (template) libraries.\n", 195 | "\n", 196 | "To access functions in the C stdlib `math.h` header file, we simply do\n", 197 | "\n", 198 | "```\n", 199 | "from libc.math cimport exp, log, sqrt\n", 200 | "```" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "## NumPy buffer special declaration" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [ 216 | "%%cython -a\n", 217 | "\n", 218 | "cimport numpy as cnp\n", 219 | "from libc.math cimport log as clog\n", 220 | "\n", 221 | "def shannon_entropy_v2(cnp.ndarray[double] p_x):\n", 222 | " cdef double res = 0.0\n", 223 | " cdef int n = p_x.shape[0]\n", 224 | " cdef int i\n", 225 | " for i in range(n):\n", 226 | " res += p_x[i] * clog(p_x[i])\n", 227 | " return -res" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "%%timeit\n", 237 | "shannon_entropy_v2(pmf)" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "## The `cnp.ndarray[double]` syntax\n", 245 | "\n", 246 | "The `cnp.ndarray[double]` declares a NumPy array _buffer_ object. Cython knows how to interact with this array-like object efficiently. The `double` in square brackets is the (scalar) dtype of the array elements." 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "## Turn off boundschecking and wraparound checking" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": null, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "%%cython -a\n", 263 | "\n", 264 | "cimport cython\n", 265 | "cimport numpy as cnp\n", 266 | "from libc.math cimport log\n", 267 | "\n", 268 | "@cython.boundscheck(False)\n", 269 | "@cython.wraparound(False)\n", 270 | "def shannon_entropy_v3(cnp.ndarray[double] p_x):\n", 271 | " cdef double res = 0.0\n", 272 | " cdef int n = p_x.shape[0]\n", 273 | " cdef int i\n", 274 | " for i in range(n):\n", 275 | " res += p_x[i] * log(p_x[i])\n", 276 | " return -res" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": null, 282 | "metadata": {}, 283 | "outputs": [], 284 | "source": [ 285 | "%%timeit\n", 286 | "shannon_entropy_v3(pmf)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "metadata": {}, 292 | "source": [ 293 | "## The `cython` cimported magic module\n", 294 | "\n", 295 | "Cython allows us to control its compile-time semantics and behavior via the magic `cython` module that we `cimport`.\n", 296 | "\n", 297 | "We can then use Cython directives like:\n", 298 | "\n", 299 | "```python\n", 300 | "@cython.boundscheck(False)\n", 301 | "@cython.wraparound(False)\n", 302 | "def func(...):\n", 303 | " ...\n", 304 | "```\n", 305 | "\n", 306 | "To control how code is generated.\n", 307 | "\n", 308 | "In this case, we're telling cython to not generate code that does boundschecking or wraparound checking (negative indexing)." 309 | ] 310 | }, 311 | { 312 | "cell_type": "markdown", 313 | "metadata": {}, 314 | "source": [ 315 | "## Typed memoryview syntax" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": null, 321 | "metadata": {}, 322 | "outputs": [], 323 | "source": [ 324 | "%%cython -a\n", 325 | "\n", 326 | "cimport cython\n", 327 | "from libc.math cimport log\n", 328 | "\n", 329 | "@cython.boundscheck(False)\n", 330 | "@cython.wraparound(False)\n", 331 | "def shannon_entropy_mv(double[::1] p_x):\n", 332 | " cdef double res = 0.0\n", 333 | " cdef int n = p_x.shape[0]\n", 334 | " cdef int i\n", 335 | " for i in range(n):\n", 336 | " res += p_x[i] * log(p_x[i])\n", 337 | " return -res" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": null, 343 | "metadata": {}, 344 | "outputs": [], 345 | "source": [ 346 | "%%timeit\n", 347 | "shannon_entropy_mv(pmf)" 348 | ] 349 | }, 350 | { 351 | "cell_type": "markdown", 352 | "metadata": {}, 353 | "source": [ 354 | "## Typed memoryview syntax\n", 355 | "\n", 356 | "The declaration\n", 357 | "\n", 358 | "```python\n", 359 | "def shannon_entropy_mv(double[::1] p_x):\n", 360 | " ...\n", 361 | "```\n", 362 | "\n", 363 | "Declares `p_x` to be a one dimensional contiguous typed memoryview -- a Cython only construct that's compatible with NumPy arrays and PEP 3118 buffer objects. We include it here because you may see this synax used in other situations." 364 | ] 365 | }, 366 | { 367 | "cell_type": "markdown", 368 | "metadata": {}, 369 | "source": [ 370 | "## Fused types example" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "%%cython -a\n", 380 | "\n", 381 | "cimport cython\n", 382 | "from libc.math cimport log\n", 383 | "\n", 384 | "@cython.boundscheck(False)\n", 385 | "@cython.wraparound(False)\n", 386 | "def shannon_entropy_mv(cython.floating[::1] p_x):\n", 387 | " cdef double res = 0.0\n", 388 | " cdef int n = p_x.shape[0]\n", 389 | " cdef int i\n", 390 | " for i in range(n):\n", 391 | " if p_x[i] > 0.0: # Have to guard against underflow...\n", 392 | " res += p_x[i] * log(p_x[i])\n", 393 | " return -res" 394 | ] 395 | }, 396 | { 397 | "cell_type": "code", 398 | "execution_count": null, 399 | "metadata": { 400 | "scrolled": true 401 | }, 402 | "outputs": [], 403 | "source": [ 404 | "print(shannon_entropy_mv(pmf.astype('f8')))\n", 405 | "print(shannon_entropy_mv(pmf.astype('f4')))" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": null, 411 | "metadata": { 412 | "collapsed": true 413 | }, 414 | "outputs": [], 415 | "source": [] 416 | } 417 | ], 418 | "metadata": { 419 | "kernelspec": { 420 | "display_name": "Python 3", 421 | "language": "python", 422 | "name": "python3" 423 | }, 424 | "language_info": { 425 | "codemirror_mode": { 426 | "name": "ipython", 427 | "version": 3 428 | }, 429 | "file_extension": ".py", 430 | "mimetype": "text/x-python", 431 | "name": "python", 432 | "nbconvert_exporter": "python", 433 | "pygments_lexer": "ipython3", 434 | "version": "3.6.1" 435 | } 436 | }, 437 | "nbformat": 4, 438 | "nbformat_minor": 2 439 | } 440 | --------------------------------------------------------------------------------