Machine Learning
Systems Architect,
PhD Mathematician
The python stochastic
package now supports seeding and control over the random number generation, both globally and on a per process instance basis. The latest release (0.3.0) includes breaking changes which refactor the package’s module structure in order to include a random
submodule, from which the user can control the random number generator used in all the stochastic process classes.
Under the hood, stochastic uses numpy
for random number generation (RNG). The changes expose this RNG in the stochastic.random
submodule, where the RNG var generator
can be seeded or swapped out for alternative implementations from the default.
The changes leverage some of the more recent improvements to numpy
's own RNG, in which they use a new implementation using BitGenerator
s, which take place of the legacy RandomState
objects. The newer BitGenerators are generally faster for all Guassian- and exponential-derived processes in stochastic. From the numpy docs:
The Generator’s normal, exponential and gamma functions
use 256-step Ziggurat methods which are 2-10 times
faster than NumPy’s Box-Muller or inverse CDF implementations.
The major changes in stochastic are:
stochastic.processes
stochastic.utils.validation
stochastic.random
module for RNG controlrng
initialization arg for customizing instance RNGBy default, stochastic uses the newer BitGenerator RNG. The random submodule has functionality for changing the default random number generation on any instances without an explicitly passed rng
argument:
from stochastic.processes import GaussianNoise
from stochastic import random
gn = GaussianNoise()
print(gn.rng)
# Generator(PCG64)
# use the legacy random number generator
random.use_randomstate()
print(gn.rng)
# <module 'numpy.random' from '/path/to/site-packages/numpy/random/__init__.py'>
# use the newer Generator
random.use_generator()
print(gn.rng)
# Generator(PCG64)
The seed value of the generator can now be set. Setting the seed creates a new Generator on the stochastic.random.generator
module variable, which is referenced in stochastic process class instances when sampling.
from stochastic.processes import GaussianNoise
from stochastic import random
gn = GaussianNoise()
print(gn.rng)
# Generator(PCG64)
random.seed(42)
print(gn.rng.bit_generator.state)
# {'bit_generator': 'PCG64', 'state': {'state': 274674114334540486603088602300644985544, 'inc': 332724090758049132448979897138935081983}, 'has_uint32': 0, 'uinteger': 0}
print(gn.sample(4))
# [ 0.15235854 -0.51999205 0.3752256 0.47028236]
random.seed(42)
print(gn.rng.bit_generator.state)
# {'bit_generator': 'PCG64', 'state': {'state': 274674114334540486603088602300644985544, 'inc': 332724090758049132448979897138935081983}, 'has_uint32': 0, 'uinteger': 0}
print(gn.sample(4))
# [ 0.15235854 -0.51999205 0.3752256 0.47028236]
Custom generators can be passed to process instances at instantiation. Such generators will function independently of the default generator in stochastic.random
.
from numpy.random import Generator
from numpy.random import PCG64
from stochastic.processes import GaussianNoise
from stochastic import random
generator = Generator(PCG64(seed=42))
gn = GaussianNoise(rng=generator)
# generator specific to this gaussian noise instance
print(gn.rng.bit_generator.state)
# {'bit_generator': 'PCG64', 'state': {'state': 274674114334540486603088602300644985544, 'inc': 332724090758049132448979897138935081983}, 'has_uint32': 0, 'uinteger': 0}
# stochastic's global generator, different from the one attached to `gn`
print(random.generator.bit_generator.state)
# {'bit_generator': 'PCG64', 'state': {'state': 228239801863081385502825691348763076514, 'inc': 61631449755775032062670113901777656135}, 'has_uint32': 0, 'uinteger': 0}