Sure! A Model
looks like this:
struct Model
args :: Vector{Symbol}
body :: Vector{Statement}
end
I have a @model
macro that builds one of these from an expression. Here’s a simple example:
julia> normalModel
@model x begin
μ ~ Normal(0, 5)
σ ~ HalfCauchy(3)
x ~ Normal(μ, σ) |> iid(10)
end
julia> normalModel.args
1-element Array{Symbol,1}:
:x
julia> normalModel.body
6-element Array{Soss.Statement,1}:
Soss.LineNumber(:(#= /home/chad/git/jl/Soss/src/examples.jl:31 =#))
Soss.Follows(:μ, :(Normal(0, 5)))
Soss.LineNumber(:(#= /home/chad/git/jl/Soss/src/examples.jl:32 =#))
Soss.Follows(:σ, :(HalfCauchy(3)))
Soss.LineNumber(:(#= /home/chad/git/jl/Soss/src/examples.jl:33 =#))
Soss.Follows(:x, :(Normal(μ, σ) |> iid(10)))
Then here’s sourceRand
:
function sourceRand(m::Model)
m = canonical(m)
proc(m, st::Let) = :($(st.x) = $(st.rhs))
proc(m, st::Follows) = :($(st.x) = rand($(st.rhs)))
proc(m, st::Return) = :(return $(st.rhs))
proc(m, st::LineNumber) = nothing
body = buildSource(m, proc) |> striplines
argsExpr = Expr(:tuple,freeVariables(m)...)
stochExpr = begin
vals = map(stochastic(m)) do x Expr(:(=), x,x) end
Expr(:tuple, vals...)
end
@gensym rand
flatten(@q (
function $rand(args...;kwargs...)
@unpack $argsExpr = kwargs
$body
$stochExpr
end
))
end
buildSource
is a little helper function for this:
function buildSource(m, proc; kwargs...)
q = @q begin end
for st in m.body
ex = proc(m, st; kwargs...)
isnothing(ex) || push!(q.args, ex)
end
q
end
Oh, and seeing the result might help:
julia> sourceRand(normalModel)
:(function ##rand#368(args...; kwargs...)
@unpack () = kwargs
μ = rand(Normal(0, 5))
σ = rand(HalfCauchy(3))
x = rand(iid(10, Normal(μ, σ)))
(μ = μ, σ = σ, x = x)
end)