Why doesn't SeedRandom work here?

From the docs on SeedRandom,

SeedRandom[] resets the generator, using as a seed the time of day and certain attributes of the current Wolfram System session.

This means that every time I use SeedRandom[] without an argument, I should get a new sequence of random numbers. (Note: When evaluating the code in this post, make sure to wait a few seconds between evaluations to ensure that the seed taken from the system timer is different.)

Indeed, evaluating

BlockRandom[
SeedRandom[]; RandomInteger[10, 10]
]


multiple times gives different results.

But now try setting the generation method and evaluate this in a new kernel:

Table[
BlockRandom[
Pause[2]; SeedRandom[Method -> "Congruential"]; RandomInteger[10, 10]
],
{4}
]

{{9, 5, 2, 8, 6, 5, 6, 4, 2, 3},
{1, 4, 7, 4, 8, 0, 9, 5, 3, 6},
{1, 4, 7, 4, 8, 0, 9, 5, 3, 6},
{1, 4, 7, 4, 8, 0, 9, 5, 3, 6}}


After the second evaluation, it keeps returning the same sequence of numbers. Why?

This does not happen if evaluating the same outside of BlockRandom.

To try to get to the bottom of this, we can try to define our own random number generator and see how Mathematica calls it. I took the Blum-Blum-Shub example from the documentation and added Print commands to the initialization, seeding and generation functions to see how it is being called under the hood. Performing the same experiment as above (BlockRandom[SeedRandom[Method -> BlumBlumShub]; RandomInteger[10, 10]]) shows that the generator is called only the very first time. After that it is not re-seeded and cached results are being returned.

Experimenting with this and trying to find out how many numbers are cached shows some weird things again: generating 100 numbers instead of 10 calls the generator every time, yet the results returned are still the same every time. The threshold at which the generator is being called seems to depend on many details, I couldn't find out what exactly. It is unusual that although all 100 numbers seem to be cached, the generator is still invoked every time (why?).

These observations about caching bring up an important question:

Does the generator framework assume that two user-defined objects that are equal at the Mathematica level (== or ===) truly have the same internal state? I was implementing a generator in C and the Mathematica side generator object is nothing more than a handle to a C-side data structure (a managed library expression).

Update

There's an internal function called RandomGetRandomState[]. This returns something more than just the generator object. Example:

BlockRandom[
SeedRandom[Method -> BlumBlumShub];
RandomInteger[10, 10];
RandomGetRandomState[]
]

RandomGeneratorState[{BlumBlumShub, {}},
{{1389677191105303686723527819391197837920730913355627310599292454172523542012932098584330324866567942749879326659731493946935471621493571, 24, 7, 5},
{BlumBlumShub[1606938044258990275541964487092870409276299325644596228407321, 7, 127, 1268550763627358771726516777152512424855599645376602106716491], 7, 1, None}}
]


What is notable here is that the result is entirely determined by the parameters in RandomInteger[10, 10]. Changing it to RandomInteger[10, 100] changes the result predictably, changing it back changes the result back too.

This suggests that the RNG framework does work with the internal state of user defined generators, and perhaps caches it. That would mean that it is not safe to store the state somewhere else (e.g. in a C data structure).

This seems to contradict the documentation, which says,

A generator object is of the form gsym[data] where gsym is the symbol that identifies the generator and to which rules are attached. data is effectively private to the top-level evaluations associated with the generator definitions.

(Emphasis by me.)

• Idle thought: what did $RandomState look like in between different calls? Table[BlockRandom[Pause[2]; SeedRandom[Method -> "Congruential"]; {RandomInteger[10, 10],$RandomState}], {4}] – J. M.'s technical difficulties Aug 13 '15 at 9:54
• @J. M. $RandomState doesn't even change with re-seeding unless I'm using the default generation method. I think it's only relevant for the default methods. – Szabolcs Aug 13 '15 at 10:05 • But, the repetition you observed also happens with other methods like "MersenneTwister" and "Rule30CA"? – J. M.'s technical difficulties Aug 13 '15 at 10:16 • Ah, I'd forgotten about GetRandomState[]; I think you're right in that $RandomState is really only intended for "Legacy". – J. M.'s technical difficulties Aug 13 '15 at 10:21
• @J. M. Yes, the repetition is the same with other methods as well. – Szabolcs Aug 13 '15 at 10:25

It would seem that if only a Method is given and it does not change what is already set that the seed is not randomized:

Table[
SeedRandom[Method -> "ExtendedCA"];
SeedRandom[1];
SeedRandom[Method -> "ExtendedCA"];
RandomInteger[10, 10]
, {3}
]

{{1, 4, 0, 7, 0, 0, 8, 6, 0, 4},
{1, 4, 0, 7, 0, 0, 8, 6, 0, 4},
{1, 4, 0, 7, 0, 0, 8, 6, 0, 4}}


If the Method results in a change then the seed is randomized:

Table[
SeedRandom[Method -> "MKL"];
SeedRandom[1];
SeedRandom[Method -> "ExtendedCA"];
RandomInteger[10, 10]
, {3}
]

{{1, 8, 5, 1, 1, 1, 3, 2, 10, 1},
{6, 0, 2, 6, 4, 5, 4, 3, 0, 1},
{3, 5, 3, 0, 3, 2, 3, 9, 5, 1}}
`
• The more I look at it, the more it seems that Mathematica messes with the internal state of my generator object (which is supposed to be private). This way "seeding" isn't such a clear concept anymore. Other things that might happen is that Mathematica simply stores multiple copies of the generator object and assumes that the internal state of the copies won't change (that would be "re-seeding with the same seed"). – Szabolcs Aug 13 '15 at 10:37