diff --git a/numpy/random/_mt19937.pyx b/numpy/random/_mt19937.pyx index e99652b73cd0..c56f6366a4b9 100644 --- a/numpy/random/_mt19937.pyx +++ b/numpy/random/_mt19937.pyx @@ -129,9 +129,9 @@ cdef class MT19937(BitGenerator): BitGenerator.__init__(self, seed) val = self._seed_seq.generate_state(RK_STATE_LEN, np.uint32) # MSB is 1; assuring non-zero initial array - self.rng_state.key[0] = 0x80000000UL - for i in range(1, RK_STATE_LEN): + for i in range(RK_STATE_LEN): self.rng_state.key[i] = val[i] + self.rng_state.key[0] |= 0x80000000UL self.rng_state.pos = i self._bitgen.state = &self.rng_state @@ -169,9 +169,9 @@ cdef class MT19937(BitGenerator): seed = SeedSequence() val = seed.generate_state(RK_STATE_LEN) # MSB is 1; assuring non-zero initial array - self.rng_state.key[0] = 0x80000000UL - for i in range(1, RK_STATE_LEN): + for i in range(RK_STATE_LEN): self.rng_state.key[i] = val[i] + self.rng_state.key[0] |= 0x80000000UL else: if hasattr(seed, 'squeeze'): seed = seed.squeeze() diff --git a/numpy/random/src/mt19937/mt19937.c b/numpy/random/src/mt19937/mt19937.c index e5ca9e0cff67..c73705420c6f 100644 --- a/numpy/random/src/mt19937/mt19937.c +++ b/numpy/random/src/mt19937/mt19937.c @@ -77,7 +77,7 @@ void mt19937_init_by_array(mt19937_state *state, uint32_t *init_key, } } - mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ + mt[0] |= 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ } void mt19937_gen(mt19937_state *state) { diff --git a/numpy/random/tests/test_generator_mt19937.py b/numpy/random/tests/test_generator_mt19937.py index d85de6b6d2a8..04987b644b7a 100644 --- a/numpy/random/tests/test_generator_mt19937.py +++ b/numpy/random/tests/test_generator_mt19937.py @@ -56,6 +56,26 @@ def test_noninstantized_bitgen(self): assert_raises(ValueError, Generator, MT19937) + def test_seed_init(self): + # gh-14844. seed[0] was not correctly initialized + # key[0] always held the same value + + class FullSeedSequence(SeedSequence): + + def __init__(self, x): + self.x = x + + def generate_state(self, n_words, dtype=np.uint32): + return np.full(n_words, self.x, dtype=dtype) + + for i in range(32): + ss = FullSeedSequence(1 << i) + key = MT19937(ss).state['state']['key'] + expected = np.full(624, 1 << i, dtype=np.uint32) + expected[0] |= 0x80000000 + np.testing.assert_array_equal(key, expected) + + class TestBinomial(object): def test_n_zero(self): # Tests the corner case of n == 0 for the binomial distribution. @@ -944,7 +964,7 @@ def test_permutation(self): arr_2d = np.atleast_2d([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]).T actual = random.permutation(arr_2d) assert_array_equal(actual, np.atleast_2d(desired).T) - + bad_x_str = "abcd" assert_raises(np.AxisError, random.permutation, bad_x_str) diff --git a/numpy/random/tests/test_randomstate.py b/numpy/random/tests/test_randomstate.py index 5131f1839c56..b4fa7a4e3e9a 100644 --- a/numpy/random/tests/test_randomstate.py +++ b/numpy/random/tests/test_randomstate.py @@ -107,6 +107,25 @@ def test_cannot_seed(self): def test_invalid_initialization(self): assert_raises(ValueError, random.RandomState, MT19937) + def test_reseed_unique(self): + # gh-14844. seed[0] was not correctly re-initialized in legacy seed() + # key[0] always held the same value + rs = random.RandomState() + key = rs._bit_generator.state['state']['key'] + res = np.empty((32,) + key.shape, dtype=key.dtype) + for i in range(32): + # enter the _legacy_seed(None) path + rs.seed() + key = rs._bit_generator.state['state']['key'] + res[i, ...] = key + res.shape = (32, -1) + assert not (res[:, 0] == 0x80000000).all() + + rs.seed(np.ones(100, dtype='int64')) + k1 = rs._bit_generator.state['state']['key'] + assert k1[0] != 0x80000000 + + class TestBinomial(object): def test_n_zero(self): @@ -618,7 +637,7 @@ def test_choice_nan_probabilities(self): a = np.array([42, 1, 2]) p = [None, None, None] assert_raises(ValueError, random.choice, a, p=p) - + def test_choice_p_non_contiguous(self): p = np.ones(10) / 5 p[1::2] = 3.0