Turing.jl: UniformDraw distribution?

Is there a “UniformDraw” distribution in Turing.jl / Distributions.jl, i.e. given an array of element, the distribution selects one of the elements randomly.

Example:

x = [1,10,32,100]
val ~ UniformDraw(x)

val is then 1, 10, 32, 100 randomly selected.

I can implement it naively like this using DiscreteUniform (including a version that takes a list of percentages/weights using Categorical).

"""
Return a normalized vector, i.e. where the sum is 1.
"""
using Turing

function simplex(v)
    return v./sum(v)
end

@model function uniformDrawTest(x,pcts=[1,2,3,4])
    # Select value uniformly
    function uniformDraw(x)
        n = length(x)
        ix1 ~ DiscreteUniform(1,n)
        return x[ix1]
    end
    # Select a value based on probabilities in pcts
    function uniformDraw(x,pcts)
        n = length(x)
        ix2 ~ Categorical(simplex(pcts))
        return x[ix2]
    end
    val1 ~ Dirac(uniformDraw(x))
    val2 ~ Dirac(uniformDraw(x,pcts)
    val3 ~ Dirac(uniformDraw(x))   # will be same as val1
end

x = [1,10,32,100]
pcts = [1,2,3,4]
model = uniformDrawTest(x,pcts)

chns = sample(model, PG(15), 10_000)
display(chns)

There are at least two drawbacks with this:

  • I have to wrap the result with Dirac.
  • But more seriously is that the indices (ix1 and ix2) are both global in the model, so val1 and val3 will always be the same value.

It’s already available in Distributions.jl as DiscreteNonParametric :slight_smile:

julia> using Distributions

julia> d = DiscreteNonParametric([1,10,32,100], ones(4) ./ 4);

julia> rand(d)
32

julia> rand(d)
32

julia> rand(d)
100

julia> rand(d)
100

julia> rand(d)
10

julia> logpdf(d, 10)
-1.3862943611198906

julia> logpdf(d, 100)
-1.3862943611198906

If you want, you can define the following constructor:

julia> UniformDraw(xs) = DiscreteNonParametric(xs, ones(length(xs)) ./ length(xs))
UniformDraw (generic function with 1 method)

julia> d = UniformDraw([1,10,32,100])
DiscreteNonParametric{Int64, Float64, Vector{Int64}, Vector{Float64}}(support=[1, 10, 32, 100], p=[0.25, 0.25, 0.25, 0.25])

julia> rand(d)
1
2 Likes

Thanks @torfjelde !

That was exactly what I looked for. (I must have search in an old Distribution documentation.)