Skip to content

Commit e48d05f

Browse files
committed
Random: allow string seeds
We used to be able to seed RNGs with a string, but that string was interpreted as the filename containing the seed. This was deprecated in #21359, in order to allow later using a string seed directly, which this patch does.
1 parent d988f8f commit e48d05f

File tree

4 files changed

+28
-6
lines changed

4 files changed

+28
-6
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Standard library changes
4949

5050
#### Random
5151
* `rand` now supports sampling over `Tuple` types ([#50251]).
52+
* Most random number generators from `Random` can now be seeded by a string, e.g.
53+
`seed!(rng, "a random seed")` ([#51527]).
5254

5355
* When seeding RNGs provided by `Random`, negative integer seeds can now be used ([#51416]).
5456

stdlib/Random/src/RNGs.jl

+12-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ MersenneTwister(seed, state::DSFMT_state) =
8383
Create a `MersenneTwister` RNG object. Different RNG objects can have
8484
their own seeds, which may be useful for generating different streams
8585
of random numbers.
86-
The `seed` may be an integer or a vector of `UInt32` integers.
86+
The `seed` may be an integer, a string, or a vector of `UInt32` integers.
8787
If no seed is provided, a randomly generated one is created (using entropy from the system).
8888
See the [`seed!`](@ref) function for reseeding an already existing `MersenneTwister` object.
8989
@@ -316,12 +316,22 @@ function hash_seed(seed::Union{AbstractArray{UInt32}, AbstractArray{UInt64}})
316316
SHA.digest!(ctx)
317317
end
318318

319+
function hash_seed(str::AbstractString)
320+
ctx = SHA.SHA2_256_CTX()
321+
for chr in str
322+
SHA.update!(ctx, reinterpret(NTuple{4, UInt8}, UInt32(chr)))
323+
end
324+
SHA.update!(ctx, (0x05,))
325+
SHA.digest!(ctx)
326+
end
327+
319328

320329
"""
321330
hash_seed(seed) -> AbstractVector{UInt8}
322331
323332
Return a cryptographic hash of `seed` of size 256 bits (32 bytes).
324-
`seed` can currently be of type `Union{Integer, DenseArray{UInt32}, DenseArray{UInt64}}`,
333+
`seed` can currently be of type
334+
`Union{Integer, AbstractString, AbstractArray{UInt32}, AbstractArray{UInt64}}`,
325335
but modules can extend this function for types they own.
326336
327337
`hash_seed` is "injective" : if `n != m`, then `hash_seed(n) != `hash_seed(m)`.

stdlib/Random/src/Xoshiro.jl

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Lots of implementation is shared with TaskLocalRNG
55

66
"""
7-
Xoshiro(seed::Integer)
7+
Xoshiro(seed::Union{Integer, AbstractString})
88
Xoshiro()
99
1010
Xoshiro256++ is a fast pseudorandom number generator described by David Blackman and
@@ -256,7 +256,6 @@ end
256256
seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed) =
257257
initstate!(rng, reinterpret(UInt64, hash_seed(seed)))
258258

259-
260259
@inline function rand(x::Union{TaskLocalRNG, Xoshiro}, ::SamplerType{UInt64})
261260
s0, s1, s2, s3 = getstate(x)
262261
tmp = s0 + s3

stdlib/Random/test/runtests.jl

+13-2
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ end
647647
# test that the following is not an error (#16925)
648648
@test Random.seed!(m..., typemax(UInt)) === m2
649649
@test Random.seed!(m..., typemax(UInt128)) === m2
650+
@test Random.seed!(m..., "a random seed") === m2
650651
end
651652
end
652653

@@ -701,7 +702,7 @@ end
701702
end
702703

703704
@testset "$RNG(seed) & Random.seed!(m::$RNG, seed) produce the same stream" for RNG=(MersenneTwister,Xoshiro)
704-
seeds = Any[0, 1, 2, 10000, 10001, rand(UInt32, 8), rand(UInt128, 3)...]
705+
seeds = Any[0, 1, 2, 10000, 10001, rand(UInt32, 8), randstring(), randstring(), rand(UInt128, 3)...]
705706
if RNG == Xoshiro
706707
push!(seeds, rand(UInt64, rand(1:4)))
707708
end
@@ -714,7 +715,7 @@ end
714715
end
715716

716717
@testset "Random.seed!(seed) sets Random.GLOBAL_SEED" begin
717-
seeds = Any[0, rand(UInt128), rand(UInt64, 4)]
718+
seeds = Any[0, rand(UInt128), rand(UInt64, 4), randstring(20)]
718719

719720
for seed=seeds
720721
Random.seed!(seed)
@@ -923,6 +924,12 @@ end
923924
@test string(m) == "MersenneTwister(-3)"
924925
Random.seed!(m, typemin(Int8))
925926
@test string(m) == "MersenneTwister(-128)"
927+
928+
# string seeds
929+
Random.seed!(m, "seed 1")
930+
x = rand(m)
931+
@test string(m) == "MersenneTwister(\"seed 1\")"
932+
@test x == rand(MersenneTwister("seed 1"))
926933
end
927934

928935
@testset "RandomDevice" begin
@@ -1179,4 +1186,8 @@ end
11791186
hash32 = Random.hash_seed(seed32)
11801187
@test Random.hash_seed(map(UInt64, seed32)) == hash32
11811188
@test hash32 keys(vseeds)
1189+
1190+
seed_str = randstring()
1191+
seed_gstr = GenericString(seed_str)
1192+
@test Random.hash_seed(seed_str) == Random.hash_seed(seed_gstr)
11821193
end

0 commit comments

Comments
 (0)