@@ -21,6 +21,7 @@ from cpython.mem cimport PyMem_Malloc, PyMem_Free
21
21
import randomstate
22
22
from binomial cimport binomial_t
23
23
from cython_overrides cimport PyFloat_AsDouble , PyInt_AsLong , PyErr_Occurred , PyErr_Clear
24
+ from randomstate .entropy import random_entropy
24
25
25
26
np .import_array ()
26
27
@@ -119,6 +120,20 @@ include "array_utilities.pxi"
119
120
include "bounded_integers.pxi"
120
121
include "aligned_malloc.pxi"
121
122
123
+ cdef object _generate_seed (nbytes ):
124
+ try :
125
+ seeds = random_entropy (nbytes )
126
+ except :
127
+ seeds = random_entropy (nbytes , 'fallback' )
128
+ if nbytes == 1 :
129
+ return seeds [0 ]
130
+
131
+ seed = long (0 )
132
+ for i in range (nbytes ):
133
+ scale = 2 ** (32 * i )
134
+ seed += scale * long (seeds [i ])
135
+ return seed
136
+
122
137
cdef double kahan_sum (double * darr , np .npy_intp n ):
123
138
cdef double c , y , t , sum
124
139
cdef np .npy_intp i
@@ -148,7 +163,8 @@ cdef class RandomState:
148
163
cdef object lock
149
164
poisson_lam_max = POISSON_LAM_MAX
150
165
__MAXSIZE = < uint64_t > sys .maxsize
151
-
166
+ cdef object __seed
167
+ cdef object __stream
152
168
153
169
IF RS_RNG_SEED == 1 :
154
170
def __init__ (self , seed = None ):
@@ -157,15 +173,22 @@ cdef class RandomState:
157
173
IF RS_RNG_MOD_NAME == 'dsfmt' :
158
174
self .rng_state .buffered_uniforms = < double * > PyArray_malloc_aligned (2 * DSFMT_N * sizeof (double ))
159
175
self .lock = Lock ()
176
+ self .__seed = seed
177
+ self .__stream = None
178
+
160
179
self ._reset_state_variables ()
161
180
self .seed (seed )
162
181
ELSE :
163
- def __init__ (self , seed = None , inc = None ):
182
+ def __init__ (self , seed = None , stream = None ):
164
183
self .rng_state .rng = < rng_t * > PyArray_malloc_aligned (sizeof (rng_t ))
165
184
self .rng_state .binomial = & self .binomial_info
166
185
self .lock = Lock ()
186
+ self .__seed = seed
187
+ self .__stream = stream
188
+
167
189
self ._reset_state_variables ()
168
- self .seed (seed , inc )
190
+ self .seed (seed , stream )
191
+
169
192
170
193
def __dealloc__ (self ):
171
194
PyArray_free_aligned (self .rng_state .rng )
@@ -196,8 +219,9 @@ cdef class RandomState:
196
219
# cdef ndarray obj "arrayObject_obj"
197
220
try :
198
221
if seed is None :
222
+ self .__seed = seed = _generate_seed (1 )
199
223
with self .lock :
200
- entropy_init (& self .rng_state )
224
+ set_seed (& self .rng_state , seed )
201
225
else :
202
226
idx = operator .index (seed )
203
227
if idx > int (2 ** 32 - 1 ) or idx < 0 :
@@ -214,7 +238,7 @@ cdef class RandomState:
214
238
self ._reset_state_variables ()
215
239
216
240
ELIF RS_RNG_SEED == 1 :
217
- def seed (self , val = None ):
241
+ def seed (self , seed = None ):
218
242
"""
219
243
seed(seed=None)
220
244
@@ -225,7 +249,7 @@ cdef class RandomState:
225
249
226
250
Parameters
227
251
----------
228
- val : int, optional
252
+ seed : int, optional
229
253
Seed for ``RandomState``.
230
254
231
255
Notes
@@ -239,18 +263,18 @@ cdef class RandomState:
239
263
--------
240
264
RandomState
241
265
"""
242
- if val is not None :
243
- if val < 0 :
244
- raise ValueError ('val < 0' )
245
- set_seed (& self .rng_state , val )
266
+ if seed is not None :
267
+ if seed < 0 :
268
+ raise ValueError ('seed < 0' )
246
269
else :
247
- entropy_init (& self .rng_state )
270
+ self .__seed = seed = _generate_seed (RS_SEED_NBYTES )
271
+ set_seed (& self .rng_state , seed )
248
272
self ._reset_state_variables ()
249
273
250
274
ELSE :
251
- def seed (self , val = None , inc = None ):
275
+ def seed (self , seed = None , stream = None ):
252
276
"""
253
- seed(val =None, inc =None)
277
+ seed(seed =None, stream =None)
254
278
255
279
Seed the generator.
256
280
@@ -259,31 +283,33 @@ cdef class RandomState:
259
283
260
284
Parameters
261
285
----------
262
- val : int, optional
286
+ seed : int, optional
263
287
Seed for ``RandomState``.
264
- inc : int, optional
265
- Increment to use for producing multiple streams
288
+ stream : int, optional
289
+ Generator stream to use
266
290
267
291
See Also
268
292
--------
269
293
RandomState
270
294
"""
271
- if val is not None and inc is not None :
272
- if val < 0 :
273
- raise ValueError ('val < 0' )
274
- if inc < 0 :
275
- raise ValueError ('inc < 0' )
276
- IF RS_RNG_MOD_NAME == 'pcg64' :
277
- IF RS_PCG128_EMULATED :
278
- set_seed (& self .rng_state ,
279
- pcg128_from_pylong (val ),
280
- pcg128_from_pylong (inc ))
281
- ELSE :
282
- set_seed (& self .rng_state , val , inc )
295
+ if seed is None :
296
+ self .__seed = seed = _generate_seed (RS_SEED_NBYTES )
297
+ elif seed < 0 :
298
+ raise ValueError ('seed < 0' )
299
+ if stream is None :
300
+ self .__stream = stream = 1
301
+ elif stream < 0 :
302
+ raise ValueError ('stream < 0' )
303
+
304
+ IF RS_RNG_MOD_NAME == 'pcg64' :
305
+ IF RS_PCG128_EMULATED :
306
+ set_seed (& self .rng_state ,
307
+ pcg128_from_pylong (seed ),
308
+ pcg128_from_pylong (stream ))
283
309
ELSE :
284
- set_seed (& self .rng_state , val , inc )
285
- else :
286
- entropy_init (& self .rng_state )
310
+ set_seed (& self .rng_state , seed , stream )
311
+ ELSE :
312
+ set_seed (& self .rng_state , seed , stream )
287
313
self ._reset_state_variables ()
288
314
289
315
def _reset_state_variables (self ):
@@ -400,11 +426,14 @@ cdef class RandomState:
400
426
+ _get_state (self .rng_state ) \
401
427
+ (self .rng_state .has_gauss , self .rng_state .gauss )
402
428
403
- return {'name' : rng_name ,
429
+ state = {'name' : rng_name ,
404
430
'state' : _get_state (self .rng_state ),
405
431
'gauss' : {'has_gauss' : self .rng_state .has_gauss , 'gauss' : self .rng_state .gauss },
406
- 'uint32' : {'has_uint32' : self .rng_state .has_uint32 , 'uint32' : self .rng_state .uinteger }
407
- }
432
+ 'uint32' : {'has_uint32' : self .rng_state .has_uint32 , 'uint32' : self .rng_state .uinteger },
433
+ 'seed' : self .__seed }
434
+ if self .__stream is not None :
435
+ state ['stream' ] = self .__stream
436
+ return state
408
437
ELSE :
409
438
def get_state (self ):
410
439
"""
@@ -438,11 +467,14 @@ cdef class RandomState:
438
467
component, see the class documentation.
439
468
"""
440
469
rng_name = _ensure_string (RS_RNG_NAME )
441
- return {'name' : rng_name ,
470
+ state = {'name' : rng_name ,
442
471
'state' : _get_state (self .rng_state ),
443
472
'gauss' : {'has_gauss' : self .rng_state .has_gauss , 'gauss' : self .rng_state .gauss },
444
- 'uint32' : {'has_uint32' : self .rng_state .has_uint32 , 'uint32' : self .rng_state .uinteger }
445
- }
473
+ 'uint32' : {'has_uint32' : self .rng_state .has_uint32 , 'uint32' : self .rng_state .uinteger },
474
+ 'seed' : self .__seed }
475
+ if self .__stream is not None :
476
+ state ['stream' ] = self .__stream
477
+ return state
446
478
447
479
def set_state (self , state ):
448
480
"""
@@ -505,6 +537,8 @@ cdef class RandomState:
505
537
self .rng_state .gauss = state ['gauss' ]['gauss' ]
506
538
self .rng_state .has_uint32 = state ['uint32' ]['has_uint32' ]
507
539
self .rng_state .uinteger = state ['uint32' ]['uint32' ]
540
+ self .__seed = state ['seed' ]
541
+ self .__stream = state ['stream' ] if 'stream' in state else None
508
542
509
543
def random_uintegers (self , size = None , int bits = 64 ):
510
544
"""
@@ -566,7 +600,9 @@ cdef class RandomState:
566
600
self .set_state (state )
567
601
568
602
def __reduce__ (self ):
569
- return (randomstate .prng .__generic_ctor , (RS_RNG_MOD_NAME ,), self .get_state ())
603
+ return (randomstate .prng .__generic_ctor ,
604
+ (_ensure_string (RS_RNG_MOD_NAME ),),
605
+ self .get_state ())
570
606
571
607
# Basic distributions:
572
608
def random_sample (self , size = None ):
0 commit comments