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

introduce a Pair API (alternative to make) #4

Merged
merged 3 commits into from
Sep 10, 2020
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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
24 changes: 23 additions & 1 deletion src/sampling.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -559,3 +561,23 @@ 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

pair_to_make((a, b)::Pair) =
b isa Tuple ?
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, r::Repetition) =
Sampler(RNG, pair_to_make(p), r)

# nothing can be inferred when only the pair type is available
@inline gentype(::Type{<:Pair}) = Any

@inline gentype(p::Pair) = gentype(pair_to_make(p))
15 changes: 15 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -565,6 +567,19 @@ 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 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}}

nt = rand(NTuple{4} => Complex => 1:3)
@test nt isa NTuple{4,Complex{Int64}}
end


## @rand

struct Die
Expand Down