Skip to content

Commit 7664231

Browse files
committed
FIX: Fix test failures against current NumPy
Update mtrand to latest NumPY Enable tests that were wrongly skipped Fix issue wiht NumPy legacy state Fix constraints for vectors in bernouli Small typo fixes in the documentation
1 parent 768c26e commit 7664231

File tree

12 files changed

+152
-111
lines changed

12 files changed

+152
-111
lines changed

doc/source/index.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ that change the core random number generator.
66
What's New or Different
77
=======================
88
* ``random_uintegers`` produces either 32-bit or 64-bit unsigned integers.
9-
These are at thte core of most PRNGs and so they are directly exposed.
9+
These are at the core of most PRNGs and so they are directly exposed.
1010
* ``randomstate.entropy.random_entropy`` provides access to the system
1111
source of randomness that is used in cryptographic applications (e.g.,
1212
``/dev/urandom`` on Unix).
@@ -21,7 +21,7 @@ What's New or Different
2121
2222
Supported Generators
2323
====================
24-
The main innovation is the includion of a number of alterntive pseudo random number
24+
The main innovation is the inclusion of a number of alternative pseudo random number
2525
generators, 'in addition' to the standard PRNG in NumPy. The included PRNGs are:
2626

2727
* MT19937 - The standard NumPy generator. Produces identical results to NumPy
@@ -39,8 +39,8 @@ generators, 'in addition' to the standard PRNG in NumPy. The included PRNGs are
3939
:meth:`randomstate.prng.pcg64.advance`. PCG-32 only as a period of
4040
:math:`2^{64}` while PCG-64 has a period of :math:`2^{128}`. See the
4141
`PCG author's page`_ for more details about this class of PRNG.
42-
* Multiplicative Lagged Fibbonacci Generator MLFG(1279, 861, \*) - A directly
43-
implemented multiplicative lagged fibbonacci generator with a very large
42+
* Multiplicative Lagged Fibonacci Generator MLFG(1279, 861, \*) - A directly
43+
implemented multiplicative lagged Fibonacci generator with a very large
4444
period and good performance. Future plans include multiple stream support.
4545
See the `wiki page on Fibonacci generators`_.
4646
* MRG32K3A - a classic and popular generator by L'Ecuyer. Future plans

randomstate/array_utilities.pxi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ cdef int check_array_constraint(np.ndarray val, object name, constraint_type con
5252
if np.any(np.less_equal(val, 0)):
5353
raise ValueError(name + " <= 0")
5454
elif cons == CONS_BOUNDED_0_1 or cons == CONS_BOUNDED_0_1_NOTNAN:
55-
if np.any(np.less_equal(val, 0)) or np.any(np.greater_equal(val, 1)):
55+
if np.any(np.less(val, 0)) or np.any(np.greater(val, 1)):
5656
raise ValueError(name + " <= 0 or " + name + " >= 1")
5757
if cons == CONS_BOUNDED_0_1_NOTNAN:
5858
if np.any(np.isnan(val)):

randomstate/interface.pyx

Lines changed: 90 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ except:
1111
import numpy as np
1212
cimport numpy as np
1313
cimport cython
14+
15+
from libc cimport string
1416
from libc.stdint cimport (uint8_t, uint16_t, uint32_t, uint64_t, int8_t,
1517
int16_t, int32_t, int64_t, intptr_t)
1618
from cpython cimport Py_INCREF
@@ -129,6 +131,13 @@ cdef double kahan_sum(double *darr, np.npy_intp n):
129131
sum = t
130132
return sum
131133

134+
cdef object _ensure_string(object s):
135+
try:
136+
return ''.join(map(chr, s))
137+
except:
138+
return s
139+
140+
132141
cdef class RandomState:
133142
CLASS_DOCSTRING
134143

@@ -140,6 +149,7 @@ cdef class RandomState:
140149
poisson_lam_max = POISSON_LAM_MAX
141150
__MAXSIZE = <uint64_t>sys.maxsize
142151

152+
143153
IF RS_RNG_SEED==1:
144154
def __init__(self, seed=None):
145155
self.rng_state.rng = <rng_t *>PyArray_malloc_aligned(sizeof(rng_t))
@@ -162,17 +172,7 @@ cdef class RandomState:
162172
IF RS_RNG_MOD_NAME == 'dsfmt':
163173
PyArray_free_aligned(self.rng_state.buffered_uniforms)
164174

165-
# Pickling support:
166-
def __getstate__(self):
167-
return self.get_state()
168-
169-
def __setstate__(self, state):
170-
self.set_state(state)
171-
172-
def __reduce__(self):
173-
return (randomstate.prng.__generic_ctor, (RS_RNG_MOD_NAME,), self.get_state())
174-
175-
IF RS_RNG_NAME == 'mt19937':
175+
IF RS_RNG_MOD_NAME == 'mt19937':
176176
def seed(self, seed=None):
177177
"""
178178
seed(seed=None)
@@ -273,7 +273,7 @@ cdef class RandomState:
273273
raise ValueError('val < 0')
274274
if inc < 0:
275275
raise ValueError('inc < 0')
276-
IF RS_RNG_NAME == 'pcg64':
276+
IF RS_RNG_MOD_NAME == 'pcg64':
277277
IF RS_PCG128_EMULATED:
278278
set_seed(&self.rng_state,
279279
pcg128_from_pylong(val),
@@ -315,7 +315,7 @@ cdef class RandomState:
315315
Advancing the prng state resets any pre-computed random numbers.
316316
This is required to ensure exact reproducibility.
317317
"""
318-
IF RS_RNG_NAME == 'pcg64':
318+
IF RS_RNG_MOD_NAME == 'pcg64':
319319
IF RS_PCG128_EMULATED:
320320
advance_state(&self.rng_state, pcg128_from_pylong(delta))
321321
ELSE:
@@ -357,7 +357,7 @@ cdef class RandomState:
357357
self.rng_state.gauss = 0.0
358358
return None
359359

360-
IF RS_RNG_NAME == 'mt19937':
360+
IF RS_RNG_MOD_NAME == 'mt19937':
361361
def get_state(self, legacy=False):
362362
"""
363363
get_state()
@@ -394,12 +394,13 @@ cdef class RandomState:
394394
For information about the specific structure of the PRNG-specific
395395
component, see the class documentation.
396396
"""
397+
rng_name = _ensure_string(RS_RNG_NAME)
397398
if legacy:
398-
return (RS_RNG_NAME,) \
399+
return (rng_name.upper(),) \
399400
+ _get_state(self.rng_state) \
400401
+ (self.rng_state.has_gauss, self.rng_state.gauss)
401402

402-
return {'name': RS_RNG_NAME,
403+
return {'name': rng_name,
403404
'state': _get_state(self.rng_state),
404405
'gauss': {'has_gauss': self.rng_state.has_gauss, 'gauss': self.rng_state.gauss},
405406
'uint32': {'has_uint32': self.rng_state.has_uint32, 'uint32': self.rng_state.uinteger}
@@ -436,7 +437,8 @@ cdef class RandomState:
436437
For information about the specific structure of the PRNG-specific
437438
component, see the class documentation.
438439
"""
439-
return {'name': RS_RNG_NAME,
440+
rng_name = _ensure_string(RS_RNG_NAME)
441+
return {'name': rng_name,
440442
'state': _get_state(self.rng_state),
441443
'gauss': {'has_gauss': self.rng_state.has_gauss, 'gauss': self.rng_state.gauss},
442444
'uint32': {'has_uint32': self.rng_state.has_uint32, 'uint32': self.rng_state.uinteger}
@@ -479,14 +481,18 @@ cdef class RandomState:
479481
For information about the specific structure of the PRNG-specific
480482
component, see the class documentation.
481483
"""
482-
rng_name = RS_RNG_NAME
483-
IF RS_RNG_NAME == 'mt19937':
484+
rng_name = _ensure_string(RS_RNG_NAME)
485+
IF RS_RNG_MOD_NAME == 'mt19937':
484486
if isinstance(state, tuple):
485487
if state[0] != 'MT19937':
486488
raise ValueError('Not a ' + rng_name + ' RNG state')
487489
_set_state(&self.rng_state, (state[1], state[2]))
488-
self.rng_state.has_gauss = state[3]
489-
self.rng_state.gauss = state[4]
490+
if len(state) > 3:
491+
self.rng_state.has_gauss = state[3]
492+
self.rng_state.gauss = state[4]
493+
else:
494+
self.rng_state.has_gauss = 0
495+
self.rng_state.gauss = 0.0
490496
self.rng_state.has_uint32 = 0
491497
self.rng_state.uinteger = 0
492498
return None
@@ -552,6 +558,17 @@ cdef class RandomState:
552558
else:
553559
raise ValueError('Unknown value of bits. Must be either 32 or 64.')
554560

561+
# Pickling support:
562+
def __getstate__(self):
563+
return self.get_state()
564+
565+
def __setstate__(self, state):
566+
self.set_state(state)
567+
568+
def __reduce__(self):
569+
return (randomstate.prng.__generic_ctor, (RS_RNG_MOD_NAME,), self.get_state())
570+
571+
# Basic distributions:
555572
def random_sample(self, size=None):
556573
"""
557574
random_sample(size=None)
@@ -595,17 +612,6 @@ cdef class RandomState:
595612
596613
"""
597614
return double_fill(&self.rng_state, &random_uniform_fill, size, self.lock)
598-
# cdef double out
599-
# cdef double [::1] out_array
600-
# cdef Py_ssize_t n
601-
# if size is None:
602-
# random_uniform_fill(&self.rng_state, 1, &out)
603-
# return out
604-
# else:
605-
# n = compute_numel(size)
606-
# out_array = np.empty(n, np.double)
607-
# random_uniform_fill(&self.rng_state, n, &out_array[0])
608-
# return np.asarray(out_array).reshape(size)
609615

610616
def tomaxint(self, size=None):
611617
"""
@@ -652,17 +658,20 @@ cdef class RandomState:
652658
[ True, True]]], dtype=bool)
653659
654660
"""
655-
cdef Py_ssize_t n
656-
cdef np.int_t [::1] randoms
661+
cdef np.npy_intp n
662+
cdef np.ndarray randoms
663+
cdef long *randoms_data
657664

658665
if size is None:
659666
return random_positive_int(&self.rng_state)
660667

661-
n = compute_numel(size)
662-
randoms = np.empty(n, np.int)
668+
randoms = <np.ndarray>np.empty(size, dtype=np.int)
669+
randoms_data = <long*>np.PyArray_DATA(randoms)
670+
n = np.PyArray_SIZE(randoms)
671+
663672
for i in range(n):
664-
randoms[i] = random_positive_int(&self.rng_state)
665-
return np.asarray(randoms).reshape(size)
673+
randoms_data[i] = random_positive_int(&self.rng_state)
674+
return randoms
666675

667676
def randint(self, low, high=None, size=None, dtype='l'):
668677
"""
@@ -757,7 +766,7 @@ cdef class RandomState:
757766
elif key == 'bool':
758767
return _rand_bool(low, high - 1, size, &self.rng_state, self.lock)
759768

760-
def bytes(self, Py_ssize_t length):
769+
def bytes(self, np.npy_intp length):
761770
"""
762771
bytes(length)
763772
@@ -3200,7 +3209,7 @@ cdef class RandomState:
32003209
"""
32013210
return disc(&self.rng_state, &random_binomial, size, self.lock, 1, 1,
32023211
p, 'p', CONS_BOUNDED_0_1_NOTNAN,
3203-
n, 'n', CONS_POSITIVE,
3212+
n, 'n', CONS_NON_NEGATIVE,
32043213
0.0, '', CONS_NONE)
32053214

32063215
def negative_binomial(self, n, p, size=None):
@@ -3466,7 +3475,6 @@ cdef class RandomState:
34663475
0.34889999999999999 #random
34673476
34683477
"""
3469-
34703478
return disc(&self.rng_state, &random_geometric, size, self.lock, 1, 0,
34713479
p, 'p', CONS_BOUNDED_0_1,
34723480
0.0, '', CONS_NONE,
@@ -3800,8 +3808,8 @@ cdef class RandomState:
38003808
# standard normally distributed random numbers. The matrix has rows
38013809
# with the same length as mean and as many rows are necessary to
38023810
# form a matrix of shape final_shape.
3803-
final_shape = tuple(shape[:])
3804-
final_shape += (mean.shape[0],)
3811+
final_shape = list(shape[:])
3812+
final_shape.append(mean.shape[0])
38053813
x = self.standard_normal(final_shape, method=method).reshape(-1, mean.shape[0])
38063814

38073815
# Transform matrix of standard normals into matrix where each row
@@ -3912,7 +3920,7 @@ cdef class RandomState:
39123920
cdef double Sum
39133921

39143922
d = len(pvals)
3915-
parr = <np.ndarray>np.PyArray_ContiguousFromObject(pvals, np.NPY_DOUBLE, 1, 1)
3923+
parr = <np.ndarray>np.PyArray_FROM_OTF(pvals, np.NPY_DOUBLE, np.NPY_ALIGNED)
39163924
pix = <double*>np.PyArray_DATA(parr)
39173925

39183926
if kahan_sum(pix, d-1) > (1.0 + 1e-12):
@@ -3944,6 +3952,7 @@ cdef class RandomState:
39443952
Sum = Sum - pix[j]
39453953
if dn > 0:
39463954
mnix[i+d-1] = dn
3955+
39473956
i = i + d
39483957

39493958
return multin
@@ -4102,34 +4111,53 @@ cdef class RandomState:
41024111
[0, 1, 2]])
41034112
41044113
"""
4105-
cdef Py_ssize_t i
4106-
cdef long j
4107-
4108-
i = len(x) - 1
4109-
4110-
# Logic adapted from random.shuffle()
4111-
if isinstance(x, np.ndarray) and \
4112-
(x.ndim > 1 or x.dtype.fields is not None):
4113-
# For a multi-dimensional ndarray, indexing returns a view onto
4114-
# each row. So we can't just use ordinary assignment to swap the
4115-
# rows; we need a bounce buffer.
4114+
cdef:
4115+
np.npy_intp i, j, n = len(x), stride, itemsize
4116+
char* x_ptr
4117+
char* buf_ptr
4118+
4119+
if type(x) is np.ndarray and x.ndim == 1 and x.size:
4120+
# Fast, statically typed path: shuffle the underlying buffer.
4121+
# Only for non-empty, 1d objects of class ndarray (subclasses such
4122+
# as MaskedArrays may not support this approach).
4123+
x_ptr = <char*><size_t>x.ctypes.data
4124+
stride = x.strides[0]
4125+
itemsize = x.dtype.itemsize
4126+
buf = np.empty_like(x[0]) # GC'd at function exit
4127+
buf_ptr = <char*><size_t>buf.ctypes.data
4128+
with self.lock:
4129+
# We trick gcc into providing a specialized implementation for
4130+
# the most common case, yielding a ~33% performance improvement.
4131+
# Note that apparently, only one branch can ever be specialized.
4132+
if itemsize == sizeof(np.npy_intp):
4133+
self._shuffle_raw(n, sizeof(np.npy_intp), stride, x_ptr, buf_ptr)
4134+
else:
4135+
self._shuffle_raw(n, itemsize, stride, x_ptr, buf_ptr)
4136+
elif isinstance(x, np.ndarray) and x.ndim > 1 and x.size:
4137+
# Multidimensional ndarrays require a bounce buffer.
41164138
buf = np.empty_like(x[0])
41174139
with self.lock:
4118-
while i > 0:
4140+
for i in reversed(range(1, n)):
41194141
j = random_interval(&self.rng_state, i)
41204142
buf[...] = x[j]
41214143
x[j] = x[i]
41224144
x[i] = buf
4123-
i = i - 1
41244145
else:
4125-
# For single-dimensional arrays, lists, and any other Python
4126-
# sequence types, indexing returns a real object that's
4127-
# independent of the array contents, so we can just swap directly.
4146+
# Untyped path.
41284147
with self.lock:
4129-
while i > 0:
4148+
for i in reversed(range(1, n)):
41304149
j = random_interval(&self.rng_state, i)
41314150
x[i], x[j] = x[j], x[i]
4132-
i = i - 1
4151+
4152+
cdef inline _shuffle_raw(self, np.npy_intp n, np.npy_intp itemsize,
4153+
np.npy_intp stride, char* data, char* buf):
4154+
cdef np.npy_intp i, j
4155+
for i in reversed(range(1, n)):
4156+
j = random_interval(&self.rng_state, i)
4157+
string.memcpy(buf, data + j * stride, itemsize)
4158+
string.memcpy(data + j * stride, data + i * stride, itemsize)
4159+
string.memcpy(data + i * stride, buf, itemsize)
4160+
41334161

41344162
def permutation(self, object x):
41354163
"""

randomstate/shims/dSFMT/dSFMT.pxi

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ then an array with that shape is filled and returned.
9393
9494
**No Compatibility Guarantee**
9595
96-
'dSFMT.RandomState' does not make a guarantee that a fixed seed and a
97-
fixed series of calls to 'dSFMT.RandomState' methods using the same
96+
``dSFMT.RandomState`` does not make a guarantee that a fixed seed and a
97+
fixed series of calls to ``dSFMT.RandomState`` methods using the same
9898
parameters will always produce the same results. This is different from
99-
'numpy.random.RandomState' guarantee. This is done to simplify improving
99+
``numpy.random.RandomState`` guarantee. This is done to simplify improving
100100
random number generators. To ensure identical results, you must use the
101101
same release version.
102102
@@ -114,9 +114,9 @@ Notes
114114
-----
115115
The Python stdlib module "random" also contains a Mersenne Twister
116116
pseudo-random number generator with a number of methods that are similar
117-
to the ones available in `RandomState`. `RandomState`, besides being
118-
NumPy-aware, has the advantage that it provides a much larger number
119-
of probability distributions to choose from.
117+
to the ones available in ``RandomState``. The `RandomState` object, besides
118+
being NumPy-aware, also has the advantage that it provides a much larger
119+
number of probability distributions to choose from.
120120
121121
**Parallel Features**
122122
@@ -138,7 +138,7 @@ segments come from the same sequence.
138138
.. [1] Mutsuo Saito and Makoto Matsumoto, "SIMD-oriented Fast Mersenne
139139
Twister: a 128-bit Pseudorandom Number Generator." Monte Carlo
140140
and Quasi-Monte Carlo Methods 2006, Springer, pp. 607 -- 622, 2008.
141-
.. [2] Hiroshi Haramoto, Makoto Matsumoto, and Pierre L'Ecuyer, "A Fast
141+
.. [2] Hiroshi Haramoto, Makoto Matsumoto, and Pierre L\'Ecuyer, "A Fast
142142
Jump Ahead Algorithm for Linear Recurrences in a Polynomial Space",
143143
Sequences and Their Applications - SETA, 290--298, 2008.
144144
"""

0 commit comments

Comments
 (0)