Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

re-enable the 0-arg MersenneTwister() constructor #21909

Merged
merged 1 commit into from
Sep 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
14 changes: 13 additions & 1 deletion base/random/RNGs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not. For MersenneTwister, I introduced it for convenience (but still not necessary), and I thought it could be a nice API (not yet documented): when you want to initialize your RNG, you can default your seed to nothing to have the behavior of non-reproducibility. WIthout nothing, you have to handle the 2 cases:

seed = Nullable{Int}()
...
rng = isnull(seed) ? MersenneTwister() : MersenneTwister(seed)
# vs:
rng = MersenneTwister(get(seed, nothing))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't you splat an empty tuple? we don't often use nothing as an input flag, other than unset keywords

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't you splat an empty tuple? we don't often use nothing as an input flag, other than unset keywords

I guess splatting a tuple would work... but it seams more natural to me to use nothing, and your mention of keyword is helpful: it's easy to have an API like do_computation(...; seed=nothing) and feed the RNG directly with seed without having to splat a tuple. I remember few times having found inconvenient to not be able to give a default value to a seed which could mean "seed randomly". A couple of relevant (kind of) examples which receive a seed as parameter which could benefit from allowing nothing: https://github.com/denizyuret/Knet.jl/pull/105/files and https://github.com/JuliaGraphs/LightGraphs.jl/pull/579/files

srand(rng::RandomDevice) = rng

### generation of floats

@inline rand(r::RandomDevice, I::FloatInterval) = rand_generic(r, I)
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
28 changes: 22 additions & 6 deletions base/random/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
34 changes: 31 additions & 3 deletions test/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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