diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl index 0274ad640b6ed..b3cf38c4301be 100644 --- a/base/random/RNGs.jl +++ b/base/random/RNGs.jl @@ -214,12 +214,12 @@ rng_native_52(::MersenneTwister) = Float64 #### helper functions # precondition: !mt_empty(r) -rand_inbounds(r::MersenneTwister, ::Close1Open2_64) = mt_pop!(r) -rand_inbounds(r::MersenneTwister, ::CloseOpen_64=CloseOpen()) = - rand_inbounds(r, Close1Open2()) - 1.0 +rand_inbounds(r::MersenneTwister, ::CloseOpen12_64) = mt_pop!(r) +rand_inbounds(r::MersenneTwister, ::CloseOpen01_64=CloseOpen01()) = + rand_inbounds(r, CloseOpen12()) - 1.0 rand_inbounds(r::MersenneTwister, ::UInt52Raw{T}) where {T<:BitInteger} = - reinterpret(UInt64, rand_inbounds(r, Close1Open2())) % T + reinterpret(UInt64, rand_inbounds(r, CloseOpen12())) % T function rand(r::MersenneTwister, x::SamplerTrivial{UInt52Raw{UInt64}}) reserve_1(r) @@ -238,7 +238,7 @@ end #### floats -rand(r::MersenneTwister, sp::SamplerTrivial{Close1Open2_64}) = +rand(r::MersenneTwister, sp::SamplerTrivial{CloseOpen12_64}) = (reserve_1(r); rand_inbounds(r, sp[])) #### integers @@ -323,7 +323,7 @@ function _rand_max383!(r::MersenneTwister, A::UnsafeView{Float64}, I::FloatInter @gc_preserve r unsafe_copyto!(A.ptr+m*sizeof(Float64), pointer(r.vals), n-m) r.idx = n-m end - if I isa CloseOpen + if I isa CloseOpen01 for i=1:n A[i] -= 1.0 end @@ -332,10 +332,10 @@ function _rand_max383!(r::MersenneTwister, A::UnsafeView{Float64}, I::FloatInter end -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::CloseOpen_64) = +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::CloseOpen01_64) = dsfmt_fill_array_close_open!(s, A, n) -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Close1Open2_64) = +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::CloseOpen12_64) = dsfmt_fill_array_close1_open2!(s, A, n) @@ -386,10 +386,10 @@ mask128(u::UInt128, ::Type{Float32}) = (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 for T in (Float16, Float32) - @eval function rand!(r::MersenneTwister, A::Array{$T}, ::SamplerTrivial{Close1Open2{$T}}) + @eval function rand!(r::MersenneTwister, A::Array{$T}, ::SamplerTrivial{CloseOpen12{$T}}) n = length(A) n128 = n * sizeof($T) ÷ 16 - _rand!(r, A, 2*n128, Close1Open2()) + _rand!(r, A, 2*n128, CloseOpen12()) @gc_preserve A begin A128 = UnsafeView{UInt128}(pointer(A), n128) for i in 1:n128 @@ -414,8 +414,8 @@ for T in (Float16, Float32) A end - @eval function rand!(r::MersenneTwister, A::Array{$T}, ::SamplerTrivial{CloseOpen{$T}}) - rand!(r, A, Close1Open2($T)) + @eval function rand!(r::MersenneTwister, A::Array{$T}, ::SamplerTrivial{CloseOpen01{$T}}) + rand!(r, A, CloseOpen12($T)) I32 = one(Float32) for i in eachindex(A) @inbounds A[i] = Float32(A[i])-I32 # faster than "A[i] -= one(T)" for T==Float16 @@ -430,7 +430,7 @@ function rand!(r::MersenneTwister, A::UnsafeView{UInt128}, ::SamplerType{UInt128 n::Int=length(A) i = n while true - rand!(r, UnsafeView{Float64}(A.ptr, 2i), Close1Open2()) + rand!(r, UnsafeView{Float64}(A.ptr, 2i), CloseOpen12()) n < 5 && break i = 0 while n-i >= 5 diff --git a/base/random/containers.jl b/base/random/containers.jl new file mode 100644 index 0000000000000..7de2cb4e97e5f --- /dev/null +++ b/base/random/containers.jl @@ -0,0 +1,159 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# machinery for generation with Sampler + +# This describes how to generate random scalars or arrays, by generating a Sampler +# and calling rand on it (which should be defined in "generation.jl"). + + +## scalars + +rand(rng::AbstractRNG, X) = rand(rng, Sampler(rng, X, Val(1))) +rand(rng::AbstractRNG=GLOBAL_RNG, ::Type{X}=Float64) where {X} = + rand(rng, Sampler(rng, X, Val(1))) + +rand(X) = rand(GLOBAL_RNG, X) +rand(::Type{X}) where {X} = rand(GLOBAL_RNG, X) + +## arrays + +rand!(A::AbstractArray{T}, X) where {T} = rand!(GLOBAL_RNG, A, X) +rand!(A::AbstractArray{T}, ::Type{X}=T) where {T,X} = rand!(GLOBAL_RNG, A, X) + +rand!(rng::AbstractRNG, A::AbstractArray{T}, X) where {T} = rand!(rng, A, Sampler(rng, X)) +rand!(rng::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} = rand!(rng, A, Sampler(rng, X)) + +function rand!(rng::AbstractRNG, A::AbstractArray{T}, sp::Sampler) where T + for i in eachindex(A) + @inbounds A[i] = rand(rng, sp) + end + A +end + +rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) +rand( dims::Dims) = rand(GLOBAL_RNG, dims) +rand(r::AbstractRNG, dims::Integer...) = rand(r, Dims(dims)) +rand( dims::Integer...) = rand(Dims(dims)) + +rand(r::AbstractRNG, X, dims::Dims) = rand!(r, Array{eltype(X)}(uninitialized, dims), X) +rand( X, dims::Dims) = rand(GLOBAL_RNG, X, dims) + +rand(r::AbstractRNG, X, d::Integer, dims::Integer...) = rand(r, X, Dims((d, dims...))) +rand( X, d::Integer, dims::Integer...) = rand(X, Dims((d, dims...))) +# note: the above methods would trigger an ambiguity warning if d was not separated out: +# rand(r, ()) would match both this method and rand(r, dims::Dims) +# moreover, a call like rand(r, NotImplementedType()) would be an infinite loop + +rand(r::AbstractRNG, ::Type{X}, dims::Dims) where {X} = rand!(r, Array{eltype(X)}(uninitialized, dims), X) +rand( ::Type{X}, dims::Dims) where {X} = rand(GLOBAL_RNG, X, dims) + +rand(r::AbstractRNG, ::Type{X}, d::Integer, dims::Integer...) where {X} = rand(r, X, Dims((d, dims...))) +rand( ::Type{X}, d::Integer, dims::Integer...) where {X} = rand(X, Dims((d, dims...))) + + +## dicts + +rand!(A::AbstractDict{K,V}, dist::Distribution{<:Pair}=Combine(Pair, K, V)) where {K,V} = + rand!(GLOBAL_RNG, A, dist) + +rand!(rng::AbstractRNG, A::AbstractDict{K,V}, + dist::Distribution{<:Pair}=Combine(Pair, K, V)) where {K,V} = + rand!(GLOBAL_RNG, A, Sampler(rng, dist)) + +function _rand!(rng::AbstractRNG, A::Union{AbstractDict,AbstractSet}, n::Integer, sp::Sampler) + empty!(A) + while length(A) < n + push!(A, rand(rng, sp)) + end + A +end + +rand!(rng::AbstractRNG, A::AbstractDict{K,V}, sp::Sampler) where {K,V} = _rand!(rng, A, length(A), sp) + +rand(rng::AbstractRNG, dist::Distribution{<:Pair}, ::Type{T}, n::Integer) where {T<:AbstractDict} = + _rand!(rng, deduce_type(T, eltype(dist).parameters...)(), n, Sampler(rng, dist)) + +rand(u::Distribution{<:Pair}, ::Type{T}, n::Integer) where {T<:AbstractDict} = rand(GLOBAL_RNG, u, T, n) + + +## sets + +rand!(A::AbstractSet{T}, X) where {T} = rand!(GLOBAL_RNG, A, X) +rand!(A::AbstractSet{T}, ::Type{X}=T) where {T,X} = rand!(GLOBAL_RNG, A, X) + +rand!(rng::AbstractRNG, A::AbstractSet, X) = rand!(rng, A, Sampler(rng, X)) +rand!(rng::AbstractRNG, A::AbstractSet{T}, ::Type{X}=T) where {T,X} = rand!(rng, A, Sampler(rng, X)) + +_rand0!(rng::AbstractRNG, A::AbstractSet, n::Integer, X) = _rand!(rng, A, n, Sampler(rng, X)) +_rand0!(rng::AbstractRNG, A::AbstractSet, n::Integer, ::Type{X}) where {X} = _rand!(rng, A, n, Sampler(rng, X)) +_rand0!(rng::AbstractRNG, A::AbstractSet, n::Integer, sp::Sampler) = _rand!(rng, A, n, sp) + +rand!(rng::AbstractRNG, A::AbstractSet, sp::Sampler) = _rand!(rng, A, length(A), sp) + + +rand(r::AbstractRNG, ::Type{T}, n::Integer) where {T<:AbstractSet} = rand(r, Float64, T, n) +rand( ::Type{T}, n::Integer) where {T<:AbstractSet} = rand(GLOBAL_RNG, T, n) + +rand(r::AbstractRNG, X, ::Type{T}, n::Integer) where {T<:AbstractSet} = _rand0!(r, deduce_type(T, eltype(X))(), n, X) +rand( X, ::Type{T}, n::Integer) where {T<:AbstractSet} = rand(GLOBAL_RNG, X, T, n) + +rand(r::AbstractRNG, ::Type{X}, ::Type{T}, n::Integer) where {X,T<:AbstractSet} = _rand0!(r, deduce_type(T, X)(), n, X) +rand( ::Type{X}, ::Type{T}, n::Integer) where {X,T<:AbstractSet} = rand(GLOBAL_RNG, X, T, n) + + +## sparse vectors & matrices + +rand(r::AbstractRNG, p::AbstractFloat, m::Integer) = sprand(r, m, p) +rand( p::AbstractFloat, m::Integer) = sprand(GLOBAL_RNG, m, p) +rand(r::AbstractRNG, p::AbstractFloat, m::Integer, n::Integer) = sprand(r, m, n, p) +rand( p::AbstractFloat, m::Integer, n::Integer) = sprand(GLOBAL_RNG, m, n, p) + +rand(r::AbstractRNG, X::Sampler, p::AbstractFloat, m::Integer) = + sprand(r, m, p, (r, n)->rand(r, X, n)) + +rand(r::AbstractRNG, X, p::AbstractFloat, m::Integer) = + rand(r, Sampler(r, X), p, m) + +rand(r::AbstractRNG, ::Type{X}, p::AbstractFloat, m::Integer) where {X} = + rand(r, Sampler(r, X), p, m) + +rand(X, p::AbstractFloat, m::Integer) = rand(GLOBAL_RNG, X, p, m) + +rand(r::AbstractRNG, X::Sampler, p::AbstractFloat, m::Integer, n::Integer) = + sprand(r, m, n, p, (r, n)->rand(r, X, n), eltype(X)) + +rand(r::AbstractRNG, X, p::AbstractFloat, m::Integer, n::Integer) = + rand(r, Sampler(r, X), p, m, n) + +rand(r::AbstractRNG, ::Type{X}, p::AbstractFloat, m::Integer, n::Integer) where {X} = + rand(r, Sampler(r, X), p, m, n) + +rand(X, p::AbstractFloat, m::Integer, n::Integer) = rand(GLOBAL_RNG, X, p, m, n) + + +## String + +let b = UInt8['0':'9';'A':'Z';'a':'z'] + global rand + rand(rng::AbstractRNG, chars, ::Type{String}, n::Integer=8) = String(rand(rng, chars, n)) + rand( chars, ::Type{String}, n::Integer=8) = rand(GLOBAL_RNG, chars, String, n) + rand(rng::AbstractRNG, ::Type{String}, n::Integer=8) = rand(rng, b, String, n) + rand( ::Type{String}, n::Integer=8) = rand(GLOBAL_RNG, b, String, n) +end + + +## BitArray + +const BitArrays = Union{BitArray,BitVector,BitMatrix} + +rand(r::AbstractRNG, ::Type{T}, dims::Dims) where {T<:BitArrays} = + rand!(r, T(uninitialized, dims)) + +rand(r::AbstractRNG, ::Type{T}, dims::Integer...) where {T<:BitArrays} = + rand!(r, T(uninitialized, convert(Dims, dims))) + +rand(::Type{T}, dims::Dims) where {T<:BitArrays} = + rand!(T(uninitialized, dims)) + +rand(::Type{T}, dims::Integer...) where {T<:BitArrays} = + rand!(T(uninitialized, convert(Dims, dims))) diff --git a/base/random/definitions.jl b/base/random/definitions.jl new file mode 100644 index 0000000000000..70c6e78d7c84a --- /dev/null +++ b/base/random/definitions.jl @@ -0,0 +1,296 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# general definitions + +abstract type AbstractRNG end + +## Distributions + +abstract type Distribution{T} end + +Base.eltype(::Type{Distribution{T}}) where {T} = T + +abstract type Combine{T} <: Distribution{T} end + +struct Combine0{T} <: Combine{T} end + +Combine(::Type{T}) where {T} = Combine0{T}() + +struct Combine1{T,X} <: Combine{T} + x::X +end + +Combine(::Type{T}, x::X) where {T,X} = Combine1{T,X}(x) +Combine(::Type{T}, ::Type{X}) where {T,X} = Combine1{T,Type{X}}(X) + +struct Combine2{T,X,Y} <: Combine{T} + x::X + y::Y +end + +Combine(::Type{T}, x::X, y::Y) where {T,X,Y} = Combine2{deduce_type(T,X,Y),X,Y}(x, y) +Combine(::Type{T}, ::Type{X}, y::Y) where {T,X,Y} = Combine2{deduce_type(T,X,Y),Type{X},Y}(X, y) +Combine(::Type{T}, x::X, ::Type{Y}) where {T,X,Y} = Combine2{deduce_type(T,X,Y),X,Type{Y}}(x, Y) +Combine(::Type{T}, ::Type{X}, ::Type{Y}) where {T,X,Y} = Combine2{deduce_type(T,X,Y),Type{X},Type{Y}}(X, Y) + +deduce_type(::Type{T}, ::Type{X}, ::Type{Y}) where {T,X,Y} = _deduce_type(T, Val(isconcrete(T)), eltype(X), eltype(Y)) +deduce_type(::Type{T}, ::Type{X}) where {T,X} = _deduce_type(T, Val(isconcrete(T)), eltype(X)) + +_deduce_type(::Type{T}, ::Val{true}, ::Type{X}, ::Type{Y}) where {T,X,Y} = T +_deduce_type(::Type{T}, ::Val{false}, ::Type{X}, ::Type{Y}) where {T,X,Y} = deduce_type(T{X}, Y) + +_deduce_type(::Type{T}, ::Val{true}, ::Type{X}) where {T,X} = T +_deduce_type(::Type{T}, ::Val{false}, ::Type{X}) where {T,X} = T{X} + + +### Uniform + +abstract type Uniform{T} <: Distribution{T} end + + +struct UniformType{T} <: Uniform{T} end + +Uniform(::Type{T}) where {T} = UniformType{T}() + +Base.getindex(::UniformType{T}) where {T} = T + +struct UniformWrap{T,E} <: Uniform{E} + val::T +end + +Uniform(x::T) where {T} = UniformWrap{T,eltype(T)}(x) + +Base.getindex(x::UniformWrap) = x.val + + +### Normal & Exponential + +abstract type Normal{T} <: Distribution{T} end + +struct Normal01{T} <: Normal{T} end + +struct Normalμσ{T} <: Normal{T} + μ::T + σ::T +end + +Normal(::Type{T}=Float64) where {T} = Normal01{T}() +Normal(μ::T, σ::T) where {T} = Normalμσ(μ, σ) + +abstract type Exponential{T} <: Distribution{T} end + +struct Exponential1{T} <: Exponential{T} end + +struct Exponentialθ{T} <: Exponential{T} + θ::T +end + +Exponential(::Type{T}=Float64) where {T<:AbstractFloat} = Exponential1{T}() +Exponential(θ::T) where {T<:AbstractFloat} = Exponentialθ(θ) + + +### integers + +# we define types which encode the generation of a specific number of bits +# the "raw" version means that the unused bits are not zeroed + +abstract type UniformBits{T<:BitInteger} end + +struct UInt10{T} <: UniformBits{T} end +struct UInt10Raw{T} <: UniformBits{T} end + +struct UInt23{T} <: UniformBits{T} end +struct UInt23Raw{T} <: UniformBits{T} end + +struct UInt52{T} <: UniformBits{T} end +struct UInt52Raw{T} <: UniformBits{T} end + +struct UInt104{T} <: UniformBits{T} end +struct UInt104Raw{T} <: UniformBits{T} end + +struct UInt2x52{T} <: UniformBits{T} end +struct UInt2x52Raw{T} <: UniformBits{T} end + +uint_sup(::Type{<:Union{UInt10,UInt10Raw}}) = UInt16 +uint_sup(::Type{<:Union{UInt23,UInt23Raw}}) = UInt32 +uint_sup(::Type{<:Union{UInt52,UInt52Raw}}) = UInt64 +uint_sup(::Type{<:Union{UInt104,UInt104Raw}}) = UInt128 +uint_sup(::Type{<:Union{UInt2x52,UInt2x52Raw}}) = UInt128 + +for UI = (:UInt10, :UInt10Raw, :UInt23, :UInt23Raw, :UInt52, :UInt52Raw, + :UInt104, :UInt104Raw, :UInt2x52, :UInt2x52Raw) + @eval begin + $UI(::Type{T}=uint_sup($UI)) where {T} = $UI{T}() + # useful for defining rand generically: + uint_default(::$UI) = $UI{uint_sup($UI)}() + end +end + + +### floats + +abstract type FloatInterval{T<:AbstractFloat} <: Uniform{T} end +abstract type CloseOpen{T<:AbstractFloat} <: FloatInterval{T} end + +struct CloseOpen01{T<:AbstractFloat} <: CloseOpen{T} end # interval [0,1) +struct CloseOpen12{T<:AbstractFloat} <: CloseOpen{T} end # interval [1,2) + +struct CloseOpenAB{T<:AbstractFloat} <: CloseOpen{T} # interval [a,b) + a::T + b::T +end + +const FloatInterval_64 = FloatInterval{Float64} +const CloseOpen01_64 = CloseOpen01{Float64} +const CloseOpen12_64 = CloseOpen12{Float64} + +CloseOpen01(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen01{T}() +CloseOpen12(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen12{T}() + +CloseOpen(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen01{T}() +CloseOpen(a::T, b::T) where {T<:AbstractFloat} = CloseOpenAB{T}(a, b) + + +Base.eltype(::Type{<:FloatInterval{T}}) where {T<:AbstractFloat} = T + +const BitFloatType = Union{Type{Float16},Type{Float32},Type{Float64}} + + +## Sampler + +abstract type Sampler{E} end + +Base.eltype(::Sampler{E}) where {E} = E + +# temporarily for BaseBenchmarks +RangeGenerator(x) = Sampler(GLOBAL_RNG, x) + +# In some cases, when only 1 random value is to be generated, +# the optimal sampler can be different than if multiple values +# have to be generated. Hence a `Repetition` parameter is used +# to choose the best one depending on the need. +const Repetition = Union{Val{1},Val{Inf}} + +# these default fall-back for all RNGs would be nice, +# but generate difficult-to-solve ambiguities +# Sampler(::AbstractRNG, X, ::Val{Inf}) = Sampler(X) +# Sampler(::AbstractRNG, ::Type{X}, ::Val{Inf}) where {X} = Sampler(X) + +Sampler(rng::AbstractRNG, sp::Sampler, ::Repetition) = + throw(ArgumentError("Sampler for this object is not defined")) + +# default shortcut for the general case +Sampler(rng::AbstractRNG, X) = Sampler(rng, X, Val(Inf)) +Sampler(rng::AbstractRNG, ::Type{X}) where {X} = Sampler(rng, X, Val(Inf)) + +### pre-defined useful Sampler types + +# default fall-back for types +struct SamplerType{T} <: Sampler{T} end + +Sampler(::AbstractRNG, ::Type{T}, ::Repetition) where {T} = SamplerType{T}() + +Base.getindex(::SamplerType{T}) where {T} = T + +# default fall-back for values +struct SamplerTrivial{T,E} <: Sampler{E} + self::T +end + +SamplerTrivial(x::T) where {T} = SamplerTrivial{T,eltype(T)}(x) + +Sampler(::AbstractRNG, x, ::Repetition) = SamplerTrivial(x) + +Base.getindex(sp::SamplerTrivial) = sp.self + +# simple sampler carrying data (which can be anything) +struct SamplerSimple{T,S,E} <: Sampler{E} + self::T + data::S +end + +SamplerSimple(x::T, data::S) where {T,S} = SamplerSimple{T,S,eltype(T)}(x, data) + +Base.getindex(sp::SamplerSimple) = sp.self + +# simple sampler carrying a (type) tag T and data +struct SamplerTag{T,S,E} <: Sampler{E} + data::S + SamplerTag{T}(s::S) where {T,S} = new{T,S,eltype(T)}(s) +end + +# a dummy container type to take advangage of SamplerTag constructor + +struct Cont{T} end + +Base.eltype(::Type{Cont{T}}) where {T} = T + +### helper samplers + +# TODO: make constraining constructors to enforce that those +# types are <: Sampler{T} + +#### Adapter to generate a randome value in [0, n] + +struct LessThan{T<:Integer,S} <: Sampler{T} + sup::T + s::S # the scalar specification/sampler to feed to rand +end + +function rand(rng::AbstractRNG, sp::LessThan) + while true + x = rand(rng, sp.s) + x <= sp.sup && return x + end +end + +struct Masked{T<:Integer,S} <: Sampler{T} + mask::T + s::S +end + +rand(rng::AbstractRNG, sp::Masked) = rand(rng, sp.s) & sp.mask + +#### Uniform + +struct UniformT{T} <: Sampler{T} end + +uniform(::Type{T}) where {T} = UniformT{T}() + +rand(rng::AbstractRNG, ::UniformT{T}) where {T} = rand(rng, T) + + +## Rand + +struct Rand{R<:AbstractRNG,S<:Sampler} + rng::R + sp::S +end + +# X can be an explicit Distribution, or an implicit one like 1:10 +Rand(rng::AbstractRNG, X) = Rand(rng, Sampler(rng, X)) +Rand(rng::AbstractRNG, ::Type{X}=Float64) where {X} = Rand(rng, Sampler(rng, X)) + +Rand(X) = Rand(GLOBAL_RNG, X) +Rand(::Type{X}=Float64) where {X} = Rand(GLOBAL_RNG, X) + +(R::Rand)(args...) = rand(R.rng, R.sp, args...) + +Base.start(R::Rand) = R + +function Base.next(::Union{Rand,Distribution}, R::Rand) + e = R() + e, R +end + +Base.done(::Union{Rand,Distribution}, ::Rand) = false + +Base.iteratorsize(::Type{<:Rand}) = Base.IsInfinite() + +Base.iteratoreltype(::Type{<:Rand}) = Base.HasEltype() +Base.eltype(::Type{<:Rand{R, <:Sampler{T}}}) where {R,T} = T + +# convenience iteration over distributions + +Base.start(d::Distribution) = Rand(GLOBAL_RNG, d) +Base.iteratorsize(::Type{<:Distribution}) = Base.IsInfinite() diff --git a/base/random/generation.jl b/base/random/generation.jl index e584bf8385c53..68939f5826b52 100644 --- a/base/random/generation.jl +++ b/base/random/generation.jl @@ -15,32 +15,41 @@ ## from types: rand(::Type, [dims...]) +Sampler(rng::AbstractRNG, d::Union{UniformWrap,UniformType}, n::Repetition) = + Sampler(rng, d[], n) + ### random floats Sampler(rng::AbstractRNG, ::Type{T}, n::Repetition) where {T<:AbstractFloat} = - Sampler(rng, CloseOpen(T), n) + Sampler(rng, CloseOpen01(T), n) # generic random generation function which can be used by RNG implementors # it is not defined as a fallback rand method as this could create ambiguities -rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen{Float16}}) = +rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float16}}) = Float16(reinterpret(Float32, (rand(r, UInt10(UInt32)) << 13) | 0x3f800000) - 1) -rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen{Float32}}) = +rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float32}}) = reinterpret(Float32, rand(r, UInt23()) | 0x3f800000) - 1 -rand(r::AbstractRNG, ::SamplerTrivial{Close1Open2_64}) = +rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen12_64}) = reinterpret(Float64, 0x3ff0000000000000 | rand(r, UInt52())) -rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen_64}) = rand(r, Close1Open2()) - 1.0 +rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01_64}) = rand(r, CloseOpen12()) - 1.0 + +Sampler(rng::AbstractRNG, d::CloseOpenAB{T}, n::Repetition) where {T} = + SamplerTag{CloseOpenAB{T}}((a=d.a, d=d.b - d.a, sp=Sampler(rng, CloseOpen01{T}(), n))) + +rand(rng::AbstractRNG, sp::SamplerTag{CloseOpenAB{T}}) where {T} = + sp.data.a + sp.data.d * rand(rng, sp.data.sp) #### BigFloat const bits_in_Limb = sizeof(Limb) << 3 const Limb_high_bit = one(Limb) << (bits_in_Limb-1) -struct SamplerBigFloat{I<:FloatInterval{BigFloat}} <: Sampler +struct SamplerBigFloat{I<:FloatInterval{BigFloat}} <: Sampler{BigFloat} prec::Int nlimbs::Int limbs::Vector{Limb} @@ -71,13 +80,13 @@ function _rand(rng::AbstractRNG, sp::SamplerBigFloat) (z, randbool) end -function _rand(rng::AbstractRNG, sp::SamplerBigFloat, ::Close1Open2{BigFloat}) +function _rand(rng::AbstractRNG, sp::SamplerBigFloat, ::CloseOpen12{BigFloat}) z = _rand(rng, sp)[1] z.exp = 1 z end -function _rand(rng::AbstractRNG, sp::SamplerBigFloat, ::CloseOpen{BigFloat}) +function _rand(rng::AbstractRNG, sp::SamplerBigFloat, ::CloseOpen01{BigFloat}) z, randbool = _rand(rng, sp) z.exp = 0 randbool && @@ -89,8 +98,8 @@ end # alternative, with 1 bit less of precision # TODO: make an API for requesting full or not-full precision -function _rand(rng::AbstractRNG, sp::SamplerBigFloat, ::CloseOpen{BigFloat}, ::Nothing) - z = _rand(rng, sp, Close1Open2(BigFloat)) +function _rand(rng::AbstractRNG, sp::SamplerBigFloat, ::CloseOpen01{BigFloat}, ::Nothing) + z = _rand(rng, sp, CloseOpen12(BigFloat)) ccall((:mpfr_sub_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, z, 1, Base.MPFR.ROUNDING_MODE[]) z @@ -107,7 +116,7 @@ rand(r::AbstractRNG, ::SamplerTrivial{UInt23Raw{UInt32}}) = rand(r, UInt32) rand(r::AbstractRNG, ::SamplerTrivial{UInt52Raw{UInt64}}) = _rand52(r, rng_native_52(r)) -_rand52(r::AbstractRNG, ::Type{Float64}) = reinterpret(UInt64, rand(r, Close1Open2())) +_rand52(r::AbstractRNG, ::Type{Float64}) = reinterpret(UInt64, rand(r, CloseOpen12())) _rand52(r::AbstractRNG, ::Type{UInt64}) = rand(r, UInt64) rand(r::AbstractRNG, ::SamplerTrivial{UInt104Raw{UInt128}}) = @@ -121,10 +130,25 @@ rand(r::AbstractRNG, ::SamplerTrivial{UInt104{UInt128}}) = rand(r, UInt104Raw()) rand(r::AbstractRNG, sp::SamplerTrivial{<:UniformBits{T}}) where {T} = rand(r, uint_default(sp[])) % T -### random complex numbers +### sampler for pairs and complex numbers + +function Sampler(rng::AbstractRNG, u::Combine2{T}, n::Repetition) where T <: Union{Pair,Complex} + sp1 = Sampler(rng, u.x, n) + sp2 = u.x == u.y ? sp1 : Sampler(rng, u.y, n) + SamplerTag{Cont{T}}((sp1, sp2)) +end + +rand(rng::AbstractRNG, sp::SamplerTag{Cont{T}}) where {T<:Union{Pair,Complex}} = + T(rand(rng, sp.data[1]), rand(rng, sp.data[2])) + +#### additional methods for complex numbers + +Sampler(rng::AbstractRNG, u::Combine1{Complex}, n::Repetition) = + Sampler(rng, Combine(Complex, u.x, u.x), n) + +Sampler(rng::AbstractRNG, ::Type{Complex{T}}, n::Repetition) where {T<:Real} = + Sampler(rng, Combine(Complex, T, T), n) -rand(r::AbstractRNG, ::SamplerType{Complex{T}}) where {T<:Real} = - complex(rand(r, T), rand(r, T)) ### random characters @@ -155,7 +179,7 @@ uint_sup(::Type{<:Union{Int128,UInt128}}) = UInt128 #### Fast -struct SamplerRangeFast{U<:BitUnsigned,T<:Union{BitInteger,Bool}} <: Sampler +struct SamplerRangeFast{U<:BitUnsigned,T<:Union{BitInteger,Bool}} <: Sampler{T} a::T # first element of the range bw::UInt # bit width m::U # range length - 1 @@ -215,7 +239,7 @@ maxmultiple(k::T, sup::T=zero(T)) where {T<:Unsigned} = unsafe_maxmultiple(k::T, sup::T) where {T<:Unsigned} = div(sup, k + (k == 0))*k - one(k) -struct SamplerRangeInt{T<:Union{Bool,Integer},U<:Unsigned} <: Sampler +struct SamplerRangeInt{T<:Union{Bool,Integer},U<:Unsigned} <: Sampler{T} a::T # first element of the range bw::Int # bit width k::U # range length or zero for full range @@ -270,7 +294,7 @@ end ### BigInt -struct SamplerBigInt <: Sampler +struct SamplerBigInt <: Sampler{BigInt} a::BigInt # first m::BigInt # range length - 1 nlimbs::Int # number of limbs in generated BigInt's (z ∈ [0, m]) @@ -336,9 +360,10 @@ end ## random values from Set -Sampler(rng::AbstractRNG, t::Set, n::Repetition) = SamplerTag{Set}(Sampler(rng, t.dict, n)) +Sampler(rng::AbstractRNG, t::Set{T}, n::Repetition) where {T} = + SamplerTag{Set{T}}(Sampler(rng, t.dict, n)) -rand(rng::AbstractRNG, sp::SamplerTag{Set,<:Sampler}) = rand(rng, sp.data).first +rand(rng::AbstractRNG, sp::SamplerTag{<:Set,<:Sampler}) = rand(rng, sp.data).first ## random values from BitSet diff --git a/base/random/normal.jl b/base/random/normal.jl index d1d6540be6d6c..cfadd4dbf1c4f 100644 --- a/base/random/normal.jl +++ b/base/random/normal.jl @@ -7,6 +7,27 @@ # randmtzig (covers also exponential variates) +## rand(::Normal) & rand(::Exponential) + +rand(rng::AbstractRNG, ::SamplerTrivial{Normal01{T}}) where {T<:Union{AbstractFloat,Complex{<:AbstractFloat}}} = + randn(rng, T) + +Sampler(rng::AbstractRNG, d::Normalμσ{T}, n::Repetition) where {T} = + SamplerSimple(d, Sampler(rng, Normal(T), n)) + +rand(rng::AbstractRNG, sp::SamplerSimple{Normalμσ{T},<:Sampler}) where {T} = + sp[].μ + sp[].σ * rand(rng, sp.data) + +rand(rng::AbstractRNG, ::SamplerTrivial{Exponential1{T}}) where {T<:AbstractFloat} = + randexp(rng, T) + +Sampler(rng::AbstractRNG, d::Exponentialθ{T}, n::Repetition) where {T} = + SamplerSimple(d, Sampler(rng, Exponential(T), n)) + +rand(rng::AbstractRNG, sp::SamplerSimple{Exponentialθ{T},<:Sampler}) where {T} = + sp[].θ * rand(rng, sp.data) + + ## randn """ diff --git a/base/random/random.jl b/base/random/random.jl index 2af2bcc31935c..3bb47f385a09c 100644 --- a/base/random/random.jl +++ b/base/random/random.jl @@ -19,207 +19,8 @@ export srand, randperm, randperm!, randcycle, randcycle!, AbstractRNG, MersenneTwister, RandomDevice, - GLOBAL_RNG, randjump - - -## general definitions - -abstract type AbstractRNG end - -### integers - -# we define types which encode the generation of a specific number of bits -# the "raw" version means that the unused bits are not zeroed - -abstract type UniformBits{T<:BitInteger} end - -struct UInt10{T} <: UniformBits{T} end -struct UInt10Raw{T} <: UniformBits{T} end - -struct UInt23{T} <: UniformBits{T} end -struct UInt23Raw{T} <: UniformBits{T} end - -struct UInt52{T} <: UniformBits{T} end -struct UInt52Raw{T} <: UniformBits{T} end - -struct UInt104{T} <: UniformBits{T} end -struct UInt104Raw{T} <: UniformBits{T} end - -struct UInt2x52{T} <: UniformBits{T} end -struct UInt2x52Raw{T} <: UniformBits{T} end - -uint_sup(::Type{<:Union{UInt10,UInt10Raw}}) = UInt16 -uint_sup(::Type{<:Union{UInt23,UInt23Raw}}) = UInt32 -uint_sup(::Type{<:Union{UInt52,UInt52Raw}}) = UInt64 -uint_sup(::Type{<:Union{UInt104,UInt104Raw}}) = UInt128 -uint_sup(::Type{<:Union{UInt2x52,UInt2x52Raw}}) = UInt128 - -for UI = (:UInt10, :UInt10Raw, :UInt23, :UInt23Raw, :UInt52, :UInt52Raw, - :UInt104, :UInt104Raw, :UInt2x52, :UInt2x52Raw) - @eval begin - $UI(::Type{T}=uint_sup($UI)) where {T} = $UI{T}() - # useful for defining rand generically: - uint_default(::$UI) = $UI{uint_sup($UI)}() - end -end - -### floats - -abstract type FloatInterval{T<:AbstractFloat} end - -struct CloseOpen{ T<:AbstractFloat} <: FloatInterval{T} end # interval [0,1) -struct Close1Open2{T<:AbstractFloat} <: FloatInterval{T} end # interval [1,2) - -const FloatInterval_64 = FloatInterval{Float64} -const CloseOpen_64 = CloseOpen{Float64} -const Close1Open2_64 = Close1Open2{Float64} - -CloseOpen( ::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen{T}() -Close1Open2(::Type{T}=Float64) where {T<:AbstractFloat} = Close1Open2{T}() - -Base.eltype(::Type{<:FloatInterval{T}}) where {T<:AbstractFloat} = T - -const BitFloatType = Union{Type{Float16},Type{Float32},Type{Float64}} - -### Sampler - -abstract type Sampler end - -# temporarily for BaseBenchmarks -RangeGenerator(x) = Sampler(GLOBAL_RNG, x) - -# In some cases, when only 1 random value is to be generated, -# the optimal sampler can be different than if multiple values -# have to be generated. Hence a `Repetition` parameter is used -# to choose the best one depending on the need. -const Repetition = Union{Val{1},Val{Inf}} - -# these default fall-back for all RNGs would be nice, -# but generate difficult-to-solve ambiguities -# Sampler(::AbstractRNG, X, ::Val{Inf}) = Sampler(X) -# Sampler(::AbstractRNG, ::Type{X}, ::Val{Inf}) where {X} = Sampler(X) - -Sampler(rng::AbstractRNG, sp::Sampler, ::Repetition) = - throw(ArgumentError("Sampler for this object is not defined")) - -# default shortcut for the general case -Sampler(rng::AbstractRNG, X) = Sampler(rng, X, Val(Inf)) -Sampler(rng::AbstractRNG, ::Type{X}) where {X} = Sampler(rng, X, Val(Inf)) - -#### pre-defined useful Sampler types - -# default fall-back for types -struct SamplerType{T} <: Sampler end - -Sampler(::AbstractRNG, ::Type{T}, ::Repetition) where {T} = SamplerType{T}() - -Base.getindex(sp::SamplerType{T}) where {T} = T - -# default fall-back for values -struct SamplerTrivial{T} <: Sampler - self::T -end - -Sampler(::AbstractRNG, X, ::Repetition) = SamplerTrivial(X) - -Base.getindex(sp::SamplerTrivial) = sp.self - -# simple sampler carrying data (which can be anything) -struct SamplerSimple{T,S} <: Sampler - self::T - data::S -end - -Base.getindex(sp::SamplerSimple) = sp.self - -# simple sampler carrying a (type) tag T and data -struct SamplerTag{T,S} <: Sampler - data::S - SamplerTag{T}(s::S) where {T,S} = new{T,S}(s) -end - - -#### helper samplers - -##### Adapter to generate a randome value in [0, n] - -struct LessThan{T<:Integer,S} <: Sampler - sup::T - s::S # the scalar specification/sampler to feed to rand -end - -function rand(rng::AbstractRNG, sp::LessThan) - while true - x = rand(rng, sp.s) - x <= sp.sup && return x - end -end - -struct Masked{T<:Integer,S} <: Sampler - mask::T - s::S -end - -rand(rng::AbstractRNG, sp::Masked) = rand(rng, sp.s) & sp.mask - -##### Uniform - -struct UniformT{T} <: Sampler end - -uniform(::Type{T}) where {T} = UniformT{T}() - -rand(rng::AbstractRNG, ::UniformT{T}) where {T} = rand(rng, T) - - -### machinery for generation with Sampler - -# This describes how to generate random scalars or arrays, by generating a Sampler -# and calling rand on it (which should be defined in "generation.jl"). -# NOTE: this section could be moved into a separate file when more containers are supported. - -#### scalars - -rand(rng::AbstractRNG, X) = rand(rng, Sampler(rng, X, Val(1))) -rand(rng::AbstractRNG=GLOBAL_RNG, ::Type{X}=Float64) where {X} = - rand(rng, Sampler(rng, X, Val(1))) - -rand(X) = rand(GLOBAL_RNG, X) -rand(::Type{X}) where {X} = rand(GLOBAL_RNG, X) - -#### arrays - -rand!(A::AbstractArray{T}, X) where {T} = rand!(GLOBAL_RNG, A, X) -rand!(A::AbstractArray{T}, ::Type{X}=T) where {T,X} = rand!(GLOBAL_RNG, A, X) - -rand!(rng::AbstractRNG, A::AbstractArray{T}, X) where {T} = rand!(rng, A, Sampler(rng, X)) -rand!(rng::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} = rand!(rng, A, Sampler(rng, X)) - -function rand!(rng::AbstractRNG, A::AbstractArray{T}, sp::Sampler) where T - for i in eachindex(A) - @inbounds A[i] = rand(rng, sp) - end - A -end - -rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) -rand( dims::Dims) = rand(GLOBAL_RNG, dims) -rand(r::AbstractRNG, dims::Integer...) = rand(r, Dims(dims)) -rand( dims::Integer...) = rand(Dims(dims)) - -rand(r::AbstractRNG, X, dims::Dims) = rand!(r, Array{eltype(X)}(uninitialized, dims), X) -rand( X, dims::Dims) = rand(GLOBAL_RNG, X, dims) - -rand(r::AbstractRNG, X, d::Integer, dims::Integer...) = rand(r, X, Dims((d, dims...))) -rand( X, d::Integer, dims::Integer...) = rand(X, Dims((d, dims...))) -# note: the above methods would trigger an ambiguity warning if d was not separated out: -# rand(r, ()) would match both this method and rand(r, dims::Dims) -# moreover, a call like rand(r, NotImplementedType()) would be an infinite loop - -rand(r::AbstractRNG, ::Type{X}, dims::Dims) where {X} = rand!(r, Array{eltype(X)}(uninitialized, dims), X) -rand( ::Type{X}, dims::Dims) where {X} = rand(GLOBAL_RNG, X, dims) - -rand(r::AbstractRNG, ::Type{X}, d::Integer, dims::Integer...) where {X} = rand(r, X, Dims((d, dims...))) -rand( ::Type{X}, d::Integer, dims::Integer...) where {X} = rand(X, Dims((d, dims...))) + GLOBAL_RNG, randjump, + Distribution, Combine, Uniform, Sampler ## __init__ & include @@ -233,6 +34,8 @@ function __init__() end end +include("definitions.jl") +include("containers.jl") include("RNGs.jl") include("generation.jl") include("normal.jl") @@ -242,9 +45,9 @@ include("misc.jl") ## rand & rand! & srand docstrings """ - rand([rng=GLOBAL_RNG], [S], [dims...]) + rand([rng=GLOBAL_RNG], [S], [C...]) -Pick a random element or array of random elements from the set of values specified by `S`; +Pick a random element or collection 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']`), @@ -253,8 +56,26 @@ Pick a random element or array of random elements from the set of values specifi * 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). +* a `Distribution` object, e.g. `Normal()` for a normal distribution (like `randn()`), + or `CloseOpen(10.0, 20.0)` for uniform `Float64` numbers in the range ``[10.0, 20.0)``; +* a `Combine` object, which can be either `Combine(Pair, S1, S2)` or `Combine(Complex, S1, S2)`, + where `S1` and `S2` are one of the specifications above; `Pair` or `Complex` can optionally be + given as concrete types, e.g. `Combine(ComplexF64, 1:3, Int)` to generate `ComplexF64` instead + of `Complex{Int}`. + +`S` usually defaults to [`Float64`](@ref). + +If `C...` is not specified, `rand` produces a scalar. Otherwise, `C...` can be: + +* a set of integers, or a tuple of `Int`, which specify the dimensions of an `Array` to generate; +* `(p::AbstractFloat, m::Integer, [n::Integer])`, which produces a sparse array of dimensions `(m, n)`, + in which the probability of any element being nonzero is independently given by `p` +* `(String, [n=8])`, which produces a random `String` of length `n`; the generated string consists of `Char` + taken from a predefined set like `randstring`, and can be specified with the `S` parameter. +* `(Dict, n)`, which produces a `Dict` of length `n`; `S` must then specify the type of its elements, + e.g. `Combine(Pair, Int, 2:3)`; +* `(Set, n)`, which produces a `Set` of length `n`; +* `(BitArray, dims...)`, which produces a `BitArray` with the specified dimensions. # Examples ```julia-repl @@ -265,6 +86,12 @@ julia> rand(Int, 2) julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) 1=>2 + +julia> rand("abc", String, 12) +"bccaacaabaac" + +julia> rand(1:10, Set, 3) +Set([3, 8, 6]) ``` !!! note diff --git a/test/random.jl b/test/random.jl index 9c25bb00648a7..29faaef85bbd4 100644 --- a/test/random.jl +++ b/test/random.jl @@ -3,7 +3,7 @@ isdefined(Main, :TestHelpers) || @eval Main include(joinpath(dirname(@__FILE__), "TestHelpers.jl")) using Main.TestHelpers.OAs -using Base.Random: Sampler, SamplerRangeFast, SamplerRangeInt, dSFMT +using Base.Random: Sampler, SamplerRangeFast, SamplerRangeInt, dSFMT, Combine, Normal, Uniform, Exponential, Rand @testset "Issue #6573" begin srand(0) @@ -290,8 +290,8 @@ let mt = MersenneTwister(0) @test rand!(mt, AF64)[end] == 0.957735065345398 @test rand!(mt, AF64)[end] == 0.6492481059865669 resize!(AF64, 2*length(mt.vals)) - @test invoke(rand!, Tuple{MersenneTwister,AbstractArray{Float64},Base.Random.SamplerTrivial{Base.Random.CloseOpen_64}}, - mt, AF64, Base.Random.SamplerTrivial(Base.Random.CloseOpen()))[end] == 0.432757268470779 + @test invoke(rand!, Tuple{MersenneTwister,AbstractArray{Float64},Base.Random.SamplerTrivial{Base.Random.CloseOpen01_64}}, + mt, AF64, Base.Random.SamplerTrivial(Base.Random.CloseOpen01()))[end] == 0.432757268470779 end # Issue #9037 @@ -662,3 +662,106 @@ end r = T(1):T(108) @test rand(RNG, SamplerRangeFast(r)) ∈ r end + + +@testset "Distributions" begin + # Normal/Exponential + @test rand(Normal()) isa Float64 + @test rand(Normal(0.0, 1.0)) isa Float64 + @test rand(Exponential()) isa Float64 + @test rand(Exponential(1.0)) isa Float64 + @test rand(Normal(Float32)) isa Float32 + @test rand(Exponential(Float32)) isa Float32 + @test rand(Normal(ComplexF64)) isa ComplexF64 + + # pairs/complexes + @test rand(Combine(Pair, 1:3, Float64)) isa Pair{Int,Float64} + z = rand(Combine(Complex, 1:3, 6:9)) + @test z.re ∈ 1:3 + @test z.im ∈ 6:9 + @test z isa Complex{Int} + z = rand(Combine(ComplexF64, 1:3, 6:9)) + @test z.re ∈ 1:3 + @test z.im ∈ 6:9 + @test z isa ComplexF64 + + # Uniform + @test rand(Uniform(Float64)) isa Float64 + @test rand(Uniform(1:10)) isa Int + @test rand(Uniform(1:10)) ∈ 1:10 + @test rand(Uniform(Int)) isa Int +end + +@testset "Containers" for rng in ([], [MersenneTwister(0)], [RandomDevice()]) + # Set + for s = (rand(rng..., 1:99, Set{Int}, 10), + rand(rng..., 1:99, Set, 10)) + @test s isa Set{Int} + @test length(s) == 10 + @test rand(s) ∈ 1:99 + end + s = Set([1, 2]) + @test s === rand!(s) + @test first(s) ∉ (1, 2) # extremely unlikely + @test length(s) == 2 + @test s === rand!(s, 3:9) <= Set(3:9) + @test length(s) == 2 + + # Dict + for s = (rand(rng..., Combine(Pair, 1:99, 1:99), Dict, 10), + rand(rng..., Combine(Pair, 1:99, 1:99), Dict{Int,Int}, 10)) + @test s isa Dict{Int,Int} + @test length(s) == 10 + p = rand(s) + @test p.first ∈ 1:99 + @test p.second ∈ 1:99 + end + s = Dict(1=>2, 2=>1) + @test s === rand!(s) + @test length(s) == 2 + @test first(s).first ∉ (1, 2) # extremely unlikely + rand!(s, Combine(Pair, 3:9, Int)) + @test length(s) == 2 + @test first(s).first ∈ 3:9 + + # sparse + @test rand(rng..., Float64, .5, 10) isa SparseVector{Float64} + @test rand(rng..., .5, 10) isa SparseVector{Float64} + @test rand(rng..., Int, .5, 10) isa SparseVector{Int} + @test rand(rng..., Float64, .5, 10, 3) isa SparseMatrixCSC{Float64} + @test rand(rng..., .5, 10, 3) isa SparseMatrixCSC{Float64} + @test rand(rng..., Int, .5, 10, 3) isa SparseMatrixCSC{Int} + + # BitArray + @test rand(rng..., BitArray, 10) isa BitVector + @test rand(rng..., BitVector, 10) isa BitVector + @test_throws MethodError rand(rng..., BitVector, 10, 20) isa BitVector + @test rand(rng..., BitArray, 10, 3) isa BitMatrix + @test rand(rng..., BitMatrix, 10, 3) isa BitMatrix + @test_throws MethodError rand(rng..., BitVector, 10, 3) isa BitMatrix + + # String + s = rand(rng..., String) + @test s isa String + @test length(s) == 8 + s = rand(rng..., String, 10) + @test s isa String + @test length(s) == 10 + s = rand(rng..., "asd", String) + @test length(s) == 8 + @test Set(s) <= Set("asd") +end + +@testset "Rand" for rng in ([], [MersenneTwister(0)], [RandomDevice()]) + for XT = zip(([Int], [1:3], []), (Int, Int, Float64)) + X, T = XT + r = Rand(rng..., X...) + @test collect(Iterators.take(r, 10)) isa Vector{T} + @test r() isa T + @test r(2, 3) isa Matrix{T} + @test r(.3, 2, 3) isa SparseMatrixCSC{T} + end + for d = (Uniform(1:10), Uniform(Int)) + @test collect(Iterators.take(d, 10)) isa Vector{Int} + end +end