Overview
SPEC 7 is an endorsed Scientific Python Ecosystem Coordination document standardizing how libraries handle random number generation seeding.
SPEC 7: Seeding Pseudo-Random Number Generation
Status: Endorsed | Full Text
Adopted By: NumPy, SciPy, scikit-image, NetworkX, IPython
The Problem
Libraries across the scientific Python ecosystem use inconsistent APIs for controlling random number generation:
- Different keyword arguments (
random_state, seed, rng)
- Reliance on global state via
numpy.random.seed
- Use of legacy
RandomState objects instead of modern Generator
Recommended Approach
- Standardize on
rng keyword: Replace random_state/seed with consistent rng parameter
- Use
numpy.random.default_rng: Normalize input and instantiate proper Generator object
- Eliminate global seeding: Deprecate reliance on
numpy.random.seed
Accepted rng Values
| Input Type |
Behavior |
None |
Create new Generator with fresh entropy |
int |
Create Generator seeded with integer |
Generator |
Use directly |
BitGenerator |
Wrap in Generator |
SeedSequence |
Create Generator from sequence |
SeedSequence Benefits
Avoids "naïve seeding" problems:
# BAD: Successive integers can produce correlated streams
rng1 = np.random.default_rng(0)
rng2 = np.random.default_rng(1)
# GOOD: SeedSequence with spawn
ss = np.random.SeedSequence(42)
child_seeds = ss.spawn(10)
rngs = [np.random.default_rng(s) for s in child_seeds]
Deprecation Pattern
def my_function(..., rng=None, random_state='deprecated'):
if random_state != 'deprecated':
if rng is not None:
raise ValueError("Cannot specify both rng and random_state")
warnings.warn("random_state is deprecated, use rng", DeprecationWarning)
rng = random_state
rng = np.random.default_rng(rng)
# ... use rng
Suggested Implementation for NumSharp
Current State
NumSharp's np.random module uses global state pattern:
np.random.seed(42);
var x = np.random.random();
Phase 1: Support Generator Pattern
public class Generator {
private readonly BitGenerator _bitgen;
public Generator(BitGenerator bitgen) {
_bitgen = bitgen;
}
public double Random() => _bitgen.NextDouble();
public NDArray Random(Shape shape) => /* ... */;
}
// Factory function
public static Generator default_rng(object seed = null) {
return seed switch {
null => new Generator(new PCG64()),
int i => new Generator(new PCG64(i)),
Generator g => g,
BitGenerator bg => new Generator(bg),
_ => throw new ArgumentException()
};
}
Phase 2: Add rng Parameter to Functions
// Pattern for functions accepting randomness
public static NDArray RandomFunction(
/* params */,
object rng = null)
{
var generator = rng switch {
null => np.random.default_rng(),
int seed => np.random.default_rng(seed),
Generator g => g,
_ => throw new ArgumentException("Invalid rng")
};
// Use generator...
}
Phase 3: BitGenerator Infrastructure
public abstract class BitGenerator {
public abstract ulong NextUInt64();
public abstract double NextDouble();
public abstract void Seed(ulong seed);
}
public class MT19937 : BitGenerator { /* Mersenne Twister */ }
public class PCG64 : BitGenerator { /* PCG family - new default */ }
public class Philox : BitGenerator { /* Counter-based */ }
public class SFC64 : BitGenerator { /* Small Fast Chaotic */ }
Phase 4: SeedSequence Support
public class SeedSequence {
private readonly ulong[] _entropy;
public SeedSequence(int seed) { /* ... */ }
public SeedSequence[] Spawn(int n) {
// Generate independent child sequences
}
public ulong[] Generate(int n) {
// Generate seed material
}
}
Considerations
- Backward Compatibility: Keep
np.random.seed() working but consider deprecation warning
- 1-to-1 Matching: NumSharp claims seed matching with NumPy - verify this uses MT19937
- New Default: NumPy's new default is PCG64, not MT19937
- Parallel Seeding: SeedSequence enables safe parallel RNG streams
Documentation
See docs/neps/NEP19.md for related NumPy RNG policy.
Related Issues
Overview
SPEC 7 is an endorsed Scientific Python Ecosystem Coordination document standardizing how libraries handle random number generation seeding.
SPEC 7: Seeding Pseudo-Random Number Generation
Status: Endorsed | Full Text
Adopted By: NumPy, SciPy, scikit-image, NetworkX, IPython
The Problem
Libraries across the scientific Python ecosystem use inconsistent APIs for controlling random number generation:
random_state,seed,rng)numpy.random.seedRandomStateobjects instead of modernGeneratorRecommended Approach
rngkeyword: Replacerandom_state/seedwith consistentrngparameternumpy.random.default_rng: Normalize input and instantiate properGeneratorobjectnumpy.random.seedAccepted
rngValuesNoneintGeneratorBitGeneratorSeedSequenceSeedSequence Benefits
Avoids "naïve seeding" problems:
Deprecation Pattern
Suggested Implementation for NumSharp
Current State
NumSharp's
np.randommodule uses global state pattern:Phase 1: Support Generator Pattern
Phase 2: Add
rngParameter to FunctionsPhase 3: BitGenerator Infrastructure
Phase 4: SeedSequence Support
Considerations
np.random.seed()working but consider deprecation warningDocumentation
See
docs/neps/NEP19.mdfor related NumPy RNG policy.Related Issues