diff --git a/README.md b/README.md index 11a07aa..d30eff2 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,18 @@ julia> julia> rand(Bernoulli(0.2), BitVector, 10) # using the Bernoulli distribu false true +julia> rand(Int8, Array, 3, 5) # more explicit syntax than rand(Int8, 3, 5) ... +3×5 Array{Int8,2}: + -4 -110 70 -4 63 + -38 4 55 -86 -106 + -56 -124 46 118 114 + +julia> rand(Int8, Array, 1:3, 3:5) # ... but dimensions can be specified with a distribution! + # the size will be computed as (rand(1:3), rand(3:5)) +2×5 Array{Int8,2}: + -2 -43 106 -74 18 + -117 -97 2 -126 125 + julia> rand(1:3, NTuple{3}) # NTuple{3} considered as a container, equivalent to rand(make(NTuple{3}, 1:3)) (3, 3, 1) diff --git a/src/containers.jl b/src/containers.jl index bb2a4a8..9c9198d 100644 --- a/src/containers.jl +++ b/src/containers.jl @@ -95,33 +95,46 @@ end macro make_array_container(Cont) definitions = - [ :(rand(rng::AbstractRNG, $Cont, dims::Dims) = rand(rng, _make_cont(t, dims))), - :(rand( $Cont, dims::Dims) = rand(GLOBAL_RNG, _make_cont(t, dims))), - :(rand(rng::AbstractRNG, $Cont, dims::Integer...) = rand(rng, _make_cont(t, Dims(dims)))), - :(rand( $Cont, dims::Integer...) = rand(GLOBAL_RNG, _make_cont(t, Dims(dims)))), - - :(rand(rng::AbstractRNG, X, $Cont, dims::Dims) = rand(rng, _make_cont(t, X, dims))), - :(rand( X, $Cont, dims::Dims) = rand(GLOBAL_RNG, _make_cont(t, X, dims))), - :(rand(rng::AbstractRNG, X, $Cont, dims::Integer...) = rand(rng, _make_cont(t, X, Dims(dims)))), - :(rand( X, $Cont, dims::Integer...) = rand(GLOBAL_RNG, _make_cont(t, X, Dims(dims)))), - - :(rand(rng::AbstractRNG, ::Type{X}, $Cont, dims::Dims) where {X} = rand(rng, _make_cont(t, X, dims))), - :(rand( ::Type{X}, $Cont, dims::Dims) where {X} = rand(GLOBAL_RNG, _make_cont(t, X, dims))), - :(rand(rng::AbstractRNG, ::Type{X}, $Cont, dims::Integer...) where {X} = rand(rng, _make_cont(t, X, Dims(dims)))), - :(rand( ::Type{X}, $Cont, dims::Integer...) where {X} = rand(GLOBAL_RNG, _make_cont(t, X, Dims(dims)))), + [ :(rand(rng::AbstractRNG, $Cont, dims...) = rand(rng, _make_cont(t, _default_sampling(t), dims...))), + :(rand( $Cont, dims...) = rand(GLOBAL_RNG, _make_cont(t, _default_sampling(t), dims...))), + + :(rand(rng::AbstractRNG, X, $Cont, dims...) = rand(rng, _make_cont(t, X, dims...))), + :(rand( X, $Cont, dims...) = rand(GLOBAL_RNG, _make_cont(t, X, dims...))), + + :(rand(rng::AbstractRNG, ::Type{X}, $Cont, dims...) where {X} = rand(rng, _make_cont(t, X, dims...))), + :(rand( ::Type{X}, $Cont, dims...) where {X} = rand(GLOBAL_RNG, _make_cont(t, X, dims...))), + + # remove ambiguities with Random + :(rand(rng::AbstractRNG, $Cont, dims::Integer...) = rand(rng, _make_cont(t, _default_sampling(t), dims...))), + :(rand( $Cont, dims::Integer...) = rand(GLOBAL_RNG, _make_cont(t, _default_sampling(t), dims...))), + + :(rand(rng::AbstractRNG, X, $Cont, dims::Integer...) = rand(rng, _make_cont(t, X, dims...))), + :(rand( X, $Cont, dims::Integer...) = rand(GLOBAL_RNG, _make_cont(t, X, dims...))), + + :(rand(rng::AbstractRNG, ::Type{X}, $Cont, dims::Integer...) where {X} = rand(rng, _make_cont(t, X, dims...))), + :(rand( ::Type{X}, $Cont, dims::Integer...) where {X} = rand(GLOBAL_RNG, _make_cont(t, X, dims...))), ] esc(Expr(:block, definitions...)) end -_make_cont(args...) = make(args...) - @make_array_container(t::Type{<:Array}) @make_array_container(t::Type{<:BitArray}) @make_array_container(t::AbstractFloat) -_make_cont(t::AbstractFloat, x, dims::Dims{1}) = make(SparseVector, x, t, dims) -_make_cont(t::AbstractFloat, dims::Dims{1}) = make(SparseVector, t, dims) -_make_cont(t::AbstractFloat, x, dims::Dims{2}) = make(SparseMatrixCSC, x, t, dims) -_make_cont(t::AbstractFloat, dims::Dims{2}) = make(SparseMatrixCSC, t, dims) + +_make_cont(args...) = make(args...) +_default_sampling(t) = default_sampling(t) + +_make_cont(t::AbstractFloat, x, dims::Dims{1}) = make(SparseVector, x, t, dims) +_make_cont(t::AbstractFloat, x, d1::Integer) = make(SparseVector, x, t, d1) +_make_cont(t::AbstractFloat, x, dims::Dims{2}) = make(SparseMatrixCSC, x, t, dims) +_make_cont(t::AbstractFloat, x, d1::Integer, d2::Integer) = make(SparseMatrixCSC, x, t, d1, d2) + +_default_sampling(t::AbstractFloat) = default_sampling(SparseVector) + +# ambiguities +rand(rng::AbstractRNG, t::AbstractFloat, dims::Dims) = rand(rng, _make_cont(t, _default_sampling(t), dims)) +rand( t::AbstractFloat, dims::Dims) = rand( _make_cont(t, _default_sampling(t), dims)) + ## sets/dicts diff --git a/src/distributions.jl b/src/distributions.jl index 34c3f60..18ba0e2 100644 --- a/src/distributions.jl +++ b/src/distributions.jl @@ -57,6 +57,18 @@ _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} +## Const + +# distribution always yielding the same value +struct Const{T} <: Distribution{T} + x::T +end + +Base.getindex(c::Const) = c.x + +rand(::AbstractRNG, c::SamplerTrivial{<:Const}) = c[][] + + ## Uniform abstract type Uniform{T} <: Distribution{T} end diff --git a/src/sampling.jl b/src/sampling.jl index 1d677a5..a4a327d 100644 --- a/src/sampling.jl +++ b/src/sampling.jl @@ -426,40 +426,57 @@ rand(rng::AbstractRNG, sp::SamplerTag{Cont{S}}) where {S<:Base.ImmutableDict} = default_sampling(::Type{<:AbstractArray{T}}) where {T} = Uniform(T) default_sampling(::Type{<:AbstractArray}) = Uniform(Float64) -make(A::Type{<:AbstractArray}, X, d1::Integer, dims::Integer...) = make(A, X, Dims((d1, dims...))) -make(A::Type{<:AbstractArray}, ::Type{X}, d1::Integer, dims::Integer...) where {X} = make(A, X, Dims((d1, dims...))) +_makedim(d::Integer) = Const(Int(d)) +_makedim(d) = _makedim(d, gentype(d)) +_makedim(d, ::Type{Int}) = d +_makedim(d, T) = throw(ArgumentError("the passed dimension distribution doesn't yield an Int")) -make(A::Type{<:AbstractArray}, dims::Dims) = make(A, default_sampling(A), dims) -make(A::Type{<:AbstractArray}, d1::Integer, dims::Integer...) = make(A, default_sampling(A), Dims((d1, dims...))) +make(A::Type{<:AbstractArray}, d1::Integer, dims::Integer...) = + make(A, default_sampling(A), Const(Dims((d1, dims...)))) -if VERSION < v"1.1.0" - # to resolve ambiguity - make(A::Type{<:AbstractArray}, X, d1::Integer) = make(A, X, Dims((d1,))) - make(A::Type{<:AbstractArray}, X, d1::Integer, d2::Integer) = make(A, X, Dims((d1, d2))) -end +make(A::Type{<:AbstractArray}, dims::Dims) = make(A, default_sampling(A), Const(dims)) +make(A::Type{<:AbstractArray}, dims::Distribution) = make(A, default_sampling(A), dims) + +# make sure that dims is interpreted as dimensions, not as an Int distribution +make(A::Type{<:AbstractArray}, X, dims::Dims) = make(A, X, Const(dims)) +make(A::Type{<:AbstractArray}, ::Type{X}, dims::Dims) where {X} = make(A, X, Const(dims)) + +make(A::Type{<:AbstractArray}, X, d1, dims...) = + make(A, X, make(Tuple, _makedim(d1), _makedim.(dims)...)) + +make(A::Type{<:AbstractArray}, ::Type{X}, d1, dims...) where {X} = + make(A, X, make(Tuple, _makedim(d1), _makedim.(dims)...)) + +# stop the recursion +make(A::Type{<:AbstractArray}, X, dims::Distribution) = + Make{maketype(A, X, dims)}(X, dims) + +make(A::Type{<:AbstractArray}, ::Type{X}, dims::Distribution) where {X} = + Make{maketype(A, X, dims)}(X, dims) Sampler(RNG::Type{<:AbstractRNG}, c::Make2{A}, n::Repetition) where {A<:AbstractArray} = - SamplerTag{A}((sampler(RNG, c[1], n), c[2])) + SamplerTag{A}((sampler(RNG, c[1], Val(Inf)), # values + Sampler(RNG, c[2], n))) # dimensions rand(rng::AbstractRNG, sp::SamplerTag{A}) where {A<:AbstractArray} = - rand!(rng, A(undef, sp.data[2]), sp.data[1]) + rand!(rng, A(undef, rand(rng, sp.data[2])), sp.data[1]) #### Array # cf. inference bug https://github.com/JuliaLang/julia/issues/28762 # we have to write out all combinations for getting proper inference -maketype(A::Type{Array{T}}, _, ::Dims{N}) where {T, N} = Array{T, N} -maketype(A::Type{Array{T,N}}, _, ::Dims{N}) where {T, N} = Array{T, N} -maketype(A::Type{Array{T,N} where T}, X, ::Dims{N}) where {N} = Array{val_gentype(X), N} -maketype(A::Type{Array}, X, ::Dims{N}) where {N} = Array{val_gentype(X), N} +maketype(A::Type{Array{T}}, _, ::Distribution{Dims{N}}) where {T, N} = Array{T, N} +maketype(A::Type{Array{T,N}}, _, ::Distribution{Dims{N}}) where {T, N} = Array{T, N} +maketype(A::Type{Array{T,N} where T}, X, ::Distribution{Dims{N}}) where {N} = Array{val_gentype(X), N} +maketype(A::Type{Array}, X, ::Distribution{Dims{N}}) where {N} = Array{val_gentype(X), N} #### BitArray default_sampling(::Type{<:BitArray}) = Uniform(Bool) -maketype(::Type{BitArray{N}}, _, ::Dims{N}) where {N} = BitArray{N} -maketype(::Type{BitArray}, _, ::Dims{N}) where {N} = BitArray{N} +maketype(::Type{BitArray{N}}, _, ::Distribution{Dims{N}}) where {N} = BitArray{N} +maketype(::Type{BitArray}, _, ::Distribution{Dims{N}}) where {N} = BitArray{N} #### sparse vectors & matrices @@ -482,6 +499,13 @@ make(T::Type{SparseMatrixCSC}, p::AbstractFloat, d1::Integer, d2::Integer) = mak make(T::Type{SparseVector}, p::AbstractFloat, dims::Dims{1}) = make(T, default_sampling(T), p, dims) make(T::Type{SparseMatrixCSC}, p::AbstractFloat, dims::Dims{2}) = make(T, default_sampling(T), p, dims) +# stop the recursion +make(T::Type{<:AbstractSparseArray}, X, p::AbstractFloat, dims::Dims) = + Make{maketype(T, X, p, dims)}(X, p, dims) + +make(T::Type{<:AbstractSparseArray}, ::Type{X}, p::AbstractFloat, dims::Dims) where {X} = + Make{maketype(T, X, p, dims)}(X, p, dims) + Sampler(RNG::Type{<:AbstractRNG}, c::Make3{A}, n::Repetition) where {A<:AbstractSparseArray} = SamplerTag{Cont{A}}((sp = sampler(RNG, c[1], n), diff --git a/test/runtests.jl b/test/runtests.jl index 351742e..8e50e5d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -79,7 +79,20 @@ const spString = Sampler(MersenneTwister, String) a = rand(rng..., Int8, A, 10) @test a isa Vector{AT} @test all(in(rInt8), a) + a = rand(rng..., Int8, A, 10:20) + @test a isa Vector{AT} + @test all(in(rInt8), a) + @test length(a) ∈ 10:20 end + a = rand(rng..., Int8, Array, make(Tuple, 1:3, 1:4)) + @test a isa Matrix{Int8} + @test all(issubset.(size(a), (1:3, 1:4))) + a = rand(rng..., Int8, Array, 3, 1:4) + @test a isa Matrix{Int8} + @test all(issubset.(size(a), (3:3, 1:4))) + a = rand(rng..., Array, 1:3, 4) + @test a isa Matrix{Float64} + @test all(issubset.(size(a), (1:3, 4:4))) end # Set @@ -478,24 +491,36 @@ end @testset "rand(make(Array/BitArray, ...))" begin for (T, Arr) = (Bool => BitArray, Float64 => Array{Float64}), k = ([], [T], [Bernoulli(T, 0.3)]), - (d, dim) = ([(6,)] => 1, - [(2,3)] => 2, - [6] => 1, - [2, 3] => 2, - [Int8(2), Int16(3)] => 2), + (needk, d, sz, dim) = ((false, [(6,)], (6,), 1), + (false, [(2,3)], (2,3), 2), + (false, [6], (6,), 1), + (false, [2, 3], (2, 3), 2), + (false, [Int8(2), Int16(3)], (2, 3), 2), + + (false, [make(Tuple, 1:3)], (1:3,), 1), + (false, [make(Tuple, 1:3, 1:9)], (1:3, 1:9), 2), + (true, [1:3], (1:3,), 1), + (true, [1:3, Uniform(1:9)], (1:3, 1:9), 2), + (true, [1:3, 9], (1:3, 9), 2), +# (true, [0x1:0x3, 9], (1:3, 9), 2), + (true, [3, Uniform(1:9)], (3, 1:9), 2), + ), A = (T == Bool ? (BitArray, BitArray{dim}) : (Array, Array{Float64}, Array{Float64,dim}, Array{U,dim} where U)) + needk && isempty(k) && continue s = rand(make(A, k..., d...)) @test s isa Arr{dim} - @test length(s) == 6 + @test all(issubset.(size(s), sz)) end @test_throws MethodError rand(make(Matrix, 2)) @test_throws MethodError rand(make(Vector, 2, 3)) @test_throws MethodError rand(make(BitMatrix, 2)) @test_throws MethodError rand(make(BitVector, 2, 3)) + @test_throws ArgumentError rand(make(Vector, Int, 2, 'a':'c')) + @test rand(make(Array, spString, 9)) isa Array{String} @test rand(make(BitArray, Sampler(MersenneTwister, [0, 0, 0, 1]), 9)) isa BitArray # TODO: below was testing without explicit `Array` as 1st argument, so this test