Skip to content

Commit

Permalink
[WIP] Xoshiro: allow any non-negative integer as a seed, via SHA2_256
Browse files Browse the repository at this point in the history
[ci skip]
  • Loading branch information
rfourquet committed Jul 12, 2021
1 parent e3a06c4 commit 6177901
Show file tree
Hide file tree
Showing 3 changed files with 9 additions and 42 deletions.
1 change: 1 addition & 0 deletions stdlib/Random/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[deps]
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
1 change: 1 addition & 0 deletions stdlib/Random/src/Random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include("DSFMT.jl")
using .DSFMT
using Base.GMP.MPZ
using Base.GMP: Limb
import SHA

using Base: BitInteger, BitInteger_types, BitUnsigned, require_one_based_indexing

Expand Down
49 changes: 7 additions & 42 deletions stdlib/Random/src/Xoshiro.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,65 +117,30 @@ end

# Shared implementation between Xoshiro and TaskLocalRNG -- seeding

function seed!(x::Union{TaskLocalRNG,Xoshiro})
function seed!(rng::Union{TaskLocalRNG,Xoshiro})
# as we get good randomness from RandomDevice, we can skip hashing
parent = RandomDevice()
# Constants have nothing up their sleeve, see task.c
# 0x02011ce34bce797f == hash(UInt(1))|0x01
# 0x5a94851fb48a6e05 == hash(UInt(2))|0x01
# 0x3688cf5d48899fa7 == hash(UInt(3))|0x01
# 0x867b4bb4c42e5661 == hash(UInt(4))|0x01
setstate!(x,
setstate!(rng,
0x02011ce34bce797f * rand(parent, UInt64),
0x5a94851fb48a6e05 * rand(parent, UInt64),
0x3688cf5d48899fa7 * rand(parent, UInt64),
0x867b4bb4c42e5661 * rand(parent, UInt64))
end

function seed!(rng::Union{TaskLocalRNG,Xoshiro}, seed::NTuple{4,UInt64})
# TODO: Consider a less ad-hoc construction
# We can afford burning a handful of cycles here, and we don't want any
# surprises with respect to bad seeds / bad interactions.

s0 = s = Base.hash_64_64(seed[1])
s1 = s += Base.hash_64_64(seed[2])
s2 = s += Base.hash_64_64(seed[3])
s3 = s += Base.hash_64_64(seed[4])

function seed!(rng::Union{TaskLocalRNG,Xoshiro}, seed::Vector{UInt32})
c = SHA.SHA2_256_CTX()
SHA.update!(c, reinterpret(UInt8, seed))
s0, s1, s2, s3 = reinterpret(UInt64, SHA.digest!(c))
setstate!(rng, s0, s1, s2, s3)

rand(rng, UInt64)
rand(rng, UInt64)
rand(rng, UInt64)
rand(rng, UInt64)
rng
end

function seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::UInt128)
seed0 = seed % UInt64
seed1 = (seed>>>64) % UInt64
seed!(rng, (seed0, seed1, zero(UInt64), zero(UInt64)))
end
seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::Integer) = seed!(rng, UInt128(seed))

function seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::AbstractVector{UInt64})
if length(seed) > 4
throw(ArgumentError("seed should have no more than 256 bits"))
end
seed0 = length(seed)>0 ? seed[1] : UInt64(0)
seed1 = length(seed)>1 ? seed[2] : UInt64(0)
seed2 = length(seed)>2 ? seed[3] : UInt64(0)
seed3 = length(seed)>3 ? seed[4] : UInt64(0)
seed!(rng, (seed0, seed1, seed2, seed3))
end
seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::Integer) = seed!(rng, make_seed(seed))

function seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::AbstractVector{UInt32})
if iseven(length(seed))
seed!(rng, reinterpret(UInt64, seed))
else
seed!(rng, UInt64[reinterpret(UInt64, @view(seed[begin:end-1])); seed[end] % UInt64])
end
end

@inline function rand(rng::Union{TaskLocalRNG, Xoshiro}, ::SamplerType{UInt128})
first = rand(rng, UInt64)
Expand Down

0 comments on commit 6177901

Please sign in to comment.