Rng¤
substrax.rng takes keys from an explicit owner and derives streams from a seed by name.
Nothing in it falls back to a default seed: two callers never share a key they did not ask
for.
from flax import nnx
from substrax.rng import fold_in_name, key_from, rngs_from_seed, split_key
rngs = rngs_from_seed(42, streams=("params", "dropout", "sample"))
# A sampler accepts an Rngs with a `sample` stream, or a key given directly.
key = key_from(rngs, streams=("sample", "default"), context="sampling")
per_walker = split_key(key, 1024)
# A sub-component derives its own key from a name, the same in every interpreter.
noise_key = fold_in_name(key, "observation_noise")
| Function | Behaviour |
|---|---|
key_from(rng, *, streams, context) |
With an nnx.Rngs, calls the first of streams it holds, in the order given; a typed key or a raw uint32 key of shape (2,) is returned as given. A missing stream raises MissingRngStreamError naming context and the streams; None, a Python integer or any other array raises TypeError |
rngs_from_seed(seed, streams=("default",)) |
One stream per name, each the seed's key folded by the name, so adding or reordering streams leaves the others unchanged |
split_key(key, num) |
jax.random.split with num at least one; asking for no keys is a defect |
fold_in_name(key, name) |
Folds in the name's BLAKE2b digest truncated to 32 bits, stable across interpreters whatever PYTHONHASHSEED is |
rngs_from_seed differs from splitting the seed key into one part per stream, which every
package used to do: there, appending a stream moved every existing stream's key, so a
recorded run stopped reproducing when a model gained a dropout stream.
The pytest plugin (substrax.testing.pytest_plugin) provides rng_key, a typed key from
seed 42, and rngs, an nnx.Rngs from the same seed with default, params, dropout
and sample streams.
substrax.rng
¤
Keys from an explicit owner, streams derived from a seed by name, splits and named folds.
key_from takes a key from an nnx.Rngs stream or a key given directly and never falls
back to a default seed; rngs_from_seed derives each stream from the seed and the stream's
name, so streams are independent of each other; split_key and fold_in_name are the
two derivations every package needs, with a digest that is stable across interpreters.
MissingRngStreamError(context, streams)
¤
Bases: ValueError
An nnx.Rngs holds none of the streams a call accepts.
Attributes:
| Name | Type | Description |
|---|---|---|
context |
str
|
What needed the key, as the caller named it. |
streams |
tuple[str, ...]
|
The stream names the caller accepts, in order of preference. |
Name what needed the key and the streams it accepts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
str
|
What needed the key, as the caller named it. |
required |
streams
|
Sequence[str]
|
The stream names the caller accepts, in order of preference. |
required |
fold_in_name(key, name)
¤
Fold a name into key through a stable digest.
The name's BLAKE2b digest, truncated to 32 bits, is folded in, so the derived key is the
same in every interpreter, whatever PYTHONHASHSEED is; Python's hash is salted per
process and is never used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Array
|
The key to derive from. |
required |
name
|
str
|
A non-empty name, such as a stream's. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
The derived key. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
key_from(rng, *, streams, context)
¤
Return a key from an explicit owner: an nnx.Rngs stream or a key given directly.
With an nnx.Rngs, the first of streams it holds is called, in the order given. A
typed key (jax.random.key) or a raw uint32 key of shape (2,) is returned as
given. There is no fallback: a missing stream, None, a Python integer or any other array
raises, so two callers never share a key drawn from a default seed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rng
|
Array | Rngs
|
The key's owner. |
required |
streams
|
Sequence[str]
|
Stream names accepted, most preferred first. |
required |
context
|
str
|
What needs the key, named in the errors ( |
required |
Returns:
| Type | Description |
|---|---|
Array
|
The key. |
Raises:
| Type | Description |
|---|---|
MissingRngStreamError
|
If |
TypeError
|
If |
rngs_from_seed(seed, streams=('default',))
¤
Build an nnx.Rngs whose every stream is the seed's key folded by the stream's name.
Each stream depends on the seed and its own name only, so adding or reordering streams
leaves the others' keys unchanged, which a split of the seed key into len(streams)
parts does not.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed
|
int
|
A non-negative integer. |
required |
streams
|
Sequence[str]
|
Distinct stream names; at least one. |
('default',)
|
Returns:
| Type | Description |
|---|---|
Rngs
|
The |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If |
split_key(key, num)
¤
Split key into num keys along a new leading axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Array
|
The key to split. |
required |
num
|
int
|
How many keys to return; at least one, so a caller asking for none is a defect. |
required |
Returns:
| Type | Description |
|---|---|
Array
|
Keys of shape |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |