flynn.gg

Christopher Flynn

Machine Learning
Systems Architect,
PhD Mathematician

Home
Projects
Open Source
Blog
Résumé

GitHub
LinkedIn

Blog


RNG support for stochastic

2020-11-01 Feed

forest Photo by Thomas Stephan on Unsplash

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 BitGenerators, 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:

Examples

By 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}

Further reading

Stochastic

Stochastic processes

Python

Python Package Index

Back to the posts.