On Slack, Jonatan Werpers asks,
How can I use a custom prior with a model? Basically I want to do something like:
my_pdf(θ) = .... m = @model n begin θ ~ my_pdf x ~ Bernoulli(θ) |> iid(n) end
Generally, you’ll need three methods:
Base.rand
Distributions.logpdf
Soss.xform
For example, say we want the logistic transform of a Normal. We could do this entirely in Soss, but for now (due to Distribution.jl overhead), there may be a performance advantage to building it directly.
Starting out is pretty easy:
using Soss
import Base: rand
struct MyPrior end
Base.rand(::MyPrior) = logistic(randn())
Distributions.logpdf(MyPrior, x) = logistic(-x^2/2)
Note that there’s no need for the logpdf
to be an actual logpdf - normalization is irrelevant for many applications. We’ll soon be working instead in terms of measures, to avoid the extra computation when possible.
Soss.xform
needs to return a transform in the sense of @Tamas_Papp’s TransformVariables.jl. For univariate cases this is especially simple:
Soss.xform(::MyPrior, _data=NamedTuple()) = as𝕀
The _data
optional argument is just to make the dispatch work out, and it’s almost always fine to have it exactly like this.
Then we can do, e.g.,
julia> post = dynamicHMC(m(n=100), (x=x,));
julia> particles(post)
(θ = 0.245 ± 0.043,)
julia> m = @model n begin
θ ~ MyPrior()
x ~ Bernoulli(θ) |> iid(n)
end;
julia> x = rand(100) .< 0.3;
julia> post = dynamicHMC(m(n=100), (x=x,));
julia> particles(post)
(θ = 0.314 ± 0.043,)