Skip to content

Random

coola.random

Utilities to control and restore random number generator states.

manual_seed seeds the global generators of random, and of NumPy and PyTorch when they are installed, in one call. get_rng_state and set_rng_state save and restore the state of every managed generator, and random_seed seeds the generators in a with block and restores their state on exit. A seed is an int in [0, 2**63).

Each generator is handled by a BaseRandomManager (PythonRandomManager, NumpyRandomManager, TorchRandomManager). The managers are stored in a RandomManagerRegistry; a manager for another generator is added with register_managers. The libraries are imported the first time they are used.

coola.random.BaseRandomManager

Bases: ABC

Implement the base class to manage randomness.

Each child class must implement the methods: - get_rng_state. - manual_seed. - set_rng_state.

The seed context manager is implemented with these methods.

A manager follows this contract:

  1. get_rng_state returns an opaque object that does not alias the live generator: drawing a number afterwards does not change it.
  2. set_rng_state(get_rng_state()) leaves the draws unchanged.
  3. manual_seed validates the seed with validate_seed before it touches a generator, and the same seed gives the same draws. seed validates the seed too, so a manager gets the seed domain even if its manual_seed does not check.
  4. A manager is stateless and cheap to build: it is shared by registries, equal to another instance of its class and hashable (__eq__ and __hash__).
  5. Building a manager of an uninstalled library raises an ImportError.

Only the global generators are managed. The generators created by the user (random.Random(), numpy.random.default_rng(), torch.Generator()) are not.

Example
>>> from coola.random import TorchRandomManager
>>> manager = TorchRandomManager()
>>> manager.manual_seed(42)

coola.random.BaseRandomManager.get_rng_state abstractmethod

get_rng_state() -> object

Get the current RNG state.

Returns:

Type Description
object

The current RNG state.

Example
>>> from coola.random import TorchRandomManager
>>> manager = TorchRandomManager()
>>> state = manager.get_rng_state()
>>> state
{'torch': tensor([...], dtype=torch.uint8)...}

coola.random.BaseRandomManager.manual_seed abstractmethod

manual_seed(seed: int) -> None

Set the seed for generating random numbers.

Parameters:

Name Type Description Default
seed int

The desired seed.

required
Example
>>> from coola.random import TorchRandomManager
>>> manager = TorchRandomManager()
>>> manager.manual_seed(42)

coola.random.BaseRandomManager.seed

seed(seed: int) -> Generator[None]

Temporarily set a seed and restore the RNG state afterward.

The state is restored even if an exception is raised inside the context.

The seed is validated with validate_seed before the state is read, so an invalid seed changes nothing.

Parameters:

Name Type Description Default
seed int

The seed to use while inside the context manager.

required

Yields:

Type Description
Generator[None]

None. The seed is set while the block runs.

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

Example
>>> import random
>>> from coola.random import PythonRandomManager
>>> manager = PythonRandomManager()
>>> with manager.seed(42):
...     first = random.random()
...
>>> with manager.seed(42):
...     second = random.random()
...
>>> first == second
True

coola.random.BaseRandomManager.set_rng_state abstractmethod

set_rng_state(state: Any) -> None

Set the RNG state.

Parameters:

Name Type Description Default
state Any

The new RNG state.

required
Example
>>> import torch
>>> from coola.random import TorchRandomManager
>>> manager = TorchRandomManager()
>>> state = manager.get_rng_state()
>>> manager.set_rng_state(state)

coola.random.NumpyRandomManager

Bases: InlineDisplayMixin, BaseRandomManager

Implement a random manager for the library numpy.

Only the legacy global generator of NumPy (numpy.random.seed, numpy.random.get_state and numpy.random.set_state) is managed. The generators created with numpy.random.default_rng() are independent objects: they are not seeded, and their state is not saved nor restored. Seed them explicitly, e.g. numpy.random.default_rng(seed).

The legacy generator only accepts a scalar seed between 0 and 2**32 - 1. A seed below 2**32 is given as is. A larger seed is split in two 32-bit words and given as an array, so two different seeds in [0, 2**63) never give the same random numbers.

Raises:

Type Description
MissingPackageError

If numpy is not installed.

Example
>>> from coola.random import NumpyRandomManager
>>> manager = NumpyRandomManager()
>>> manager.manual_seed(42)

coola.random.NumpyRandomManager.manual_seed

manual_seed(seed: int) -> None

Set the seed of the NumPy random number generator.

Parameters:

Name Type Description Default
seed int

The seed.

required

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

coola.random.PythonRandomManager

Bases: InlineDisplayMixin, BaseRandomManager

Implement a random manager for the python standard library random.

Only the global generator of the module (random.seed, random.getstate and random.setstate) is managed. The generators created with random.Random() are independent objects: they are not seeded, and their state is not saved nor restored.

Example
>>> from coola.random import PythonRandomManager
>>> manager = PythonRandomManager()
>>> manager.manual_seed(42)

coola.random.PythonRandomManager.manual_seed

manual_seed(seed: int) -> None

Set the seed of the Python random number generator.

Parameters:

Name Type Description Default
seed int

The seed.

required

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

coola.random.RandomManagerRegistry

Bases: Registry[str, BaseRandomManager], BaseRandomManager

Implement a registry of random managers that is a manager.

The registry maps string keys to random managers, in registration order. It has the API of coola.registry.Registry (register, register_many, unregister, copy, in, [], ...) and it is a BaseRandomManager, so it seeds, saves and restores the state of all its managers, and a registry can be registered in another registry.

The state of the registry is a dict that maps each key to the state of its manager.

Parameters:

Name Type Description Default
initial_state dict[K, V] | None

Optional initial mapping of string keys to managers. The mapping is copied.

None
Example
>>> from coola.random import RandomManagerRegistry, PythonRandomManager
>>> registry = RandomManagerRegistry({"random": PythonRandomManager()})
>>> registry
RandomManagerRegistry(
  (random): PythonRandomManager()
)
>>> registry.manual_seed(42)

coola.random.RandomManagerRegistry.manual_seed

manual_seed(seed: int) -> None

Set the seed of all the random number generators.

Parameters:

Name Type Description Default
seed int

The seed.

required

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

coola.random.RandomManagerRegistry.set_rng_state

set_rng_state(state: dict[str, Any]) -> None

Set the RNG state of all the managers.

The keys of the state must be the registered keys. All the managers are restored before an error is raised.

Parameters:

Name Type Description Default
state dict[str, Any]

The state to restore, as returned by get_rng_state.

required

Raises:

Type Description
ValueError

If the keys of the state differ from the registered keys. Nothing is restored.

Exception

If one manager fails to restore its state, the error of this manager is raised. If several managers fail, an ExceptionGroup is raised.

Example
>>> from coola.random import RandomManagerRegistry, PythonRandomManager
>>> registry = RandomManagerRegistry({"random": PythonRandomManager()})
>>> state = registry.get_rng_state()
>>> registry.set_rng_state(state)

coola.random.TorchRandomManager

Bases: InlineDisplayMixin, BaseRandomManager

Implement a random manager for the library torch.

Only the default generators of PyTorch (the CPU one, the CUDA ones and the MPS one if it is available) are managed. The generators created with torch.Generator() are independent objects: they are not seeded, and their state is not saved nor restored. Use make_torch_generator to create a seeded generator. The CUDA state is saved only if CUDA is already initialized: reading it would initialize CUDA, which fails in a forked worker, and an uninitialized CUDA has no state to save.

Raises:

Type Description
MissingPackageError

If torch is not installed.

Example
>>> from coola.random import TorchRandomManager
>>> manager = TorchRandomManager()
>>> manager.manual_seed(42)

coola.random.TorchRandomManager.manual_seed

manual_seed(seed: int) -> None

Set the seed of the PyTorch random number generator.

Parameters:

Name Type Description Default
seed int

The seed.

required

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

coola.random.TorchRandomManager.set_rng_state

set_rng_state(state: dict[str, Any]) -> None

Restore the state of the PyTorch random number generators.

Parameters:

Name Type Description Default
state dict[str, Any]

The state returned by get_rng_state. Only the generators in the state are restored.

required

Raises:

Type Description
ValueError

If the number of CUDA generators of the state differs from the number of CUDA devices, or if the state has an MPS generator but MPS is not available.

coola.random.get_default_registry

get_default_registry() -> RandomManagerRegistry

Get or create the default global registry with common random managers.

Returns a singleton registry instance that is pre-configured with managers for common random number generation libraries including Python's random module, NumPy (if available), and PyTorch (if available).

This function uses a singleton pattern to ensure the same registry instance is returned on subsequent calls, which is efficient and maintains consistency across an application.

Returns:

Type Description
RandomManagerRegistry

A singleton RandomManagerRegistry with a manager for Python's

RandomManagerRegistry

random module and optional managers for NumPy/PyTorch when those

RandomManagerRegistry

dependencies are available.

Notes

The singleton pattern means modifications to the returned registry affect all future calls to this function. If you need an isolated registry, create a new RandomManagerRegistry instance directly.

Example
>>> from coola.random import get_default_registry
>>> registry = get_default_registry()
>>> # Registry is ready to use with available random managers
>>> registry
RandomManagerRegistry(
  (random): PythonRandomManager()
  (numpy): NumpyRandomManager()
  (torch): TorchRandomManager()
)

coola.random.get_rng_state

get_rng_state() -> dict[str, Any]

Get the current RNG state.

Returns:

Type Description
dict[str, Any]

Mapping of manager names to their serialized RNG states.

Example
>>> from coola.random import get_rng_state
>>> state = get_rng_state()
>>> "random" in state
True

coola.random.make_numpy_generator

make_numpy_generator(seed: int) -> Generator

Create a numpy.random.Generator initialized with a given seed.

The generator is independent from the global generator of NumPy: it is the way to get reproducible random numbers in a thread, where random_seed must not be used.

Parameters:

Name Type Description Default
seed int

A random seed.

required

Returns:

Type Description
Generator

A numpy.random.Generator object.

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

MissingPackageError

If numpy is not installed.

Example
>>> from coola.random import make_numpy_generator
>>> generator = make_numpy_generator(42)
>>> generator.random(2)
array([0.77395605, 0.43887844])
>>> generator = make_numpy_generator(42)
>>> generator.random(2)
array([0.77395605, 0.43887844])

coola.random.make_torch_generator

make_torch_generator(
    seed: int, *, device: device | str | None = "cpu"
) -> Generator

Create a torch.Generator initialized with a given seed.

The generator is independent from the default generators of PyTorch: it is the way to get reproducible random numbers in a thread, where random_seed must not be used.

Parameters:

Name Type Description Default
seed int

A random seed.

required
device device | str | None

The desired device for the generator, a torch.device, a string or None.

'cpu'

Returns:

Type Description
Generator

A torch.Generator object.

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

Example
>>> import torch
>>> from coola.random import make_torch_generator
>>> generator = make_torch_generator(42)
>>> torch.rand(2, 4, generator=generator)
tensor([[0.8823, 0.9150, 0.3829, 0.9593],
        [0.3904, 0.6009, 0.2566, 0.7936]])
>>> generator = make_torch_generator(42)
>>> torch.rand(2, 4, generator=generator)
tensor([[0.8823, 0.9150, 0.3829, 0.9593],
        [0.3904, 0.6009, 0.2566, 0.7936]])

coola.random.manual_seed

manual_seed(seed: int) -> None

Set the seed for generating random numbers.

Note

The first call with the default registry creates it, and it imports every installed library that it can seed (NumPy and PyTorch), which can take about a second. The libraries are imported then, not when they are used later, so that a library that is imported after this call is still seeded.

Parameters:

Name Type Description Default
seed int

The desired random seed. It must be an int in the range [0, 2**63) (the range supported by all the backends).

required

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is out of range. No generator is seeded in this case.

Example
>>> import random
>>> from coola.random import manual_seed
>>> manual_seed(42)
>>> first = random.random()
>>> manual_seed(42)
>>> second = random.random()
>>> first == second
True

coola.random.random_seed

random_seed(
    seed: int,
    *,
    registry: RandomManagerRegistry | None = None,
) -> Generator[None]

Temporarily set a random seed and restore previous RNG state.

Note

The first call with the default registry creates it, and it imports every installed library that it can seed (NumPy and PyTorch), which can take about a second. The libraries are imported then, not when they are used later, so that a library that is imported after this call is still seeded.

Parameters:

Name Type Description Default
seed int

Seed used while inside the context manager.

required
registry RandomManagerRegistry | None

Random manager registry to use. If None, the default registry is used.

None

Yields:

Type Description
Generator[None]

None. The seed is set while the block runs.

Example
>>> import random
>>> from coola.random import random_seed
>>> with random_seed(42):
...     first = random.random()
...
>>> with random_seed(42):
...     second = random.random()
...
>>> first == second
True

coola.random.register_managers

register_managers(
    mapping: Mapping[str, BaseRandomManager],
    *,
    exist_ok: bool = False,
) -> None

Register custom managers to the default global registry.

This allows users to add support for custom random number generators without modifying global state directly.

Parameters:

Name Type Description Default
mapping Mapping[str, BaseRandomManager]

Mapping of manager names to manager instances.

required
exist_ok bool

If False, raise an error when a manager name is already registered.

False
Example
>>> from coola.random import register_managers, PythonRandomManager
>>> register_managers({"custom": PythonRandomManager()})  # doctest: +SKIP

coola.random.sample_torch_seed

sample_torch_seed(seed: int) -> int

Get a random seed derived from a given seed.

The value is drawn from a torch.Generator seeded with seed, so it is deterministic and does not change the state of the default generators.

Parameters:

Name Type Description Default
seed int

A random seed to make the process reproducible.

required

Returns:

Type Description
int

A random seed in [0, 2**63 - 1). It is a valid seed for manual_seed.

Raises:

Type Description
TypeError

If the seed is not an int.

ValueError

If the seed is not in the range [0, 2**63).

Example
>>> from coola.random import sample_torch_seed
>>> sample_torch_seed(44)
6176747449835261348

coola.random.set_rng_state

set_rng_state(state: dict[str, Any]) -> None

Set the RNG state.

Parameters:

Name Type Description Default
state dict[str, Any]

The new RNG state.

required
Example
>>> from coola.random import get_rng_state, set_rng_state
>>> before = get_rng_state()
>>> set_rng_state(before)
>>> after = get_rng_state()
>>> before.keys() == after.keys()
True

coola.random.validate_seed

validate_seed(seed: int) -> None

Validate a random seed.

The seed must be an int in the range [0, 2**63), which is supported by all the backends (random, NumPy and PyTorch).

Parameters:

Name Type Description Default
seed int

The seed to validate.

required

Raises:

Type Description
TypeError

If the seed is not an int (bool is rejected too).

ValueError

If the seed is out of range.

Example
>>> from coola.random import validate_seed
>>> validate_seed(42)