Randomness and reproducibility#
Most stochastic functions in XGI accept a seed argument so that results can
be made reproducible. This page documents what to pass and what to expect.
Accepted types for seed#
XGI accepts three kinds of values:
None(default): a fresh, non-deterministic random state is used.int: a deterministic seed. Each call seeded with the same integer produces identical output.numpy.random.Generator: the modern NumPy random API. TheGeneratorcarries state and advances it as randomness is consumed.
This mirrors the conventions used by NumPy, SciPy, and scikit-learn.
Semantics#
The four cases worth pinning down explicitly:
Pattern |
Result |
|---|---|
Same |
Identical outputs |
Single |
Different outputs (state advances) |
Two fresh |
Identical outputs |
|
Different outputs (the conversion paths are unrelated) |
The last case sometimes surprises users: passing 42 and passing a
Generator constructed from 42 are not equivalent. The Generator has
its own internal state derived from the seed via a SeedSequence, and reading
from it produces a different first integer than 42 itself.
Examples#
Reusing a single Generator — state advances:
import xgi
import numpy as np
H = xgi.random_hypergraph(20, [0.15, 0.01], seed=42)
rng = np.random.default_rng(42)
pos_a = xgi.barycenter_spring_layout(H, seed=rng)
pos_b = xgi.barycenter_spring_layout(H, seed=rng)
# pos_a != pos_b — rng advanced between calls
Fresh Generator from the same seed — identical outputs:
pos_a = xgi.barycenter_spring_layout(H, seed=np.random.default_rng(42))
pos_b = xgi.barycenter_spring_layout(H, seed=np.random.default_rng(42))
# pos_a == pos_b
Same int seed reused — identical outputs:
pos_a = xgi.barycenter_spring_layout(H, seed=42)
pos_b = xgi.barycenter_spring_layout(H, seed=42)
# pos_a == pos_b
Why use a Generator#
Passing a single Generator to several calls is useful when you want each
call to draw from a different part of the same random stream. This is the
recommended pattern for any pipeline that performs multiple stochastic steps:
the whole pipeline becomes reproducible from a single integer seed, but each
step still gets independent randomness.
rng = np.random.default_rng(42)
H = xgi.random_hypergraph(20, [0.15, 0.01], seed=rng)
pos = xgi.barycenter_spring_layout(H, seed=rng)
swapped = xgi.node_swap(H, 1, 2) # purely deterministic, no seed needed
# everything above is reproducible by re-running with the same starting seed
NumPy reference#
For deeper background on NumPy’s modern random API, see the upstream documentation: