From a8844501f5371b9c38f6a8328a346104790a6815 Mon Sep 17 00:00:00 2001 From: Kevin Sheppard Date: Mon, 1 Apr 2019 13:45:48 +0100 Subject: [PATCH 1/3] MAINT: Sync with randomgen changes Synchronize with changes in randomgen --- numpy/random/__init__.py | 4 +- numpy/random/randomgen/__init__.py | 5 +- numpy/random/randomgen/_pickle.py | 38 +- .../random/randomgen/bounded_integers.pxd.in | 13 +- .../random/randomgen/bounded_integers.pyx.in | 17 +- numpy/random/randomgen/common.pxd | 14 +- numpy/random/randomgen/common.pyx | 203 +- numpy/random/randomgen/distributions.pxd | 8 +- numpy/random/randomgen/dsfmt.pyx | 120 +- numpy/random/randomgen/entropy.pyx | 7 +- .../randomgen/examples/cython/extending.pyx | 10 +- .../cython/extending_distributions.pyx | 37 +- .../randomgen/examples/numba/extending.py | 2 +- numpy/random/randomgen/generator.pyx | 440 +- numpy/random/randomgen/legacy/__init__.py | 2 +- numpy/random/randomgen/legacy/_legacy.pyx | 2263 --------- numpy/random/randomgen/legacy/legacy.py | 130 - .../randomgen/legacy/legacy_distributions.pxd | 4 +- numpy/random/randomgen/mt19937.pyx | 103 +- numpy/random/randomgen/mtrand.pyx | 4172 +++++++++++++++++ numpy/random/randomgen/pcg32.pyx | 102 +- numpy/random/randomgen/pcg64.pyx | 104 +- numpy/random/randomgen/philox.pyx | 107 +- numpy/random/randomgen/setup.py | 22 +- .../src/distributions/distributions.c | 3 + numpy/random/randomgen/src/pcg32/pcg32.h | 4 + numpy/random/randomgen/src/pcg64/pcg64.h | 4 +- .../randomgen/tests/test_against_numpy.py | 55 +- numpy/random/randomgen/tests/test_direct.py | 191 +- numpy/random/randomgen/tests/test_legacy.py | 17 - .../randomgen/tests/test_numpy_mt19937.py | 499 +- .../randomgen/tests/test_randomstate.py | 1808 +++++++ .../tests/test_randomstate_regression.py | 157 + numpy/random/randomgen/tests/test_smoke.py | 38 +- numpy/random/randomgen/threefry.pyx | 104 +- numpy/random/randomgen/threefry32.pyx | 102 +- numpy/random/randomgen/xoroshiro128.pyx | 102 +- numpy/random/randomgen/xorshift1024.pyx | 105 +- numpy/random/randomgen/xoshiro256starstar.pyx | 103 +- numpy/random/randomgen/xoshiro512starstar.pyx | 101 +- 40 files changed, 7590 insertions(+), 3730 deletions(-) delete mode 100644 numpy/random/randomgen/legacy/_legacy.pyx delete mode 100644 numpy/random/randomgen/legacy/legacy.py create mode 100644 numpy/random/randomgen/mtrand.pyx delete mode 100644 numpy/random/randomgen/tests/test_legacy.py create mode 100644 numpy/random/randomgen/tests/test_randomstate.py create mode 100644 numpy/random/randomgen/tests/test_randomstate_regression.py diff --git a/numpy/random/__init__.py b/numpy/random/__init__.py index 818ece58aba2..b5ff1cfcab4b 100644 --- a/numpy/random/__init__.py +++ b/numpy/random/__init__.py @@ -137,7 +137,7 @@ ] # from .mtrand import * -from .randomgen.legacy import LegacyGenerator as RandomState +from .randomgen.mtrand import RandomState mtrand = RandomState() for _x in dir(mtrand): if _x[0] != '_' and _x not in ('poisson_lam_max', 'state'): @@ -145,7 +145,7 @@ del _x # Some aliases: -ranf = random = sample = random_sample +ranf = random = sample = mtrand.random_sample __all__.extend(['ranf', 'random', 'sample']) def __RandomState_ctor(): diff --git a/numpy/random/randomgen/__init__.py b/numpy/random/randomgen/__init__.py index 2a4e20bbc43a..1af2fc3b200c 100644 --- a/numpy/random/randomgen/__init__.py +++ b/numpy/random/randomgen/__init__.py @@ -10,11 +10,10 @@ from .xorshift1024 import Xorshift1024 from .xoshiro256starstar import Xoshiro256StarStar from .xoshiro512starstar import Xoshiro512StarStar - +from .mtrand import RandomState __all__ = ['RandomGenerator', 'DSFMT', 'MT19937', 'PCG64', 'PCG32', 'Philox', 'ThreeFry', 'ThreeFry32', 'Xoroshiro128', 'Xorshift1024', - 'Xoshiro256StarStar', 'Xoshiro512StarStar', - 'hypergeometric', 'multinomial', 'random_sample'] + 'Xoshiro256StarStar', 'Xoshiro512StarStar', 'RandomState'] #from ._version import get_versions diff --git a/numpy/random/randomgen/_pickle.py b/numpy/random/randomgen/_pickle.py index b192bc8a4b1f..bcfc00402dee 100644 --- a/numpy/random/randomgen/_pickle.py +++ b/numpy/random/randomgen/_pickle.py @@ -10,7 +10,7 @@ from .xorshift1024 import Xorshift1024 from .xoshiro256starstar import Xoshiro256StarStar from .xoshiro512starstar import Xoshiro512StarStar -from .legacy import LegacyGenerator +from .mtrand import RandomState BasicRNGS = {'MT19937': MT19937, 'DSFMT': DSFMT, @@ -78,9 +78,9 @@ def __brng_ctor(brng_name='mt19937'): return brng() -def __legacy_ctor(brng_name='mt19937'): +def __randomstate_ctor(brng_name='mt19937'): """ - Pickling helper function that returns a LegacyGenerator object + Pickling helper function that returns a legacy RandomState-like object Parameters ---------- @@ -89,8 +89,8 @@ def __legacy_ctor(brng_name='mt19937'): Returns ------- - lg: LegacyGenerator - LegacyGenerator using the named core BasicRNG + rs: RandomState + Legacy RandomState using the named core BasicRNG """ try: brng_name = brng_name.decode('ascii') @@ -101,30 +101,4 @@ def __legacy_ctor(brng_name='mt19937'): else: raise ValueError(str(brng_name) + ' is not a known BasicRNG module.') - return LegacyGenerator(brng()) - - -def _experiment_ctor(brng_name='mt19937'): - """ - Pickling helper function that returns a LegacyGenerator object - - Parameters - ---------- - brng_name: str - String containing the name of the Basic RNG - - Returns - ------- - brng: BasicRNG - Basic RNG instance - """ - try: - brng_name = brng_name.decode('ascii') - except AttributeError: - pass - if brng_name in BasicRNGS: - brng = BasicRNGS[brng_name] - else: - raise ValueError(str(brng_name) + ' is not a known BasicRNG module.') - - return LegacyGenerator(brng()) + return RandomState(brng()) diff --git a/numpy/random/randomgen/bounded_integers.pxd.in b/numpy/random/randomgen/bounded_integers.pxd.in index 41bf1a178923..50df0c7897cf 100644 --- a/numpy/random/randomgen/bounded_integers.pxd.in +++ b/numpy/random/randomgen/bounded_integers.pxd.in @@ -3,22 +3,11 @@ from __future__ import absolute_import from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t, intptr_t) import numpy as np -cimport numpy as np +cimport numpy as np ctypedef np.npy_bool bool_t from .common cimport brng_t -_randint_types = {'bool': (0, 2), - 'int8': (-2**7, 2**7), - 'int16': (-2**15, 2**15), - 'int32': (-2**31, 2**31), - 'int64': (-2**63, 2**63), - 'uint8': (0, 2**8), - 'uint16': (0, 2**16), - 'uint32': (0, 2**32), - 'uint64': (0, 2**64) - } - cdef inline uint64_t _gen_mask(uint64_t max_val) nogil: """Mask generator for use in bounded random numbers""" # Smallest bit mask >= max diff --git a/numpy/random/randomgen/bounded_integers.pyx.in b/numpy/random/randomgen/bounded_integers.pyx.in index d66f09c42d4d..56d558b14175 100644 --- a/numpy/random/randomgen/bounded_integers.pyx.in +++ b/numpy/random/randomgen/bounded_integers.pyx.in @@ -9,6 +9,15 @@ from .distributions cimport * np.import_array() +_randint_types = {'bool': (0, 2), + 'int8': (-2**7, 2**7), + 'int16': (-2**15, 2**15), + 'int32': (-2**31, 2**31), + 'int64': (-2**63, 2**63), + 'uint8': (0, 2**8), + 'uint16': (0, 2**16), + 'uint32': (0, 2**32), + 'uint64': (0, 2**64)} {{ py: type_info = (('uint32', 'uint32', 'uint64', 'NPY_UINT64', 0, 0, 0, '0X100000000ULL'), @@ -21,7 +30,6 @@ type_info = (('uint32', 'uint32', 'uint64', 'NPY_UINT64', 0, 0, 0, '0X100000000U )}} {{for nptype, utype, nptype_up, npctype, remaining, bitshift, lb, ub in type_info}} {{ py: otype = nptype + '_' if nptype == 'bool' else nptype }} - cdef object _rand_{{nptype}}_broadcast(np.ndarray low, np.ndarray high, object size, bint use_masked, brng_t *state, object lock): @@ -42,7 +50,6 @@ cdef object _rand_{{nptype}}_broadcast(np.ndarray low, np.ndarray high, object s cdef np.broadcast it cdef int buf_rem = 0 - # Array path low_arr = low high_arr = high @@ -83,7 +90,6 @@ cdef object _rand_{{nptype}}_broadcast(np.ndarray low, np.ndarray high, object s np.PyArray_MultiIter_NEXT(it) return out_arr {{endfor}} - {{ py: big_type_info = (('uint64', 'uint64', 'NPY_UINT64', '0x0ULL', '0xFFFFFFFFFFFFFFFFULL'), @@ -166,7 +172,6 @@ cdef object _rand_{{nptype}}_broadcast(object low, object high, object size, return out_arr {{endfor}} - {{ py: type_info = (('uint64', 'uint64', '0x0ULL', '0xFFFFFFFFFFFFFFFFULL'), @@ -241,8 +246,8 @@ cdef object _rand_{{nptype}}(object low, object high, object size, high_arr = np.array(high, copy=False) low_ndim = np.PyArray_NDIM(low_arr) high_ndim = np.PyArray_NDIM(high_arr) - if ((low_ndim == 0 or (low_ndim==1 and low_arr.size==1 and size is not None)) and - (high_ndim == 0 or (high_ndim==1 and high_arr.size==1 and size is not None))): + if ((low_ndim == 0 or (low_ndim == 1 and low_arr.size == 1 and size is not None)) and + (high_ndim == 0 or (high_ndim == 1 and high_arr.size == 1 and size is not None))): low = int(low_arr) high = int(high_arr) # Subtract 1 since internal generator produces on closed interval [low, high] diff --git a/numpy/random/randomgen/common.pxd b/numpy/random/randomgen/common.pxd index 99c53a210fa9..824225cebf95 100644 --- a/numpy/random/randomgen/common.pxd +++ b/numpy/random/randomgen/common.pxd @@ -27,14 +27,18 @@ cdef enum ConstraintType: ctypedef ConstraintType constraint_type +cdef object benchmark(brng_t *brng, object lock, Py_ssize_t cnt, object method) +cdef object random_raw(brng_t *brng, object lock, object size, object output) +cdef object prepare_cffi(brng_t *brng) +cdef object prepare_ctypes(brng_t *brng) cdef int check_constraint(double val, object name, constraint_type cons) except -1 cdef int check_array_constraint(np.ndarray val, object name, constraint_type cons) except -1 cdef extern from "src/aligned_malloc/aligned_malloc.h": - cdef void *PyArray_realloc_aligned(void *p, size_t n); - cdef void *PyArray_malloc_aligned(size_t n); - cdef void *PyArray_calloc_aligned(size_t n, size_t s); - cdef void PyArray_free_aligned(void *p); + cdef void *PyArray_realloc_aligned(void *p, size_t n) + cdef void *PyArray_malloc_aligned(size_t n) + cdef void *PyArray_calloc_aligned(size_t n, size_t s) + cdef void PyArray_free_aligned(void *p) ctypedef double (*random_double_fill)(brng_t *state, np.npy_intp count, double* out) nogil ctypedef double (*random_double_0)(void *state) nogil @@ -105,5 +109,5 @@ cdef inline void compute_complex(double *rv_r, double *rv_i, double loc_r, scale_r = sqrt(var_r) scale_i = sqrt(var_i) - rv_i[0] = loc_i + scale_i * (rho * rv_r[0] + scale_c * rv_i[0]) + rv_i[0] = loc_i + scale_i * (rho * rv_r[0] + scale_c * rv_i[0]) rv_r[0] = loc_r + scale_r * rv_r[0] diff --git a/numpy/random/randomgen/common.pyx b/numpy/random/randomgen/common.pyx index 30f62bf9129d..2ade3e821cc4 100644 --- a/numpy/random/randomgen/common.pyx +++ b/numpy/random/randomgen/common.pyx @@ -1,7 +1,5 @@ #!python #cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 -from __future__ import absolute_import - from collections import namedtuple from cpython cimport PyFloat_AsDouble import sys @@ -16,6 +14,150 @@ interface = namedtuple('interface', ['state_address', 'state', 'next_uint64', 'next_uint32', 'next_double', 'brng']) +cdef object benchmark(brng_t *brng, object lock, Py_ssize_t cnt, object method): + """Benchmark command used by BasicRNG""" + cdef Py_ssize_t i + if method==u'uint64': + with lock, nogil: + for i in range(cnt): + brng.next_uint64(brng.state) + elif method==u'double': + with lock, nogil: + for i in range(cnt): + brng.next_double(brng.state) + else: + raise ValueError('Unknown method') + + +cdef object random_raw(brng_t *brng, object lock, object size, object output): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying PRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + cdef np.ndarray randoms + cdef uint64_t *randoms_data + cdef Py_ssize_t i, n + + if not output: + if size is None: + with lock: + brng.next_raw(brng.state) + return None + n = np.asarray(size).sum() + with lock, nogil: + for i in range(n): + brng.next_raw(brng.state) + return None + + if size is None: + with lock: + return brng.next_raw(brng.state) + + randoms = np.empty(size, np.uint64) + randoms_data = np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + with lock, nogil: + for i in range(n): + randoms_data[i] = brng.next_raw(brng.state) + return randoms + +cdef object prepare_cffi(brng_t *brng): + """ + Bundles the interfaces to interact with a Basic RNG using cffi + + Parameters + ---------- + brng : pointer + A pointer to a Basic RNG instance + + Returns + ------- + interface : namedtuple + The functions required to interface with the Basic RNG using cffi + + * state_address - Memory address of the state struct + * state - pointer to the state struct + * next_uint64 - function pointer to produce 64 bit integers + * next_uint32 - function pointer to produce 32 bit integers + * next_double - function pointer to produce doubles + * brng - pointer to the Basic RNG struct + """ + try: + import cffi + except ImportError: + raise ImportError('cffi cannot be imported.') + + ffi = cffi.FFI() + _cffi = interface(brng.state, + ffi.cast('void *', brng.state), + ffi.cast('uint64_t (*)(void *)', brng.next_uint64), + ffi.cast('uint32_t (*)(void *)', brng.next_uint32), + ffi.cast('double (*)(void *)', brng.next_double), + ffi.cast('void *', brng)) + return _cffi + +cdef object prepare_ctypes(brng_t *brng): + """ + Bundles the interfaces to interact with a Basic RNG using ctypes + + Parameters + ---------- + brng : pointer + A pointer to a Basic RNG instance + + Returns + ------- + interface : namedtuple + The functions required to interface with the Basic RNG using ctypes: + + * state_address - Memory address of the state struct + * state - pointer to the state struct + * next_uint64 - function pointer to produce 64 bit integers + * next_uint32 - function pointer to produce 32 bit integers + * next_double - function pointer to produce doubles + * brng - pointer to the Basic RNG struct + """ + import ctypes + + _ctypes = interface(brng.state, + ctypes.c_void_p(brng.state), + ctypes.cast(brng.next_uint64, + ctypes.CFUNCTYPE(ctypes.c_uint64, + ctypes.c_void_p)), + ctypes.cast(brng.next_uint32, + ctypes.CFUNCTYPE(ctypes.c_uint32, + ctypes.c_void_p)), + ctypes.cast(brng.next_double, + ctypes.CFUNCTYPE(ctypes.c_double, + ctypes.c_void_p)), + ctypes.c_void_p(brng)) + return _ctypes + cdef double kahan_sum(double *darr, np.npy_intp n): cdef double c, y, t, sum cdef np.npy_intp i @@ -29,6 +171,7 @@ cdef double kahan_sum(double *darr, np.npy_intp n): return sum cdef np.ndarray int_to_array(object value, object name, object bits, object uint_size): + """Convert a large integer to an array of unsigned integers""" len = bits // uint_size value = np.asarray(value) if uint_size == 32: @@ -67,8 +210,12 @@ cdef check_output(object out, object dtype, object size): raise TypeError('Supplied output array has the wrong type. ' 'Expected {0}, got {0}'.format(dtype, out_array.dtype)) if size is not None: - # TODO: enable this !!! if tuple(size) != out_array.shape: - raise ValueError('size and out cannot be simultaneously used') + try: + tup_size = tuple(size) + except TypeError: + tup_size = tuple([size]) + if tup_size != out.shape: + raise ValueError('size must match out.shape when used together') cdef object double_fill(void *func, brng_t *state, object size, object lock, object out): @@ -175,7 +322,6 @@ cdef int check_array_constraint(np.ndarray val, object name, constraint_type con return 0 - cdef int check_constraint(double val, object name, constraint_type cons) except -1: if cons == CONS_NON_NEGATIVE: if np.signbit(val) or np.isnan(val): @@ -257,7 +403,6 @@ cdef object cont_broadcast_2(void *func, void *state, object size, object lock, randoms = np.empty(it.shape, np.double) # randoms = np.PyArray_SimpleNew(it.nd, np.PyArray_DIMS(it), np.NPY_DOUBLE) - randoms_data = np.PyArray_DATA(randoms) n = np.PyArray_SIZE(randoms) @@ -296,7 +441,7 @@ cdef object cont_broadcast_3(void *func, void *state, object size, object lock, randoms = np.empty(size, np.double) else: it = np.PyArray_MultiIterNew3(a_arr, b_arr, c_arr) - #randoms = np.PyArray_SimpleNew(it.nd, np.PyArray_DIMS(it), np.NPY_DOUBLE) + # randoms = np.PyArray_SimpleNew(it.nd, np.PyArray_DIMS(it), np.NPY_DOUBLE) randoms = np.empty(it.shape, np.double) randoms_data = np.PyArray_DATA(randoms) @@ -381,11 +526,11 @@ cdef object cont(void *func, void *state, object size, object lock, int narg, randoms = out n = np.PyArray_SIZE(randoms) - cdef double *randoms_data = np.PyArray_DATA(randoms) - cdef random_double_0 f0; - cdef random_double_1 f1; - cdef random_double_2 f2; - cdef random_double_3 f3; + cdef double *randoms_data = np.PyArray_DATA(randoms) + cdef random_double_0 f0 + cdef random_double_1 f1 + cdef random_double_2 f2 + cdef random_double_3 f3 with lock, nogil: if narg == 0: @@ -425,7 +570,7 @@ cdef object discrete_broadcast_d(void *func, void *state, object size, object lo if size is not None: randoms = np.empty(size, np.int64) else: - #randoms = np.empty(np.shape(a_arr), np.double) + # randoms = np.empty(np.shape(a_arr), np.double) randoms = np.PyArray_SimpleNew(np.PyArray_NDIM(a_arr), np.PyArray_DIMS(a_arr), np.NPY_INT64) randoms_data = np.PyArray_DATA(randoms) @@ -485,7 +630,6 @@ cdef object discrete_broadcast_di(void *func, void *state, object size, object l cdef random_uint_di f = (func) cdef np.npy_intp i, n - if a_constraint != CONS_NONE: check_array_constraint(a_arr, a_name, a_constraint) @@ -553,7 +697,7 @@ cdef object discrete_broadcast_iii(void *func, void *state, object size, object return randoms cdef object discrete_broadcast_i(void *func, void *state, object size, object lock, - np.ndarray a_arr, object a_name, constraint_type a_constraint): + np.ndarray a_arr, object a_name, constraint_type a_constraint): cdef np.ndarray randoms cdef int64_t *randoms_data cdef np.broadcast it @@ -589,7 +733,7 @@ cdef object disc(void *func, void *state, object size, object lock, object c, object c_name, constraint_type c_constraint): cdef double _da = 0, _db = 0 - cdef int64_t _ia = 0, _ib = 0 , _ic = 0 + cdef int64_t _ia = 0, _ib = 0, _ic = 0 cdef bint is_scalar = True if narg_double > 0: a_arr = np.PyArray_FROM_OTF(a, np.NPY_DOUBLE, np.NPY_ALIGNED) @@ -607,7 +751,7 @@ cdef object disc(void *func, void *state, object size, object lock, if narg_int64 > 1: b_arr = np.PyArray_FROM_OTF(b, np.NPY_INT64, np.NPY_ALIGNED) is_scalar = is_scalar and np.PyArray_NDIM(b_arr) == 0 - if narg_int64 > 2 : + if narg_int64 > 2: c_arr = np.PyArray_FROM_OTF(c, np.NPY_INT64, np.NPY_ALIGNED) is_scalar = is_scalar and np.PyArray_NDIM(c_arr) == 0 @@ -631,7 +775,6 @@ cdef object disc(void *func, void *state, object size, object lock, else: raise NotImplementedError("No vector path available") - if narg_double > 0: _da = PyFloat_AsDouble(a) if a_constraint != CONS_NONE and is_scalar: @@ -654,7 +797,7 @@ cdef object disc(void *func, void *state, object size, object lock, _ib = b if b_constraint != CONS_NONE and is_scalar: check_constraint(_ib, b_name, b_constraint) - if narg_int64 > 2 : + if narg_int64 > 2: _ic = c if c_constraint != CONS_NONE and is_scalar: check_constraint(_ic, c_name, c_constraint) @@ -679,15 +822,15 @@ cdef object disc(void *func, void *state, object size, object lock, cdef np.npy_intp i, n cdef np.ndarray randoms = np.empty(size, np.int64) cdef np.int64_t *randoms_data - cdef random_uint_0 f0; - cdef random_uint_d fd; - cdef random_uint_dd fdd; - cdef random_uint_di fdi; - cdef random_uint_i fi; - cdef random_uint_iii fiii; + cdef random_uint_0 f0 + cdef random_uint_d fd + cdef random_uint_dd fdd + cdef random_uint_di fdi + cdef random_uint_i fi + cdef random_uint_iii fiii n = np.PyArray_SIZE(randoms) - randoms_data = np.PyArray_DATA(randoms) + randoms_data = np.PyArray_DATA(randoms) with lock, nogil: if narg_int64 == 0: @@ -721,8 +864,8 @@ cdef object disc(void *func, void *state, object size, object lock, cdef object cont_broadcast_1_f(void *func, brng_t *state, object size, object lock, - np.ndarray a_arr, object a_name, constraint_type a_constraint, - object out): + np.ndarray a_arr, object a_name, constraint_type a_constraint, + object out): cdef np.ndarray randoms cdef float a_val @@ -788,8 +931,8 @@ cdef object cont_f(void *func, brng_t *state, object size, object lock, randoms = out n = np.PyArray_SIZE(randoms) - cdef float *randoms_data = np.PyArray_DATA(randoms) - cdef random_float_1 f1 = func; + cdef float *randoms_data = np.PyArray_DATA(randoms) + cdef random_float_1 f1 = func with lock, nogil: for i in range(n): diff --git a/numpy/random/randomgen/distributions.pxd b/numpy/random/randomgen/distributions.pxd index 35d92db51bbb..ddb7a84bf190 100644 --- a/numpy/random/randomgen/distributions.pxd +++ b/numpy/random/randomgen/distributions.pxd @@ -3,7 +3,7 @@ from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t, intptr_t) import numpy as np -cimport numpy as np +cimport numpy as np cdef extern from "src/distributions/distributions.h": @@ -80,13 +80,13 @@ cdef extern from "src/distributions/distributions.h": double random_rayleigh(brng_t *brng_state, double mode) nogil double random_standard_t(brng_t *brng_state, double df) nogil double random_noncentral_chisquare(brng_t *brng_state, double df, - double nonc) nogil + double nonc) nogil double random_noncentral_f(brng_t *brng_state, double dfnum, - double dfden, double nonc) nogil + double dfden, double nonc) nogil double random_wald(brng_t *brng_state, double mean, double scale) nogil double random_vonmises(brng_t *brng_state, double mu, double kappa) nogil double random_triangular(brng_t *brng_state, double left, double mode, - double right) nogil + double right) nogil int64_t random_poisson(brng_t *brng_state, double lam) nogil int64_t random_negative_binomial(brng_t *brng_state, double n, double p) nogil diff --git a/numpy/random/randomgen/dsfmt.pyx b/numpy/random/randomgen/dsfmt.pyx index 2c4bfabe6937..129caafff17a 100644 --- a/numpy/random/randomgen/dsfmt.pyx +++ b/numpy/random/randomgen/dsfmt.pyx @@ -1,9 +1,12 @@ -from __future__ import absolute_import - import operator from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock + import numpy as np cimport numpy as np @@ -15,24 +18,24 @@ from .entropy import random_entropy np.import_array() DEF DSFMT_MEXP = 19937 -DEF DSFMT_N = 191 # ((DSFMT_MEXP - 128) / 104 + 1) -DEF DSFMT_N_PLUS_1 = 192 # DSFMT_N + 1 +DEF DSFMT_N = 191 # ((DSFMT_MEXP - 128) / 104 + 1) +DEF DSFMT_N_PLUS_1 = 192 # DSFMT_N + 1 DEF DSFMT_N64 = DSFMT_N * 2 cdef extern from "src/dsfmt/dSFMT.h": union W128_T: - uint64_t u[2]; - uint32_t u32[4]; - double d[2]; + uint64_t u[2] + uint32_t u32[4] + double d[2] - ctypedef W128_T w128_t; + ctypedef W128_T w128_t struct DSFMT_T: - w128_t status[DSFMT_N_PLUS_1]; - int idx; + w128_t status[DSFMT_N_PLUS_1] + int idx - ctypedef DSFMT_T dsfmt_t; + ctypedef DSFMT_T dsfmt_t struct s_dsfmt_state: dsfmt_t *state @@ -51,7 +54,7 @@ cdef extern from "src/dsfmt/dSFMT.h": void dsfmt_init_gen_rand(dsfmt_t *dsfmt, uint32_t seed) void dsfmt_init_by_array(dsfmt_t *dsfmt, uint32_t init_key[], int key_length) - void dsfmt_jump(dsfmt_state *state); + void dsfmt_jump(dsfmt_state *state) cdef uint64_t dsfmt_uint64(void* st) nogil: return dsfmt_next64(st) @@ -137,12 +140,13 @@ cdef class DSFMT: Jump Ahead Algorithm for Linear Recurrences in a Polynomial Space", Sequences and Their Applications - SETA, 290--298, 2008. """ - cdef dsfmt_state *rng_state + cdef dsfmt_state *rng_state cdef brng_t *_brng cdef public object capsule cdef public object _cffi cdef public object _ctypes cdef public object _generator + cdef public object lock def __init__(self, seed=None): self.rng_state = malloc(sizeof(dsfmt_state)) @@ -151,6 +155,8 @@ cdef class DSFMT: self.rng_state.buffer_loc = DSFMT_N64 self._brng = malloc(sizeof(brng_t)) self.seed(seed) + self.lock = Lock() + self._brng.state = self.rng_state self._brng.next_uint64 = &dsfmt_uint64 self._brng.next_uint32 = &dsfmt_uint32 @@ -185,17 +191,39 @@ cdef class DSFMT: cdef _reset_state_variables(self): self.rng_state.buffer_loc = DSFMT_N64 + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(self._brng, self.lock, size, output) def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None): """ @@ -289,14 +317,14 @@ cdef class DSFMT: for j in range(2): state[loc] = self.rng_state.state.status[i].u[j] loc += 1 - buffered_uniforms = np.empty(DSFMT_N64,dtype=np.double) + buffered_uniforms = np.empty(DSFMT_N64, dtype=np.double) for i in range(DSFMT_N64): buffered_uniforms[i] = self.rng_state.buffered_uniforms[i] return {'brng': self.__class__.__name__, - 'state': {'state':np.asarray(state), - 'idx':self.rng_state.state.idx}, + 'state': {'state': np.asarray(state), + 'idx': self.rng_state.state.idx}, 'buffer_loc': self.rng_state.buffer_loc, - 'buffered_uniforms':np.asarray(buffered_uniforms)} + 'buffered_uniforms': np.asarray(buffered_uniforms)} @state.setter def state(self, value): @@ -321,12 +349,12 @@ cdef class DSFMT: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -335,25 +363,10 @@ cdef class DSFMT: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&dsfmt_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&dsfmt_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&dsfmt_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -374,19 +387,8 @@ cdef class DSFMT: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/entropy.pyx b/numpy/random/randomgen/entropy.pyx index d7822bfdf412..0e429e9f2631 100644 --- a/numpy/random/randomgen/entropy.pyx +++ b/numpy/random/randomgen/entropy.pyx @@ -1,7 +1,3 @@ -from __future__ import absolute_import - -import operator - cimport numpy as np import numpy as np @@ -25,6 +21,7 @@ cdef Py_ssize_t compute_numel(size): n = size return n + def seed_by_array(object seed, Py_ssize_t n): """ Transforms a seed array into an initial state @@ -150,4 +147,4 @@ def random_entropy(size=None, source='system'): if n == 0: return random - return np.asarray(randoms).reshape(size) \ No newline at end of file + return np.asarray(randoms).reshape(size) diff --git a/numpy/random/randomgen/examples/cython/extending.pyx b/numpy/random/randomgen/examples/cython/extending.pyx index c387a13af4f3..a8d314bb19a3 100644 --- a/numpy/random/randomgen/examples/cython/extending.pyx +++ b/numpy/random/randomgen/examples/cython/extending.pyx @@ -1,13 +1,17 @@ +#cython: language_level=3 +from libc.stdint cimport uint32_t +from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer + import numpy as np cimport numpy as np cimport cython -from libc.stdint cimport uint32_t -from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer + from randomgen.common cimport brng_t from randomgen.xoroshiro128 import Xoroshiro128 np.import_array() + def uniform_mean(Py_ssize_t N): cdef Py_ssize_t i cdef brng_t *rng @@ -26,6 +30,7 @@ def uniform_mean(Py_ssize_t N): randoms = np.asarray(random_values) return randoms.mean() + cdef uint32_t bounded_uint(uint32_t lb, uint32_t ub, brng_t *rng): cdef uint32_t mask, delta, val mask = delta = ub - lb @@ -41,6 +46,7 @@ cdef uint32_t bounded_uint(uint32_t lb, uint32_t ub, brng_t *rng): return lb + val + @cython.boundscheck(False) @cython.wraparound(False) def bounded_uints(uint32_t lb, uint32_t ub, Py_ssize_t n): diff --git a/numpy/random/randomgen/examples/cython/extending_distributions.pyx b/numpy/random/randomgen/examples/cython/extending_distributions.pyx index 630d952bf75e..03f04af041a5 100644 --- a/numpy/random/randomgen/examples/cython/extending_distributions.pyx +++ b/numpy/random/randomgen/examples/cython/extending_distributions.pyx @@ -1,3 +1,4 @@ +#cython: language_level=3 import numpy as np cimport numpy as np cimport cython @@ -6,6 +7,7 @@ from randomgen.common cimport * from randomgen.distributions cimport random_gauss_zig from randomgen.xoroshiro128 import Xoroshiro128 + @cython.boundscheck(False) @cython.wraparound(False) def normals_zig(Py_ssize_t n): @@ -25,24 +27,25 @@ def normals_zig(Py_ssize_t n): randoms = np.asarray(random_values) return randoms + @cython.boundscheck(False) @cython.wraparound(False) def uniforms(Py_ssize_t n): - cdef Py_ssize_t i - cdef brng_t *rng - cdef const char *capsule_name = "BasicRNG" - cdef double[::1] random_values + cdef Py_ssize_t i + cdef brng_t *rng + cdef const char *capsule_name = "BasicRNG" + cdef double[::1] random_values - x = Xoroshiro128() - capsule = x.capsule - # Optional check that the capsule if from a Basic RNG - if not PyCapsule_IsValid(capsule, capsule_name): - raise ValueError("Invalid pointer to anon_func_state") - # Cast the pointer - rng = PyCapsule_GetPointer(capsule, capsule_name) - random_values = np.empty(n) - for i in range(n): - # Call the function - random_values[i] = rng.next_double(rng.state) - randoms = np.asarray(random_values) - return randoms \ No newline at end of file + x = Xoroshiro128() + capsule = x.capsule + # Optional check that the capsule if from a Basic RNG + if not PyCapsule_IsValid(capsule, capsule_name): + raise ValueError("Invalid pointer to anon_func_state") + # Cast the pointer + rng = PyCapsule_GetPointer(capsule, capsule_name) + random_values = np.empty(n) + for i in range(n): + # Call the function + random_values[i] = rng.next_double(rng.state) + randoms = np.asarray(random_values) + return randoms diff --git a/numpy/random/randomgen/examples/numba/extending.py b/numpy/random/randomgen/examples/numba/extending.py index 4dafeb5c4d12..72e903b1f19a 100644 --- a/numpy/random/randomgen/examples/numba/extending.py +++ b/numpy/random/randomgen/examples/numba/extending.py @@ -26,7 +26,7 @@ def bounded_uint(lb, ub, state): return lb + val -bounded_uint(323, 2394691, s.value) +print(bounded_uint(323, 2394691, s.value)) @nb.jit(nopython=True) diff --git a/numpy/random/randomgen/generator.pyx b/numpy/random/randomgen/generator.pyx index 23482ce7ffb2..ffd269f9820b 100644 --- a/numpy/random/randomgen/generator.pyx +++ b/numpy/random/randomgen/generator.pyx @@ -1,7 +1,5 @@ #!python #cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 -from __future__ import absolute_import - import operator import warnings @@ -14,28 +12,14 @@ cimport numpy as np import numpy as np cimport cython -try: - from threading import Lock -except ImportError: - from dummy_threading import Lock - from .bounded_integers cimport * +from .bounded_integers import _randint_types from .common cimport * from .distributions cimport * from .xoroshiro128 import Xoroshiro128 np.import_array() -_randint_types = {'bool': (0, 2), - 'int8': (-2**7, 2**7), - 'int16': (-2**15, 2**15), - 'int32': (-2**31, 2**31), - 'int64': (-2**63, 2**63), - 'uint8': (0, 2**8), - 'uint16': (0, 2**16), - 'uint32': (0, 2**32), - 'uint64': (0, 2**64) - } cdef class RandomGenerator: """ @@ -106,7 +90,7 @@ cdef class RandomGenerator: raise ValueError("Invalid brng. The brng must be instantized.") self._brng = PyCapsule_GetPointer(capsule, name) self._binomial = malloc(sizeof(binomial_t)) - self.lock = Lock() + self.lock = brng.lock def __dealloc__(self): free(self._binomial) @@ -146,7 +130,7 @@ cdef class RandomGenerator: The best method to access seed is to directly use a basic RNG instance. This example demonstrates this best practice. - >>> from np.random.randomgen import RandomGenerator, PCG64 + >>> from numpy.random.randomgen import RandomGenerator, PCG64 >>> brng = PCG64(1234567891011) >>> rg = brng.generator >>> brng.seed(1110987654321) @@ -161,6 +145,7 @@ cdef class RandomGenerator: >>> rg = RandomGenerator(PCG64(1234567891011)) >>> rg.seed(1110987654321) + """ # TODO: Should this remain self._basicrng.seed(*args, **kwargs) @@ -181,6 +166,7 @@ cdef class RandomGenerator: ----- This is a trivial pass-through function. RandomGenerator does not directly contain or manipulate the basic RNG's state. + """ return self._basicrng.state @@ -188,122 +174,6 @@ cdef class RandomGenerator: def state(self, value): self._basicrng.state = value - def random_uintegers(self, size=None, int bits=64): - """ - random_uintegers(size=None, bits=64) - - Return random unsigned integers - - Parameters - ---------- - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - bits : int {32, 64} - Size of the unsigned integer to return, either 32 bit or 64 bit. - - Returns - ------- - out : uint or ndarray - Drawn samples. - - Notes - ----- - This method effectively exposes access to the raw underlying - pseudo-random number generator since these all produce unsigned - integers. In practice these are most useful for generating other - random numbers. - - These should not be used to produce bounded random numbers by - simple truncation. - """ - cdef np.npy_intp i, n - cdef np.ndarray array - cdef uint32_t* data32 - cdef uint64_t* data64 - if bits == 64: - if size is None: - with self.lock: - return self._brng.next_uint64(self._brng.state) - array = np.empty(size, np.uint64) - n = np.PyArray_SIZE(array) - data64 = np.PyArray_DATA(array) - with self.lock, nogil: - for i in range(n): - data64[i] = self._brng.next_uint64(self._brng.state) - elif bits == 32: - if size is None: - with self.lock: - return self._brng.next_uint32(self._brng.state) - array = np.empty(size, np.uint32) - n = np.PyArray_SIZE(array) - data32 = np.PyArray_DATA(array) - with self.lock, nogil: - for i in range(n): - data32[i] = self._brng.next_uint32(self._brng.state) - else: - raise ValueError('Unknown value of bits. Must be either 32 or 64.') - - return array - - def random_raw(self, size=None, output=True): - """ - random_raw(self, size=None) - - Return randoms as generated by the underlying PRNG - - Parameters - ---------- - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - output : bool, optional - Output values. Used for performance testing since the generated - values are not returned. - - Returns - ------- - out : uint or ndarray - Drawn samples. - - Notes - ----- - This method directly exposes the the raw underlying pseudo-random - number generator. All values are returned as unsigned 64-bit - values irrespective of the number of bits produced by the PRNG. - - See the class docstring for the number of bits returned. - """ - cdef np.ndarray randoms - cdef uint64_t *randoms_data - cdef Py_ssize_t i, n - - if not output: - if size is None: - with self.lock: - self._brng.next_raw(self._brng.state) - return None - n = np.asarray(size).sum() - with self.lock, nogil: - for i in range(n): - self._brng.next_raw(self._brng.state) - return None - - if size is None: - with self.lock: - return self._brng.next_raw(self._brng.state) - - randoms = np.empty(size, np.uint64) - randoms_data = np.PyArray_DATA(randoms) - n = np.PyArray_SIZE(randoms) - - with self.lock, nogil: - for i in range(n): - randoms_data[i] = self._brng.next_raw(self._brng.state) - return randoms - def random_sample(self, size=None, dtype=np.float64, out=None): """ random_sample(size=None, dtype='d', out=None) @@ -352,6 +222,7 @@ cdef class RandomGenerator: array([[-3.99149989, -0.52338984], # random [-2.99091858, -0.79479508], [-1.23204345, -1.75224494]]) + """ cdef double temp key = np.dtype(dtype).name @@ -405,7 +276,6 @@ cdef class RandomGenerator: b, 'b', CONS_POSITIVE, 0.0, '', CONS_NONE, None) - def exponential(self, scale=1.0, size=None): """ exponential(scale=1.0, size=None) @@ -495,6 +365,7 @@ cdef class RandomGenerator: Output a 3x8000 array: >>> n = np.random.standard_exponential((3, 8000)) + """ key = np.dtype(dtype).name if key == 'float64': @@ -658,37 +529,38 @@ cdef class RandomGenerator: ---------- .. [1] Daniel Lemire., "Fast Random Integer Generation in an Interval", CoRR, Aug. 13, 2018, http://arxiv.org/abs/1805.10941. + """ if high is None: high = low low = 0 key = np.dtype(dtype).name - if not key in _randint_types: + if key not in _randint_types: raise TypeError('Unsupported dtype "%s" for randint' % key) if key == 'int32': - ret = _rand_int32(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_int32(low, high, size, use_masked, self._brng, self.lock) elif key == 'int64': - ret = _rand_int64(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_int64(low, high, size, use_masked, self._brng, self.lock) elif key == 'int16': - ret = _rand_int16(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_int16(low, high, size, use_masked, self._brng, self.lock) elif key == 'int8': - ret = _rand_int8(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_int8(low, high, size, use_masked, self._brng, self.lock) elif key == 'uint64': - ret = _rand_uint64(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_uint64(low, high, size, use_masked, self._brng, self.lock) elif key == 'uint32': - ret = _rand_uint32(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_uint32(low, high, size, use_masked, self._brng, self.lock) elif key == 'uint16': - ret = _rand_uint16(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_uint16(low, high, size, use_masked, self._brng, self.lock) elif key == 'uint8': - ret = _rand_uint8(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_uint8(low, high, size, use_masked, self._brng, self.lock) elif key == 'bool': - ret = _rand_bool(low, high, size, use_masked, self._brng, self.lock) + ret = _rand_bool(low, high, size, use_masked, self._brng, self.lock) if size is None and dtype in (np.bool, np.int, np.long): - if np.array(ret).shape == (): - return dtype(ret) + if np.array(ret).shape == (): + return dtype(ret) return ret def bytes(self, np.npy_intp length): @@ -716,7 +588,6 @@ cdef class RandomGenerator: cdef Py_ssize_t n_uint32 = ((length - 1) // 4 + 1) return self.randint(0, 4294967296, size=n_uint32, dtype=np.uint32).tobytes()[:length] - @cython.wraparound(True) def choice(self, a, size=None, replace=True, p=None): """ @@ -849,7 +720,7 @@ cdef class RandomGenerator: cdf /= cdf[-1] uniform_samples = self.random_sample(shape) idx = cdf.searchsorted(uniform_samples, side='right') - idx = np.array(idx, copy=False) # searchsorted returns a scalar + idx = np.array(idx, copy=False) # searchsorted returns a scalar else: idx = self.randint(0, pop_size, size=shape) else: @@ -888,7 +759,7 @@ cdef class RandomGenerator: # In most cases a scalar will have been made an array idx = idx.item(0) - #Use samples as indices for a if a is array-like + # Use samples as indices for a if a is array-like if a.ndim == 0: return idx @@ -904,7 +775,6 @@ cdef class RandomGenerator: return a[idx] - def uniform(self, low=0.0, high=1.0, size=None): """ uniform(low=0.0, high=1.0, size=None) @@ -980,6 +850,7 @@ cdef class RandomGenerator: >>> count, bins, ignored = plt.hist(s, 15, density=True) >>> plt.plot(bins, np.ones_like(bins), linewidth=2, color='r') >>> plt.show() + """ cdef bint is_scalar = True cdef np.ndarray alow, ahigh, arange @@ -1003,8 +874,10 @@ cdef class RandomGenerator: None) temp = np.subtract(ahigh, alow) - Py_INCREF(temp) # needed to get around Pyrex's automatic reference-counting - # rules because EnsureArray steals a reference + # needed to get around Pyrex's automatic reference-counting + # rules because EnsureArray steals a reference + Py_INCREF(temp) + arange = np.PyArray_EnsureArray(temp) if not np.all(np.isfinite(arange)): raise OverflowError('Range exceeds valid bounds') @@ -1049,19 +922,13 @@ cdef class RandomGenerator: -------- random - Notes - ----- - This is a convenience function. If you want an interface that takes - a shape-tuple as the first argument, refer to `numpy.random.random_sample`. - - ``dtype`` can only be changed using a keyword argument. - Examples -------- >>> np.random.rand(3,2) array([[ 0.14022471, 0.96360618], #random [ 0.37601032, 0.25528411], #random [ 0.49313049, 0.94909878]]) #random + """ if len(args) == 0: return self.random_sample(dtype=dtype) @@ -1086,9 +953,6 @@ cdef class RandomGenerator: distribution of mean 0 and variance 1. A single float randomly sampled from the distribution is returned if no argument is provided. - This is a convenience function. If you want an interface that takes a - tuple as the first argument, use `numpy.random.standard_normal` instead. - Parameters ---------- d0, d1, ..., dn : int, optional @@ -1109,7 +973,7 @@ cdef class RandomGenerator: See Also -------- standard_normal : Similar, but takes a tuple as its argument. - normal : Also accepts mu and sigma arguments + normal : Also accepts mu and sigma arguments. Notes ----- @@ -1138,7 +1002,7 @@ cdef class RandomGenerator: """ random_integers(low, high=None, size=None) - Random integers of type np.int64 between `low` and `high`, inclusive. + Random integers of type np.int between `low` and `high`, inclusive. Return random integers of type np.int64 from the "discrete uniform" distribution in the closed interval [`low`, `high`]. If `high` is @@ -1221,8 +1085,9 @@ cdef class RandomGenerator: else: warnings.warn(("This function is deprecated. Please call " - "randint({low}, {high} + 1) instead".format( - low=low, high=high)), DeprecationWarning) + "randint({low}, {high} + 1)" + "instead".format(low=low, high=high)), + DeprecationWarning) return self.randint(low, high + 1, size=size, dtype='l') @@ -1298,7 +1163,6 @@ cdef class RandomGenerator: else: raise TypeError('Unsupported dtype "%s" for standard_normal' % key) - def normal(self, loc=0.0, scale=1.0, size=None): """ normal(loc=0.0, scale=1.0, size=None) @@ -1457,10 +1321,11 @@ cdef class RandomGenerator: Draw samples from the distribution: >>> s = np.random.complex_normal(size=1000) + """ cdef np.ndarray ogamma, orelation, oloc, randoms, v_real, v_imag, rho cdef double *randoms_data - cdef double fgamma_r, fgamma_i, frelation_r, frelation_i, frho, fvar_r , fvar_i, \ + cdef double fgamma_r, fgamma_i, frelation_r, frelation_i, frho, fvar_r, fvar_i, \ floc_r, floc_i, f_real, f_imag, i_r_scale, r_scale, i_scale, f_rho cdef np.npy_intp i, j, n, n2 cdef np.broadcast it @@ -1530,7 +1395,7 @@ cdef class RandomGenerator: cov = 0.5 * np.imag(orelation) rho = np.zeros_like(cov) idx = (v_real.flat > 0) & (v_imag.flat > 0) - rho.flat[idx] = cov.flat[idx] / np.sqrt(v_real.flat[idx] * v_imag.flat[idx]) + rho.flat[idx] = cov.flat[idx] / np.sqrt(v_real.flat[idx] * v_imag.flat[idx]) if np.any(cov.flat[~idx] != 0) or np.any(np.abs(rho) > 1): raise ValueError('Im(relation) ** 2 > Re(gamma ** 2 - relation ** 2)') @@ -1637,6 +1502,7 @@ cdef class RandomGenerator: ... (sps.gamma(shape) * scale**shape)) >>> plt.plot(bins, y, linewidth=2, color='r') >>> plt.show() + """ cdef void *func key = np.dtype(dtype).name @@ -1711,7 +1577,7 @@ cdef class RandomGenerator: -------- Draw samples from the distribution: - >>> shape, scale = 2., 2. # mean and dispersion + >>> shape, scale = 2., 2. # mean=4, std=2*sqrt(2) >>> s = np.random.gamma(shape, scale, 1000) Display the histogram of the samples, along with @@ -1749,10 +1615,10 @@ cdef class RandomGenerator: Parameters ---------- - dfnum : int or array_like of ints - Degrees of freedom in numerator. Must be non-negative. - dfden : int or array_like of ints - Degrees of freedom in denominator. Must be non-negative. + dfnum : float or array_like of floats + Degrees of freedom in numerator, should be > 0. + dfden : float or array_like of float + Degrees of freedom in denominator, should be > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -1832,12 +1698,16 @@ cdef class RandomGenerator: Parameters ---------- - dfnum : int or array_like of ints - Parameter, should be > 1. - dfden : int or array_like of ints - Parameter, should be > 1. + dfnum : float or array_like of floats + Numerator degrees of freedom, should be > 0. + + .. versionchanged:: 1.14.0 + Earlier NumPy versions required dfnum > 1. + dfden : float or array_like of floats + Denominator degrees of freedom, should be > 0. nonc : float or array_like of floats - Parameter, should be >= 0. + Non-centrality parameter, the sum of the squares of the numerator + means, should be >= 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -1905,8 +1775,8 @@ cdef class RandomGenerator: Parameters ---------- - df : int or array_like of ints - Number of degrees of freedom. + df : float or array_like of floats + Number of degrees of freedom, should be > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -1971,9 +1841,11 @@ cdef class RandomGenerator: Parameters ---------- - df : int or array_like of ints - Degrees of freedom, should be > 0 as of NumPy 1.10.0, - should be > 1 for earlier versions. + df : float or array_like of floats + Degrees of freedom, should be > 0. + + .. versionchanged:: 1.10.0 + Earlier NumPy versions required dfnum > 1. nonc : float or array_like of floats Non-centrality, should be non-negative. size : int or tuple of ints, optional @@ -1994,14 +1866,14 @@ cdef class RandomGenerator: .. math:: P(x;df,nonc) = \\sum^{\\infty}_{i=0} \\frac{e^{-nonc/2}(nonc/2)^{i}}{i!} - \\P_{Y_{df+2i}}(x), + P_{Y_{df+2i}}(x), where :math:`Y_{q}` is the Chi-square with q degrees of freedom. References ---------- - .. [1] Wikipedia, "Noncentral chi-square distribution" - https://en.wikipedia.org/wiki/Noncentral_chi-square_distribution + .. [1] Wikipedia, "Noncentral chi-squared distribution" + https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution Examples -------- @@ -2081,7 +1953,7 @@ cdef class RandomGenerator: ---------- .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "Cauchy Distribution", - http://www.itl.nist.gov/div898/handbook/eda/section3/eda3663.htm + https://www.itl.nist.gov/div898/handbook/eda/section3/eda3663.htm .. [2] Weisstein, Eric W. "Cauchy Distribution." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/CauchyDistribution.html @@ -2092,9 +1964,9 @@ cdef class RandomGenerator: -------- Draw samples and plot the distribution: + >>> import matplotlib.pyplot as plt >>> s = np.random.standard_cauchy(1000000) >>> s = s[(s>-25) & (s<25)] # truncate distribution so it plots well - >>> import matplotlib.pyplot as plt >>> plt.hist(s, bins=100) >>> plt.show() @@ -2115,7 +1987,7 @@ cdef class RandomGenerator: Parameters ---------- - df : int or array_like of ints + df : float or array_like of floats Degrees of freedom, should be > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then @@ -2306,7 +2178,7 @@ cdef class RandomGenerator: Parameters ---------- a : float or array_like of floats - Shape of the distribution. All values must be positive. + Shape of the distribution. Must all be positive. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2487,7 +2359,7 @@ cdef class RandomGenerator: Parameters ---------- a : float or array_like of floats - Parameter of the distribution. Must be positive. + Parameter of the distribution. Must be non-negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2525,7 +2397,7 @@ cdef class RandomGenerator: Dataplot Reference Manual, Volume 2: Let Subcommands and Library Functions", National Institute of Standards and Technology Handbook Series, June 2003. - http://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/powpdf.pdf + https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/powpdf.pdf Examples -------- @@ -2592,8 +2464,8 @@ cdef class RandomGenerator: loc : float or array_like of floats, optional The position, :math:`\\mu`, of the distribution peak. Default is 0. scale : float or array_like of floats, optional - :math:`\\lambda`, the exponential decay. Default is 1. Must be - non-negative. + :math:`\\lambda`, the exponential decay. Default is 1. Must be non- + negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2675,8 +2547,8 @@ cdef class RandomGenerator: loc : float or array_like of floats, optional The location of the mode of the distribution. Default is 0. scale : float or array_like of floats, optional - The scale parameter of the distribution. Default is 1. Must be - non-negative. + The scale parameter of the distribution. Default is 1. Must be non- + negative. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2792,7 +2664,7 @@ cdef class RandomGenerator: loc : float or array_like of floats, optional Parameter of the distribution. Default is 0. scale : float or array_like of floats, optional - Parameter of the distribution. Must be >= 0. + Parameter of the distribution. Must be non-negative. Default is 1. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then @@ -2848,8 +2720,8 @@ cdef class RandomGenerator: >>> def logist(x, loc, scale): ... return np.exp((loc-x)/scale)/(scale*(1+np.exp((loc-x)/scale))**2) - >>> scale = logist(bins, loc, scale).max() - >>> plt.plot(bins, logist(bins, loc, scale)*count.max()/scale) + >>> plt.plot(bins, logist(bins, loc, scale)*count.max()/\ + ... logist(bins, loc, scale).max()) >>> plt.show() """ @@ -3087,8 +2959,8 @@ cdef class RandomGenerator: .. [2] Chhikara, Raj S., and Folks, J. Leroy, "The Inverse Gaussian Distribution: Theory : Methodology, and Applications", CRC Press, 1988. - .. [3] Wikipedia, "Wald distribution" - https://en.wikipedia.org/wiki/Wald_distribution + .. [3] Wikipedia, "Inverse Gaussian distribution" + https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution Examples -------- @@ -3337,7 +3209,7 @@ cdef class RandomGenerator: randoms = np.empty(size, np.int64) cnt = np.PyArray_SIZE(randoms) - randoms_data = np.PyArray_DATA(randoms) + randoms_data = np.PyArray_DATA(randoms) with self.lock, nogil: for i in range(cnt): @@ -3346,7 +3218,6 @@ cdef class RandomGenerator: return randoms - def negative_binomial(self, n, p, size=None): """ negative_binomial(n, p, size=None) @@ -3354,14 +3225,13 @@ cdef class RandomGenerator: Draw samples from a negative binomial distribution. Samples are drawn from a negative binomial distribution with specified - parameters, `n` successes and `p` probability of success where `n` is an - integer > 0 and `p` is in the interval [0, 1]. + parameters, `n` successes and `p` probability of success where `n` + is > 0 and `p` is in the interval [0, 1]. Parameters ---------- - n : int or array_like of ints - Parameter of the distribution, > 0. Floats are also accepted, - but they will be truncated to integers. + n : float or array_like of floats + Parameter of the distribution, > 0. p : float or array_like of floats Parameter of the distribution, >= 0 and <=1. size : int or tuple of ints, optional @@ -3379,14 +3249,17 @@ cdef class RandomGenerator: Notes ----- - The probability density for the negative binomial distribution is + The probability mass function of the negative binomial distribution is - .. math:: P(N;n,p) = \\binom{N+n-1}{N}p^{n}(1-p)^{N}, + .. math:: P(N;n,p) = \\frac{\\Gamma(N+n)}{N!\\Gamma(n)}p^{n}(1-p)^{N}, where :math:`n` is the number of successes, :math:`p` is the - probability of success, and :math:`N+n` is the number of trials. - The negative binomial distribution gives the probability of N - failures given n successes, with a success on the last trial. + probability of success, :math:`N+n` is the number of trials, and + :math:`\\Gamma` is the gamma function. When :math:`n` is an integer, + :math:`\\frac{\\Gamma(N+n)}{N!\\Gamma(n)} = \\binom{N+n-1}{N}`, which is + the more common form of this term in the the pmf. The negative + binomial distribution gives the probability of N failures given n + successes, with a success on the last trial. If one throws a die repeatedly until the third time a "1" appears, then the probability distribution of the number of non-"1"s that @@ -3417,9 +3290,9 @@ cdef class RandomGenerator: """ return disc(&random_negative_binomial, self._brng, size, self.lock, 2, 0, - n, 'n', CONS_POSITIVE, - p, 'p', CONS_BOUNDED_0_1, - 0.0, '', CONS_NONE) + n, 'n', CONS_POSITIVE, + p, 'p', CONS_BOUNDED_0_1, + 0.0, '', CONS_NONE) def poisson(self, lam=1.0, size=None): """ @@ -3488,9 +3361,9 @@ cdef class RandomGenerator: """ return disc(&random_poisson, self._brng, size, self.lock, 1, 0, - lam, 'lam', CONS_POISSON, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE) + lam, 'lam', CONS_POISSON, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) def zipf(self, a, size=None): """ @@ -3567,9 +3440,9 @@ cdef class RandomGenerator: """ return disc(&random_zipf, self._brng, size, self.lock, 1, 0, - a, 'a', CONS_GT_1, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE) + a, 'a', CONS_GT_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) def geometric(self, p, size=None): """ @@ -3618,9 +3491,9 @@ cdef class RandomGenerator: """ return disc(&random_geometric, self._brng, size, self.lock, 1, 0, - p, 'p', CONS_BOUNDED_GT_0_1, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE) + p, 'p', CONS_BOUNDED_GT_0_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) def hypergeometric(self, ngood, nbad, nsample, size=None): """ @@ -3631,7 +3504,7 @@ cdef class RandomGenerator: Samples are drawn from a hypergeometric distribution with specified parameters, `ngood` (ways to make a good selection), `nbad` (ways to make a bad selection), and `nsample` (number of items sampled, which is less - than or equal to the sum ``ngood + nbad``) + than or equal to the sum ``ngood + nbad``). Parameters ---------- @@ -3727,12 +3600,6 @@ cdef class RandomGenerator: lnbad = nbad lnsample = nsample - if lngood < 0: - raise ValueError("ngood < 0") - if lnbad < 0: - raise ValueError("nbad < 0") - if lnsample < 1: - raise ValueError("nsample < 1") if lngood + lnbad < lnsample: raise ValueError("ngood + nbad < nsample") return disc(&random_hypergeometric, self._brng, size, self.lock, 0, 3, @@ -3740,7 +3607,7 @@ cdef class RandomGenerator: lnbad, 'nbad', CONS_NON_NEGATIVE, lnsample, 'nsample', CONS_GTE_1) - if np.any(np.less(np.add(ongood, onbad),onsample)): + if np.any(np.less(np.add(ongood, onbad), onsample)): raise ValueError("ngood + nbad < nsample") return discrete_broadcast_iii(&random_hypergeometric, self._brng, size, self.lock, ongood, 'ngood', CONS_NON_NEGATIVE, @@ -3858,7 +3725,7 @@ cdef class RandomGenerator: Behavior when the covariance matrix is not positive semidefinite. tol : float, optional Tolerance when checking the singular values in covariance matrix. - `cov` is cast to double before the check. + cov is cast to double before the check. Returns ------- @@ -3927,7 +3794,7 @@ cdef class RandomGenerator: standard deviation: >>> list((x[0,0,:] - mean) < 0.6) - [True, False] # random + [True, True] # random """ from numpy.dual import svd @@ -3943,11 +3810,11 @@ cdef class RandomGenerator: shape = size if len(mean.shape) != 1: - raise ValueError("mean must be 1 dimensional") + raise ValueError("mean must be 1 dimensional") if (len(cov.shape) != 2) or (cov.shape[0] != cov.shape[1]): - raise ValueError("cov must be 2 dimensional and square") + raise ValueError("cov must be 2 dimensional and square") if mean.shape[0] != cov.shape[0]: - raise ValueError("mean and cov must have same length") + raise ValueError("mean and cov must have same length") # Compute shape of output and create a matrix of independent # standard normally distributed random numbers. The matrix has rows @@ -3977,17 +3844,15 @@ cdef class RandomGenerator: if check_valid != 'ignore': if check_valid != 'warn' and check_valid != 'raise': - raise ValueError( - "check_valid must equal 'warn', 'raise', or 'ignore'") + raise ValueError("check_valid must equal 'warn', 'raise', or 'ignore'") psd = np.allclose(np.dot(v.T * s, v), cov, rtol=tol, atol=tol) if not psd: if check_valid == 'warn': warnings.warn("covariance is not positive-semidefinite.", - RuntimeWarning) + RuntimeWarning) else: - raise ValueError( - "covariance is not positive-semidefinite.") + raise ValueError("covariance is not positive-semidefinite.") x = np.dot(x, np.sqrt(s)[:, None] * v) x += mean @@ -4036,15 +3901,15 @@ cdef class RandomGenerator: Throw a dice 20 times: >>> np.random.multinomial(20, [1/6.]*6, size=1) - array([[2, 5, 3, 3, 3, 4]]) # random + array([[4, 1, 7, 5, 2, 1]]) # random It landed 4 times on 1, once on 2, etc. Now, throw the dice 20 times, and 20 times again: >>> np.random.multinomial(20, [1/6.]*6, size=2) - array([[5, 2, 1, 2, 5, 5], - [5, 3, 4, 4, 2, 2]]) # random + array([[3, 4, 3, 3, 4, 3], + [2, 4, 3, 4, 0, 7]]) # random For the first run, we threw 3 times 1, 4 times 2, etc. For the second, we threw 2 times 1, 4 times 2, etc. @@ -4061,7 +3926,7 @@ cdef class RandomGenerator: other should be sampled like so: >>> np.random.multinomial(100, [1.0 / 3, 2.0 / 3]) # RIGHT - array([38, 62]) # random + array([38, 62]) # random not like: @@ -4192,39 +4057,36 @@ cdef class RandomGenerator: """ - #================= + # ================= # Pure python algo - #================= - #alpha = N.atleast_1d(alpha) - #k = alpha.size - - #if n == 1: - # val = N.zeros(k) - # for i in range(k): - # val[i] = sgamma(alpha[i], n) - # val /= N.sum(val) - #else: - # val = N.zeros((k, n)) - # for i in range(k): - # val[i] = sgamma(alpha[i], n) - # val /= N.sum(val, axis = 0) - # val = val.T - - #return val - - cdef np.npy_intp k - cdef np.npy_intp totsize - cdef np.ndarray alpha_arr, val_arr - cdef double *alpha_data - cdef double *val_data - cdef np.npy_intp i, j - cdef double acc, invacc - - k = len(alpha) - alpha_arr = np.PyArray_FROM_OTF(alpha, np.NPY_DOUBLE, np.NPY_ALIGNED) + # ================= + # alpha = N.atleast_1d(alpha) + # k = alpha.size + + # if n == 1: + # val = N.zeros(k) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val) + # else: + # val = N.zeros((k, n)) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val, axis = 0) + # val = val.T + # return val + + cdef np.npy_intp k, totsize, i, j + cdef np.ndarray alpha_arr, val_arr + cdef double *alpha_data + cdef double *val_data + cdef double acc, invacc + + k = len(alpha) + alpha_arr = np.PyArray_FROM_OTF(alpha, np.NPY_DOUBLE, np.NPY_ALIGNED) if np.any(np.less_equal(alpha_arr, 0)): raise ValueError('alpha <= 0') - alpha_data = np.PyArray_DATA(alpha_arr) + alpha_data = np.PyArray_DATA(alpha_arr) if size is None: shape = (k,) @@ -4234,7 +4096,7 @@ cdef class RandomGenerator: except: shape = tuple(size) + (k,) - diric = np.zeros(shape, np.float64) + diric = np.zeros(shape, np.float64) val_arr = diric val_data= np.PyArray_DATA(val_arr) @@ -4246,10 +4108,10 @@ cdef class RandomGenerator: for j in range(k): val_data[i+j] = random_standard_gamma_zig(self._brng, alpha_data[j]) - acc = acc + val_data[i + j] - invacc = 1/acc + acc = acc + val_data[i + j] + invacc = 1/acc for j in range(k): - val_data[i + j] = val_data[i + j] * invacc + val_data[i + j] = val_data[i + j] * invacc i = i + k return diric @@ -4307,7 +4169,7 @@ cdef class RandomGenerator: # of bytes for the swaps to avoid leaving one of the objects # within the buffer and erroneously decrementing it's refcount # when the function exits. - buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit + buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit buf_ptr = buf.ctypes.data with self.lock: # We trick gcc into providing a specialized implementation for @@ -4318,11 +4180,13 @@ cdef class RandomGenerator: else: self._shuffle_raw(n, itemsize, stride, x_ptr, buf_ptr) elif isinstance(x, np.ndarray) and x.ndim and x.size: - buf = np.empty_like(x[0,...]) + buf = np.empty_like(x[0, ...]) with self.lock: for i in reversed(range(1, n)): j = random_interval(self._brng, i) - if i == j : continue # i == j is not needed and memcpy is undefined. + if i == j: + # i == j is not needed and memcpy is undefined. + continue buf[...] = x[j] x[j] = x[i] x[i] = buf @@ -4431,9 +4295,7 @@ rand = _random_generator.rand randint = _random_generator.randint randn = _random_generator.randn random_integers = _random_generator.random_integers -random_raw = _random_generator.random_raw random_sample = _random_generator.random_sample -random_uintegers = _random_generator.random_uintegers rayleigh = _random_generator.rayleigh shuffle = _random_generator.shuffle standard_cauchy = _random_generator.standard_cauchy diff --git a/numpy/random/randomgen/legacy/__init__.py b/numpy/random/randomgen/legacy/__init__.py index 01f0c13121e6..9ce1f665dd15 100644 --- a/numpy/random/randomgen/legacy/__init__.py +++ b/numpy/random/randomgen/legacy/__init__.py @@ -1,3 +1,3 @@ -from .legacy import LegacyGenerator +from ..mtrand import RandomState as LegacyGenerator __all__ = ['LegacyGenerator'] diff --git a/numpy/random/randomgen/legacy/_legacy.pyx b/numpy/random/randomgen/legacy/_legacy.pyx deleted file mode 100644 index 1b2168374a10..000000000000 --- a/numpy/random/randomgen/legacy/_legacy.pyx +++ /dev/null @@ -1,2263 +0,0 @@ -#!python -#cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 -from __future__ import absolute_import - -import warnings -import operator - -from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer -from libc.stdlib cimport malloc, free -cimport numpy as np -import numpy as np -cimport cython - -try: - from threading import Lock -except ImportError: - from dummy_threading import Lock - -from ..bounded_integers cimport (_rand_int32, _rand_int64, _rand_int16, - _rand_uint32, _rand_uint64, _rand_uint16, - _rand_bool, _rand_int8, _rand_uint8) -from ..common cimport (cont, disc, double_fill, CONS_NONE, CONS_POSITIVE, - CONS_NON_NEGATIVE, CONS_BOUNDED_0_1) -from ..distributions cimport brng_t, random_double_fill -from .legacy_distributions cimport (aug_brng_t, legacy_beta, legacy_pareto, - legacy_exponential, legacy_f, legacy_chisquare, legacy_gamma, - legacy_gauss, legacy_standard_exponential, legacy_negative_binomial, - legacy_normal, legacy_standard_gamma, legacy_standard_cauchy, - legacy_standard_t, legacy_noncentral_f, legacy_noncentral_chisquare, - legacy_negative_binomial, legacy_power, legacy_lognormal, legacy_wald, - legacy_weibull) -from ..xoroshiro128 import Xoroshiro128 - -np.import_array() - -_randint_types = {'bool': (0, 2), - 'int8': (-2**7, 2**7), - 'int16': (-2**15, 2**15), - 'int32': (-2**31, 2**31), - 'int64': (-2**63, 2**63), - 'uint8': (0, 2**8), - 'uint16': (0, 2**16), - 'uint32': (0, 2**32), - 'uint64': (0, 2**64) - } - - -cdef class _LegacyGenerator: - """ - _LegacyGenerator(brng=None) - - Container providing legacy generators. - - ``_LegacyGenerator`` exposes a number of methods for generating random - numbers for a set of distributions where the method used to produce random - samples has changed. Three core generators have changed: normal, exponential - and gamma. These have been replaced by faster Ziggurat-based methods in - ``RandomGenerator``. ``_LegacyGenerator`` retains the slower methods - to produce samples from these distributions as well as from distributions - that depend on these such as the Chi-square, power or Weibull. - - **No Compatibility Guarantee** - - ``_LegacyGenerator`` is evolving and so it isn't possible to provide a - compatibility guarantee like NumPy does. In particular, better algorithms - have already been added. This will change once ``RandomGenerator`` - stabilizes. - - Parameters - ---------- - brng : Basic RNG, optional - Basic RNG to use as the core generator. If none is provided, uses - Xoroshiro128. - - Examples - -------- - Exactly reproducing a NumPy stream requires both a ``RandomGenerator`` - and a ``_LegacyGenerator``. These must share a common ``MT19937`` basic - RNG. Functions that are available in LegacyGenerator must be called - from ``_LegacyGenerator``, and other functions must be called from - ``RandomGenerator``. - - >>> from numpy.random.randomgen import RandomGenerator, MT19937 - >>> from numpy.random.randomgen.legacy._legacy import _LegacyGenerator - >>> mt = MT19937(12345) - >>> lg = _LegacyGenerator(mt) - >>> rg = RandomGenerator(mt) - >>> x = lg.standard_normal(10) - >>> rg.shuffle(x) - >>> x[0] - 0.09290787674371767 - >>> lg.standard_exponential() - 1.6465621229906502 - - The equivalent commands from NumPy produce identical output. - - >>> from numpy.random import RandomState - >>> rs = RandomState(12345) - >>> x = rs.standard_normal(10) - >>> rs.shuffle(x) - >>> x[0] - 0.09290787674371767 - >>> rs.standard_exponential() - 1.6465621229906502 - """ - cdef public object _basicrng - cdef brng_t *_brng - cdef aug_brng_t *_aug_state - cdef object lock - - def __init__(self, brng=None): - if brng is None: - brng = Xoroshiro128() - self._basicrng = brng - - capsule = brng.capsule - cdef const char *name = "BasicRNG" - if not PyCapsule_IsValid(capsule, name): - raise ValueError("Invalid brng. The brng must be instantized.") - self._brng = PyCapsule_GetPointer(capsule, name) - self._aug_state = malloc(sizeof(aug_brng_t)) - self._aug_state.basicrng = self._brng - self._reset_gauss() - self.lock = Lock() - - def __dealloc__(self): - free(self._aug_state) - - def __repr__(self): - return self.__str__() + ' at 0x{:X}'.format(id(self)) - - def __str__(self): - return self.__class__.__name__ + '(' + self._basicrng.__class__.__name__ + ')' - - # Pickling support: - def __getstate__(self): - return self.state - - def __setstate__(self, state): - self.state = state - - def __reduce__(self): - from .._pickle import __legacy_ctor - return (__legacy_ctor, - (self.state['brng'],), - self.state) - - cdef _reset_gauss(self): - self._aug_state.has_gauss = 0 - self._aug_state.gauss = 0.0 - - def seed(self, *args, **kwargs): - """ - Reseed the basic RNG. - - Parameters depend on the basic RNG used. - - Notes - ----- - Arguments are directly passed to the basic RNG. This is a convenience - function. - - The best method to access seed is to directly use a basic RNG instance. - This example demonstrates this best practice. - - >>> from numpy.random.randomgen import MT19937 - >>> from numpy.random.randomgen.legacy import LegacyGenerator - >>> brng = MT19937(123456789) - >>> lg = brng.generator - >>> brng.seed(987654321) - - The method used to create the generator is not important. - - >>> brng = MT19937(123456789) - >>> lg = LegacyGenerator(brng) - >>> brng.seed(987654321) - - These best practice examples are equivalent to - - >>> lg = LegacyGenerator(MT19937(123456789)) - >>> lg.seed(987654321) - """ - - # TODO: Should this remain - self._basicrng.seed(*args, **kwargs) - self._reset_gauss() - - @property - def state(self): - """ - Get or set the augmented state - - Returns the basic RNGs state as well as two values added to track - normal generation using the Polar (Box-Muller-like) method. - - Returns - ------- - state : dict - Dictionary containing the information required to describe the - state of the Basic RNG with two additional fields, gauss and - has_gauss, required to store generated Polar transformation - (Box-Muller-like) normals. - """ - st = self._basicrng.state - st['has_gauss'] = self._aug_state.has_gauss - st['gauss'] = self._aug_state.gauss - return st - - @state.setter - def state(self, value): - if isinstance(value, (tuple, list)): - if value[0] != 'MT19937': - raise ValueError('tuple only supported for MT19937') - st = {'brng': value[0], - 'state': {'key': value[1], 'pos': value[2]}} - if len(value) > 3: - st['has_gauss'] = value[3] - st['gauss'] = value[4] - value = st - self._aug_state.gauss = value.get('gauss', 0.0) - self._aug_state.has_gauss = value.get('has_gauss', 0) - self._basicrng.state = value - - def get_state(self): - """ - get_state() - Return a tuple representing the internal state of the generator. - For more details, see `set_state`. - Returns - ------- - out : tuple(str, ndarray of 624 uints, int, int, float) - The returned tuple has the following items: - 1. the string 'MT19937'. - 2. a 1-D array of 624 unsigned integer keys. - 3. an integer ``pos``. - 4. an integer ``has_gauss``. - 5. a float ``cached_gaussian``. - See Also - -------- - set_state - Notes - ----- - `set_state` and `get_state` are not needed to work with any of the - random distributions in NumPy. If the internal state is manually altered, - the user should know exactly what he/she is doing. - """ - st = self._basicrng.state - if st['brng'] != 'MT19937': - raise RuntimeError('get_state can only be used with the MT19937 ' - 'basic RNG. When using other basic RNGs, ' - 'use `state`.') - st['has_gauss'] = self._aug_state.has_gauss - st['gauss'] = self._aug_state.gauss - - return (st['brng'], st['state']['key'], st['state']['pos'], - st['has_gauss'], st['gauss']) - - def set_state(self, state): - """ - set_state(state) - Set the internal state of the generator from a tuple. - For use if one has reason to manually (re-)set the internal state of the - "Mersenne Twister"[1]_ pseudo-random number generating algorithm. - Parameters - ---------- - state : tuple(str, ndarray of 624 uints, int, int, float) - The `state` tuple has the following items: - 1. the string 'MT19937', specifying the Mersenne Twister algorithm. - 2. a 1-D array of 624 unsigned integers ``keys``. - 3. an integer ``pos``. - 4. an integer ``has_gauss``. - 5. a float ``cached_gaussian``. - Returns - ------- - out : None - Returns 'None' on success. - See Also - -------- - get_state - Notes - ----- - `set_state` and `get_state` are not needed to work with any of the - random distributions in NumPy. If the internal state is manually altered, - the user should know exactly what he/she is doing. - For backwards compatibility, the form (str, array of 624 uints, int) is - also accepted although it is missing some information about the cached - Gaussian value: ``state = ('MT19937', keys, pos)``. - References - ---------- - .. [1] M. Matsumoto and T. Nishimura, "Mersenne Twister: A - 623-dimensionally equidistributed uniform pseudorandom number - generator," *ACM Trans. on Modeling and Computer Simulation*, - Vol. 8, No. 1, pp. 3-30, Jan. 1998. - """ - if not isinstance(state, (tuple, list)): - raise TypeError('state must be a tuple when using set_state. ' - 'Use `state` to set the state using a dictionary.') - if state[0] != 'MT19937': - raise ValueError('set_state can only be used with legacy MT19937' - 'state instances.') - st = {'brng': state[0], - 'state': {'key': state[1], 'pos': state[2]}} - if len(state) > 3: - st['has_gauss'] = state[3] - st['gauss'] = state[4] - self._aug_state.gauss = st.get('gauss', 0.0) - self._aug_state.has_gauss = st.get('has_gauss', 0) - self._basicrng.state = st - - def random_sample(self, size=None): - """ - random_sample(size=None) - Return random floats in the half-open interval [0.0, 1.0). - Results are from the "continuous uniform" distribution over the - stated interval. To sample :math:`Unif[a, b), b > a` multiply - the output of `random_sample` by `(b-a)` and add `a`:: - (b - a) * random_sample() + a - Parameters - ---------- - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - Returns - ------- - out : float or ndarray of floats - Array of random floats of shape `size` (unless ``size=None``, in which - case a single float is returned). - Examples - -------- - np.random.random_sample() - 0.47108547995356098 - type(np.random.random_sample()) - - np.random.random_sample((5,)) - array([ 0.30220482, 0.86820401, 0.1654503 , 0.11659149, 0.54323428]) - Three-by-two array of random numbers from [-5, 0): - 5 * np.random.random_sample((3, 2)) - 5 - array([[-3.99149989, -0.52338984], - [-2.99091858, -0.79479508], - [-1.23204345, -1.75224494]]) - """ - cdef double temp - return double_fill(&random_double_fill, self._brng, size, self.lock, None) - - def beta(self, a, b, size=None): - """ - beta(a, b, size=None) - - Draw samples from a Beta distribution. - - The Beta distribution is a special case of the Dirichlet distribution, - and is related to the Gamma distribution. It has the probability - distribution function - - .. math:: f(x; a,b) = \\frac{1}{B(\\alpha, \\beta)} x^{\\alpha - 1} - (1 - x)^{\\beta - 1}, - - where the normalization, B, is the beta function, - - .. math:: B(\\alpha, \\beta) = \\int_0^1 t^{\\alpha - 1} - (1 - t)^{\\beta - 1} dt. - - It is often seen in Bayesian inference and order statistics. - - Parameters - ---------- - a : float or array_like of floats - Alpha, positive (>0). - b : float or array_like of floats - Beta, positive (>0). - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``a`` and ``b`` are both scalars. - Otherwise, ``np.broadcast(a, b).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized beta distribution. - - """ - return cont(&legacy_beta, self._aug_state, size, self.lock, 2, - a, 'a', CONS_POSITIVE, - b, 'b', CONS_POSITIVE, - 0.0, '', CONS_NONE, None) - - def exponential(self, scale=1.0, size=None): - """ - exponential(scale=1.0, size=None) - - Draw samples from an exponential distribution. - - Its probability density function is - - .. math:: f(x; \\frac{1}{\\beta}) = \\frac{1}{\\beta} \\exp(-\\frac{x}{\\beta}), - - for ``x > 0`` and 0 elsewhere. :math:`\\beta` is the scale parameter, - which is the inverse of the rate parameter :math:`\\lambda = 1/\\beta`. - The rate parameter is an alternative, widely used parameterization - of the exponential distribution [3]_. - - The exponential distribution is a continuous analogue of the - geometric distribution. It describes many common situations, such as - the size of raindrops measured over many rainstorms [1]_, or the time - between page requests to Wikipedia [2]_. - - Parameters - ---------- - scale : float or array_like of floats - The scale parameter, :math:`\\beta = 1/\\lambda`. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``scale`` is a scalar. Otherwise, - ``np.array(scale).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized exponential distribution. - - References - ---------- - .. [1] Peyton Z. Peebles Jr., "Probability, Random Variables and - Random Signal Principles", 4th ed, 2001, p. 57. - .. [2] Wikipedia, "Poisson process", - https://en.wikipedia.org/wiki/Poisson_process - .. [3] Wikipedia, "Exponential distribution", - https://en.wikipedia.org/wiki/Exponential_distribution - - """ - return cont(&legacy_exponential, self._aug_state, size, self.lock, 1, - scale, 'scale', CONS_NON_NEGATIVE, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE, - None) - - def standard_exponential(self, size=None): - """ - standard_exponential(size=None) - - Draw samples from the standard exponential distribution. - - `standard_exponential` is identical to the exponential distribution - with a scale parameter of 1. - - Parameters - ---------- - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - - Returns - ------- - out : float or ndarray - Drawn samples. - - Examples - -------- - Output a 3x8000 array: - - >>> n = np.random.standard_exponential((3, 8000)) - """ - return cont(&legacy_standard_exponential, self._aug_state, size, self.lock, 0, - None, None, CONS_NONE, - None, None, CONS_NONE, - None, None, CONS_NONE, - None) - - def randint(self, low, high=None, size=None, dtype=int): - """ - randint(low, high=None, size=None, dtype='l') - Return random integers from `low` (inclusive) to `high` (exclusive). - Return random integers from the "discrete uniform" distribution of - the specified dtype in the "half-open" interval [`low`, `high`). If - `high` is None (the default), then results are from [0, `low`). - Parameters - ---------- - low : int or array-like of ints - Lowest (signed) integers to be drawn from the distribution (unless - ``high=None``, in which case this parameter is one above the - *highest* such integer). - high : int or array-like of ints, optional - If provided, one above the largest (signed) integer to be drawn - from the distribution (see above for behavior if ``high=None``). - If array-like, must contain integer values - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - dtype : {str, dtype}, optional - Desired dtype of the result. All dtypes are determined by their - name, i.e., 'int64', 'int', etc, so byteorder is not available - and a specific precision may have different C types depending - on the platform. The default value is 'np.int'. - .. versionadded:: 1.11.0 - Returns - ------- - out : int or ndarray of ints - `size`-shaped array of random integers from the appropriate - distribution, or a single such random int if `size` not provided. - See Also - -------- - random.random_integers : similar to `randint`, only for the closed - interval [`low`, `high`], and 1 is the lowest value if `high` is - omitted. In particular, this other one is the one to use to generate - uniformly distributed discrete non-integers. - Examples - -------- - >>> np.random.randint(2, size=10) - array([1, 0, 0, 0, 1, 1, 0, 0, 1, 0]) # random - >>> np.random.randint(1, size=10) - array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # random - Generate a 2 x 4 array of ints between 0 and 4, inclusive: - >>> np.random.randint(5, size=(2, 4)) - array([[4, 0, 2, 1], # random - [3, 2, 2, 0]]) - Generate a 1 x 3 array with 3 different upper bounds - >>> np.random.randint(1, [3, 5, 10]) - array([2, 2, 9]) # random - Generate a 1 by 3 array with 3 different lower bounds - >>> np.random.randint([1, 5, 7], 10) - array([9, 8, 7]) # random - Generate a 2 by 4 array using broadcasting with dtype of uint8 - >>> np.random.randint([1, 3, 5, 7], [[10], [20]], dtype=np.uint8) - array([[ 8, 6, 9, 7], # random - [ 1, 16, 9, 12]], dtype=uint8) - """ - cdef bint use_masked=1 - - if high is None: - high = low - low = 0 - - key = np.dtype(dtype).name - if not key in _randint_types: - raise TypeError('Unsupported dtype "%s" for randint' % key) - - if key == 'int32': - ret = _rand_int32(low, high, size, use_masked, self._brng, self.lock) - elif key == 'int64': - ret = _rand_int64(low, high, size, use_masked, self._brng, self.lock) - elif key == 'int16': - ret = _rand_int16(low, high, size, use_masked, self._brng, self.lock) - elif key == 'int8': - ret = _rand_int8(low, high, size, use_masked, self._brng, self.lock) - elif key == 'uint64': - ret = _rand_uint64(low, high, size, use_masked, self._brng, self.lock) - elif key == 'uint32': - ret = _rand_uint32(low, high, size, use_masked, self._brng, self.lock) - elif key == 'uint16': - ret = _rand_uint16(low, high, size, use_masked, self._brng, self.lock) - elif key == 'uint8': - ret = _rand_uint8(low, high, size, use_masked, self._brng, self.lock) - elif key == 'bool': - ret = _rand_bool(low, high, size, use_masked, self._brng, self.lock) - - if size is None and dtype in (np.bool, np.int, np.long): - if np.array(ret).shape == (): - return dtype(ret) - return ret - - def rand(self, *args): - """ - rand(d0, d1, ..., dn) - Random values in a given shape. - Create an array of the given shape and populate it with - random samples from a uniform distribution - over ``[0, 1)``. - Parameters - ---------- - d0, d1, ..., dn : int, optional - The dimensions of the returned array, should all be positive. - If no argument is given a single Python float is returned. - Returns - ------- - out : ndarray, shape ``(d0, d1, ..., dn)`` - Random values. - See Also - -------- - random - Notes - ----- - This is a convenience function. If you want an interface that - takes a shape-tuple as the first argument, refer to - np.random.random_sample . - Examples - -------- - >>> np.random.rand(3,2) - array([[ 0.14022471, 0.96360618], #random - [ 0.37601032, 0.25528411], #random - [ 0.49313049, 0.94909878]]) #random - """ - if len(args) == 0: - return self.random_sample() - else: - return self.random_sample(size=args) - - def randn(self, *args): - """ - randn(d0, d1, ..., dn) - - Return a sample (or samples) from the "standard normal" distribution. - - If positive, int_like or int-convertible arguments are provided, - `randn` generates an array of shape ``(d0, d1, ..., dn)``, filled - with random floats sampled from a univariate "normal" (Gaussian) - distribution of mean 0 and variance 1 (if any of the :math:`d_i` are - floats, they are first converted to integers by truncation). A single - float randomly sampled from the distribution is returned if no - argument is provided. - - This is a convenience function. If you want an interface that takes a - tuple as the first argument, use `standard_normal` instead. - - Parameters - ---------- - d0, d1, ..., dn : int, optional - The dimensions of the returned array, should be all positive. - If no argument is given a single Python float is returned. - - Returns - ------- - Z : ndarray or float - A ``(d0, d1, ..., dn)``-shaped array of floating-point samples from - the standard normal distribution, or a single such float if - no parameters were supplied. - - See Also - -------- - standard_normal : Similar, but takes a tuple as its argument. - - Notes - ----- - For random samples from :math:`N(\\mu, \\sigma^2)`, use: - - ``sigma * np.random.randn(...) + mu`` - - Examples - -------- - >>> np.random.randn() - 2.1923875335537315 #random - - Two-by-four array of samples from N(3, 6.25): - - >>> 2.5 * np.random.randn(2, 4) + 3 - array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], #random - [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) #random - - """ - if len(args) == 0: - return self.standard_normal() - else: - return self.standard_normal(size=args) - - # Complicated, continuous distributions: - def standard_normal(self, size=None): - """ - standard_normal(size=None) - - Draw samples from a standard Normal distribution (mean=0, stdev=1). - - Parameters - ---------- - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - - Returns - ------- - out : float or ndarray - Drawn samples. - - Examples - -------- - >>> s = np.random.standard_normal(8000) - >>> s - array([ 0.6888893 , 0.78096262, -0.89086505, ..., 0.49876311, #random - -0.38672696, -0.4685006 ]) #random - >>> s.shape - (8000,) - >>> s = np.random.standard_normal(size=(3, 4, 2)) - >>> s.shape - (3, 4, 2) - - """ - return cont(&legacy_gauss, self._aug_state, size, self.lock, 0, - None, None, CONS_NONE, - None, None, CONS_NONE, - None, None, CONS_NONE, - None) - - def normal(self, loc=0.0, scale=1.0, size=None): - """ - normal(loc=0.0, scale=1.0, size=None) - - Draw random samples from a normal (Gaussian) distribution. - - The probability density function of the normal distribution, first - derived by De Moivre and 200 years later by both Gauss and Laplace - independently [2]_, is often called the bell curve because of - its characteristic shape (see the example below). - - The normal distributions occurs often in nature. For example, it - describes the commonly occurring distribution of samples influenced - by a large number of tiny, random disturbances, each with its own - unique distribution [2]_. - - Parameters - ---------- - loc : float or array_like of floats - Mean ("centre") of the distribution. - scale : float or array_like of floats - Standard deviation (spread or "width") of the distribution. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``loc`` and ``scale`` are both scalars. - Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized normal distribution. - - See Also - -------- - scipy.stats.norm : probability density function, distribution or - cumulative density function, etc. - - Notes - ----- - The probability density for the Gaussian distribution is - - .. math:: p(x) = \\frac{1}{\\sqrt{ 2 \\pi \\sigma^2 }} - e^{ - \\frac{ (x - \\mu)^2 } {2 \\sigma^2} }, - - where :math:`\\mu` is the mean and :math:`\\sigma` the standard - deviation. The square of the standard deviation, :math:`\\sigma^2`, - is called the variance. - - The function has its peak at the mean, and its "spread" increases with - the standard deviation (the function reaches 0.607 times its maximum at - :math:`x + \\sigma` and :math:`x - \\sigma` [2]_). This implies that - `numpy.random.normal` is more likely to return samples lying close to - the mean, rather than those far away. - - References - ---------- - .. [1] Wikipedia, "Normal distribution", - https://en.wikipedia.org/wiki/Normal_distribution - .. [2] P. R. Peebles Jr., "Central Limit Theorem" in "Probability, - Random Variables and Random Signal Principles", 4th ed., 2001, - pp. 51, 51, 125. - - Examples - -------- - Draw samples from the distribution: - - >>> mu, sigma = 0, 0.1 # mean and standard deviation - >>> s = np.random.normal(mu, sigma, 1000) - - Verify the mean and the variance: - - >>> abs(mu - np.mean(s)) < 0.01 - True - - >>> abs(sigma - np.std(s, ddof=1)) < 0.01 - True - - Display the histogram of the samples, along with - the probability density function: - - >>> import matplotlib.pyplot as plt - >>> count, bins, ignored = plt.hist(s, 30, density=True) - >>> plt.plot(bins, 1/(sigma * np.sqrt(2 * np.pi)) * - ... np.exp( - (bins - mu)**2 / (2 * sigma**2) ), - ... linewidth=2, color='r') - >>> plt.show() - - """ - return cont(&legacy_normal, self._aug_state, size, self.lock, 2, - loc, '', CONS_NONE, - scale, 'scale', CONS_NON_NEGATIVE, - 0.0, '', CONS_NONE, - None) - - def standard_gamma(self, shape, size=None): - """ - standard_gamma(shape, size=None) - - Draw samples from a standard Gamma distribution. - - Samples are drawn from a Gamma distribution with specified parameters, - shape (sometimes designated "k") and scale=1. - - Parameters - ---------- - shape : float or array_like of floats - Parameter, should be > 0. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``shape`` is a scalar. Otherwise, - ``np.array(shape).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized standard gamma distribution. - - See Also - -------- - scipy.stats.gamma : probability density function, distribution or - cumulative density function, etc. - - Notes - ----- - The probability density for the Gamma distribution is - - .. math:: p(x) = x^{k-1}\\frac{e^{-x/\\theta}}{\\theta^k\\Gamma(k)}, - - where :math:`k` is the shape and :math:`\\theta` the scale, - and :math:`\\Gamma` is the Gamma function. - - The Gamma distribution is often used to model the times to failure of - electronic components, and arises naturally in processes for which the - waiting times between Poisson distributed events are relevant. - - References - ---------- - .. [1] Weisstein, Eric W. "Gamma Distribution." From MathWorld--A - Wolfram Web Resource. - http://mathworld.wolfram.com/GammaDistribution.html - .. [2] Wikipedia, "Gamma distribution", - https://en.wikipedia.org/wiki/Gamma_distribution - - Examples - -------- - Draw samples from the distribution: - - >>> shape, scale = 2., 1. # mean and width - >>> s = np.random.standard_gamma(shape, 1000000) - - Display the histogram of the samples, along with - the probability density function: - - >>> import matplotlib.pyplot as plt - >>> import scipy.special as sps - >>> count, bins, ignored = plt.hist(s, 50, density=True) - >>> y = bins**(shape-1) * ((np.exp(-bins/scale))/ \\ - ... (sps.gamma(shape) * scale**shape)) - >>> plt.plot(bins, y, linewidth=2, color='r') - >>> plt.show() - """ - return cont(&legacy_standard_gamma, self._aug_state, size, self.lock, 1, - shape, 'shape', CONS_NON_NEGATIVE, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE, - None) - - def gamma(self, shape, scale=1.0, size=None): - """ - gamma(shape, scale=1.0, size=None) - - Draw samples from a Gamma distribution. - - Samples are drawn from a Gamma distribution with specified parameters, - `shape` (sometimes designated "k") and `scale` (sometimes designated - "theta"), where both parameters are > 0. - - Parameters - ---------- - shape : float or array_like of floats - The shape of the gamma distribution. Should be greater than zero. - scale : float or array_like of floats, optional - The scale of the gamma distribution. Should be greater than zero. - Default is equal to 1. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``shape`` and ``scale`` are both scalars. - Otherwise, ``np.broadcast(shape, scale).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized gamma distribution. - - See Also - -------- - scipy.stats.gamma : probability density function, distribution or - cumulative density function, etc. - - Notes - ----- - The probability density for the Gamma distribution is - - .. math:: p(x) = x^{k-1}\\frac{e^{-x/\\theta}}{\\theta^k\\Gamma(k)}, - - where :math:`k` is the shape and :math:`\\theta` the scale, - and :math:`\\Gamma` is the Gamma function. - - The Gamma distribution is often used to model the times to failure of - electronic components, and arises naturally in processes for which the - waiting times between Poisson distributed events are relevant. - - References - ---------- - .. [1] Weisstein, Eric W. "Gamma Distribution." From MathWorld--A - Wolfram Web Resource. - http://mathworld.wolfram.com/GammaDistribution.html - .. [2] Wikipedia, "Gamma distribution", - https://en.wikipedia.org/wiki/Gamma_distribution - - Examples - -------- - Draw samples from the distribution: - - >>> shape, scale = 2., 2. # mean and dispersion - >>> s = np.random.gamma(shape, scale, 1000) - - Display the histogram of the samples, along with - the probability density function: - - >>> import matplotlib.pyplot as plt - >>> import scipy.special as sps - >>> count, bins, ignored = plt.hist(s, 50, density=True) - >>> y = bins**(shape-1)*(np.exp(-bins/scale) / - ... (sps.gamma(shape)*scale**shape)) - >>> plt.plot(bins, y, linewidth=2, color='r') - >>> plt.show() - - """ - return cont(&legacy_gamma, self._aug_state, size, self.lock, 2, - shape, 'shape', CONS_NON_NEGATIVE, - scale, 'scale', CONS_NON_NEGATIVE, - 0.0, '', CONS_NONE, None) - - def f(self, dfnum, dfden, size=None): - """ - f(dfnum, dfden, size=None) - - Draw samples from an F distribution. - - Samples are drawn from an F distribution with specified parameters, - `dfnum` (degrees of freedom in numerator) and `dfden` (degrees of - freedom in denominator), where both parameters should be greater than - zero. - - The random variate of the F distribution (also known as the - Fisher distribution) is a continuous probability distribution - that arises in ANOVA tests, and is the ratio of two chi-square - variates. - - Parameters - ---------- - dfnum : int or array_like of ints - Degrees of freedom in numerator. Should be greater than zero. - dfden : int or array_like of ints - Degrees of freedom in denominator. Should be greater than zero. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``dfnum`` and ``dfden`` are both scalars. - Otherwise, ``np.broadcast(dfnum, dfden).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized Fisher distribution. - - See Also - -------- - scipy.stats.f : probability density function, distribution or - cumulative density function, etc. - - Notes - ----- - The F statistic is used to compare in-group variances to between-group - variances. Calculating the distribution depends on the sampling, and - so it is a function of the respective degrees of freedom in the - problem. The variable `dfnum` is the number of samples minus one, the - between-groups degrees of freedom, while `dfden` is the within-groups - degrees of freedom, the sum of the number of samples in each group - minus the number of groups. - - References - ---------- - .. [1] Glantz, Stanton A. "Primer of Biostatistics.", McGraw-Hill, - Fifth Edition, 2002. - .. [2] Wikipedia, "F-distribution", - https://en.wikipedia.org/wiki/F-distribution - - Examples - -------- - An example from Glantz[1], pp 47-40: - - Two groups, children of diabetics (25 people) and children from people - without diabetes (25 controls). Fasting blood glucose was measured, - case group had a mean value of 86.1, controls had a mean value of - 82.2. Standard deviations were 2.09 and 2.49 respectively. Are these - data consistent with the null hypothesis that the parents diabetic - status does not affect their children's blood glucose levels? - Calculating the F statistic from the data gives a value of 36.01. - - Draw samples from the distribution: - - >>> dfnum = 1. # between group degrees of freedom - >>> dfden = 48. # within groups degrees of freedom - >>> s = np.random.f(dfnum, dfden, 1000) - - The lower bound for the top 1% of the samples is : - - >>> np.sort(s)[-10] - 7.61988120985 # random - - So there is about a 1% chance that the F statistic will exceed 7.62, - the measured value is 36, so the null hypothesis is rejected at the 1% - level. - - """ - return cont(&legacy_f, self._aug_state, size, self.lock, 2, - dfnum, 'dfnum', CONS_POSITIVE, - dfden, 'dfden', CONS_POSITIVE, - 0.0, '', CONS_NONE, None) - - def noncentral_f(self, dfnum, dfden, nonc, size=None): - """ - noncentral_f(dfnum, dfden, nonc, size=None) - - Draw samples from the noncentral F distribution. - - Samples are drawn from an F distribution with specified parameters, - `dfnum` (degrees of freedom in numerator) and `dfden` (degrees of - freedom in denominator), where both parameters > 1. - `nonc` is the non-centrality parameter. - - Parameters - ---------- - dfnum : int or array_like of ints - Parameter, should be > 1. - dfden : int or array_like of ints - Parameter, should be > 1. - nonc : float or array_like of floats - Parameter, should be >= 0. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``dfnum``, ``dfden``, and ``nonc`` - are all scalars. Otherwise, ``np.broadcast(dfnum, dfden, nonc).size`` - samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized noncentral Fisher distribution. - - Notes - ----- - When calculating the power of an experiment (power = probability of - rejecting the null hypothesis when a specific alternative is true) the - non-central F statistic becomes important. When the null hypothesis is - true, the F statistic follows a central F distribution. When the null - hypothesis is not true, then it follows a non-central F statistic. - - References - ---------- - .. [1] Weisstein, Eric W. "Noncentral F-Distribution." - From MathWorld--A Wolfram Web Resource. - http://mathworld.wolfram.com/NoncentralF-Distribution.html - .. [2] Wikipedia, "Noncentral F-distribution", - https://en.wikipedia.org/wiki/Noncentral_F-distribution - - Examples - -------- - In a study, testing for a specific alternative to the null hypothesis - requires use of the Noncentral F distribution. We need to calculate the - area in the tail of the distribution that exceeds the value of the F - distribution for the null hypothesis. We'll plot the two probability - distributions for comparison. - - >>> dfnum = 3 # between group deg of freedom - >>> dfden = 20 # within groups degrees of freedom - >>> nonc = 3.0 - >>> nc_vals = np.random.noncentral_f(dfnum, dfden, nonc, 1000000) - >>> NF = np.histogram(nc_vals, bins=50, density=True) - >>> c_vals = np.random.f(dfnum, dfden, 1000000) - >>> F = np.histogram(c_vals, bins=50, density=True) - >>> import matplotlib.pyplot as plt - >>> plt.plot(F[1][1:], F[0]) - >>> plt.plot(NF[1][1:], NF[0]) - >>> plt.show() - - """ - return cont(&legacy_noncentral_f, self._aug_state, size, self.lock, 3, - dfnum, 'dfnum', CONS_POSITIVE, - dfden, 'dfden', CONS_POSITIVE, - nonc, 'nonc', CONS_NON_NEGATIVE, None) - - def chisquare(self, df, size=None): - """ - chisquare(df, size=None) - - Draw samples from a chi-square distribution. - - When `df` independent random variables, each with standard normal - distributions (mean 0, variance 1), are squared and summed, the - resulting distribution is chi-square (see Notes). This distribution - is often used in hypothesis testing. - - Parameters - ---------- - df : int or array_like of ints - Number of degrees of freedom. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``df`` is a scalar. Otherwise, - ``np.array(df).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized chi-square distribution. - - Raises - ------ - ValueError - When `df` <= 0 or when an inappropriate `size` (e.g. ``size=-1``) - is given. - - Notes - ----- - The variable obtained by summing the squares of `df` independent, - standard normally distributed random variables: - - .. math:: Q = \\sum_{i=0}^{\\mathtt{df}} X^2_i - - is chi-square distributed, denoted - - .. math:: Q \\sim \\chi^2_k. - - The probability density function of the chi-squared distribution is - - .. math:: p(x) = \\frac{(1/2)^{k/2}}{\\Gamma(k/2)} - x^{k/2 - 1} e^{-x/2}, - - where :math:`\\Gamma` is the gamma function, - - .. math:: \\Gamma(x) = \\int_0^{-\\infty} t^{x - 1} e^{-t} dt. - - References - ---------- - .. [1] NIST "Engineering Statistics Handbook" - https://www.itl.nist.gov/div898/handbook/eda/section3/eda3666.htm - - Examples - -------- - >>> np.random.chisquare(2,4) - array([ 1.89920014, 9.00867716, 3.13710533, 5.62318272]) # random - - """ - return cont(&legacy_chisquare, self._aug_state, size, self.lock, 1, - df, 'df', CONS_POSITIVE, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE, None) - - def noncentral_chisquare(self, df, nonc, size=None): - """ - noncentral_chisquare(df, nonc, size=None) - - Draw samples from a noncentral chi-square distribution. - - The noncentral :math:`\\chi^2` distribution is a generalisation of - the :math:`\\chi^2` distribution. - - Parameters - ---------- - df : int or array_like of ints - Degrees of freedom, should be > 0 as of NumPy 1.10.0, - should be > 1 for earlier versions. - nonc : float or array_like of floats - Non-centrality, should be non-negative. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``df`` and ``nonc`` are both scalars. - Otherwise, ``np.broadcast(df, nonc).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized noncentral chi-square distribution. - - Notes - ----- - The probability density function for the noncentral Chi-square - distribution is - - .. math:: P(x;df,nonc) = \\sum^{\\infty}_{i=0} - \\frac{e^{-nonc/2}(nonc/2)^{i}}{i!} - \\P_{Y_{df+2i}}(x), - - where :math:`Y_{q}` is the Chi-square with q degrees of freedom. - - References - ---------- - .. [1] Wikipedia, "Noncentral chi-square distribution" - https://en.wikipedia.org/wiki/Noncentral_chi-square_distribution - - Examples - -------- - Draw values from the distribution and plot the histogram - - >>> import matplotlib.pyplot as plt - >>> values = plt.hist(np.random.noncentral_chisquare(3, 20, 100000), - ... bins=200, density=True) - >>> plt.show() - - Draw values from a noncentral chisquare with very small noncentrality, - and compare to a chisquare. - - >>> plt.figure() - >>> values = plt.hist(np.random.noncentral_chisquare(3, .0000001, 100000), - ... bins=np.arange(0., 25, .1), density=True) - >>> values2 = plt.hist(np.random.chisquare(3, 100000), - ... bins=np.arange(0., 25, .1), density=True) - >>> plt.plot(values[1][0:-1], values[0]-values2[0], 'ob') - >>> plt.show() - - Demonstrate how large values of non-centrality lead to a more symmetric - distribution. - - >>> plt.figure() - >>> values = plt.hist(np.random.noncentral_chisquare(3, 20, 100000), - ... bins=200, density=True) - >>> plt.show() - - """ - return cont(&legacy_noncentral_chisquare, self._aug_state, size, self.lock, 2, - df, 'df', CONS_POSITIVE, - nonc, 'nonc', CONS_NON_NEGATIVE, - 0.0, '', CONS_NONE, None) - - def standard_cauchy(self, size=None): - """ - standard_cauchy(size=None) - - Draw samples from a standard Cauchy distribution with mode = 0. - - Also known as the Lorentz distribution. - - Parameters - ---------- - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - - Returns - ------- - samples : ndarray or scalar - The drawn samples. - - Notes - ----- - The probability density function for the full Cauchy distribution is - - .. math:: P(x; x_0, \\gamma) = \\frac{1}{\\pi \\gamma \\bigl[ 1+ - (\\frac{x-x_0}{\\gamma})^2 \\bigr] } - - and the Standard Cauchy distribution just sets :math:`x_0=0` and - :math:`\\gamma=1` - - The Cauchy distribution arises in the solution to the driven harmonic - oscillator problem, and also describes spectral line broadening. It - also describes the distribution of values at which a line tilted at - a random angle will cut the x axis. - - When studying hypothesis tests that assume normality, seeing how the - tests perform on data from a Cauchy distribution is a good indicator of - their sensitivity to a heavy-tailed distribution, since the Cauchy looks - very much like a Gaussian distribution, but with heavier tails. - - References - ---------- - .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "Cauchy - Distribution", - http://www.itl.nist.gov/div898/handbook/eda/section3/eda3663.htm - .. [2] Weisstein, Eric W. "Cauchy Distribution." From MathWorld--A - Wolfram Web Resource. - http://mathworld.wolfram.com/CauchyDistribution.html - .. [3] Wikipedia, "Cauchy distribution" - https://en.wikipedia.org/wiki/Cauchy_distribution - - Examples - -------- - Draw samples and plot the distribution: - - >>> s = np.random.standard_cauchy(1000000) - >>> s = s[(s>-25) & (s<25)] # truncate distribution so it plots well - >>> import matplotlib.pyplot as plt - >>> plt.hist(s, bins=100) - >>> plt.show() - - """ - return cont(&legacy_standard_cauchy, self._aug_state, size, self.lock, 0, - 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, None) - - def standard_t(self, df, size=None): - """ - standard_t(df, size=None) - - Draw samples from a standard Student's t distribution with `df` degrees - of freedom. - - A special case of the hyperbolic distribution. As `df` gets - large, the result resembles that of the standard normal - distribution (`standard_normal`). - - Parameters - ---------- - df : int or array_like of ints - Degrees of freedom, should be > 0. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``df`` is a scalar. Otherwise, - ``np.array(df).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized standard Student's t distribution. - - Notes - ----- - The probability density function for the t distribution is - - .. math:: P(x, df) = \\frac{\\Gamma(\\frac{df+1}{2})}{\\sqrt{\\pi df} - \\Gamma(\\frac{df}{2})}\\Bigl( 1+\\frac{x^2}{df} \\Bigr)^{-(df+1)/2} - - The t test is based on an assumption that the data come from a - Normal distribution. The t test provides a way to test whether - the sample mean (that is the mean calculated from the data) is - a good estimate of the true mean. - - The derivation of the t-distribution was first published in - 1908 by William Gosset while working for the Guinness Brewery - in Dublin. Due to proprietary issues, he had to publish under - a pseudonym, and so he used the name Student. - - References - ---------- - .. [1] Dalgaard, Peter, "Introductory Statistics With R", - Springer, 2002. - .. [2] Wikipedia, "Student's t-distribution" - https://en.wikipedia.org/wiki/Student's_t-distribution - - Examples - -------- - From Dalgaard page 83 [1]_, suppose the daily energy intake for 11 - women in kilojoules (kJ) is: - - >>> intake = np.array([5260., 5470, 5640, 6180, 6390, 6515, 6805, 7515, \\ - ... 7515, 8230, 8770]) - - Does their energy intake deviate systematically from the recommended - value of 7725 kJ? - - We have 10 degrees of freedom, so is the sample mean within 95% of the - recommended value? - - >>> s = np.random.standard_t(10, size=100000) - >>> np.mean(intake) - 6753.636363636364 - >>> intake.std(ddof=1) - 1142.1232221373727 - - Calculate the t statistic, setting the ddof parameter to the unbiased - value so the divisor in the standard deviation will be degrees of - freedom, N-1. - - >>> t = (np.mean(intake)-7725)/(intake.std(ddof=1)/np.sqrt(len(intake))) - >>> import matplotlib.pyplot as plt - >>> h = plt.hist(s, bins=100, density=True) - - For a one-sided t-test, how far out in the distribution does the t - statistic appear? - - >>> np.sum(s>> a, m = 3., 2. # shape and mode - >>> s = (np.random.pareto(a, 1000) + 1) * m - - Display the histogram of the samples, along with the probability - density function: - - >>> import matplotlib.pyplot as plt - >>> count, bins, _ = plt.hist(s, 100, density=True) - >>> fit = a*m**a / bins**(a+1) - >>> plt.plot(bins, max(count)*fit/max(fit), linewidth=2, color='r') - >>> plt.show() - - """ - return cont(&legacy_pareto, self._aug_state, size, self.lock, 1, - a, 'a', CONS_POSITIVE, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE, None) - - def weibull(self, a, size=None): - """ - weibull(a, size=None) - - Draw samples from a Weibull distribution. - - Draw samples from a 1-parameter Weibull distribution with the given - shape parameter `a`. - - .. math:: X = (-ln(U))^{1/a} - - Here, U is drawn from the uniform distribution over (0,1]. - - The more common 2-parameter Weibull, including a scale parameter - :math:`\\lambda` is just :math:`X = \\lambda(-ln(U))^{1/a}`. - - Parameters - ---------- - a : float or array_like of floats - Shape parameter of the distribution. Must be nonnegative. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``a`` is a scalar. Otherwise, - ``np.array(a).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized Weibull distribution. - - See Also - -------- - scipy.stats.weibull_max - scipy.stats.weibull_min - scipy.stats.genextreme - gumbel - - Notes - ----- - The Weibull (or Type III asymptotic extreme value distribution - for smallest values, SEV Type III, or Rosin-Rammler - distribution) is one of a class of Generalized Extreme Value - (GEV) distributions used in modeling extreme value problems. - This class includes the Gumbel and Frechet distributions. - - The probability density for the Weibull distribution is - - .. math:: p(x) = \\frac{a} - {\\lambda}(\\frac{x}{\\lambda})^{a-1}e^{-(x/\\lambda)^a}, - - where :math:`a` is the shape and :math:`\\lambda` the scale. - - The function has its peak (the mode) at - :math:`\\lambda(\\frac{a-1}{a})^{1/a}`. - - When ``a = 1``, the Weibull distribution reduces to the exponential - distribution. - - References - ---------- - .. [1] Waloddi Weibull, Royal Technical University, Stockholm, - 1939 "A Statistical Theory Of The Strength Of Materials", - Ingeniorsvetenskapsakademiens Handlingar Nr 151, 1939, - Generalstabens Litografiska Anstalts Forlag, Stockholm. - .. [2] Waloddi Weibull, "A Statistical Distribution Function of - Wide Applicability", Journal Of Applied Mechanics ASME Paper - 1951. - .. [3] Wikipedia, "Weibull distribution", - https://en.wikipedia.org/wiki/Weibull_distribution - - Examples - -------- - Draw samples from the distribution: - - >>> a = 5. # shape - >>> s = np.random.weibull(a, 1000) - - Display the histogram of the samples, along with - the probability density function: - - >>> import matplotlib.pyplot as plt - >>> x = np.arange(1,100.)/50. - >>> def weib(x,n,a): - ... return (a / n) * (x / n)**(a - 1) * np.exp(-(x / n)**a) - - >>> count, bins, ignored = plt.hist(np.random.weibull(5.,1000)) - >>> x = np.arange(1,100.)/50. - >>> scale = count.max()/weib(x, 1., 5.).max() - >>> plt.plot(x, weib(x, 1., 5.)*scale) - >>> plt.show() - - """ - return cont(&legacy_weibull, self._aug_state, size, self.lock, 1, - a, 'a', CONS_NON_NEGATIVE, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE, None) - - def power(self, a, size=None): - """ - power(a, size=None) - - Draws samples in [0, 1] from a power distribution with positive - exponent a - 1. - - Also known as the power function distribution. - - Parameters - ---------- - a : float or array_like of floats - Parameter of the distribution. Should be greater than zero. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``a`` is a scalar. Otherwise, - ``np.array(a).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized power distribution. - - Raises - ------ - ValueError - If a < 1. - - Notes - ----- - The probability density function is - - .. math:: P(x; a) = ax^{a-1}, 0 \\le x \\le 1, a>0. - - The power function distribution is just the inverse of the Pareto - distribution. It may also be seen as a special case of the Beta - distribution. - - It is used, for example, in modeling the over-reporting of insurance - claims. - - References - ---------- - .. [1] Christian Kleiber, Samuel Kotz, "Statistical size distributions - in economics and actuarial sciences", Wiley, 2003. - .. [2] Heckert, N. A. and Filliben, James J. "NIST Handbook 148: - Dataplot Reference Manual, Volume 2: Let Subcommands and Library - Functions", National Institute of Standards and Technology - Handbook Series, June 2003. - http://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/powpdf.pdf - - Examples - -------- - Draw samples from the distribution: - - >>> a = 5. # shape - >>> samples = 1000 - >>> s = np.random.power(a, samples) - - Display the histogram of the samples, along with - the probability density function: - - >>> import matplotlib.pyplot as plt - >>> count, bins, ignored = plt.hist(s, bins=30) - >>> x = np.linspace(0, 1, 100) - >>> y = a*x**(a-1.) - >>> normed_y = samples*np.diff(bins)[0]*y - >>> plt.plot(x, normed_y) - >>> plt.show() - - Compare the power function distribution to the inverse of the Pareto. - - >>> from scipy import stats - >>> rvs = np.random.power(5, 1000000) - >>> rvsp = np.random.pareto(5, 1000000) - >>> xx = np.linspace(0,1,100) - >>> powpdf = stats.powerlaw.pdf(xx,5) - - >>> plt.figure() - >>> plt.hist(rvs, bins=50, density=True) - >>> plt.plot(xx,powpdf,'r-') - >>> plt.title('np.random.power(5)') - - >>> plt.figure() - >>> plt.hist(1./(1.+rvsp), bins=50, density=True) - >>> plt.plot(xx,powpdf,'r-') - >>> plt.title('inverse of 1 + np.random.pareto(5)') - - >>> plt.figure() - >>> plt.hist(1./(1.+rvsp), bins=50, density=True) - >>> plt.plot(xx,powpdf,'r-') - >>> plt.title('inverse of stats.pareto(5)') - - """ - return cont(&legacy_power, self._aug_state, size, self.lock, 1, - a, 'a', CONS_POSITIVE, - 0.0, '', CONS_NONE, - 0.0, '', CONS_NONE, None) - - def lognormal(self, mean=0.0, sigma=1.0, size=None): - """ - lognormal(mean=0.0, sigma=1.0, size=None) - - Draw samples from a log-normal distribution. - - Draw samples from a log-normal distribution with specified mean, - standard deviation, and array shape. Note that the mean and standard - deviation are not the values for the distribution itself, but of the - underlying normal distribution it is derived from. - - Parameters - ---------- - mean : float or array_like of floats, optional - Mean value of the underlying normal distribution. Default is 0. - sigma : float or array_like of floats, optional - Standard deviation of the underlying normal distribution. Should - be greater than zero. Default is 1. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``mean`` and ``sigma`` are both scalars. - Otherwise, ``np.broadcast(mean, sigma).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized log-normal distribution. - - See Also - -------- - scipy.stats.lognorm : probability density function, distribution, - cumulative density function, etc. - - Notes - ----- - A variable `x` has a log-normal distribution if `log(x)` is normally - distributed. The probability density function for the log-normal - distribution is: - - .. math:: p(x) = \\frac{1}{\\sigma x \\sqrt{2\\pi}} - e^{(-\\frac{(ln(x)-\\mu)^2}{2\\sigma^2})} - - where :math:`\\mu` is the mean and :math:`\\sigma` is the standard - deviation of the normally distributed logarithm of the variable. - A log-normal distribution results if a random variable is the *product* - of a large number of independent, identically-distributed variables in - the same way that a normal distribution results if the variable is the - *sum* of a large number of independent, identically-distributed - variables. - - References - ---------- - .. [1] Limpert, E., Stahel, W. A., and Abbt, M., "Log-normal - Distributions across the Sciences: Keys and Clues," - BioScience, Vol. 51, No. 5, May, 2001. - https://stat.ethz.ch/~stahel/lognormal/bioscience.pdf - .. [2] Reiss, R.D. and Thomas, M., "Statistical Analysis of Extreme - Values," Basel: Birkhauser Verlag, 2001, pp. 31-32. - - Examples - -------- - Draw samples from the distribution: - - >>> mu, sigma = 3., 1. # mean and standard deviation - >>> s = np.random.lognormal(mu, sigma, 1000) - - Display the histogram of the samples, along with - the probability density function: - - >>> import matplotlib.pyplot as plt - >>> count, bins, ignored = plt.hist(s, 100, density=True, align='mid') - - >>> x = np.linspace(min(bins), max(bins), 10000) - >>> pdf = (np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2)) - ... / (x * sigma * np.sqrt(2 * np.pi))) - - >>> plt.plot(x, pdf, linewidth=2, color='r') - >>> plt.axis('tight') - >>> plt.show() - - Demonstrate that taking the products of random samples from a uniform - distribution can be fit well by a log-normal probability density - function. - - >>> # Generate a thousand samples: each is the product of 100 random - >>> # values, drawn from a normal distribution. - >>> b = [] - >>> for i in range(1000): - ... a = 10. + np.random.randn(100) - ... b.append(np.product(a)) - - >>> b = np.array(b) / np.min(b) # scale values to be positive - >>> count, bins, ignored = plt.hist(b, 100, density=True, align='mid') - >>> sigma = np.std(np.log(b)) - >>> mu = np.mean(np.log(b)) - - >>> x = np.linspace(min(bins), max(bins), 10000) - >>> pdf = (np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2)) - ... / (x * sigma * np.sqrt(2 * np.pi))) - - >>> plt.plot(x, pdf, color='r', linewidth=2) - >>> plt.show() - - """ - return cont(&legacy_lognormal, self._aug_state, size, self.lock, 2, - mean, 'mean', CONS_NONE, - sigma, 'sigma', CONS_NON_NEGATIVE, - 0.0, '', CONS_NONE, None) - - def wald(self, mean, scale, size=None): - """ - wald(mean, scale, size=None) - - Draw samples from a Wald, or inverse Gaussian, distribution. - - As the scale approaches infinity, the distribution becomes more like a - Gaussian. Some references claim that the Wald is an inverse Gaussian - with mean equal to 1, but this is by no means universal. - - The inverse Gaussian distribution was first studied in relationship to - Brownian motion. In 1956 M.C.K. Tweedie used the name inverse Gaussian - because there is an inverse relationship between the time to cover a - unit distance and distance covered in unit time. - - Parameters - ---------- - mean : float or array_like of floats - Distribution mean, must be > 0. - scale : float or array_like of floats - Scale parameter, must be > 0. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``mean`` and ``scale`` are both scalars. - Otherwise, ``np.broadcast(mean, scale).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized Wald distribution. - - Notes - ----- - The probability density function for the Wald distribution is - - .. math:: P(x;mean,scale) = \\sqrt{\\frac{scale}{2\\pi x^3}}e^ - \\frac{-scale(x-mean)^2}{2\\cdotp mean^2x} - - As noted above the inverse Gaussian distribution first arise - from attempts to model Brownian motion. It is also a - competitor to the Weibull for use in reliability modeling and - modeling stock returns and interest rate processes. - - References - ---------- - .. [1] Brighton Webs Ltd., Wald Distribution, - https://web.archive.org/web/20090423014010/http://www.brighton-webs.co.uk:80/distributions/wald.asp - .. [2] Chhikara, Raj S., and Folks, J. Leroy, "The Inverse Gaussian - Distribution: Theory : Methodology, and Applications", CRC Press, - 1988. - .. [3] Wikipedia, "Wald distribution" - https://en.wikipedia.org/wiki/Wald_distribution - - Examples - -------- - Draw values from the distribution and plot the histogram: - - >>> import matplotlib.pyplot as plt - >>> h = plt.hist(np.random.wald(3, 2, 100000), bins=200, density=True) - >>> plt.show() - - """ - return cont(&legacy_wald, self._aug_state, size, self.lock, 2, - mean, 'mean', CONS_POSITIVE, - scale, 'scale', CONS_POSITIVE, - 0.0, '', CONS_NONE, None) - - - - def negative_binomial(self, n, p, size=None): - """ - negative_binomial(n, p, size=None) - - Draw samples from a negative binomial distribution. - - Samples are drawn from a negative binomial distribution with specified - parameters, `n` successes and `p` probability of success where `n` is an - integer > 0 and `p` is in the interval [0, 1]. - - Parameters - ---------- - n : int or array_like of ints - Parameter of the distribution, > 0. Floats are also accepted, - but they will be truncated to integers. - p : float or array_like of floats - Parameter of the distribution, >= 0 and <=1. - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. If size is ``None`` (default), - a single value is returned if ``n`` and ``p`` are both scalars. - Otherwise, ``np.broadcast(n, p).size`` samples are drawn. - - Returns - ------- - out : ndarray or scalar - Drawn samples from the parameterized negative binomial distribution, - where each sample is equal to N, the number of failures that - occurred before a total of n successes was reached. - - Notes - ----- - The probability density for the negative binomial distribution is - - .. math:: P(N;n,p) = \\binom{N+n-1}{N}p^{n}(1-p)^{N}, - - where :math:`n` is the number of successes, :math:`p` is the - probability of success, and :math:`N+n` is the number of trials. - The negative binomial distribution gives the probability of N - failures given n successes, with a success on the last trial. - - If one throws a die repeatedly until the third time a "1" appears, - then the probability distribution of the number of non-"1"s that - appear before the third "1" is a negative binomial distribution. - - References - ---------- - .. [1] Weisstein, Eric W. "Negative Binomial Distribution." From - MathWorld--A Wolfram Web Resource. - http://mathworld.wolfram.com/NegativeBinomialDistribution.html - .. [2] Wikipedia, "Negative binomial distribution", - https://en.wikipedia.org/wiki/Negative_binomial_distribution - - Examples - -------- - Draw samples from the distribution: - - A real world example. A company drills wild-cat oil - exploration wells, each with an estimated probability of - success of 0.1. What is the probability of having one success - for each successive well, that is what is the probability of a - single success after drilling 5 wells, after 6 wells, etc.? - - >>> s = np.random.negative_binomial(1, 0.9, 100000) - >>> for i in range(1, 11): # doctest: +SKIP - ... probability = sum(s>> mean = [0, 0] - >>> cov = [[1, 0], [0, 100]] # diagonal covariance - - Diagonal covariance means that points are oriented along x or y-axis: - - >>> import matplotlib.pyplot as plt - >>> x, y = np.random.multivariate_normal(mean, cov, 5000).T - >>> plt.plot(x, y, 'x') - >>> plt.axis('equal') - >>> plt.show() - - Note that the covariance matrix must be positive semidefinite (a.k.a. - nonnegative-definite). Otherwise, the behavior of this method is - undefined and backwards compatibility is not guaranteed. - - References - ---------- - .. [1] Papoulis, A., "Probability, Random Variables, and Stochastic - Processes," 3rd ed., New York: McGraw-Hill, 1991. - .. [2] Duda, R. O., Hart, P. E., and Stork, D. G., "Pattern - Classification," 2nd ed., New York: Wiley, 2001. - - Examples - -------- - >>> mean = (1, 2) - >>> cov = [[1, 0], [0, 1]] - >>> x = np.random.multivariate_normal(mean, cov, (3, 3)) - >>> x.shape - (3, 3, 2) - - The following is probably true, given that 0.6 is roughly twice the - standard deviation: - - >>> list((x[0,0,:] - mean) < 0.6) - [True, True] # random - - """ - from numpy.dual import svd - - # Check preconditions on arguments - mean = np.array(mean) - cov = np.array(cov) - if size is None: - shape = [] - elif isinstance(size, (int, long, np.integer)): - shape = [size] - else: - shape = size - - if len(mean.shape) != 1: - raise ValueError("mean must be 1 dimensional") - if (len(cov.shape) != 2) or (cov.shape[0] != cov.shape[1]): - raise ValueError("cov must be 2 dimensional and square") - if mean.shape[0] != cov.shape[0]: - raise ValueError("mean and cov must have same length") - - # Compute shape of output and create a matrix of independent - # standard normally distributed random numbers. The matrix has rows - # with the same length as mean and as many rows are necessary to - # form a matrix of shape final_shape. - final_shape = list(shape[:]) - final_shape.append(mean.shape[0]) - x = self.standard_normal(final_shape).reshape(-1, mean.shape[0]) - - # Transform matrix of standard normals into matrix where each row - # contains multivariate normals with the desired covariance. - # Compute A such that dot(transpose(A),A) == cov. - # Then the matrix products of the rows of x and A has the desired - # covariance. Note that sqrt(s)*v where (u,s,v) is the singular value - # decomposition of cov is such an A. - # - # Also check that cov is positive-semidefinite. If so, the u.T and v - # matrices should be equal up to roundoff error if cov is - # symmetric and the singular value of the corresponding row is - # not zero. We continue to use the SVD rather than Cholesky in - # order to preserve current outputs. Note that symmetry has not - # been checked. - - # GH10839, ensure double to make tol meaningful - cov = cov.astype(np.double) - (u, s, v) = svd(cov) - - if check_valid != 'ignore': - if check_valid != 'warn' and check_valid != 'raise': - raise ValueError( - "check_valid must equal 'warn', 'raise', or 'ignore'") - - psd = np.allclose(np.dot(v.T * s, v), cov, rtol=tol, atol=tol) - if not psd: - if check_valid == 'warn': - warnings.warn("covariance is not positive-semidefinite.", - RuntimeWarning) - else: - raise ValueError( - "covariance is not positive-semidefinite.") - - x = np.dot(x, np.sqrt(s)[:, None] * v) - x += mean - x.shape = tuple(final_shape) - return x - - def dirichlet(self, object alpha, size=None): - """ - dirichlet(alpha, size=None) - - Draw samples from the Dirichlet distribution. - - Draw `size` samples of dimension k from a Dirichlet distribution. A - Dirichlet-distributed random variable can be seen as a multivariate - generalization of a Beta distribution. The Dirichlet distribution - is a conjugate prior of a multinomial distribution in Bayesian - inference. - - Parameters - ---------- - alpha : array - Parameter of the distribution (k dimension for sample of - dimension k). - size : int or tuple of ints, optional - Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. - - Returns - ------- - samples : ndarray, - The drawn samples, of shape (size, alpha.ndim). - - Raises - ------- - ValueError - If any value in alpha is less than or equal to zero - - Notes - ----- - The Dirichlet distribution is a distribution over vectors - :math:`x` that fulfil the conditions :math:`x_i>0` and - :math:`\\sum_{i=1}^k x_i = 1`. - - The probability density function :math:`p` of a - Dirichlet-distributed random vector :math:`X` is - proportional to - - .. math:: p(x) \\propto \\prod_{i=1}^{k}{x^{\\alpha_i-1}_i}, - - where :math:`\\alpha` is a vector containing the positive - concentration parameters. - - The method uses the following property for computation: let :math:`Y` - be a random vector which has components that follow a standard gamma - distribution, then :math:`X = \\frac{1}{\\sum_{i=1}^k{Y_i}} Y` - is Dirichlet-distributed - - References - ---------- - .. [1] David McKay, "Information Theory, Inference and Learning - Algorithms," chapter 23, - http://www.inference.org.uk/mackay/itila/ - .. [2] Wikipedia, "Dirichlet distribution", - https://en.wikipedia.org/wiki/Dirichlet_distribution - - Examples - -------- - Taking an example cited in Wikipedia, this distribution can be used if - one wanted to cut strings (each of initial length 1.0) into K pieces - with different lengths, where each piece had, on average, a designated - average length, but allowing some variation in the relative sizes of - the pieces. - - >>> s = np.random.dirichlet((10, 5, 3), 20).transpose() - - >>> import matplotlib.pyplot as plt - >>> plt.barh(range(20), s[0]) - >>> plt.barh(range(20), s[1], left=s[0], color='g') - >>> plt.barh(range(20), s[2], left=s[0]+s[1], color='r') - >>> plt.title("Lengths of Strings") - - """ - - #================= - # Pure python algo - #================= - #alpha = N.atleast_1d(alpha) - #k = alpha.size - - #if n == 1: - # val = N.zeros(k) - # for i in range(k): - # val[i] = sgamma(alpha[i], n) - # val /= N.sum(val) - #else: - # val = N.zeros((k, n)) - # for i in range(k): - # val[i] = sgamma(alpha[i], n) - # val /= N.sum(val, axis = 0) - # val = val.T - - #return val - - cdef np.npy_intp k - cdef np.npy_intp totsize - cdef np.ndarray alpha_arr, val_arr - cdef double *alpha_data - cdef double *val_data - cdef np.npy_intp i, j - cdef double acc, invacc - - k = len(alpha) - alpha_arr = np.PyArray_FROM_OTF(alpha, np.NPY_DOUBLE, np.NPY_ALIGNED) - if np.any(np.less_equal(alpha_arr, 0)): - raise ValueError('alpha <= 0') - alpha_data = np.PyArray_DATA(alpha_arr) - - if size is None: - shape = (k,) - else: - try: - shape = (operator.index(size), k) - except: - shape = tuple(size) + (k,) - - diric = np.zeros(shape, np.float64) - val_arr = diric - val_data= np.PyArray_DATA(val_arr) - - i = 0 - totsize = np.PyArray_SIZE(val_arr) - with self.lock, nogil: - while i < totsize: - acc = 0.0 - for j in range(k): - val_data[i+j] = legacy_standard_gamma(self._aug_state, - alpha_data[j]) - acc = acc + val_data[i + j] - invacc = 1/acc - for j in range(k): - val_data[i + j] = val_data[i + j] * invacc - i = i + k - - return diric diff --git a/numpy/random/randomgen/legacy/legacy.py b/numpy/random/randomgen/legacy/legacy.py deleted file mode 100644 index bb0d0c721475..000000000000 --- a/numpy/random/randomgen/legacy/legacy.py +++ /dev/null @@ -1,130 +0,0 @@ -from ..generator import RandomGenerator -from ..mt19937 import MT19937 -from ._legacy import _LegacyGenerator - -# Attributes in RandomGenerator that should not appear in LegacyGenerator -_HIDDEN_ATTRIBUTES = ['complex_normal', 'random_raw', 'random_uintegers'] - -_LEGACY_ATTRIBUTES = tuple(a for a in dir( - - _LegacyGenerator) if not a.startswith('_')) - -_LEGACY_ATTRIBUTES += ('__getstate__', '__setstate__', '__reduce__') - - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - - # From six, https://raw.githubusercontent.com/benjaminp/six - class metaclass(type): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -class LegacyGeneratorType(type): - def __getattribute__(self, name): - if name in _LEGACY_ATTRIBUTES: - return object.__getattribute__(_LegacyGenerator, name) - return object.__getattribute__(RandomGenerator, name) - - -class LegacyGenerator(with_metaclass(LegacyGeneratorType, RandomGenerator)): - """ - LegacyGenerator(brng=None) - - Container providing legacy generators. - - ``LegacyGenerator`` exposes a number of methods for generating random - numbers for a set of distributions where the method used to produce random - samples has changed. Three core generators have changed: normal, - exponential and gamma. These have been replaced by faster Ziggurat-based - methods in ``RadnomGenerator``. ``LegacyGenerator`` retains the slower - methods to produce samples from these distributions as well as from - distributions that depend on these such as the Chi-square, power or - Weibull. - - **No Compatibility Guarantee** - - ``LegacyGenerator`` is evolving and so it isn't possible to provide a - compatibility guarantee like NumPy does. In particular, better algorithms - have already been added. This will change once ``RandomGenerator`` - stabilizes. - - Parameters - ---------- - brng : Basic RNG, optional - Basic RNG to use as the core generator. If none is provided, uses - MT19937. - - - Examples - -------- - Exactly reproducing a NumPy stream requires using ``MT19937`` as - the Basic RNG. - - >>> from randomgen import MT19937 - >>> lg = LegacyGenerator(MT19937(12345)) - >>> x = lg.standard_normal(10) - >>> lg.shuffle(x) - >>> x[0] - 0.09290787674371767 - >>> lg.standard_exponential() - 1.6465621229906502 - - The equivalent commands from NumPy produce identical output. - - >>> from numpy.random import RandomState - >>> rs = RandomState(12345) - >>> x = rs.standard_normal(10) - >>> rs.shuffle(x) - >>> x[0] - 0.09290787674371767 - >>> rs.standard_exponential() - 1.6465621229906502 - """ - - __atttributes = sorted(set(dir(_LegacyGenerator) + - dir(RandomGenerator)).difference(_HIDDEN_ATTRIBUTES)) - - def __init__(self, brng=None): - if brng is None: - brng = MT19937() - elif isinstance(brng, MT19937): - pass - else: - brng = MT19937(brng) - super(LegacyGenerator, self).__init__(brng) - self.__legacy = _LegacyGenerator(brng) - - def __getattribute__(self, name): - if name in _HIDDEN_ATTRIBUTES: - raise AttributeError('No attribute {0}'.format(name)) - if name in _LEGACY_ATTRIBUTES: - return self.__legacy.__getattribute__(name) - return object.__getattribute__(self, name) - - def __dir__(self): - return self.__atttributes - - # Pickling support: - def __getstate__(self): - return self.state - - def __setstate__(self, state): - self.state = state - - def __reduce__(self): - from .._pickle import _experiment_ctor - return (_experiment_ctor, - (self.state['brng'],), - self.state) diff --git a/numpy/random/randomgen/legacy/legacy_distributions.pxd b/numpy/random/randomgen/legacy/legacy_distributions.pxd index ab33d4a9da5c..bc00994dbacb 100644 --- a/numpy/random/randomgen/legacy/legacy_distributions.pxd +++ b/numpy/random/randomgen/legacy/legacy_distributions.pxd @@ -3,7 +3,7 @@ from libc.stdint cimport uint64_t import numpy as np -cimport numpy as np +cimport numpy as np from ..distributions cimport brng_t @@ -12,7 +12,7 @@ cdef extern from "../src/legacy/distributions-boxmuller.h": struct aug_brng: brng_t *basicrng int has_gauss - double gauss + double gauss ctypedef aug_brng aug_brng_t diff --git a/numpy/random/randomgen/mt19937.pyx b/numpy/random/randomgen/mt19937.pyx index 0e476e53bc75..f6c76e7ab1bd 100644 --- a/numpy/random/randomgen/mt19937.pyx +++ b/numpy/random/randomgen/mt19937.pyx @@ -1,10 +1,13 @@ -from __future__ import absolute_import - import operator from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock + import numpy as np cimport numpy as np @@ -121,11 +124,13 @@ cdef class MT19937: cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None): self.rng_state = malloc(sizeof(mt19937_state)) self._brng = malloc(sizeof(brng_t)) self.seed(seed) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &mt19937_uint64 @@ -157,41 +162,39 @@ cdef class MT19937: (self.state['brng'],), self.state) - def __random_integer(self, bits=64): + def random_raw(self, size=None, output=True): """ - 64-bit Random Integers from the PRNG + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG Parameters ---------- - bits : {32, 64} - Number of random bits to return + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. Returns ------- - rv : int - Next random value + out : uint or ndarray + Drawn samples. Notes ----- - Testing only + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. """ - if bits == 64: - return self._brng.next_uint64(self._brng.state) - elif bits == 32: - return self._brng.next_uint32(self._brng.state) - else: - raise ValueError('bits must be 32 or 64') + return random_raw(self._brng, self.lock, size, output) def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None): """ @@ -231,9 +234,10 @@ cdef class MT19937: raise ValueError("Seed must be between 0 and 2**32 - 1") mt19937_seed(self.rng_state, seed) except TypeError: - obj = np.asarray(seed).astype(np.int64, casting='safe') + obj = np.asarray(seed) if obj.size == 0: raise ValueError("Seed must be non-empty") + obj = obj.astype(np.int64, casting='safe') if obj.ndim != 1: raise ValueError("Seed array must be 1-d") if ((obj > int(2**32 - 1)) | (obj < 0)).any(): @@ -278,16 +282,15 @@ cdef class MT19937: key[i] = self.rng_state.key[i] return {'brng': self.__class__.__name__, - 'state': {'key':key, 'pos': self.rng_state.pos}} + 'state': {'key': key, 'pos': self.rng_state.pos}} @state.setter def state(self, value): if isinstance(value, tuple): - if value[0] != 'MT19937' or len(value) not in (3,5): - raise ValueError('state is not a legacy MT19937 state') + if value[0] != 'MT19937' or len(value) not in (3, 5): + raise ValueError('state is not a legacy MT19937 state') value ={'brng': 'MT19937', - 'state':{'key': value[1], 'pos': value[2]}} - + 'state': {'key': value[1], 'pos': value[2]}} if not isinstance(value, dict): raise TypeError('state must be a dict') @@ -303,12 +306,12 @@ cdef class MT19937: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -317,25 +320,10 @@ cdef class MT19937: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&mt19937_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&mt19937_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&mt19937_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -356,19 +344,8 @@ cdef class MT19937: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/mtrand.pyx b/numpy/random/randomgen/mtrand.pyx new file mode 100644 index 000000000000..41006c56016d --- /dev/null +++ b/numpy/random/randomgen/mtrand.pyx @@ -0,0 +1,4172 @@ +#!python +#cython: wraparound=False, nonecheck=False, boundscheck=False, cdivision=True, language_level=3 +import operator +import warnings +from collections.abc import Mapping +from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer +from cpython cimport (Py_INCREF, PyFloat_AsDouble) +from libc cimport string +from libc.stdlib cimport malloc, free +cimport numpy as np +import numpy as np +cimport cython + +from .bounded_integers cimport * +from .bounded_integers import _randint_types +from .common cimport * +from .distributions cimport * +from .legacy.legacy_distributions cimport * +from .mt19937 import MT19937 as _MT19937 + +np.import_array() + +cdef class RandomState: + """ + RandomState(brng=None) + + Container for the Mersenne Twister pseudo-random number generator. + + `RandomState` exposes a number of methods for generating random numbers + drawn from a variety of probability distributions. In addition to the + distribution-specific arguments, each method takes a keyword argument + `size` that defaults to ``None``. If `size` is ``None``, then a single + value is generated and returned. If `size` is an integer, then a 1-D + array filled with generated values is returned. If `size` is a tuple, + then an array with that shape is filled and returned. + + *Compatibility Guarantee* + A fixed seed and a fixed series of calls to 'RandomState' methods using + the same parameters will always produce the same results up to roundoff + error except when the values were incorrect. Incorrect values will be + fixed and the NumPy version in which the fix was made will be noted in + the relevant docstring. Extension of existing parameter ranges and the + addition of new parameters is allowed as long the previous behavior + remains unchanged. + + Parameters + ---------- + brng : {None, int, array_like, BasicRNG}, optional + Random seed used to initialize the pseudo-random number generator or + an instantized BasicRNG. If an integer or array, used as a seed for + the MT19937 BasicRNG. Values can be any integer between 0 and + 2**32 - 1 inclusive, an array (or other sequence) of such integers, + or ``None`` (the default). If `seed` is ``None``, then the `MT19937` + BasicRNG is initialized by reading data from ``/dev/urandom`` + (or the Windows analogue) if available or seed from the clock + otherwise. + + Notes + ----- + The Python stdlib module "random" also contains a Mersenne Twister + pseudo-random number generator with a number of methods that are similar + to the ones available in `RandomState`. `RandomState`, besides being + NumPy-aware, has the advantage that it provides a much larger number + of probability distributions to choose from. + + """ + cdef public object _basicrng + cdef brng_t *_brng + cdef aug_brng_t *_aug_state + cdef binomial_t *_binomial + cdef object lock + poisson_lam_max = POISSON_LAM_MAX + + def __init__(self, brng=None): + if brng is None: + brng = _MT19937() + elif not hasattr(brng, 'capsule'): + brng = _MT19937(brng) + + self._basicrng = brng + capsule = brng.capsule + cdef const char *name = "BasicRNG" + if not PyCapsule_IsValid(capsule, name): + raise ValueError("Invalid brng. The brng must be instantized.") + self._brng = PyCapsule_GetPointer(capsule, name) + self._aug_state = malloc(sizeof(aug_brng_t)) + self._aug_state.basicrng = self._brng + self._binomial = malloc(sizeof(binomial_t)) + self._reset_gauss() + self.lock = brng.lock + + def __dealloc__(self): + free(self._binomial) + free(self._aug_state) + + def __repr__(self): + return self.__str__() + ' at 0x{:X}'.format(id(self)) + + def __str__(self): + _str = self.__class__.__name__ + _str += '(' + self._basicrng.__class__.__name__ + ')' + return _str + + # Pickling support: + def __getstate__(self): + return self.get_state(legacy=False) + + def __setstate__(self, state): + self.set_state(state) + + def __reduce__(self): + state = self.get_state(legacy=False) + from ._pickle import __randomstate_ctor + return (__randomstate_ctor, + (state['brng'],), + state) + + cdef _reset_gauss(self): + self._aug_state.has_gauss = 0 + self._aug_state.gauss = 0.0 + + def seed(self, *args, **kwargs): + """ + seed(self, *args, **kwargs) + + Reseed the basic RNG. + + Parameters depend on the basic RNG used. + + Notes + ----- + Arguments are directly passed to the basic RNG. This is a convenience + function. + + The best method to access seed is to directly use a basic RNG instance. + This example demonstrates this best practice. + + >>> from np.random.randomgen import MT19937 + >>> from np.random import RandomState + >>> brng = MT19937(123456789) + >>> rs = RandomState(brng) + >>> brng.seed(987654321) + + These best practice examples are equivalent to + + >>> rs = RandomState(MT19937()) + >>> rs.seed(987654321) + """ + self._basicrng.seed(*args, **kwargs) + self._reset_gauss() + return self + + def get_state(self, legacy=True): + """ + get_state() + + Return a tuple representing the internal state of the generator. + + For more details, see `set_state`. + + Returns + ------- + out : {tuple(str, ndarray of 624 uints, int, int, float), dict} + The returned tuple has the following items: + + 1. the string 'MT19937'. + 2. a 1-D array of 624 unsigned integer keys. + 3. an integer ``pos``. + 4. an integer ``has_gauss``. + 5. a float ``cached_gaussian``. + + If `legacy` is False, or the basic RNG is not NT19937, then + state is returned as a dictionary. + + legacy : bool + Flag indicating the return a legacy tuple state when the basic RNG + is MT19937. + + See Also + -------- + set_state + + Notes + ----- + `set_state` and `get_state` are not needed to work with any of the + random distributions in NumPy. If the internal state is manually altered, + the user should know exactly what he/she is doing. + + """ + st = self._basicrng.state + if st['brng'] != 'MT19937' and legacy: + warnings.warn('get_state and legacy can only be used with the ' + 'MT19937 basic RNG. To silence this warning, ' + 'set `legacy` to False.', RuntimeWarning) + legacy = False + st['has_gauss'] = self._aug_state.has_gauss + st['gauss'] = self._aug_state.gauss + if legacy: + return (st['brng'], st['state']['key'], st['state']['pos'], + st['has_gauss'], st['gauss']) + return st + + def set_state(self, state): + """ + set_state(state) + + Set the internal state of the generator from a tuple. + + For use if one has reason to manually (re-)set the internal state of the + "Mersenne Twister"[1]_ pseudo-random number generating algorithm. + + Parameters + ---------- + state : {tuple(str, ndarray of 624 uints, int, int, float), dict} + The `state` tuple has the following items: + + 1. the string 'MT19937', specifying the Mersenne Twister algorithm. + 2. a 1-D array of 624 unsigned integers ``keys``. + 3. an integer ``pos``. + 4. an integer ``has_gauss``. + 5. a float ``cached_gaussian``. + + If state is a dictionary, it is directly set using the BasicRNGs + `state` property. + + Returns + ------- + out : None + Returns 'None' on success. + + See Also + -------- + get_state + + Notes + ----- + `set_state` and `get_state` are not needed to work with any of the + random distributions in NumPy. If the internal state is manually altered, + the user should know exactly what he/she is doing. + + For backwards compatibility, the form (str, array of 624 uints, int) is + also accepted although it is missing some information about the cached + Gaussian value: ``state = ('MT19937', keys, pos)``. + + References + ---------- + .. [1] M. Matsumoto and T. Nishimura, "Mersenne Twister: A + 623-dimensionally equidistributed uniform pseudorandom number + generator," *ACM Trans. on Modeling and Computer Simulation*, + Vol. 8, No. 1, pp. 3-30, Jan. 1998. + + """ + if isinstance(state, Mapping): + if 'brng' not in state or 'state' not in state: + raise ValueError('state dictionary is not valid.') + st = state + else: + if not isinstance(state, (tuple, list)): + raise TypeError('state must be a dict or a tuple.') + if state[0] != 'MT19937': + raise ValueError('set_state can only be used with legacy MT19937' + 'state instances.') + st = {'brng': state[0], + 'state': {'key': state[1], 'pos': state[2]}} + if len(state) > 3: + st['has_gauss'] = state[3] + st['gauss'] = state[4] + value = st + + self._aug_state.gauss = st.get('gauss', 0.0) + self._aug_state.has_gauss = st.get('has_gauss', 0) + self._basicrng.state = st + + def random_sample(self, size=None): + """ + random_sample(size=None) + + Return random floats in the half-open interval [0.0, 1.0). + + Results are from the "continuous uniform" distribution over the + stated interval. To sample :math:`Unif[a, b), b > a` multiply + the output of `random_sample` by `(b-a)` and add `a`:: + + (b - a) * random_sample() + a + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + out : float or ndarray of floats + Array of random floats of shape `size` (unless ``size=None``, in which + case a single float is returned). + + Examples + -------- + >>> np.random.random_sample() + 0.47108547995356098 # random + >>> type(np.random.random_sample()) + + >>> np.random.random_sample((5,)) + array([ 0.30220482, 0.86820401, 0.1654503 , 0.11659149, 0.54323428]) # random + + Three-by-two array of random numbers from [-5, 0): + + >>> 5 * np.random.random_sample((3, 2)) - 5 + array([[-3.99149989, -0.52338984], # random + [-2.99091858, -0.79479508], + [-1.23204345, -1.75224494]]) + + """ + cdef double temp + return double_fill(&random_double_fill, self._brng, size, self.lock, None) + + def beta(self, a, b, size=None): + """ + beta(a, b, size=None) + + Draw samples from a Beta distribution. + + The Beta distribution is a special case of the Dirichlet distribution, + and is related to the Gamma distribution. It has the probability + distribution function + + .. math:: f(x; a,b) = \\frac{1}{B(\\alpha, \\beta)} x^{\\alpha - 1} + (1 - x)^{\\beta - 1}, + + where the normalisation, B, is the beta function, + + .. math:: B(\\alpha, \\beta) = \\int_0^1 t^{\\alpha - 1} + (1 - t)^{\\beta - 1} dt. + + It is often seen in Bayesian inference and order statistics. + + Parameters + ---------- + a : float or array_like of floats + Alpha, positive (>0). + b : float or array_like of floats + Beta, positive (>0). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` and ``b`` are both scalars. + Otherwise, ``np.broadcast(a, b).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized beta distribution. + + """ + return cont(&legacy_beta, self._aug_state, size, self.lock, 2, + a, 'a', CONS_POSITIVE, + b, 'b', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) + + def exponential(self, scale=1.0, size=None): + """ + exponential(scale=1.0, size=None) + + Draw samples from an exponential distribution. + + Its probability density function is + + .. math:: f(x; \\frac{1}{\\beta}) = \\frac{1}{\\beta} \\exp(-\\frac{x}{\\beta}), + + for ``x > 0`` and 0 elsewhere. :math:`\\beta` is the scale parameter, + which is the inverse of the rate parameter :math:`\\lambda = 1/\\beta`. + The rate parameter is an alternative, widely used parameterization + of the exponential distribution [3]_. + + The exponential distribution is a continuous analogue of the + geometric distribution. It describes many common situations, such as + the size of raindrops measured over many rainstorms [1]_, or the time + between page requests to Wikipedia [2]_. + + Parameters + ---------- + scale : float or array_like of floats + The scale parameter, :math:`\\beta = 1/\\lambda`. Must be + non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``scale`` is a scalar. Otherwise, + ``np.array(scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized exponential distribution. + + References + ---------- + .. [1] Peyton Z. Peebles Jr., "Probability, Random Variables and + Random Signal Principles", 4th ed, 2001, p. 57. + .. [2] Wikipedia, "Poisson process", + https://en.wikipedia.org/wiki/Poisson_process + .. [3] Wikipedia, "Exponential distribution", + https://en.wikipedia.org/wiki/Exponential_distribution + + """ + return cont(&legacy_exponential, self._aug_state, size, self.lock, 1, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) + + def standard_exponential(self, size=None): + """ + standard_exponential(size=None) + + Draw samples from the standard exponential distribution. + + `standard_exponential` is identical to the exponential distribution + with a scale parameter of 1. + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + out : float or ndarray + Drawn samples. + + Examples + -------- + Output a 3x8000 array: + + >>> n = np.random.standard_exponential((3, 8000)) + + """ + return cont(&legacy_standard_exponential, self._aug_state, size, self.lock, 0, + None, None, CONS_NONE, + None, None, CONS_NONE, + None, None, CONS_NONE, + None) + + def tomaxint(self, size=None): + """ + tomaxint(size=None) + + Random integers between 0 and ``sys.maxint``, inclusive. + + Return a sample of uniformly distributed random integers in the interval + [0, ``sys.maxint``]. + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + out : ndarray + Drawn samples, with shape `size`. + + See Also + -------- + randint : Uniform sampling over a given half-open interval of integers. + random_integers : Uniform sampling over a given closed interval of + integers. + + Examples + -------- + >>> rs = np.random.RandomState() # need a RandomGenerator object + >>> rs.tomaxint((2,2,2)) + array([[[1170048599, 1600360186], # random + [ 739731006, 1947757578]], + [[1871712945, 752307660], + [1601631370, 1479324245]]]) + >>> import sys + >>> sys.maxint + 2147483647 + >>> rs.tomaxint((2,2,2)) < sys.maxint + array([[[ True, True], + [ True, True]], + [[ True, True], + [ True, True]]]) + + """ + cdef np.npy_intp n + cdef np.ndarray randoms + cdef int64_t *randoms_data + + if size is None: + with self.lock: + return random_positive_int(self._brng) + + randoms = np.empty(size, dtype=np.int64) + randoms_data = np.PyArray_DATA(randoms) + n = np.PyArray_SIZE(randoms) + + for i in range(n): + with self.lock, nogil: + randoms_data[i] = random_positive_int(self._brng) + return randoms + + def randint(self, low, high=None, size=None, dtype=int): + """ + randint(low, high=None, size=None, dtype='l') + + Return random integers from `low` (inclusive) to `high` (exclusive). + + Return random integers from the "discrete uniform" distribution of + the specified dtype in the "half-open" interval [`low`, `high`). If + `high` is None (the default), then results are from [0, `low`). + + Parameters + ---------- + low : int or array-like of ints + Lowest (signed) integers to be drawn from the distribution (unless + ``high=None``, in which case this parameter is one above the + *highest* such integer). + high : int or array-like of ints, optional + If provided, one above the largest (signed) integer to be drawn + from the distribution (see above for behavior if ``high=None``). + If array-like, must contain integer values + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + dtype : {str, dtype}, optional + Desired dtype of the result. All dtypes are determined by their + name, i.e., 'int64', 'int', etc, so byteorder is not available + and a specific precision may have different C types depending + on the platform. The default value is 'np.int'. + + .. versionadded:: 1.11.0 + + Returns + ------- + out : int or ndarray of ints + `size`-shaped array of random integers from the appropriate + distribution, or a single such random int if `size` not provided. + + See Also + -------- + random.random_integers : similar to `randint`, only for the closed + interval [`low`, `high`], and 1 is the lowest value if `high` is + omitted. In particular, this other one is the one to use to generate + uniformly distributed discrete non-integers. + + Examples + -------- + >>> np.random.randint(2, size=10) + array([1, 0, 0, 0, 1, 1, 0, 0, 1, 0]) # random + >>> np.random.randint(1, size=10) + array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # random + + Generate a 2 x 4 array of ints between 0 and 4, inclusive: + + >>> np.random.randint(5, size=(2, 4)) + array([[4, 0, 2, 1], # random + [3, 2, 2, 0]]) + + Generate a 1 x 3 array with 3 different upper bounds + + >>> np.random.randint(1, [3, 5, 10]) + array([2, 2, 9]) # random + + Generate a 1 by 3 array with 3 different lower bounds + + >>> np.random.randint([1, 5, 7], 10) + array([9, 8, 7]) # random + + Generate a 2 by 4 array using broadcasting with dtype of uint8 + + >>> np.random.randint([1, 3, 5, 7], [[10], [20]], dtype=np.uint8) + array([[ 8, 6, 9, 7], # random + [ 1, 16, 9, 12]], dtype=uint8) + """ + cdef bint use_masked=1 + + if high is None: + high = low + low = 0 + + key = np.dtype(dtype).name + if key not in _randint_types: + raise TypeError('Unsupported dtype "%s" for randint' % key) + + if key == 'int32': + ret = _rand_int32(low, high, size, use_masked, self._brng, self.lock) + elif key == 'int64': + ret = _rand_int64(low, high, size, use_masked, self._brng, self.lock) + elif key == 'int16': + ret = _rand_int16(low, high, size, use_masked, self._brng, self.lock) + elif key == 'int8': + ret = _rand_int8(low, high, size, use_masked, self._brng, self.lock) + elif key == 'uint64': + ret = _rand_uint64(low, high, size, use_masked, self._brng, self.lock) + elif key == 'uint32': + ret = _rand_uint32(low, high, size, use_masked, self._brng, self.lock) + elif key == 'uint16': + ret = _rand_uint16(low, high, size, use_masked, self._brng, self.lock) + elif key == 'uint8': + ret = _rand_uint8(low, high, size, use_masked, self._brng, self.lock) + elif key == 'bool': + ret = _rand_bool(low, high, size, use_masked, self._brng, self.lock) + + if size is None and dtype in (np.bool, np.int, np.long): + if np.array(ret).shape == (): + return dtype(ret) + return ret + + def bytes(self, np.npy_intp length): + """ + bytes(length) + + Return random bytes. + + Parameters + ---------- + length : int + Number of random bytes. + + Returns + ------- + out : str + String of length `length`. + + Examples + -------- + >>> np.random.bytes(10) + ' eh\\x85\\x022SZ\\xbf\\xa4' #random + + """ + cdef Py_ssize_t n_uint32 = ((length - 1) // 4 + 1) + return self.randint(0, 4294967296, size=n_uint32, dtype=np.uint32).tobytes()[:length] + + @cython.wraparound(True) + def choice(self, a, size=None, replace=True, p=None): + """ + choice(a, size=None, replace=True, p=None) + + Generates a random sample from a given 1-D array + + .. versionadded:: 1.7.0 + + Parameters + ---------- + a : 1-D array-like or int + If an ndarray, a random sample is generated from its elements. + If an int, the random sample is generated as if a were np.arange(a) + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + replace : boolean, optional + Whether the sample is with or without replacement + p : 1-D array-like, optional + The probabilities associated with each entry in a. + If not given the sample assumes a uniform distribution over all + entries in a. + + Returns + -------- + samples : single item or ndarray + The generated random samples + + Raises + ------- + ValueError + If a is an int and less than zero, if a or p are not 1-dimensional, + if a is an array-like of size 0, if p is not a vector of + probabilities, if a and p have different lengths, or if + replace=False and the sample size is greater than the population + size + + See Also + --------- + randint, shuffle, permutation + + Examples + --------- + Generate a uniform random sample from np.arange(5) of size 3: + + >>> np.random.choice(5, 3) + array([0, 3, 4]) # random + >>> #This is equivalent to np.random.randint(0,5,3) + + Generate a non-uniform random sample from np.arange(5) of size 3: + + >>> np.random.choice(5, 3, p=[0.1, 0, 0.3, 0.6, 0]) + array([3, 3, 0]) # random + + Generate a uniform random sample from np.arange(5) of size 3 without + replacement: + + >>> np.random.choice(5, 3, replace=False) + array([3,1,0]) # random + >>> #This is equivalent to np.random.permutation(np.arange(5))[:3] + + Generate a non-uniform random sample from np.arange(5) of size + 3 without replacement: + + >>> np.random.choice(5, 3, replace=False, p=[0.1, 0, 0.3, 0.6, 0]) + array([2, 3, 0]) # random + + Any of the above can be repeated with an arbitrary array-like + instead of just integers. For instance: + + >>> aa_milne_arr = ['pooh', 'rabbit', 'piglet', 'Christopher'] + >>> np.random.choice(aa_milne_arr, 5, p=[0.5, 0.1, 0.1, 0.3]) + array(['pooh', 'pooh', 'pooh', 'Christopher', 'piglet'], # random + dtype='np.PyArray_FROM_OTF(p, np.NPY_DOUBLE, np.NPY_ALIGNED) + pix = np.PyArray_DATA(p) + + if p.ndim != 1: + raise ValueError("'p' must be 1-dimensional") + if p.size != pop_size: + raise ValueError("'a' and 'p' must have same size") + p_sum = kahan_sum(pix, d) + if np.isnan(p_sum): + raise ValueError("probabilities contain NaN") + if np.logical_or.reduce(p < 0): + raise ValueError("probabilities are not non-negative") + if abs(p_sum - 1.) > atol: + raise ValueError("probabilities do not sum to 1") + + shape = size + if shape is not None: + size = np.prod(shape, dtype=np.intp) + else: + size = 1 + + # Actual sampling + if replace: + if p is not None: + cdf = p.cumsum() + cdf /= cdf[-1] + uniform_samples = self.random_sample(shape) + idx = cdf.searchsorted(uniform_samples, side='right') + idx = np.array(idx, copy=False) # searchsorted returns a scalar + else: + idx = self.randint(0, pop_size, size=shape) + else: + if size > pop_size: + raise ValueError("Cannot take a larger sample than " + "population when 'replace=False'") + elif size < 0: + raise ValueError("negative dimensions are not allowed") + + if p is not None: + if np.count_nonzero(p > 0) < size: + raise ValueError("Fewer non-zero entries in p than size") + n_uniq = 0 + p = p.copy() + found = np.zeros(shape, dtype=np.int64) + flat_found = found.ravel() + while n_uniq < size: + x = self.rand(size - n_uniq) + if n_uniq > 0: + p[flat_found[0:n_uniq]] = 0 + cdf = np.cumsum(p) + cdf /= cdf[-1] + new = cdf.searchsorted(x, side='right') + _, unique_indices = np.unique(new, return_index=True) + unique_indices.sort() + new = new.take(unique_indices) + flat_found[n_uniq:n_uniq + new.size] = new + n_uniq += new.size + idx = found + else: + idx = self.permutation(pop_size)[:size] + if shape is not None: + idx.shape = shape + + if shape is None and isinstance(idx, np.ndarray): + # In most cases a scalar will have been made an array + idx = idx.item(0) + + # Use samples as indices for a if a is array-like + if a.ndim == 0: + return idx + + if shape is not None and idx.ndim == 0: + # If size == () then the user requested a 0-d array as opposed to + # a scalar object when size is None. However a[idx] is always a + # scalar and not an array. So this makes sure the result is an + # array, taking into account that np.array(item) may not work + # for object arrays. + res = np.empty((), dtype=a.dtype) + res[()] = a[idx] + return res + + return a[idx] + + def uniform(self, low=0.0, high=1.0, size=None): + """ + uniform(low=0.0, high=1.0, size=None) + + Draw samples from a uniform distribution. + + Samples are uniformly distributed over the half-open interval + ``[low, high)`` (includes low, but excludes high). In other words, + any value within the given interval is equally likely to be drawn + by `uniform`. + + Parameters + ---------- + low : float or array_like of floats, optional + Lower boundary of the output interval. All values generated will be + greater than or equal to low. The default value is 0. + high : float or array_like of floats + Upper boundary of the output interval. All values generated will be + less than high. The default value is 1.0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``low`` and ``high`` are both scalars. + Otherwise, ``np.broadcast(low, high).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized uniform distribution. + + See Also + -------- + randint : Discrete uniform distribution, yielding integers. + random_integers : Discrete uniform distribution over the closed + interval ``[low, high]``. + random_sample : Floats uniformly distributed over ``[0, 1)``. + random : Alias for `random_sample`. + rand : Convenience function that accepts dimensions as input, e.g., + ``rand(2,2)`` would generate a 2-by-2 array of floats, + uniformly distributed over ``[0, 1)``. + + Notes + ----- + The probability density function of the uniform distribution is + + .. math:: p(x) = \\frac{1}{b - a} + + anywhere within the interval ``[a, b)``, and zero elsewhere. + + When ``high`` == ``low``, values of ``low`` will be returned. + If ``high`` < ``low``, the results are officially undefined + and may eventually raise an error, i.e. do not rely on this + function to behave when passed arguments satisfying that + inequality condition. + + Examples + -------- + Draw samples from the distribution: + + >>> s = np.random.uniform(-1,0,1000) + + All values are within the given interval: + + >>> np.all(s >= -1) + True + >>> np.all(s < 0) + True + + Display the histogram of the samples, along with the + probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 15, density=True) + >>> plt.plot(bins, np.ones_like(bins), linewidth=2, color='r') + >>> plt.show() + + """ + cdef bint is_scalar = True + cdef np.ndarray alow, ahigh, arange + cdef double _low, _high, range + cdef object temp + + alow = np.PyArray_FROM_OTF(low, np.NPY_DOUBLE, np.NPY_ALIGNED) + ahigh = np.PyArray_FROM_OTF(high, np.NPY_DOUBLE, np.NPY_ALIGNED) + + if np.PyArray_NDIM(alow) == np.PyArray_NDIM(ahigh) == 0: + _low = PyFloat_AsDouble(low) + _high = PyFloat_AsDouble(high) + range = _high - _low + if not np.isfinite(range): + raise OverflowError('Range exceeds valid bounds') + + return cont(&random_uniform, self._brng, size, self.lock, 2, + _low, '', CONS_NONE, + range, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) + + temp = np.subtract(ahigh, alow) + Py_INCREF(temp) + # needed to get around Pyrex's automatic reference-counting + # rules because EnsureArray steals a reference + arange = np.PyArray_EnsureArray(temp) + if not np.all(np.isfinite(arange)): + raise OverflowError('Range exceeds valid bounds') + return cont(&random_uniform, self._brng, size, self.lock, 2, + alow, '', CONS_NONE, + arange, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) + + def rand(self, *args): + """ + rand(d0, d1, ..., dn) + + Random values in a given shape. + + .. note:: + This is a convenience function for users porting code from Matlab, + and wraps `numpy.random.random_sample`. That function takes a + tuple to specify the size of the output, which is consistent with + other NumPy functions like `numpy.zeros` and `numpy.ones`. + + Create an array of the given shape and populate it with + random samples from a uniform distribution + over ``[0, 1)``. + + Parameters + ---------- + d0, d1, ..., dn : int, optional + The dimensions of the returned array, should all be positive. + If no argument is given a single Python float is returned. + + Returns + ------- + out : ndarray, shape ``(d0, d1, ..., dn)`` + Random values. + + See Also + -------- + random + + Examples + -------- + >>> np.random.rand(3,2) + array([[ 0.14022471, 0.96360618], #random + [ 0.37601032, 0.25528411], #random + [ 0.49313049, 0.94909878]]) #random + + """ + if len(args) == 0: + return self.random_sample() + else: + return self.random_sample(size=args) + + def randn(self, *args): + """ + randn(d0, d1, ..., dn) + + Return a sample (or samples) from the "standard normal" distribution. + + .. note:: + This is a convenience function for users porting code from Matlab, + and wraps `numpy.random.standard_normal`. That function takes a + tuple to specify the size of the output, which is consistent with + other NumPy functions like `numpy.zeros` and `numpy.ones`. + + If positive int_like arguments are provided, `randn` generates an array + of shape ``(d0, d1, ..., dn)``, filled + with random floats sampled from a univariate "normal" (Gaussian) + distribution of mean 0 and variance 1. A single float randomly sampled + from the distribution is returned if no argument is provided. + + Parameters + ---------- + d0, d1, ..., dn : int, optional + The dimensions of the returned array, should be all positive. + If no argument is given a single Python float is returned. + + Returns + ------- + Z : ndarray or float + A ``(d0, d1, ..., dn)``-shaped array of floating-point samples from + the standard normal distribution, or a single such float if + no parameters were supplied. + + See Also + -------- + standard_normal : Similar, but takes a tuple as its argument. + normal : Also accepts mu and sigma arguments. + + Notes + ----- + For random samples from :math:`N(\\mu, \\sigma^2)`, use: + + ``sigma * np.random.randn(...) + mu`` + + Examples + -------- + >>> np.random.randn() + 2.1923875335537315 # random + + Two-by-four array of samples from N(3, 6.25): + + >>> 3 + 2.5 * np.random.randn(2, 4) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random + """ + if len(args) == 0: + return self.standard_normal() + else: + return self.standard_normal(size=args) + + def random_integers(self, low, high=None, size=None): + """ + random_integers(low, high=None, size=None) + + Random integers of type np.int between `low` and `high`, inclusive. + + Return random integers of type np.int from the "discrete uniform" + distribution in the closed interval [`low`, `high`]. If `high` is + None (the default), then results are from [1, `low`]. The np.int + type translates to the C long type used by Python 2 for "short" + integers and its precision is platform dependent. + + This function has been deprecated. Use randint instead. + + .. deprecated:: 1.11.0 + + Parameters + ---------- + low : int + Lowest (signed) integer to be drawn from the distribution (unless + ``high=None``, in which case this parameter is the *highest* such + integer). + high : int, optional + If provided, the largest (signed) integer to be drawn from the + distribution (see above for behavior if ``high=None``). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + out : int or ndarray of ints + `size`-shaped array of random integers from the appropriate + distribution, or a single such random int if `size` not provided. + + See Also + -------- + randint : Similar to `random_integers`, only for the half-open + interval [`low`, `high`), and 0 is the lowest value if `high` is + omitted. + + Notes + ----- + To sample from N evenly spaced floating-point numbers between a and b, + use:: + + a + (b - a) * (np.random.random_integers(N) - 1) / (N - 1.) + + Examples + -------- + >>> np.random.random_integers(5) + 4 # random + >>> type(np.random.random_integers(5)) + + >>> np.random.random_integers(5, size=(3,2)) + array([[5, 4], # random + [3, 3], + [4, 5]]) + + Choose five random numbers from the set of five evenly-spaced + numbers between 0 and 2.5, inclusive (*i.e.*, from the set + :math:`{0, 5/8, 10/8, 15/8, 20/8}`): + + >>> 2.5 * (np.random.random_integers(5, size=(5,)) - 1) / 4. + array([ 0.625, 1.25 , 0.625, 0.625, 2.5 ]) # random + + Roll two six sided dice 1000 times and sum the results: + + >>> d1 = np.random.random_integers(1, 6, 1000) + >>> d2 = np.random.random_integers(1, 6, 1000) + >>> dsums = d1 + d2 + + Display results as a histogram: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(dsums, 11, density=True) + >>> plt.show() + + """ + if high is None: + warnings.warn(("This function is deprecated. Please call " + "randint(1, {low} + 1) instead".format(low=low)), + DeprecationWarning) + high = low + low = 1 + + else: + warnings.warn(("This function is deprecated. Please call " + "randint({low}, {high} + 1) " + "instead".format(low=low, high=high)), + DeprecationWarning) + + return self.randint(low, high + 1, size=size, dtype='l') + + # Complicated, continuous distributions: + def standard_normal(self, size=None): + """ + standard_normal(size=None) + + Draw samples from a standard Normal distribution (mean=0, stdev=1). + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + out : float or ndarray + A floating-point array of shape ``size`` of drawn samples, or a + single sample if ``size`` was not specified. + + Notes + ----- + For random samples from :math:`N(\\mu, \\sigma^2)`, use one of:: + + mu + sigma * np.random.standard_normal(size=...) + np.random.normal(mu, sigma, size=...) + + See Also + -------- + normal : + Equivalent function with additional ``loc`` and ``scale`` arguments + for setting the mean and standard deviation. + + Examples + -------- + >>> np.random.standard_normal() + 2.1923875335537315 #random + + >>> s = np.random.standard_normal(8000) + >>> s + array([ 0.6888893 , 0.78096262, -0.89086505, ..., 0.49876311, # random + -0.38672696, -0.4685006 ]) # random + >>> s.shape + (8000,) + >>> s = np.random.standard_normal(size=(3, 4, 2)) + >>> s.shape + (3, 4, 2) + + Two-by-four array of samples from :math:`N(3, 6.25)`: + + >>> 3 + 2.5 * np.random.standard_normal(size=(2, 4)) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random + + """ + return cont(&legacy_gauss, self._aug_state, size, self.lock, 0, + None, None, CONS_NONE, + None, None, CONS_NONE, + None, None, CONS_NONE, + None) + + def normal(self, loc=0.0, scale=1.0, size=None): + """ + normal(loc=0.0, scale=1.0, size=None) + + Draw random samples from a normal (Gaussian) distribution. + + The probability density function of the normal distribution, first + derived by De Moivre and 200 years later by both Gauss and Laplace + independently [2]_, is often called the bell curve because of + its characteristic shape (see the example below). + + The normal distributions occurs often in nature. For example, it + describes the commonly occurring distribution of samples influenced + by a large number of tiny, random disturbances, each with its own + unique distribution [2]_. + + Parameters + ---------- + loc : float or array_like of floats + Mean ("centre") of the distribution. + scale : float or array_like of floats + Standard deviation (spread or "width") of the distribution. Must be + non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized normal distribution. + + See Also + -------- + scipy.stats.norm : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Gaussian distribution is + + .. math:: p(x) = \\frac{1}{\\sqrt{ 2 \\pi \\sigma^2 }} + e^{ - \\frac{ (x - \\mu)^2 } {2 \\sigma^2} }, + + where :math:`\\mu` is the mean and :math:`\\sigma` the standard + deviation. The square of the standard deviation, :math:`\\sigma^2`, + is called the variance. + + The function has its peak at the mean, and its "spread" increases with + the standard deviation (the function reaches 0.607 times its maximum at + :math:`x + \\sigma` and :math:`x - \\sigma` [2]_). This implies that + `numpy.random.normal` is more likely to return samples lying close to + the mean, rather than those far away. + + References + ---------- + .. [1] Wikipedia, "Normal distribution", + https://en.wikipedia.org/wiki/Normal_distribution + .. [2] P. R. Peebles Jr., "Central Limit Theorem" in "Probability, + Random Variables and Random Signal Principles", 4th ed., 2001, + pp. 51, 51, 125. + + Examples + -------- + Draw samples from the distribution: + + >>> mu, sigma = 0, 0.1 # mean and standard deviation + >>> s = np.random.normal(mu, sigma, 1000) + + Verify the mean and the variance: + + >>> abs(mu - np.mean(s)) + 0.0 # may vary + + >>> abs(sigma - np.std(s, ddof=1)) + 0.1 # may vary + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 30, density=True) + >>> plt.plot(bins, 1/(sigma * np.sqrt(2 * np.pi)) * + ... np.exp( - (bins - mu)**2 / (2 * sigma**2) ), + ... linewidth=2, color='r') + >>> plt.show() + + Two-by-four array of samples from N(3, 6.25): + + >>> np.random.normal(3, 2.5, size=(2, 4)) + array([[-4.49401501, 4.00950034, -1.81814867, 7.29718677], # random + [ 0.39924804, 4.68456316, 4.99394529, 4.84057254]]) # random + + """ + return cont(&legacy_normal, self._aug_state, size, self.lock, 2, + loc, '', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + None) + + def standard_gamma(self, shape, size=None): + """ + standard_gamma(shape, size=None) + + Draw samples from a standard Gamma distribution. + + Samples are drawn from a Gamma distribution with specified parameters, + shape (sometimes designated "k") and scale=1. + + Parameters + ---------- + shape : float or array_like of floats + Parameter, must be non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``shape`` is a scalar. Otherwise, + ``np.array(shape).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized standard gamma distribution. + + See Also + -------- + scipy.stats.gamma : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Gamma distribution is + + .. math:: p(x) = x^{k-1}\\frac{e^{-x/\\theta}}{\\theta^k\\Gamma(k)}, + + where :math:`k` is the shape and :math:`\\theta` the scale, + and :math:`\\Gamma` is the Gamma function. + + The Gamma distribution is often used to model the times to failure of + electronic components, and arises naturally in processes for which the + waiting times between Poisson distributed events are relevant. + + References + ---------- + .. [1] Weisstein, Eric W. "Gamma Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/GammaDistribution.html + .. [2] Wikipedia, "Gamma distribution", + https://en.wikipedia.org/wiki/Gamma_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> shape, scale = 2., 1. # mean and width + >>> s = np.random.standard_gamma(shape, 1000000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> import scipy.special as sps + >>> count, bins, ignored = plt.hist(s, 50, density=True) + >>> y = bins**(shape-1) * ((np.exp(-bins/scale))/ \\ + ... (sps.gamma(shape) * scale**shape)) + >>> plt.plot(bins, y, linewidth=2, color='r') + >>> plt.show() + + """ + return cont(&legacy_standard_gamma, self._aug_state, size, self.lock, 1, + shape, 'shape', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, + None) + + def gamma(self, shape, scale=1.0, size=None): + """ + gamma(shape, scale=1.0, size=None) + + Draw samples from a Gamma distribution. + + Samples are drawn from a Gamma distribution with specified parameters, + `shape` (sometimes designated "k") and `scale` (sometimes designated + "theta"), where both parameters are > 0. + + Parameters + ---------- + shape : float or array_like of floats + The shape of the gamma distribution. Must be non-negative. + scale : float or array_like of floats, optional + The scale of the gamma distribution. Must be non-negative. + Default is equal to 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``shape`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(shape, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized gamma distribution. + + See Also + -------- + scipy.stats.gamma : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Gamma distribution is + + .. math:: p(x) = x^{k-1}\\frac{e^{-x/\\theta}}{\\theta^k\\Gamma(k)}, + + where :math:`k` is the shape and :math:`\\theta` the scale, + and :math:`\\Gamma` is the Gamma function. + + The Gamma distribution is often used to model the times to failure of + electronic components, and arises naturally in processes for which the + waiting times between Poisson distributed events are relevant. + + References + ---------- + .. [1] Weisstein, Eric W. "Gamma Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/GammaDistribution.html + .. [2] Wikipedia, "Gamma distribution", + https://en.wikipedia.org/wiki/Gamma_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> shape, scale = 2., 2. # mean=4, std=2*sqrt(2) + >>> s = np.random.gamma(shape, scale, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> import scipy.special as sps + >>> count, bins, ignored = plt.hist(s, 50, density=True) + >>> y = bins**(shape-1)*(np.exp(-bins/scale) / + ... (sps.gamma(shape)*scale**shape)) + >>> plt.plot(bins, y, linewidth=2, color='r') + >>> plt.show() + + """ + return cont(&legacy_gamma, self._aug_state, size, self.lock, 2, + shape, 'shape', CONS_NON_NEGATIVE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def f(self, dfnum, dfden, size=None): + """ + f(dfnum, dfden, size=None) + + Draw samples from an F distribution. + + Samples are drawn from an F distribution with specified parameters, + `dfnum` (degrees of freedom in numerator) and `dfden` (degrees of + freedom in denominator), where both parameters should be greater than + zero. + + The random variate of the F distribution (also known as the + Fisher distribution) is a continuous probability distribution + that arises in ANOVA tests, and is the ratio of two chi-square + variates. + + Parameters + ---------- + dfnum : float or array_like of floats + Degrees of freedom in numerator, should be > 0. + dfden : float or array_like of float + Degrees of freedom in denominator, should be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``dfnum`` and ``dfden`` are both scalars. + Otherwise, ``np.broadcast(dfnum, dfden).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Fisher distribution. + + See Also + -------- + scipy.stats.f : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The F statistic is used to compare in-group variances to between-group + variances. Calculating the distribution depends on the sampling, and + so it is a function of the respective degrees of freedom in the + problem. The variable `dfnum` is the number of samples minus one, the + between-groups degrees of freedom, while `dfden` is the within-groups + degrees of freedom, the sum of the number of samples in each group + minus the number of groups. + + References + ---------- + .. [1] Glantz, Stanton A. "Primer of Biostatistics.", McGraw-Hill, + Fifth Edition, 2002. + .. [2] Wikipedia, "F-distribution", + https://en.wikipedia.org/wiki/F-distribution + + Examples + -------- + An example from Glantz[1], pp 47-40: + + Two groups, children of diabetics (25 people) and children from people + without diabetes (25 controls). Fasting blood glucose was measured, + case group had a mean value of 86.1, controls had a mean value of + 82.2. Standard deviations were 2.09 and 2.49 respectively. Are these + data consistent with the null hypothesis that the parents diabetic + status does not affect their children's blood glucose levels? + Calculating the F statistic from the data gives a value of 36.01. + + Draw samples from the distribution: + + >>> dfnum = 1. # between group degrees of freedom + >>> dfden = 48. # within groups degrees of freedom + >>> s = np.random.f(dfnum, dfden, 1000) + + The lower bound for the top 1% of the samples is : + + >>> np.sort(s)[-10] + 7.61988120985 # random + + So there is about a 1% chance that the F statistic will exceed 7.62, + the measured value is 36, so the null hypothesis is rejected at the 1% + level. + + """ + return cont(&legacy_f, self._aug_state, size, self.lock, 2, + dfnum, 'dfnum', CONS_POSITIVE, + dfden, 'dfden', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) + + def noncentral_f(self, dfnum, dfden, nonc, size=None): + """ + noncentral_f(dfnum, dfden, nonc, size=None) + + Draw samples from the noncentral F distribution. + + Samples are drawn from an F distribution with specified parameters, + `dfnum` (degrees of freedom in numerator) and `dfden` (degrees of + freedom in denominator), where both parameters > 1. + `nonc` is the non-centrality parameter. + + Parameters + ---------- + dfnum : float or array_like of floats + Numerator degrees of freedom, should be > 0. + + .. versionchanged:: 1.14.0 + Earlier NumPy versions required dfnum > 1. + dfden : float or array_like of floats + Denominator degrees of freedom, should be > 0. + nonc : float or array_like of floats + Non-centrality parameter, the sum of the squares of the numerator + means, should be >= 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``dfnum``, ``dfden``, and ``nonc`` + are all scalars. Otherwise, ``np.broadcast(dfnum, dfden, nonc).size`` + samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized noncentral Fisher distribution. + + Notes + ----- + When calculating the power of an experiment (power = probability of + rejecting the null hypothesis when a specific alternative is true) the + non-central F statistic becomes important. When the null hypothesis is + true, the F statistic follows a central F distribution. When the null + hypothesis is not true, then it follows a non-central F statistic. + + References + ---------- + .. [1] Weisstein, Eric W. "Noncentral F-Distribution." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/NoncentralF-Distribution.html + .. [2] Wikipedia, "Noncentral F-distribution", + https://en.wikipedia.org/wiki/Noncentral_F-distribution + + Examples + -------- + In a study, testing for a specific alternative to the null hypothesis + requires use of the Noncentral F distribution. We need to calculate the + area in the tail of the distribution that exceeds the value of the F + distribution for the null hypothesis. We'll plot the two probability + distributions for comparison. + + >>> dfnum = 3 # between group deg of freedom + >>> dfden = 20 # within groups degrees of freedom + >>> nonc = 3.0 + >>> nc_vals = np.random.noncentral_f(dfnum, dfden, nonc, 1000000) + >>> NF = np.histogram(nc_vals, bins=50, density=True) + >>> c_vals = np.random.f(dfnum, dfden, 1000000) + >>> F = np.histogram(c_vals, bins=50, density=True) + >>> import matplotlib.pyplot as plt + >>> plt.plot(F[1][1:], F[0]) + >>> plt.plot(NF[1][1:], NF[0]) + >>> plt.show() + + """ + return cont(&legacy_noncentral_f, self._aug_state, size, self.lock, 3, + dfnum, 'dfnum', CONS_POSITIVE, + dfden, 'dfden', CONS_POSITIVE, + nonc, 'nonc', CONS_NON_NEGATIVE, None) + + def chisquare(self, df, size=None): + """ + chisquare(df, size=None) + + Draw samples from a chi-square distribution. + + When `df` independent random variables, each with standard normal + distributions (mean 0, variance 1), are squared and summed, the + resulting distribution is chi-square (see Notes). This distribution + is often used in hypothesis testing. + + Parameters + ---------- + df : float or array_like of floats + Number of degrees of freedom, should be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``df`` is a scalar. Otherwise, + ``np.array(df).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized chi-square distribution. + + Raises + ------ + ValueError + When `df` <= 0 or when an inappropriate `size` (e.g. ``size=-1``) + is given. + + Notes + ----- + The variable obtained by summing the squares of `df` independent, + standard normally distributed random variables: + + .. math:: Q = \\sum_{i=0}^{\\mathtt{df}} X^2_i + + is chi-square distributed, denoted + + .. math:: Q \\sim \\chi^2_k. + + The probability density function of the chi-squared distribution is + + .. math:: p(x) = \\frac{(1/2)^{k/2}}{\\Gamma(k/2)} + x^{k/2 - 1} e^{-x/2}, + + where :math:`\\Gamma` is the gamma function, + + .. math:: \\Gamma(x) = \\int_0^{-\\infty} t^{x - 1} e^{-t} dt. + + References + ---------- + .. [1] NIST "Engineering Statistics Handbook" + https://www.itl.nist.gov/div898/handbook/eda/section3/eda3666.htm + + Examples + -------- + >>> np.random.chisquare(2,4) + array([ 1.89920014, 9.00867716, 3.13710533, 5.62318272]) # random + + """ + return cont(&legacy_chisquare, self._aug_state, size, self.lock, 1, + df, 'df', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def noncentral_chisquare(self, df, nonc, size=None): + """ + noncentral_chisquare(df, nonc, size=None) + + Draw samples from a noncentral chi-square distribution. + + The noncentral :math:`\\chi^2` distribution is a generalisation of + the :math:`\\chi^2` distribution. + + Parameters + ---------- + df : float or array_like of floats + Degrees of freedom, should be > 0. + + .. versionchanged:: 1.10.0 + Earlier NumPy versions required dfnum > 1. + nonc : float or array_like of floats + Non-centrality, should be non-negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``df`` and ``nonc`` are both scalars. + Otherwise, ``np.broadcast(df, nonc).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized noncentral chi-square distribution. + + Notes + ----- + The probability density function for the noncentral Chi-square + distribution is + + .. math:: P(x;df,nonc) = \\sum^{\\infty}_{i=0} + \\frac{e^{-nonc/2}(nonc/2)^{i}}{i!} + P_{Y_{df+2i}}(x), + + where :math:`Y_{q}` is the Chi-square with q degrees of freedom. + + References + ---------- + .. [1] Wikipedia, "Noncentral chi-squared distribution" + https://en.wikipedia.org/wiki/Noncentral_chi-squared_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram + + >>> import matplotlib.pyplot as plt + >>> values = plt.hist(np.random.noncentral_chisquare(3, 20, 100000), + ... bins=200, density=True) + >>> plt.show() + + Draw values from a noncentral chisquare with very small noncentrality, + and compare to a chisquare. + + >>> plt.figure() + >>> values = plt.hist(np.random.noncentral_chisquare(3, .0000001, 100000), + ... bins=np.arange(0., 25, .1), density=True) + >>> values2 = plt.hist(np.random.chisquare(3, 100000), + ... bins=np.arange(0., 25, .1), density=True) + >>> plt.plot(values[1][0:-1], values[0]-values2[0], 'ob') + >>> plt.show() + + Demonstrate how large values of non-centrality lead to a more symmetric + distribution. + + >>> plt.figure() + >>> values = plt.hist(np.random.noncentral_chisquare(3, 20, 100000), + ... bins=200, density=True) + >>> plt.show() + """ + return cont(&legacy_noncentral_chisquare, self._aug_state, size, self.lock, 2, + df, 'df', CONS_POSITIVE, + nonc, 'nonc', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def standard_cauchy(self, size=None): + """ + standard_cauchy(size=None) + + Draw samples from a standard Cauchy distribution with mode = 0. + + Also known as the Lorentz distribution. + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + samples : ndarray or scalar + The drawn samples. + + Notes + ----- + The probability density function for the full Cauchy distribution is + + .. math:: P(x; x_0, \\gamma) = \\frac{1}{\\pi \\gamma \\bigl[ 1+ + (\\frac{x-x_0}{\\gamma})^2 \\bigr] } + + and the Standard Cauchy distribution just sets :math:`x_0=0` and + :math:`\\gamma=1` + + The Cauchy distribution arises in the solution to the driven harmonic + oscillator problem, and also describes spectral line broadening. It + also describes the distribution of values at which a line tilted at + a random angle will cut the x axis. + + When studying hypothesis tests that assume normality, seeing how the + tests perform on data from a Cauchy distribution is a good indicator of + their sensitivity to a heavy-tailed distribution, since the Cauchy looks + very much like a Gaussian distribution, but with heavier tails. + + References + ---------- + .. [1] NIST/SEMATECH e-Handbook of Statistical Methods, "Cauchy + Distribution", + https://www.itl.nist.gov/div898/handbook/eda/section3/eda3663.htm + .. [2] Weisstein, Eric W. "Cauchy Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/CauchyDistribution.html + .. [3] Wikipedia, "Cauchy distribution" + https://en.wikipedia.org/wiki/Cauchy_distribution + + Examples + -------- + Draw samples and plot the distribution: + + >>> import matplotlib.pyplot as plt + >>> s = np.random.standard_cauchy(1000000) + >>> s = s[(s>-25) & (s<25)] # truncate distribution so it plots well + >>> plt.hist(s, bins=100) + >>> plt.show() + + """ + return cont(&legacy_standard_cauchy, self._aug_state, size, self.lock, 0, + 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, 0.0, '', CONS_NONE, None) + + def standard_t(self, df, size=None): + """ + standard_t(df, size=None) + + Draw samples from a standard Student's t distribution with `df` degrees + of freedom. + + A special case of the hyperbolic distribution. As `df` gets + large, the result resembles that of the standard normal + distribution (`standard_normal`). + + Parameters + ---------- + df : float or array_like of floats + Degrees of freedom, should be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``df`` is a scalar. Otherwise, + ``np.array(df).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized standard Student's t distribution. + + Notes + ----- + The probability density function for the t distribution is + + .. math:: P(x, df) = \\frac{\\Gamma(\\frac{df+1}{2})}{\\sqrt{\\pi df} + \\Gamma(\\frac{df}{2})}\\Bigl( 1+\\frac{x^2}{df} \\Bigr)^{-(df+1)/2} + + The t test is based on an assumption that the data come from a + Normal distribution. The t test provides a way to test whether + the sample mean (that is the mean calculated from the data) is + a good estimate of the true mean. + + The derivation of the t-distribution was first published in + 1908 by William Gosset while working for the Guinness Brewery + in Dublin. Due to proprietary issues, he had to publish under + a pseudonym, and so he used the name Student. + + References + ---------- + .. [1] Dalgaard, Peter, "Introductory Statistics With R", + Springer, 2002. + .. [2] Wikipedia, "Student's t-distribution" + https://en.wikipedia.org/wiki/Student's_t-distribution + + Examples + -------- + From Dalgaard page 83 [1]_, suppose the daily energy intake for 11 + women in kilojoules (kJ) is: + + >>> intake = np.array([5260., 5470, 5640, 6180, 6390, 6515, 6805, 7515, \\ + ... 7515, 8230, 8770]) + + Does their energy intake deviate systematically from the recommended + value of 7725 kJ? + + We have 10 degrees of freedom, so is the sample mean within 95% of the + recommended value? + + >>> s = np.random.standard_t(10, size=100000) + >>> np.mean(intake) + 6753.636363636364 + >>> intake.std(ddof=1) + 1142.1232221373727 + + Calculate the t statistic, setting the ddof parameter to the unbiased + value so the divisor in the standard deviation will be degrees of + freedom, N-1. + + >>> t = (np.mean(intake)-7725)/(intake.std(ddof=1)/np.sqrt(len(intake))) + >>> import matplotlib.pyplot as plt + >>> h = plt.hist(s, bins=100, density=True) + + For a one-sided t-test, how far out in the distribution does the t + statistic appear? + + >>> np.sum(s=0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``mu`` and ``kappa`` are both scalars. + Otherwise, ``np.broadcast(mu, kappa).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized von Mises distribution. + + See Also + -------- + scipy.stats.vonmises : probability density function, distribution, or + cumulative density function, etc. + + Notes + ----- + The probability density for the von Mises distribution is + + .. math:: p(x) = \\frac{e^{\\kappa cos(x-\\mu)}}{2\\pi I_0(\\kappa)}, + + where :math:`\\mu` is the mode and :math:`\\kappa` the dispersion, + and :math:`I_0(\\kappa)` is the modified Bessel function of order 0. + + The von Mises is named for Richard Edler von Mises, who was born in + Austria-Hungary, in what is now the Ukraine. He fled to the United + States in 1939 and became a professor at Harvard. He worked in + probability theory, aerodynamics, fluid mechanics, and philosophy of + science. + + References + ---------- + .. [1] Abramowitz, M. and Stegun, I. A. (Eds.). "Handbook of + Mathematical Functions with Formulas, Graphs, and Mathematical + Tables, 9th printing," New York: Dover, 1972. + .. [2] von Mises, R., "Mathematical Theory of Probability + and Statistics", New York: Academic Press, 1964. + + Examples + -------- + Draw samples from the distribution: + + >>> mu, kappa = 0.0, 4.0 # mean and dispersion + >>> s = np.random.vonmises(mu, kappa, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> from scipy.special import i0 + >>> plt.hist(s, 50, density=True) + >>> x = np.linspace(-np.pi, np.pi, num=51) + >>> y = np.exp(kappa*np.cos(x-mu))/(2*np.pi*i0(kappa)) + >>> plt.plot(x, y, linewidth=2, color='r') + >>> plt.show() + + """ + return cont(&random_vonmises, self._brng, size, self.lock, 2, + mu, 'mu', CONS_NONE, + kappa, 'kappa', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def pareto(self, a, size=None): + """ + pareto(a, size=None) + + Draw samples from a Pareto II or Lomax distribution with + specified shape. + + The Lomax or Pareto II distribution is a shifted Pareto + distribution. The classical Pareto distribution can be + obtained from the Lomax distribution by adding 1 and + multiplying by the scale parameter ``m`` (see Notes). The + smallest value of the Lomax distribution is zero while for the + classical Pareto distribution it is ``mu``, where the standard + Pareto distribution has location ``mu = 1``. Lomax can also + be considered as a simplified version of the Generalized + Pareto distribution (available in SciPy), with the scale set + to one and the location set to zero. + + The Pareto distribution must be greater than zero, and is + unbounded above. It is also known as the "80-20 rule". In + this distribution, 80 percent of the weights are in the lowest + 20 percent of the range, while the other 20 percent fill the + remaining 80 percent of the range. + + Parameters + ---------- + a : float or array_like of floats + Shape of the distribution. Must all be positive. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Pareto distribution. + + See Also + -------- + scipy.stats.lomax : probability density function, distribution or + cumulative density function, etc. + scipy.stats.genpareto : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Pareto distribution is + + .. math:: p(x) = \\frac{am^a}{x^{a+1}} + + where :math:`a` is the shape and :math:`m` the scale. + + The Pareto distribution, named after the Italian economist + Vilfredo Pareto, is a power law probability distribution + useful in many real world problems. Outside the field of + economics it is generally referred to as the Bradford + distribution. Pareto developed the distribution to describe + the distribution of wealth in an economy. It has also found + use in insurance, web page access statistics, oil field sizes, + and many other problems, including the download frequency for + projects in Sourceforge [1]_. It is one of the so-called + "fat-tailed" distributions. + + + References + ---------- + .. [1] Francis Hunt and Paul Johnson, On the Pareto Distribution of + Sourceforge projects. + .. [2] Pareto, V. (1896). Course of Political Economy. Lausanne. + .. [3] Reiss, R.D., Thomas, M.(2001), Statistical Analysis of Extreme + Values, Birkhauser Verlag, Basel, pp 23-30. + .. [4] Wikipedia, "Pareto distribution", + https://en.wikipedia.org/wiki/Pareto_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> a, m = 3., 2. # shape and mode + >>> s = (np.random.pareto(a, 1000) + 1) * m + + Display the histogram of the samples, along with the probability + density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, _ = plt.hist(s, 100, density=True) + >>> fit = a*m**a / bins**(a+1) + >>> plt.plot(bins, max(count)*fit/max(fit), linewidth=2, color='r') + >>> plt.show() + + """ + return cont(&legacy_pareto, self._aug_state, size, self.lock, 1, + a, 'a', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def weibull(self, a, size=None): + """ + weibull(a, size=None) + + Draw samples from a Weibull distribution. + + Draw samples from a 1-parameter Weibull distribution with the given + shape parameter `a`. + + .. math:: X = (-ln(U))^{1/a} + + Here, U is drawn from the uniform distribution over (0,1]. + + The more common 2-parameter Weibull, including a scale parameter + :math:`\\lambda` is just :math:`X = \\lambda(-ln(U))^{1/a}`. + + Parameters + ---------- + a : float or array_like of floats + Shape parameter of the distribution. Must be nonnegative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Weibull distribution. + + See Also + -------- + scipy.stats.weibull_max + scipy.stats.weibull_min + scipy.stats.genextreme + gumbel + + Notes + ----- + The Weibull (or Type III asymptotic extreme value distribution + for smallest values, SEV Type III, or Rosin-Rammler + distribution) is one of a class of Generalized Extreme Value + (GEV) distributions used in modeling extreme value problems. + This class includes the Gumbel and Frechet distributions. + + The probability density for the Weibull distribution is + + .. math:: p(x) = \\frac{a} + {\\lambda}(\\frac{x}{\\lambda})^{a-1}e^{-(x/\\lambda)^a}, + + where :math:`a` is the shape and :math:`\\lambda` the scale. + + The function has its peak (the mode) at + :math:`\\lambda(\\frac{a-1}{a})^{1/a}`. + + When ``a = 1``, the Weibull distribution reduces to the exponential + distribution. + + References + ---------- + .. [1] Waloddi Weibull, Royal Technical University, Stockholm, + 1939 "A Statistical Theory Of The Strength Of Materials", + Ingeniorsvetenskapsakademiens Handlingar Nr 151, 1939, + Generalstabens Litografiska Anstalts Forlag, Stockholm. + .. [2] Waloddi Weibull, "A Statistical Distribution Function of + Wide Applicability", Journal Of Applied Mechanics ASME Paper + 1951. + .. [3] Wikipedia, "Weibull distribution", + https://en.wikipedia.org/wiki/Weibull_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> a = 5. # shape + >>> s = np.random.weibull(a, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> x = np.arange(1,100.)/50. + >>> def weib(x,n,a): + ... return (a / n) * (x / n)**(a - 1) * np.exp(-(x / n)**a) + + >>> count, bins, ignored = plt.hist(np.random.weibull(5.,1000)) + >>> x = np.arange(1,100.)/50. + >>> scale = count.max()/weib(x, 1., 5.).max() + >>> plt.plot(x, weib(x, 1., 5.)*scale) + >>> plt.show() + + """ + return cont(&legacy_weibull, self._aug_state, size, self.lock, 1, + a, 'a', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def power(self, a, size=None): + """ + power(a, size=None) + + Draws samples in [0, 1] from a power distribution with positive + exponent a - 1. + + Also known as the power function distribution. + + Parameters + ---------- + a : float or array_like of floats + Parameter of the distribution. Should be greater than zero. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized power distribution. + + Raises + ------ + ValueError + If a < 1. + + Notes + ----- + The probability density function is + + .. math:: P(x; a) = ax^{a-1}, 0 \\le x \\le 1, a>0. + + The power function distribution is just the inverse of the Pareto + distribution. It may also be seen as a special case of the Beta + distribution. + + It is used, for example, in modeling the over-reporting of insurance + claims. + + References + ---------- + .. [1] Christian Kleiber, Samuel Kotz, "Statistical size distributions + in economics and actuarial sciences", Wiley, 2003. + .. [2] Heckert, N. A. and Filliben, James J. "NIST Handbook 148: + Dataplot Reference Manual, Volume 2: Let Subcommands and Library + Functions", National Institute of Standards and Technology + Handbook Series, June 2003. + https://www.itl.nist.gov/div898/software/dataplot/refman2/auxillar/powpdf.pdf + + Examples + -------- + Draw samples from the distribution: + + >>> a = 5. # shape + >>> samples = 1000 + >>> s = np.random.power(a, samples) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, bins=30) + >>> x = np.linspace(0, 1, 100) + >>> y = a*x**(a-1.) + >>> normed_y = samples*np.diff(bins)[0]*y + >>> plt.plot(x, normed_y) + >>> plt.show() + + Compare the power function distribution to the inverse of the Pareto. + + >>> from scipy import stats + >>> rvs = np.random.power(5, 1000000) + >>> rvsp = np.random.pareto(5, 1000000) + >>> xx = np.linspace(0,1,100) + >>> powpdf = stats.powerlaw.pdf(xx,5) + + >>> plt.figure() + >>> plt.hist(rvs, bins=50, density=True) + >>> plt.plot(xx,powpdf,'r-') + >>> plt.title('np.random.power(5)') + + >>> plt.figure() + >>> plt.hist(1./(1.+rvsp), bins=50, density=True) + >>> plt.plot(xx,powpdf,'r-') + >>> plt.title('inverse of 1 + np.random.pareto(5)') + + >>> plt.figure() + >>> plt.hist(1./(1.+rvsp), bins=50, density=True) + >>> plt.plot(xx,powpdf,'r-') + >>> plt.title('inverse of stats.pareto(5)') + + """ + return cont(&legacy_power, self._aug_state, size, self.lock, 1, + a, 'a', CONS_POSITIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def laplace(self, loc=0.0, scale=1.0, size=None): + """ + laplace(loc=0.0, scale=1.0, size=None) + + Draw samples from the Laplace or double exponential distribution with + specified location (or mean) and scale (decay). + + The Laplace distribution is similar to the Gaussian/normal distribution, + but is sharper at the peak and has fatter tails. It represents the + difference between two independent, identically distributed exponential + random variables. + + Parameters + ---------- + loc : float or array_like of floats, optional + The position, :math:`\\mu`, of the distribution peak. Default is 0. + scale : float or array_like of floats, optional + :math:`\\lambda`, the exponential decay. Default is 1. Must be non- + negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Laplace distribution. + + Notes + ----- + It has the probability density function + + .. math:: f(x; \\mu, \\lambda) = \\frac{1}{2\\lambda} + \\exp\\left(-\\frac{|x - \\mu|}{\\lambda}\\right). + + The first law of Laplace, from 1774, states that the frequency + of an error can be expressed as an exponential function of the + absolute magnitude of the error, which leads to the Laplace + distribution. For many problems in economics and health + sciences, this distribution seems to model the data better + than the standard Gaussian distribution. + + References + ---------- + .. [1] Abramowitz, M. and Stegun, I. A. (Eds.). "Handbook of + Mathematical Functions with Formulas, Graphs, and Mathematical + Tables, 9th printing," New York: Dover, 1972. + .. [2] Kotz, Samuel, et. al. "The Laplace Distribution and + Generalizations, " Birkhauser, 2001. + .. [3] Weisstein, Eric W. "Laplace Distribution." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/LaplaceDistribution.html + .. [4] Wikipedia, "Laplace distribution", + https://en.wikipedia.org/wiki/Laplace_distribution + + Examples + -------- + Draw samples from the distribution + + >>> loc, scale = 0., 1. + >>> s = np.random.laplace(loc, scale, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 30, density=True) + >>> x = np.arange(-8., 8., .01) + >>> pdf = np.exp(-abs(x-loc)/scale)/(2.*scale) + >>> plt.plot(x, pdf) + + Plot Gaussian for comparison: + + >>> g = (1/(scale * np.sqrt(2 * np.pi)) * + ... np.exp(-(x - loc)**2 / (2 * scale**2))) + >>> plt.plot(x,g) + + """ + return cont(&random_laplace, self._brng, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def gumbel(self, loc=0.0, scale=1.0, size=None): + """ + gumbel(loc=0.0, scale=1.0, size=None) + + Draw samples from a Gumbel distribution. + + Draw samples from a Gumbel distribution with specified location and + scale. For more information on the Gumbel distribution, see + Notes and References below. + + Parameters + ---------- + loc : float or array_like of floats, optional + The location of the mode of the distribution. Default is 0. + scale : float or array_like of floats, optional + The scale parameter of the distribution. Default is 1. Must be non- + negative. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Gumbel distribution. + + See Also + -------- + scipy.stats.gumbel_l + scipy.stats.gumbel_r + scipy.stats.genextreme + weibull + + Notes + ----- + The Gumbel (or Smallest Extreme Value (SEV) or the Smallest Extreme + Value Type I) distribution is one of a class of Generalized Extreme + Value (GEV) distributions used in modeling extreme value problems. + The Gumbel is a special case of the Extreme Value Type I distribution + for maximums from distributions with "exponential-like" tails. + + The probability density for the Gumbel distribution is + + .. math:: p(x) = \\frac{e^{-(x - \\mu)/ \\beta}}{\\beta} e^{ -e^{-(x - \\mu)/ + \\beta}}, + + where :math:`\\mu` is the mode, a location parameter, and + :math:`\\beta` is the scale parameter. + + The Gumbel (named for German mathematician Emil Julius Gumbel) was used + very early in the hydrology literature, for modeling the occurrence of + flood events. It is also used for modeling maximum wind speed and + rainfall rates. It is a "fat-tailed" distribution - the probability of + an event in the tail of the distribution is larger than if one used a + Gaussian, hence the surprisingly frequent occurrence of 100-year + floods. Floods were initially modeled as a Gaussian process, which + underestimated the frequency of extreme events. + + It is one of a class of extreme value distributions, the Generalized + Extreme Value (GEV) distributions, which also includes the Weibull and + Frechet. + + The function has a mean of :math:`\\mu + 0.57721\\beta` and a variance + of :math:`\\frac{\\pi^2}{6}\\beta^2`. + + References + ---------- + .. [1] Gumbel, E. J., "Statistics of Extremes," + New York: Columbia University Press, 1958. + .. [2] Reiss, R.-D. and Thomas, M., "Statistical Analysis of Extreme + Values from Insurance, Finance, Hydrology and Other Fields," + Basel: Birkhauser Verlag, 2001. + + Examples + -------- + Draw samples from the distribution: + + >>> mu, beta = 0, 0.1 # location and scale + >>> s = np.random.gumbel(mu, beta, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 30, density=True) + >>> plt.plot(bins, (1/beta)*np.exp(-(bins - mu)/beta) + ... * np.exp( -np.exp( -(bins - mu) /beta) ), + ... linewidth=2, color='r') + >>> plt.show() + + Show how an extreme value distribution can arise from a Gaussian process + and compare to a Gaussian: + + >>> means = [] + >>> maxima = [] + >>> for i in range(0,1000) : + ... a = np.random.normal(mu, beta, 1000) + ... means.append(a.mean()) + ... maxima.append(a.max()) + >>> count, bins, ignored = plt.hist(maxima, 30, density=True) + >>> beta = np.std(maxima) * np.sqrt(6) / np.pi + >>> mu = np.mean(maxima) - 0.57721*beta + >>> plt.plot(bins, (1/beta)*np.exp(-(bins - mu)/beta) + ... * np.exp(-np.exp(-(bins - mu)/beta)), + ... linewidth=2, color='r') + >>> plt.plot(bins, 1/(beta * np.sqrt(2 * np.pi)) + ... * np.exp(-(bins - mu)**2 / (2 * beta**2)), + ... linewidth=2, color='g') + >>> plt.show() + + """ + return cont(&random_gumbel, self._brng, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def logistic(self, loc=0.0, scale=1.0, size=None): + """ + logistic(loc=0.0, scale=1.0, size=None) + + Draw samples from a logistic distribution. + + Samples are drawn from a logistic distribution with specified + parameters, loc (location or mean, also median), and scale (>0). + + Parameters + ---------- + loc : float or array_like of floats, optional + Parameter of the distribution. Default is 0. + scale : float or array_like of floats, optional + Parameter of the distribution. Must be non-negative. + Default is 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``loc`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(loc, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized logistic distribution. + + See Also + -------- + scipy.stats.logistic : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Logistic distribution is + + .. math:: P(x) = P(x) = \\frac{e^{-(x-\\mu)/s}}{s(1+e^{-(x-\\mu)/s})^2}, + + where :math:`\\mu` = location and :math:`s` = scale. + + The Logistic distribution is used in Extreme Value problems where it + can act as a mixture of Gumbel distributions, in Epidemiology, and by + the World Chess Federation (FIDE) where it is used in the Elo ranking + system, assuming the performance of each player is a logistically + distributed random variable. + + References + ---------- + .. [1] Reiss, R.-D. and Thomas M. (2001), "Statistical Analysis of + Extreme Values, from Insurance, Finance, Hydrology and Other + Fields," Birkhauser Verlag, Basel, pp 132-133. + .. [2] Weisstein, Eric W. "Logistic Distribution." From + MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/LogisticDistribution.html + .. [3] Wikipedia, "Logistic-distribution", + https://en.wikipedia.org/wiki/Logistic_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> loc, scale = 10, 1 + >>> s = np.random.logistic(loc, scale, 10000) + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, bins=50) + + # plot against distribution + + >>> def logist(x, loc, scale): + ... return np.exp((loc-x)/scale)/(scale*(1+np.exp((loc-x)/scale))**2) + >>> plt.plot(bins, logist(bins, loc, scale)*count.max()/\\ + ... logist(bins, loc, scale).max()) + >>> plt.show() + + """ + return cont(&random_logistic, self._brng, size, self.lock, 2, + loc, 'loc', CONS_NONE, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def lognormal(self, mean=0.0, sigma=1.0, size=None): + """ + lognormal(mean=0.0, sigma=1.0, size=None) + + Draw samples from a log-normal distribution. + + Draw samples from a log-normal distribution with specified mean, + standard deviation, and array shape. Note that the mean and standard + deviation are not the values for the distribution itself, but of the + underlying normal distribution it is derived from. + + Parameters + ---------- + mean : float or array_like of floats, optional + Mean value of the underlying normal distribution. Default is 0. + sigma : float or array_like of floats, optional + Standard deviation of the underlying normal distribution. Must be + non-negative. Default is 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``mean`` and ``sigma`` are both scalars. + Otherwise, ``np.broadcast(mean, sigma).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized log-normal distribution. + + See Also + -------- + scipy.stats.lognorm : probability density function, distribution, + cumulative density function, etc. + + Notes + ----- + A variable `x` has a log-normal distribution if `log(x)` is normally + distributed. The probability density function for the log-normal + distribution is: + + .. math:: p(x) = \\frac{1}{\\sigma x \\sqrt{2\\pi}} + e^{(-\\frac{(ln(x)-\\mu)^2}{2\\sigma^2})} + + where :math:`\\mu` is the mean and :math:`\\sigma` is the standard + deviation of the normally distributed logarithm of the variable. + A log-normal distribution results if a random variable is the *product* + of a large number of independent, identically-distributed variables in + the same way that a normal distribution results if the variable is the + *sum* of a large number of independent, identically-distributed + variables. + + References + ---------- + .. [1] Limpert, E., Stahel, W. A., and Abbt, M., "Log-normal + Distributions across the Sciences: Keys and Clues," + BioScience, Vol. 51, No. 5, May, 2001. + https://stat.ethz.ch/~stahel/lognormal/bioscience.pdf + .. [2] Reiss, R.D. and Thomas, M., "Statistical Analysis of Extreme + Values," Basel: Birkhauser Verlag, 2001, pp. 31-32. + + Examples + -------- + Draw samples from the distribution: + + >>> mu, sigma = 3., 1. # mean and standard deviation + >>> s = np.random.lognormal(mu, sigma, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 100, density=True, align='mid') + + >>> x = np.linspace(min(bins), max(bins), 10000) + >>> pdf = (np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2)) + ... / (x * sigma * np.sqrt(2 * np.pi))) + + >>> plt.plot(x, pdf, linewidth=2, color='r') + >>> plt.axis('tight') + >>> plt.show() + + Demonstrate that taking the products of random samples from a uniform + distribution can be fit well by a log-normal probability density + function. + + >>> # Generate a thousand samples: each is the product of 100 random + >>> # values, drawn from a normal distribution. + >>> b = [] + >>> for i in range(1000): + ... a = 10. + np.random.random(100) + ... b.append(np.product(a)) + + >>> b = np.array(b) / np.min(b) # scale values to be positive + >>> count, bins, ignored = plt.hist(b, 100, density=True, align='mid') + >>> sigma = np.std(np.log(b)) + >>> mu = np.mean(np.log(b)) + + >>> x = np.linspace(min(bins), max(bins), 10000) + >>> pdf = (np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2)) + ... / (x * sigma * np.sqrt(2 * np.pi))) + + >>> plt.plot(x, pdf, color='r', linewidth=2) + >>> plt.show() + + """ + return cont(&legacy_lognormal, self._aug_state, size, self.lock, 2, + mean, 'mean', CONS_NONE, + sigma, 'sigma', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, None) + + def rayleigh(self, scale=1.0, size=None): + """ + rayleigh(scale=1.0, size=None) + + Draw samples from a Rayleigh distribution. + + The :math:`\\chi` and Weibull distributions are generalizations of the + Rayleigh. + + Parameters + ---------- + scale : float or array_like of floats, optional + Scale, also equals the mode. Must be non-negative. Default is 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``scale`` is a scalar. Otherwise, + ``np.array(scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Rayleigh distribution. + + Notes + ----- + The probability density function for the Rayleigh distribution is + + .. math:: P(x;scale) = \\frac{x}{scale^2}e^{\\frac{-x^2}{2 \\cdotp scale^2}} + + The Rayleigh distribution would arise, for example, if the East + and North components of the wind velocity had identical zero-mean + Gaussian distributions. Then the wind speed would have a Rayleigh + distribution. + + References + ---------- + .. [1] Brighton Webs Ltd., "Rayleigh Distribution," + https://web.archive.org/web/20090514091424/http://brighton-webs.co.uk:80/distributions/rayleigh.asp + .. [2] Wikipedia, "Rayleigh distribution" + https://en.wikipedia.org/wiki/Rayleigh_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram + + >>> from matplotlib.pyplot import hist + >>> values = hist(np.random.rayleigh(3, 100000), bins=200, density=True) + + Wave heights tend to follow a Rayleigh distribution. If the mean wave + height is 1 meter, what fraction of waves are likely to be larger than 3 + meters? + + >>> meanvalue = 1 + >>> modevalue = np.sqrt(2 / np.pi) * meanvalue + >>> s = np.random.rayleigh(modevalue, 1000000) + + The percentage of waves larger than 3 meters is: + + >>> 100.*sum(s>3)/1000000. + 0.087300000000000003 # random + + """ + return cont(&random_rayleigh, self._brng, size, self.lock, 1, + scale, 'scale', CONS_NON_NEGATIVE, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE, None) + + def wald(self, mean, scale, size=None): + """ + wald(mean, scale, size=None) + + Draw samples from a Wald, or inverse Gaussian, distribution. + + As the scale approaches infinity, the distribution becomes more like a + Gaussian. Some references claim that the Wald is an inverse Gaussian + with mean equal to 1, but this is by no means universal. + + The inverse Gaussian distribution was first studied in relationship to + Brownian motion. In 1956 M.C.K. Tweedie used the name inverse Gaussian + because there is an inverse relationship between the time to cover a + unit distance and distance covered in unit time. + + Parameters + ---------- + mean : float or array_like of floats + Distribution mean, must be > 0. + scale : float or array_like of floats + Scale parameter, must be > 0. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``mean`` and ``scale`` are both scalars. + Otherwise, ``np.broadcast(mean, scale).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Wald distribution. + + Notes + ----- + The probability density function for the Wald distribution is + + .. math:: P(x;mean,scale) = \\sqrt{\\frac{scale}{2\\pi x^3}}e^ + \\frac{-scale(x-mean)^2}{2\\cdotp mean^2x} + + As noted above the inverse Gaussian distribution first arise + from attempts to model Brownian motion. It is also a + competitor to the Weibull for use in reliability modeling and + modeling stock returns and interest rate processes. + + References + ---------- + .. [1] Brighton Webs Ltd., Wald Distribution, + https://web.archive.org/web/20090423014010/http://www.brighton-webs.co.uk:80/distributions/wald.asp + .. [2] Chhikara, Raj S., and Folks, J. Leroy, "The Inverse Gaussian + Distribution: Theory : Methodology, and Applications", CRC Press, + 1988. + .. [3] Wikipedia, "Inverse Gaussian distribution" + https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram: + + >>> import matplotlib.pyplot as plt + >>> h = plt.hist(np.random.wald(3, 2, 100000), bins=200, density=True) + >>> plt.show() + + """ + return cont(&legacy_wald, self._aug_state, size, self.lock, 2, + mean, 'mean', CONS_POSITIVE, + scale, 'scale', CONS_POSITIVE, + 0.0, '', CONS_NONE, None) + + def triangular(self, left, mode, right, size=None): + """ + triangular(left, mode, right, size=None) + + Draw samples from the triangular distribution over the + interval ``[left, right]``. + + The triangular distribution is a continuous probability + distribution with lower limit left, peak at mode, and upper + limit right. Unlike the other distributions, these parameters + directly define the shape of the pdf. + + Parameters + ---------- + left : float or array_like of floats + Lower limit. + mode : float or array_like of floats + The value where the peak of the distribution occurs. + The value should fulfill the condition ``left <= mode <= right``. + right : float or array_like of floats + Upper limit, should be larger than `left`. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``left``, ``mode``, and ``right`` + are all scalars. Otherwise, ``np.broadcast(left, mode, right).size`` + samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized triangular distribution. + + Notes + ----- + The probability density function for the triangular distribution is + + .. math:: P(x;l, m, r) = \\begin{cases} + \\frac{2(x-l)}{(r-l)(m-l)}& \\text{for $l \\leq x \\leq m$},\\\\ + \\frac{2(r-x)}{(r-l)(r-m)}& \\text{for $m \\leq x \\leq r$},\\\\ + 0& \\text{otherwise}. + \\end{cases} + + The triangular distribution is often used in ill-defined + problems where the underlying distribution is not known, but + some knowledge of the limits and mode exists. Often it is used + in simulations. + + References + ---------- + .. [1] Wikipedia, "Triangular distribution" + https://en.wikipedia.org/wiki/Triangular_distribution + + Examples + -------- + Draw values from the distribution and plot the histogram: + + >>> import matplotlib.pyplot as plt + >>> h = plt.hist(np.random.triangular(-3, 0, 8, 100000), bins=200, + ... density=True) + >>> plt.show() + + """ + cdef bint is_scalar = True + cdef double fleft, fmode, fright + cdef np.ndarray oleft, omode, oright + + oleft = np.PyArray_FROM_OTF(left, np.NPY_DOUBLE, np.NPY_ALIGNED) + omode = np.PyArray_FROM_OTF(mode, np.NPY_DOUBLE, np.NPY_ALIGNED) + oright = np.PyArray_FROM_OTF(right, np.NPY_DOUBLE, np.NPY_ALIGNED) + + if np.PyArray_NDIM(oleft) == np.PyArray_NDIM(omode) == np.PyArray_NDIM(oright) == 0: + fleft = PyFloat_AsDouble(left) + fright = PyFloat_AsDouble(right) + fmode = PyFloat_AsDouble(mode) + + if fleft > fmode: + raise ValueError("left > mode") + if fmode > fright: + raise ValueError("mode > right") + if fleft == fright: + raise ValueError("left == right") + return cont(&random_triangular, self._brng, size, self.lock, 3, + fleft, '', CONS_NONE, + fmode, '', CONS_NONE, + fright, '', CONS_NONE, None) + + if np.any(np.greater(oleft, omode)): + raise ValueError("left > mode") + if np.any(np.greater(omode, oright)): + raise ValueError("mode > right") + if np.any(np.equal(oleft, oright)): + raise ValueError("left == right") + + return cont_broadcast_3(&random_triangular, self._brng, size, self.lock, + oleft, '', CONS_NONE, + omode, '', CONS_NONE, + oright, '', CONS_NONE) + + # Complicated, discrete distributions: + def binomial(self, n, p, size=None): + """ + binomial(n, p, size=None) + + Draw samples from a binomial distribution. + + Samples are drawn from a binomial distribution with specified + parameters, n trials and p probability of success where + n an integer >= 0 and p is in the interval [0,1]. (n may be + input as a float, but it is truncated to an integer in use) + + Parameters + ---------- + n : int or array_like of ints + Parameter of the distribution, >= 0. Floats are also accepted, + but they will be truncated to integers. + p : float or array_like of floats + Parameter of the distribution, >= 0 and <=1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``n`` and ``p`` are both scalars. + Otherwise, ``np.broadcast(n, p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized binomial distribution, where + each sample is equal to the number of successes over the n trials. + + See Also + -------- + scipy.stats.binom : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the binomial distribution is + + .. math:: P(N) = \\binom{n}{N}p^N(1-p)^{n-N}, + + where :math:`n` is the number of trials, :math:`p` is the probability + of success, and :math:`N` is the number of successes. + + When estimating the standard error of a proportion in a population by + using a random sample, the normal distribution works well unless the + product p*n <=5, where p = population proportion estimate, and n = + number of samples, in which case the binomial distribution is used + instead. For example, a sample of 15 people shows 4 who are left + handed, and 11 who are right handed. Then p = 4/15 = 27%. 0.27*15 = 4, + so the binomial distribution should be used in this case. + + References + ---------- + .. [1] Dalgaard, Peter, "Introductory Statistics with R", + Springer-Verlag, 2002. + .. [2] Glantz, Stanton A. "Primer of Biostatistics.", McGraw-Hill, + Fifth Edition, 2002. + .. [3] Lentner, Marvin, "Elementary Applied Statistics", Bogden + and Quigley, 1972. + .. [4] Weisstein, Eric W. "Binomial Distribution." From MathWorld--A + Wolfram Web Resource. + http://mathworld.wolfram.com/BinomialDistribution.html + .. [5] Wikipedia, "Binomial distribution", + https://en.wikipedia.org/wiki/Binomial_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> n, p = 10, .5 # number of trials, probability of each trial + >>> s = np.random.binomial(n, p, 1000) + # result of flipping a coin 10 times, tested 1000 times. + + A real world example. A company drills 9 wild-cat oil exploration + wells, each with an estimated probability of success of 0.1. All nine + wells fail. What is the probability of that happening? + + Let's do 20,000 trials of the model, and count the number that + generate zero positive results. + + >>> sum(np.random.binomial(9, 0.1, 20000) == 0)/20000. + # answer = 0.38885, or 38%. + + """ + + # Uses a custom implementation since self._binomial is required + cdef double _dp = 0 + cdef int64_t _in = 0 + cdef bint is_scalar = True + cdef np.npy_intp i, cnt + cdef np.ndarray randoms + cdef np.int64_t *randoms_data + cdef np.broadcast it + + p_arr = np.PyArray_FROM_OTF(p, np.NPY_DOUBLE, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(p_arr) == 0 + n_arr = np.PyArray_FROM_OTF(n, np.NPY_INT64, np.NPY_ALIGNED) + is_scalar = is_scalar and np.PyArray_NDIM(n_arr) == 0 + + if not is_scalar: + check_array_constraint(p_arr, 'p', CONS_BOUNDED_0_1) + check_array_constraint(n_arr, 'n', CONS_NON_NEGATIVE) + if size is not None: + randoms = np.empty(size, np.int64) + else: + it = np.PyArray_MultiIterNew2(p_arr, n_arr) + randoms = np.empty(it.shape, np.int64) + + randoms_data = np.PyArray_DATA(randoms) + cnt = np.PyArray_SIZE(randoms) + + it = np.PyArray_MultiIterNew3(randoms, p_arr, n_arr) + with self.lock, nogil: + for i in range(cnt): + _dp = (np.PyArray_MultiIter_DATA(it, 1))[0] + _in = (np.PyArray_MultiIter_DATA(it, 2))[0] + (np.PyArray_MultiIter_DATA(it, 0))[0] = random_binomial(self._brng, _dp, _in, self._binomial) + + np.PyArray_MultiIter_NEXT(it) + + return randoms + + _dp = PyFloat_AsDouble(p) + _in = n + check_constraint(_dp, 'p', CONS_BOUNDED_0_1) + check_constraint(_in, 'n', CONS_NON_NEGATIVE) + + if size is None: + with self.lock: + return random_binomial(self._brng, _dp, _in, self._binomial) + + randoms = np.empty(size, np.int64) + cnt = np.PyArray_SIZE(randoms) + randoms_data = np.PyArray_DATA(randoms) + + with self.lock, nogil: + for i in range(cnt): + randoms_data[i] = random_binomial(self._brng, _dp, _in, + self._binomial) + + return randoms + + def negative_binomial(self, n, p, size=None): + """ + negative_binomial(n, p, size=None) + + Draw samples from a negative binomial distribution. + + Samples are drawn from a negative binomial distribution with specified + parameters, `n` successes and `p` probability of success where `n` + is > 0 and `p` is in the interval [0, 1]. + + Parameters + ---------- + n : float or array_like of floats + Parameter of the distribution, > 0. + p : float or array_like of floats + Parameter of the distribution, >= 0 and <=1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``n`` and ``p`` are both scalars. + Otherwise, ``np.broadcast(n, p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized negative binomial distribution, + where each sample is equal to N, the number of failures that + occurred before a total of n successes was reached. + + Notes + ----- + The probability mass function of the negative binomial distribution is + + .. math:: P(N;n,p) = \\frac{\\Gamma(N+n)}{N!\\Gamma(n)}p^{n}(1-p)^{N}, + + where :math:`n` is the number of successes, :math:`p` is the + probability of success, :math:`N+n` is the number of trials, and + :math:`\\Gamma` is the gamma function. When :math:`n` is an integer, + :math:`\\frac{\\Gamma(N+n)}{N!\\Gamma(n)} = \\binom{N+n-1}{N}`, which is + the more common form of this term in the the pmf. The negative + binomial distribution gives the probability of N failures given n + successes, with a success on the last trial. + + If one throws a die repeatedly until the third time a "1" appears, + then the probability distribution of the number of non-"1"s that + appear before the third "1" is a negative binomial distribution. + + References + ---------- + .. [1] Weisstein, Eric W. "Negative Binomial Distribution." From + MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/NegativeBinomialDistribution.html + .. [2] Wikipedia, "Negative binomial distribution", + https://en.wikipedia.org/wiki/Negative_binomial_distribution + + Examples + -------- + Draw samples from the distribution: + + A real world example. A company drills wild-cat oil + exploration wells, each with an estimated probability of + success of 0.1. What is the probability of having one success + for each successive well, that is what is the probability of a + single success after drilling 5 wells, after 6 wells, etc.? + + >>> s = np.random.negative_binomial(1, 0.1, 100000) + >>> for i in range(1, 11): # doctest: +SKIP + ... probability = sum(s= 0. A sequence of expectation + intervals must be broadcastable over the requested size. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``lam`` is a scalar. Otherwise, + ``np.array(lam).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Poisson distribution. + + Notes + ----- + The Poisson distribution + + .. math:: f(k; \\lambda)=\\frac{\\lambda^k e^{-\\lambda}}{k!} + + For events with an expected separation :math:`\\lambda` the Poisson + distribution :math:`f(k; \\lambda)` describes the probability of + :math:`k` events occurring within the observed + interval :math:`\\lambda`. + + Because the output is limited to the range of the C int64 type, a + ValueError is raised when `lam` is within 10 sigma of the maximum + representable value. + + References + ---------- + .. [1] Weisstein, Eric W. "Poisson Distribution." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/PoissonDistribution.html + .. [2] Wikipedia, "Poisson distribution", + https://en.wikipedia.org/wiki/Poisson_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> import numpy as np + >>> s = np.random.poisson(5, 10000) + + Display histogram of the sample: + + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s, 14, density=True) + >>> plt.show() + + Draw each 100 values for lambda 100 and 500: + + >>> s = np.random.poisson(lam=(100., 500.), size=(100, 2)) + + """ + return disc(&random_poisson, self._brng, size, self.lock, 1, 0, + lam, 'lam', CONS_POISSON, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + def zipf(self, a, size=None): + """ + zipf(a, size=None) + + Draw samples from a Zipf distribution. + + Samples are drawn from a Zipf distribution with specified parameter + `a` > 1. + + The Zipf distribution (also known as the zeta distribution) is a + continuous probability distribution that satisfies Zipf's law: the + frequency of an item is inversely proportional to its rank in a + frequency table. + + Parameters + ---------- + a : float or array_like of floats + Distribution parameter. Should be greater than 1. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``a`` is a scalar. Otherwise, + ``np.array(a).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized Zipf distribution. + + See Also + -------- + scipy.stats.zipf : probability density function, distribution, or + cumulative density function, etc. + + Notes + ----- + The probability density for the Zipf distribution is + + .. math:: p(x) = \\frac{x^{-a}}{\\zeta(a)}, + + where :math:`\\zeta` is the Riemann Zeta function. + + It is named for the American linguist George Kingsley Zipf, who noted + that the frequency of any word in a sample of a language is inversely + proportional to its rank in the frequency table. + + References + ---------- + .. [1] Zipf, G. K., "Selected Studies of the Principle of Relative + Frequency in Language," Cambridge, MA: Harvard Univ. Press, + 1932. + + Examples + -------- + Draw samples from the distribution: + + >>> a = 2. # parameter + >>> s = np.random.zipf(a, 1000) + + Display the histogram of the samples, along with + the probability density function: + + >>> import matplotlib.pyplot as plt + >>> from scipy import special + + Truncate s values at 50 so plot is interesting: + + >>> count, bins, ignored = plt.hist(s[s<50], 50, density=True) + >>> x = np.arange(1., 50.) + >>> y = x**(-a) / special.zetac(a) + >>> plt.plot(x, y/max(y), linewidth=2, color='r') + >>> plt.show() + + """ + return disc(&random_zipf, self._brng, size, self.lock, 1, 0, + a, 'a', CONS_GT_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + def geometric(self, p, size=None): + """ + geometric(p, size=None) + + Draw samples from the geometric distribution. + + Bernoulli trials are experiments with one of two outcomes: + success or failure (an example of such an experiment is flipping + a coin). The geometric distribution models the number of trials + that must be run in order to achieve success. It is therefore + supported on the positive integers, ``k = 1, 2, ...``. + + The probability mass function of the geometric distribution is + + .. math:: f(k) = (1 - p)^{k - 1} p + + where `p` is the probability of success of an individual trial. + + Parameters + ---------- + p : float or array_like of floats + The probability of success of an individual trial. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``p`` is a scalar. Otherwise, + ``np.array(p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized geometric distribution. + + Examples + -------- + Draw ten thousand values from the geometric distribution, + with the probability of an individual success equal to 0.35: + + >>> z = np.random.geometric(p=0.35, size=10000) + + How many trials succeeded after a single run? + + >>> (z == 1).sum() / 10000. + 0.34889999999999999 #random + + """ + return disc(&random_geometric, self._brng, size, self.lock, 1, 0, + p, 'p', CONS_BOUNDED_GT_0_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + def hypergeometric(self, ngood, nbad, nsample, size=None): + """ + hypergeometric(ngood, nbad, nsample, size=None) + + Draw samples from a Hypergeometric distribution. + + Samples are drawn from a hypergeometric distribution with specified + parameters, `ngood` (ways to make a good selection), `nbad` (ways to make + a bad selection), and `nsample` (number of items sampled, which is less + than or equal to the sum ``ngood + nbad``). + + Parameters + ---------- + ngood : int or array_like of ints + Number of ways to make a good selection. Must be nonnegative. + nbad : int or array_like of ints + Number of ways to make a bad selection. Must be nonnegative. + nsample : int or array_like of ints + Number of items sampled. Must be at least 1 and at most + ``ngood + nbad``. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if `ngood`, `nbad`, and `nsample` + are all scalars. Otherwise, ``np.broadcast(ngood, nbad, nsample).size`` + samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized hypergeometric distribution. Each + sample is the number of good items within a randomly selected subset of + size `nsample` taken from a set of `ngood` good items and `nbad` bad items. + + See Also + -------- + scipy.stats.hypergeom : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Hypergeometric distribution is + + .. math:: P(x) = \\frac{\\binom{g}{x}\\binom{b}{n-x}}{\\binom{g+b}{n}}, + + where :math:`0 \\le x \\le n` and :math:`n-b \\le x \\le g` + + for P(x) the probability of ``x`` good results in the drawn sample, + g = `ngood`, b = `nbad`, and n = `nsample`. + + Consider an urn with black and white marbles in it, `ngood` of them + are black and `nbad` are white. If you draw `nsample` balls without + replacement, then the hypergeometric distribution describes the + distribution of black balls in the drawn sample. + + Note that this distribution is very similar to the binomial + distribution, except that in this case, samples are drawn without + replacement, whereas in the Binomial case samples are drawn with + replacement (or the sample space is infinite). As the sample space + becomes large, this distribution approaches the binomial. + + References + ---------- + .. [1] Lentner, Marvin, "Elementary Applied Statistics", Bogden + and Quigley, 1972. + .. [2] Weisstein, Eric W. "Hypergeometric Distribution." From + MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/HypergeometricDistribution.html + .. [3] Wikipedia, "Hypergeometric distribution", + https://en.wikipedia.org/wiki/Hypergeometric_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> ngood, nbad, nsamp = 100, 2, 10 + # number of good, number of bad, and number of samples + >>> s = np.random.hypergeometric(ngood, nbad, nsamp, 1000) + >>> from matplotlib.pyplot import hist + >>> hist(s) + # note that it is very unlikely to grab both bad items + + Suppose you have an urn with 15 white and 15 black marbles. + If you pull 15 marbles at random, how likely is it that + 12 or more of them are one color? + + >>> s = np.random.hypergeometric(15, 15, 15, 100000) + >>> sum(s>=12)/100000. + sum(s<=3)/100000. + # answer = 0.003 ... pretty unlikely! + + """ + cdef bint is_scalar = True + cdef np.ndarray ongood, onbad, onsample + cdef int64_t lngood, lnbad, lnsample + + ongood = np.PyArray_FROM_OTF(ngood, np.NPY_INT64, np.NPY_ALIGNED) + onbad = np.PyArray_FROM_OTF(nbad, np.NPY_INT64, np.NPY_ALIGNED) + onsample = np.PyArray_FROM_OTF(nsample, np.NPY_INT64, np.NPY_ALIGNED) + + if np.PyArray_NDIM(ongood) == np.PyArray_NDIM(onbad) == np.PyArray_NDIM(onsample) == 0: + + lngood = ngood + lnbad = nbad + lnsample = nsample + + if lngood + lnbad < lnsample: + raise ValueError("ngood + nbad < nsample") + return disc(&random_hypergeometric, self._brng, size, self.lock, 0, 3, + lngood, 'ngood', CONS_NON_NEGATIVE, + lnbad, 'nbad', CONS_NON_NEGATIVE, + lnsample, 'nsample', CONS_GTE_1) + + if np.any(np.less(np.add(ongood, onbad), onsample)): + raise ValueError("ngood + nbad < nsample") + return discrete_broadcast_iii(&random_hypergeometric, self._brng, size, self.lock, + ongood, 'ngood', CONS_NON_NEGATIVE, + onbad, 'nbad', CONS_NON_NEGATIVE, + onsample, 'nsample', CONS_GTE_1) + + def logseries(self, p, size=None): + """ + logseries(p, size=None) + + Draw samples from a logarithmic series distribution. + + Samples are drawn from a log series distribution with specified + shape parameter, 0 < ``p`` < 1. + + Parameters + ---------- + p : float or array_like of floats + Shape parameter for the distribution. Must be in the range (0, 1). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. If size is ``None`` (default), + a single value is returned if ``p`` is a scalar. Otherwise, + ``np.array(p).size`` samples are drawn. + + Returns + ------- + out : ndarray or scalar + Drawn samples from the parameterized logarithmic series distribution. + + See Also + -------- + scipy.stats.logser : probability density function, distribution or + cumulative density function, etc. + + Notes + ----- + The probability density for the Log Series distribution is + + .. math:: P(k) = \\frac{-p^k}{k \\ln(1-p)}, + + where p = probability. + + The log series distribution is frequently used to represent species + richness and occurrence, first proposed by Fisher, Corbet, and + Williams in 1943 [2]. It may also be used to model the numbers of + occupants seen in cars [3]. + + References + ---------- + .. [1] Buzas, Martin A.; Culver, Stephen J., Understanding regional + species diversity through the log series distribution of + occurrences: BIODIVERSITY RESEARCH Diversity & Distributions, + Volume 5, Number 5, September 1999 , pp. 187-195(9). + .. [2] Fisher, R.A,, A.S. Corbet, and C.B. Williams. 1943. The + relation between the number of species and the number of + individuals in a random sample of an animal population. + Journal of Animal Ecology, 12:42-58. + .. [3] D. J. Hand, F. Daly, D. Lunn, E. Ostrowski, A Handbook of Small + Data Sets, CRC Press, 1994. + .. [4] Wikipedia, "Logarithmic distribution", + https://en.wikipedia.org/wiki/Logarithmic_distribution + + Examples + -------- + Draw samples from the distribution: + + >>> a = .6 + >>> s = np.random.logseries(a, 10000) + >>> import matplotlib.pyplot as plt + >>> count, bins, ignored = plt.hist(s) + + # plot against distribution + + >>> def logseries(k, p): + ... return -p**k/(k*np.log(1-p)) + >>> plt.plot(bins, logseries(bins, a)*count.max()/ + ... logseries(bins, a).max(), 'r') + >>> plt.show() + + """ + return disc(&random_logseries, self._brng, size, self.lock, 1, 0, + p, 'p', CONS_BOUNDED_0_1, + 0.0, '', CONS_NONE, + 0.0, '', CONS_NONE) + + # Multivariate distributions: + def multivariate_normal(self, mean, cov, size=None, check_valid='warn', + tol=1e-8): + """ + multivariate_normal(mean, cov, size=None, check_valid='warn', tol=1e-8) + + Draw random samples from a multivariate normal distribution. + + The multivariate normal, multinormal or Gaussian distribution is a + generalization of the one-dimensional normal distribution to higher + dimensions. Such a distribution is specified by its mean and + covariance matrix. These parameters are analogous to the mean + (average or "center") and variance (standard deviation, or "width," + squared) of the one-dimensional normal distribution. + + Parameters + ---------- + mean : 1-D array_like, of length N + Mean of the N-dimensional distribution. + cov : 2-D array_like, of shape (N, N) + Covariance matrix of the distribution. It must be symmetric and + positive-semidefinite for proper sampling. + size : int or tuple of ints, optional + Given a shape of, for example, ``(m,n,k)``, ``m*n*k`` samples are + generated, and packed in an `m`-by-`n`-by-`k` arrangement. Because + each sample is `N`-dimensional, the output shape is ``(m,n,k,N)``. + If no shape is specified, a single (`N`-D) sample is returned. + check_valid : { 'warn', 'raise', 'ignore' }, optional + Behavior when the covariance matrix is not positive semidefinite. + tol : float, optional + Tolerance when checking the singular values in covariance matrix. + cov is cast to double before the check. + + Returns + ------- + out : ndarray + The drawn samples, of shape *size*, if that was provided. If not, + the shape is ``(N,)``. + + In other words, each entry ``out[i,j,...,:]`` is an N-dimensional + value drawn from the distribution. + + Notes + ----- + The mean is a coordinate in N-dimensional space, which represents the + location where samples are most likely to be generated. This is + analogous to the peak of the bell curve for the one-dimensional or + univariate normal distribution. + + Covariance indicates the level to which two variables vary together. + From the multivariate normal distribution, we draw N-dimensional + samples, :math:`X = [x_1, x_2, ... x_N]`. The covariance matrix + element :math:`C_{ij}` is the covariance of :math:`x_i` and :math:`x_j`. + The element :math:`C_{ii}` is the variance of :math:`x_i` (i.e. its + "spread"). + + Instead of specifying the full covariance matrix, popular + approximations include: + + - Spherical covariance (`cov` is a multiple of the identity matrix) + - Diagonal covariance (`cov` has non-negative elements, and only on + the diagonal) + + This geometrical property can be seen in two dimensions by plotting + generated data-points: + + >>> mean = [0, 0] + >>> cov = [[1, 0], [0, 100]] # diagonal covariance + + Diagonal covariance means that points are oriented along x or y-axis: + + >>> import matplotlib.pyplot as plt + >>> x, y = np.random.multivariate_normal(mean, cov, 5000).T + >>> plt.plot(x, y, 'x') + >>> plt.axis('equal') + >>> plt.show() + + Note that the covariance matrix must be positive semidefinite (a.k.a. + nonnegative-definite). Otherwise, the behavior of this method is + undefined and backwards compatibility is not guaranteed. + + References + ---------- + .. [1] Papoulis, A., "Probability, Random Variables, and Stochastic + Processes," 3rd ed., New York: McGraw-Hill, 1991. + .. [2] Duda, R. O., Hart, P. E., and Stork, D. G., "Pattern + Classification," 2nd ed., New York: Wiley, 2001. + + Examples + -------- + >>> mean = (1, 2) + >>> cov = [[1, 0], [0, 1]] + >>> x = np.random.multivariate_normal(mean, cov, (3, 3)) + >>> x.shape + (3, 3, 2) + + The following is probably true, given that 0.6 is roughly twice the + standard deviation: + + >>> list((x[0,0,:] - mean) < 0.6) + [True, True] # random + + """ + from numpy.dual import svd + + # Check preconditions on arguments + mean = np.array(mean) + cov = np.array(cov) + if size is None: + shape = [] + elif isinstance(size, (int, long, np.integer)): + shape = [size] + else: + shape = size + + if len(mean.shape) != 1: + raise ValueError("mean must be 1 dimensional") + if (len(cov.shape) != 2) or (cov.shape[0] != cov.shape[1]): + raise ValueError("cov must be 2 dimensional and square") + if mean.shape[0] != cov.shape[0]: + raise ValueError("mean and cov must have same length") + + # Compute shape of output and create a matrix of independent + # standard normally distributed random numbers. The matrix has rows + # with the same length as mean and as many rows are necessary to + # form a matrix of shape final_shape. + final_shape = list(shape[:]) + final_shape.append(mean.shape[0]) + x = self.standard_normal(final_shape).reshape(-1, mean.shape[0]) + + # Transform matrix of standard normals into matrix where each row + # contains multivariate normals with the desired covariance. + # Compute A such that dot(transpose(A),A) == cov. + # Then the matrix products of the rows of x and A has the desired + # covariance. Note that sqrt(s)*v where (u,s,v) is the singular value + # decomposition of cov is such an A. + # + # Also check that cov is positive-semidefinite. If so, the u.T and v + # matrices should be equal up to roundoff error if cov is + # symmetric and the singular value of the corresponding row is + # not zero. We continue to use the SVD rather than Cholesky in + # order to preserve current outputs. Note that symmetry has not + # been checked. + + # GH10839, ensure double to make tol meaningful + cov = cov.astype(np.double) + (u, s, v) = svd(cov) + + if check_valid != 'ignore': + if check_valid != 'warn' and check_valid != 'raise': + raise ValueError( + "check_valid must equal 'warn', 'raise', or 'ignore'") + + psd = np.allclose(np.dot(v.T * s, v), cov, rtol=tol, atol=tol) + if not psd: + if check_valid == 'warn': + warnings.warn("covariance is not positive-semidefinite.", + RuntimeWarning) + else: + raise ValueError( + "covariance is not positive-semidefinite.") + + x = np.dot(x, np.sqrt(s)[:, None] * v) + x += mean + x.shape = tuple(final_shape) + return x + + def multinomial(self, np.npy_intp n, object pvals, size=None): + """ + multinomial(n, pvals, size=None) + + Draw samples from a multinomial distribution. + + The multinomial distribution is a multivariate generalisation of the + binomial distribution. Take an experiment with one of ``p`` + possible outcomes. An example of such an experiment is throwing a dice, + where the outcome can be 1 through 6. Each sample drawn from the + distribution represents `n` such experiments. Its values, + ``X_i = [X_0, X_1, ..., X_p]``, represent the number of times the + outcome was ``i``. + + Parameters + ---------- + n : int + Number of experiments. + pvals : sequence of floats, length p + Probabilities of each of the ``p`` different outcomes. These + should sum to 1 (however, the last element is always assumed to + account for the remaining probability, as long as + ``sum(pvals[:-1]) <= 1)``. + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + out : ndarray + The drawn samples, of shape *size*, if that was provided. If not, + the shape is ``(N,)``. + + In other words, each entry ``out[i,j,...,:]`` is an N-dimensional + value drawn from the distribution. + + Examples + -------- + Throw a dice 20 times: + + >>> np.random.multinomial(20, [1/6.]*6, size=1) + array([[4, 1, 7, 5, 2, 1]]) # random + + It landed 4 times on 1, once on 2, etc. + + Now, throw the dice 20 times, and 20 times again: + + >>> np.random.multinomial(20, [1/6.]*6, size=2) + array([[3, 4, 3, 3, 4, 3], # random + [2, 4, 3, 4, 0, 7]]) + + For the first run, we threw 3 times 1, 4 times 2, etc. For the second, + we threw 2 times 1, 4 times 2, etc. + + A loaded die is more likely to land on number 6: + + >>> np.random.multinomial(100, [1/7.]*5 + [2/7.]) + array([11, 16, 14, 17, 16, 26]) # random + + The probability inputs should be normalized. As an implementation + detail, the value of the last entry is ignored and assumed to take + up any leftover probability mass, but this should not be relied on. + A biased coin which has twice as much weight on one side as on the + other should be sampled like so: + + >>> np.random.multinomial(100, [1.0 / 3, 2.0 / 3]) # RIGHT + array([38, 62]) # random + + not like: + + >>> np.random.multinomial(100, [1.0, 2.0]) # WRONG + array([100, 0]) + + """ + cdef np.npy_intp d, i, j, dn, sz + cdef np.ndarray parr "arrayObject_parr", mnarr "arrayObject_mnarr" + cdef double *pix + cdef int64_t *mnix + cdef double Sum + + d = len(pvals) + parr = np.PyArray_FROM_OTF(pvals, np.NPY_DOUBLE, np.NPY_ALIGNED) + pix = np.PyArray_DATA(parr) + + if kahan_sum(pix, d-1) > (1.0 + 1e-12): + raise ValueError("sum(pvals[:-1]) > 1.0") + + if size is None: + shape = (d,) + else: + try: + shape = (operator.index(size), d) + except: + shape = tuple(size) + (d,) + + multin = np.zeros(shape, dtype=np.int64) + mnarr = multin + mnix = np.PyArray_DATA(mnarr) + sz = np.PyArray_SIZE(mnarr) + + with self.lock, nogil: + i = 0 + while i < sz: + Sum = 1.0 + dn = n + for j in range(d-1): + mnix[i+j] = random_binomial(self._brng, pix[j]/Sum, dn, + self._binomial) + dn = dn - mnix[i+j] + if dn <= 0: + break + Sum = Sum - pix[j] + if dn > 0: + mnix[i+d-1] = dn + + i = i + d + + return multin + + def dirichlet(self, object alpha, size=None): + """ + dirichlet(alpha, size=None) + + Draw samples from the Dirichlet distribution. + + Draw `size` samples of dimension k from a Dirichlet distribution. A + Dirichlet-distributed random variable can be seen as a multivariate + generalization of a Beta distribution. The Dirichlet distribution + is a conjugate prior of a multinomial distribution in Bayesian + inference. + + Parameters + ---------- + alpha : array + Parameter of the distribution (k dimension for sample of + dimension k). + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + + Returns + ------- + samples : ndarray, + The drawn samples, of shape (size, alpha.ndim). + + Raises + ------- + ValueError + If any value in alpha is less than or equal to zero + + Notes + ----- + + The Dirichlet distribution is a distribution over vectors + :math:`x` that fulfil the conditions :math:`x_i>0` and + :math:`\\sum_{i=1}^k x_i = 1`. + + The probability density function :math:`p` of a + Dirichlet-distributed random vector :math:`X` is + proportional to + + .. math:: p(x) \\propto \\prod_{i=1}^{k}{x^{\\alpha_i-1}_i}, + + where :math:`\\alpha` is a vector containing the positive + concentration parameters. + + The method uses the following property for computation: let :math:`Y` + be a random vector which has components that follow a standard gamma + distribution, then :math:`X = \\frac{1}{\\sum_{i=1}^k{Y_i}} Y` + is Dirichlet-distributed + + References + ---------- + .. [1] David McKay, "Information Theory, Inference and Learning + Algorithms," chapter 23, + http://www.inference.org.uk/mackay/itila/ + .. [2] Wikipedia, "Dirichlet distribution", + https://en.wikipedia.org/wiki/Dirichlet_distribution + + Examples + -------- + Taking an example cited in Wikipedia, this distribution can be used if + one wanted to cut strings (each of initial length 1.0) into K pieces + with different lengths, where each piece had, on average, a designated + average length, but allowing some variation in the relative sizes of + the pieces. + + >>> s = np.random.dirichlet((10, 5, 3), 20).transpose() + + >>> import matplotlib.pyplot as plt + >>> plt.barh(range(20), s[0]) + >>> plt.barh(range(20), s[1], left=s[0], color='g') + >>> plt.barh(range(20), s[2], left=s[0]+s[1], color='r') + >>> plt.title("Lengths of Strings") + + """ + + # ================= + # Pure python algo + # ================= + # alpha = N.atleast_1d(alpha) + # k = alpha.size + + # if n == 1: + # val = N.zeros(k) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val) + # else: + # val = N.zeros((k, n)) + # for i in range(k): + # val[i] = sgamma(alpha[i], n) + # val /= N.sum(val, axis = 0) + # val = val.T + # return val + + cdef np.npy_intp k, totsize, i, j + cdef np.ndarray alpha_arr, val_arr + cdef double *alpha_data + cdef double *val_data + cdef double acc, invacc + + k = len(alpha) + alpha_arr = np.PyArray_FROM_OTF(alpha, np.NPY_DOUBLE, np.NPY_ALIGNED) + if np.any(np.less_equal(alpha_arr, 0)): + raise ValueError('alpha <= 0') + alpha_data = np.PyArray_DATA(alpha_arr) + + if size is None: + shape = (k,) + else: + try: + shape = (operator.index(size), k) + except: + shape = tuple(size) + (k,) + + diric = np.zeros(shape, np.float64) + val_arr = diric + val_data = np.PyArray_DATA(val_arr) + + i = 0 + totsize = np.PyArray_SIZE(val_arr) + with self.lock, nogil: + while i < totsize: + acc = 0.0 + for j in range(k): + val_data[i+j] = legacy_standard_gamma(self._aug_state, + alpha_data[j]) + acc = acc + val_data[i + j] + invacc = 1/acc + for j in range(k): + val_data[i + j] = val_data[i + j] * invacc + i = i + k + + return diric + + # Shuffling and permutations: + def shuffle(self, object x): + """ + shuffle(x) + + Modify a sequence in-place by shuffling its contents. + + This function only shuffles the array along the first axis of a + multi-dimensional array. The order of sub-arrays is changed but + their contents remains the same. + + Parameters + ---------- + x : array_like + The array or list to be shuffled. + + Returns + ------- + None + + Examples + -------- + >>> arr = np.arange(10) + >>> np.random.shuffle(arr) + >>> arr + [1 7 5 2 9 4 3 6 0 8] # random + + Multi-dimensional arrays are only shuffled along the first axis: + + >>> arr = np.arange(9).reshape((3, 3)) + >>> np.random.shuffle(arr) + >>> arr + array([[3, 4, 5], # random + [6, 7, 8], + [0, 1, 2]]) + + """ + cdef: + np.npy_intp i, j, n = len(x), stride, itemsize + char* x_ptr + char* buf_ptr + + if type(x) is np.ndarray and x.ndim == 1 and x.size: + # Fast, statically typed path: shuffle the underlying buffer. + # Only for non-empty, 1d objects of class ndarray (subclasses such + # as MaskedArrays may not support this approach). + x_ptr = x.ctypes.data + stride = x.strides[0] + itemsize = x.dtype.itemsize + # As the array x could contain python objects we use a buffer + # of bytes for the swaps to avoid leaving one of the objects + # within the buffer and erroneously decrementing it's refcount + # when the function exits. + buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit + buf_ptr = buf.ctypes.data + with self.lock: + # We trick gcc into providing a specialized implementation for + # the most common case, yielding a ~33% performance improvement. + # Note that apparently, only one branch can ever be specialized. + if itemsize == sizeof(np.npy_intp): + self._shuffle_raw(n, sizeof(np.npy_intp), stride, x_ptr, buf_ptr) + else: + self._shuffle_raw(n, itemsize, stride, x_ptr, buf_ptr) + elif isinstance(x, np.ndarray) and x.ndim and x.size: + buf = np.empty_like(x[0, ...]) + with self.lock: + for i in reversed(range(1, n)): + j = random_interval(self._brng, i) + if i == j: + continue # i == j is not needed and memcpy is undefined. + buf[...] = x[j] + x[j] = x[i] + x[i] = buf + else: + # Untyped path. + with self.lock: + for i in reversed(range(1, n)): + j = random_interval(self._brng, i) + x[i], x[j] = x[j], x[i] + + cdef inline _shuffle_raw(self, np.npy_intp n, np.npy_intp itemsize, + np.npy_intp stride, char* data, char* buf): + cdef np.npy_intp i, j + for i in reversed(range(1, n)): + j = random_interval(self._brng, i) + string.memcpy(buf, data + j * stride, itemsize) + string.memcpy(data + j * stride, data + i * stride, itemsize) + string.memcpy(data + i * stride, buf, itemsize) + + def permutation(self, object x): + """ + permutation(x) + + Randomly permute a sequence, or return a permuted range. + + If `x` is a multi-dimensional array, it is only shuffled along its + first index. + + Parameters + ---------- + x : int or array_like + If `x` is an integer, randomly permute ``np.arange(x)``. + If `x` is an array, make a copy and shuffle the elements + randomly. + + Returns + ------- + out : ndarray + Permuted sequence or array range. + + Examples + -------- + >>> np.random.permutation(10) + array([1, 7, 4, 3, 0, 9, 2, 5, 8, 6]) # random + + >>> np.random.permutation([1, 4, 9, 12, 15]) + array([15, 1, 9, 4, 12]) # random + + >>> arr = np.arange(9).reshape((3, 3)) + >>> np.random.permutation(arr) + array([[6, 7, 8], # random + [0, 1, 2], + [3, 4, 5]]) + + """ + if isinstance(x, (int, long, np.integer)): + arr = np.arange(x) + self.shuffle(arr) + return arr + + arr = np.asarray(x) + + # shuffle has fast-path for 1-d + if arr.ndim == 1: + # Return a copy if same memory + if np.may_share_memory(arr, x): + arr = np.array(arr) + self.shuffle(arr) + return arr + + # Shuffle index array, dtype to ensure fast path + idx = np.arange(arr.shape[0], dtype=np.intp) + self.shuffle(idx) + return arr[idx] + +_mtrand = RandomState() + +beta = _mtrand.beta +binomial = _mtrand.binomial +bytes = _mtrand.bytes +chisquare = _mtrand.chisquare +choice = _mtrand.choice +dirichlet = _mtrand.dirichlet +exponential = _mtrand.exponential +f = _mtrand.f +gamma = _mtrand.gamma +get_state = _mtrand.get_state +geometric = _mtrand.geometric +gumbel = _mtrand.gumbel +hypergeometric = _mtrand.hypergeometric +laplace = _mtrand.laplace +logistic = _mtrand.logistic +lognormal = _mtrand.lognormal +logseries = _mtrand.logseries +multinomial = _mtrand.multinomial +multivariate_normal = _mtrand.multivariate_normal +negative_binomial = _mtrand.negative_binomial +noncentral_chisquare = _mtrand.noncentral_chisquare +noncentral_f = _mtrand.noncentral_f +normal = _mtrand.normal +pareto = _mtrand.pareto +permutation = _mtrand.permutation +poisson = _mtrand.poisson +power = _mtrand.power +rand = _mtrand.rand +randint = _mtrand.randint +randn = _mtrand.randn +random = _mtrand.random_sample +random_integers = _mtrand.random_integers +random_sample = _mtrand.random_sample +ranf = _mtrand.random_sample +rayleigh = _mtrand.rayleigh +sample = _mtrand.random_sample +seed = _mtrand.seed +set_state = _mtrand.set_state +shuffle = _mtrand.shuffle +standard_cauchy = _mtrand.standard_cauchy +standard_exponential = _mtrand.standard_exponential +standard_gamma = _mtrand.standard_gamma +standard_normal = _mtrand.standard_normal +standard_t = _mtrand.standard_t +triangular = _mtrand.triangular +uniform = _mtrand.uniform +vonmises = _mtrand.vonmises +wald = _mtrand.wald +weibull = _mtrand.weibull +zipf = _mtrand.zipf diff --git a/numpy/random/randomgen/pcg32.pyx b/numpy/random/randomgen/pcg32.pyx index ef2668d98384..b163f3f950cd 100644 --- a/numpy/random/randomgen/pcg32.pyx +++ b/numpy/random/randomgen/pcg32.pyx @@ -1,8 +1,11 @@ -from __future__ import absolute_import - from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock + import numpy as np cimport numpy as np @@ -23,14 +26,14 @@ cdef extern from "src/pcg32/pcg32.h": ctypedef pcg_state_setseq_64 pcg32_random_t struct s_pcg32_state: - pcg32_random_t *pcg_state + pcg32_random_t *pcg_state ctypedef s_pcg32_state pcg32_state uint64_t pcg32_next64(pcg32_state *state) nogil uint32_t pcg32_next32(pcg32_state *state) nogil double pcg32_next_double(pcg32_state *state) nogil - void pcg32_jump(pcg32_state *state) + void pcg32_jump(pcg32_state *state) void pcg32_advance_state(pcg32_state *state, uint64_t step) void pcg32_set_seed(pcg32_state *state, uint64_t seed, uint64_t inc) @@ -122,12 +125,14 @@ cdef class PCG32: cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None, inc=0): self.rng_state = malloc(sizeof(pcg32_state)) self.rng_state.pcg_state = malloc(sizeof(pcg32_random_t)) self._brng = malloc(sizeof(brng_t)) self.seed(seed, inc) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &pcg32_uint64 @@ -159,42 +164,39 @@ cdef class PCG32: free(self.rng_state) free(self._brng) - def __random_integer(self, bits=64): + def random_raw(self, size=None, output=True): """ - 64-bit Random Integers from the PRNG + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG Parameters ---------- - bits : {32, 64} - Number of random bits to return + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. Returns ------- - rv : int - Next random value + out : uint or ndarray + Drawn samples. Notes ----- - Testing only + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. """ - if bits == 64: - return self._brng.next_uint64(self._brng.state) - elif bits == 32: - return self._brng.next_uint32(self._brng.state) - else: - raise ValueError('bits must be 32 or 64') + return random_raw(self._brng, self.lock, size, output) def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') - + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None, inc=0): """ @@ -218,7 +220,7 @@ cdef class PCG32: ValueError If seed values are out of range for the PRNG. """ - ub = 2 ** 64 + ub = 2 ** 64 if seed is None: try: seed = random_entropy(2) @@ -257,7 +259,7 @@ cdef class PCG32: """ return {'brng': self.__class__.__name__, 'state': {'state': self.rng_state.pcg_state.state, - 'inc':self.rng_state.pcg_state.inc}} + 'inc': self.rng_state.pcg_state.inc}} @state.setter def state(self, value): @@ -267,7 +269,7 @@ cdef class PCG32: if brng != self.__class__.__name__: raise ValueError('state must be for a {0} ' 'PRNG'.format(self.__class__.__name__)) - self.rng_state.pcg_state.state = value['state']['state'] + self.rng_state.pcg_state.state = value['state']['state'] self.rng_state.pcg_state.inc = value['state']['inc'] def advance(self, delta): @@ -327,12 +329,12 @@ cdef class PCG32: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -341,25 +343,10 @@ cdef class PCG32: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&pcg32_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&pcg32_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&pcg32_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -380,19 +367,8 @@ cdef class PCG32: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/pcg64.pyx b/numpy/random/randomgen/pcg64.pyx index 66bf3a47148b..d69535111b27 100644 --- a/numpy/random/randomgen/pcg64.pyx +++ b/numpy/random/randomgen/pcg64.pyx @@ -1,8 +1,11 @@ -from __future__ import absolute_import - from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock + import numpy as np cimport numpy as np @@ -35,15 +38,15 @@ cdef extern from "src/pcg64/pcg64.h": ctypedef pcg_state_setseq_128 pcg64_random_t struct s_pcg64_state: - pcg64_random_t *pcg_state - int has_uint32 - uint32_t uinteger + pcg64_random_t *pcg_state + int has_uint32 + uint32_t uinteger ctypedef s_pcg64_state pcg64_state uint64_t pcg64_next64(pcg64_state *state) nogil uint32_t pcg64_next32(pcg64_state *state) nogil - void pcg64_jump(pcg64_state *state) + void pcg64_jump(pcg64_state *state) void pcg64_advance(pcg64_state *state, uint64_t *step) void pcg64_set_seed(pcg64_state *state, uint64_t *seed, uint64_t *inc) @@ -133,12 +136,14 @@ cdef class PCG64: cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None, inc=0): self.rng_state = malloc(sizeof(pcg64_state)) self.rng_state.pcg_state = malloc(sizeof(pcg64_random_t)) self._brng = malloc(sizeof(brng_t)) self.seed(seed, inc) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &pcg64_uint64 @@ -174,42 +179,39 @@ cdef class PCG64: self.rng_state.has_uint32 = 0 self.rng_state.uinteger = 0 - def __random_integer(self, bits=64): + def random_raw(self, size=None, output=True): """ - 64-bit Random Integers from the RNG + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG Parameters ---------- - bits : {32, 64} - Number of random bits to return + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. Returns ------- - rv : int - Next random value + out : uint or ndarray + Drawn samples. Notes ----- - Testing only + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. """ - if bits == 64: - return self._brng.next_uint64(self._brng.state) - elif bits == 32: - return self._brng.next_uint32(self._brng.state) - else: - raise ValueError('bits must be 32 or 64') + return random_raw(self._brng, self.lock, size, output) def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') - + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None, inc=0): """ @@ -235,7 +237,7 @@ cdef class PCG64: """ cdef np.ndarray _seed, _inc - ub = 2 ** 128 + ub = 2 ** 128 if seed is None: try: _seed = random_entropy(4) @@ -288,7 +290,7 @@ cdef class PCG64: # inc = self.rng_state.pcg_state.inc return {'brng': self.__class__.__name__, - 'state': {'state': state, 'inc':inc}, + 'state': {'state': state, 'inc': inc}, 'has_uint32': self.rng_state.has_uint32, 'uinteger': self.rng_state.uinteger} @@ -381,12 +383,12 @@ cdef class PCG64: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -395,25 +397,10 @@ cdef class PCG64: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&pcg64_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&pcg64_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&pcg64_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -434,19 +421,8 @@ cdef class PCG64: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/philox.pyx b/numpy/random/randomgen/philox.pyx index a40cd3e251c6..05f3b17b4c51 100644 --- a/numpy/random/randomgen/philox.pyx +++ b/numpy/random/randomgen/philox.pyx @@ -1,8 +1,11 @@ -from __future__ import absolute_import - from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock + import numpy as np from .common import interface @@ -25,14 +28,14 @@ cdef extern from 'src/philox/philox.h': ctypedef s_r123array4x64 r123array4x64 ctypedef s_r123array2x64 r123array2x64 - ctypedef r123array4x64 philox4x64_ctr_t; - ctypedef r123array2x64 philox4x64_key_t; + ctypedef r123array4x64 philox4x64_ctr_t + ctypedef r123array2x64 philox4x64_key_t struct s_philox_state: - philox4x64_ctr_t *ctr; - philox4x64_key_t *key; - int buffer_pos; - uint64_t buffer[PHILOX_BUFFER_SIZE]; + philox4x64_ctr_t *ctr + philox4x64_key_t *key + int buffer_pos + uint64_t buffer[PHILOX_BUFFER_SIZE] int has_uint32 uint32_t uinteger @@ -158,12 +161,13 @@ cdef class Philox: the International Conference for High Performance Computing, Networking, Storage and Analysis (SC11), New York, NY: ACM, 2011. """ - cdef philox_state *rng_state + cdef philox_state *rng_state cdef brng_t *_brng cdef public object capsule cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None, counter=None, key=None): self.rng_state = malloc(sizeof(philox_state)) @@ -173,6 +177,7 @@ cdef class Philox: sizeof(philox4x64_key_t)) self._brng = malloc(sizeof(brng_t)) self.seed(seed, counter, key) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &philox_uint64 @@ -213,16 +218,39 @@ cdef class Philox: for i in range(PHILOX_BUFFER_SIZE): self.rng_state.buffer[i] = 0 + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(self._brng, self.lock, size, output) + def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None, counter=None, key=None): """ @@ -257,7 +285,7 @@ cdef class Philox: """ if seed is not None and key is not None: raise ValueError('seed and key cannot be both used') - ub = 2 ** 64 + ub = 2 ** 64 if key is None: if seed is None: try: @@ -393,14 +421,15 @@ cdef class Philox: self._reset_state_variables() return self + @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -409,25 +438,10 @@ cdef class Philox: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&philox_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&philox_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&philox_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -448,19 +462,8 @@ cdef class Philox: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/setup.py b/numpy/random/randomgen/setup.py index ccea6e78c370..94680e73e617 100644 --- a/numpy/random/randomgen/setup.py +++ b/numpy/random/randomgen/setup.py @@ -122,17 +122,6 @@ def generate_libraries(ext, build_dir): ], define_macros=defs + DSFMT_DEFS, ) - config.add_extension('legacy._legacy', - sources=['legacy/_legacy.c', - 'src/legacy/distributions-boxmuller.c', - 'src/distributions/distributions.c' ], - include_dirs=['.', 'legacy'], - libraries=EXTRA_LIBRARIES, - extra_compile_args=EXTRA_COMPILE_ARGS, - extra_link_args=EXTRA_LINK_ARGS, - depends=[join('legacy', '_legacy.pyx')], - define_macros=defs + DSFMT_DEFS, - ) for gen in ['mt19937']: # gen.pyx, src/gen/gen.c, src/gen/gen-jump.c config.add_extension(gen, @@ -186,6 +175,17 @@ def generate_libraries(ext, build_dir): depends=['%s.pyx' % gen], define_macros=defs, ) + config.add_extension('mtrand', + sources=['mtrand.c', + 'src/legacy/distributions-boxmuller.c', + 'src/distributions/distributions.c' ], + include_dirs=['.', 'legacy'], + libraries=EXTRA_LIBRARIES, + extra_compile_args=EXTRA_COMPILE_ARGS, + extra_link_args=EXTRA_LINK_ARGS, + depends=['mtrand.pyx'], + define_macros=defs + DSFMT_DEFS, + ) config.add_subpackage('legacy') return config if __name__ == '__main__': diff --git a/numpy/random/randomgen/src/distributions/distributions.c b/numpy/random/randomgen/src/distributions/distributions.c index 10c195ae4d23..20cdbe8632bf 100644 --- a/numpy/random/randomgen/src/distributions/distributions.c +++ b/numpy/random/randomgen/src/distributions/distributions.c @@ -879,6 +879,9 @@ int64_t random_binomial(brng_t *brng_state, double p, int64_t n, binomial_t *binomial) { double q; + if ((n == 0LL) || (p == 0.0f)) + return 0; + if (p <= 0.5) { if (p * n <= 30.0) { return random_binomial_inversion(brng_state, n, p, binomial); diff --git a/numpy/random/randomgen/src/pcg32/pcg32.h b/numpy/random/randomgen/src/pcg32/pcg32.h index 15410bd821c7..557113d8f6af 100644 --- a/numpy/random/randomgen/src/pcg32/pcg32.h +++ b/numpy/random/randomgen/src/pcg32/pcg32.h @@ -1,3 +1,5 @@ +#ifndef _RANDOMDGEN__PCG32_H_ +#define _RANDOMDGEN__PCG32_H_ #ifdef _WIN32 #ifndef _INTTYPES @@ -83,3 +85,5 @@ static inline double pcg32_next_double(pcg32_state *state) { void pcg32_advance_state(pcg32_state *state, uint64_t step); void pcg32_set_seed(pcg32_state *state, uint64_t seed, uint64_t inc); + +#endif diff --git a/numpy/random/randomgen/src/pcg64/pcg64.h b/numpy/random/randomgen/src/pcg64/pcg64.h index 854930176f09..156c73a36988 100644 --- a/numpy/random/randomgen/src/pcg64/pcg64.h +++ b/numpy/random/randomgen/src/pcg64/pcg64.h @@ -212,8 +212,6 @@ typedef pcg_state_setseq_128 pcg64_random_t; } #endif -#endif /* PCG64_H_INCLUDED */ - typedef struct s_pcg64_state { pcg64_random_t *pcg_state; int has_uint32; @@ -239,3 +237,5 @@ static inline uint32_t pcg64_next32(pcg64_state *state) { void pcg64_advance(pcg64_state *state, uint64_t *step); void pcg64_set_seed(pcg64_state *state, uint64_t *seed, uint64_t *inc); + +#endif /* PCG64_H_INCLUDED */ diff --git a/numpy/random/randomgen/tests/test_against_numpy.py b/numpy/random/randomgen/tests/test_against_numpy.py index c31113b511e7..14f21aad3a2d 100644 --- a/numpy/random/randomgen/tests/test_against_numpy.py +++ b/numpy/random/randomgen/tests/test_against_numpy.py @@ -6,7 +6,7 @@ import pytest from numpy.random.randomgen import RandomGenerator, MT19937, generator -from numpy.random.randomgen.legacy import LegacyGenerator +from numpy.random.randomgen.mtrand import RandomState def compare_0_input(f1, f2): @@ -96,7 +96,7 @@ def setup_class(cls): cls.brng = MT19937 cls.seed = [2 ** 21 + 2 ** 16 + 2 ** 5 + 1] cls.rg = RandomGenerator(cls.brng(*cls.seed)) - cls.lg = LegacyGenerator(cls.brng(*cls.seed)) + cls.rs = RandomState(cls.brng(*cls.seed)) cls.nprs = cls.np.RandomState(*cls.seed) cls.initial_state = cls.rg.state cls._set_common_state() @@ -114,7 +114,7 @@ def _set_common_state(cls): @classmethod def _set_common_state_legacy(cls): - state = cls.lg.state + state = cls.rs.get_state(legacy=False) st = [[]] * 5 st[0] = 'MT19937' st[1] = state['state']['key'] @@ -131,7 +131,7 @@ def _is_state_common(self): def _is_state_common_legacy(self): state = self.nprs.get_state() - state2 = self.lg.state + state2 = self.rs.get_state(legacy=False) assert (state[1] == state2['state']['key']).all() assert (state[2] == state2['state']['pos']) assert (state[3] == state2['has_gauss']) @@ -166,21 +166,21 @@ def test_standard_normal(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_0_input(self.nprs.standard_normal, - self.lg.standard_normal) + self.rs.standard_normal) self._is_state_common_legacy() def test_standard_cauchy(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_0_input(self.nprs.standard_cauchy, - self.lg.standard_cauchy) + self.rs.standard_cauchy) self._is_state_common_legacy() def test_standard_exponential(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_0_input(self.nprs.standard_exponential, - self.lg.standard_exponential) + self.rs.standard_exponential) self._is_state_common_legacy() def test_tomaxint(self): @@ -404,7 +404,6 @@ def test_dir(self): nprs_d = set(dir(self.nprs)) rs_d = dir(self.rg) excluded = {'get_state', 'set_state'} - nprs_d.discard('_LegacyGenerator__legacy') nprs_d.difference_update(excluded) assert (len(nprs_d.difference(rs_d)) == 0) @@ -416,7 +415,7 @@ def test_dir(self): 'division', 'get_state', 'set_state', 'seed', 'ranf', 'random', 'sample', 'absolute_import', 'print_function', 'RandomState', 'randomgen', - 'tests'] + 'tests', 'Lock'] mod += known_exlcuded diff = set(npmod).difference(mod) assert_equal(len(diff), 0) @@ -426,112 +425,112 @@ def test_chisquare(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_1_input(self.nprs.chisquare, - self.lg.chisquare) + self.rs.chisquare) self._is_state_common_legacy() def test_standard_gamma(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_1_input(self.nprs.standard_gamma, - self.lg.standard_gamma) + self.rs.standard_gamma) self._is_state_common_legacy() def test_standard_t(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_1_input(self.nprs.standard_t, - self.lg.standard_t) + self.rs.standard_t) self._is_state_common_legacy() def test_pareto(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_1_input(self.nprs.pareto, - self.lg.pareto) + self.rs.pareto) self._is_state_common_legacy() def test_power(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_1_input(self.nprs.power, - self.lg.power) + self.rs.power) self._is_state_common_legacy() def test_weibull(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_1_input(self.nprs.weibull, - self.lg.weibull) + self.rs.weibull) self._is_state_common_legacy() def test_beta(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.beta, - self.lg.beta) + self.rs.beta) self._is_state_common_legacy() def test_exponential(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_1_input(self.nprs.exponential, - self.lg.exponential) + self.rs.exponential) self._is_state_common_legacy() def test_f(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.f, - self.lg.f) + self.rs.f) self._is_state_common_legacy() def test_gamma(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.gamma, - self.lg.gamma) + self.rs.gamma) self._is_state_common_legacy() def test_lognormal(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.lognormal, - self.lg.lognormal) + self.rs.lognormal) self._is_state_common_legacy() def test_noncentral_chisquare(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.noncentral_chisquare, - self.lg.noncentral_chisquare) + self.rs.noncentral_chisquare) self._is_state_common_legacy() def test_normal(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.normal, - self.lg.normal) + self.rs.normal) self._is_state_common_legacy() def test_wald(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.wald, - self.lg.wald) + self.rs.wald) self._is_state_common_legacy() def test_negative_binomial(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_2_input(self.nprs.negative_binomial, - self.lg.negative_binomial, + self.rs.negative_binomial, is_np=True) self._is_state_common_legacy() def test_randn(self): self._set_common_state_legacy() self._is_state_common_legacy() - f = self.lg.randn + f = self.rs.randn g = self.nprs.randn assert_allclose(f(10), g(10)) assert_allclose(f(3, 4, 5), g(3, 4, 5)) @@ -540,7 +539,7 @@ def test_randn(self): def test_dirichlet(self): self._set_common_state_legacy() self._is_state_common_legacy() - f = self.lg.dirichlet + f = self.rs.dirichlet g = self.nprs.dirichlet a = [3, 4, 5, 6, 7, 10] assert_allclose(f(a), g(a)) @@ -552,7 +551,7 @@ def test_noncentral_f(self): self._set_common_state_legacy() self._is_state_common_legacy() compare_3_input(self.nprs.noncentral_f, - self.lg.noncentral_f) + self.rs.noncentral_f) self._is_state_common_legacy() def test_multivariate_normal(self): @@ -560,7 +559,7 @@ def test_multivariate_normal(self): self._is_state_common_legacy() mu = [1, 2, 3] cov = [[1, .2, .3], [.2, 4, 1], [.3, 1, 10]] - f = self.lg.multivariate_normal + f = self.rs.multivariate_normal g = self.nprs.multivariate_normal assert_allclose(f(mu, cov), g(mu, cov)) assert_allclose(f(np.array(mu), cov), g(np.array(mu), cov)) diff --git a/numpy/random/randomgen/tests/test_direct.py b/numpy/random/randomgen/tests/test_direct.py index cbe2717a7233..5bfc872cb188 100644 --- a/numpy/random/randomgen/tests/test_direct.py +++ b/numpy/random/randomgen/tests/test_direct.py @@ -10,6 +10,21 @@ from ...randomgen import RandomGenerator, MT19937, DSFMT, ThreeFry32, ThreeFry, \ PCG32, PCG64, Philox, Xoroshiro128, Xorshift1024, Xoshiro256StarStar, \ Xoshiro512StarStar +from ...randomgen.common import interface + +try: + import cffi # noqa: F401 + + MISSING_CFFI = False +except ImportError: + MISSING_CFFI = True + +try: + import ctypes # noqa: F401 + + MISSING_CTYPES = False +except ImportError: + MISSING_CTYPES = False if (sys.version_info > (3, 0)): long = int @@ -17,6 +32,16 @@ pwd = os.path.dirname(os.path.abspath(__file__)) +def assert_state_equal(actual, target): + for key in actual: + if isinstance(actual[key], dict): + assert_state_equal(actual[key], target[key]) + elif isinstance(actual[key], np.ndarray): + assert_array_equal(actual[key], target[key]) + else: + assert actual[key] == target[key] + + def uniform32_from_uint64(x): x = np.uint64(x) upper = np.array(x >> np.uint64(32), dtype=np.uint32) @@ -126,6 +151,8 @@ def setup_class(cls): cls.bits = 64 cls.dtype = np.uint64 cls.seed_error_type = TypeError + cls.invalid_seed_types = [] + cls.invalid_seed_values = [] @classmethod def _read_csv(cls, filename): @@ -139,14 +166,25 @@ def _read_csv(cls, filename): return {'seed': seed, 'data': np.array(data, dtype=cls.dtype)} def test_raw(self): - rs = RandomGenerator(self.brng(*self.data1['seed'])) - uints = rs.random_raw(1000) + brng = self.brng(*self.data1['seed']) + uints = brng.random_raw(1000) assert_equal(uints, self.data1['data']) - rs = RandomGenerator(self.brng(*self.data2['seed'])) - uints = rs.random_raw(1000) + brng = self.brng(*self.data1['seed']) + uints = brng.random_raw() + assert_equal(uints, self.data1['data'][0]) + + brng = self.brng(*self.data2['seed']) + uints = brng.random_raw(1000) assert_equal(uints, self.data2['data']) + def test_random_raw(self): + brng = self.brng(*self.data1['seed']) + uints = brng.random_raw(output=False) + assert uints is None + uints = brng.random_raw(1000, output=False) + assert uints is None + @pytest.mark.skip(reason='Polar transform no longer supported') def test_gauss_inv(self): n = 25 @@ -214,6 +252,87 @@ def test_seed_out_of_range_array(self): assert_raises(ValueError, rs.seed, [2 ** (2 * self.bits + 1)]) assert_raises(ValueError, rs.seed, [-1]) + def test_repr(self): + rs = RandomGenerator(self.brng(*self.data1['seed'])) + assert 'RandomGenerator' in rs.__repr__() + assert str(hex(id(rs)))[2:].upper() in rs.__repr__() + + def test_str(self): + rs = RandomGenerator(self.brng(*self.data1['seed'])) + assert 'RandomGenerator' in str(rs) + assert str(self.brng.__name__) in str(rs) + assert str(hex(id(rs)))[2:].upper() not in str(rs) + + def test_generator(self): + brng = self.brng(*self.data1['seed']) + assert isinstance(brng.generator, RandomGenerator) + + def test_pickle(self): + import pickle + + brng = self.brng(*self.data1['seed']) + state = brng.state + brng_pkl = pickle.dumps(brng) + reloaded = pickle.loads(brng_pkl) + reloaded_state = reloaded.state + assert_array_equal(brng.generator.standard_normal(1000), + reloaded.generator.standard_normal(1000)) + assert brng is not reloaded + assert_state_equal(reloaded_state, state) + + def test_invalid_state_type(self): + brng = self.brng(*self.data1['seed']) + with pytest.raises(TypeError): + brng.state = {'1'} + + def test_invalid_state_value(self): + brng = self.brng(*self.data1['seed']) + state = brng.state + state['brng'] = 'otherBRNG' + with pytest.raises(ValueError): + brng.state = state + + def test_invalid_seed_type(self): + brng = self.brng(*self.data1['seed']) + for st in self.invalid_seed_types: + with pytest.raises(TypeError): + brng.seed(*st) + + def test_invalid_seed_values(self): + brng = self.brng(*self.data1['seed']) + for st in self.invalid_seed_values: + with pytest.raises(ValueError): + brng.seed(*st) + + def test_benchmark(self): + brng = self.brng(*self.data1['seed']) + brng._benchmark(1) + brng._benchmark(1, 'double') + with pytest.raises(ValueError): + brng._benchmark(1, 'int32') + + @pytest.mark.skipif(MISSING_CFFI, reason='cffi not available') + def test_cffi(self): + brng = self.brng(*self.data1['seed']) + cffi_interface = brng.cffi + assert isinstance(cffi_interface, interface) + other_cffi_interface = brng.cffi + assert other_cffi_interface is cffi_interface + + @pytest.mark.skipif(MISSING_CTYPES, reason='ctypes not available') + def test_ctypes(self): + brng = self.brng(*self.data1['seed']) + ctypes_interface = brng.ctypes + assert isinstance(ctypes_interface, interface) + other_ctypes_interface = brng.ctypes + assert other_ctypes_interface is ctypes_interface + + def test_getstate(self): + brng = self.brng(*self.data1['seed']) + state = brng.state + alt_state = brng.__getstate__() + assert_state_equal(state, alt_state) + class TestXoroshiro128(Base): @classmethod @@ -226,6 +345,8 @@ def setup_class(cls): cls.data2 = cls._read_csv( join(pwd, './data/xoroshiro128-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [('apple',), (2 + 3j,), (3.1,)] + cls.invalid_seed_values = [(-2,), (np.empty((2, 2), dtype=np.int64),)] class TestXoshiro256StarStar(Base): @@ -239,6 +360,8 @@ def setup_class(cls): cls.data2 = cls._read_csv( join(pwd, './data/xoshiro256starstar-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [('apple',), (2 + 3j,), (3.1,)] + cls.invalid_seed_values = [(-2,), (np.empty((2, 2), dtype=np.int64),)] class TestXoshiro512StarStar(Base): @@ -252,6 +375,8 @@ def setup_class(cls): cls.data2 = cls._read_csv( join(pwd, './data/xoshiro512starstar-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [('apple',), (2 + 3j,), (3.1,)] + cls.invalid_seed_values = [(-2,), (np.empty((2, 2), dtype=np.int64),)] class TestXorshift1024(Base): @@ -265,6 +390,8 @@ def setup_class(cls): cls.data2 = cls._read_csv( join(pwd, './data/xorshift1024-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [('apple',), (2 + 3j,), (3.1,)] + cls.invalid_seed_values = [(-2,), (np.empty((2, 2), dtype=np.int64),)] class TestThreeFry(Base): @@ -278,6 +405,16 @@ def setup_class(cls): cls.data2 = cls._read_csv( join(pwd, './data/threefry-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [] + cls.invalid_seed_values = [(1, None, 1), (-1,), (2 ** 257 + 1,), + (None, None, 2 ** 257 + 1)] + + def test_set_key(self): + brng = self.brng(*self.data1['seed']) + state = brng.state + keyed = self.brng(counter=state['state']['counter'], + key=state['state']['key']) + assert_state_equal(brng.state, keyed.state) class TestPCG64(Base): @@ -289,6 +426,10 @@ def setup_class(cls): cls.data1 = cls._read_csv(join(pwd, './data/pcg64-testset-1.csv')) cls.data2 = cls._read_csv(join(pwd, './data/pcg64-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [(np.array([1, 2]),), (3.2,), + (None, np.zeros(1))] + cls.invalid_seed_values = [(-1,), (2 ** 129 + 1,), (None, -1), + (None, 2 ** 129 + 1)] def test_seed_float_array(self): rs = RandomGenerator(self.brng(*self.data1['seed'])) @@ -317,6 +458,16 @@ def setup_class(cls): cls.data2 = cls._read_csv( join(pwd, './data/philox-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [] + cls.invalid_seed_values = [(1, None, 1), (-1,), (2 ** 257 + 1,), + (None, None, 2 ** 257 + 1)] + + def test_set_key(self): + brng = self.brng(*self.data1['seed']) + state = brng.state + keyed = self.brng(counter=state['state']['counter'], + key=state['state']['key']) + assert_state_equal(brng.state, keyed.state) class TestMT19937(Base): @@ -328,6 +479,8 @@ def setup_class(cls): cls.data1 = cls._read_csv(join(pwd, './data/mt19937-testset-1.csv')) cls.data2 = cls._read_csv(join(pwd, './data/mt19937-testset-2.csv')) cls.seed_error_type = ValueError + cls.invalid_seed_types = [] + cls.invalid_seed_values = [(-1,), np.array([2 ** 33])] def test_seed_out_of_range(self): # GH #82 @@ -359,6 +512,19 @@ def test_seed_float_array(self): assert_raises(TypeError, rs.seed, [np.pi]) assert_raises(TypeError, rs.seed, [0, np.pi]) + def test_state_tuple(self): + rs = RandomGenerator(self.brng(*self.data1['seed'])) + state = rs.state + desired = rs.randint(2 ** 16) + tup = (state['brng'], state['state']['key'], state['state']['pos']) + rs.state = tup + actual = rs.randint(2 ** 16) + assert_equal(actual, desired) + tup = tup + (0, 0.0) + rs.state = tup + actual = rs.randint(2 ** 16) + assert_equal(actual, desired) + class TestDSFMT(Base): @classmethod @@ -369,6 +535,9 @@ def setup_class(cls): cls.data1 = cls._read_csv(join(pwd, './data/dSFMT-testset-1.csv')) cls.data2 = cls._read_csv(join(pwd, './data/dSFMT-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [] + cls.invalid_seed_values = [(-1,), np.array([2 ** 33]), + (np.array([2 ** 33, 2 ** 33]),)] def test_uniform_double(self): rs = RandomGenerator(self.brng(*self.data1['seed'])) @@ -445,6 +614,16 @@ def setup_class(cls): cls.data1 = cls._read_csv(join(pwd, './data/threefry32-testset-1.csv')) cls.data2 = cls._read_csv(join(pwd, './data/threefry32-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [] + cls.invalid_seed_values = [(1, None, 1), (-1,), (2 ** 257 + 1,), + (None, None, 2 ** 129 + 1)] + + def test_set_key(self): + brng = self.brng(*self.data1['seed']) + state = brng.state + keyed = self.brng(counter=state['state']['counter'], + key=state['state']['key']) + assert_state_equal(brng.state, keyed.state) class TestPCG32(TestPCG64): @@ -456,3 +635,7 @@ def setup_class(cls): cls.data1 = cls._read_csv(join(pwd, './data/pcg32-testset-1.csv')) cls.data2 = cls._read_csv(join(pwd, './data/pcg32-testset-2.csv')) cls.seed_error_type = TypeError + cls.invalid_seed_types = [(np.array([1, 2]),), (3.2,), + (None, np.zeros(1))] + cls.invalid_seed_values = [(-1,), (2 ** 129 + 1,), (None, -1), + (None, 2 ** 129 + 1)] diff --git a/numpy/random/randomgen/tests/test_legacy.py b/numpy/random/randomgen/tests/test_legacy.py deleted file mode 100644 index 6f8f9aee77d0..000000000000 --- a/numpy/random/randomgen/tests/test_legacy.py +++ /dev/null @@ -1,17 +0,0 @@ -import pickle - -from ...randomgen.legacy import LegacyGenerator - - -def test_pickle(): - lg = LegacyGenerator() - lg.random_sample(100) - lg.standard_normal() - lg2 = pickle.loads(pickle.dumps(lg)) - assert lg.standard_normal() == lg2.standard_normal() - assert lg.random_sample() == lg2.random_sample() - - -def test_weibull(): - lg = LegacyGenerator() - assert lg.weibull(0.0) == 0.0 diff --git a/numpy/random/randomgen/tests/test_numpy_mt19937.py b/numpy/random/randomgen/tests/test_numpy_mt19937.py index 5a08eca02476..b2249b684f67 100644 --- a/numpy/random/randomgen/tests/test_numpy_mt19937.py +++ b/numpy/random/randomgen/tests/test_numpy_mt19937.py @@ -10,10 +10,8 @@ assert_array_almost_equal, suppress_warnings) from ...randomgen import RandomGenerator, MT19937 -from ...randomgen.legacy import LegacyGenerator random = mt19937 = RandomGenerator(MT19937()) -legacy = LegacyGenerator(MT19937()) class TestSeed(object): @@ -90,6 +88,9 @@ def test_size(self): assert_raises(TypeError, mt19937.multinomial, 1, p, float(1)) + def test_invalid_prob(self): + assert_raises(ValueError, random.multinomial, 100, [1.1, 0.2]) + class TestSetState(object): def setup(self): @@ -124,19 +125,6 @@ def test_gaussian_reset_in_media_res(self): new = self.brng.standard_normal(size=3) assert_(np.all(old == new)) - def test_backwards_compatibility(self): - # Make sure we can accept old state tuples that do not have the - # cached Gaussian value. - old_state = self.legacy_state - legacy.state = old_state - x1 = legacy.standard_normal(size=16) - legacy.state = old_state - x2 = legacy.standard_normal(size=16) - legacy.state = old_state + (0, 0.0) - x3 = legacy.standard_normal(size=16) - assert_(np.all(x1 == x2)) - assert_(np.all(x1 == x3)) - def test_negative_binomial(self): # Ensure that the negative binomial results take floating point # arguments without truncation. @@ -419,14 +407,24 @@ def test_rand(self): [0.4575674820298663, 0.7781880808593471]]) assert_array_almost_equal(actual, desired, decimal=15) + def test_rand_singleton(self): + mt19937.seed(self.seed) + actual = mt19937.rand() + desired = 0.61879477158567997 + assert_array_almost_equal(actual, desired, decimal=15) + def test_randn(self): - legacy.seed(self.seed) - actual = legacy.randn(3, 2) - desired = np.array([[1.34016345771863121, 1.73759122771936081], - [1.498988344300628, -0.2286433324536169], - [2.031033998682787, 2.17032494605655257]]) + mt19937.seed(self.seed) + actual = mt19937.randn(3, 2) + desired = np.array([[-3.472754000610961, -0.108938564229143], + [-0.245965753396411, -0.704101550261701], + [0.360102487116356, 0.127832101772367]]) assert_array_almost_equal(actual, desired, decimal=15) + mt19937.seed(self.seed) + actual = mt19937.randn() + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + def test_randint(self): mt19937.seed(self.seed) actual = mt19937.randint(-99, 99, size=(3, 2)) @@ -514,6 +512,21 @@ def test_random_sample(self): [0.4575674820298663, 0.7781880808593471]]) assert_array_almost_equal(actual, desired, decimal=15) + mt19937.seed(self.seed) + actual = mt19937.random_sample() + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + + def test_random_sample_float(self): + mt19937.seed(self.seed) + actual = mt19937.random_sample((3, 2)) + desired = np.array([[0.6187948, 0.5916236], + [0.8886836, 0.8916548], + [0.4575675, 0.7781881]]) + assert_array_almost_equal(actual, desired, decimal=7) + + def test_random_sample_unsupported_type(self): + assert_raises(TypeError, mt19937.random_sample, dtype='int32') + def test_choice_uniform_replace(self): mt19937.seed(self.seed) actual = mt19937.choice(4, 4) @@ -662,6 +675,18 @@ def test_shuffle_masked(self): assert_equal( sorted(b.data[~b.mask]), sorted(b_orig.data[~b_orig.mask])) + def test_permutation(self): + mt19937.seed(self.seed) + alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] + actual = mt19937.permutation(alist) + desired = [0, 1, 9, 6, 2, 4, 5, 8, 7, 3] + assert_array_equal(actual, desired) + + mt19937.seed(self.seed) + arr_2d = np.atleast_2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).T + actual = mt19937.permutation(arr_2d) + assert_array_equal(actual, np.atleast_2d(desired).T) + def test_beta(self): mt19937.seed(self.seed) actual = mt19937.beta(.1, .9, size=(3, 2)) @@ -680,23 +705,23 @@ def test_binomial(self): assert_array_equal(actual, desired) def test_chisquare(self): - legacy.seed(self.seed) - actual = legacy.chisquare(50, size=(3, 2)) - desired = np.array([[63.87858175501090585, 68.68407748911370447], - [65.77116116901505904, 47.09686762438974483], - [72.3828403199695174, 74.18408615260374006]]) + mt19937.seed(self.seed) + actual = mt19937.chisquare(50, size=(3, 2)) + desired = np.array([[22.2534560369812, 46.9302393710074], + [52.9974164611614, 85.3559029505718], + [46.1580841240719, 36.1933148548090]]) assert_array_almost_equal(actual, desired, decimal=13) def test_dirichlet(self): - legacy.seed(self.seed) + mt19937.seed(self.seed) alpha = np.array([51.72840233779265162, 39.74494232180943953]) - actual = legacy.dirichlet(alpha, size=(3, 2)) - desired = np.array([[[0.54539444573611562, 0.45460555426388438], - [0.62345816822039413, 0.37654183177960598]], - [[0.55206000085785778, 0.44793999914214233], - [0.58964023305154301, 0.41035976694845688]], - [[0.59266909280647828, 0.40733090719352177], - [0.56974431743975207, 0.43025568256024799]]]) + actual = mt19937.dirichlet(alpha, size=(3, 2)) + desired = np.array([[[0.444382290764855, 0.555617709235145], + [0.468440809291970, 0.531559190708030]], + [[0.613461427360549, 0.386538572639451], + [0.529103072088183, 0.470896927911817]], + [[0.513490650101800, 0.486509349898200], + [0.558550925712797, 0.441449074287203]]]) assert_array_almost_equal(actual, desired, decimal=15) bad_alpha = np.array([5.4e-01, -1.0e-16]) assert_raises(ValueError, mt19937.dirichlet, bad_alpha) @@ -719,11 +744,11 @@ def test_dirichlet_bad_alpha(self): assert_raises(ValueError, mt19937.dirichlet, alpha) def test_exponential(self): - legacy.seed(self.seed) - actual = legacy.exponential(1.1234, size=(3, 2)) - desired = np.array([[1.08342649775011624, 1.00607889924557314], - [2.46628830085216721, 2.49668106809923884], - [0.68717433461363442, 1.69175666993575979]]) + random.seed(self.seed) + actual = random.exponential(1.1234, size=(3, 2)) + desired = np.array([[5.350682337747634, 1.152307441755771], + [3.867015473358779, 1.538765912839396], + [0.347846818048527, 2.715656549872026]]) assert_array_almost_equal(actual, desired, decimal=15) def test_exponential_0(self): @@ -731,19 +756,19 @@ def test_exponential_0(self): assert_raises(ValueError, mt19937.exponential, scale=-0.) def test_f(self): - legacy.seed(self.seed) - actual = legacy.f(12, 77, size=(3, 2)) - desired = np.array([[1.21975394418575878, 1.75135759791559775], - [1.44803115017146489, 1.22108959480396262], - [1.02176975757740629, 1.34431827623300415]]) + random.seed(self.seed) + actual = random.f(12, 77, size=(3, 2)) + desired = np.array([[0.809498839488467, 2.867222762455471], + [0.588036831639353, 1.012185639664636], + [1.147554281917365, 1.150886518432105]]) assert_array_almost_equal(actual, desired, decimal=15) def test_gamma(self): - legacy.seed(self.seed) - actual = legacy.gamma(5, 3, size=(3, 2)) - desired = np.array([[24.60509188649287182, 28.54993563207210627], - [26.13476110204064184, 12.56988482927716078], - [31.71863275789960568, 33.30143302795922011]]) + random.seed(self.seed) + actual = random.gamma(5, 3, size=(3, 2)) + desired = np.array([[12.46569350177219, 16.46580642087044], + [43.65744473309084, 11.98722785682592], + [6.50371499559955, 7.48465689751638]]) assert_array_almost_equal(actual, desired, decimal=14) def test_gamma_0(self): @@ -817,11 +842,11 @@ def test_logistic(self): assert_array_almost_equal(actual, desired, decimal=15) def test_lognormal(self): - legacy.seed(self.seed) - actual = legacy.lognormal(mean=.123456789, sigma=2.0, size=(3, 2)) - desired = np.array([[16.50698631688883822, 36.54846706092654784], - [22.67886599981281748, 0.71617561058995771], - [65.72798501792723869, 86.84341601437161273]]) + random.seed(self.seed) + actual = random.lognormal(mean=.123456789, sigma=2.0, size=(3, 2)) + desired = np.array([[1.0894838661036e-03, 9.0990021488311e-01], + [6.9178869932225e-01, 2.7672077560016e-01], + [2.3248645126975e+00, 1.4609997951330e+00]]) assert_array_almost_equal(actual, desired, decimal=13) def test_lognormal_0(self): @@ -848,23 +873,25 @@ def test_multinomial(self): assert_array_equal(actual, desired) def test_multivariate_normal(self): - legacy.seed(self.seed) + random.seed(self.seed) mean = (.123456789, 10) cov = [[1, 0], [0, 1]] size = (3, 2) - actual = legacy.multivariate_normal(mean, cov, size) - desired = np.array([[[1.463620246718631, 11.73759122771936], - [1.622445133300628, 9.771356667546383]], - [[2.154490787682787, 12.170324946056553], - [1.719909438201865, 9.230548443648306]], - [[0.689515026297799, 9.880729819607714], - [-0.023054015651998, 9.201096623542879]]]) + actual = random.multivariate_normal(mean, cov, size) + np.set_printoptions(precision=20) + print(actual) + desired = np.array([[[-3.34929721161096100, 9.891061435770858], + [-0.12250896439641100, 9.295898449738300]], + [[0.48355927611635563, 10.127832101772366], + [3.11093021424924300, 10.283109168794352]], + [[-0.20332082341774727, 9.868532121697195], + [-1.33806889550667330, 9.813657233804179]]]) assert_array_almost_equal(actual, desired, decimal=15) # Check for default size, was raising deprecation warning - actual = legacy.multivariate_normal(mean, cov) - desired = np.array([0.895289569463708, 9.17180864067987]) + actual = random.multivariate_normal(mean, cov) + desired = np.array([-1.097443117192574, 10.535787051184261]) assert_array_almost_equal(actual, desired, decimal=15) # Check that non positive-semidefinite covariance warns with @@ -883,54 +910,65 @@ def test_multivariate_normal(self): cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) with suppress_warnings() as sup: - w = sup.record(RuntimeWarning) mt19937.multivariate_normal(mean, cov) + w = sup.record(RuntimeWarning) assert len(w) == 0 + mu = np.zeros(2) + cov = np.eye(2) + assert_raises(ValueError, mt19937.multivariate_normal, mean, cov, + check_valid='other') + assert_raises(ValueError, mt19937.multivariate_normal, + np.zeros((2, 1, 1)), cov) + assert_raises(ValueError, mt19937.multivariate_normal, + mu, np.empty((3, 2))) + assert_raises(ValueError, mt19937.multivariate_normal, + mu, np.eye(3)) + def test_negative_binomial(self): - legacy.seed(self.seed) - actual = legacy.negative_binomial(n=100, p=.12345, size=(3, 2)) - desired = np.array([[848, 841], - [892, 611], - [779, 647]]) + random.seed(self.seed) + actual = random.negative_binomial(n=100, p=.12345, size=(3, 2)) + desired = np.array([[521, 736], + [665, 690], + [723, 751]]) assert_array_equal(actual, desired) def test_noncentral_chisquare(self): - legacy.seed(self.seed) - actual = legacy.noncentral_chisquare(df=5, nonc=5, size=(3, 2)) - desired = np.array([[23.91905354498517511, 13.35324692733826346], - [31.22452661329736401, 16.60047399466177254], - [5.03461598262724586, 17.94973089023519464]]) + random.seed(self.seed) + actual = random.noncentral_chisquare(df=5, nonc=5, size=(3, 2)) + desired = np.array([[9.47783251920357, 10.02066178260461], + [3.15869984192364, 10.5581565031544], + [5.01652540543548, 13.7689551218441]]) assert_array_almost_equal(actual, desired, decimal=14) - actual = legacy.noncentral_chisquare(df=.5, nonc=.2, size=(3, 2)) - desired = np.array([[1.47145377828516666, 0.15052899268012659], - [0.00943803056963588, 1.02647251615666169], - [0.332334982684171, 0.15451287602753125]]) + actual = random.noncentral_chisquare(df=.5, nonc=.2, size=(3, 2)) + desired = np.array([[0.00145153051285, 0.22432468724778], + [0.02956713468556, 0.00207192946898], + [1.41985055641800, 0.15451287602753]]) assert_array_almost_equal(actual, desired, decimal=14) - legacy.seed(self.seed) - actual = legacy.noncentral_chisquare(df=5, nonc=0, size=(3, 2)) - desired = np.array([[9.597154162763948, 11.725484450296079], - [10.413711048138335, 3.694475922923986], - [13.484222138963087, 14.377255424602957]]) + random.seed(self.seed) + actual = random.noncentral_chisquare(df=5, nonc=0, size=(3, 2)) + desired = np.array([[3.64881368071039, 5.48224544747803], + [20.41999842025404, 3.44075915187367], + [1.29765160605552, 1.64125033268606]]) assert_array_almost_equal(actual, desired, decimal=14) def test_noncentral_f(self): - legacy.seed(self.seed) - actual = legacy.noncentral_f(dfnum=5, dfden=2, nonc=1, + random.seed(self.seed) + actual = random.noncentral_f(dfnum=5, dfden=2, nonc=1, size=(3, 2)) - desired = np.array([[1.40598099674926669, 0.34207973179285761], - [3.57715069265772545, 7.92632662577829805], - [0.43741599463544162, 1.1774208752428319]]) + desired = np.array([[1.22680230963236, 2.56457837623956], + [2.7653304499494, 7.4336268865443], + [1.16362730891403, 2.54104276581491]]) assert_array_almost_equal(actual, desired, decimal=14) def test_normal(self): - legacy.seed(self.seed) - actual = legacy.normal(loc=.123456789, scale=2.0, size=(3, 2)) - desired = np.array([[2.80378370443726244, 3.59863924443872163], - [3.121433477601256, -0.33382987590723379], - [4.18552478636557357, 4.46410668111310471]]) + random.seed(self.seed) + actual = random.normal(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[-6.822051212221923, -0.094420339458285], + [-0.368474717792823, -1.284746311523402], + [0.843661763232711, 0.379120992544734]]) assert_array_almost_equal(actual, desired, decimal=15) def test_normal_0(self): @@ -938,12 +976,13 @@ def test_normal_0(self): assert_raises(ValueError, mt19937.normal, scale=-0.) def test_pareto(self): - legacy.seed(self.seed) - actual = legacy.pareto(a=.123456789, size=(3, 2)) - desired = np.array( - [[2.46852460439034849e+03, 1.41286880810518346e+03], - [5.28287797029485181e+07, 6.57720981047328785e+07], - [1.40840323350391515e+02, 1.98390255135251704e+05]]) + random.seed(self.seed) + actual = random.pareto(a=.123456789, size=(3, 2)) + np.set_printoptions(precision=20) + print(actual) + desired = np.array([[5.6883528121891552e+16, 4.0569373841667057e+03], + [1.2854967019379475e+12, 6.5833156486851483e+04], + [1.1281132447159091e+01, 3.1895968171107006e+08]]) # For some reason on 32-bit x86 Ubuntu 12.10 the [1, 0] entry in this # matrix differs by 24 nulps. Discussion: # http://mail.scipy.org/pipermail/numpy-discussion/2012-September/063801.html @@ -969,11 +1008,11 @@ def test_poisson_exceptions(self): assert_raises(ValueError, mt19937.poisson, [lambig] * 10) def test_power(self): - legacy.seed(self.seed) - actual = legacy.power(a=.123456789, size=(3, 2)) - desired = np.array([[0.02048932883240791, 0.01424192241128213], - [0.38446073748535298, 0.39499689943484395], - [0.00177699707563439, 0.13115505880863756]]) + random.seed(self.seed) + actual = random.power(a=.123456789, size=(3, 2)) + desired = np.array([[9.328833342693975e-01, 2.742250409261003e-02], + [7.684513237993961e-01, 9.297548209160028e-02], + [2.214811188828573e-05, 4.693448360603472e-01]]) assert_array_almost_equal(actual, desired, decimal=15) def test_rayleigh(self): @@ -989,11 +1028,11 @@ def test_rayleigh_0(self): assert_raises(ValueError, mt19937.rayleigh, scale=-0.) def test_standard_cauchy(self): - legacy.seed(self.seed) - actual = legacy.standard_cauchy(size=(3, 2)) - desired = np.array([[0.77127660196445336, -6.55601161955910605], - [0.93582023391158309, -2.07479293013759447], - [-4.74601644297011926, 0.18338989290760804]]) + random.seed(self.seed) + actual = random.standard_cauchy(size=(3, 2)) + desired = np.array([[31.87809592667601, 0.349332782046838], + [2.816995747731641, 10.552372563459114], + [2.485608017991235, 7.843211273201831]]) assert_array_almost_equal(actual, desired, decimal=15) def test_standard_exponential(self): @@ -1005,31 +1044,43 @@ def test_standard_exponential(self): assert_array_almost_equal(actual, desired, decimal=15) def test_standard_gamma(self): - legacy.seed(self.seed) - actual = legacy.standard_gamma(shape=3, size=(3, 2)) - desired = np.array([[5.50841531318455058, 6.62953470301903103], - [5.93988484943779227, 2.31044849402133989], - [7.54838614231317084, 8.012756093271868]]) + random.seed(self.seed) + actual = random.standard_gamma(shape=3, size=(3, 2)) + desired = np.array([[2.28483515569645, 3.29899524967824], + [11.12492298902645, 2.16784417297277], + [0.92121813690910, 1.12853552328470]]) assert_array_almost_equal(actual, desired, decimal=14) + def test_standard_gamma_float(self): + random.seed(self.seed) + actual = random.standard_gamma(shape=3, size=(3, 2)) + desired = np.array([[2.2848352, 3.2989952], + [11.124923, 2.1678442], + [0.9212181, 1.1285355]]) + assert_array_almost_equal(actual, desired, decimal=7) + + def test_standard_gamma_unknown_type(self): + assert_raises(TypeError, random.standard_gamma, 1., + dtype='int32') + def test_standard_gamma_0(self): assert_equal(mt19937.standard_gamma(shape=0), 0) assert_raises(ValueError, mt19937.standard_gamma, shape=-0.) def test_standard_normal(self): - legacy.seed(self.seed) - actual = legacy.standard_normal(size=(3, 2)) - desired = np.array([[1.34016345771863121, 1.73759122771936081], - [1.498988344300628, -0.2286433324536169], - [2.031033998682787, 2.17032494605655257]]) + random.seed(self.seed) + actual = random.standard_normal(size=(3, 2)) + desired = np.array([[-3.472754000610961, -0.108938564229143], + [-0.245965753396411, -0.704101550261701], + [0.360102487116356, 0.127832101772367]]) assert_array_almost_equal(actual, desired, decimal=15) def test_standard_t(self): - legacy.seed(self.seed) - actual = legacy.standard_t(df=10, size=(3, 2)) - desired = np.array([[0.97140611862659965, -0.08830486548450577], - [1.36311143689505321, -0.55317463909867071], - [-0.18473749069684214, 0.61181537341755321]]) + random.seed(self.seed) + actual = random.standard_t(df=10, size=(3, 2)) + desired = np.array([[-3.68722108185508, -0.672031186266171], + [2.900224996448669, -0.199656996187739], + [-1.12179956985969, 1.85668262342106]]) assert_array_almost_equal(actual, desired, decimal=15) def test_triangular(self): @@ -1102,19 +1153,19 @@ def test_vonmises_small(self): assert_(np.isfinite(r).all()) def test_wald(self): - legacy.seed(self.seed) - actual = legacy.wald(mean=1.23, scale=1.54, size=(3, 2)) - desired = np.array([[3.82935265715889983, 5.13125249184285526], - [0.35045403618358717, 1.50832396872003538], - [0.24124319895843183, 0.22031101461955038]]) + random.seed(self.seed) + actual = random.wald(mean=1.23, scale=1.54, size=(3, 2)) + desired = np.array([[0.10653278160339, 0.98771068102461], + [0.89276055317879, 0.13640126419923], + [0.9194319091599, 0.36037816317472]]) assert_array_almost_equal(actual, desired, decimal=14) def test_weibull(self): - legacy.seed(self.seed) - actual = legacy.weibull(a=1.23, size=(3, 2)) - desired = np.array([[0.97097342648766727, 0.91422896443565516], - [1.89517770034962929, 1.91414357960479564], - [0.67057783752390987, 1.39494046635066793]]) + random.seed(self.seed) + actual = random.weibull(a=1.23, size=(3, 2)) + desired = np.array([[3.557276979846361, 1.020870580998542], + [2.731847777612348, 1.29148068905082], + [0.385531483942839, 2.049551716717254]]) assert_array_almost_equal(actual, desired, decimal=15) def test_weibull_0(self): @@ -1139,7 +1190,6 @@ def setup(self): def set_seed(self): random.seed(self.seed) - legacy.seed(self.seed) def test_uniform(self): low = [0] @@ -1161,10 +1211,10 @@ def test_normal(self): loc = [0] scale = [1] bad_scale = [-1] - normal = legacy.normal - desired = np.array([2.2129019979039612, - 2.1283977976520019, - 1.8417114045748335]) + normal = random.normal + desired = np.array([0.454879818179180, + -0.62749179463661, + -0.06063266769872]) self.set_seed() actual = normal(loc * 3, scale) @@ -1183,104 +1233,90 @@ def test_beta(self): b = [2] bad_a = [-1] bad_b = [-2] - beta = legacy.beta - desired = np.array([0.19843558305989056, - 0.075230336409423643, - 0.24976865978980844]) + beta = mt19937.beta + desired = np.array([0.63222080311226, + 0.33310522220774, + 0.64494078460190]) self.set_seed() actual = beta(a * 3, b) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, beta, bad_a * 3, b) assert_raises(ValueError, beta, a * 3, bad_b) - assert_raises(ValueError, mt19937.beta, bad_a * 3, b) - assert_raises(ValueError, mt19937.beta, a * 3, bad_b) self.set_seed() actual = beta(a, b * 3) assert_array_almost_equal(actual, desired, decimal=14) - assert_raises(ValueError, mt19937.beta, bad_a, b * 3) - assert_raises(ValueError, mt19937.beta, a, bad_b * 3) def test_exponential(self): scale = [1] bad_scale = [-1] - exponential = legacy.exponential - desired = np.array([0.76106853658845242, - 0.76386282278691653, - 0.71243813125891797]) + exponential = mt19937.exponential + desired = np.array([1.68591211640990, + 3.14186859487914, + 0.67717375919228]) self.set_seed() actual = exponential(scale * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, exponential, bad_scale * 3) - assert_raises(ValueError, mt19937.exponential, bad_scale * 3) def test_standard_gamma(self): shape = [1] bad_shape = [-1] - std_gamma = legacy.standard_gamma - desired = np.array([0.76106853658845242, - 0.76386282278691653, - 0.71243813125891797]) + std_gamma = mt19937.standard_gamma + desired = np.array([1.68591211640990, + 3.14186859487914, + 0.67717375919228]) self.set_seed() actual = std_gamma(shape * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, std_gamma, bad_shape * 3) - assert_raises(ValueError, mt19937.standard_gamma, bad_shape * 3) def test_gamma(self): shape = [1] scale = [2] bad_shape = [-1] bad_scale = [-2] - gamma = legacy.gamma - desired = np.array([1.5221370731769048, - 1.5277256455738331, - 1.4248762625178359]) + gamma = mt19937.gamma + desired = np.array([3.37182423281980, + 6.28373718975827, + 1.35434751838456]) self.set_seed() actual = gamma(shape * 3, scale) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, gamma, bad_shape * 3, scale) assert_raises(ValueError, gamma, shape * 3, bad_scale) - assert_raises(ValueError, mt19937.gamma, bad_shape * 3, scale) - assert_raises(ValueError, mt19937.gamma, shape * 3, bad_scale) self.set_seed() actual = gamma(shape, scale * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, gamma, bad_shape, scale * 3) assert_raises(ValueError, gamma, shape, bad_scale * 3) - assert_raises(ValueError, mt19937.gamma, bad_shape, scale * 3) - assert_raises(ValueError, mt19937.gamma, shape, bad_scale * 3) def test_f(self): dfnum = [1] dfden = [2] bad_dfnum = [-1] bad_dfden = [-2] - f = legacy.f - desired = np.array([0.80038951638264799, - 0.86768719635363512, - 2.7251095168386801]) + f = mt19937.f + desired = np.array([0.84207044881810, + 3.08607209903483, + 3.12823105933169]) self.set_seed() actual = f(dfnum * 3, dfden) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, f, bad_dfnum * 3, dfden) assert_raises(ValueError, f, dfnum * 3, bad_dfden) - assert_raises(ValueError, mt19937.f, bad_dfnum * 3, dfden) - assert_raises(ValueError, mt19937.f, dfnum * 3, bad_dfden) self.set_seed() actual = f(dfnum, dfden * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, f, bad_dfnum, dfden * 3) assert_raises(ValueError, f, dfnum, bad_dfden * 3) - assert_raises(ValueError, mt19937.f, bad_dfnum, dfden * 3) - assert_raises(ValueError, mt19937.f, dfnum, bad_dfden * 3) def test_noncentral_f(self): dfnum = [2] @@ -1289,10 +1325,10 @@ def test_noncentral_f(self): bad_dfnum = [0] bad_dfden = [-1] bad_nonc = [-2] - nonc_f = legacy.noncentral_f - desired = np.array([9.1393943263705211, - 13.025456344595602, - 8.8018098359100545]) + nonc_f = mt19937.noncentral_f + desired = np.array([3.83710578542563, + 8.74926819712029, + 0.48892943835401]) self.set_seed() actual = nonc_f(dfnum * 3, dfden, nonc) @@ -1301,9 +1337,6 @@ def test_noncentral_f(self): assert_raises(ValueError, nonc_f, bad_dfnum * 3, dfden, nonc) assert_raises(ValueError, nonc_f, dfnum * 3, bad_dfden, nonc) assert_raises(ValueError, nonc_f, dfnum * 3, dfden, bad_nonc) - assert_raises(ValueError, mt_nonc_f, bad_dfnum * 3, dfden, nonc) - assert_raises(ValueError, mt_nonc_f, dfnum * 3, bad_dfden, nonc) - assert_raises(ValueError, mt_nonc_f, dfnum * 3, dfden, bad_nonc) self.set_seed() actual = nonc_f(dfnum, dfden * 3, nonc) @@ -1311,9 +1344,6 @@ def test_noncentral_f(self): assert_raises(ValueError, nonc_f, bad_dfnum, dfden * 3, nonc) assert_raises(ValueError, nonc_f, dfnum, bad_dfden * 3, nonc) assert_raises(ValueError, nonc_f, dfnum, dfden * 3, bad_nonc) - assert_raises(ValueError, mt_nonc_f, bad_dfnum, dfden * 3, nonc) - assert_raises(ValueError, mt_nonc_f, dfnum, bad_dfden * 3, nonc) - assert_raises(ValueError, mt_nonc_f, dfnum, dfden * 3, bad_nonc) self.set_seed() actual = nonc_f(dfnum, dfden, nonc * 3) @@ -1343,10 +1373,10 @@ def test_noncentral_chisquare(self): nonc = [2] bad_df = [-1] bad_nonc = [-2] - nonc_chi = legacy.noncentral_chisquare - desired = np.array([9.0015599467913763, - 4.5804135049718742, - 6.0872302432834564]) + nonc_chi = random.noncentral_chisquare + desired = np.array([2.20478739452297, + 1.45177405755115, + 1.00418921695354]) self.set_seed() actual = nonc_chi(df * 3, nonc) @@ -1368,10 +1398,10 @@ def test_noncentral_chisquare(self): def test_standard_t(self): df = [1] bad_df = [-1] - t = legacy.standard_t - desired = np.array([3.0702872575217643, - 5.8560725167361607, - 1.0274791436474273]) + t = random.standard_t + desired = np.array([0.60081050724244, + -0.90380889829210, + -0.64499590504117]) self.set_seed() actual = t(df * 3) @@ -1401,10 +1431,10 @@ def test_vonmises(self): def test_pareto(self): a = [1] bad_a = [-1] - pareto = legacy.pareto - desired = np.array([1.1405622680198362, - 1.1465519762044529, - 1.0389564467453547]) + pareto = random.pareto + desired = np.array([4.397371719158540, + 22.14707898642946, + 0.968306954322200]) self.set_seed() actual = pareto(a * 3) @@ -1415,10 +1445,10 @@ def test_pareto(self): def test_weibull(self): a = [1] bad_a = [-1] - weibull = legacy.weibull - desired = np.array([0.76106853658845242, - 0.76386282278691653, - 0.71243813125891797]) + weibull = random.weibull + desired = np.array([1.68591211640990, + 3.14186859487914, + 0.67717375919228]) self.set_seed() actual = weibull(a * 3) @@ -1429,10 +1459,10 @@ def test_weibull(self): def test_power(self): a = [1] bad_a = [-1] - power = legacy.power - desired = np.array([0.53283302478975902, - 0.53413660089041659, - 0.50955303552646702]) + power = random.power + desired = np.array([0.81472463783615, + 0.95679800459547, + 0.49194916077287]) self.set_seed() actual = power(a * 3) @@ -1502,10 +1532,10 @@ def test_lognormal(self): mean = [0] sigma = [1] bad_sigma = [-1] - lognormal = legacy.lognormal - desired = np.array([9.1422086044848427, - 8.4013952870126261, - 6.3073234116578671]) + lognormal = random.lognormal + desired = np.array([1.57598396702930, + 0.53392932731280, + 0.94116889802361]) self.set_seed() actual = lognormal(mean * 3, sigma) @@ -1537,10 +1567,10 @@ def test_wald(self): scale = [1] bad_mean = [0] bad_scale = [-2] - wald = legacy.wald - desired = np.array([0.11873681120271318, - 0.12450084820795027, - 0.9096122728408238]) + wald = random.wald + desired = np.array([0.36297361471752, + 0.52190135028254, + 0.55111022040727]) self.set_seed() actual = wald(mean * 3, scale) @@ -1594,6 +1624,10 @@ def test_triangular(self): assert_raises(ValueError, triangular, bad_left_two, bad_mode_two, right * 3) + assert_raises(ValueError, triangular, 10., 0., 20.) + assert_raises(ValueError, triangular, 10., 25., 20.) + assert_raises(ValueError, triangular, 10., 10., 10.) + def test_binomial(self): n = [1] p = [0.5] @@ -1623,8 +1657,8 @@ def test_negative_binomial(self): bad_n = [-1] bad_p_one = [-1] bad_p_two = [1.5] - neg_binom = legacy.negative_binomial - desired = np.array([1, 0, 1]) + neg_binom = random.negative_binomial + desired = np.array([3, 1, 2], dtype=np.int64) self.set_seed() actual = neg_binom(n * 3, p) @@ -1632,9 +1666,6 @@ def test_negative_binomial(self): assert_raises(ValueError, neg_binom, bad_n * 3, p) assert_raises(ValueError, neg_binom, n * 3, bad_p_one) assert_raises(ValueError, neg_binom, n * 3, bad_p_two) - assert_raises(ValueError, mt19937.negative_binomial, bad_n * 3, p) - assert_raises(ValueError, mt19937.negative_binomial, n * 3, bad_p_one) - assert_raises(ValueError, mt19937.negative_binomial, n * 3, bad_p_two) self.set_seed() actual = neg_binom(n, p * 3) @@ -1642,9 +1673,6 @@ def test_negative_binomial(self): assert_raises(ValueError, neg_binom, bad_n, p * 3) assert_raises(ValueError, neg_binom, n, bad_p_one * 3) assert_raises(ValueError, neg_binom, n, bad_p_two * 3) - assert_raises(ValueError, mt19937.negative_binomial, bad_n, p * 3) - assert_raises(ValueError, mt19937.negative_binomial, n, bad_p_one * 3) - assert_raises(ValueError, mt19937.negative_binomial, n, bad_p_two * 3) def test_poisson(self): max_lam = random.poisson_lam_max @@ -1720,6 +1748,11 @@ def test_hypergeometric(self): assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_one * 3) assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_two * 3) + assert_raises(ValueError, hypergeom, -1, 10, 20) + assert_raises(ValueError, hypergeom, 10, -1, 20) + assert_raises(ValueError, hypergeom, 10, 10, 0) + assert_raises(ValueError, hypergeom, 10, 10, 25) + def test_logseries(self): p = [0.5] bad_p_one = [2] @@ -1835,22 +1868,22 @@ def test_two_arg_funcs(self): out = func(self.argOne, argTwo[0]) assert_equal(out.shape, self.tgtShape) - def test_randint(self): - itype = [np.bool, np.int8, np.uint8, np.int16, np.uint16, - np.int32, np.uint32, np.int64, np.uint64] - func = mt19937.randint - high = np.array([1]) - low = np.array([0]) - - for dt in itype: - out = func(low, high, dtype=dt) - assert_equal(out.shape, self.tgtShape) + def test_randint(self): + itype = [np.bool, np.int8, np.uint8, np.int16, np.uint16, + np.int32, np.uint32, np.int64, np.uint64] + func = mt19937.randint + high = np.array([1]) + low = np.array([0]) + + for dt in itype: + out = func(low, high, dtype=dt) + assert_equal(out.shape, self.tgtShape) - out = func(low[0], high, dtype=dt) - assert_equal(out.shape, self.tgtShape) + out = func(low[0], high, dtype=dt) + assert_equal(out.shape, self.tgtShape) - out = func(low, high[0], dtype=dt) - assert_equal(out.shape, self.tgtShape) + out = func(low, high[0], dtype=dt) + assert_equal(out.shape, self.tgtShape) def test_three_arg_funcs(self): funcs = [mt19937.noncentral_f, mt19937.triangular, diff --git a/numpy/random/randomgen/tests/test_randomstate.py b/numpy/random/randomgen/tests/test_randomstate.py new file mode 100644 index 000000000000..371be78de5bf --- /dev/null +++ b/numpy/random/randomgen/tests/test_randomstate.py @@ -0,0 +1,1808 @@ +import warnings +import pickle + +import numpy as np +from numpy.testing import ( + assert_, assert_raises, assert_equal, assert_warns, + assert_no_warnings, assert_array_equal, assert_array_almost_equal, + suppress_warnings + ) +import sys + +from ...randomgen import MT19937, Xoshiro256StarStar, mtrand as random + + +def assert_mt19937_state_equal(a, b): + assert_equal(a['brng'], b['brng']) + assert_array_equal(a['state']['key'], b['state']['key']) + assert_array_equal(a['state']['pos'], b['state']['pos']) + assert_equal(a['has_gauss'], b['has_gauss']) + assert_equal(a['gauss'], b['gauss']) + + +class TestSeed(object): + def test_scalar(self): + s = random.RandomState(0) + assert_equal(s.randint(1000), 684) + s = random.RandomState(4294967295) + assert_equal(s.randint(1000), 419) + + def test_array(self): + s = random.RandomState(range(10)) + assert_equal(s.randint(1000), 468) + s = random.RandomState(np.arange(10)) + assert_equal(s.randint(1000), 468) + s = random.RandomState([0]) + assert_equal(s.randint(1000), 973) + s = random.RandomState([4294967295]) + assert_equal(s.randint(1000), 265) + + def test_invalid_scalar(self): + # seed must be an unsigned 32 bit integer + assert_raises(TypeError, random.RandomState, -0.5) + assert_raises(ValueError, random.RandomState, -1) + + def test_invalid_array(self): + # seed must be an unsigned 32 bit integer + assert_raises(TypeError, random.RandomState, [-0.5]) + assert_raises(ValueError, random.RandomState, [-1]) + assert_raises(ValueError, random.RandomState, [4294967296]) + assert_raises(ValueError, random.RandomState, [1, 2, 4294967296]) + assert_raises(ValueError, random.RandomState, [1, -2, 4294967296]) + + def test_invalid_array_shape(self): + # gh-9832 + assert_raises(ValueError, random.RandomState, np.array([], + dtype=np.int64)) + assert_raises(ValueError, random.RandomState, [[1, 2, 3]]) + assert_raises(ValueError, random.RandomState, [[1, 2, 3], + [4, 5, 6]]) + + def test_seed_equivalency(self): + rs = random.RandomState(0) + rs2 = random.RandomState(MT19937(0)) + assert_mt19937_state_equal(rs.get_state(legacy=False), + rs2.get_state(legacy=False)) + + def test_invalid_initialization(self): + assert_raises(ValueError, random.RandomState, MT19937) + + +class TestBinomial(object): + def test_n_zero(self): + # Tests the corner case of n == 0 for the binomial distribution. + # binomial(0, p) should be zero for any p in [0, 1]. + # This test addresses issue #3480. + zeros = np.zeros(2, dtype='int') + for p in [0, .5, 1]: + assert_(random.binomial(0, p) == 0) + assert_array_equal(random.binomial(zeros, p), zeros) + + def test_p_is_nan(self): + # Issue #4571. + assert_raises(ValueError, random.binomial, 1, np.nan) + + +class TestMultinomial(object): + def test_basic(self): + random.multinomial(100, [0.2, 0.8]) + + def test_zero_probability(self): + random.multinomial(100, [0.2, 0.8, 0.0, 0.0, 0.0]) + + def test_int_negative_interval(self): + assert_(-5 <= random.randint(-5, -1) < -1) + x = random.randint(-5, -1, 5) + assert_(np.all(-5 <= x)) + assert_(np.all(x < -1)) + + def test_size(self): + # gh-3173 + p = [0.5, 0.5] + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, np.array((2, 2))).shape, + (2, 2, 2)) + + assert_raises(TypeError, random.multinomial, 1, p, + float(1)) + assert_raises(ValueError, random.multinomial, 1, [1.1, .1]) + + +class TestSetState(object): + def setup(self): + self.seed = 1234567890 + self.random_state = random.RandomState(self.seed) + self.state = self.random_state.get_state() + + def test_basic(self): + old = self.random_state.tomaxint(16) + self.random_state.set_state(self.state) + new = self.random_state.tomaxint(16) + assert_(np.all(old == new)) + + def test_gaussian_reset(self): + # Make sure the cached every-other-Gaussian is reset. + old = self.random_state.standard_normal(size=3) + self.random_state.set_state(self.state) + new = self.random_state.standard_normal(size=3) + assert_(np.all(old == new)) + + def test_gaussian_reset_in_media_res(self): + # When the state is saved with a cached Gaussian, make sure the + # cached Gaussian is restored. + + self.random_state.standard_normal() + state = self.random_state.get_state() + old = self.random_state.standard_normal(size=3) + self.random_state.set_state(state) + new = self.random_state.standard_normal(size=3) + assert_(np.all(old == new)) + + def test_backwards_compatibility(self): + # Make sure we can accept old state tuples that do not have the + # cached Gaussian value. + old_state = self.state[:-2] + x1 = self.random_state.standard_normal(size=16) + self.random_state.set_state(old_state) + x2 = self.random_state.standard_normal(size=16) + self.random_state.set_state(self.state) + x3 = self.random_state.standard_normal(size=16) + assert_(np.all(x1 == x2)) + assert_(np.all(x1 == x3)) + + def test_negative_binomial(self): + # Ensure that the negative binomial results take floating point + # arguments without truncation. + self.random_state.negative_binomial(0.5, 0.5) + + def test_get_state_warning(self): + rs = random.RandomState(Xoshiro256StarStar()) + with suppress_warnings() as sup: + w = sup.record(RuntimeWarning) + state = rs.get_state() + assert_(len(w) == 1) + assert isinstance(state, dict) + assert state['brng'] == 'Xoshiro256StarStar' + + def test_invalid_legacy_state_setting(self): + state = self.random_state.get_state() + new_state = ('Unknown', ) + state[1:] + assert_raises(ValueError, self.random_state.set_state, new_state) + assert_raises(TypeError, self.random_state.set_state, + np.array(new_state, dtype=np.object)) + state = self.random_state.get_state(legacy=False) + del state['brng'] + assert_raises(ValueError, self.random_state.set_state, state) + + def test_pickle(self): + self.random_state.seed(0) + self.random_state.random_sample(100) + self.random_state.standard_normal() + pickled = self.random_state.get_state(legacy=False) + assert_equal(pickled['has_gauss'], 1) + rs_unpick = pickle.loads(pickle.dumps(self.random_state)) + unpickled = rs_unpick.get_state(legacy=False) + assert_mt19937_state_equal(pickled, unpickled) + + def test_state_setting(self): + attr_state = self.random_state.__getstate__() + self.random_state.standard_normal() + self.random_state.__setstate__(attr_state) + state = self.random_state.get_state(legacy=False) + assert_mt19937_state_equal(attr_state, state) + + def test_repr(self): + assert repr(self.random_state).startswith('RandomState(MT19937)') + + +class TestRandint(object): + + rfunc = random.randint + + # valid integer/boolean types + itype = [np.bool_, np.int8, np.uint8, np.int16, np.uint16, + np.int32, np.uint32, np.int64, np.uint64] + + def test_unsupported_type(self): + assert_raises(TypeError, self.rfunc, 1, dtype=float) + + def test_bounds_checking(self): + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + assert_raises(ValueError, self.rfunc, lbnd - 1, ubnd, dtype=dt) + assert_raises(ValueError, self.rfunc, lbnd, ubnd + 1, dtype=dt) + assert_raises(ValueError, self.rfunc, ubnd, lbnd, dtype=dt) + assert_raises(ValueError, self.rfunc, 1, 0, dtype=dt) + + def test_rng_zero_and_extremes(self): + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + + tgt = ubnd - 1 + assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt) + + tgt = lbnd + assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt) + + tgt = (lbnd + ubnd)//2 + assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt) + + def test_full_range(self): + # Test for ticket #1690 + + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + + try: + self.rfunc(lbnd, ubnd, dtype=dt) + except Exception as e: + raise AssertionError("No error should have been raised, " + "but one was with the following " + "message:\n\n%s" % str(e)) + + def test_in_bounds_fuzz(self): + # Don't use fixed seed + random.seed() + + for dt in self.itype[1:]: + for ubnd in [4, 8, 16]: + vals = self.rfunc(2, ubnd, size=2**16, dtype=dt) + assert_(vals.max() < ubnd) + assert_(vals.min() >= 2) + + vals = self.rfunc(0, 2, size=2**16, dtype=np.bool_) + + assert_(vals.max() < 2) + assert_(vals.min() >= 0) + + def test_repeatability(self): + import hashlib + # We use a md5 hash of generated sequences of 1000 samples + # in the range [0, 6) for all but bool, where the range + # is [0, 2). Hashes are for little endian numbers. + tgt = {'bool': '7dd3170d7aa461d201a65f8bcf3944b0', + 'int16': '1b7741b80964bb190c50d541dca1cac1', + 'int32': '4dc9fcc2b395577ebb51793e58ed1a05', + 'int64': '17db902806f448331b5a758d7d2ee672', + 'int8': '27dd30c4e08a797063dffac2490b0be6', + 'uint16': '1b7741b80964bb190c50d541dca1cac1', + 'uint32': '4dc9fcc2b395577ebb51793e58ed1a05', + 'uint64': '17db902806f448331b5a758d7d2ee672', + 'uint8': '27dd30c4e08a797063dffac2490b0be6'} + + for dt in self.itype[1:]: + random.seed(1234) + + # view as little endian for hash + if sys.byteorder == 'little': + val = self.rfunc(0, 6, size=1000, dtype=dt) + else: + val = self.rfunc(0, 6, size=1000, dtype=dt).byteswap() + + res = hashlib.md5(val.view(np.int8)).hexdigest() + assert_(tgt[np.dtype(dt).name] == res) + + # bools do not depend on endianness + random.seed(1234) + val = self.rfunc(0, 2, size=1000, dtype=bool).view(np.int8) + res = hashlib.md5(val).hexdigest() + assert_(tgt[np.dtype(bool).name] == res) + + def test_int64_uint64_corner_case(self): + # When stored in Numpy arrays, `lbnd` is casted + # as np.int64, and `ubnd` is casted as np.uint64. + # Checking whether `lbnd` >= `ubnd` used to be + # done solely via direct comparison, which is incorrect + # because when Numpy tries to compare both numbers, + # it casts both to np.float64 because there is + # no integer superset of np.int64 and np.uint64. However, + # `ubnd` is too large to be represented in np.float64, + # causing it be round down to np.iinfo(np.int64).max, + # leading to a ValueError because `lbnd` now equals + # the new `ubnd`. + + dt = np.int64 + tgt = np.iinfo(np.int64).max + lbnd = np.int64(np.iinfo(np.int64).max) + ubnd = np.uint64(np.iinfo(np.int64).max + 1) + + # None of these function calls should + # generate a ValueError now. + actual = random.randint(lbnd, ubnd, dtype=dt) + assert_equal(actual, tgt) + + def test_respect_dtype_singleton(self): + # See gh-7203 + for dt in self.itype: + lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min + ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1 + + sample = self.rfunc(lbnd, ubnd, dtype=dt) + assert_equal(sample.dtype, np.dtype(dt)) + + for dt in (bool, int, np.long): + lbnd = 0 if dt is bool else np.iinfo(dt).min + ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 + + # gh-7284: Ensure that we get Python data types + sample = self.rfunc(lbnd, ubnd, dtype=dt) + assert_(not hasattr(sample, 'dtype')) + assert_equal(type(sample), dt) + + +class TestRandomDist(object): + # Make sure the random distribution returns the correct value for a + # given seed + + def setup(self): + self.seed = 1234567890 + + def test_rand(self): + random.seed(self.seed) + actual = random.rand(3, 2) + desired = np.array([[0.61879477158567997, 0.59162362775974664], + [0.88868358904449662, 0.89165480011560816], + [0.4575674820298663, 0.7781880808593471]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_randn(self): + random.seed(self.seed) + actual = random.randn(3, 2) + desired = np.array([[1.34016345771863121, 1.73759122771936081], + [1.498988344300628, -0.2286433324536169], + [2.031033998682787, 2.17032494605655257]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_randint(self): + random.seed(self.seed) + actual = random.randint(-99, 99, size=(3, 2)) + desired = np.array([[31, 3], + [-52, 41], + [-48, -66]]) + assert_array_equal(actual, desired) + + def test_random_integers(self): + random.seed(self.seed) + with suppress_warnings() as sup: + w = sup.record(DeprecationWarning) + actual = random.random_integers(-99, 99, size=(3, 2)) + assert_(len(w) == 1) + desired = np.array([[31, 3], + [-52, 41], + [-48, -66]]) + assert_array_equal(actual, desired) + + random.seed(self.seed) + with suppress_warnings() as sup: + w = sup.record(DeprecationWarning) + actual = random.random_integers(198, size=(3, 2)) + assert_(len(w) == 1) + assert_array_equal(actual, desired + 100) + + def test_tomaxint(self): + random.seed(self.seed) + rs = random.RandomState(self.seed) + actual = rs.tomaxint(size=(3, 2)) + if np.iinfo(np.int).max == 2147483647: + desired = np.array([[1328851649, 731237375], + [1270502067, 320041495], + [1908433478, 499156889]], dtype=np.int64) + else: + desired = np.array([[5707374374421908479, 5456764827585442327], + [8196659375100692377, 8224063923314595285], + [4220315081820346526, 7177518203184491332]], + dtype=np.int64) + + assert_equal(actual, desired) + + rs.seed(self.seed) + actual = rs.tomaxint() + assert_equal(actual, desired[0, 0]) + + def test_random_integers_max_int(self): + # Tests whether random_integers can generate the + # maximum allowed Python int that can be converted + # into a C long. Previous implementations of this + # method have thrown an OverflowError when attempting + # to generate this integer. + with suppress_warnings() as sup: + w = sup.record(DeprecationWarning) + actual = random.random_integers(np.iinfo('l').max, + np.iinfo('l').max) + assert_(len(w) == 1) + + desired = np.iinfo('l').max + assert_equal(actual, desired) + + def test_random_integers_deprecated(self): + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + + # DeprecationWarning raised with high == None + assert_raises(DeprecationWarning, + random.random_integers, + np.iinfo('l').max) + + # DeprecationWarning raised with high != None + assert_raises(DeprecationWarning, + random.random_integers, + np.iinfo('l').max, np.iinfo('l').max) + + def test_random_sample(self): + random.seed(self.seed) + actual = random.random_sample((3, 2)) + desired = np.array([[0.61879477158567997, 0.59162362775974664], + [0.88868358904449662, 0.89165480011560816], + [0.4575674820298663, 0.7781880808593471]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_rand_singleton(self): + random.seed(self.seed) + actual = random.rand() + desired = np.array(0.61879477158567997) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_choice_uniform_replace(self): + random.seed(self.seed) + actual = random.choice(4, 4) + desired = np.array([2, 3, 2, 3]) + assert_array_equal(actual, desired) + + def test_choice_nonuniform_replace(self): + random.seed(self.seed) + actual = random.choice(4, 4, p=[0.4, 0.4, 0.1, 0.1]) + desired = np.array([1, 1, 2, 2]) + assert_array_equal(actual, desired) + + def test_choice_uniform_noreplace(self): + random.seed(self.seed) + actual = random.choice(4, 3, replace=False) + desired = np.array([0, 1, 3]) + assert_array_equal(actual, desired) + + def test_choice_nonuniform_noreplace(self): + random.seed(self.seed) + actual = random.choice(4, 3, replace=False, p=[0.1, 0.3, 0.5, 0.1]) + desired = np.array([2, 3, 1]) + assert_array_equal(actual, desired) + + def test_choice_noninteger(self): + random.seed(self.seed) + actual = random.choice(['a', 'b', 'c', 'd'], 4) + desired = np.array(['c', 'd', 'c', 'd']) + assert_array_equal(actual, desired) + + def test_choice_exceptions(self): + sample = random.choice + assert_raises(ValueError, sample, -1, 3) + assert_raises(ValueError, sample, 3., 3) + assert_raises(ValueError, sample, [[1, 2], [3, 4]], 3) + assert_raises(ValueError, sample, [], 3) + assert_raises(ValueError, sample, [1, 2, 3, 4], 3, + p=[[0.25, 0.25], [0.25, 0.25]]) + assert_raises(ValueError, sample, [1, 2], 3, p=[0.4, 0.4, 0.2]) + assert_raises(ValueError, sample, [1, 2], 3, p=[1.1, -0.1]) + assert_raises(ValueError, sample, [1, 2], 3, p=[0.4, 0.4]) + assert_raises(ValueError, sample, [1, 2, 3], 4, replace=False) + # gh-13087 + assert_raises(ValueError, sample, [1, 2, 3], -2, replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1,), replace=False) + assert_raises(ValueError, sample, [1, 2, 3], (-1, 1), replace=False) + assert_raises(ValueError, sample, [1, 2, 3], 2, + replace=False, p=[1, 0, 0]) + + def test_choice_return_shape(self): + p = [0.1, 0.9] + # Check scalar + assert_(np.isscalar(random.choice(2, replace=True))) + assert_(np.isscalar(random.choice(2, replace=False))) + assert_(np.isscalar(random.choice(2, replace=True, p=p))) + assert_(np.isscalar(random.choice(2, replace=False, p=p))) + assert_(np.isscalar(random.choice([1, 2], replace=True))) + assert_(random.choice([None], replace=True) is None) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(random.choice(arr, replace=True) is a) + + # Check 0-d array + s = tuple() + assert_(not np.isscalar(random.choice(2, s, replace=True))) + assert_(not np.isscalar(random.choice(2, s, replace=False))) + assert_(not np.isscalar(random.choice(2, s, replace=True, p=p))) + assert_(not np.isscalar(random.choice(2, s, replace=False, p=p))) + assert_(not np.isscalar(random.choice([1, 2], s, replace=True))) + assert_(random.choice([None], s, replace=True).ndim == 0) + a = np.array([1, 2]) + arr = np.empty(1, dtype=object) + arr[0] = a + assert_(random.choice(arr, s, replace=True).item() is a) + + # Check multi dimensional array + s = (2, 3) + p = [0.1, 0.1, 0.1, 0.1, 0.4, 0.2] + assert_equal(random.choice(6, s, replace=True).shape, s) + assert_equal(random.choice(6, s, replace=False).shape, s) + assert_equal(random.choice(6, s, replace=True, p=p).shape, s) + assert_equal(random.choice(6, s, replace=False, p=p).shape, s) + assert_equal(random.choice(np.arange(6), s, replace=True).shape, s) + + # Check zero-size + assert_equal(random.randint(0, 0, size=(3, 0, 4)).shape, (3, 0, 4)) + assert_equal(random.randint(0, -10, size=0).shape, (0,)) + assert_equal(random.randint(10, 10, size=0).shape, (0,)) + assert_equal(random.choice(0, size=0).shape, (0,)) + assert_equal(random.choice([], size=(0,)).shape, (0,)) + assert_equal(random.choice(['a', 'b'], size=(3, 0, 4)).shape, + (3, 0, 4)) + assert_raises(ValueError, random.choice, [], 10) + + def test_choice_nan_probabilities(self): + a = np.array([42, 1, 2]) + p = [None, None, None] + assert_raises(ValueError, random.choice, a, p=p) + + def test_bytes(self): + random.seed(self.seed) + actual = random.bytes(10) + desired = b'\x82Ui\x9e\xff\x97+Wf\xa5' + assert_equal(actual, desired) + + def test_shuffle(self): + # Test lists, arrays (of various dtypes), and multidimensional versions + # of both, c-contiguous or not: + for conv in [lambda x: np.array([]), + lambda x: x, + lambda x: np.asarray(x).astype(np.int8), + lambda x: np.asarray(x).astype(np.float32), + lambda x: np.asarray(x).astype(np.complex64), + lambda x: np.asarray(x).astype(object), + lambda x: [(i, i) for i in x], + lambda x: np.asarray([[i, i] for i in x]), + lambda x: np.vstack([x, x]).T, + # gh-11442 + lambda x: (np.asarray([(i, i) for i in x], + [("a", int), ("b", int)]) + .view(np.recarray)), + # gh-4270 + lambda x: np.asarray([(i, i) for i in x], + [("a", object, 1), + ("b", np.int32, 1)])]: + random.seed(self.seed) + alist = conv([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) + random.shuffle(alist) + actual = alist + desired = conv([0, 1, 9, 6, 2, 4, 5, 8, 7, 3]) + assert_array_equal(actual, desired) + + def test_permutation(self): + random.seed(self.seed) + alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] + actual = random.permutation(alist) + desired = [0, 1, 9, 6, 2, 4, 5, 8, 7, 3] + assert_array_equal(actual, desired) + + random.seed(self.seed) + arr_2d = np.atleast_2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).T + actual = random.permutation(arr_2d) + assert_array_equal(actual, np.atleast_2d(desired).T) + + def test_shuffle_masked(self): + # gh-3263 + a = np.ma.masked_values(np.reshape(range(20), (5, 4)) % 3 - 1, -1) + b = np.ma.masked_values(np.arange(20) % 3 - 1, -1) + a_orig = a.copy() + b_orig = b.copy() + for i in range(50): + random.shuffle(a) + assert_equal( + sorted(a.data[~a.mask]), sorted(a_orig.data[~a_orig.mask])) + random.shuffle(b) + assert_equal( + sorted(b.data[~b.mask]), sorted(b_orig.data[~b_orig.mask])) + + def test_beta(self): + random.seed(self.seed) + actual = random.beta(.1, .9, size=(3, 2)) + desired = np.array( + [[1.45341850513746058e-02, 5.31297615662868145e-04], + [1.85366619058432324e-06, 4.19214516800110563e-03], + [1.58405155108498093e-04, 1.26252891949397652e-04]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_binomial(self): + random.seed(self.seed) + actual = random.binomial(100.123, .456, size=(3, 2)) + desired = np.array([[37, 43], + [42, 48], + [46, 45]]) + assert_array_equal(actual, desired) + + random.seed(self.seed) + actual = random.binomial(100.123, .456) + desired = 37 + assert_array_equal(actual, desired) + + def test_chisquare(self): + random.seed(self.seed) + actual = random.chisquare(50, size=(3, 2)) + desired = np.array([[63.87858175501090585, 68.68407748911370447], + [65.77116116901505904, 47.09686762438974483], + [72.3828403199695174, 74.18408615260374006]]) + assert_array_almost_equal(actual, desired, decimal=13) + + def test_dirichlet(self): + random.seed(self.seed) + alpha = np.array([51.72840233779265162, 39.74494232180943953]) + actual = random.dirichlet(alpha, size=(3, 2)) + desired = np.array([[[0.54539444573611562, 0.45460555426388438], + [0.62345816822039413, 0.37654183177960598]], + [[0.55206000085785778, 0.44793999914214233], + [0.58964023305154301, 0.41035976694845688]], + [[0.59266909280647828, 0.40733090719352177], + [0.56974431743975207, 0.43025568256024799]]]) + assert_array_almost_equal(actual, desired, decimal=15) + + random.seed(self.seed) + alpha = np.array([51.72840233779265162, 39.74494232180943953]) + actual = random.dirichlet(alpha) + assert_array_almost_equal(actual, desired[0, 0], decimal=15) + + def test_dirichlet_size(self): + # gh-3173 + p = np.array([51.72840233779265162, 39.74494232180943953]) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, np.array((2, 2))).shape, (2, 2, 2)) + + assert_raises(TypeError, random.dirichlet, p, float(1)) + + def test_dirichlet_bad_alpha(self): + # gh-2089 + alpha = np.array([5.4e-01, -1.0e-16]) + assert_raises(ValueError, random.dirichlet, alpha) + + def test_exponential(self): + random.seed(self.seed) + actual = random.exponential(1.1234, size=(3, 2)) + desired = np.array([[1.08342649775011624, 1.00607889924557314], + [2.46628830085216721, 2.49668106809923884], + [0.68717433461363442, 1.69175666993575979]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_exponential_0(self): + assert_equal(random.exponential(scale=0), 0) + assert_raises(ValueError, random.exponential, scale=-0.) + + def test_f(self): + random.seed(self.seed) + actual = random.f(12, 77, size=(3, 2)) + desired = np.array([[1.21975394418575878, 1.75135759791559775], + [1.44803115017146489, 1.22108959480396262], + [1.02176975757740629, 1.34431827623300415]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_gamma(self): + random.seed(self.seed) + actual = random.gamma(5, 3, size=(3, 2)) + desired = np.array([[24.60509188649287182, 28.54993563207210627], + [26.13476110204064184, 12.56988482927716078], + [31.71863275789960568, 33.30143302795922011]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_gamma_0(self): + assert_equal(random.gamma(shape=0, scale=0), 0) + assert_raises(ValueError, random.gamma, shape=-0., scale=-0.) + + def test_geometric(self): + random.seed(self.seed) + actual = random.geometric(.123456789, size=(3, 2)) + desired = np.array([[8, 7], + [17, 17], + [5, 12]]) + assert_array_equal(actual, desired) + + def test_gumbel(self): + random.seed(self.seed) + actual = random.gumbel(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[0.19591898743416816, 0.34405539668096674], + [-1.4492522252274278, -1.47374816298446865], + [1.10651090478803416, -0.69535848626236174]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_gumbel_0(self): + assert_equal(random.gumbel(scale=0), 0) + assert_raises(ValueError, random.gumbel, scale=-0.) + + def test_hypergeometric(self): + random.seed(self.seed) + actual = random.hypergeometric(10.1, 5.5, 14, size=(3, 2)) + desired = np.array([[10, 10], + [10, 10], + [9, 9]]) + assert_array_equal(actual, desired) + + # Test nbad = 0 + actual = random.hypergeometric(5, 0, 3, size=4) + desired = np.array([3, 3, 3, 3]) + assert_array_equal(actual, desired) + + actual = random.hypergeometric(15, 0, 12, size=4) + desired = np.array([12, 12, 12, 12]) + assert_array_equal(actual, desired) + + # Test ngood = 0 + actual = random.hypergeometric(0, 5, 3, size=4) + desired = np.array([0, 0, 0, 0]) + assert_array_equal(actual, desired) + + actual = random.hypergeometric(0, 15, 12, size=4) + desired = np.array([0, 0, 0, 0]) + assert_array_equal(actual, desired) + + def test_laplace(self): + random.seed(self.seed) + actual = random.laplace(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[0.66599721112760157, 0.52829452552221945], + [3.12791959514407125, 3.18202813572992005], + [-0.05391065675859356, 1.74901336242837324]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_laplace_0(self): + assert_equal(random.laplace(scale=0), 0) + assert_raises(ValueError, random.laplace, scale=-0.) + + def test_logistic(self): + random.seed(self.seed) + actual = random.logistic(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[1.09232835305011444, 0.8648196662399954], + [4.27818590694950185, 4.33897006346929714], + [-0.21682183359214885, 2.63373365386060332]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_lognormal(self): + random.seed(self.seed) + actual = random.lognormal(mean=.123456789, sigma=2.0, size=(3, 2)) + desired = np.array([[16.50698631688883822, 36.54846706092654784], + [22.67886599981281748, 0.71617561058995771], + [65.72798501792723869, 86.84341601437161273]]) + assert_array_almost_equal(actual, desired, decimal=13) + + def test_lognormal_0(self): + assert_equal(random.lognormal(sigma=0), 1) + assert_raises(ValueError, random.lognormal, sigma=-0.) + + def test_logseries(self): + random.seed(self.seed) + actual = random.logseries(p=.923456789, size=(3, 2)) + desired = np.array([[2, 2], + [6, 17], + [3, 6]]) + assert_array_equal(actual, desired) + + def test_multinomial(self): + random.seed(self.seed) + actual = random.multinomial(20, [1/6.]*6, size=(3, 2)) + desired = np.array([[[4, 3, 5, 4, 2, 2], + [5, 2, 8, 2, 2, 1]], + [[3, 4, 3, 6, 0, 4], + [2, 1, 4, 3, 6, 4]], + [[4, 4, 2, 5, 2, 3], + [4, 3, 4, 2, 3, 4]]]) + assert_array_equal(actual, desired) + + def test_multivariate_normal(self): + random.seed(self.seed) + mean = (.123456789, 10) + cov = [[1, 0], [0, 1]] + size = (3, 2) + actual = random.multivariate_normal(mean, cov, size) + desired = np.array([[[1.463620246718631, 11.73759122771936], + [1.622445133300628, 9.771356667546383]], + [[2.154490787682787, 12.170324946056553], + [1.719909438201865, 9.230548443648306]], + [[0.689515026297799, 9.880729819607714], + [-0.023054015651998, 9.201096623542879]]]) + + assert_array_almost_equal(actual, desired, decimal=15) + + # Check for default size, was raising deprecation warning + actual = random.multivariate_normal(mean, cov) + desired = np.array([0.895289569463708, 9.17180864067987]) + assert_array_almost_equal(actual, desired, decimal=15) + + # Check that non positive-semidefinite covariance warns with + # RuntimeWarning + mean = [0, 0] + cov = [[1, 2], [2, 1]] + assert_warns(RuntimeWarning, random.multivariate_normal, mean, cov) + + # and that it doesn't warn with RuntimeWarning check_valid='ignore' + assert_no_warnings(random.multivariate_normal, mean, cov, + check_valid='ignore') + + # and that it raises with RuntimeWarning check_valid='raises' + assert_raises(ValueError, random.multivariate_normal, mean, cov, + check_valid='raise') + + cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) + with suppress_warnings() as sup: + random.multivariate_normal(mean, cov) + w = sup.record(RuntimeWarning) + assert len(w) == 0 + + mu = np.zeros(2) + cov = np.eye(2) + assert_raises(ValueError, random.multivariate_normal, mean, + cov, check_valid='other') + assert_raises(ValueError, random.multivariate_normal, + np.zeros((2, 1, 1)), cov) + assert_raises(ValueError, random.multivariate_normal, + mu, np.empty((3, 2))) + assert_raises(ValueError, random.multivariate_normal, + mu, np.eye(3)) + + def test_negative_binomial(self): + random.seed(self.seed) + actual = random.negative_binomial(n=100, p=.12345, size=(3, 2)) + desired = np.array([[848, 841], + [892, 611], + [779, 647]]) + assert_array_equal(actual, desired) + + def test_noncentral_chisquare(self): + random.seed(self.seed) + actual = random.noncentral_chisquare(df=5, nonc=5, size=(3, 2)) + desired = np.array([[23.91905354498517511, 13.35324692733826346], + [31.22452661329736401, 16.60047399466177254], + [5.03461598262724586, 17.94973089023519464]]) + assert_array_almost_equal(actual, desired, decimal=14) + + actual = random.noncentral_chisquare(df=.5, nonc=.2, size=(3, 2)) + desired = np.array([[1.47145377828516666, 0.15052899268012659], + [0.00943803056963588, 1.02647251615666169], + [0.332334982684171, 0.15451287602753125]]) + assert_array_almost_equal(actual, desired, decimal=14) + + random.seed(self.seed) + actual = random.noncentral_chisquare(df=5, nonc=0, size=(3, 2)) + desired = np.array([[9.597154162763948, 11.725484450296079], + [10.413711048138335, 3.694475922923986], + [13.484222138963087, 14.377255424602957]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_noncentral_f(self): + random.seed(self.seed) + actual = random.noncentral_f(dfnum=5, dfden=2, nonc=1, + size=(3, 2)) + desired = np.array([[1.40598099674926669, 0.34207973179285761], + [3.57715069265772545, 7.92632662577829805], + [0.43741599463544162, 1.1774208752428319]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_normal(self): + random.seed(self.seed) + actual = random.normal(loc=.123456789, scale=2.0, size=(3, 2)) + desired = np.array([[2.80378370443726244, 3.59863924443872163], + [3.121433477601256, -0.33382987590723379], + [4.18552478636557357, 4.46410668111310471]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_normal_0(self): + assert_equal(random.normal(scale=0), 0) + assert_raises(ValueError, random.normal, scale=-0.) + + def test_pareto(self): + random.seed(self.seed) + actual = random.pareto(a=.123456789, size=(3, 2)) + desired = np.array( + [[2.46852460439034849e+03, 1.41286880810518346e+03], + [5.28287797029485181e+07, 6.57720981047328785e+07], + [1.40840323350391515e+02, 1.98390255135251704e+05]]) + # For some reason on 32-bit x86 Ubuntu 12.10 the [1, 0] entry in this + # matrix differs by 24 nulps. Discussion: + # https://mail.python.org/pipermail/numpy-discussion/2012-September/063801.html + # Consensus is that this is probably some gcc quirk that affects + # rounding but not in any important way, so we just use a looser + # tolerance on this test: + np.testing.assert_array_almost_equal_nulp(actual, desired, nulp=30) + + def test_poisson(self): + random.seed(self.seed) + actual = random.poisson(lam=.123456789, size=(3, 2)) + desired = np.array([[0, 0], + [1, 0], + [0, 0]]) + assert_array_equal(actual, desired) + + def test_poisson_exceptions(self): + lambig = np.iinfo('l').max + lamneg = -1 + assert_raises(ValueError, random.poisson, lamneg) + assert_raises(ValueError, random.poisson, [lamneg]*10) + assert_raises(ValueError, random.poisson, lambig) + assert_raises(ValueError, random.poisson, [lambig]*10) + + def test_power(self): + random.seed(self.seed) + actual = random.power(a=.123456789, size=(3, 2)) + desired = np.array([[0.02048932883240791, 0.01424192241128213], + [0.38446073748535298, 0.39499689943484395], + [0.00177699707563439, 0.13115505880863756]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_rayleigh(self): + random.seed(self.seed) + actual = random.rayleigh(scale=10, size=(3, 2)) + desired = np.array([[13.8882496494248393, 13.383318339044731], + [20.95413364294492098, 21.08285015800712614], + [11.06066537006854311, 17.35468505778271009]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_rayleigh_0(self): + assert_equal(random.rayleigh(scale=0), 0) + assert_raises(ValueError, random.rayleigh, scale=-0.) + + def test_standard_cauchy(self): + random.seed(self.seed) + actual = random.standard_cauchy(size=(3, 2)) + desired = np.array([[0.77127660196445336, -6.55601161955910605], + [0.93582023391158309, -2.07479293013759447], + [-4.74601644297011926, 0.18338989290760804]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_exponential(self): + random.seed(self.seed) + actual = random.standard_exponential(size=(3, 2)) + desired = np.array([[0.96441739162374596, 0.89556604882105506], + [2.1953785836319808, 2.22243285392490542], + [0.6116915921431676, 1.50592546727413201]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_gamma(self): + random.seed(self.seed) + actual = random.standard_gamma(shape=3, size=(3, 2)) + desired = np.array([[5.50841531318455058, 6.62953470301903103], + [5.93988484943779227, 2.31044849402133989], + [7.54838614231317084, 8.012756093271868]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_standard_gamma_0(self): + assert_equal(random.standard_gamma(shape=0), 0) + assert_raises(ValueError, random.standard_gamma, shape=-0.) + + def test_standard_normal(self): + random.seed(self.seed) + actual = random.standard_normal(size=(3, 2)) + desired = np.array([[1.34016345771863121, 1.73759122771936081], + [1.498988344300628, -0.2286433324536169], + [2.031033998682787, 2.17032494605655257]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_randn_singleton(self): + random.seed(self.seed) + actual = random.randn() + desired = np.array(1.34016345771863121) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_standard_t(self): + random.seed(self.seed) + actual = random.standard_t(df=10, size=(3, 2)) + desired = np.array([[0.97140611862659965, -0.08830486548450577], + [1.36311143689505321, -0.55317463909867071], + [-0.18473749069684214, 0.61181537341755321]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_triangular(self): + random.seed(self.seed) + actual = random.triangular(left=5.12, mode=10.23, right=20.34, + size=(3, 2)) + desired = np.array([[12.68117178949215784, 12.4129206149193152], + [16.20131377335158263, 16.25692138747600524], + [11.20400690911820263, 14.4978144835829923]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_uniform(self): + random.seed(self.seed) + actual = random.uniform(low=1.23, high=10.54, size=(3, 2)) + desired = np.array([[6.99097932346268003, 6.73801597444323974], + [9.50364421400426274, 9.53130618907631089], + [5.48995325769805476, 8.47493103280052118]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_uniform_range_bounds(self): + fmin = np.finfo('float').min + fmax = np.finfo('float').max + + func = random.uniform + assert_raises(OverflowError, func, -np.inf, 0) + assert_raises(OverflowError, func, 0, np.inf) + assert_raises(OverflowError, func, fmin, fmax) + assert_raises(OverflowError, func, [-np.inf], [0]) + assert_raises(OverflowError, func, [0], [np.inf]) + + # (fmax / 1e17) - fmin is within range, so this should not throw + # account for i386 extended precision DBL_MAX / 1e17 + DBL_MAX > + # DBL_MAX by increasing fmin a bit + random.uniform(low=np.nextafter(fmin, 1), high=fmax / 1e17) + + def test_scalar_exception_propagation(self): + # Tests that exceptions are correctly propagated in distributions + # when called with objects that throw exceptions when converted to + # scalars. + # + # Regression test for gh: 8865 + + class ThrowingFloat(np.ndarray): + def __float__(self): + raise TypeError + + throwing_float = np.array(1.0).view(ThrowingFloat) + assert_raises(TypeError, random.uniform, throwing_float, + throwing_float) + + class ThrowingInteger(np.ndarray): + def __int__(self): + raise TypeError + + throwing_int = np.array(1).view(ThrowingInteger) + assert_raises(TypeError, random.hypergeometric, throwing_int, 1, 1) + + def test_vonmises(self): + random.seed(self.seed) + actual = random.vonmises(mu=1.23, kappa=1.54, size=(3, 2)) + desired = np.array([[2.28567572673902042, 2.89163838442285037], + [0.38198375564286025, 2.57638023113890746], + [1.19153771588353052, 1.83509849681825354]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_vonmises_small(self): + # check infinite loop, gh-4720 + random.seed(self.seed) + r = random.vonmises(mu=0., kappa=1.1e-8, size=10**6) + np.testing.assert_(np.isfinite(r).all()) + + def test_wald(self): + random.seed(self.seed) + actual = random.wald(mean=1.23, scale=1.54, size=(3, 2)) + desired = np.array([[3.82935265715889983, 5.13125249184285526], + [0.35045403618358717, 1.50832396872003538], + [0.24124319895843183, 0.22031101461955038]]) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_weibull(self): + random.seed(self.seed) + actual = random.weibull(a=1.23, size=(3, 2)) + desired = np.array([[0.97097342648766727, 0.91422896443565516], + [1.89517770034962929, 1.91414357960479564], + [0.67057783752390987, 1.39494046635066793]]) + assert_array_almost_equal(actual, desired, decimal=15) + + def test_weibull_0(self): + random.seed(self.seed) + assert_equal(random.weibull(a=0, size=12), np.zeros(12)) + assert_raises(ValueError, random.weibull, a=-0.) + + def test_zipf(self): + random.seed(self.seed) + actual = random.zipf(a=1.23, size=(3, 2)) + desired = np.array([[66, 29], + [1, 1], + [3, 13]]) + assert_array_equal(actual, desired) + + +class TestBroadcast(object): + # tests that functions that broadcast behave + # correctly when presented with non-scalar arguments + def setup(self): + self.seed = 123456789 + + def setSeed(self): + random.seed(self.seed) + + # TODO: Include test for randint once it can broadcast + # Can steal the test written in PR #6938 + + def test_uniform(self): + low = [0] + high = [1] + uniform = random.uniform + desired = np.array([0.53283302478975902, + 0.53413660089041659, + 0.50955303552646702]) + + self.setSeed() + actual = uniform(low * 3, high) + assert_array_almost_equal(actual, desired, decimal=14) + + self.setSeed() + actual = uniform(low, high * 3) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_normal(self): + loc = [0] + scale = [1] + bad_scale = [-1] + normal = random.normal + desired = np.array([2.2129019979039612, + 2.1283977976520019, + 1.8417114045748335]) + + self.setSeed() + actual = normal(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, normal, loc * 3, bad_scale) + + self.setSeed() + actual = normal(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, normal, loc, bad_scale * 3) + + def test_beta(self): + a = [1] + b = [2] + bad_a = [-1] + bad_b = [-2] + beta = random.beta + desired = np.array([0.19843558305989056, + 0.075230336409423643, + 0.24976865978980844]) + + self.setSeed() + actual = beta(a * 3, b) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, beta, bad_a * 3, b) + assert_raises(ValueError, beta, a * 3, bad_b) + + self.setSeed() + actual = beta(a, b * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, beta, bad_a, b * 3) + assert_raises(ValueError, beta, a, bad_b * 3) + + def test_exponential(self): + scale = [1] + bad_scale = [-1] + exponential = random.exponential + desired = np.array([0.76106853658845242, + 0.76386282278691653, + 0.71243813125891797]) + + self.setSeed() + actual = exponential(scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, exponential, bad_scale * 3) + + def test_standard_gamma(self): + shape = [1] + bad_shape = [-1] + std_gamma = random.standard_gamma + desired = np.array([0.76106853658845242, + 0.76386282278691653, + 0.71243813125891797]) + + self.setSeed() + actual = std_gamma(shape * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, std_gamma, bad_shape * 3) + + def test_gamma(self): + shape = [1] + scale = [2] + bad_shape = [-1] + bad_scale = [-2] + gamma = random.gamma + desired = np.array([1.5221370731769048, + 1.5277256455738331, + 1.4248762625178359]) + + self.setSeed() + actual = gamma(shape * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gamma, bad_shape * 3, scale) + assert_raises(ValueError, gamma, shape * 3, bad_scale) + + self.setSeed() + actual = gamma(shape, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gamma, bad_shape, scale * 3) + assert_raises(ValueError, gamma, shape, bad_scale * 3) + + def test_f(self): + dfnum = [1] + dfden = [2] + bad_dfnum = [-1] + bad_dfden = [-2] + f = random.f + desired = np.array([0.80038951638264799, + 0.86768719635363512, + 2.7251095168386801]) + + self.setSeed() + actual = f(dfnum * 3, dfden) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, f, bad_dfnum * 3, dfden) + assert_raises(ValueError, f, dfnum * 3, bad_dfden) + + self.setSeed() + actual = f(dfnum, dfden * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, f, bad_dfnum, dfden * 3) + assert_raises(ValueError, f, dfnum, bad_dfden * 3) + + def test_noncentral_f(self): + dfnum = [2] + dfden = [3] + nonc = [4] + bad_dfnum = [0] + bad_dfden = [-1] + bad_nonc = [-2] + nonc_f = random.noncentral_f + desired = np.array([9.1393943263705211, + 13.025456344595602, + 8.8018098359100545]) + + self.setSeed() + actual = nonc_f(dfnum * 3, dfden, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_f, bad_dfnum * 3, dfden, nonc) + assert_raises(ValueError, nonc_f, dfnum * 3, bad_dfden, nonc) + assert_raises(ValueError, nonc_f, dfnum * 3, dfden, bad_nonc) + + self.setSeed() + actual = nonc_f(dfnum, dfden * 3, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_f, bad_dfnum, dfden * 3, nonc) + assert_raises(ValueError, nonc_f, dfnum, bad_dfden * 3, nonc) + assert_raises(ValueError, nonc_f, dfnum, dfden * 3, bad_nonc) + + self.setSeed() + actual = nonc_f(dfnum, dfden, nonc * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_f, bad_dfnum, dfden, nonc * 3) + assert_raises(ValueError, nonc_f, dfnum, bad_dfden, nonc * 3) + assert_raises(ValueError, nonc_f, dfnum, dfden, bad_nonc * 3) + + def test_noncentral_f_small_df(self): + self.setSeed() + desired = np.array([6.869638627492048, 0.785880199263955]) + actual = random.noncentral_f(0.9, 0.9, 2, size=2) + assert_array_almost_equal(actual, desired, decimal=14) + + def test_chisquare(self): + df = [1] + bad_df = [-1] + chisquare = random.chisquare + desired = np.array([0.57022801133088286, + 0.51947702108840776, + 0.1320969254923558]) + + self.setSeed() + actual = chisquare(df * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, chisquare, bad_df * 3) + + def test_noncentral_chisquare(self): + df = [1] + nonc = [2] + bad_df = [-1] + bad_nonc = [-2] + nonc_chi = random.noncentral_chisquare + desired = np.array([9.0015599467913763, + 4.5804135049718742, + 6.0872302432834564]) + + self.setSeed() + actual = nonc_chi(df * 3, nonc) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_chi, bad_df * 3, nonc) + assert_raises(ValueError, nonc_chi, df * 3, bad_nonc) + + self.setSeed() + actual = nonc_chi(df, nonc * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, nonc_chi, bad_df, nonc * 3) + assert_raises(ValueError, nonc_chi, df, bad_nonc * 3) + + def test_standard_t(self): + df = [1] + bad_df = [-1] + t = random.standard_t + desired = np.array([3.0702872575217643, + 5.8560725167361607, + 1.0274791436474273]) + + self.setSeed() + actual = t(df * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, t, bad_df * 3) + + def test_vonmises(self): + mu = [2] + kappa = [1] + bad_kappa = [-1] + vonmises = random.vonmises + desired = np.array([2.9883443664201312, + -2.7064099483995943, + -1.8672476700665914]) + + self.setSeed() + actual = vonmises(mu * 3, kappa) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, vonmises, mu * 3, bad_kappa) + + self.setSeed() + actual = vonmises(mu, kappa * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, vonmises, mu, bad_kappa * 3) + + def test_pareto(self): + a = [1] + bad_a = [-1] + pareto = random.pareto + desired = np.array([1.1405622680198362, + 1.1465519762044529, + 1.0389564467453547]) + + self.setSeed() + actual = pareto(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, pareto, bad_a * 3) + + def test_weibull(self): + a = [1] + bad_a = [-1] + weibull = random.weibull + desired = np.array([0.76106853658845242, + 0.76386282278691653, + 0.71243813125891797]) + + self.setSeed() + actual = weibull(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, weibull, bad_a * 3) + + def test_power(self): + a = [1] + bad_a = [-1] + power = random.power + desired = np.array([0.53283302478975902, + 0.53413660089041659, + 0.50955303552646702]) + + self.setSeed() + actual = power(a * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, power, bad_a * 3) + + def test_laplace(self): + loc = [0] + scale = [1] + bad_scale = [-1] + laplace = random.laplace + desired = np.array([0.067921356028507157, + 0.070715642226971326, + 0.019290950698972624]) + + self.setSeed() + actual = laplace(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, laplace, loc * 3, bad_scale) + + self.setSeed() + actual = laplace(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, laplace, loc, bad_scale * 3) + + def test_gumbel(self): + loc = [0] + scale = [1] + bad_scale = [-1] + gumbel = random.gumbel + desired = np.array([0.2730318639556768, + 0.26936705726291116, + 0.33906220393037939]) + + self.setSeed() + actual = gumbel(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gumbel, loc * 3, bad_scale) + + self.setSeed() + actual = gumbel(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, gumbel, loc, bad_scale * 3) + + def test_logistic(self): + loc = [0] + scale = [1] + bad_scale = [-1] + logistic = random.logistic + desired = np.array([0.13152135837586171, + 0.13675915696285773, + 0.038216792802833396]) + + self.setSeed() + actual = logistic(loc * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, logistic, loc * 3, bad_scale) + + self.setSeed() + actual = logistic(loc, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, logistic, loc, bad_scale * 3) + + def test_lognormal(self): + mean = [0] + sigma = [1] + bad_sigma = [-1] + lognormal = random.lognormal + desired = np.array([9.1422086044848427, + 8.4013952870126261, + 6.3073234116578671]) + + self.setSeed() + actual = lognormal(mean * 3, sigma) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, lognormal, mean * 3, bad_sigma) + + self.setSeed() + actual = lognormal(mean, sigma * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, lognormal, mean, bad_sigma * 3) + + def test_rayleigh(self): + scale = [1] + bad_scale = [-1] + rayleigh = random.rayleigh + desired = np.array([1.2337491937897689, + 1.2360119924878694, + 1.1936818095781789]) + + self.setSeed() + actual = rayleigh(scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, rayleigh, bad_scale * 3) + + def test_wald(self): + mean = [0.5] + scale = [1] + bad_mean = [0] + bad_scale = [-2] + wald = random.wald + desired = np.array([0.11873681120271318, + 0.12450084820795027, + 0.9096122728408238]) + + self.setSeed() + actual = wald(mean * 3, scale) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, wald, bad_mean * 3, scale) + assert_raises(ValueError, wald, mean * 3, bad_scale) + + self.setSeed() + actual = wald(mean, scale * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, wald, bad_mean, scale * 3) + assert_raises(ValueError, wald, mean, bad_scale * 3) + assert_raises(ValueError, wald, 0.0, 1) + assert_raises(ValueError, wald, 0.5, 0.0) + + def test_triangular(self): + left = [1] + right = [3] + mode = [2] + bad_left_one = [3] + bad_mode_one = [4] + bad_left_two, bad_mode_two = right * 2 + triangular = random.triangular + desired = np.array([2.03339048710429, + 2.0347400359389356, + 2.0095991069536208]) + + self.setSeed() + actual = triangular(left * 3, mode, right) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one * 3, mode, right) + assert_raises(ValueError, triangular, left * 3, bad_mode_one, right) + assert_raises(ValueError, triangular, bad_left_two * 3, bad_mode_two, + right) + + self.setSeed() + actual = triangular(left, mode * 3, right) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one, mode * 3, right) + assert_raises(ValueError, triangular, left, bad_mode_one * 3, right) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two * 3, + right) + + self.setSeed() + actual = triangular(left, mode, right * 3) + assert_array_almost_equal(actual, desired, decimal=14) + assert_raises(ValueError, triangular, bad_left_one, mode, right * 3) + assert_raises(ValueError, triangular, left, bad_mode_one, right * 3) + assert_raises(ValueError, triangular, bad_left_two, bad_mode_two, + right * 3) + + assert_raises(ValueError, triangular, 10., 0., 20.) + assert_raises(ValueError, triangular, 10., 25., 20.) + assert_raises(ValueError, triangular, 10., 10., 10.) + + def test_binomial(self): + n = [1] + p = [0.5] + bad_n = [-1] + bad_p_one = [-1] + bad_p_two = [1.5] + binom = random.binomial + desired = np.array([1, 1, 1]) + + self.setSeed() + actual = binom(n * 3, p) + assert_array_equal(actual, desired) + assert_raises(ValueError, binom, bad_n * 3, p) + assert_raises(ValueError, binom, n * 3, bad_p_one) + assert_raises(ValueError, binom, n * 3, bad_p_two) + + self.setSeed() + actual = binom(n, p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, binom, bad_n, p * 3) + assert_raises(ValueError, binom, n, bad_p_one * 3) + assert_raises(ValueError, binom, n, bad_p_two * 3) + + def test_negative_binomial(self): + n = [1] + p = [0.5] + bad_n = [-1] + bad_p_one = [-1] + bad_p_two = [1.5] + neg_binom = random.negative_binomial + desired = np.array([1, 0, 1]) + + self.setSeed() + actual = neg_binom(n * 3, p) + assert_array_equal(actual, desired) + assert_raises(ValueError, neg_binom, bad_n * 3, p) + assert_raises(ValueError, neg_binom, n * 3, bad_p_one) + assert_raises(ValueError, neg_binom, n * 3, bad_p_two) + + self.setSeed() + actual = neg_binom(n, p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, neg_binom, bad_n, p * 3) + assert_raises(ValueError, neg_binom, n, bad_p_one * 3) + assert_raises(ValueError, neg_binom, n, bad_p_two * 3) + + def test_poisson(self): + max_lam = random.RandomState().poisson_lam_max + + lam = [1] + bad_lam_one = [-1] + bad_lam_two = [max_lam * 2] + poisson = random.poisson + desired = np.array([1, 1, 0]) + + self.setSeed() + actual = poisson(lam * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, poisson, bad_lam_one * 3) + assert_raises(ValueError, poisson, bad_lam_two * 3) + + def test_zipf(self): + a = [2] + bad_a = [0] + zipf = random.zipf + desired = np.array([2, 2, 1]) + + self.setSeed() + actual = zipf(a * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, zipf, bad_a * 3) + with np.errstate(invalid='ignore'): + assert_raises(ValueError, zipf, np.nan) + assert_raises(ValueError, zipf, [0, 0, np.nan]) + + def test_geometric(self): + p = [0.5] + bad_p_one = [-1] + bad_p_two = [1.5] + geom = random.geometric + desired = np.array([2, 2, 2]) + + self.setSeed() + actual = geom(p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, geom, bad_p_one * 3) + assert_raises(ValueError, geom, bad_p_two * 3) + + def test_hypergeometric(self): + ngood = [1] + nbad = [2] + nsample = [2] + bad_ngood = [-1] + bad_nbad = [-2] + bad_nsample_one = [0] + bad_nsample_two = [4] + hypergeom = random.hypergeometric + desired = np.array([1, 1, 1]) + + self.setSeed() + actual = hypergeom(ngood * 3, nbad, nsample) + assert_array_equal(actual, desired) + assert_raises(ValueError, hypergeom, bad_ngood * 3, nbad, nsample) + assert_raises(ValueError, hypergeom, ngood * 3, bad_nbad, nsample) + assert_raises(ValueError, hypergeom, ngood * 3, nbad, bad_nsample_one) + assert_raises(ValueError, hypergeom, ngood * 3, nbad, bad_nsample_two) + + self.setSeed() + actual = hypergeom(ngood, nbad * 3, nsample) + assert_array_equal(actual, desired) + assert_raises(ValueError, hypergeom, bad_ngood, nbad * 3, nsample) + assert_raises(ValueError, hypergeom, ngood, bad_nbad * 3, nsample) + assert_raises(ValueError, hypergeom, ngood, nbad * 3, bad_nsample_one) + assert_raises(ValueError, hypergeom, ngood, nbad * 3, bad_nsample_two) + + self.setSeed() + actual = hypergeom(ngood, nbad, nsample * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, hypergeom, bad_ngood, nbad, nsample * 3) + assert_raises(ValueError, hypergeom, ngood, bad_nbad, nsample * 3) + assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_one * 3) + assert_raises(ValueError, hypergeom, ngood, nbad, bad_nsample_two * 3) + + assert_raises(ValueError, hypergeom, 10, 10, 25) + + def test_logseries(self): + p = [0.5] + bad_p_one = [2] + bad_p_two = [-1] + logseries = random.logseries + desired = np.array([1, 1, 1]) + + self.setSeed() + actual = logseries(p * 3) + assert_array_equal(actual, desired) + assert_raises(ValueError, logseries, bad_p_one * 3) + assert_raises(ValueError, logseries, bad_p_two * 3) + + +class TestThread(object): + # make sure each state produces the same sequence even in threads + def setup(self): + self.seeds = range(4) + + def check_function(self, function, sz): + from threading import Thread + + out1 = np.empty((len(self.seeds),) + sz) + out2 = np.empty((len(self.seeds),) + sz) + + # threaded generation + t = [Thread(target=function, args=(random.RandomState(s), o)) + for s, o in zip(self.seeds, out1)] + [x.start() for x in t] + [x.join() for x in t] + + # the same serial + for s, o in zip(self.seeds, out2): + function(random.RandomState(s), o) + + # these platforms change x87 fpu precision mode in threads + if np.intp().dtype.itemsize == 4 and sys.platform == "win32": + assert_array_almost_equal(out1, out2) + else: + assert_array_equal(out1, out2) + + def test_normal(self): + def gen_random(state, out): + out[...] = state.normal(size=10000) + self.check_function(gen_random, sz=(10000,)) + + def test_exp(self): + def gen_random(state, out): + out[...] = state.exponential(scale=np.ones((100, 1000))) + self.check_function(gen_random, sz=(100, 1000)) + + def test_multinomial(self): + def gen_random(state, out): + out[...] = state.multinomial(10, [1/6.]*6, size=10000) + self.check_function(gen_random, sz=(10000, 6)) + + +# See Issue #4263 +class TestSingleEltArrayInput(object): + def setup(self): + self.argOne = np.array([2]) + self.argTwo = np.array([3]) + self.argThree = np.array([4]) + self.tgtShape = (1,) + + def test_one_arg_funcs(self): + funcs = (random.exponential, random.standard_gamma, + random.chisquare, random.standard_t, + random.pareto, random.weibull, + random.power, random.rayleigh, + random.poisson, random.zipf, + random.geometric, random.logseries) + + probfuncs = (random.geometric, random.logseries) + + for func in funcs: + if func in probfuncs: # p < 1.0 + out = func(np.array([0.5])) + + else: + out = func(self.argOne) + + assert_equal(out.shape, self.tgtShape) + + def test_two_arg_funcs(self): + funcs = (random.uniform, random.normal, + random.beta, random.gamma, + random.f, random.noncentral_chisquare, + random.vonmises, random.laplace, + random.gumbel, random.logistic, + random.lognormal, random.wald, + random.binomial, random.negative_binomial) + + probfuncs = (random.binomial, random.negative_binomial) + + for func in funcs: + if func in probfuncs: # p <= 1 + argTwo = np.array([0.5]) + + else: + argTwo = self.argTwo + + out = func(self.argOne, argTwo) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne[0], argTwo) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne, argTwo[0]) + assert_equal(out.shape, self.tgtShape) + +# TODO: Uncomment once randint can broadcast arguments +# def test_randint(self): +# itype = [bool, np.int8, np.uint8, np.int16, np.uint16, +# np.int32, np.uint32, np.int64, np.uint64] +# func = random.randint +# high = np.array([1]) +# low = np.array([0]) +# +# for dt in itype: +# out = func(low, high, dtype=dt) +# self.assert_equal(out.shape, self.tgtShape) +# +# out = func(low[0], high, dtype=dt) +# self.assert_equal(out.shape, self.tgtShape) +# +# out = func(low, high[0], dtype=dt) +# self.assert_equal(out.shape, self.tgtShape) + + def test_three_arg_funcs(self): + funcs = [random.noncentral_f, random.triangular, + random.hypergeometric] + + for func in funcs: + out = func(self.argOne, self.argTwo, self.argThree) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne[0], self.argTwo, self.argThree) + assert_equal(out.shape, self.tgtShape) + + out = func(self.argOne, self.argTwo[0], self.argThree) + assert_equal(out.shape, self.tgtShape) diff --git a/numpy/random/randomgen/tests/test_randomstate_regression.py b/numpy/random/randomgen/tests/test_randomstate_regression.py new file mode 100644 index 000000000000..cf21ee756b87 --- /dev/null +++ b/numpy/random/randomgen/tests/test_randomstate_regression.py @@ -0,0 +1,157 @@ +import sys +from numpy.testing import ( + assert_, assert_array_equal, assert_raises, + ) +from numpy.compat import long +import numpy as np + +from ...randomgen import mtrand as random + + +class TestRegression(object): + + def test_VonMises_range(self): + # Make sure generated random variables are in [-pi, pi]. + # Regression test for ticket #986. + for mu in np.linspace(-7., 7., 5): + r = random.vonmises(mu, 1, 50) + assert_(np.all(r > -np.pi) and np.all(r <= np.pi)) + + def test_hypergeometric_range(self): + # Test for ticket #921 + assert_(np.all(random.hypergeometric(3, 18, 11, size=10) < 4)) + assert_(np.all(random.hypergeometric(18, 3, 11, size=10) > 0)) + + # Test for ticket #5623 + args = [ + (2**20 - 2, 2**20 - 2, 2**20 - 2), # Check for 32-bit systems + ] + is_64bits = sys.maxsize > 2**32 + if is_64bits and sys.platform != 'win32': + # Check for 64-bit systems + args.append((2**40 - 2, 2**40 - 2, 2**40 - 2)) + for arg in args: + assert_(random.hypergeometric(*arg) > 0) + + def test_logseries_convergence(self): + # Test for ticket #923 + N = 1000 + random.seed(0) + rvsn = random.logseries(0.8, size=N) + # these two frequency counts should be close to theoretical + # numbers with this large sample + # theoretical large N result is 0.49706795 + freq = np.sum(rvsn == 1) / float(N) + msg = "Frequency was %f, should be > 0.45" % freq + assert_(freq > 0.45, msg) + # theoretical large N result is 0.19882718 + freq = np.sum(rvsn == 2) / float(N) + msg = "Frequency was %f, should be < 0.23" % freq + assert_(freq < 0.23, msg) + + def test_permutation_longs(self): + random.seed(1234) + a = random.permutation(12) + random.seed(1234) + b = random.permutation(long(12)) + assert_array_equal(a, b) + + def test_shuffle_mixed_dimension(self): + # Test for trac ticket #2074 + for t in [[1, 2, 3, None], + [(1, 1), (2, 2), (3, 3), None], + [1, (2, 2), (3, 3), None], + [(1, 1), 2, 3, None]]: + random.seed(12345) + shuffled = list(t) + random.shuffle(shuffled) + assert_array_equal(shuffled, [t[0], t[3], t[1], t[2]]) + + def test_call_within_randomstate(self): + # Check that custom RandomState does not call into global state + m = random.RandomState() + res = np.array([0, 8, 7, 2, 1, 9, 4, 7, 0, 3]) + for i in range(3): + random.seed(i) + m.seed(4321) + # If m.state is not honored, the result will change + assert_array_equal(m.choice(10, size=10, p=np.ones(10)/10.), res) + + def test_multivariate_normal_size_types(self): + # Test for multivariate_normal issue with 'size' argument. + # Check that the multivariate_normal size argument can be a + # numpy integer. + random.multivariate_normal([0], [[0]], size=1) + random.multivariate_normal([0], [[0]], size=np.int_(1)) + random.multivariate_normal([0], [[0]], size=np.int64(1)) + + def test_beta_small_parameters(self): + # Test that beta with small a and b parameters does not produce + # NaNs due to roundoff errors causing 0 / 0, gh-5851 + random.seed(1234567890) + x = random.beta(0.0001, 0.0001, size=100) + assert_(not np.any(np.isnan(x)), 'Nans in random.beta') + + def test_choice_sum_of_probs_tolerance(self): + # The sum of probs should be 1.0 with some tolerance. + # For low precision dtypes the tolerance was too tight. + # See numpy github issue 6123. + random.seed(1234) + a = [1, 2, 3] + counts = [4, 4, 2] + for dt in np.float16, np.float32, np.float64: + probs = np.array(counts, dtype=dt) / sum(counts) + c = random.choice(a, p=probs) + assert_(c in a) + assert_raises(ValueError, random.choice, a, p=probs*0.9) + + def test_shuffle_of_array_of_different_length_strings(self): + # Test that permuting an array of different length strings + # will not cause a segfault on garbage collection + # Tests gh-7710 + random.seed(1234) + + a = np.array(['a', 'a' * 1000]) + + for _ in range(100): + random.shuffle(a) + + # Force Garbage Collection - should not segfault. + import gc + gc.collect() + + def test_shuffle_of_array_of_objects(self): + # Test that permuting an array of objects will not cause + # a segfault on garbage collection. + # See gh-7719 + random.seed(1234) + a = np.array([np.arange(1), np.arange(4)]) + + for _ in range(1000): + random.shuffle(a) + + # Force Garbage Collection - should not segfault. + import gc + gc.collect() + + def test_permutation_subclass(self): + class N(np.ndarray): + pass + + random.seed(1) + orig = np.arange(3).view(N) + perm = random.permutation(orig) + assert_array_equal(perm, np.array([0, 2, 1])) + assert_array_equal(orig, np.arange(3).view(N)) + + class M(object): + a = np.arange(5) + + def __array__(self): + return self.a + + random.seed(1) + m = M() + perm = random.permutation(m) + assert_array_equal(perm, np.array([2, 1, 4, 0, 3])) + assert_array_equal(m.__array__(), np.arange(5)) diff --git a/numpy/random/randomgen/tests/test_smoke.py b/numpy/random/randomgen/tests/test_smoke.py index ab6f50e330ca..b00b9efb297d 100644 --- a/numpy/random/randomgen/tests/test_smoke.py +++ b/numpy/random/randomgen/tests/test_smoke.py @@ -2,6 +2,7 @@ import pickle import sys import time +from functools import partial import numpy as np import pytest @@ -152,13 +153,6 @@ def test_jump(self): brng_name = self.rg._basicrng.__class__.__name__ pytest.skip('Jump is not supported by {0}'.format(brng_name)) - def test_random_uintegers(self): - assert_(len(self.rg.random_uintegers(10)) == 10) - - def test_random_raw(self): - assert_(len(self.rg.random_raw(10)) == 10) - assert_(self.rg.random_raw((10, 10)).shape == (10, 10)) - def test_uniform(self): r = self.rg.uniform(-1.0, 0.0, size=10) assert_(len(r) == 10) @@ -200,6 +194,20 @@ def test_standard_exponential(self): assert_(len(self.rg.standard_exponential(10)) == 10) params_0(self.rg.standard_exponential) + def test_standard_exponential_float(self): + randoms = self.rg.standard_exponential(10, dtype='float32') + assert_(len(randoms) == 10) + assert randoms.dtype == np.float32 + params_0(partial(self.rg.standard_exponential, dtype='float32')) + + def test_standard_exponential_float_log(self): + randoms = self.rg.standard_exponential(10, dtype='float32', + method='inv') + assert_(len(randoms) == 10) + assert randoms.dtype == np.float32 + params_0(partial(self.rg.standard_exponential, dtype='float32', + method='inv')) + def test_standard_cauchy(self): assert_(len(self.rg.standard_cauchy(10)) == 10) params_0(self.rg.standard_cauchy) @@ -214,9 +222,9 @@ def test_binomial(self): def test_reset_state(self): state = self.rg.state - int_1 = self.rg.random_raw(1) + int_1 = self.rg.randint(2**31) self.rg.state = state - int_2 = self.rg.random_raw(1) + int_2 = self.rg.randint(2**31) assert_(int_1 == int_2) def test_entropy_init(self): @@ -256,14 +264,14 @@ def test_reset_state_uint32(self): n2 = rg2.randint(0, 2 ** 24, 10, dtype=np.uint32) assert_array_equal(n1, n2) - def test_reset_state_uintegers(self): + def test_reset_state_float(self): rg = RandomGenerator(self.brng(*self.seed)) - rg.random_uintegers(bits=32) + rg.random_sample(dtype='float32') state = rg.state - n1 = rg.random_uintegers(bits=32, size=10) + n1 = rg.random_sample(size=10, dtype='float32') rg2 = RandomGenerator(self.brng()) rg2.state = state - n2 = rg2.random_uintegers(bits=32, size=10) + n2 = rg2.random_sample(size=10, dtype='float32') assert_((n1 == n2).all()) def test_shuffle(self): @@ -679,6 +687,10 @@ def test_output_fill(self): direct = rg.standard_normal(size=size) assert_equal(direct, existing) + sized = np.empty(size) + rg.state = state + rg.standard_normal(out=sized, size=sized.shape) + existing = np.empty(size, dtype=np.float32) rg.state = state rg.standard_normal(out=existing, dtype=np.float32) diff --git a/numpy/random/randomgen/threefry.pyx b/numpy/random/randomgen/threefry.pyx index 63364dc3de46..aa4ce622dd08 100644 --- a/numpy/random/randomgen/threefry.pyx +++ b/numpy/random/randomgen/threefry.pyx @@ -1,8 +1,11 @@ -from __future__ import absolute_import - from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock + import numpy as np from .common import interface @@ -24,10 +27,10 @@ cdef extern from 'src/threefry/threefry.h': ctypedef r123array4x64 threefry4x64_ctr_t struct s_threefry_state: - threefry4x64_ctr_t *ctr; - threefry4x64_key_t *key; - int buffer_pos; - uint64_t buffer[THREEFRY_BUFFER_SIZE]; + threefry4x64_ctr_t *ctr + threefry4x64_key_t *key + int buffer_pos + uint64_t buffer[THREEFRY_BUFFER_SIZE] int has_uint32 uint32_t uinteger @@ -153,13 +156,13 @@ cdef class ThreeFry: the International Conference for High Performance Computing, Networking, Storage and Analysis (SC11), New York, NY: ACM, 2011. """ - cdef threefry_state *rng_state + cdef threefry_state *rng_state cdef brng_t *_brng cdef public object capsule cdef object _ctypes cdef object _cffi cdef object _generator - + cdef public object lock def __init__(self, seed=None, counter=None, key=None): self.rng_state = malloc(sizeof(threefry_state)) @@ -167,6 +170,7 @@ cdef class ThreeFry: self.rng_state.key = malloc(sizeof(threefry4x64_key_t)) self._brng = malloc(sizeof(brng_t)) self.seed(seed, counter, key) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &threefry_uint64 @@ -207,16 +211,39 @@ cdef class ThreeFry: for i in range(THREEFRY_BUFFER_SIZE): self.rng_state.buffer[i] = 0 + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(self._brng, self.lock, size, output) + def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None, counter=None, key=None): """ @@ -294,7 +321,7 @@ cdef class ThreeFry: key[i] = self.rng_state.key.v[i] for i in range(THREEFRY_BUFFER_SIZE): buffer[i] = self.rng_state.buffer[i] - state = {'counter':ctr,'key':key} + state = {'counter': ctr, 'key': key} return {'brng': self.__class__.__name__, 'state': state, 'buffer': buffer, @@ -385,14 +412,15 @@ cdef class ThreeFry: self._reset_state_variables() return self + @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -401,25 +429,10 @@ cdef class ThreeFry: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&threefry_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&threefry_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&threefry_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -440,19 +453,8 @@ cdef class ThreeFry: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/threefry32.pyx b/numpy/random/randomgen/threefry32.pyx index c4d32a324db0..81f3ee93e040 100644 --- a/numpy/random/randomgen/threefry32.pyx +++ b/numpy/random/randomgen/threefry32.pyx @@ -1,4 +1,7 @@ -from __future__ import absolute_import +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock import numpy as np from cpython.pycapsule cimport PyCapsule_New @@ -23,10 +26,10 @@ cdef extern from 'src/threefry32/threefry32.h': ctypedef r123array4x32 threefry4x32_ctr_t struct s_threefry32_state: - threefry4x32_ctr_t *ctr; - threefry4x32_key_t *key; - int buffer_pos; - uint32_t buffer[THREEFRY_BUFFER_SIZE]; + threefry4x32_ctr_t *ctr + threefry4x32_key_t *key + int buffer_pos + uint32_t buffer[THREEFRY_BUFFER_SIZE] ctypedef s_threefry32_state threefry32_state @@ -156,12 +159,13 @@ cdef class ThreeFry32: the International Conference for High Performance Computing, Networking, Storage and Analysis (SC11), New York, NY: ACM, 2011. """ - cdef threefry32_state *rng_state + cdef threefry32_state *rng_state cdef brng_t *_brng cdef public object capsule cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None, counter=None, key=None): self.rng_state = malloc(sizeof(threefry32_state)) @@ -169,6 +173,7 @@ cdef class ThreeFry32: self.rng_state.key = malloc(sizeof(threefry4x32_key_t)) self._brng = malloc(sizeof(brng_t)) self.seed(seed, counter, key) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &threefry32_uint64 @@ -207,16 +212,39 @@ cdef class ThreeFry32: for i in range(THREEFRY_BUFFER_SIZE): self.rng_state.buffer[i] = 0 + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(self._brng, self.lock, size, output) + def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method == u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method == u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None, counter=None, key=None): """ @@ -380,14 +408,15 @@ cdef class ThreeFry32: self._reset_state_variables() return self + @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -396,25 +425,10 @@ cdef class ThreeFry32: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface( self.rng_state, - ctypes.c_void_p( self.rng_state), - ctypes.cast( &threefry32_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast( &threefry32_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast( &threefry32_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p( self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -435,22 +449,8 @@ cdef class ThreeFry32: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface( self.rng_state, - ffi.cast('void *', self.rng_state), - ffi.cast('uint64_t (*)(void *)', - self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)', - self._brng.next_uint32), - ffi.cast('double (*)(void *)', - self._brng.next_double), - ffi.cast('void *', self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/xoroshiro128.pyx b/numpy/random/randomgen/xoroshiro128.pyx index a85f1fde5eb4..2ae876ea6be8 100644 --- a/numpy/random/randomgen/xoroshiro128.pyx +++ b/numpy/random/randomgen/xoroshiro128.pyx @@ -1,4 +1,7 @@ -from __future__ import absolute_import +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New @@ -16,15 +19,15 @@ np.import_array() cdef extern from "src/xoroshiro128/xoroshiro128.h": struct s_xoroshiro128_state: - uint64_t s[2] - int has_uint32 - uint32_t uinteger + uint64_t s[2] + int has_uint32 + uint32_t uinteger ctypedef s_xoroshiro128_state xoroshiro128_state uint64_t xoroshiro128_next64(xoroshiro128_state *state) nogil uint32_t xoroshiro128_next32(xoroshiro128_state *state) nogil - void xoroshiro128_jump(xoroshiro128_state *state) + void xoroshiro128_jump(xoroshiro128_state *state) cdef uint64_t xoroshiro128_uint64(void* st) nogil: return xoroshiro128_next64(st) @@ -120,17 +123,19 @@ cdef class Xoroshiro128: .. [1] "xoroshiro+ / xorshift* / xorshift+ generators and the PRNG shootout", http://xorshift.di.unimi.it/ """ - cdef xoroshiro128_state *rng_state + cdef xoroshiro128_state *rng_state cdef brng_t *_brng cdef public object capsule cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None): self.rng_state = malloc(sizeof(xoroshiro128_state)) self._brng = malloc(sizeof(brng_t)) self.seed(seed) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &xoroshiro128_uint64 @@ -166,17 +171,39 @@ cdef class Xoroshiro128: self.rng_state.has_uint32 = 0 self.rng_state.uinteger = 0 + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(self._brng, self.lock, size, output) + def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - """Private benchmark command""" - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None): """ @@ -198,7 +225,7 @@ cdef class Xoroshiro128: ValueError If seed values are out of range for the PRNG. """ - ub = 2 ** 64 + ub = 2 ** 64 if seed is None: try: state = random_entropy(4) @@ -273,12 +300,12 @@ cdef class Xoroshiro128: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -288,24 +315,10 @@ cdef class Xoroshiro128: * brng - pointer to the Basic RNG struct """ - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&xoroshiro128_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&xoroshiro128_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&xoroshiro128_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) + + return self._ctypes @property def cffi(self): @@ -326,19 +339,8 @@ cdef class Xoroshiro128: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/xorshift1024.pyx b/numpy/random/randomgen/xorshift1024.pyx index 62848bb81363..28b80cc9a96a 100644 --- a/numpy/random/randomgen/xorshift1024.pyx +++ b/numpy/random/randomgen/xorshift1024.pyx @@ -1,4 +1,7 @@ -from __future__ import absolute_import +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New @@ -16,16 +19,16 @@ np.import_array() cdef extern from "src/xorshift1024/xorshift1024.h": struct s_xorshift1024_state: - uint64_t s[16] - int p - int has_uint32 - uint32_t uinteger + uint64_t s[16] + int p + int has_uint32 + uint32_t uinteger ctypedef s_xorshift1024_state xorshift1024_state uint64_t xorshift1024_next64(xorshift1024_state *state) nogil uint32_t xorshift1024_next32(xorshift1024_state *state) nogil - void xorshift1024_jump(xorshift1024_state *state) + void xorshift1024_jump(xorshift1024_state *state) cdef uint64_t xorshift1024_uint64(void* st) nogil: return xorshift1024_next64(st) @@ -126,17 +129,19 @@ cdef class Xorshift1024: generators." CoRR, abs/1403.0930, 2014. """ - cdef xorshift1024_state *rng_state + cdef xorshift1024_state *rng_state cdef brng_t *_brng cdef public object capsule cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None): self.rng_state = malloc(sizeof(xorshift1024_state)) self._brng = malloc(sizeof(brng_t)) self.seed(seed) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &xorshift1024_uint64 @@ -172,41 +177,39 @@ cdef class Xorshift1024: self.rng_state.has_uint32 = 0 self.rng_state.uinteger = 0 - def __random_integer(self, bits=64): + def random_raw(self, size=None, output=True): """ - 64-bit Random Integers from the PRNG + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG Parameters ---------- - bits : {32, 64} - Number of random bits to return + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. Returns ------- - rv : int - Next random value + out : uint or ndarray + Drawn samples. Notes ----- - Testing only + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. """ - if bits == 64: - return self._brng.next_uint64(self._brng.state) - elif bits == 32: - return self._brng.next_uint32(self._brng.state) - else: - raise ValueError('bits must be 32 or 64') + return random_raw(self._brng, self.lock, size, output) def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None): """ @@ -229,7 +232,7 @@ cdef class Xorshift1024: If seed values are out of range for the PRNG. """ - ub = 2 ** 64 + ub = 2 ** 64 if seed is None: try: state = random_entropy(32) @@ -285,7 +288,7 @@ cdef class Xorshift1024: for i in range(16): s[i] = self.rng_state.s[i] return {'brng': self.__class__.__name__, - 'state': {'s':s,'p':self.rng_state.p}, + 'state': {'s': s, 'p': self.rng_state.p}, 'has_uint32': self.rng_state.has_uint32, 'uinteger': self.rng_state.uinteger} @@ -306,12 +309,12 @@ cdef class Xorshift1024: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -320,25 +323,10 @@ cdef class Xorshift1024: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&xorshift1024_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&xorshift1024_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&xorshift1024_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -359,19 +347,8 @@ cdef class Xorshift1024: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/xoshiro256starstar.pyx b/numpy/random/randomgen/xoshiro256starstar.pyx index 2d1e1955ee36..d45540aa9a23 100644 --- a/numpy/random/randomgen/xoshiro256starstar.pyx +++ b/numpy/random/randomgen/xoshiro256starstar.pyx @@ -1,11 +1,14 @@ -from __future__ import absolute_import - from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New import numpy as np cimport numpy as np +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock + from .common import interface from .common cimport * from .distributions cimport brng_t @@ -16,15 +19,15 @@ np.import_array() cdef extern from "src/xoshiro256starstar/xoshiro256starstar.h": struct s_xoshiro256starstar_state: - uint64_t s[4] - int has_uint32 - uint32_t uinteger + uint64_t s[4] + int has_uint32 + uint32_t uinteger ctypedef s_xoshiro256starstar_state xoshiro256starstar_state uint64_t xoshiro256starstar_next64(xoshiro256starstar_state *state) nogil uint32_t xoshiro256starstar_next32(xoshiro256starstar_state *state) nogil - void xoshiro256starstar_jump(xoshiro256starstar_state *state) + void xoshiro256starstar_jump(xoshiro256starstar_state *state) cdef uint64_t xoshiro256starstar_uint64(void* st) nogil: return xoshiro256starstar_next64(st) @@ -120,17 +123,19 @@ cdef class Xoshiro256StarStar: .. [1] "xoroshiro+ / xorshift* / xorshift+ generators and the PRNG shootout", http://xorshift.di.unimi.it/ """ - cdef xoshiro256starstar_state *rng_state + cdef xoshiro256starstar_state *rng_state cdef brng_t *_brng cdef public object capsule cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None): self.rng_state = malloc(sizeof(xoshiro256starstar_state)) self._brng = malloc(sizeof(brng_t)) self.seed(seed) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &xoshiro256starstar_uint64 @@ -166,17 +171,39 @@ cdef class Xoshiro256StarStar: self.rng_state.has_uint32 = 0 self.rng_state.uinteger = 0 + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(self._brng, self.lock, size, output) + def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - """Private benchmark command""" - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None): """ @@ -198,7 +225,7 @@ cdef class Xoshiro256StarStar: ValueError If seed values are out of range for the PRNG. """ - ub = 2 ** 64 + ub = 2 ** 64 if seed is None: try: state = random_entropy(8) @@ -279,12 +306,12 @@ cdef class Xoshiro256StarStar: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -293,25 +320,10 @@ cdef class Xoshiro256StarStar: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&xoshiro256starstar_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&xoshiro256starstar_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&xoshiro256starstar_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -332,19 +344,8 @@ cdef class Xoshiro256StarStar: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): diff --git a/numpy/random/randomgen/xoshiro512starstar.pyx b/numpy/random/randomgen/xoshiro512starstar.pyx index 935deecfa898..0a6b104fdad2 100644 --- a/numpy/random/randomgen/xoshiro512starstar.pyx +++ b/numpy/random/randomgen/xoshiro512starstar.pyx @@ -1,4 +1,7 @@ -from __future__ import absolute_import +try: + from threading import Lock +except ImportError: + from dummy_threading import Lock from libc.stdlib cimport malloc, free from cpython.pycapsule cimport PyCapsule_New @@ -16,15 +19,15 @@ np.import_array() cdef extern from "src/xoshiro512starstar/xoshiro512starstar.h": struct s_xoshiro512starstar_state: - uint64_t s[8] - int has_uint32 - uint32_t uinteger + uint64_t s[8] + int has_uint32 + uint32_t uinteger ctypedef s_xoshiro512starstar_state xoshiro512starstar_state uint64_t xoshiro512starstar_next64(xoshiro512starstar_state *state) nogil uint32_t xoshiro512starstar_next32(xoshiro512starstar_state *state) nogil - void xoshiro512starstar_jump(xoshiro512starstar_state *state) + void xoshiro512starstar_jump(xoshiro512starstar_state *state) cdef uint64_t xoshiro512starstar_uint64(void* st) nogil: return xoshiro512starstar_next64(st) @@ -120,17 +123,19 @@ cdef class Xoshiro512StarStar: .. [1] "xoroshiro+ / xorshift* / xorshift+ generators and the PRNG shootout", http://xorshift.di.unimi.it/ """ - cdef xoshiro512starstar_state *rng_state + cdef xoshiro512starstar_state *rng_state cdef brng_t *_brng cdef public object capsule cdef object _ctypes cdef object _cffi cdef object _generator + cdef public object lock def __init__(self, seed=None): self.rng_state = malloc(sizeof(xoshiro512starstar_state)) self._brng = malloc(sizeof(brng_t)) self.seed(seed) + self.lock = Lock() self._brng.state = self.rng_state self._brng.next_uint64 = &xoshiro512starstar_uint64 @@ -166,17 +171,39 @@ cdef class Xoshiro512StarStar: self.rng_state.has_uint32 = 0 self.rng_state.uinteger = 0 + def random_raw(self, size=None, output=True): + """ + random_raw(self, size=None) + + Return randoms as generated by the underlying BasicRNG + + Parameters + ---------- + size : int or tuple of ints, optional + Output shape. If the given shape is, e.g., ``(m, n, k)``, then + ``m * n * k`` samples are drawn. Default is None, in which case a + single value is returned. + output : bool, optional + Output values. Used for performance testing since the generated + values are not returned. + + Returns + ------- + out : uint or ndarray + Drawn samples. + + Notes + ----- + This method directly exposes the the raw underlying pseudo-random + number generator. All values are returned as unsigned 64-bit + values irrespective of the number of bits produced by the PRNG. + + See the class docstring for the number of bits returned. + """ + return random_raw(self._brng, self.lock, size, output) + def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - """Private benchmark command""" - cdef Py_ssize_t i - if method==u'uint64': - for i in range(cnt): - self._brng.next_uint64(self._brng.state) - elif method==u'double': - for i in range(cnt): - self._brng.next_double(self._brng.state) - else: - raise ValueError('Unknown method') + return benchmark(self._brng, self.lock, cnt, method) def seed(self, seed=None): """ @@ -198,7 +225,7 @@ cdef class Xoshiro512StarStar: ValueError If seed values are out of range for the PRNG. """ - ub = 2 ** 64 + ub = 2 ** 64 if seed is None: try: state = random_entropy(16) @@ -273,12 +300,12 @@ cdef class Xoshiro512StarStar: @property def ctypes(self): """ - Ctypes interface + ctypes interface Returns ------- interface : namedtuple - Named tuple containing CFFI wrapper + Named tuple containing ctypes wrapper * state_address - Memory address of the state struct * state - pointer to the state struct @@ -287,25 +314,10 @@ cdef class Xoshiro512StarStar: * next_double - function pointer to produce doubles * brng - pointer to the Basic RNG struct """ + if self._ctypes is None: + self._ctypes = prepare_ctypes(self._brng) - if self._ctypes is not None: - return self._ctypes - - import ctypes - - self._ctypes = interface(self.rng_state, - ctypes.c_void_p(self.rng_state), - ctypes.cast(&xoshiro512starstar_uint64, - ctypes.CFUNCTYPE(ctypes.c_uint64, - ctypes.c_void_p)), - ctypes.cast(&xoshiro512starstar_uint32, - ctypes.CFUNCTYPE(ctypes.c_uint32, - ctypes.c_void_p)), - ctypes.cast(&xoshiro512starstar_double, - ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p)), - ctypes.c_void_p(self._brng)) - return self.ctypes + return self._ctypes @property def cffi(self): @@ -326,19 +338,8 @@ cdef class Xoshiro512StarStar: """ if self._cffi is not None: return self._cffi - try: - import cffi - except ImportError: - raise ImportError('cffi is cannot be imported.') - - ffi = cffi.FFI() - self._cffi = interface(self.rng_state, - ffi.cast('void *',self.rng_state), - ffi.cast('uint64_t (*)(void *)',self._brng.next_uint64), - ffi.cast('uint32_t (*)(void *)',self._brng.next_uint32), - ffi.cast('double (*)(void *)',self._brng.next_double), - ffi.cast('void *',self._brng)) - return self.cffi + self._cffi = prepare_cffi(self._brng) + return self._cffi @property def generator(self): From ce5857dad299cb741e18a39a7c9fff0cb94f64f4 Mon Sep 17 00:00:00 2001 From: Kevin Sheppard Date: Mon, 1 Apr 2019 17:08:41 +0100 Subject: [PATCH 2/3] TST: Incorporate edge tests Incorporate testing of edge cases into main tests Rename test files to describe their purpose --- ...y_mt19937.py => test_generator_mt19937.py} | 501 ++++++++++-------- ... => test_generator_mt19937_regressions.py} | 0 numpy/random/randomgen/tests/test_smoke.py | 5 - 3 files changed, 272 insertions(+), 234 deletions(-) rename numpy/random/randomgen/tests/{test_numpy_mt19937.py => test_generator_mt19937.py} (82%) rename numpy/random/randomgen/tests/{test_numpy_mt19937_regressions.py => test_generator_mt19937_regressions.py} (100%) diff --git a/numpy/random/randomgen/tests/test_numpy_mt19937.py b/numpy/random/randomgen/tests/test_generator_mt19937.py similarity index 82% rename from numpy/random/randomgen/tests/test_numpy_mt19937.py rename to numpy/random/randomgen/tests/test_generator_mt19937.py index b2249b684f67..3c999ab73e2b 100644 --- a/numpy/random/randomgen/tests/test_numpy_mt19937.py +++ b/numpy/random/randomgen/tests/test_generator_mt19937.py @@ -11,7 +11,7 @@ from ...randomgen import RandomGenerator, MT19937 -random = mt19937 = RandomGenerator(MT19937()) +random = RandomGenerator(MT19937()) class TestSeed(object): @@ -44,6 +44,9 @@ def test_invalid_array(self): assert_raises(ValueError, MT19937, [1, 2, 4294967296]) assert_raises(ValueError, MT19937, [1, -2, 4294967296]) + def test_noninstantized_brng(self): + assert_raises(ValueError, RandomGenerator, MT19937) + class TestBinomial(object): def test_n_zero(self): @@ -77,15 +80,15 @@ def test_int_negative_interval(self): def test_size(self): # gh-3173 p = [0.5, 0.5] - assert_equal(mt19937.multinomial(1, p, np.uint32(1)).shape, (1, 2)) - assert_equal(mt19937.multinomial(1, p, np.uint32(1)).shape, (1, 2)) - assert_equal(mt19937.multinomial(1, p, np.uint32(1)).shape, (1, 2)) - assert_equal(mt19937.multinomial(1, p, [2, 2]).shape, (2, 2, 2)) - assert_equal(mt19937.multinomial(1, p, (2, 2)).shape, (2, 2, 2)) - assert_equal(mt19937.multinomial(1, p, np.array((2, 2))).shape, + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.multinomial(1, p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.multinomial(1, p, np.array((2, 2))).shape, (2, 2, 2)) - assert_raises(TypeError, mt19937.multinomial, 1, p, + assert_raises(TypeError, random.multinomial, 1, p, float(1)) def test_invalid_prob(self): @@ -132,7 +135,7 @@ def test_negative_binomial(self): class TestRandint(object): - rfunc = mt19937.randint + rfunc = random.randint # valid integer/boolean types itype = [bool, np.int8, np.uint8, np.int16, np.uint16, @@ -244,7 +247,7 @@ def test_full_range_array(self): def test_in_bounds_fuzz(self): # Don't use fixed seed - mt19937.seed() + random.seed() for dt in self.itype[1:]: for ubnd in [4, 8, 16]: @@ -263,13 +266,13 @@ def test_scalar_array_equiv(self): ubnd = 2 if dt is bool else np.iinfo(dt).max + 1 size = 1000 - mt19937.seed(1234) + random.seed(1234) scalar = self.rfunc(lbnd, ubnd, size=size, dtype=dt) - mt19937.seed(1234) + random.seed(1234) scalar_array = self.rfunc([lbnd], [ubnd], size=size, dtype=dt) - mt19937.seed(1234) + random.seed(1234) array = self.rfunc([lbnd] * size, [ubnd] * size, size=size, dtype=dt) assert_array_equal(scalar, scalar_array) @@ -291,7 +294,7 @@ def test_repeatability(self): 'uint8': '27dd30c4e08a797063dffac2490b0be6'} for dt in self.itype[1:]: - mt19937.seed(1234) + random.seed(1234) # view as little endian for hash if sys.byteorder == 'little': @@ -303,7 +306,7 @@ def test_repeatability(self): assert_(tgt[np.dtype(dt).name] == res) # bools do not depend on endianness - mt19937.seed(1234) + random.seed(1234) val = self.rfunc(0, 2, size=1000, dtype=bool).view(np.int8) res = hashlib.md5(val).hexdigest() assert_(tgt[np.dtype(bool).name] == res) @@ -316,15 +319,15 @@ def test_repeatability_broadcasting(self): np.bool, bool, np.bool_) else np.iinfo(dt).max + 1 # view as little endian for hash - mt19937.seed(1234) + random.seed(1234) val = self.rfunc(lbnd, ubnd, size=1000, dtype=dt) - mt19937.seed(1234) + random.seed(1234) val_bc = self.rfunc([lbnd] * 1000, ubnd, dtype=dt) assert_array_equal(val, val_bc) - mt19937.seed(1234) + random.seed(1234) val_bc = self.rfunc([lbnd] * 1000, [ubnd] * 1000, dtype=dt) assert_array_equal(val, val_bc) @@ -349,7 +352,7 @@ def test_int64_uint64_corner_case(self): # None of these function calls should # generate a ValueError now. - actual = mt19937.randint(lbnd, ubnd, dtype=dt) + actual = random.randint(lbnd, ubnd, dtype=dt) assert_equal(actual, tgt) def test_respect_dtype_singleton(self): @@ -400,34 +403,34 @@ def setup(self): self.seed = 1234567890 def test_rand(self): - mt19937.seed(self.seed) - actual = mt19937.rand(3, 2) + random.seed(self.seed) + actual = random.rand(3, 2) desired = np.array([[0.61879477158567997, 0.59162362775974664], [0.88868358904449662, 0.89165480011560816], [0.4575674820298663, 0.7781880808593471]]) assert_array_almost_equal(actual, desired, decimal=15) def test_rand_singleton(self): - mt19937.seed(self.seed) - actual = mt19937.rand() + random.seed(self.seed) + actual = random.rand() desired = 0.61879477158567997 assert_array_almost_equal(actual, desired, decimal=15) def test_randn(self): - mt19937.seed(self.seed) - actual = mt19937.randn(3, 2) + random.seed(self.seed) + actual = random.randn(3, 2) desired = np.array([[-3.472754000610961, -0.108938564229143], [-0.245965753396411, -0.704101550261701], [0.360102487116356, 0.127832101772367]]) assert_array_almost_equal(actual, desired, decimal=15) - mt19937.seed(self.seed) - actual = mt19937.randn() + random.seed(self.seed) + actual = random.randn() assert_array_almost_equal(actual, desired[0, 0], decimal=15) def test_randint(self): - mt19937.seed(self.seed) - actual = mt19937.randint(-99, 99, size=(3, 2)) + random.seed(self.seed) + actual = random.randint(-99, 99, size=(3, 2)) desired = np.array([[31, 3], [-52, 41], [-48, -66]]) @@ -436,9 +439,9 @@ def test_randint(self): def test_randint_masked(self): # Test masked rejection sampling algorithm to generate array of # uint32 in an interval. - mt19937.seed(self.seed) - actual = mt19937.randint(0, 99, size=(3, 2), dtype=np.uint32, - use_masked=True) + random.seed(self.seed) + actual = random.randint(0, 99, size=(3, 2), dtype=np.uint32, + use_masked=True) desired = np.array([[2, 47], [12, 51], [33, 43]], dtype=np.uint32) @@ -446,9 +449,9 @@ def test_randint_masked(self): def test_randint_lemire_32(self): # Test lemire algorithm to generate array of uint32 in an interval. - mt19937.seed(self.seed) - actual = mt19937.randint(0, 99, size=(3, 2), dtype=np.uint32, - use_masked=False) + random.seed(self.seed) + actual = random.randint(0, 99, size=(3, 2), dtype=np.uint32, + use_masked=False) desired = np.array([[61, 33], [58, 14], [87, 23]], dtype=np.uint32) @@ -456,19 +459,19 @@ def test_randint_lemire_32(self): def test_randint_lemire_64(self): # Test lemire algorithm to generate array of uint64 in an interval. - mt19937.seed(self.seed) - actual = mt19937.randint(0, 99 + 0xFFFFFFFFF, size=(3, 2), - dtype=np.uint64, use_masked=False) + random.seed(self.seed) + actual = random.randint(0, 99 + 0xFFFFFFFFF, size=(3, 2), + dtype=np.uint64, use_masked=False) desired = np.array([[42523252834, 40656066204], [61069871386, 61274051182], [31443797706, 53476677934]], dtype=np.uint64) assert_array_equal(actual, desired) def test_random_integers(self): - mt19937.seed(self.seed) + random.seed(self.seed) with suppress_warnings() as sup: w = sup.record(DeprecationWarning) - actual = mt19937.random_integers(-99, 99, size=(3, 2)) + actual = random.random_integers(-99, 99, size=(3, 2)) assert_(len(w) == 1) desired = np.array([[31, 3], [-52, 41], @@ -483,8 +486,8 @@ def test_random_integers_max_int(self): # to generate this integer. with suppress_warnings() as sup: w = sup.record(DeprecationWarning) - actual = mt19937.random_integers(np.iinfo('l').max, - np.iinfo('l').max) + actual = random.random_integers(np.iinfo('l').max, + np.iinfo('l').max) assert_(len(w) == 1) desired = np.iinfo('l').max @@ -496,70 +499,76 @@ def test_random_integers_deprecated(self): # DeprecationWarning raised with high == None assert_raises(DeprecationWarning, - mt19937.random_integers, + random.random_integers, np.iinfo('l').max) # DeprecationWarning raised with high != None assert_raises(DeprecationWarning, - mt19937.random_integers, + random.random_integers, np.iinfo('l').max, np.iinfo('l').max) def test_random_sample(self): - mt19937.seed(self.seed) - actual = mt19937.random_sample((3, 2)) + random.seed(self.seed) + actual = random.random_sample((3, 2)) desired = np.array([[0.61879477158567997, 0.59162362775974664], [0.88868358904449662, 0.89165480011560816], [0.4575674820298663, 0.7781880808593471]]) assert_array_almost_equal(actual, desired, decimal=15) - mt19937.seed(self.seed) - actual = mt19937.random_sample() + random.seed(self.seed) + actual = random.random_sample() assert_array_almost_equal(actual, desired[0, 0], decimal=15) def test_random_sample_float(self): - mt19937.seed(self.seed) - actual = mt19937.random_sample((3, 2)) + random.seed(self.seed) + actual = random.random_sample((3, 2)) desired = np.array([[0.6187948, 0.5916236], [0.8886836, 0.8916548], [0.4575675, 0.7781881]]) assert_array_almost_equal(actual, desired, decimal=7) + def test_random_sample_float_scalar(self): + random.seed(self.seed) + actual = random.random_sample(dtype=np.float32) + desired = 0.6187948 + assert_array_almost_equal(actual, desired, decimal=7) + def test_random_sample_unsupported_type(self): - assert_raises(TypeError, mt19937.random_sample, dtype='int32') + assert_raises(TypeError, random.random_sample, dtype='int32') def test_choice_uniform_replace(self): - mt19937.seed(self.seed) - actual = mt19937.choice(4, 4) + random.seed(self.seed) + actual = random.choice(4, 4) desired = np.array([2, 3, 2, 3]) assert_array_equal(actual, desired) def test_choice_nonuniform_replace(self): - mt19937.seed(self.seed) - actual = mt19937.choice(4, 4, p=[0.4, 0.4, 0.1, 0.1]) + random.seed(self.seed) + actual = random.choice(4, 4, p=[0.4, 0.4, 0.1, 0.1]) desired = np.array([1, 1, 2, 2]) assert_array_equal(actual, desired) def test_choice_uniform_noreplace(self): - mt19937.seed(self.seed) - actual = mt19937.choice(4, 3, replace=False) + random.seed(self.seed) + actual = random.choice(4, 3, replace=False) desired = np.array([0, 1, 3]) assert_array_equal(actual, desired) def test_choice_nonuniform_noreplace(self): - mt19937.seed(self.seed) - actual = mt19937.choice(4, 3, replace=False, - p=[0.1, 0.3, 0.5, 0.1]) + random.seed(self.seed) + actual = random.choice(4, 3, replace=False, + p=[0.1, 0.3, 0.5, 0.1]) desired = np.array([2, 3, 1]) assert_array_equal(actual, desired) def test_choice_noninteger(self): - mt19937.seed(self.seed) - actual = mt19937.choice(['a', 'b', 'c', 'd'], 4) + random.seed(self.seed) + actual = random.choice(['a', 'b', 'c', 'd'], 4) desired = np.array(['c', 'd', 'c', 'd']) assert_array_equal(actual, desired) def test_choice_exceptions(self): - sample = mt19937.choice + sample = random.choice assert_raises(ValueError, sample, -1, 3) assert_raises(ValueError, sample, 3., 3) assert_raises(ValueError, sample, [[1, 2], [3, 4]], 3) @@ -580,57 +589,57 @@ def test_choice_exceptions(self): def test_choice_return_shape(self): p = [0.1, 0.9] # Check scalar - assert_(np.isscalar(mt19937.choice(2, replace=True))) - assert_(np.isscalar(mt19937.choice(2, replace=False))) - assert_(np.isscalar(mt19937.choice(2, replace=True, p=p))) - assert_(np.isscalar(mt19937.choice(2, replace=False, p=p))) - assert_(np.isscalar(mt19937.choice([1, 2], replace=True))) - assert_(mt19937.choice([None], replace=True) is None) + assert_(np.isscalar(random.choice(2, replace=True))) + assert_(np.isscalar(random.choice(2, replace=False))) + assert_(np.isscalar(random.choice(2, replace=True, p=p))) + assert_(np.isscalar(random.choice(2, replace=False, p=p))) + assert_(np.isscalar(random.choice([1, 2], replace=True))) + assert_(random.choice([None], replace=True) is None) a = np.array([1, 2]) arr = np.empty(1, dtype=object) arr[0] = a - assert_(mt19937.choice(arr, replace=True) is a) + assert_(random.choice(arr, replace=True) is a) # Check 0-d array s = tuple() - assert_(not np.isscalar(mt19937.choice(2, s, replace=True))) - assert_(not np.isscalar(mt19937.choice(2, s, replace=False))) - assert_(not np.isscalar(mt19937.choice(2, s, replace=True, p=p))) - assert_(not np.isscalar(mt19937.choice(2, s, replace=False, p=p))) - assert_(not np.isscalar(mt19937.choice([1, 2], s, replace=True))) - assert_(mt19937.choice([None], s, replace=True).ndim == 0) + assert_(not np.isscalar(random.choice(2, s, replace=True))) + assert_(not np.isscalar(random.choice(2, s, replace=False))) + assert_(not np.isscalar(random.choice(2, s, replace=True, p=p))) + assert_(not np.isscalar(random.choice(2, s, replace=False, p=p))) + assert_(not np.isscalar(random.choice([1, 2], s, replace=True))) + assert_(random.choice([None], s, replace=True).ndim == 0) a = np.array([1, 2]) arr = np.empty(1, dtype=object) arr[0] = a - assert_(mt19937.choice(arr, s, replace=True).item() is a) + assert_(random.choice(arr, s, replace=True).item() is a) # Check multi dimensional array s = (2, 3) p = [0.1, 0.1, 0.1, 0.1, 0.4, 0.2] - assert_(mt19937.choice(6, s, replace=True).shape, s) - assert_(mt19937.choice(6, s, replace=False).shape, s) - assert_(mt19937.choice(6, s, replace=True, p=p).shape, s) - assert_(mt19937.choice(6, s, replace=False, p=p).shape, s) - assert_(mt19937.choice(np.arange(6), s, replace=True).shape, s) + assert_(random.choice(6, s, replace=True).shape, s) + assert_(random.choice(6, s, replace=False).shape, s) + assert_(random.choice(6, s, replace=True, p=p).shape, s) + assert_(random.choice(6, s, replace=False, p=p).shape, s) + assert_(random.choice(np.arange(6), s, replace=True).shape, s) # Check zero-size - assert_equal(mt19937.randint(0, 0, size=(3, 0, 4)).shape, (3, 0, 4)) - assert_equal(mt19937.randint(0, -10, size=0).shape, (0,)) - assert_equal(mt19937.randint(10, 10, size=0).shape, (0,)) - assert_equal(mt19937.choice(0, size=0).shape, (0,)) - assert_equal(mt19937.choice([], size=(0,)).shape, (0,)) - assert_equal(mt19937.choice(['a', 'b'], size=(3, 0, 4)).shape, + assert_equal(random.randint(0, 0, size=(3, 0, 4)).shape, (3, 0, 4)) + assert_equal(random.randint(0, -10, size=0).shape, (0,)) + assert_equal(random.randint(10, 10, size=0).shape, (0,)) + assert_equal(random.choice(0, size=0).shape, (0,)) + assert_equal(random.choice([], size=(0,)).shape, (0,)) + assert_equal(random.choice(['a', 'b'], size=(3, 0, 4)).shape, (3, 0, 4)) - assert_raises(ValueError, mt19937.choice, [], 10) + assert_raises(ValueError, random.choice, [], 10) def test_choice_nan_probabilities(self): a = np.array([42, 1, 2]) p = [None, None, None] - assert_raises(ValueError, mt19937.choice, a, p=p) + assert_raises(ValueError, random.choice, a, p=p) def test_bytes(self): - mt19937.seed(self.seed) - actual = mt19937.bytes(10) + random.seed(self.seed) + actual = random.bytes(10) desired = b'\x82Ui\x9e\xff\x97+Wf\xa5' assert_equal(actual, desired) @@ -654,9 +663,9 @@ def test_shuffle(self): lambda x: np.asarray([(i, i) for i in x], [("a", object, 1), ("b", np.int32, 1)])]: - mt19937.seed(self.seed) + random.seed(self.seed) alist = conv([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]) - mt19937.shuffle(alist) + random.shuffle(alist) actual = alist desired = conv([0, 1, 9, 6, 2, 4, 5, 8, 7, 3]) assert_array_equal(actual, desired) @@ -668,28 +677,28 @@ def test_shuffle_masked(self): a_orig = a.copy() b_orig = b.copy() for i in range(50): - mt19937.shuffle(a) + random.shuffle(a) assert_equal( sorted(a.data[~a.mask]), sorted(a_orig.data[~a_orig.mask])) - mt19937.shuffle(b) + random.shuffle(b) assert_equal( sorted(b.data[~b.mask]), sorted(b_orig.data[~b_orig.mask])) def test_permutation(self): - mt19937.seed(self.seed) + random.seed(self.seed) alist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] - actual = mt19937.permutation(alist) + actual = random.permutation(alist) desired = [0, 1, 9, 6, 2, 4, 5, 8, 7, 3] assert_array_equal(actual, desired) - mt19937.seed(self.seed) + random.seed(self.seed) arr_2d = np.atleast_2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).T - actual = mt19937.permutation(arr_2d) + actual = random.permutation(arr_2d) assert_array_equal(actual, np.atleast_2d(desired).T) def test_beta(self): - mt19937.seed(self.seed) - actual = mt19937.beta(.1, .9, size=(3, 2)) + random.seed(self.seed) + actual = random.beta(.1, .9, size=(3, 2)) desired = np.array( [[1.45341850513746058e-02, 5.31297615662868145e-04], [1.85366619058432324e-06, 4.19214516800110563e-03], @@ -697,25 +706,25 @@ def test_beta(self): assert_array_almost_equal(actual, desired, decimal=15) def test_binomial(self): - mt19937.seed(self.seed) - actual = mt19937.binomial(100.123, .456, size=(3, 2)) + random.seed(self.seed) + actual = random.binomial(100.123, .456, size=(3, 2)) desired = np.array([[37, 43], [42, 48], [46, 45]]) assert_array_equal(actual, desired) def test_chisquare(self): - mt19937.seed(self.seed) - actual = mt19937.chisquare(50, size=(3, 2)) + random.seed(self.seed) + actual = random.chisquare(50, size=(3, 2)) desired = np.array([[22.2534560369812, 46.9302393710074], [52.9974164611614, 85.3559029505718], [46.1580841240719, 36.1933148548090]]) assert_array_almost_equal(actual, desired, decimal=13) def test_dirichlet(self): - mt19937.seed(self.seed) + random.seed(self.seed) alpha = np.array([51.72840233779265162, 39.74494232180943953]) - actual = mt19937.dirichlet(alpha, size=(3, 2)) + actual = random.dirichlet(alpha, size=(3, 2)) desired = np.array([[[0.444382290764855, 0.555617709235145], [0.468440809291970, 0.531559190708030]], [[0.613461427360549, 0.386538572639451], @@ -724,24 +733,29 @@ def test_dirichlet(self): [0.558550925712797, 0.441449074287203]]]) assert_array_almost_equal(actual, desired, decimal=15) bad_alpha = np.array([5.4e-01, -1.0e-16]) - assert_raises(ValueError, mt19937.dirichlet, bad_alpha) + assert_raises(ValueError, random.dirichlet, bad_alpha) + + random.seed(self.seed) + alpha = np.array([51.72840233779265162, 39.74494232180943953]) + actual = random.dirichlet(alpha) + assert_array_almost_equal(actual, desired[0, 0], decimal=15) def test_dirichlet_size(self): # gh-3173 p = np.array([51.72840233779265162, 39.74494232180943953]) - assert_equal(mt19937.dirichlet(p, np.uint32(1)).shape, (1, 2)) - assert_equal(mt19937.dirichlet(p, np.uint32(1)).shape, (1, 2)) - assert_equal(mt19937.dirichlet(p, np.uint32(1)).shape, (1, 2)) - assert_equal(mt19937.dirichlet(p, [2, 2]).shape, (2, 2, 2)) - assert_equal(mt19937.dirichlet(p, (2, 2)).shape, (2, 2, 2)) - assert_equal(mt19937.dirichlet(p, np.array((2, 2))).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, np.uint32(1)).shape, (1, 2)) + assert_equal(random.dirichlet(p, [2, 2]).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, (2, 2)).shape, (2, 2, 2)) + assert_equal(random.dirichlet(p, np.array((2, 2))).shape, (2, 2, 2)) - assert_raises(TypeError, mt19937.dirichlet, p, float(1)) + assert_raises(TypeError, random.dirichlet, p, float(1)) def test_dirichlet_bad_alpha(self): # gh-2089 alpha = np.array([5.4e-01, -1.0e-16]) - assert_raises(ValueError, mt19937.dirichlet, alpha) + assert_raises(ValueError, random.dirichlet, alpha) def test_exponential(self): random.seed(self.seed) @@ -752,8 +766,8 @@ def test_exponential(self): assert_array_almost_equal(actual, desired, decimal=15) def test_exponential_0(self): - assert_equal(mt19937.exponential(scale=0), 0) - assert_raises(ValueError, mt19937.exponential, scale=-0.) + assert_equal(random.exponential(scale=0), 0) + assert_raises(ValueError, random.exponential, scale=-0.) def test_f(self): random.seed(self.seed) @@ -772,70 +786,70 @@ def test_gamma(self): assert_array_almost_equal(actual, desired, decimal=14) def test_gamma_0(self): - assert_equal(mt19937.gamma(shape=0, scale=0), 0) - assert_raises(ValueError, mt19937.gamma, shape=-0., scale=-0.) + assert_equal(random.gamma(shape=0, scale=0), 0) + assert_raises(ValueError, random.gamma, shape=-0., scale=-0.) def test_geometric(self): - mt19937.seed(self.seed) - actual = mt19937.geometric(.123456789, size=(3, 2)) + random.seed(self.seed) + actual = random.geometric(.123456789, size=(3, 2)) desired = np.array([[8, 7], [17, 17], [5, 12]]) assert_array_equal(actual, desired) def test_gumbel(self): - mt19937.seed(self.seed) - actual = mt19937.gumbel(loc=.123456789, scale=2.0, size=(3, 2)) + random.seed(self.seed) + actual = random.gumbel(loc=.123456789, scale=2.0, size=(3, 2)) desired = np.array([[0.19591898743416816, 0.34405539668096674], [-1.4492522252274278, -1.47374816298446865], [1.10651090478803416, -0.69535848626236174]]) assert_array_almost_equal(actual, desired, decimal=15) def test_gumbel_0(self): - assert_equal(mt19937.gumbel(scale=0), 0) - assert_raises(ValueError, mt19937.gumbel, scale=-0.) + assert_equal(random.gumbel(scale=0), 0) + assert_raises(ValueError, random.gumbel, scale=-0.) def test_hypergeometric(self): - mt19937.seed(self.seed) - actual = mt19937.hypergeometric(10.1, 5.5, 14, size=(3, 2)) + random.seed(self.seed) + actual = random.hypergeometric(10.1, 5.5, 14, size=(3, 2)) desired = np.array([[10, 10], [10, 10], [9, 9]]) assert_array_equal(actual, desired) # Test nbad = 0 - actual = mt19937.hypergeometric(5, 0, 3, size=4) + actual = random.hypergeometric(5, 0, 3, size=4) desired = np.array([3, 3, 3, 3]) assert_array_equal(actual, desired) - actual = mt19937.hypergeometric(15, 0, 12, size=4) + actual = random.hypergeometric(15, 0, 12, size=4) desired = np.array([12, 12, 12, 12]) assert_array_equal(actual, desired) # Test ngood = 0 - actual = mt19937.hypergeometric(0, 5, 3, size=4) + actual = random.hypergeometric(0, 5, 3, size=4) desired = np.array([0, 0, 0, 0]) assert_array_equal(actual, desired) - actual = mt19937.hypergeometric(0, 15, 12, size=4) + actual = random.hypergeometric(0, 15, 12, size=4) desired = np.array([0, 0, 0, 0]) assert_array_equal(actual, desired) def test_laplace(self): - mt19937.seed(self.seed) - actual = mt19937.laplace(loc=.123456789, scale=2.0, size=(3, 2)) + random.seed(self.seed) + actual = random.laplace(loc=.123456789, scale=2.0, size=(3, 2)) desired = np.array([[0.66599721112760157, 0.52829452552221945], [3.12791959514407125, 3.18202813572992005], [-0.05391065675859356, 1.74901336242837324]]) assert_array_almost_equal(actual, desired, decimal=15) def test_laplace_0(self): - assert_equal(mt19937.laplace(scale=0), 0) - assert_raises(ValueError, mt19937.laplace, scale=-0.) + assert_equal(random.laplace(scale=0), 0) + assert_raises(ValueError, random.laplace, scale=-0.) def test_logistic(self): - mt19937.seed(self.seed) - actual = mt19937.logistic(loc=.123456789, scale=2.0, size=(3, 2)) + random.seed(self.seed) + actual = random.logistic(loc=.123456789, scale=2.0, size=(3, 2)) desired = np.array([[1.09232835305011444, 0.8648196662399954], [4.27818590694950185, 4.33897006346929714], [-0.21682183359214885, 2.63373365386060332]]) @@ -850,20 +864,20 @@ def test_lognormal(self): assert_array_almost_equal(actual, desired, decimal=13) def test_lognormal_0(self): - assert_equal(mt19937.lognormal(sigma=0), 1) - assert_raises(ValueError, mt19937.lognormal, sigma=-0.) + assert_equal(random.lognormal(sigma=0), 1) + assert_raises(ValueError, random.lognormal, sigma=-0.) def test_logseries(self): - mt19937.seed(self.seed) - actual = mt19937.logseries(p=.923456789, size=(3, 2)) + random.seed(self.seed) + actual = random.logseries(p=.923456789, size=(3, 2)) desired = np.array([[2, 2], [6, 17], [3, 6]]) assert_array_equal(actual, desired) def test_multinomial(self): - mt19937.seed(self.seed) - actual = mt19937.multinomial(20, [1 / 6.] * 6, size=(3, 2)) + random.seed(self.seed) + actual = random.multinomial(20, [1 / 6.] * 6, size=(3, 2)) desired = np.array([[[4, 3, 5, 4, 2, 2], [5, 2, 8, 2, 2, 1]], [[3, 4, 3, 6, 0, 4], @@ -879,7 +893,6 @@ def test_multivariate_normal(self): size = (3, 2) actual = random.multivariate_normal(mean, cov, size) np.set_printoptions(precision=20) - print(actual) desired = np.array([[[-3.34929721161096100, 9.891061435770858], [-0.12250896439641100, 9.295898449738300]], [[0.48355927611635563, 10.127832101772366], @@ -898,31 +911,31 @@ def test_multivariate_normal(self): # RuntimeWarning mean = [0, 0] cov = [[1, 2], [2, 1]] - assert_warns(RuntimeWarning, mt19937.multivariate_normal, mean, cov) + assert_warns(RuntimeWarning, random.multivariate_normal, mean, cov) # and that it doesn't warn with RuntimeWarning check_valid='ignore' - assert_no_warnings(mt19937.multivariate_normal, mean, cov, + assert_no_warnings(random.multivariate_normal, mean, cov, check_valid='ignore') # and that it raises with RuntimeWarning check_valid='raises' - assert_raises(ValueError, mt19937.multivariate_normal, mean, cov, + assert_raises(ValueError, random.multivariate_normal, mean, cov, check_valid='raise') cov = np.array([[1, 0.1], [0.1, 1]], dtype=np.float32) with suppress_warnings() as sup: - mt19937.multivariate_normal(mean, cov) + random.multivariate_normal(mean, cov) w = sup.record(RuntimeWarning) assert len(w) == 0 mu = np.zeros(2) cov = np.eye(2) - assert_raises(ValueError, mt19937.multivariate_normal, mean, cov, + assert_raises(ValueError, random.multivariate_normal, mean, cov, check_valid='other') - assert_raises(ValueError, mt19937.multivariate_normal, + assert_raises(ValueError, random.multivariate_normal, np.zeros((2, 1, 1)), cov) - assert_raises(ValueError, mt19937.multivariate_normal, + assert_raises(ValueError, random.multivariate_normal, mu, np.empty((3, 2))) - assert_raises(ValueError, mt19937.multivariate_normal, + assert_raises(ValueError, random.multivariate_normal, mu, np.eye(3)) def test_negative_binomial(self): @@ -972,14 +985,12 @@ def test_normal(self): assert_array_almost_equal(actual, desired, decimal=15) def test_normal_0(self): - assert_equal(mt19937.normal(scale=0), 0) - assert_raises(ValueError, mt19937.normal, scale=-0.) + assert_equal(random.normal(scale=0), 0) + assert_raises(ValueError, random.normal, scale=-0.) def test_pareto(self): random.seed(self.seed) actual = random.pareto(a=.123456789, size=(3, 2)) - np.set_printoptions(precision=20) - print(actual) desired = np.array([[5.6883528121891552e+16, 4.0569373841667057e+03], [1.2854967019379475e+12, 6.5833156486851483e+04], [1.1281132447159091e+01, 3.1895968171107006e+08]]) @@ -992,8 +1003,8 @@ def test_pareto(self): np.testing.assert_array_almost_equal_nulp(actual, desired, nulp=30) def test_poisson(self): - mt19937.seed(self.seed) - actual = mt19937.poisson(lam=.123456789, size=(3, 2)) + random.seed(self.seed) + actual = random.poisson(lam=.123456789, size=(3, 2)) desired = np.array([[0, 0], [1, 0], [0, 0]]) @@ -1002,10 +1013,10 @@ def test_poisson(self): def test_poisson_exceptions(self): lambig = np.iinfo('l').max lamneg = -1 - assert_raises(ValueError, mt19937.poisson, lamneg) - assert_raises(ValueError, mt19937.poisson, [lamneg] * 10) - assert_raises(ValueError, mt19937.poisson, lambig) - assert_raises(ValueError, mt19937.poisson, [lambig] * 10) + assert_raises(ValueError, random.poisson, lamneg) + assert_raises(ValueError, random.poisson, [lamneg] * 10) + assert_raises(ValueError, random.poisson, lambig) + assert_raises(ValueError, random.poisson, [lambig] * 10) def test_power(self): random.seed(self.seed) @@ -1016,16 +1027,16 @@ def test_power(self): assert_array_almost_equal(actual, desired, decimal=15) def test_rayleigh(self): - mt19937.seed(self.seed) - actual = mt19937.rayleigh(scale=10, size=(3, 2)) + random.seed(self.seed) + actual = random.rayleigh(scale=10, size=(3, 2)) desired = np.array([[13.8882496494248393, 13.383318339044731], [20.95413364294492098, 21.08285015800712614], [11.06066537006854311, 17.35468505778271009]]) assert_array_almost_equal(actual, desired, decimal=14) def test_rayleigh_0(self): - assert_equal(mt19937.rayleigh(scale=0), 0) - assert_raises(ValueError, mt19937.rayleigh, scale=-0.) + assert_equal(random.rayleigh(scale=0), 0) + assert_raises(ValueError, random.rayleigh, scale=-0.) def test_standard_cauchy(self): random.seed(self.seed) @@ -1036,13 +1047,16 @@ def test_standard_cauchy(self): assert_array_almost_equal(actual, desired, decimal=15) def test_standard_exponential(self): - mt19937.seed(self.seed) - actual = mt19937.standard_exponential(size=(3, 2), method='inv') + random.seed(self.seed) + actual = random.standard_exponential(size=(3, 2), method='inv') desired = np.array([[0.96441739162374596, 0.89556604882105506], [2.1953785836319808, 2.22243285392490542], [0.6116915921431676, 1.50592546727413201]]) assert_array_almost_equal(actual, desired, decimal=15) + def test_standard_expoential_type_error(self): + assert_raises(TypeError, random.standard_exponential, dtype=np.int32) + def test_standard_gamma(self): random.seed(self.seed) actual = random.standard_gamma(shape=3, size=(3, 2)) @@ -1051,6 +1065,12 @@ def test_standard_gamma(self): [0.92121813690910, 1.12853552328470]]) assert_array_almost_equal(actual, desired, decimal=14) + def test_standard_gammma_scalar_float(self): + random.seed(self.seed) + actual = random.standard_gamma(3, dtype=np.float32) + desired = 1.3877466 + assert_array_almost_equal(actual, desired, decimal=7) + def test_standard_gamma_float(self): random.seed(self.seed) actual = random.standard_gamma(shape=3, size=(3, 2)) @@ -1059,13 +1079,33 @@ def test_standard_gamma_float(self): [0.9212181, 1.1285355]]) assert_array_almost_equal(actual, desired, decimal=7) + def test_standard_gammma_float_out(self): + actual = np.zeros((3, 2), dtype=np.float32) + random.seed(self.seed) + random.standard_gamma(10.0, out=actual, dtype=np.float32) + desired = np.array([[6.9824033, 7.3731737], + [14.860578, 7.5327270], + [11.767488, 6.2320185]], dtype=np.float32) + assert_array_almost_equal(actual, desired, decimal=7) + + random.seed(self.seed) + random.standard_gamma(10.0, out=actual, size=(3, 2), dtype=np.float32) + assert_array_almost_equal(actual, desired, decimal=7) + def test_standard_gamma_unknown_type(self): assert_raises(TypeError, random.standard_gamma, 1., dtype='int32') + def test_out_size_mismatch(self): + out = np.zeros(10) + assert_raises(ValueError, random.standard_gamma, 10.0, size=20, + out=out) + assert_raises(ValueError, random.standard_gamma, 10.0, size=(10, 1), + out=out) + def test_standard_gamma_0(self): - assert_equal(mt19937.standard_gamma(shape=0), 0) - assert_raises(ValueError, mt19937.standard_gamma, shape=-0.) + assert_equal(random.standard_gamma(shape=0), 0) + assert_raises(ValueError, random.standard_gamma, shape=-0.) def test_standard_normal(self): random.seed(self.seed) @@ -1075,6 +1115,9 @@ def test_standard_normal(self): [0.360102487116356, 0.127832101772367]]) assert_array_almost_equal(actual, desired, decimal=15) + def test_standard_normal_unsupported_type(self): + assert_raises(TypeError, random.standard_normal, dtype=np.int32) + def test_standard_t(self): random.seed(self.seed) actual = random.standard_t(df=10, size=(3, 2)) @@ -1084,17 +1127,17 @@ def test_standard_t(self): assert_array_almost_equal(actual, desired, decimal=15) def test_triangular(self): - mt19937.seed(self.seed) - actual = mt19937.triangular(left=5.12, mode=10.23, right=20.34, - size=(3, 2)) + random.seed(self.seed) + actual = random.triangular(left=5.12, mode=10.23, right=20.34, + size=(3, 2)) desired = np.array([[12.68117178949215784, 12.4129206149193152], [16.20131377335158263, 16.25692138747600524], [11.20400690911820263, 14.4978144835829923]]) assert_array_almost_equal(actual, desired, decimal=14) def test_uniform(self): - mt19937.seed(self.seed) - actual = mt19937.uniform(low=1.23, high=10.54, size=(3, 2)) + random.seed(self.seed) + actual = random.uniform(low=1.23, high=10.54, size=(3, 2)) desired = np.array([[6.99097932346268003, 6.73801597444323974], [9.50364421400426274, 9.53130618907631089], [5.48995325769805476, 8.47493103280052118]]) @@ -1104,7 +1147,7 @@ def test_uniform_range_bounds(self): fmin = np.finfo('float').min fmax = np.finfo('float').max - func = mt19937.uniform + func = random.uniform assert_raises(OverflowError, func, -np.inf, 0) assert_raises(OverflowError, func, 0, np.inf) assert_raises(OverflowError, func, fmin, fmax) @@ -1114,7 +1157,7 @@ def test_uniform_range_bounds(self): # (fmax / 1e17) - fmin is within range, so this should not throw # account for i386 extended precision DBL_MAX / 1e17 + DBL_MAX > # DBL_MAX by increasing fmin a bit - mt19937.uniform(low=np.nextafter(fmin, 1), high=fmax / 1e17) + random.uniform(low=np.nextafter(fmin, 1), high=fmax / 1e17) def test_scalar_exception_propagation(self): # Tests that exceptions are correctly propagated in distributions @@ -1128,7 +1171,7 @@ def __float__(self): raise TypeError throwing_float = np.array(1.0).view(ThrowingFloat) - assert_raises(TypeError, mt19937.uniform, + assert_raises(TypeError, random.uniform, throwing_float, throwing_float) class ThrowingInteger(np.ndarray): @@ -1136,11 +1179,11 @@ def __int__(self): raise TypeError throwing_int = np.array(1).view(ThrowingInteger) - assert_raises(TypeError, mt19937.hypergeometric, throwing_int, 1, 1) + assert_raises(TypeError, random.hypergeometric, throwing_int, 1, 1) def test_vonmises(self): - mt19937.seed(self.seed) - actual = mt19937.vonmises(mu=1.23, kappa=1.54, size=(3, 2)) + random.seed(self.seed) + actual = random.vonmises(mu=1.23, kappa=1.54, size=(3, 2)) desired = np.array([[2.28567572673902042, 2.89163838442285037], [0.38198375564286025, 2.57638023113890746], [1.19153771588353052, 1.83509849681825354]]) @@ -1148,8 +1191,8 @@ def test_vonmises(self): def test_vonmises_small(self): # check infinite loop, gh-4720 - mt19937.seed(self.seed) - r = mt19937.vonmises(mu=0., kappa=1.1e-8, size=10 ** 6) + random.seed(self.seed) + r = random.vonmises(mu=0., kappa=1.1e-8, size=10 ** 6) assert_(np.isfinite(r).all()) def test_wald(self): @@ -1169,13 +1212,13 @@ def test_weibull(self): assert_array_almost_equal(actual, desired, decimal=15) def test_weibull_0(self): - mt19937.seed(self.seed) - assert_equal(mt19937.weibull(a=0, size=12), np.zeros(12)) - assert_raises(ValueError, mt19937.weibull, a=-0.) + random.seed(self.seed) + assert_equal(random.weibull(a=0, size=12), np.zeros(12)) + assert_raises(ValueError, random.weibull, a=-0.) def test_zipf(self): - mt19937.seed(self.seed) - actual = mt19937.zipf(a=1.23, size=(3, 2)) + random.seed(self.seed) + actual = random.zipf(a=1.23, size=(3, 2)) desired = np.array([[66, 29], [1, 1], [3, 13]]) @@ -1220,20 +1263,20 @@ def test_normal(self): actual = normal(loc * 3, scale) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, normal, loc * 3, bad_scale) - assert_raises(ValueError, mt19937.normal, loc * 3, bad_scale) + assert_raises(ValueError, random.normal, loc * 3, bad_scale) self.set_seed() actual = normal(loc, scale * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, normal, loc, bad_scale * 3) - assert_raises(ValueError, mt19937.normal, loc, bad_scale * 3) + assert_raises(ValueError, random.normal, loc, bad_scale * 3) def test_beta(self): a = [1] b = [2] bad_a = [-1] bad_b = [-2] - beta = mt19937.beta + beta = random.beta desired = np.array([0.63222080311226, 0.33310522220774, 0.64494078460190]) @@ -1251,7 +1294,7 @@ def test_beta(self): def test_exponential(self): scale = [1] bad_scale = [-1] - exponential = mt19937.exponential + exponential = random.exponential desired = np.array([1.68591211640990, 3.14186859487914, 0.67717375919228]) @@ -1264,7 +1307,7 @@ def test_exponential(self): def test_standard_gamma(self): shape = [1] bad_shape = [-1] - std_gamma = mt19937.standard_gamma + std_gamma = random.standard_gamma desired = np.array([1.68591211640990, 3.14186859487914, 0.67717375919228]) @@ -1279,7 +1322,7 @@ def test_gamma(self): scale = [2] bad_shape = [-1] bad_scale = [-2] - gamma = mt19937.gamma + gamma = random.gamma desired = np.array([3.37182423281980, 6.28373718975827, 1.35434751838456]) @@ -1301,7 +1344,7 @@ def test_f(self): dfden = [2] bad_dfnum = [-1] bad_dfden = [-2] - f = mt19937.f + f = random.f desired = np.array([0.84207044881810, 3.08607209903483, 3.12823105933169]) @@ -1325,14 +1368,14 @@ def test_noncentral_f(self): bad_dfnum = [0] bad_dfden = [-1] bad_nonc = [-2] - nonc_f = mt19937.noncentral_f + nonc_f = random.noncentral_f desired = np.array([3.83710578542563, 8.74926819712029, 0.48892943835401]) self.set_seed() actual = nonc_f(dfnum * 3, dfden, nonc) - mt_nonc_f = mt19937.noncentral_f + mt_nonc_f = random.noncentral_f assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, nonc_f, bad_dfnum * 3, dfden, nonc) assert_raises(ValueError, nonc_f, dfnum * 3, bad_dfden, nonc) @@ -1380,7 +1423,7 @@ def test_noncentral_chisquare(self): self.set_seed() actual = nonc_chi(df * 3, nonc) - mt_nonc_chi2 = mt19937.noncentral_chisquare + mt_nonc_chi2 = random.noncentral_chisquare assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, nonc_chi, bad_df * 3, nonc) assert_raises(ValueError, nonc_chi, df * 3, bad_nonc) @@ -1407,7 +1450,7 @@ def test_standard_t(self): actual = t(df * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, t, bad_df * 3) - assert_raises(ValueError, mt19937.standard_t, bad_df * 3) + assert_raises(ValueError, random.standard_t, bad_df * 3) def test_vonmises(self): mu = [2] @@ -1440,7 +1483,7 @@ def test_pareto(self): actual = pareto(a * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, pareto, bad_a * 3) - assert_raises(ValueError, mt19937.pareto, bad_a * 3) + assert_raises(ValueError, random.pareto, bad_a * 3) def test_weibull(self): a = [1] @@ -1454,7 +1497,7 @@ def test_weibull(self): actual = weibull(a * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, weibull, bad_a * 3) - assert_raises(ValueError, mt19937.weibull, bad_a * 3) + assert_raises(ValueError, random.weibull, bad_a * 3) def test_power(self): a = [1] @@ -1468,7 +1511,7 @@ def test_power(self): actual = power(a * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, power, bad_a * 3) - assert_raises(ValueError, mt19937.power, bad_a * 3) + assert_raises(ValueError, random.power, bad_a * 3) def test_laplace(self): loc = [0] @@ -1526,7 +1569,7 @@ def test_logistic(self): actual = logistic(loc, scale * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, logistic, loc, bad_scale * 3) - assert_equal(mt19937.logistic(1.0, 0.0), 1.0) + assert_equal(random.logistic(1.0, 0.0), 1.0) def test_lognormal(self): mean = [0] @@ -1541,13 +1584,13 @@ def test_lognormal(self): actual = lognormal(mean * 3, sigma) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, lognormal, mean * 3, bad_sigma) - assert_raises(ValueError, mt19937.lognormal, mean * 3, bad_sigma) + assert_raises(ValueError, random.lognormal, mean * 3, bad_sigma) self.set_seed() actual = lognormal(mean, sigma * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, lognormal, mean, bad_sigma * 3) - assert_raises(ValueError, mt19937.lognormal, mean, bad_sigma * 3) + assert_raises(ValueError, random.lognormal, mean, bad_sigma * 3) def test_rayleigh(self): scale = [1] @@ -1577,16 +1620,16 @@ def test_wald(self): assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, wald, bad_mean * 3, scale) assert_raises(ValueError, wald, mean * 3, bad_scale) - assert_raises(ValueError, mt19937.wald, bad_mean * 3, scale) - assert_raises(ValueError, mt19937.wald, mean * 3, bad_scale) + assert_raises(ValueError, random.wald, bad_mean * 3, scale) + assert_raises(ValueError, random.wald, mean * 3, bad_scale) self.set_seed() actual = wald(mean, scale * 3) assert_array_almost_equal(actual, desired, decimal=14) assert_raises(ValueError, wald, bad_mean, scale * 3) assert_raises(ValueError, wald, mean, bad_scale * 3) - assert_raises(ValueError, mt19937.wald, bad_mean, scale * 3) - assert_raises(ValueError, mt19937.wald, mean, bad_scale * 3) + assert_raises(ValueError, random.wald, bad_mean, scale * 3) + assert_raises(ValueError, random.wald, mean, bad_scale * 3) def test_triangular(self): left = [1] @@ -1823,14 +1866,14 @@ def setup(self): self.tgtShape = (1,) def test_one_arg_funcs(self): - funcs = (mt19937.exponential, mt19937.standard_gamma, - mt19937.chisquare, mt19937.standard_t, - mt19937.pareto, mt19937.weibull, - mt19937.power, mt19937.rayleigh, - mt19937.poisson, mt19937.zipf, - mt19937.geometric, mt19937.logseries) + funcs = (random.exponential, random.standard_gamma, + random.chisquare, random.standard_t, + random.pareto, random.weibull, + random.power, random.rayleigh, + random.poisson, random.zipf, + random.geometric, random.logseries) - probfuncs = (mt19937.geometric, mt19937.logseries) + probfuncs = (random.geometric, random.logseries) for func in funcs: if func in probfuncs: # p < 1.0 @@ -1842,15 +1885,15 @@ def test_one_arg_funcs(self): assert_equal(out.shape, self.tgtShape) def test_two_arg_funcs(self): - funcs = (mt19937.uniform, mt19937.normal, - mt19937.beta, mt19937.gamma, - mt19937.f, mt19937.noncentral_chisquare, - mt19937.vonmises, mt19937.laplace, - mt19937.gumbel, mt19937.logistic, - mt19937.lognormal, mt19937.wald, - mt19937.binomial, mt19937.negative_binomial) + funcs = (random.uniform, random.normal, + random.beta, random.gamma, + random.f, random.noncentral_chisquare, + random.vonmises, random.laplace, + random.gumbel, random.logistic, + random.lognormal, random.wald, + random.binomial, random.negative_binomial) - probfuncs = (mt19937.binomial, mt19937.negative_binomial) + probfuncs = (random.binomial, random.negative_binomial) for func in funcs: if func in probfuncs: # p <= 1 @@ -1871,7 +1914,7 @@ def test_two_arg_funcs(self): def test_randint(self): itype = [np.bool, np.int8, np.uint8, np.int16, np.uint16, np.int32, np.uint32, np.int64, np.uint64] - func = mt19937.randint + func = random.randint high = np.array([1]) low = np.array([0]) @@ -1886,8 +1929,8 @@ def test_randint(self): assert_equal(out.shape, self.tgtShape) def test_three_arg_funcs(self): - funcs = [mt19937.noncentral_f, mt19937.triangular, - mt19937.hypergeometric] + funcs = [random.noncentral_f, random.triangular, + random.hypergeometric] for func in funcs: out = func(self.argOne, self.argTwo, self.argThree) diff --git a/numpy/random/randomgen/tests/test_numpy_mt19937_regressions.py b/numpy/random/randomgen/tests/test_generator_mt19937_regressions.py similarity index 100% rename from numpy/random/randomgen/tests/test_numpy_mt19937_regressions.py rename to numpy/random/randomgen/tests/test_generator_mt19937_regressions.py diff --git a/numpy/random/randomgen/tests/test_smoke.py b/numpy/random/randomgen/tests/test_smoke.py index b00b9efb297d..15f6142a37ff 100644 --- a/numpy/random/randomgen/tests/test_smoke.py +++ b/numpy/random/randomgen/tests/test_smoke.py @@ -237,11 +237,6 @@ def test_seed(self): rg2 = RandomGenerator(self.brng(*self.seed)) rg.random_sample() rg2.random_sample() - if not comp_state(rg.state, rg2.state): - for key in rg.state: - print(key) - print(rg.state[key]) - print(rg2.state[key]) assert_(comp_state(rg.state, rg2.state)) def test_reset_state_gauss(self): From ac66298eef7b005864c99caa28a2174bcf163c05 Mon Sep 17 00:00:00 2001 From: Kevin Sheppard Date: Mon, 1 Apr 2019 21:00:21 +0100 Subject: [PATCH 3/3] MAINT/DOC: Update import locations Import import locatiosn to reflect numpy paths --- numpy/random/randomgen/dsfmt.pyx | 6 +++--- numpy/random/randomgen/examples/cython/extending.pyx | 4 ++-- .../randomgen/examples/cython/extending_distributions.pyx | 6 +++--- numpy/random/randomgen/generator.pyx | 4 +++- numpy/random/randomgen/mt19937.pyx | 6 +++--- numpy/random/randomgen/pcg32.pyx | 2 +- numpy/random/randomgen/pcg64.pyx | 2 +- numpy/random/randomgen/philox.pyx | 2 +- numpy/random/randomgen/tests/test_against_numpy.py | 2 +- numpy/random/randomgen/threefry.pyx | 2 +- numpy/random/randomgen/threefry32.pyx | 2 +- numpy/random/randomgen/xoroshiro128.pyx | 2 +- numpy/random/randomgen/xorshift1024.pyx | 2 +- numpy/random/randomgen/xoshiro256starstar.pyx | 2 +- numpy/random/randomgen/xoshiro512starstar.pyx | 2 +- 15 files changed, 24 insertions(+), 22 deletions(-) diff --git a/numpy/random/randomgen/dsfmt.pyx b/numpy/random/randomgen/dsfmt.pyx index 129caafff17a..9fa7588f9cad 100644 --- a/numpy/random/randomgen/dsfmt.pyx +++ b/numpy/random/randomgen/dsfmt.pyx @@ -103,8 +103,8 @@ cdef class DSFMT: generators should be initialized with the same seed to ensure that the segments come from the same sequence. - >>> from randomgen.entropy import random_entropy - >>> from randomgen import RandomGenerator, DSFMT + >>> from numpy.random.randomgen.entropy import random_entropy + >>> from numpy.random.randomgen import RandomGenerator, DSFMT >>> seed = random_entropy() >>> rs = [RandomGenerator(DSFMT(seed)) for _ in range(10)] # Advance rs[i] by i jumps @@ -397,7 +397,7 @@ cdef class DSFMT: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the basic RNG """ if self._generator is None: diff --git a/numpy/random/randomgen/examples/cython/extending.pyx b/numpy/random/randomgen/examples/cython/extending.pyx index a8d314bb19a3..b472312b41d8 100644 --- a/numpy/random/randomgen/examples/cython/extending.pyx +++ b/numpy/random/randomgen/examples/cython/extending.pyx @@ -6,8 +6,8 @@ import numpy as np cimport numpy as np cimport cython -from randomgen.common cimport brng_t -from randomgen.xoroshiro128 import Xoroshiro128 +from numpy.random.randomgen.common cimport brng_t +from numpy.random.randomgen import Xoroshiro128 np.import_array() diff --git a/numpy/random/randomgen/examples/cython/extending_distributions.pyx b/numpy/random/randomgen/examples/cython/extending_distributions.pyx index 03f04af041a5..26d749b10c51 100644 --- a/numpy/random/randomgen/examples/cython/extending_distributions.pyx +++ b/numpy/random/randomgen/examples/cython/extending_distributions.pyx @@ -3,9 +3,9 @@ import numpy as np cimport numpy as np cimport cython from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer -from randomgen.common cimport * -from randomgen.distributions cimport random_gauss_zig -from randomgen.xoroshiro128 import Xoroshiro128 +from numpy.random.randomgen.common cimport * +from numpy.random.randomgen.distributions cimport random_gauss_zig +from numpy.random.randomgen import Xoroshiro128 @cython.boundscheck(False) diff --git a/numpy/random/randomgen/generator.pyx b/numpy/random/randomgen/generator.pyx index ffd269f9820b..8a5143164e01 100644 --- a/numpy/random/randomgen/generator.pyx +++ b/numpy/random/randomgen/generator.pyx @@ -411,7 +411,9 @@ cdef class RandomGenerator: Examples -------- - >>> rg = np.random.randomgen.RandomGenerator() # need a RandomGenerator object + Need to construct a RandomGenerator object + + >>> rg = np.random.randomgen.RandomGenerator() >>> rg.tomaxint((2,2,2)) array([[[1170048599, 1600360186], # random [ 739731006, 1947757578]], diff --git a/numpy/random/randomgen/mt19937.pyx b/numpy/random/randomgen/mt19937.pyx index f6c76e7ab1bd..235f12be7b15 100644 --- a/numpy/random/randomgen/mt19937.pyx +++ b/numpy/random/randomgen/mt19937.pyx @@ -99,8 +99,8 @@ cdef class MT19937: generators should be initialized with the same seed to ensure that the segments come from the same sequence. - >>> from randomgen.entropy import random_entropy - >>> from randomgen import RandomGenerator, MT19937 + >>> from numpy.random.randomgen.entropy import random_entropy + >>> from numpy.random.randomgen import RandomGenerator, MT19937 >>> seed = random_entropy() >>> rs = [RandomGenerator(MT19937(seed) for _ in range(10)] # Advance rs[i] by i jumps @@ -354,7 +354,7 @@ cdef class MT19937: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the core PRNG """ if self._generator is None: diff --git a/numpy/random/randomgen/pcg32.pyx b/numpy/random/randomgen/pcg32.pyx index b163f3f950cd..b48108a4cc23 100644 --- a/numpy/random/randomgen/pcg32.pyx +++ b/numpy/random/randomgen/pcg32.pyx @@ -377,7 +377,7 @@ cdef class PCG32: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the core PRNG """ if self._generator is None: diff --git a/numpy/random/randomgen/pcg64.pyx b/numpy/random/randomgen/pcg64.pyx index d69535111b27..3572b17ca7a5 100644 --- a/numpy/random/randomgen/pcg64.pyx +++ b/numpy/random/randomgen/pcg64.pyx @@ -431,7 +431,7 @@ cdef class PCG64: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator using this instance as the core RNG """ if self._generator is None: diff --git a/numpy/random/randomgen/philox.pyx b/numpy/random/randomgen/philox.pyx index 05f3b17b4c51..235c76e6b5da 100644 --- a/numpy/random/randomgen/philox.pyx +++ b/numpy/random/randomgen/philox.pyx @@ -472,7 +472,7 @@ cdef class Philox: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the core PRNG """ if self._generator is None: diff --git a/numpy/random/randomgen/tests/test_against_numpy.py b/numpy/random/randomgen/tests/test_against_numpy.py index 14f21aad3a2d..8a9ef2962750 100644 --- a/numpy/random/randomgen/tests/test_against_numpy.py +++ b/numpy/random/randomgen/tests/test_against_numpy.py @@ -6,7 +6,7 @@ import pytest from numpy.random.randomgen import RandomGenerator, MT19937, generator -from numpy.random.randomgen.mtrand import RandomState +from numpy.random import RandomState def compare_0_input(f1, f2): diff --git a/numpy/random/randomgen/threefry.pyx b/numpy/random/randomgen/threefry.pyx index aa4ce622dd08..03d245ea1e60 100644 --- a/numpy/random/randomgen/threefry.pyx +++ b/numpy/random/randomgen/threefry.pyx @@ -463,7 +463,7 @@ cdef class ThreeFry: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the core PRNG """ if self._generator is None: diff --git a/numpy/random/randomgen/threefry32.pyx b/numpy/random/randomgen/threefry32.pyx index 81f3ee93e040..e497926f55cf 100644 --- a/numpy/random/randomgen/threefry32.pyx +++ b/numpy/random/randomgen/threefry32.pyx @@ -459,7 +459,7 @@ cdef class ThreeFry32: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the core PRNG """ if self._generator is None: diff --git a/numpy/random/randomgen/xoroshiro128.pyx b/numpy/random/randomgen/xoroshiro128.pyx index 2ae876ea6be8..be10cb430ba1 100644 --- a/numpy/random/randomgen/xoroshiro128.pyx +++ b/numpy/random/randomgen/xoroshiro128.pyx @@ -349,7 +349,7 @@ cdef class Xoroshiro128: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the basic RNG """ if self._generator is None: diff --git a/numpy/random/randomgen/xorshift1024.pyx b/numpy/random/randomgen/xorshift1024.pyx index 28b80cc9a96a..bff569990871 100644 --- a/numpy/random/randomgen/xorshift1024.pyx +++ b/numpy/random/randomgen/xorshift1024.pyx @@ -357,7 +357,7 @@ cdef class Xorshift1024: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the core PRNG """ if self._generator is None: diff --git a/numpy/random/randomgen/xoshiro256starstar.pyx b/numpy/random/randomgen/xoshiro256starstar.pyx index d45540aa9a23..175c6e7e6896 100644 --- a/numpy/random/randomgen/xoshiro256starstar.pyx +++ b/numpy/random/randomgen/xoshiro256starstar.pyx @@ -354,7 +354,7 @@ cdef class Xoshiro256StarStar: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the basic RNG """ if self._generator is None: diff --git a/numpy/random/randomgen/xoshiro512starstar.pyx b/numpy/random/randomgen/xoshiro512starstar.pyx index 0a6b104fdad2..96d62ea68797 100644 --- a/numpy/random/randomgen/xoshiro512starstar.pyx +++ b/numpy/random/randomgen/xoshiro512starstar.pyx @@ -348,7 +348,7 @@ cdef class Xoshiro512StarStar: Returns ------- - gen : randomgen.generator.RandomGenerator + gen : numpy.random.randomgen.generator.RandomGenerator Random generator used this instance as the basic RNG """ if self._generator is None: