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 Random`GetRandomState[]
. This returns something more than just the generator object. Example:
BlockRandom[
SeedRandom[Method -> BlumBlumShub];
RandomInteger[10, 10];
Random`GetRandomState[]
]
Random`GeneratorState[{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]
wheregsym
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.)
Link to Wolfram Community question.
Comments
Post a Comment