From 55a7bea619ee1ed82c10c7b95c0763eeed215d24 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 19 May 2017 12:17:16 +0200 Subject: [PATCH 1/3] add rand(::IntSet) --- base/random.jl | 16 +++++++++++++++- test/random.jl | 7 ++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/base/random.jl b/base/random.jl index 847c61e2f9a1f..6357acccc8b2d 100644 --- a/base/random.jl +++ b/base/random.jl @@ -339,7 +339,7 @@ function rand(r::AbstractRNG, ::Type{Char}) (c < 0xd800) ? Char(c) : Char(c+0x800) end -# random values from Dict or Set (for efficiency) +# random values from Dict, Set, IntSet (for efficiency) function rand(r::AbstractRNG, t::Dict) isempty(t) && throw(ArgumentError("dict must be non-empty")) n = length(t.slots) @@ -352,6 +352,20 @@ rand(t::Dict) = rand(GLOBAL_RNG, t) rand(r::AbstractRNG, s::Set) = rand(r, s.dict).first rand(s::Set) = rand(GLOBAL_RNG, s) +function rand(r::AbstractRNG, s::IntSet) + isempty(s) && throw(ArgumentError("collection must be non-empty")) + # s can be empty while s.bits is not, so we cannot rely on the + # length check in RangeGenerator below + rg = RangeGenerator(1:length(s.bits)) + while true + n = rand(r, rg) + @inbounds b = s.bits[n] + b && return n + end +end + +rand(s::IntSet) = rand(GLOBAL_RNG, s) + ## Arrays of random numbers rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) diff --git a/test/random.jl b/test/random.jl index 0d54f0a426cc4..8a1a918167caf 100644 --- a/test/random.jl +++ b/test/random.jl @@ -53,11 +53,16 @@ let mt = MersenneTwister(0) rand(coll, 2, 3) end -# rand using Dict, Set +# rand using Dict, Set, IntSet adict = Dict(1=>2, 3=>4, 5=>6) @test rand(adict) in adict +@test_throws ArgumentError rand(Dict()) aset = Set(1:10) @test rand(aset) in aset +@test_throws ArgumentError rand(Set()) +anintset = IntSet(1:10) +@test rand(anintset) in anintset +@test_throws ArgumentError rand(IntSet()) # randn @test randn(MersenneTwister(42)) == -0.5560268761463861 From 97702900e19c65646d0b3a40cfc251e49ec5f410 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Fri, 19 May 2017 12:29:35 +0200 Subject: [PATCH 2/3] optimize rand(::Dict) And change "dict" -> "collection" in the exception, as it is also used from rand(::Set). --- base/random.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/random.jl b/base/random.jl index 6357acccc8b2d..3c63686376152 100644 --- a/base/random.jl +++ b/base/random.jl @@ -341,11 +341,11 @@ end # random values from Dict, Set, IntSet (for efficiency) function rand(r::AbstractRNG, t::Dict) - isempty(t) && throw(ArgumentError("dict must be non-empty")) - n = length(t.slots) + isempty(t) && throw(ArgumentError("collection must be non-empty")) + rg = RangeGenerator(1:length(t.slots)) while true - i = rand(r, 1:n) - Base.isslotfilled(t, i) && return (t.keys[i] => t.vals[i]) + i = rand(r, rg) + Base.isslotfilled(t, i) && @inbounds return (t.keys[i] => t.vals[i]) end end rand(t::Dict) = rand(GLOBAL_RNG, t) From 99f207d3a6701bba422ac23beccf4aa99f44c923 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Sat, 20 May 2017 16:08:39 +0200 Subject: [PATCH 3/3] complete the rand API with Dict, Set, IntSet --- base/random.jl | 29 ++++++++++++++++++++++++++++- test/random.jl | 30 +++++++++++++++++++----------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/base/random.jl b/base/random.jl index 3c63686376152..7d15e802b678a 100644 --- a/base/random.jl +++ b/base/random.jl @@ -257,11 +257,22 @@ globalRNG() = GLOBAL_RNG Pick a random element or array of random elements from the set of values specified by `S`; `S` can be * an indexable collection (for example `1:n` or `['x','y','z']`), or +* a `Dict`, a `Set` or an `IntSet`, or * a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for integers (this is not applicable to [`BigInt`](@ref)), and to ``[0, 1)`` for floating point numbers; `S` defaults to [`Float64`](@ref). + +```julia-repl +julia> rand(Int, 2) +2-element Array{Int64,1}: + 1339893410598768192 + 1575814717733606317 + +julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) +1=>2 +``` """ @inline rand() = rand(GLOBAL_RNG, CloseOpen) @inline rand(T::Type) = rand(GLOBAL_RNG, T) @@ -276,7 +287,7 @@ rand(r::AbstractArray) = rand(GLOBAL_RNG, r) """ rand!([rng=GLOBAL_RNG], A, [coll]) -Populate the array `A` with random values. If the indexable collection `coll` is specified, +Populate the array `A` with random values. If the collection `coll` is specified, the values are picked randomly from `coll`. This is equivalent to `copy!(A, rand(rng, coll, size(A)))` or `copy!(A, rand(rng, eltype(A), size(A)))` but without allocating a new array. """ @@ -384,6 +395,22 @@ function rand!{T}(r::AbstractRNG, A::AbstractArray{T}) A end +function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) + for i in eachindex(A) + @inbounds A[i] = rand(r, s) + end + A +end + +rand!(A::AbstractArray, s::Union{Dict,Set,IntSet}) = rand!(GLOBAL_RNG, A, s) + +rand(r::AbstractRNG, s::Dict{K,V}, dims::Dims) where {K,V} = rand!(r, Array{Pair{K,V}}(dims), s) +rand(r::AbstractRNG, s::Set{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) +rand(r::AbstractRNG, s::IntSet, dims::Dims) = rand!(r, Array{Int}(dims), s) +rand(r::AbstractRNG, s::Union{Dict,Set,IntSet}, dims::Integer...) = rand(r, s, convert(Dims, dims)) +rand(s::Union{Dict,Set,IntSet}, dims::Integer...) = rand(GLOBAL_RNG, s, dims) +rand(s::Union{Dict,Set,IntSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) + # MersenneTwister function rand_AbstractArray_Float64!{I<:FloatInterval}(r::MersenneTwister, A::AbstractArray{Float64}, n=length(A), ::Type{I}=CloseOpen) diff --git a/test/random.jl b/test/random.jl index 8a1a918167caf..79bbc7ecb2126 100644 --- a/test/random.jl +++ b/test/random.jl @@ -53,17 +53,6 @@ let mt = MersenneTwister(0) rand(coll, 2, 3) end -# rand using Dict, Set, IntSet -adict = Dict(1=>2, 3=>4, 5=>6) -@test rand(adict) in adict -@test_throws ArgumentError rand(Dict()) -aset = Set(1:10) -@test rand(aset) in aset -@test_throws ArgumentError rand(Set()) -anintset = IntSet(1:10) -@test rand(anintset) in anintset -@test_throws ArgumentError rand(IntSet()) - # randn @test randn(MersenneTwister(42)) == -0.5560268761463861 A = zeros(2, 2) @@ -320,6 +309,11 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) ftypes = [Float16, Float32, Float64] cftypes = [Complex32, Complex64, Complex128, ftypes...] types = [Bool, Char, Base.BitInteger_types..., ftypes...] + collections = [(IntSet(rand(1:100, 20)), Int), + (Set(rand(Int, 20)), Int), + (Dict(zip(rand(Int,10), rand(Int, 10))), Pair{Int,Int}), + (1:100, Int), + (rand(Int, 100), Int)] b2 = big(2) u3 = UInt(3) for f in [rand, randn, randexp] @@ -341,6 +335,20 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) end end end + for (C, T) in collections + a0 = rand(rng..., C) ::T + a1 = rand(rng..., C, 5) ::Vector{T} + a2 = rand(rng..., C, 2, 3) ::Array{T, 2} + a3 = rand!(rng..., Array{T}(5), C) ::Vector{T} + a4 = rand!(rng..., Array{T}(2, 3), C) ::Array{T, 2} + for a in [a0, a1..., a2..., a3..., a4...] + @test a in C + end + end + for C in [1:0, Dict(), Set(), IntSet(), Int[]] + @test_throws ArgumentError rand(rng..., C) + @test_throws ArgumentError rand(rng..., C, 5) + end for f! in [rand!, randn!, randexp!] for T in (f! === rand! ? types : f! === randn! ? cftypes : ftypes) X = T == Bool ? T[0,1] : T[0,1,2]