Skip to content

Commit b1b7532

Browse files
authored
Merge pull request #62 from bashtage/numpy-sync
ENH: Update to latest NumPy additions
2 parents 4593c83 + 2eca1c3 commit b1b7532

File tree

5 files changed

+110
-27
lines changed

5 files changed

+110
-27
lines changed

randomstate/array_utilities.pxi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ cdef uint64_t MAXSIZE = <uint64_t>sys.maxsize
4646

4747
cdef int check_array_constraint(np.ndarray val, object name, constraint_type cons) except -1:
4848
if cons == CONS_NON_NEGATIVE:
49-
if np.any(np.less(val, 0)):
49+
if np.any(np.signbit(val)):
5050
raise ValueError(name + " < 0")
5151
elif cons == CONS_POSITIVE:
5252
if np.any(np.less_equal(val, 0)):
@@ -75,7 +75,7 @@ cdef int check_array_constraint(np.ndarray val, object name, constraint_type con
7575

7676
cdef int check_constraint(double val, object name, constraint_type cons) except -1:
7777
if cons == CONS_NON_NEGATIVE:
78-
if val < 0:
78+
if np.signbit(val):
7979
raise ValueError(name + " < 0")
8080
elif cons == CONS_POSITIVE:
8181
if val <= 0:

randomstate/distributions.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,11 @@ static long random_poisson_mult(aug_state *state, double lam)
643643
}
644644
}
645645

646+
/*
647+
* The transformed rejection method for generating Poisson random variables
648+
* W. Hoermann
649+
* Insurance: Mathematics and Economics 12, 39-45 (1993)
650+
*/
646651
#define LS2PI 0.91893853320467267
647652
#define TWELFTH 0.083333333333333333333333
648653
static long random_poisson_ptrs(aug_state *state, double lam)

randomstate/randomstate.pyx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,12 +1561,12 @@ cdef class RandomState:
15611561
if method == 'bm':
15621562
return cont(&self.rng_state, &random_normal, size, self.lock, 2,
15631563
loc, '', CONS_NONE,
1564-
scale, 'scale', CONS_POSITIVE,
1564+
scale, 'scale', CONS_NON_NEGATIVE,
15651565
0.0, '', CONS_NONE)
15661566
else:
15671567
return cont(&self.rng_state, &random_normal_zig, size, self.lock, 2,
15681568
loc, '', CONS_NONE,
1569-
scale, 'scale', CONS_POSITIVE,
1569+
scale, 'scale', CONS_NON_NEGATIVE,
15701570
0.0, '', CONS_NONE)
15711571

15721572
def beta(self, a, b, size=None):
@@ -1659,7 +1659,7 @@ cdef class RandomState:
16591659
16601660
"""
16611661
return cont(&self.rng_state, &random_exponential, size, self.lock, 1,
1662-
scale, 'scale', CONS_POSITIVE,
1662+
scale, 'scale', CONS_NON_NEGATIVE,
16631663
0.0, '', CONS_NONE,
16641664
0.0, '', CONS_NONE)
16651665

@@ -1784,12 +1784,12 @@ cdef class RandomState:
17841784
if key == 'float64':
17851785
return cont(&self.rng_state, &random_standard_gamma,
17861786
size, self.lock, 1,
1787-
shape, 'shape', CONS_POSITIVE,
1787+
shape, 'shape', CONS_NON_NEGATIVE,
17881788
0.0, '', CONS_NONE,
17891789
0.0, '', CONS_NONE)
17901790
if key == 'float32':
17911791
return cont_float(&self.rng_state, &random_standard_gamma_float,
1792-
size, self.lock, shape, 'shape', CONS_POSITIVE)
1792+
size, self.lock, shape, 'shape', CONS_NON_NEGATIVE)
17931793
else:
17941794
raise TypeError('Unsupported dtype "%s" for standard_gamma' % key)
17951795

@@ -1867,8 +1867,8 @@ cdef class RandomState:
18671867
18681868
"""
18691869
return cont(&self.rng_state, &random_gamma, size, self.lock, 2,
1870-
shape, 'shape', CONS_POSITIVE,
1871-
scale, 'scale', CONS_POSITIVE,
1870+
shape, 'shape', CONS_NON_NEGATIVE,
1871+
scale, 'scale', CONS_NON_NEGATIVE,
18721872
0.0, '', CONS_NONE)
18731873

18741874
def f(self, dfnum, dfden, size=None):
@@ -2616,7 +2616,7 @@ cdef class RandomState:
26162616
26172617
"""
26182618
return cont(&self.rng_state, &random_weibull, size, self.lock, 1,
2619-
a, 'a', CONS_POSITIVE,
2619+
a, 'a', CONS_NON_NEGATIVE,
26202620
0.0, '', CONS_NONE,
26212621
0.0, '', CONS_NONE)
26222622

@@ -2801,7 +2801,7 @@ cdef class RandomState:
28012801
"""
28022802
return cont(&self.rng_state, &random_laplace, size, self.lock, 2,
28032803
loc, 'loc', CONS_NONE,
2804-
scale, 'scale', CONS_POSITIVE,
2804+
scale, 'scale', CONS_NON_NEGATIVE,
28052805
0.0, '', CONS_NONE)
28062806

28072807
def gumbel(self, loc=0.0, scale=1.0, size=None):
@@ -2918,7 +2918,7 @@ cdef class RandomState:
29182918
"""
29192919
return cont(&self.rng_state, &random_gumbel, size, self.lock, 2,
29202920
loc, 'loc', CONS_NONE,
2921-
scale, 'scale', CONS_POSITIVE,
2921+
scale, 'scale', CONS_NON_NEGATIVE,
29222922
0.0, '', CONS_NONE)
29232923

29242924
def logistic(self, loc=0.0, scale=1.0, size=None):
@@ -3107,7 +3107,7 @@ cdef class RandomState:
31073107
"""
31083108
return cont(&self.rng_state, &random_lognormal, size, self.lock, 2,
31093109
mean, 'mean', CONS_NONE,
3110-
sigma, 'sigma', CONS_POSITIVE,
3110+
sigma, 'sigma', CONS_NON_NEGATIVE,
31113111
0.0, '', CONS_NONE)
31123112

31133113
def rayleigh(self, scale=1.0, size=None):
@@ -3173,7 +3173,7 @@ cdef class RandomState:
31733173
31743174
"""
31753175
return cont(&self.rng_state, &random_rayleigh, size, self.lock, 1,
3176-
scale, 'scale', CONS_POSITIVE,
3176+
scale, 'scale', CONS_NON_NEGATIVE,
31773177
0.0, '', CONS_NONE,
31783178
0.0, '', CONS_NONE)
31793179

@@ -4354,7 +4354,11 @@ cdef class RandomState:
43544354
x_ptr = <char*><size_t>x.ctypes.data
43554355
stride = x.strides[0]
43564356
itemsize = x.dtype.itemsize
4357-
buf = np.empty_like(x[0]) # GC'd at function exit
4357+
# As the array x could contain python objects we use a buffer
4358+
# of bytes for the swaps to avoid leaving one of the objects
4359+
# within the buffer and erroneously decrementing it's refcount
4360+
# when the function exits.
4361+
buf = np.empty(itemsize, dtype=np.int8) # GC'd at function exit
43584362
buf_ptr = <char*><size_t>buf.ctypes.data
43594363
with self.lock:
43604364
# We trick gcc into providing a specialized implementation for

randomstate/tests/test_numpy_mt19937_regressions.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,35 @@ def test_choice_sum_of_probs_tolerance(self):
113113
assert_(c in a)
114114
assert_raises(ValueError, mt19937.choice, a, p=probs*0.9)
115115

116+
117+
def test_shuffle_of_array_of_different_length_strings(self):
118+
# Test that permuting an array of different length strings
119+
# will not cause a segfault on garbage collection
120+
# Tests gh-7710
121+
mt19937.seed(1234)
122+
123+
a = np.array(['a', 'a' * 1000])
124+
125+
for _ in range(100):
126+
mt19937.shuffle(a)
127+
128+
# Force Garbage Collection - should not segfault.
129+
import gc
130+
gc.collect()
131+
132+
def test_shuffle_of_array_of_objects(self):
133+
# Test that permuting an array of objects will not cause
134+
# a segfault on garbage collection.
135+
# See gh-7719
136+
mt19937.seed(1234)
137+
a = np.array([np.arange(1), np.arange(4)])
138+
139+
for _ in range(1000):
140+
mt19937.shuffle(a)
141+
142+
# Force Garbage Collection - should not segfault.
143+
import gc
144+
gc.collect()
145+
116146
if __name__ == "__main__":
117147
run_module_suite()

randomstate/tests/tests_numpy_mt19937.py

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ def test_negative_binomial(self):
132132
# arguments without truncation.
133133
self.prng.negative_binomial(0.5, 0.5)
134134

135+
135136
class TestRandint(TestCase):
136137

137138
rfunc = random.randint
@@ -157,7 +158,6 @@ def test_rng_zero_and_extremes(self):
157158
lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min
158159
ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1
159160
tgt = ubnd - 1
160-
161161
assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
162162
tgt = lbnd
163163
assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
@@ -261,22 +261,26 @@ def test_randint(self):
261261

262262
def test_random_integers(self):
263263
mt19937.seed(self.seed)
264-
actual = mt19937.random_integers(-99, 99, size=(3, 2))
265-
desired = np.array([[31, 3],
266-
[-52, 41],
267-
[-48, -66]])
268-
assert_array_equal(actual, desired)
264+
with warnings.catch_warnings():
265+
warnings.simplefilter("ignore", DeprecationWarning)
266+
actual = mt19937.random_integers(-99, 99, size=(3, 2))
267+
desired = np.array([[31, 3],
268+
[-52, 41],
269+
[-48, -66]])
270+
assert_array_equal(actual, desired)
269271

270272
def test_random_integers_max_int(self):
271273
# Tests whether random_integers can generate the
272274
# maximum allowed Python int that can be converted
273275
# into a C long. Previous implementations of this
274-
# method have thrown an OverflowError when attemping
276+
# method have thrown an OverflowError when attempting
275277
# to generate this integer.
276-
actual = mt19937.random_integers(np.iinfo('l').max,
277-
np.iinfo('l').max)
278-
desired = np.iinfo('l').max
279-
assert_equal(actual, desired)
278+
with warnings.catch_warnings():
279+
warnings.simplefilter("ignore", DeprecationWarning)
280+
actual = mt19937.random_integers(np.iinfo('l').max,
281+
np.iinfo('l').max)
282+
desired = np.iinfo('l').max
283+
assert_equal(actual, desired)
280284

281285
def test_random_integers_deprecated(self):
282286
with warnings.catch_warnings():
@@ -482,6 +486,10 @@ def test_exponential(self):
482486
[0.68717433461363442, 1.69175666993575979]])
483487
assert_array_almost_equal(actual, desired, decimal=15)
484488

489+
def test_exponential_0(self):
490+
assert_equal(mt19937.exponential(scale=0), 0)
491+
assert_raises(ValueError, mt19937.exponential, scale=-0.)
492+
485493
def test_f(self):
486494
mt19937.seed(self.seed)
487495
actual = mt19937.f(12, 77, size=(3, 2))
@@ -498,6 +506,10 @@ def test_gamma(self):
498506
[31.71863275789960568, 33.30143302795922011]])
499507
assert_array_almost_equal(actual, desired, decimal=14)
500508

509+
def test_gamma_0(self):
510+
assert_equal(mt19937.gamma(shape=0, scale=0), 0)
511+
assert_raises(ValueError, mt19937.gamma, shape=-0., scale=-0.)
512+
501513
def test_geometric(self):
502514
mt19937.seed(self.seed)
503515
actual = mt19937.geometric(.123456789, size=(3, 2))
@@ -514,6 +526,10 @@ def test_gumbel(self):
514526
[1.10651090478803416, -0.69535848626236174]])
515527
assert_array_almost_equal(actual, desired, decimal=15)
516528

529+
def test_gumbel_0(self):
530+
assert_equal(mt19937.gumbel(scale=0), 0)
531+
assert_raises(ValueError, mt19937.gumbel, scale=-0.)
532+
517533
def test_hypergeometric(self):
518534
mt19937.seed(self.seed)
519535
actual = mt19937.hypergeometric(10.1, 5.5, 14, size=(3, 2))
@@ -548,6 +564,10 @@ def test_laplace(self):
548564
[-0.05391065675859356, 1.74901336242837324]])
549565
assert_array_almost_equal(actual, desired, decimal=15)
550566

567+
def test_laplace_0(self):
568+
assert_equal(mt19937.laplace(scale=0), 0)
569+
assert_raises(ValueError, mt19937.laplace, scale=-0.)
570+
551571
def test_logistic(self):
552572
mt19937.seed(self.seed)
553573
actual = mt19937.logistic(loc=.123456789, scale=2.0, size=(3, 2))
@@ -556,6 +576,10 @@ def test_logistic(self):
556576
[-0.21682183359214885, 2.63373365386060332]])
557577
assert_array_almost_equal(actual, desired, decimal=15)
558578

579+
def test_laplace_0(self):
580+
assert_(mt19937.laplace(scale=0) in [0, 1])
581+
assert_raises(ValueError, mt19937.laplace, scale=-0.)
582+
559583
def test_lognormal(self):
560584
mt19937.seed(self.seed)
561585
actual = mt19937.lognormal(mean=.123456789, sigma=2.0, size=(3, 2))
@@ -564,6 +588,10 @@ def test_lognormal(self):
564588
[65.72798501792723869, 86.84341601437161273]])
565589
assert_array_almost_equal(actual, desired, decimal=13)
566590

591+
def test_lognormal_0(self):
592+
assert_equal(mt19937.lognormal(sigma=0), 1)
593+
assert_raises(ValueError, mt19937.lognormal, sigma=-0.)
594+
567595
def test_logseries(self):
568596
mt19937.seed(self.seed)
569597
actual = mt19937.logseries(p=.923456789, size=(3, 2))
@@ -654,6 +682,10 @@ def test_normal(self):
654682
[4.18552478636557357, 4.46410668111310471]])
655683
assert_array_almost_equal(actual, desired, decimal=15)
656684

685+
def test_normal_0(self):
686+
assert_equal(mt19937.normal(scale=0), 0)
687+
assert_raises(ValueError, mt19937.normal, scale=-0.)
688+
657689
def test_pareto(self):
658690
mt19937.seed(self.seed)
659691
actual = mt19937.pareto(a=.123456789, size=(3, 2))
@@ -701,6 +733,10 @@ def test_rayleigh(self):
701733
[11.06066537006854311, 17.35468505778271009]])
702734
assert_array_almost_equal(actual, desired, decimal=14)
703735

736+
def test_rayleigh_0(self):
737+
assert_equal(mt19937.rayleigh(scale=0), 0)
738+
assert_raises(ValueError, mt19937.rayleigh, scale=-0.)
739+
704740
def test_standard_cauchy(self):
705741
mt19937.seed(self.seed)
706742
actual = mt19937.standard_cauchy(size=(3, 2))
@@ -725,6 +761,10 @@ def test_standard_gamma(self):
725761
[7.54838614231317084, 8.012756093271868]])
726762
assert_array_almost_equal(actual, desired, decimal=14)
727763

764+
def test_standard_gamma_0(self):
765+
assert_equal(mt19937.standard_gamma(shape=0), 0)
766+
assert_raises(ValueError, mt19937.standard_gamma, shape=-0.)
767+
728768
def test_standard_normal(self):
729769
mt19937.seed(self.seed)
730770
actual = mt19937.standard_normal(size=(3, 2))
@@ -800,6 +840,10 @@ def test_weibull(self):
800840
[0.67057783752390987, 1.39494046635066793]])
801841
assert_array_almost_equal(actual, desired, decimal=15)
802842

843+
def test_weibull_0(self):
844+
assert_equal(mt19937.weibull(a=0), 0)
845+
assert_raises(ValueError, mt19937.weibull, a=-0.)
846+
803847
def test_zipf(self):
804848
mt19937.seed(self.seed)
805849
actual = mt19937.zipf(a=1.23, size=(3, 2))
@@ -1404,7 +1448,7 @@ def gen_random(state, out):
14041448
def test_multinomial(self):
14051449
def gen_random(state, out):
14061450
out[...] = state.multinomial(10, [1/6.]*6, size=10000)
1407-
self.check_function(gen_random, sz=(10000,6))
1451+
self.check_function(gen_random, sz=(10000, 6))
14081452

14091453
# See Issue #4263
14101454
class TestSingleEltArrayInput(TestCase):

0 commit comments

Comments
 (0)