This rule raises an issue when legacy `numpy.random.RandomState` is used.

== Why is this an issue?

Using a predictable seed is a common best practice when using NumPy to create reproducible results. To that end, using `np.random.seed(number)` to set the seed of the global `numpy.random.RandomState` has been the privileged solution for a long time.

`numpy.random.RandomState` and its associated methods rely on a global state, which may be problematic when threads or other forms of concurrency are involved. The global state may be altered and the global seed may be reset at various points in the program (for instance, through an imported package or script), which would lead to irreproducible results.

Instead, the preferred best practice to generate reproducible pseudorandom numbers is to instantiate a `numpy.random.Generator` object with a seed and reuse it in different parts of the code. This avoids the reliance on a global state. Whenever a new seed is needed, a new generator may be created instead of mutating a global state.

Below is the list of legacy functions and their alternatives:

[cols="1,1"]
|===
|Legacy function name
|numpy.random.Generator alternative

|numpy.random.RandomState.seed
|numpy.random.default_rng

|numpy.random.RandomState.rand
|numpy.random.Generator.random

|numpy.random.RandomState.randn
|numpy.random.Generator.standard_normal

|numpy.random.RandomState.randint
|numpy.random.Generator.integers

|numpy.random.RandomState.random_integers
|numpy.random.Generator.integers

|numpy.random.RandomState.random_sample
|numpy.random.Generator.random

|numpy.random.RandomState.choice
|numpy.random.Generator.choice

|numpy.random.RandomState.bytes
|numpy.random.Generator.bytes

|numpy.random.RandomState.shuffle
|numpy.random.Generator.shuffle

|numpy.random.RandomState.permutation
|numpy.random.Generator.permutation

|numpy.random.RandomState.beta
|numpy.random.Generator.beta

|numpy.random.RandomState.binomial
|numpy.random.Generator.binomial

|numpy.random.RandomState.chisquare
|numpy.random.Generator.chisquare

|numpy.random.RandomState.dirichlet
|numpy.random.Generator.dirichlet

|numpy.random.RandomState.exponential
|numpy.random.Generator.exponential

|numpy.random.RandomState.f
|numpy.random.Generator.f

|numpy.random.RandomState.gamma
|numpy.random.Generator.gamma

|numpy.random.RandomState.geometric
|numpy.random.Generator.geometric

|numpy.random.RandomState.gumbel
|numpy.random.Generator.gumbel

|numpy.random.RandomState.hypergeometric
|numpy.random.Generator.hypergeometric

|numpy.random.RandomState.laplace
|numpy.random.Generator.laplace

|numpy.random.RandomState.logistic
|numpy.random.Generator.logistic

|numpy.random.RandomState.lognormal
|numpy.random.Generator.lognormal

|numpy.random.RandomState.logseries
|numpy.random.Generator.logseries

|numpy.random.RandomState.multinomial
|numpy.random.Generator.multinomial

|numpy.random.RandomState.multivariate_normal
|numpy.random.Generator.multivariate_normal

|numpy.random.RandomState.negative_binomial
|numpy.random.Generator.negative_binomial

|numpy.random.RandomState.noncentral_chisquare
|numpy.random.Generator.noncentral_chisquare

|numpy.random.RandomState.noncentral_f
|numpy.random.Generator.noncentral_f

|numpy.random.RandomState.normal
|numpy.random.Generator.normal

|numpy.random.RandomState.pareto
|numpy.random.Generator.pareto

|numpy.random.RandomState.poisson
|numpy.random.Generator.poisson

|numpy.random.RandomState.power
|numpy.random.Generator.power

|numpy.random.RandomState.rayleigh
|numpy.random.Generator.rayleigh

|numpy.random.RandomState.standard_cauchy
|numpy.random.Generator.standard_cauchy

|numpy.random.RandomState.standard_exponential
|numpy.random.Generator.standard_exponential

|numpy.random.RandomState.standard_gamma
|numpy.random.Generator.standard_gamma

|numpy.random.RandomState.standard_normal
|numpy.random.Generator.standard_normal

|numpy.random.RandomState.standard_t
|numpy.random.Generator.standard_t

|numpy.random.RandomState.triangular
|numpy.random.Generator.triangular

|numpy.random.RandomState.uniform
|numpy.random.Generator.uniform

|numpy.random.RandomState.vonmises
|numpy.random.Generator.vonmises

|numpy.random.RandomState.wald
|numpy.random.Generator.wald

|numpy.random.RandomState.weibull
|numpy.random.Generator.weibull

|numpy.random.RandomState.zipf
|numpy.random.Generator.zipf

|numpy.random.beta
|numpy.random.Generator.beta

|numpy.random.binomial
|numpy.random.Generator.binomial

|numpy.random.bytes
|numpy.random.Generator.bytes

|numpy.random.chisquare
|numpy.random.Generator.chisquare

|numpy.random.choice
|numpy.random.Generator.choice

|numpy.random.dirichlet
|numpy.random.Generator.dirichlet

|numpy.random.exponential
|numpy.random.Generator.exponential

|numpy.random.f
|numpy.random.Generator.f

|numpy.random.gamma
|numpy.random.Generator.gamma

|numpy.random.geometric
|numpy.random.Generator.geometric

|numpy.random.gumbel
|numpy.random.Generator.gumbel

|numpy.random.hypergeometric
|numpy.random.Generator.hypergeometric

|numpy.random.laplace
|numpy.random.Generator.laplace

|numpy.random.logistic
|numpy.random.Generator.logistic

|numpy.random.lognormal
|numpy.random.Generator.lognormal

|numpy.random.logseries
|numpy.random.Generator.logseries

|numpy.random.multinomial
|numpy.random.Generator.multinomial

|numpy.random.multivariate_normal
|numpy.random.Generator.multivariate_normal

|numpy.random.negative_binomial
|numpy.random.Generator.negative_binomial

|numpy.random.noncentral_chisquare
|numpy.random.Generator.noncentral_chisquare

|numpy.random.noncentral_f
|numpy.random.Generator.noncentral_f

|numpy.random.normal
|numpy.random.Generator.normal

|numpy.random.pareto
|numpy.random.Generator.pareto

|numpy.random.permutation
|numpy.random.Generator.permutation

|numpy.random.poisson
|numpy.random.Generator.poisson

|numpy.random.power
|numpy.random.Generator.power

|numpy.random.rand
|numpy.random.Generator.random

|numpy.random.randint
|numpy.random.Generator.integers

|numpy.random.randn
|numpy.random.Generator.standard_normal

|numpy.random.random
|numpy.random.Generator.random

|numpy.random.random_integers
|numpy.random.Generator.integers

|numpy.random.random_sample
|numpy.random.Generator.random

|numpy.random.ranf
|numpy.random.Generator.random

|numpy.random.rayleigh
|numpy.random.Generator.rayleigh

|numpy.random.sample
|numpy.random.Generator.random

|numpy.random.seed
|numpy.random.default_rng

|numpy.random.shuffle
|numpy.random.Generator.shuffle

|numpy.random.standard_cauchy
|numpy.random.Generator.standard_cauchy

|numpy.random.standard_exponential
|numpy.random.Generator.standard_exponential

|numpy.random.standard_gamma
|numpy.random.Generator.standard_gamma

|numpy.random.standard_normal
|numpy.random.Generator.standard_normal

|numpy.random.standard_t
|numpy.random.Generator.standard_t

|numpy.random.triangular
|numpy.random.Generator.triangular

|numpy.random.uniform
|numpy.random.Generator.uniform

|numpy.random.vonmises
|numpy.random.Generator.vonmises

|numpy.random.wald
|numpy.random.Generator.wald

|numpy.random.weibull
|numpy.random.Generator.weibull

|numpy.random.zipf 
|numpy.random.Generator.zipf 
|===



== How to fix it

To fix this issue, replace usages of `numpy.random.RandomState` to `numpy.random.Generator`.

=== Code examples

==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
----
import numpy as np
def foo():
    np.random.seed(42)
    x = np.random.randn()  # Noncompliant: this relies on numpy.random.RandomState, which is deprecated
----

==== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
----
import numpy as np
def foo():
    generator = np.random.default_rng(42)
    x = generator.standard_normal()
----

== Resources
=== Documentation

* NumPy Documentation - https://numpy.org/doc/stable/reference/random/generator.html#random-generator[Random Generator]
* NumPy Documentation - https://numpy.org/doc/stable/reference/random/legacy.html#legacy-random-generation[Legacy Random Generation]
* NumPy Documentation - https://numpy.org/neps/nep-0019-rng-policy.html[NEP19 RNG Policy]
