diff options
Diffstat (limited to 'numpy/random/pcg32.pyx')
-rw-r--r-- | numpy/random/pcg32.pyx | 402 |
1 files changed, 0 insertions, 402 deletions
diff --git a/numpy/random/pcg32.pyx b/numpy/random/pcg32.pyx deleted file mode 100644 index 612a1b58d..000000000 --- a/numpy/random/pcg32.pyx +++ /dev/null @@ -1,402 +0,0 @@ -try: - from threading import Lock -except ImportError: - from dummy_threading import Lock - -from cpython.pycapsule cimport PyCapsule_New - -import numpy as np -cimport numpy as np - -from .common cimport * -from .distributions cimport bitgen_t -from .entropy import random_entropy - -__all__ = ['PCG32'] - -np.import_array() - -cdef extern from "src/pcg32/pcg32.h": - - cdef struct pcg_state_setseq_64: - uint64_t state - uint64_t inc - - ctypedef pcg_state_setseq_64 pcg32_random_t - - struct s_pcg32_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_advance_state(pcg32_state *state, uint64_t step) - void pcg32_set_seed(pcg32_state *state, uint64_t seed, uint64_t inc) - -cdef uint64_t pcg32_uint64(void* st) nogil: - return pcg32_next64(<pcg32_state *>st) - -cdef uint32_t pcg32_uint32(void *st) nogil: - return pcg32_next32(<pcg32_state *> st) - -cdef double pcg32_double(void* st) nogil: - return pcg32_next_double(<pcg32_state *>st) - -cdef uint64_t pcg32_raw(void* st) nogil: - return <uint64_t>pcg32_next32(<pcg32_state *> st) - - -cdef class PCG32: - u""" - PCG32(seed=None, inc=0) - - Container for the PCG-32 pseudo-random number generator. - - Parameters - ---------- - seed : {None, long}, optional - Random seed initializing the pseudo-random number generator. - Can be an integer in [0, 2**64] or ``None`` (the default). - If `seed` is ``None``, then ``PCG32`` will try to read data - from ``/dev/urandom`` (or the Windows analog) if available. If - unavailable, a 64-bit hash of the time and process ID is used. - inc : {None, int}, optional - Stream to return. - Can be an integer in [0, 2**64] or ``None`` (the default). If `inc` is - ``None``, then 0 is used. Can be used with the same seed to - produce multiple streams using other values of inc. - - Attributes - ---------- - lock: threading.Lock - Lock instance that is shared so that the same bit git generator can - be used in multiple Generators without corrupting the state. Code that - generates values from a bit generator should hold the bit generator's - lock. - - Notes - ----- - PCG-32 is a 64-bit implementation of O'Neill's permutation congruential - generator ([1]_, [2]_). PCG-32 has a period of :math:`2^{64}` and supports - advancing an arbitrary number of steps as well as :math:`2^{63}` streams. - - ``PCG32`` provides a capsule containing function pointers that produce - doubles, and unsigned 32 and 64- bit integers. These are not - directly consumable in Python and must be consumed by a ``Generator`` - or similar object that supports low-level access. - - Supports the method advance to advance the RNG an arbitrary number of - steps. The state of the PCG-32 PRNG is represented by 2 64-bit unsigned - integers. - - See ``PCG64`` for a similar implementation with a smaller period. - - **State and Seeding** - - The ``PCG32`` state vector consists of 2 unsigned 64-bit values. - ``PCG32`` is seeded using a single 64-bit unsigned integer. - In addition, a second 64-bit unsigned integer is used to set the stream. - - **Parallel Features** - - ``PCG32`` can be used in parallel applications in one of two ways. - The preferable method is to use sub-streams, which are generated by using the - same value of ``seed`` and incrementing the second value, ``inc``. - - >>> from numpy.random import Generator, PCG32 - >>> rg = [Generator(PCG32(1234, i + 1)) for i in range(10)] - - The alternative method is to call ``advance`` with a different value on - each instance to produce non-overlapping sequences. - - >>> rg = [Generator(PCG32(1234, i + 1)) for i in range(10)] - >>> for i in range(10): - ... rg[i].bit_generator.advance(i * 2**32) - - **Compatibility Guarantee** - - ``PCG32`` makes a guarantee that a fixed seed and will always produce - the same random integer stream. - - References - ---------- - .. [1] "PCG, A Family of Better Random Number Generators", - http://www.pcg-random.org/ - .. [2] O'Neill, Melissa E. "PCG: A Family of Simple Fast Space-Efficient - Statistically Good Algorithms for Random Number Generation" - """ - cdef pcg32_state rng_state - cdef pcg32_random_t pcg32_random_state - cdef bitgen_t _bitgen - cdef public object capsule - cdef object _ctypes - cdef object _cffi - cdef public object lock - - def __init__(self, seed=None, inc=0): - self.rng_state.pcg_state = &self.pcg32_random_state - self.seed(seed, inc) - self.lock = Lock() - - self._bitgen.state = <void *>&self.rng_state - self._bitgen.next_uint64 = &pcg32_uint64 - self._bitgen.next_uint32 = &pcg32_uint32 - self._bitgen.next_double = &pcg32_double - self._bitgen.next_raw = &pcg32_raw - - self._ctypes = None - self._cffi = None - - cdef const char *name = "BitGenerator" - self.capsule = PyCapsule_New(<void *>&self._bitgen, name, NULL) - - # Pickling support: - def __getstate__(self): - return self.state - - def __setstate__(self, state): - self.state = state - - def __reduce__(self): - from ._pickle import __bit_generator_ctor - return __bit_generator_ctor, (self.state['bit_generator'],), self.state - - def random_raw(self, size=None, output=True): - """ - random_raw(self, size=None) - - Return randoms as generated by the underlying BitGenerator - - 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._bitgen, self.lock, size, output) - - def _benchmark(self, Py_ssize_t cnt, method=u'uint64'): - return benchmark(&self._bitgen, self.lock, cnt, method) - - def seed(self, seed=None, inc=0): - """ - seed(seed=None, inc=0) - - Seed the generator. - - This method is called at initialization. It can be called again to - re-seed the generator. - - Parameters - ---------- - seed : int, optional - Seed for ``PCG64``. Integer between 0 and 2**64-1. - inc : int, optional - Increment to use for PCG stream. Integer between 0 and 2**64-1. - - Raises - ------ - ValueError - If seed values are out of range for the PRNG. - """ - ub = 2 ** 64 - if seed is None: - try: - seed = <np.ndarray>random_entropy(2) - except RuntimeError: - seed = <np.ndarray>random_entropy(2, 'fallback') - seed = seed.view(np.uint64).squeeze() - else: - err_msg = 'seed must be a scalar integer between 0 and ' \ - '{ub}'.format(ub=ub) - if not np.isscalar(seed): - raise TypeError(err_msg) - if int(seed) != seed: - raise TypeError(err_msg) - if seed < 0 or seed > ub: - raise ValueError(err_msg) - - if not np.isscalar(inc): - raise TypeError('inc must be a scalar integer between 0 ' - 'and {ub}'.format(ub=ub)) - if inc < 0 or inc > ub or int(inc) != inc: - raise ValueError('inc must be a scalar integer between 0 ' - 'and {ub}'.format(ub=ub)) - - pcg32_set_seed(&self.rng_state, <uint64_t>seed, <uint64_t>inc) - - cdef jump_inplace(self, iter): - """ - Jump state in-place - - Not part of public API - - Parameters - ---------- - iter : integer, positive - Number of times to jump the state of the rng. - - Notes - ----- - The step size is phi when divided by the period 2**64 - """ - step = int(0x9e3779b97f4a7c16) - self.advance(iter * step) - - def jumped(self, jumps=1): - """ - jumped(jumps=1) - - Returns a new bit generator with the state jumped - - Jumps the state as-if 11400714819323198486 * jumps random numbers - have been generated. - - Parameters - ---------- - jumps : integer, positive - Number of times to jump the state of the bit generator returned - - Returns - ------- - bit_generator : PCG32 - New instance of generator jumped iter times - - Notes - ----- - The jump size is phi-1 when divided by the period 2**64 - """ - cdef PCG32 bit_generator - - bit_generator = self.__class__() - bit_generator.state = self.state - bit_generator.jump_inplace(jumps) - - return bit_generator - - @property - def state(self): - """ - Get or set the PRNG state - - Returns - ------- - state : dict - Dictionary containing the information required to describe the - state of the PRNG - """ - return {'bit_generator': self.__class__.__name__, - 'state': {'state': self.rng_state.pcg_state.state, - 'inc': self.rng_state.pcg_state.inc}} - - @state.setter - def state(self, value): - if not isinstance(value, dict): - raise TypeError('state must be a dict') - bitgen = value.get('bit_generator', '') - if bitgen != 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.inc = value['state']['inc'] - - def advance(self, delta): - """ - advance(delta) - - Advance the underlying RNG as-if delta draws have occurred. - - Parameters - ---------- - delta : integer, positive - Number of draws to advance the RNG. Must be less than the - size state variable in the underlying RNG. - - Returns - ------- - self : PCG32 - RNG advanced delta steps - - Notes - ----- - Advancing a RNG updates the underlying RNG state as-if a given - number of calls to the underlying RNG have been made. In general - there is not a one-to-one relationship between the number output - random values from a particular distribution and the number of - draws from the core RNG. This occurs for two reasons: - - * The random values are simulated using a rejection-based method - and so, on average, more than one value from the underlying - RNG is required to generate an single draw. - * The number of bits required to generate a simulated value - differs from the number of bits generated by the underlying - RNG. For example, two 16-bit integer values can be simulated - from a single draw of a 32-bit RNG. - """ - delta = wrap_int(delta, 64) - pcg32_advance_state(&self.rng_state, <uint64_t>delta) - return self - - @property - def ctypes(self): - """ - ctypes interface - - Returns - ------- - interface : namedtuple - Named tuple containing ctypes wrapper - - * 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 - * bitgen - pointer to the bit generator struct - """ - if self._ctypes is None: - self._ctypes = prepare_ctypes(&self._bitgen) - - return self._ctypes - - @property - def cffi(self): - """ - CFFI interface - - Returns - ------- - interface : namedtuple - Named tuple containing CFFI wrapper - - * 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 - * bitgen - pointer to the bit generator struct - """ - if self._cffi is not None: - return self._cffi - self._cffi = prepare_cffi(&self._bitgen) - return self._cffi |