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:
get_rng_statereturns an opaque object that does not alias the live generator: drawing a number afterwards does not change it.set_rng_state(get_rng_state())leaves the draws unchanged.manual_seedvalidates the seed withvalidate_seedbefore it touches a generator, and the same seed gives the same draws.seedvalidates the seed too, so a manager gets the seed domain even if itsmanual_seeddoes not check.- 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__). - 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]
|
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If the seed is not an |
ValueError
|
If the seed is not in the range |
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 |
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 |
ValueError
|
If the seed is not in the range |
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 |
ValueError
|
If the seed is not in the range |
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 |
ValueError
|
If the seed is not in the range |
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
|
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 |
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 |
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 |
ValueError
|
If the seed is not in the range |
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 |
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
|
|
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 |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the seed is not an |
ValueError
|
If the seed is not in the range |
MissingPackageError
|
If |
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 |
'cpu'
|
Returns:
| Type | Description |
|---|---|
Generator
|
A |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the seed is not an |
ValueError
|
If the seed is not in the range |
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 |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the seed is not an |
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
|
Yields:
| Type | Description |
|---|---|
Generator[None]
|
|
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
|
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 |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the seed is not an |
ValueError
|
If the seed is not in the range |
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 |
ValueError
|
If the seed is out of range. |
Example
>>> from coola.random import validate_seed
>>> validate_seed(42)