Skip to content

Commit f2e223f

Browse files
committed
Random: allow seeding from an RNG
Seeding from an RNG is the most flexible way: the caller doesn't need to know anything about the internals of the fed RNG, which can request all the entropy it needs from the parent PRG in whatever form it needs.
1 parent b680b4e commit f2e223f

File tree

4 files changed

+26
-16
lines changed

4 files changed

+26
-16
lines changed

stdlib/Random/src/RNGs.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,13 @@ function initstate!(r::MersenneTwister, data::StridedVector, seed)
365365
end
366366

367367
# When a seed is not provided, we generate one via `RandomDevice()` rather
368-
# than calling directly `initstate!` with `rand(RandomDevice(), UInt32, whatever)` because the
368+
# than calling directly `initstate!` with `rand(RandomDevice(), UInt32, 8)` because the
369369
# seed is printed in `show(::MersenneTwister)`, so we need one; the cost of `hash_seed` is a
370-
# small overhead compared to `initstate!`, so this simple solution is fine.
371-
# A random seed with 128 bits is a good compromise for almost surely always getting distinct
370+
# small overhead compared to `initstate!`.
371+
# A random seed with 128 bits is a good compromise for almost surely getting distinct
372372
# seeds, while having them printed reasonably tersely.
373-
seed!(r::MersenneTwister, ::Nothing) = seed!(r, rand(RandomDevice(), UInt128))
373+
seed!(r::MersenneTwister, seeder::AbstractRNG) = seed!(r, rand(seeder, UInt128))
374+
seed!(r::MersenneTwister, ::Nothing) = seed!(r, RandomDevice())
374375
seed!(r::MersenneTwister, seed) = initstate!(r, hash_seed(seed), seed)
375376

376377

stdlib/Random/src/Random.jl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,10 @@ sequence of numbers if and only if a `seed` is provided. Some RNGs
414414
don't accept a seed, like `RandomDevice`.
415415
After the call to `seed!`, `rng` is equivalent to a newly created
416416
object initialized with the same seed.
417+
417418
The types of accepted seeds depend on the type of `rng`, but in general,
418-
integer seeds should work.
419+
integer seeds should work. Providing `nothing` as the seed should be
420+
equivalent to not providing one.
419421
420422
If `rng` is not specified, it defaults to seeding the state of the
421423
shared task-local generator.
@@ -455,11 +457,12 @@ julia> rand(Xoshiro(), Bool) # not reproducible either
455457
true
456458
```
457459
"""
458-
seed!(rng::AbstractRNG) = seed!(rng, nothing)
459-
#=
460-
We have this generic definition instead of the alternative option
461-
`seed!(rng::AbstractRNG, ::Nothing) = seed!(rng)`
462-
because it would lead too easily to ambiguities, e.g. when we define `seed!(::Xoshiro, seed)`.
463-
=#
460+
function seed!(rng::AbstractRNG, seed=nothing)
461+
if seed === nothing
462+
seed!(rng, RandomDevice())
463+
else
464+
throw(MethodError(seed!, (rng, seed)))
465+
end
466+
end
464467

465468
end # module

stdlib/Random/src/Xoshiro.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,10 @@ copy!(dst::Union{TaskLocalRNG, Xoshiro}, src::Union{TaskLocalRNG, Xoshiro}) = se
243243
# use a magic (random) number to scramble `h` so that `hash(x)` is distinct from `hash(getstate(x))`
244244
hash(x::Union{TaskLocalRNG, Xoshiro}, h::UInt) = hash(getstate(x), h + 0x49a62c2dda6fa9be % UInt)
245245

246-
function seed!(rng::Union{TaskLocalRNG, Xoshiro}, ::Nothing)
247-
# as we get good randomness from RandomDevice, we can skip hashing
248-
initstate!(rng, rand(RandomDevice(), NTuple{4, UInt64}))
249-
end
246+
seed!(rng::Union{TaskLocalRNG, Xoshiro}, seeder::AbstractRNG) =
247+
initstate!(rng, rand(seeder, NTuple{4, UInt64}))
248+
249+
seed!(rng::Union{TaskLocalRNG, Xoshiro}, ::Nothing) = @invoke seed!(rng::AbstractRNG, nothing::Any)
250250

251251
seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed) =
252252
initstate!(rng, reinterpret(UInt64, hash_seed(seed)))

stdlib/Random/test/runtests.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,7 @@ end
675675
@test Random.seed!(m..., typemax(UInt)) === m2
676676
@test Random.seed!(m..., typemax(UInt128)) === m2
677677
@test Random.seed!(m..., "a random seed") === m2
678+
@test Random.seed!(m..., Random.default_rng()) === m2
678679
end
679680
end
680681

@@ -728,7 +729,7 @@ end
728729
@test rand(m, Int) (a, b, c, d)
729730
end
730731

731-
@testset "$RNG(seed) & Random.seed!(m::$RNG, seed) produce the same stream" for RNG=(MersenneTwister,Xoshiro)
732+
@testset "$RNG(seed) & Random.seed!(m::$RNG, seed) produce the same stream" for RNG=(MersenneTwister, Xoshiro)
732733
seeds = Any[0, 1, 2, 10000, 10001, rand(UInt32, 8), randstring(), randstring(), rand(UInt128, 3)...]
733734
if RNG == Xoshiro
734735
push!(seeds, rand(UInt64, rand(1:4)))
@@ -739,6 +740,11 @@ end
739740
Random.seed!(m, seed)
740741
@test a == [rand(m) for _=1:100]
741742
end
743+
# rng as a seed
744+
m = RNG(Xoshiro(0))
745+
a = [rand(m) for _=1:100]
746+
Random.seed!(m, Xoshiro(0))
747+
@test a == [rand(m) for _=1:100]
742748
end
743749

744750
@testset "Random.seed!(seed) sets Random.GLOBAL_SEED" begin

0 commit comments

Comments
 (0)