Skip to content

StanBlocks.jl

StanBlocks.jl is a (limited) Julia-to-Stan transpiler: write probabilistic models in Julia syntax and automatically generate compilable Stan code.

Caveats This transpiler has many caveats. See Caveats below. :::

Quick Start

julia
using StanBlocks
import StanLogDensityProblems, JSON

# Model definition (no data bound yet)
earn_height_model = @slic begin
    beta  ~ flat(;n=2)
    sigma ~ flat(;lower=0.)
    earn  ~ normal(beta[1] + beta[2] * to_vector(height), sigma)
end

# Bind data
earn_height_posterior = earn_height_model(; earn, height)

# Inspect the generated Stan code
println(stan_code(earn_height_posterior))

# Compile and instantiate (requires StanLogDensityProblems.jl + JSON.jl)
earn_height_problem = stan_instantiate(earn_height_posterior)

Key Features

Activity Analysis

StanBlocks automatically determines which Stan block each variable belongs to:

BlockDescription
dataPassed in from Julia (observed quantities)
transformed_dataComputed once from data (e.g. log_y = log.(y))
parametersSampled by HMC; determine posterior dimension
transformed_parametersDeterministic functions of parameters, evaluated each gradient step
generated_quantitiesComputed per sample, do not enter the likelihood

Type Inference

Stan requires explicit types and shapes. StanBlocks infers them automatically from passed values and from the model structure. Shapes extracted from data are exposed as integer data in the generated Stan code.

User-Defined Functions

The @deffun macro extends the Stan function library with variadic signatures and limited dynamic dispatch:

julia
@deffun begin
    my_normal_lpdf(y, mu, sigma) = normal_lpdf(y, mu, sigma)
    my_normal_rng(mu, sigma)::real = normal_rng(mu, sigma)

    # Dispatch on function-argument type (higher-order functions)
    dispatch_lpdf(y, ::typeof(my_normal), args...) = my_normal_lpdf(y, args...)
end

Constraints

Constraints are specified as keyword arguments to distributions. Many distributions (e.g. beta, gamma) infer their bounds automatically:

julia
model = @slic (;y) begin
    sigma ~ std_normal(;lower=0.)   # half-normal (explicit lower bound)
    theta ~ beta(1., 1.)            # [0,1] bounds inferred automatically
    y ~ normal(0., sigma)
end

Sub-Models and Composition

Models can be composed by referencing one model inside another:

julia
prior = @slic begin
    mu  ~ std_normal()
    tau ~ std_normal(;lower=0.)
end

hierarchical = @slic (;y) begin
    theta ~ prior()
    y ~ normal(theta, 1.)
end

Caveats

Constant Terms in Log-Density

Stan's ~ statement drops constant terms that do not depend on model parameters. StanBlocks does not drop constants—it always includes the full log-density. This means the absolute value of the log-density will generally differ from a hand-written Stan model, but the posterior geometry is identical and sampling is unaffected.

Limited Coverage

Not all Julia constructs can be transpiled. In particular, arbitrary Julia functions (not defined via @deffun) cannot be transpiled automatically.

Name Resolution

User-defined functions and sub-models currently need to be defined in Main.

See Also

You are viewing the dev branch. This branch may include code written with Claude Code with less human supervision. Only human-approved code is merged into main.