JAKE
JAKE

Reputation: 547

Does numpy.random.seed() always give the same random number every time?

I know that numpy.random.seed(seed) will output the same numbers if I use the same seed. My question is, does this change over time? If I try to call it again tomorrow, will it still output the same set of random numbers as yesterday?

Upvotes: 3

Views: 5233

Answers (2)

Mateen Ulhaq
Mateen Ulhaq

Reputation: 27281

The np.random documentation describes the PRNGs used. Apparently, there was a partial switch from MT19937 to PCG64 in the recent past. If you want consistency, you'll need to:

  1. fix the PRNG used, and
  2. ensure that you're using a local handle (e.g. RandomState, Generator) so that any changes to other external libraries don't mess things up by calling np.random globals themselves.

In this example, we make use of the newer BitGenerator API, which provides a selection of various PRNGs.

from numpy.random import Generator, PCG64

rg = Generator(PCG64(1234))

Which may be used as follows:

>>> rg.uniform(0, 10, 10)
array([9.767, 3.802, 9.232, 2.617, 3.191, 1.181, 2.418, 3.185, 9.641,
       2.636])

If we re-run this any number of times (even within the same REPL!), we will always obtain the same random number generator. PCG64, like MT19937, provides the following guarantee:

Compatibility Guarantee

PCG64 makes a guarantee that a fixed seed and will always produce the same random integer stream.

Though, as @user2357112 supports Monica noted, changes to the random API functions that use the random integer sequence (e.g. np.random.Generator.uniform) are still technically possible, though unlikely.

In order to generate multiple generators, one can make use of SeedSequence.spawn(k) to generate k different SeedSequences. This is useful for consistent concurrent computations:

from numpy.random import Generator, PCG64, SeedSequence

sg = SeedSequence(1234)
rgs = [Generator(PCG64(s)) for s in sg.spawn(10)]

Upvotes: 4

user2357112
user2357112

Reputation: 282198

The legacy RandomState API and the module-level random generation functions (actually methods of a hidden RandomState) have a backward compatibility guarantee:

Compatibility Guarantee

A fixed bit generator using a fixed seed and a fixed series of calls to ‘RandomState’ methods using the same parameters will always produce the same results up to roundoff error except when the values were incorrect. RandomState is effectively frozen and will only receive updates that are required by changes in the the internals of Numpy. More substantial changes, including algorithmic improvements, are reserved for Generator.

Identical sequences of calls from an identical seed will produce identical-up-to-rounding-error results, unless there was something wrong with the old results (like if it turned out a method wasn't producing the distribution it was supposed to).

This comes at the expense of being locked into bad design choices. For example, numpy.random.choice with replace=False is atrociously slow due to a bad implementation that cannot be fixed without breaking backward compatibility. numpy.random.Generator.choice does not have this problem, since it is not bound by the same compatibility guarantee.

Upvotes: 4

Related Questions