Skip to content

Commit ea6819a

Browse files
committed
Merge pull request #30 from bashtage/uppdate-to-latest-numpy
FIX: Fix test failures against current NumPy
2 parents 768c26e + 7664231 commit ea6819a

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)