From 867b5d20a8f4d8fa8601badc7c862b78916c67f1 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Tue, 4 Jun 2019 12:17:18 +0200 Subject: [PATCH 1/3] introduce a Pair API (alternative to make) --- README.md | 16 ++++++++++++++++ src/sampling.jl | 23 ++++++++++++++++++++++- test/runtests.jl | 12 ++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 11a07aa..d6c0811 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,16 @@ is omitted). For example, `rand(make(Array, 2, 3), 3)` creates an array of matri Of course, `make` is not necessary, in that the same can be achieved with an ad hoc `struct`, which in some cases is clearer (e.g. `Normal(m, s)` rather than something like `make(Float64, Val(:Normal), m, s)`). +As an experimental feature, the following alternative API is available: +- `rand(T => x)` is equivalent to `rand(make(T, x))` +- `rand(T => (x, y, ...))` is equivalent to `rand(make(T, x, y, ...))` + +This is for convenience only (it may be more readable), but may be less efficient due to the +fact that the type of a pair containing a type doesn't know this exact type (e.g. `Pair => Int` +has type `Pair{UnionAll,DataType}`), so `rand` can't infer the type of the generated value. +Thanks to inlining, the inferred types can however be sufficiently tight in some cases +(e.g. `rand(Complex => Int, 3)` is of type `Vector{Complex{Int64}}` instead of `Vector{Any}`). + Point 3) allows something like `rand(1:30, Set, 10)` to produce a `Set` of length `10` with values from `1:30`. The idea is that `rand([rng], [S], Cont, etc...)` should always be equivalent to `rand([rng], make(Cont, [S], etc...))`. This design goes somewhat against the trend in `Base` to create @@ -204,6 +214,12 @@ julia> collect(Iterators.take(Uniform(1:10), 3)) # distributions can be iterated 7 10 5 + +julia> rand(Complex => Int) # equivalent to rand(make(Complex, Int)) (experimental) +4610038282330316390 + 4899086469899572461im + +julia> rand(Pair => (String, Int8)) # equivalent to rand(make(Pair, String, Int8)) (experimental) +"ODNXIePK" => 4 ``` In some cases, the `Rand` iterator can provide efficiency gains compared to diff --git a/src/sampling.jl b/src/sampling.jl index d6a26a2..b85f40b 100644 --- a/src/sampling.jl +++ b/src/sampling.jl @@ -154,12 +154,14 @@ Sampler(RNG::Type{<:AbstractRNG}, c::Categorical, n::Repetition) = ## random elements from pairs +#= disabled in favor of a special meaning for pairs + Sampler(RNG::Type{<:AbstractRNG}, t::Pair, n::Repetition) = SamplerSimple(t, Sampler(RNG, Bool, n)) rand(rng::AbstractRNG, sp::SamplerSimple{<:Pair}) = @inbounds return sp[][1 + rand(rng, sp.data)] - +=# ## composite types @@ -559,3 +561,22 @@ let b = UInt8['0':'9';'A':'Z';'a':'z'], rand(rng::AbstractRNG, sp::SamplerTag{Cont{String}}) = String(rand(rng, sp.data.first, sp.data.second)) end + + +## X => a / X => (a, as...) syntax as an alternative to make(X, a) / make(X, a, as...) + +# this is experimental + +@inline Sampler(RNG::Type{<:AbstractRNG}, (a, b)::Pair{<:Union{DataType,UnionAll}}, + r::Repetition) = + b isa Tuple ? + Sampler(RNG, make(a, b...), r) : + Sampler(RNG, make(a, b), r) + +# nothing can be inferred when only the pair type is available +@inline gentype(::Type{<:Pair{<:Union{DataType,UnionAll}}}) = Any + +@inline gentype((a, b)::Pair{<:Union{DataType,UnionAll}}) = + b isa Tuple ? + gentype(make(a, b...)) : + gentype(make(a, b)) diff --git a/test/runtests.jl b/test/runtests.jl index 95709e9..e6da225 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -280,11 +280,13 @@ end @test fieldtype(typeof(a), 2) == UInt64 end +#= @testset "rand(::Pair)" begin @test rand(1=>3) ∈ (1, 3) @test rand(1=>2, 3) isa Vector{Int} @test rand(1=>'2', 3) isa Vector{Union{Char, Int}} end +=# @testset "rand(::AbstractFloat)" begin # check that overridden methods still work @@ -565,6 +567,16 @@ end @test rand(make(Float64)) isa Float64 end +@testset "rand(T => x) & rand(T => (x, y, ...))" begin + @test rand(Complex => Int) isa Complex{Int} + @test rand(Pair => (String, Int8)) isa Pair{String,Int8} + @test_throws ArgumentError rand(1=>2) # should not call rand(make(1, 2)) + + @test rand(Complex => Int, 3) isa Vector{Complex{Int}} + @test rand(Pair => (String, Int8), Set, 3) isa Set{Pair{String,Int8}} +end + + ## @rand struct Die From c15362d5cebb44cc592dc49125deb57aa46d9821 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Wed, 9 Sep 2020 20:09:51 +0200 Subject: [PATCH 2/3] Pair API: enable recursive pairs --- src/sampling.jl | 19 +++++++++++-------- test/runtests.jl | 3 +++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/sampling.jl b/src/sampling.jl index b85f40b..c0c40e8 100644 --- a/src/sampling.jl +++ b/src/sampling.jl @@ -567,16 +567,19 @@ end # this is experimental -@inline Sampler(RNG::Type{<:AbstractRNG}, (a, b)::Pair{<:Union{DataType,UnionAll}}, - r::Repetition) = +pair_to_make((a, b)::Pair) = b isa Tuple ? - Sampler(RNG, make(a, b...), r) : - Sampler(RNG, make(a, b), r) + make(a, map(pair_to_make, b)...) : + make(a, pair_to_make(b)) + +pair_to_make(x) = x + +@inline Sampler(RNG::Type{<:AbstractRNG}, p::Pair{<:Union{DataType,UnionAll}}, + r::Repetition) = + Sampler(RNG, pair_to_make(p), r) # nothing can be inferred when only the pair type is available @inline gentype(::Type{<:Pair{<:Union{DataType,UnionAll}}}) = Any -@inline gentype((a, b)::Pair{<:Union{DataType,UnionAll}}) = - b isa Tuple ? - gentype(make(a, b...)) : - gentype(make(a, b)) +@inline gentype(p::Pair{<:Union{DataType,UnionAll}}) = + gentype(pair_to_make(p)) diff --git a/test/runtests.jl b/test/runtests.jl index e6da225..fcf3900 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -574,6 +574,9 @@ end @test rand(Complex => Int, 3) isa Vector{Complex{Int}} @test rand(Pair => (String, Int8), Set, 3) isa Set{Pair{String,Int8}} + + nt = rand(NTuple{4} => Complex => 1:3) + @test nt isa NTuple{4,Complex{Int64}} end From 71899e8fda9c83d4cfbe368cd6fc2a19c7d92183 Mon Sep 17 00:00:00 2001 From: Rafael Fourquet Date: Wed, 9 Sep 2020 20:19:52 +0200 Subject: [PATCH 3/3] Pair API: allow any type of Pair Do not restrict the first argument to be a `DataType`/`UnionAll`, as `make` is about to start accepting non-type objects as first argument. --- src/sampling.jl | 10 ++++------ test/runtests.jl | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/sampling.jl b/src/sampling.jl index c0c40e8..c20f0ed 100644 --- a/src/sampling.jl +++ b/src/sampling.jl @@ -574,12 +574,10 @@ pair_to_make((a, b)::Pair) = pair_to_make(x) = x -@inline Sampler(RNG::Type{<:AbstractRNG}, p::Pair{<:Union{DataType,UnionAll}}, - r::Repetition) = - Sampler(RNG, pair_to_make(p), r) +@inline Sampler(RNG::Type{<:AbstractRNG}, p::Pair, r::Repetition) = + Sampler(RNG, pair_to_make(p), r) # nothing can be inferred when only the pair type is available -@inline gentype(::Type{<:Pair{<:Union{DataType,UnionAll}}}) = Any +@inline gentype(::Type{<:Pair}) = Any -@inline gentype(p::Pair{<:Union{DataType,UnionAll}}) = - gentype(pair_to_make(p)) +@inline gentype(p::Pair) = gentype(pair_to_make(p)) diff --git a/test/runtests.jl b/test/runtests.jl index fcf3900..4912be8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -570,7 +570,7 @@ end @testset "rand(T => x) & rand(T => (x, y, ...))" begin @test rand(Complex => Int) isa Complex{Int} @test rand(Pair => (String, Int8)) isa Pair{String,Int8} - @test_throws ArgumentError rand(1=>2) # should not call rand(make(1, 2)) + @test_throws MethodError rand(1=>2) # calls rand(make(1, 2)) @test rand(Complex => Int, 3) isa Vector{Complex{Int}} @test rand(Pair => (String, Int8), Set, 3) isa Set{Pair{String,Int8}}