From 81b839d2cfc6218c0434e30e7a26bd4c4186e7cc Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sun, 14 May 2017 12:25:03 +0200 Subject: [PATCH] re-enable the 0-arg MersenneTwister() constructor The arguments to `srand(rng, ...)` and `typeof(rng)(...)` should mirror each-other, in particular `srand(MersenneTwister(0))` and `MersenneTwister()` should produce an equivalent object. Also, `srand(::RandomDevice)` has been added for consistency, which could be useful in generic code. --- base/deprecated.jl | 3 --- base/random/RNGs.jl | 14 +++++++++++++- base/random/random.jl | 28 ++++++++++++++++++++++------ test/random.jl | 34 +++++++++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 2dc1cec006b6f..8aaeeaa5c852f 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1267,9 +1267,6 @@ end end end -# PR #16984 -@deprecate MersenneTwister() MersenneTwister(0) - # #19635 for fname in (:ones, :zeros) @eval @deprecate ($fname)(T::Type, arr) ($fname)(T, size(arr)) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index 6a12e3877f814..ae5c4d32d9ce5 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -40,9 +40,13 @@ end # os-test Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. +The entropy is obtained from the operating system. """ RandomDevice +RandomDevice(::Void) = RandomDevice() +srand(rng::RandomDevice) = rng + ### generation of floats @inline rand(r::RandomDevice, I::FloatInterval) = rand_generic(r, I) @@ -71,10 +75,17 @@ MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = """ MersenneTwister(seed) + MersenneTwister() Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which may be useful for generating different streams of random numbers. +The `seed` may be a non-negative integer or a vector of +`UInt32` integers. If no seed is provided, a randomly generated one +is created (using entropy from the system). +See the [`srand`](@ref) function for reseeding an already existing +`MersenneTwister` object. + # Examples ```jldoctest @@ -96,7 +107,8 @@ julia> x1 == x2 true ``` """ -MersenneTwister(seed) = srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) +MersenneTwister(seed=nothing) = + srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) function copy!(dst::MersenneTwister, src::MersenneTwister) copy!(resize!(dst.seed, length(src.seed)), src.seed) diff --git a/base/random/random.jl b/base/random/random.jl index 2535443f9a8d4..9d3cc99bc69fe 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -117,13 +117,14 @@ rand! srand([rng=GLOBAL_RNG], seed) -> rng srand([rng=GLOBAL_RNG]) -> rng -Reseed the random number generator. If a `seed` is provided, the RNG will give a -reproducible sequence of numbers, otherwise Julia will get entropy from the system. For -`MersenneTwister`, the `seed` may be a non-negative integer or a vector of [`UInt32`](@ref) -integers. `RandomDevice` does not support seeding. +Reseed the random number generator: `rng` will give a reproducible +sequence of numbers if and only if a `seed` is provided. Some RNGs +don't accept a seed, like `RandomDevice`. +After the call to `srand`, `rng` is equivalent to a newly created +object initialized with the same seed. # Examples -```jldoctest +```julia-repl julia> srand(1234); julia> x1 = rand(2) @@ -140,8 +141,23 @@ julia> x2 = rand(2) julia> x1 == x2 true + +julia> rng = MersenneTwister(1234); rand(rng, 2) == x1 +true + +julia> MersenneTwister(1) == srand(rng, 1) +true + +julia> rand(srand(rng), Bool) # not reproducible +true + +julia> rand(srand(rng), Bool) +false + +julia> rand(MersenneTwister(), Bool) # not reproducible either +true ``` """ -srand +srand(rng::AbstractRNG, ::Void) = srand(rng) end # module diff --git a/test/random.jl b/test/random.jl index 07923ac39131c..87382784c2ce9 100644 --- a/test/random.jl +++ b/test/random.jl @@ -38,8 +38,7 @@ A = zeros(UInt128, 2, 2) @test_throws BoundsError rand!(MersenneTwister(0), A, 5) # rand from AbstractArray -let mt = MersenneTwister(0) - srand(mt) +let mt = MersenneTwister() @test rand(mt, 0:3:1000) in 0:3:1000 @test issubset(rand!(mt, Array{Int}(100), 0:3:1000), 0:3:1000) coll = Any[2, UInt128(128), big(619), "string"] @@ -424,7 +423,7 @@ function hist(X, n) end # test uniform distribution of floats -for rng in [srand(MersenneTwister(0)), RandomDevice()], +for rng in [MersenneTwister(), RandomDevice()], T in [Float16, Float32, Float64, BigFloat], prec in (T == BigFloat ? [3, 53, 64, 100, 256, 1000] : [256]) setprecision(BigFloat, prec) do @@ -589,3 +588,32 @@ end # this shouldn't crash (#22403) @test_throws MethodError rand!(Union{UInt,Int}[1, 2, 3]) + +@testset "$RNG() & srand(rng::$RNG) initializes randomly" for RNG in (MersenneTwister, RandomDevice) + m = RNG() + a = rand(m, Int) + m = RNG() + @test rand(m, Int) != a + # passing `nothing` is equivalent to passing nothing + m = RNG(nothing) + b = rand(m, Int) + @test b != a + srand(m) + c = rand(m, Int) + @test c ∉ (a, b) + srand(m) + @test rand(m, Int) ∉ (a, b, c) + srand(m, nothing) + d = rand(m, Int) + @test d ∉ (a, b, c) + srand(m, nothing) + @test rand(m, Int) ∉ (a, b, c, d) +end + +@testset "MersenneTwister($seed_) & srand(m::MersenneTwister, $seed_) produce the same stream" for seed_ in [0:5; 10000:10005] + # "seed_" instead of "seed" because `seed` is a global variable in this file, and there is an "overwriting" warning + m = MersenneTwister(seed_) + a = [rand(m) for _=1:100] + srand(m, seed_) + @test a == [rand(m) for _=1:100] +end