diff options
Diffstat (limited to 'numpy/random/threefry.pyx')
-rw-r--r-- | numpy/random/threefry.pyx | 466 |
1 files changed, 0 insertions, 466 deletions
diff --git a/numpy/random/threefry.pyx b/numpy/random/threefry.pyx deleted file mode 100644 index eb98bc0af..000000000 --- a/numpy/random/threefry.pyx +++ /dev/null @@ -1,466 +0,0 @@ -from cpython.pycapsule cimport PyCapsule_New - -try: - from threading import Lock -except ImportError: - from dummy_threading import Lock - -import numpy as np - -from .common cimport * -from .distributions cimport bitgen_t -from .entropy import random_entropy, seed_by_array - -__all__ = ['ThreeFry'] - -np.import_array() - -DEF THREEFRY_BUFFER_SIZE=4 - -cdef extern from 'src/threefry/threefry.h': - struct s_r123array4x64: - uint64_t v[4] - - ctypedef s_r123array4x64 r123array4x64 - - ctypedef r123array4x64 threefry4x64_key_t - 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] - int has_uint32 - uint32_t uinteger - - ctypedef s_threefry_state threefry_state - - uint64_t threefry_next64(threefry_state *state) nogil - uint32_t threefry_next32(threefry_state *state) nogil - void threefry_jump(threefry_state *state) - void threefry_advance(uint64_t *step, threefry_state *state) - - -cdef uint64_t threefry_uint64(void* st) nogil: - return threefry_next64(<threefry_state *>st) - -cdef uint32_t threefry_uint32(void *st) nogil: - return threefry_next32(<threefry_state *> st) - -cdef double threefry_double(void* st) nogil: - return uint64_to_double(threefry_next64(<threefry_state *>st)) - -cdef class ThreeFry: - """ - ThreeFry(seed=None, counter=None, key=None) - - Container for the ThreeFry (4x64) pseudo-random number generator. - - Parameters - ---------- - seed : {None, int, array_like}, optional - Random seed initializing the pseudo-random number generator. - Can be an integer in [0, 2**64-1], array of integers in - [0, 2**64-1] or ``None`` (the default). If `seed` is ``None``, - data will be read from ``/dev/urandom`` (or the Windows analog) - if available. If unavailable, a hash of the time and process ID is - used. - counter : {None, int, array_like}, optional - Counter to use in the ThreeFry state. Can be either - a Python int in [0, 2**256) or a 4-element uint64 array. - If not provided, the RNG is initialized at 0. - key : {None, int, array_like}, optional - Key to use in the ThreeFry state. Unlike seed, which is run through - another RNG before use, the value in key is directly set. Can be either - a Python int in [0, 2**256) or a 4-element uint64 array. - key and seed cannot both be used. - - 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 - ----- - ThreeFry is a 64-bit PRNG that uses a counter-based design based on - weaker (and faster) versions of cryptographic functions [1]_. Instances - using different values of the key produce independent sequences. ``ThreeFry`` - has a period of :math:`2^{256} - 1` and supports arbitrary advancing and - jumping the sequence in increments of :math:`2^{128}`. These features allow - multiple non-overlapping sequences to be generated. - - ``ThreeFry`` 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. - - See ``Philox`` for a closely related PRNG. - - **State and Seeding** - - The ``ThreeFry`` state vector consists of a 2 256-bit values encoded as - 4-element uint64 arrays. One is a counter which is incremented by 1 for - every 4 64-bit randoms produced. The second is a key which determined - the sequence produced. Using different keys produces independent - sequences. - - ``ThreeFry`` is seeded using either a single 64-bit unsigned integer - or a vector of 64-bit unsigned integers. In either case, the seed is - used as an input for a second random number generator, - SplitMix64, and the output of this PRNG function is used as the initial state. - Using a single 64-bit value for the seed can only initialize a small range of - the possible initial state values. - - **Parallel Features** - - ``ThreeFry`` can be used in parallel applications by calling the ``jumped`` - method to advances the state as-if :math:`2^{128}` random numbers have - been generated. Alternatively, ``advance`` can be used to advance the - counter for any positive step in [0, 2**256). When using ``jumped``, all - generators should be chained to ensure that the segments come from the same - sequence. - - >>> from numpy.random import Generator, ThreeFry - >>> bit_generator = ThreeFry(1234) - >>> rg = [] - >>> for _ in range(10): - ... rg.append(Generator(bit_generator)) - ... # Chain the BitGenerators - ... bit_generator = bit_generator.jumped() - - Alternatively, ``ThreeFry`` can be used in parallel applications by using - a sequence of distinct keys where each instance uses different key. - - >>> key = 2**196 + 2**132 + 2**65 + 2**33 + 2**17 + 2**9 - >>> rg = [Generator(ThreeFry(key=key+i)) for i in range(10)] - - **Compatibility Guarantee** - - ``ThreeFry`` makes a guarantee that a fixed seed and will always produce - the same random integer stream. - - Examples - -------- - >>> from numpy.random import Generator, ThreeFry - >>> rg = Generator(ThreeFry(1234)) - >>> rg.standard_normal() - 0.123 # random - - References - ---------- - .. [1] John K. Salmon, Mark A. Moraes, Ron O. Dror, and David E. Shaw, - "Parallel Random Numbers: As Easy as 1, 2, 3," Proceedings of - the International Conference for High Performance Computing, - Networking, Storage and Analysis (SC11), New York, NY: ACM, 2011. - """ - cdef threefry_state rng_state - cdef threefry4x64_ctr_t threefry_ctr - cdef threefry4x64_key_t threefry_key - cdef bitgen_t _bitgen - cdef public object capsule - cdef object _ctypes - cdef object _cffi - cdef public object lock - - def __init__(self, seed=None, counter=None, key=None): - self.rng_state.ctr = &self.threefry_ctr - self.rng_state.key = &self.threefry_key - self.seed(seed, counter, key) - self.lock = Lock() - - self._bitgen.state = <void *>&self.rng_state - self._bitgen.next_uint64 = &threefry_uint64 - self._bitgen.next_uint32 = &threefry_uint32 - self._bitgen.next_double = &threefry_double - self._bitgen.next_raw = &threefry_uint64 - - 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 - - cdef _reset_state_variables(self): - self.rng_state.has_uint32 = 0 - self.rng_state.uinteger = 0 - self.rng_state.buffer_pos = THREEFRY_BUFFER_SIZE - 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 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, counter=None, key=None): - """ - seed(seed=None, counter=None, key=None) - - Seed the generator. - - This method is called when ``ThreeFry`` is initialized. It can be - called again to re-seed the generator. For details, see - ``ThreeFry``. - - Parameters - ---------- - seed : int, optional - Seed for ``ThreeFry``. - counter : {None, int array}, optional - Positive integer less than 2**256 containing the counter position - or a 4 element array of uint64 containing the counter - key : {None, int, array}, optional - Positive integer less than 2**256 containing the key - or a 4 element array of uint64 containing the key. key and - seed cannot be simultaneously used. - - Raises - ------ - ValueError - If values are out of range for the PRNG. - - Notes - ----- - The two representation of the counter and key are related through - array[i] = (value // 2**(64*i)) % 2**64. - """ - if seed is not None and key is not None: - raise ValueError('seed and key cannot be both used') - if key is None: - if seed is None: - try: - state = random_entropy(8) - except RuntimeError: - state = random_entropy(8, 'fallback') - state = state.view(np.uint64) - else: - state = seed_by_array(seed, 4) - for i in range(4): - self.rng_state.key.v[i] = state[i] - else: - key = int_to_array(key, 'key', 256, 64) - for i in range(4): - self.rng_state.key.v[i] = key[i] - - counter = 0 if counter is None else counter - counter = int_to_array(counter, 'counter', 256, 64) - for i in range(4): - self.rng_state.ctr.v[i] = counter[i] - - self._reset_state_variables() - - @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 - """ - ctr = np.empty(4, dtype=np.uint64) - key = np.empty(4, dtype=np.uint64) - buffer = np.empty(THREEFRY_BUFFER_SIZE, dtype=np.uint64) - for i in range(4): - ctr[i] = self.rng_state.ctr.v[i] - 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} - return {'bit_generator': self.__class__.__name__, - 'state': state, - 'buffer': buffer, - 'buffer_pos': self.rng_state.buffer_pos, - 'has_uint32': self.rng_state.has_uint32, - 'uinteger': self.rng_state.uinteger} - - @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__)) - for i in range(4): - self.rng_state.ctr.v[i] = <uint64_t>value['state']['counter'][i] - self.rng_state.key.v[i] = <uint64_t>value['state']['key'][i] - for i in range(THREEFRY_BUFFER_SIZE): - self.rng_state.buffer[i] = <uint64_t>value['buffer'][i] - self.rng_state.has_uint32 = value['has_uint32'] - self.rng_state.uinteger = value['uinteger'] - self.rng_state.buffer_pos = value['buffer_pos'] - - 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. - """ - self.advance(iter * int(2**128)) - - def jumped(self, jumps=1): - """ - jumped(jumps=1) - - Returns a new bit generator with the state jumped - - The state of the returned big generator is jumped as-if - 2**(128 * jumps) random numbers have been generated. - - Parameters - ---------- - iter : integer, positive - Number of times to jump the state of the bit generator returned - - Returns - ------- - bit_generator : ThreeFry - New instance of generator jumped iter times - """ - cdef ThreeFry bit_generator - - bit_generator = self.__class__() - bit_generator.state = self.state - bit_generator.jump_inplace(jumps) - - return bit_generator - - 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 : ThreeFry - 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. - - Advancing the RNG state resets any pre-computed random numbers. - This is required to ensure exact reproducibility. - """ - delta = wrap_int(delta, 256) - - cdef np.ndarray delta_a - delta_a = int_to_array(delta, 'step', 256, 64) - threefry_advance(<uint64_t *>delta_a.data, &self.rng_state) - self._reset_state_variables() - 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 BitGenerator 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 BitGenerator struct - """ - if self._cffi is not None: - return self._cffi - self._cffi = prepare_cffi(&self._bitgen) - return self._cffi |