How can I retrieve the current seed of NumPy's random number generator?

41,225

Solution 1

The short answer is that you simply can't (at least not in general).

The Mersenne Twister RNG used by numpy has 219937-1 possible internal states, whereas a single 64 bit integer has only 264 possible values. It's therefore impossible to map every RNG state to a unique integer seed.

You can get and set the internal state of the RNG directly using np.random.get_state and np.random.set_state. The output of get_state is a tuple whose second element is a (624,) array of 32 bit integers. This array has more than enough bits to represent every possible internal state of the RNG (2624 * 32 > 219937-1).

The tuple returned by get_state can be used much like a seed in order to create reproducible sequences of random numbers. For example:

import numpy as np

# randomly initialize the RNG from some platform-dependent source of entropy
np.random.seed(None)

# get the initial state of the RNG
st0 = np.random.get_state()

# draw some random numbers
print(np.random.randint(0, 100, 10))
# [ 8 76 76 33 77 26  3  1 68 21]

# set the state back to what it was originally
np.random.set_state(st0)

# draw again
print(np.random.randint(0, 100, 10))
# [ 8 76 76 33 77 26  3  1 68 21]

Solution 2

This contribution is intended to serve as a clarification to the right answer from ali_m, and as an important correction to the suggestion from Dong Justin.


These are my findings:

  1. After setting the random seed using np.random.seed(X) you can find it again using np.random.get_state()[1][0].
  2. It will, however, be of little use to you.

The output from the following code sections will show you why both statements are correct.


Statement 1 - you can find the random seed using np.random.get_state()[1][0].

If you set the random seed using np.random.seed(123), you can retrieve the random state as a tuple using state = np.random.get_state(). Below is a closer look at state (I'm using the Variable explorer in Spyder). I'm using a screenshot since using print(state) will flood your console because of the size of the array in the second element of the tuple.

enter image description here

You can easily see 123 as the first number in the array contained in the second element. And using seed = np.random.get_state()[1][0] will give you 123. Perfect? Not quite, because:

Statement 2 - It will, however, be of little use to you:

It may not seem so at first though, because you could use np.random.seed(123), retrieve the same number with seed = np.random.get_state()[1][0], reset the seed with np.random.seed(444), and then (seemingly) set it back to the 123 scenario with np.random.seed(seed). But then you'd already know what your random seed was before, so you wouldn't need to do it that way. The next code section will also show that you can not take the first number of any random state using np.random.get_state()[1][0] and expect to recreate that exact scenario. Note that you'll most likely have to shut down and restart your kernel completely (or call np.random.seed(None)) in order to be able to see this.

The following snippet uses np.random.randint() to generate 5 random integers between -10 and 10, as well as storing some info about the process:

Snippet 1

# 1. Imports
import pandas as pd
import numpy as np

# 2. set random seed
#seedSet = None
seedSet = 123
np.random.seed(seedSet)

# 3. describe random state
state = np.random.get_state()
state5 = np.random.get_state()[1][:5]
seedState = np.random.get_state()[1][0]

# 4. generate random numbers
random = np.random.randint(-10, 10, size = 5)

# 5. organize and present findings
df = pd.DataFrame.from_dict({'seedSet':seedSet, 'seedState':seedState, 'state':state, 'random':random})
print(df)

Notice that the column named seedState is the same as the first number under state. I could have printed it as a stand-alone number, but I wanted to keep it all in the same place. Also notice that, seedSet = 123, and np.random.seed(seedSet) so far have been commented out. And because no random seed has been set, your numbers will differ from mine. But that is not what is important here, but rather the internal consisteny of your results:

Output 1:

   random seedSet   seedState       state
0       2    None  1558056443  1558056443
1      -1    None  1558056443  1808451632
2       4    None  1558056443   730968006
3      -4    None  1558056443  3568749506
4      -6    None  1558056443  3809593045

In this particular case seed = np.random.get_state()[1][0] equals 1558056443. And following the logic from Dong Justins answer (as well as my own answer prior to this edit), you could set the random seed with np.random.seed(1558056443) and obtain the same random state. The next snippet will show that you can not:

Snippet 2

# 1. Imports
import pandas as pd
import numpy as np

# 2. set random seed
#seedSet = None
seedSet = 1558056443
np.random.seed(seedSet)

# 3. describe random state
#state = np.random.get_state()
state = np.random.get_state()[1][:5]
seedState = np.random.get_state()[1][0]

# 4. generate random numbers
random = np.random.randint(-10, 10, size = 5)

# 5. organize and present findings
df = pd.DataFrame.from_dict({'seedSet':seedSet, 'seedState':seedState, 'state':state, 'random':random})
print(df)

Output 2:

   random     seedSet   seedState       state
0       8  1558056443  1558056443  1558056443
1       3  1558056443  1558056443  1391218083
2       7  1558056443  1558056443  2754892524
3      -8  1558056443  1558056443  1971852777
4       4  1558056443  1558056443  2881604748

See the difference? np.random.get_state()[1][0] is identical for Output 1 and Output 2, but the rest of the output is not (most importantly the random numbers are not the same). So, as ali_m already has clearly stated:

It's therefore impossible to map every RNG state to a unique integer seed.

Solution 3

Check the first element of the array returned by np.random.get_state(), it seems exactly the random seed to me.

Solution 4

This answer complements important details others missed. First, to rephrase the conclusion:

Original random seeds (set via np.random.seed) cannot be retrieved after generating numbers, but intermediates (current state) can.

Refer to @vestland's answer; it may, however, mislead: the generated numbers differ not due to inability to map states, but that an incomplete encoding is used: get_state()[1]. The complete representation includes pos = get_state()[2]. To illustrate:

import numpy as np

state0 = np.random.get_state()
rand0  = np.random.randint(0, 10, 1)
state1 = np.random.get_state()
rand1  = np.random.randint(0, 10, 1)

assert all(s0 == s1 for s0, s1 in zip(state0[1], state1[1]))

We generated a number, yet get_state()[1] remained identical. However:

np.random.set_state(state0)
assert np.random.randint(0, 10, 1) == rand0

and likewise for state1 & rand1. Hence, @vestland's numbers differ because when not setting a seed, pos = 623 - whereas if we use np.random.seed, pos = 624. Why the inconvenient discrepancy? No clue.


In summary on np.random.seed(s):

  • get_state()[1][0] immediately after setting: retrieves s that exactly recreates the state
  • get_state()[1][0] after generating numbers: may or may not retrieve s, but it will not recreate the current state (at get_state())
  • get_state()[1][0] after generating many numbers: will not retrieve s. This is because pos exhausted its representation.
  • get_state() at any point: will exactly recreate that point.

Lastly, behavior may also differ due to get_state()[3:] (and of course [0]).

Solution 5

While what the top answer says is generally true, in that it’s not possible in general, it is in fact possible. I would redirect you to this persons blog: https://kamila.akagi.moe/posts/mersenne-twister/

This individual developed a mersenne twister cracking algorithm to recover initial seeds, and provided the details and algorithm in full. I am not the author, and do not understand what the material in full, but anybody interested in doing this should check this out.

Share:
41,225

Related videos on Youtube

Mast
Author by

Mast

Mostly active on Code Review and I can usually be found in their chat (The 2nd Monitor). I'm one of the room owners there, feel free to ping me if anything is amiss. Active core member of the Charcoal project. Electrical engineer by day, jack of all trades by night.

Updated on September 07, 2021

Comments

  • Mast
    Mast over 2 years

    The following imports NumPy and sets the seed.

    import numpy as np
    np.random.seed(42)
    

    However, I'm not interested in setting the seed but more in reading it. random.get_state() does not seem to contain the seed. The documentation doesn't show an obvious answer.

    How do I retrieve the current seed used by numpy.random, assuming I did not set it manually?

    I want to use the current seed to carry over for the next iteration of a process.

    • ali_m
      ali_m over 8 years
      Could you explain what you mean by "use the current seed to carry over for the next iteration of a process"? Is there a reason why you can't simply use a combination of np.random.get_state and np.random.set_state, or else pass around an instance of np.random.RandomState to keep track of the internal state of the RNG?
    • Mast
      Mast over 8 years
      @ali_m If I fixed the seed, I know what seed to use to reproduce the result. However, if I haven't fixed the seed, how can I see what seed is used?
    • Charlie Parker
      Charlie Parker almost 8 years
      why did the answer to your question say The short answer is that you simply can't (at least not in the general case). however you accepted the answer. Did he manage to answer your question or not? I am confused.
    • Mast
      Mast almost 8 years
      @CharlieParker I accepted the answer lacking a better alternative. If you have an answer which says it can and how to do it, go ahead and post it. Acceptance marks can be moved.
    • Fangda Han
      Fangda Han about 3 years
      why don't your first set a seed by seed = np.random.randint(0, 100000)?
  • Charlie Parker
    Charlie Parker almost 8 years
    why are you saying that you can't The short answer is that you simply can't (at least not in the general case). it seems to me that u can from what I read on your answer. I'm confused.
  • Charlie Parker
    Charlie Parker almost 8 years
    I understand that the mapping doesn't work. But, if I use the giant tuple returned by numpy I'm all good right? I guess when you said "you can't" I'm not actually sure what you are referring too with respect to the original question and how that mapping thing has anything to do, does that make sense?
  • Charlie Parker
    Charlie Parker almost 8 years
    I don't know if anywhere there is a rigorous definition what seed needs to be in any context. If the tuple or any other structure returns you to the same randomness state that you desired, isn't that then a seed? I didn't know seed had to be an integer. But your answer seems to work, or is there a caveat except that integer representation that you mentioned?
  • ali_m
    ali_m almost 8 years
    Look, I'm not especially interested in debating the definition of "seed". As long as you're happy to call the output of get_state a "seed" then the code shown in my answer will work for you. I interpreted the OP's question as "what function does the inverse of np.random.seed?", and this is impossible for the reasons I discussed above.
  • Charlie Parker
    Charlie Parker almost 8 years
    I wasn't debating, I'm not interesting in debating. I was genuinely trying to make sure I understood your answer and that there wasn't a weird unexpected caveat later. As far as I can tell it works and your answer makes more sense to me, which is all good (hence my upvote ;) ). Thanks :)
  • Mast
    Mast about 6 years
    Yes, while it's not explicit, that's most probably how the answer already provided manages to work as it does.
  • bukzor
    bukzor about 6 years
    "impossible to map every RNG state to a unique integer seed." Since python has unlimited-precision integers, the state space of 64-bit integers is irrelevant, and this statement is false.
  • ali_m
    ali_m about 6 years
    @bukzor Numpy's RNG implementation uses C unsigned integers internally which have finite precision. In fact, it actually uses unsigned 32 bit integers, although it's also possible to pass an array containing multiple of these which I wasn't aware of. See here.
  • bukzor
    bukzor almost 6 years
    @ali_m Then it certainly fits in a python int.
  • ali_m
    ali_m almost 6 years
    @bukzor That's a moot point, since the seed argument to RandomState is required to be an integer between 0 and 2**32-1, or an array of such integers. In principle you could generate an array of uint32s by unpacking the bits in a native Python integer, but that's not something that RandomState itself supports.
  • OverLordGoldDragon
    OverLordGoldDragon about 4 years
    TL;DR intermediate random states cannot be restored (e.g. a state after generating 5 numbers). Nice writeup.
  • marsipan
    marsipan about 4 years
    Please check the code and output. In snippet 1, seedSet should be None, and state5 should be state. The column order is the dataframes disagrees with the outputs. The text says "seed=123" has been commented out...it has not, and in any case is not used. In snippet2, the commented statement should also be removed for clarity (the statement state = np.random.get_state() should not be in the code at all).
  • OverLordGoldDragon
    OverLordGoldDragon over 3 years
    Actually I got this in exact reverse; original random states (set via np.random.seed) cannot be retrieved after generating numbers, but intermediates (current state) can.
  • Admin
    Admin over 2 years
    Please add further details to expand on your answer, such as working code or documentation citations.