diff options
Diffstat (limited to 'numpy/random')
-rw-r--r-- | numpy/random/_generator.pyi | 4 | ||||
-rw-r--r-- | numpy/random/_generator.pyx | 198 | ||||
-rw-r--r-- | numpy/random/mtrand.pyx | 40 | ||||
-rw-r--r-- | numpy/random/setup.py | 16 | ||||
-rw-r--r-- | numpy/random/src/distributions/distributions.c | 2 | ||||
-rw-r--r-- | numpy/random/tests/test_direct.py | 13 | ||||
-rw-r--r-- | numpy/random/tests/test_generator_mt19937.py | 76 | ||||
-rw-r--r-- | numpy/random/tests/test_generator_mt19937_regressions.py | 2 |
8 files changed, 268 insertions, 83 deletions
diff --git a/numpy/random/_generator.pyi b/numpy/random/_generator.pyi index 64b683d7c..c574bef9a 100644 --- a/numpy/random/_generator.pyi +++ b/numpy/random/_generator.pyi @@ -623,7 +623,9 @@ class Generator: method: Literal["svd", "eigh", "cholesky"] = ..., ) -> ndarray[Any, dtype[float64]]: ... def multinomial( - self, n: _ArrayLikeInt_co, pvals: _ArrayLikeFloat_co, size: Optional[_ShapeLike] = ... + self, n: _ArrayLikeInt_co, + pvals: _ArrayLikeFloat_co, + size: Optional[_ShapeLike] = ... ) -> ndarray[Any, dtype[int64]]: ... def multivariate_hypergeometric( self, diff --git a/numpy/random/_generator.pyx b/numpy/random/_generator.pyx index 5bacb9f6f..391987a1e 100644 --- a/numpy/random/_generator.pyx +++ b/numpy/random/_generator.pyx @@ -876,8 +876,10 @@ cdef class Generator: greater than or equal to low. The default value is 0. high : float or array_like of floats Upper boundary of the output interval. All values generated will be - less than high. high - low must be non-negative. The default value - is 1.0. + less than high. The high limit may be included in the returned array of + floats due to floating-point rounding in the equation + ``low + (high-low) * random_sample()``. high - low must be + non-negative. The default value is 1.0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2095,7 +2097,7 @@ cdef class Generator: Raises ------ ValueError - If a < 1. + If a <= 0. Notes ----- @@ -3105,7 +3107,7 @@ cdef class Generator: `a` > 1. The Zipf distribution (also known as the zeta distribution) is a - continuous probability distribution that satisfies Zipf's law: the + discrete probability distribution that satisfies Zipf's law: the frequency of an item is inversely proportional to its rank in a frequency table. @@ -3133,9 +3135,10 @@ cdef class Generator: ----- The probability density for the Zipf distribution is - .. math:: p(x) = \\frac{x^{-a}}{\\zeta(a)}, + .. math:: p(k) = \\frac{k^{-a}}{\\zeta(a)}, - where :math:`\\zeta` is the Riemann Zeta function. + for integers :math:`k \geq 1`, where :math:`\\zeta` is the Riemann Zeta + function. It is named for the American linguist George Kingsley Zipf, who noted that the frequency of any word in a sample of a language is inversely @@ -3151,22 +3154,29 @@ cdef class Generator: -------- Draw samples from the distribution: - >>> a = 2. # parameter - >>> s = np.random.default_rng().zipf(a, 1000) + >>> a = 4.0 + >>> n = 20000 + >>> s = np.random.default_rng().zipf(a, size=n) Display the histogram of the samples, along with - the probability density function: + the expected histogram based on the probability + density function: >>> import matplotlib.pyplot as plt - >>> from scipy import special # doctest: +SKIP + >>> from scipy.special import zeta # doctest: +SKIP + + `bincount` provides a fast histogram for small integers. - Truncate s values at 50 so plot is interesting: + >>> count = np.bincount(s) + >>> k = np.arange(1, s.max() + 1) - >>> count, bins, ignored = plt.hist(s[s<50], - ... 50, density=True) - >>> x = np.arange(1., 50.) - >>> y = x**(-a) / special.zetac(a) # doctest: +SKIP - >>> plt.plot(x, y/max(y), linewidth=2, color='r') # doctest: +SKIP + >>> plt.bar(k, count[1:], alpha=0.5, label='sample count') + >>> plt.plot(k, n*(k**-a)/zeta(a), 'k.-', alpha=0.5, + ... label='expected count') # doctest: +SKIP + >>> plt.semilogy() + >>> plt.grid(alpha=0.4) + >>> plt.legend() + >>> plt.title(f'Zipf sample, a={a}, size={n}') >>> plt.show() """ @@ -3557,6 +3567,7 @@ cdef class Generator: (3, 3, 2) We can use a different method other than the default to factorize cov: + >>> y = rng.multivariate_normal(mean, cov, (3, 3), method='cholesky') >>> y.shape (3, 3, 2) @@ -3673,24 +3684,35 @@ cdef class Generator: ---------- n : int or array-like of ints Number of experiments. - pvals : sequence of floats, length p - Probabilities of each of the ``p`` different outcomes. These - must sum to 1 (however, the last element is always assumed to - account for the remaining probability, as long as - ``sum(pvals[:-1]) <= 1)``. + pvals : array-like of floats + Probabilities of each of the ``p`` different outcomes with shape + ``(k0, k1, ..., kn, p)``. Each element ``pvals[i,j,...,:]`` must + sum to 1 (however, the last element is always assumed to account + for the remaining probability, as long as + ``sum(pvals[..., :-1], axis=-1) <= 1.0``. Must have at least 1 + dimension where pvals.shape[-1] > 0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then - ``m * n * k`` samples are drawn. Default is None, in which case a - single value is returned. + ``m * n * k`` samples are drawn each with ``p`` elements. Default + is None where the output size is determined by the broadcast shape + of ``n`` and all by the final dimension of ``pvals``, which is + denoted as ``b=(b0, b1, ..., bq)``. If size is not None, then it + must be compatible with the broadcast shape ``b``. Specifically, + size must have ``q`` or more elements and size[-(q-j):] must equal + ``bj``. Returns ------- out : ndarray - The drawn samples, of shape *size*, if that was provided. If not, - the shape is ``(N,)``. + The drawn samples, of shape size, if provided. When size is + provided, the output shape is size + (p,) If not specified, + the shape is determined by the broadcast shape of ``n`` and + ``pvals``, ``(b0, b1, ..., bq)`` augmented with the dimension of + the multinomial, ``p``, so that that output shape is + ``(b0, b1, ..., bq, p)``. - In other words, each entry ``out[i,j,...,:]`` is an N-dimensional - value drawn from the distribution. + Each entry ``out[i,j,...,:]`` is a ``p``-dimensional value drawn + from the distribution. Examples -------- @@ -3728,6 +3750,38 @@ cdef class Generator: >>> rng.multinomial(100, [1/7.]*5 + [2/7.]) array([11, 16, 14, 17, 16, 26]) # random + Simulate 10 throws of a 4-sided die and 20 throws of a 6-sided die + + >>> rng.multinomial([10, 20],[[1/4]*4 + [0]*2, [1/6]*6]) + array([[2, 1, 4, 3, 0, 0], + [3, 3, 3, 6, 1, 4]], dtype=int64) # random + + Generate categorical random variates from two categories where the + first has 3 outcomes and the second has 2. + + >>> rng.multinomial(1, [[.1, .5, .4 ], [.3, .7, .0]]) + array([[0, 0, 1], + [0, 1, 0]], dtype=int64) # random + + ``argmax(axis=-1)`` is then used to return the categories. + + >>> pvals = [[.1, .5, .4 ], [.3, .7, .0]] + >>> rvs = rng.multinomial(1, pvals, size=(4,2)) + >>> rvs.argmax(axis=-1) + array([[0, 1], + [2, 0], + [2, 1], + [2, 0]], dtype=int64) # random + + The same output dimension can be produced using broadcasting. + + >>> rvs = rng.multinomial([[1]] * 4, pvals) + >>> rvs.argmax(axis=-1) + array([[0, 1], + [2, 0], + [2, 1], + [2, 0]], dtype=int64) # random + The probability inputs should be normalized. As an implementation detail, the value of the last entry is ignored and assumed to take up any leftover probability mass, but this should not be relied on. @@ -3742,47 +3796,82 @@ cdef class Generator: >>> rng.multinomial(100, [1.0, 2.0]) # WRONG Traceback (most recent call last): ValueError: pvals < 0, pvals > 1 or pvals contains NaNs - """ - cdef np.npy_intp d, i, sz, offset + cdef np.npy_intp d, i, sz, offset, pi cdef np.ndarray parr, mnarr, on, temp_arr cdef double *pix + cdef int ndim cdef int64_t *mnix cdef int64_t ni cdef np.broadcast it + on = <np.ndarray>np.PyArray_FROM_OTF(n, + np.NPY_INT64, + np.NPY_ARRAY_ALIGNED | + np.NPY_ARRAY_C_CONTIGUOUS) + parr = <np.ndarray>np.PyArray_FROM_OTF(pvals, + np.NPY_DOUBLE, + np.NPY_ARRAY_ALIGNED | + np.NPY_ARRAY_C_CONTIGUOUS) + ndim = parr.ndim + d = parr.shape[ndim - 1] if ndim >= 1 else 0 + if d == 0: + raise ValueError( + "pvals must have at least 1 dimension and the last dimension " + "of pvals must be greater than 0." + ) - d = len(pvals) - on = <np.ndarray>np.PyArray_FROM_OTF(n, np.NPY_INT64, np.NPY_ALIGNED) - parr = <np.ndarray>np.PyArray_FROMANY( - pvals, np.NPY_DOUBLE, 1, 1, np.NPY_ARRAY_ALIGNED | np.NPY_ARRAY_C_CONTIGUOUS) - pix = <double*>np.PyArray_DATA(parr) check_array_constraint(parr, 'pvals', CONS_BOUNDED_0_1) - if kahan_sum(pix, d-1) > (1.0 + 1e-12): - # When floating, but not float dtype, and close, improve the error - # 1.0001 works for float16 and float32 - if (isinstance(pvals, np.ndarray) - and np.issubdtype(pvals.dtype, np.floating) - and pvals.dtype != float - and pvals.sum() < 1.0001): - msg = ("sum(pvals[:-1].astype(np.float64)) > 1.0. The pvals " - "array is cast to 64-bit floating point prior to " - "checking the sum. Precision changes when casting may " - "cause problems even if the sum of the original pvals " - "is valid.") - else: - msg = "sum(pvals[:-1]) > 1.0" - raise ValueError(msg) + pix = <double*>np.PyArray_DATA(parr) + sz = np.PyArray_SIZE(parr) + # Cython 0.29.20 would not correctly translate the range-based for + # loop to a C for loop + # for offset in range(<np.npy_intp>0, sz, d): + offset = 0 + while offset < sz: + if kahan_sum(pix + offset, d-1) > (1.0 + 1e-12): + # When floating, but not float dtype, and close, improve the error + # 1.0001 works for float16 and float32 + slice_repr = "[:-1]" if ndim == 1 else "[...,:-1]" + if (isinstance(pvals, np.ndarray) + and np.issubdtype(pvals.dtype, np.floating) + and pvals.dtype != float + and pvals.sum() < 1.0001): + msg = (f"sum(pvals{slice_repr}.astype(np.float64)) > 1.0." + " The pvals array is cast to 64-bit floating" + " point prior to checking the sum. Precision " + "changes when casting may cause problems even " + "if the sum of the original pvals is valid.") + else: + msg = f"sum(pvals{slice_repr}) > 1.0" + raise ValueError(msg) + offset += d - if np.PyArray_NDIM(on) != 0: # vector + if np.PyArray_NDIM(on) != 0 or ndim > 1: # vector check_array_constraint(on, 'n', CONS_NON_NEGATIVE) + # This provides the offsets to use in the C-contig parr when + # broadcasting + offsets = <np.ndarray>np.arange( + 0, np.PyArray_SIZE(parr), d, dtype=np.intp + ).reshape((<object>parr).shape[:ndim - 1]) if size is None: - it = np.PyArray_MultiIterNew1(on) + it = np.PyArray_MultiIterNew2(on, offsets) else: temp = np.empty(size, dtype=np.int8) temp_arr = <np.ndarray>temp - it = np.PyArray_MultiIterNew2(on, temp_arr) - validate_output_shape(it.shape, temp_arr) + it = np.PyArray_MultiIterNew3(on, offsets, temp_arr) + # Validate size and the broadcast shape + try: + size = (operator.index(size),) + except: + size = tuple(size) + # This test verifies that an axis with dim 1 in size has not + # been increased by broadcasting with the input + if it.shape != size: + raise ValueError( + f"Output size {size} is not compatible with " + f"broadcast dimensions of inputs {it.shape}." + ) shape = it.shape + (d,) multin = np.zeros(shape, dtype=np.int64) mnarr = <np.ndarray>multin @@ -3792,7 +3881,8 @@ cdef class Generator: with self.lock, nogil: for i in range(sz): ni = (<int64_t*>np.PyArray_MultiIter_DATA(it, 0))[0] - random_multinomial(&self._bitgen, ni, &mnix[offset], pix, d, &self._binomial) + pi = (<np.npy_intp*>np.PyArray_MultiIter_DATA(it, 1))[0] + random_multinomial(&self._bitgen, ni, &mnix[offset], &pix[pi], d, &self._binomial) offset += d np.PyArray_MultiIter_NEXT(it) return multin diff --git a/numpy/random/mtrand.pyx b/numpy/random/mtrand.pyx index 06e75a698..3e13503d0 100644 --- a/numpy/random/mtrand.pyx +++ b/numpy/random/mtrand.pyx @@ -1033,7 +1033,10 @@ cdef class RandomState: greater than or equal to low. The default value is 0. high : float or array_like of floats Upper boundary of the output interval. All values generated will be - less than or equal to high. The default value is 1.0. + less than or equal to high. The high limit may be included in the + returned array of floats due to floating-point rounding in the + equation ``low + (high-low) * random_sample()``. The default value + is 1.0. size : int or tuple of ints, optional Output shape. If the given shape is, e.g., ``(m, n, k)``, then ``m * n * k`` samples are drawn. If size is ``None`` (default), @@ -2524,7 +2527,7 @@ cdef class RandomState: Raises ------ ValueError - If a < 1. + If a <= 0. See Also -------- @@ -3606,7 +3609,7 @@ cdef class RandomState: `a` > 1. The Zipf distribution (also known as the zeta distribution) is a - continuous probability distribution that satisfies Zipf's law: the + discrete probability distribution that satisfies Zipf's law: the frequency of an item is inversely proportional to its rank in a frequency table. @@ -3639,9 +3642,10 @@ cdef class RandomState: ----- The probability density for the Zipf distribution is - .. math:: p(x) = \\frac{x^{-a}}{\\zeta(a)}, + .. math:: p(k) = \\frac{k^{-a}}{\\zeta(a)}, - where :math:`\\zeta` is the Riemann Zeta function. + for integers :math:`k \geq 1`, where :math:`\\zeta` is the Riemann Zeta + function. It is named for the American linguist George Kingsley Zipf, who noted that the frequency of any word in a sample of a language is inversely @@ -3657,21 +3661,29 @@ cdef class RandomState: -------- Draw samples from the distribution: - >>> a = 2. # parameter - >>> s = np.random.zipf(a, 1000) + >>> a = 4.0 + >>> n = 20000 + >>> s = np.random.zipf(a, n) Display the histogram of the samples, along with - the probability density function: + the expected histogram based on the probability + density function: >>> import matplotlib.pyplot as plt - >>> from scipy import special # doctest: +SKIP + >>> from scipy.special import zeta # doctest: +SKIP + + `bincount` provides a fast histogram for small integers. - Truncate s values at 50 so plot is interesting: + >>> count = np.bincount(s) + >>> k = np.arange(1, s.max() + 1) - >>> count, bins, ignored = plt.hist(s[s<50], 50, density=True) - >>> x = np.arange(1., 50.) - >>> y = x**(-a) / special.zetac(a) # doctest: +SKIP - >>> plt.plot(x, y/max(y), linewidth=2, color='r') # doctest: +SKIP + >>> plt.bar(k, count[1:], alpha=0.5, label='sample count') + >>> plt.plot(k, n*(k**-a)/zeta(a), 'k.-', alpha=0.5, + ... label='expected count') # doctest: +SKIP + >>> plt.semilogy() + >>> plt.grid(alpha=0.4) + >>> plt.legend() + >>> plt.title(f'Zipf sample, a={a}, size={n}') >>> plt.show() """ diff --git a/numpy/random/setup.py b/numpy/random/setup.py index dce9a101e..866c0cb2f 100644 --- a/numpy/random/setup.py +++ b/numpy/random/setup.py @@ -65,12 +65,26 @@ def configuration(parent_package='', top_path=None): 'src/distributions/random_mvhg_marginals.c', 'src/distributions/random_hypergeometric.c', ] + + def gl_if_msvc(build_cmd): + """ Add flag if we are using MSVC compiler + + We can't see this in our scope, because we have not initialized the + distutils build command, so use this deferred calculation to run when + we are building the library. + """ + # Keep in sync with numpy/core/setup.py + if build_cmd.compiler.compiler_type == 'msvc': + # explicitly disable whole-program optimization + return ['/GL-'] + return [] + config.add_installed_library('npyrandom', sources=npyrandom_sources, install_dir='lib', build_info={ 'include_dirs' : [], # empty list required for creating npyrandom.h - 'extra_compiler_args' : (['/GL-'] if is_msvc else []), + 'extra_compiler_args': [gl_if_msvc], }) for gen in ['mt19937']: diff --git a/numpy/random/src/distributions/distributions.c b/numpy/random/src/distributions/distributions.c index adf4db4a7..bd1e1faa4 100644 --- a/numpy/random/src/distributions/distributions.c +++ b/numpy/random/src/distributions/distributions.c @@ -17,7 +17,7 @@ static NPY_INLINE uint64_t next_uint64(bitgen_t *bitgen_state) { } static NPY_INLINE float next_float(bitgen_t *bitgen_state) { - return (next_uint32(bitgen_state) >> 9) * (1.0f / 8388608.0f); + return (next_uint32(bitgen_state) >> 8) * (1.0f / 16777216.0f); } /* Random generators for external use */ diff --git a/numpy/random/tests/test_direct.py b/numpy/random/tests/test_direct.py index ea1ebacb6..58d966adf 100644 --- a/numpy/random/tests/test_direct.py +++ b/numpy/random/tests/test_direct.py @@ -46,25 +46,27 @@ def assert_state_equal(actual, target): assert actual[key] == target[key] +def uint32_to_float32(u): + return ((u >> np.uint32(8)) * (1.0 / 2**24)).astype(np.float32) + + def uniform32_from_uint64(x): x = np.uint64(x) upper = np.array(x >> np.uint64(32), dtype=np.uint32) lower = np.uint64(0xffffffff) lower = np.array(x & lower, dtype=np.uint32) joined = np.column_stack([lower, upper]).ravel() - out = (joined >> np.uint32(9)) * (1.0 / 2 ** 23) - return out.astype(np.float32) + return uint32_to_float32(joined) def uniform32_from_uint53(x): x = np.uint64(x) >> np.uint64(16) x = np.uint32(x & np.uint64(0xffffffff)) - out = (x >> np.uint32(9)) * (1.0 / 2 ** 23) - return out.astype(np.float32) + return uint32_to_float32(x) def uniform32_from_uint32(x): - return (x >> np.uint32(9)) * (1.0 / 2 ** 23) + return uint32_to_float32(x) def uniform32_from_uint(x, bits): @@ -126,6 +128,7 @@ def gauss_from_uint(x, n, bits): return gauss[:n] + def test_seedsequence(): from numpy.random.bit_generator import (ISeedSequence, ISpawnableSeedSequence, diff --git a/numpy/random/tests/test_generator_mt19937.py b/numpy/random/tests/test_generator_mt19937.py index 7ddccaf86..e5411b8ef 100644 --- a/numpy/random/tests/test_generator_mt19937.py +++ b/numpy/random/tests/test_generator_mt19937.py @@ -136,12 +136,6 @@ class TestMultinomial: contig = random.multinomial(100, pvals=np.ascontiguousarray(pvals)) assert_array_equal(non_contig, contig) - def test_multidimensional_pvals(self): - assert_raises(ValueError, random.multinomial, 10, [[0, 1]]) - assert_raises(ValueError, random.multinomial, 10, [[0], [1]]) - assert_raises(ValueError, random.multinomial, 10, [[[0], [1]], [[1], [0]]]) - assert_raises(ValueError, random.multinomial, 10, np.array([[0, 1], [1, 0]])) - def test_multinomial_pvals_float32(self): x = np.array([9.9e-01, 9.9e-01, 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09, 1.0e-09], dtype=np.float32) @@ -774,6 +768,18 @@ class TestRandomDist: desired = 0.0969992 assert_array_almost_equal(actual, desired, decimal=7) + @pytest.mark.parametrize('dtype, uint_view_type', + [(np.float32, np.uint32), + (np.float64, np.uint64)]) + def test_random_distribution_of_lsb(self, dtype, uint_view_type): + random = Generator(MT19937(self.seed)) + sample = random.random(100000, dtype=dtype) + num_ones_in_lsb = np.count_nonzero(sample.view(uint_view_type) & 1) + # The probability of a 1 in the least significant bit is 0.25. + # With a sample size of 100000, the probability that num_ones_in_lsb + # is outside the following range is less than 5e-11. + assert 24100 < num_ones_in_lsb < 25900 + def test_random_unsupported_type(self): assert_raises(TypeError, random.random, dtype='int32') @@ -2349,6 +2355,64 @@ class TestBroadcast: [2, 3, 6, 4, 2, 3]], dtype=np.int64) assert_array_equal(actual, desired) + random = Generator(MT19937(self.seed)) + actual = random.multinomial([5, 20], [[1 / 6.] * 6] * 2) + desired = np.array([[0, 0, 2, 1, 2, 0], + [2, 3, 6, 4, 2, 3]], dtype=np.int64) + assert_array_equal(actual, desired) + + random = Generator(MT19937(self.seed)) + actual = random.multinomial([[5], [20]], [[1 / 6.] * 6] * 2) + desired = np.array([[[0, 0, 2, 1, 2, 0], + [0, 0, 2, 1, 1, 1]], + [[4, 2, 3, 3, 5, 3], + [7, 2, 2, 1, 4, 4]]], dtype=np.int64) + assert_array_equal(actual, desired) + + @pytest.mark.parametrize("n", [10, + np.array([10, 10]), + np.array([[[10]], [[10]]]) + ] + ) + def test_multinomial_pval_broadcast(self, n): + random = Generator(MT19937(self.seed)) + pvals = np.array([1 / 4] * 4) + actual = random.multinomial(n, pvals) + n_shape = tuple() if isinstance(n, int) else n.shape + expected_shape = n_shape + (4,) + assert actual.shape == expected_shape + pvals = np.vstack([pvals, pvals]) + actual = random.multinomial(n, pvals) + expected_shape = np.broadcast_shapes(n_shape, pvals.shape[:-1]) + (4,) + assert actual.shape == expected_shape + + pvals = np.vstack([[pvals], [pvals]]) + actual = random.multinomial(n, pvals) + expected_shape = np.broadcast_shapes(n_shape, pvals.shape[:-1]) + assert actual.shape == expected_shape + (4,) + actual = random.multinomial(n, pvals, size=(3, 2) + expected_shape) + assert actual.shape == (3, 2) + expected_shape + (4,) + + with pytest.raises(ValueError): + # Ensure that size is not broadcast + actual = random.multinomial(n, pvals, size=(1,) * 6) + + def test_invalid_pvals_broadcast(self): + random = Generator(MT19937(self.seed)) + pvals = [[1 / 6] * 6, [1 / 4] * 6] + assert_raises(ValueError, random.multinomial, 1, pvals) + assert_raises(ValueError, random.multinomial, 6, 0.5) + + def test_empty_outputs(self): + random = Generator(MT19937(self.seed)) + actual = random.multinomial(np.empty((10, 0, 6), "i8"), [1 / 6] * 6) + assert actual.shape == (10, 0, 6, 6) + actual = random.multinomial(12, np.empty((10, 0, 10))) + assert actual.shape == (10, 0, 10) + actual = random.multinomial(np.empty((3, 0, 7), "i8"), + np.empty((3, 0, 7, 4))) + assert actual.shape == (3, 0, 7, 4) + class TestThread: # make sure each state produces the same sequence even in threads diff --git a/numpy/random/tests/test_generator_mt19937_regressions.py b/numpy/random/tests/test_generator_mt19937_regressions.py index 88d2792a6..0227d6502 100644 --- a/numpy/random/tests/test_generator_mt19937_regressions.py +++ b/numpy/random/tests/test_generator_mt19937_regressions.py @@ -1,7 +1,7 @@ from numpy.testing import (assert_, assert_array_equal) import numpy as np import pytest -from numpy.random import Generator, MT19937, RandomState +from numpy.random import Generator, MT19937 mt19937 = Generator(MT19937()) |