Skip to content

API Reference

Model Definition

StanBlocks.@slic Macro

Defines SlicModels (see test/slic.jl for usage examples).

The defining module is captured automatically via __module__, so that @deffun functions defined in the same module (e.g. a package extension) are found during symbol resolution.

A leading string literal inside the begin ... end block is captured as the model docstring and rendered as a // ... comment header in the generated Stan code.

source
StanBlocks.@deffun Macro
julia
@deffun function_definition

Define a Stan-compatible function with type inference and code generation.

Parses a Julia-style function definition (with type-annotated arguments and return type), generates the corresponding Stan function, and registers type-inference signatures so the transpiler can propagate types through calls to this function.

For functions ending in _lpdf/_lpmf/_lcdf/_lccdf, the return type is automatically set to real and companion _lpdfs/_rng stubs are generated for use in generated_quantities.

UDF bodies must not contain ~ sampling statements or target += increments — UDFs cannot introduce parameters or directly manipulate the log density. The macro errors at expansion time if either is found.

Standard Julia macros inside the body are expanded against the calling module before tracing, so @views, @., @inbounds, and user-defined macros work transparently.

Inlining

Annotating with @inline (or giving the function a Julia-convention trailing ! in its name) causes every call to be expanded at the call site instead of producing a Stan function:

julia
@deffun @inline scale(x::vector[n], s::real)::vector[n] = x * s
@deffun set_first!(buf::vector[n])::vector[n] = (buf[1] = 42.; buf)

Inline UDFs do not appear in Stan's functions {} block. Multi-statement bodies, vararg parameters, and higher-order function arguments are all supported. Locals are renamed per call site, and pre-statements hoist into the enclosing block.

@inline cannot be combined with @lhs / @lpxf.

Example

julia
@deffun garch11_lpdf(y::vector[T], mu::real, alpha0::real, alpha1::real, beta1::real)::real = begin
    sigma2 = alpha0
    rv = 0.
    for t in 1:T
        rv += normal_lpdf(y[t], mu, sqrt(sigma2))
        sigma2 = alpha0 + alpha1 * square(y[t] - mu) + beta1 * sigma2
    end
    return rv
end

See src/slic_stan/builtin.jl for many more examples.

source
StanBlocks.@defsig Macro

Utility macro to define function signatures (see src/slic_stan/builtin.jl for usage examples).

Note:

This macro is mainly useful for bulk built-in function signature definitions. StanBlocks.jl users should generally prefer using @deffun.

source
StanBlocks.@usertype Macro
julia
@usertype struct RaggedVector
    mem  :: vector
    ends :: int[]
end

Declare a custom Stan-renderable record type. Lowers to a real Julia struct whose abstract supertype is StanBlocks.stan.types.usertype (added automatically); field type annotations are SLIC types and are kept only for documentation — fields are stored as Any so plain Julia construction works for data plumbing. Method dispatch on the type tag (Base.length(r::RaggedVector)) works via standard Julia. Stan-side, values render as positional tuples; field access (r.mem) reuses the existing ntup machinery.

source

Sampling-form Dispatch

StanBlocks.@lpxf Macro
julia
@lpxf foo_lpdf
@lpxf begin foo_lpdf; bar_lpmf end

Register the three SLIC dispatch hooks (lpxf_expr, rng_expr, likelihood_expr) for one or more user-defined log-probability functions.

The argument(s) must be bare symbols ending in _lpdf, _lpmf, _lcdf, or _lccdf. For each foo_lpdf (or _lpmf/etc.), the macro emits the registrations:

julia
StanBlocks.lpxf_expr(::typeof(foo))       = foo_lpdf
StanBlocks.rng_expr(::typeof(foo))        = foo_rng
StanBlocks.likelihood_expr(::typeof(foo)) = foo_lpdfs

The companion foo_rng and foo_lpdfs (resp. _lpmfs/_lcdfs/_lccdfs) names must already exist when the registrations execute. This macro does not parse function bodies and does not wrap @deffun.

source
StanBlocks.@lhs Macro
julia
@lhs foo_lpdf(y::T, args...) = body

Inside a @deffun block, opt this method into base-level LHS inference. Without @lhs, only the _lpdf-keyed tracetype is registered (so the method dispatches when called explicitly), but lhs ~ foo(args...) cannot trace because the base foo has no tracetype keyed on its argument signature. @lhs registers tracetype(::CanonicalExpr{<:typeof(foo), <:Tuple{lhs_type[2:end]...}}) so the sampling form works.

Compose with @lpxf (any order — @lhs @lpxf … or @lpxf @lhs …) to also register the dispatch hooks for foo/foo_rng/foo_lpdfs.

Standalone @lhs (outside @deffun) is not supported and errors immediately.

source

Runtime Assertions

StanBlocks.@stan_assert Macro
julia
@stan_assert cond
@stan_assert cond message

Stan-compatible runtime assertion. Expands to if !cond; reject(msg); end, where Stan's reject aborts the current MCMC proposal with the message. Without an explicit message, a default "assertion failed: <cond>" is used.

Use inside @deffun bodies (control flow is not allowed in @slic model bodies — wrap the check in a helper if needed at the model level).

Example

julia
@deffun safe_log(x::real)::real = begin
    @stan_assert x > 0 "safe_log: argument must be positive"
    return log(x)
end
source

Model Inspection and Compilation

StanBlocks.stan_code Function
julia
stan_code(model) -> String

Return the generated Stan source for model (a SlicModel or StanModel) as a plain String.

For a SlicModel, tracing runs first via stan_model; for an already-traced StanModel, only the rendering pass runs. Output covers the full Stan program — data, transformed_data, parameters, transformed_parameters, model, and generated_quantities blocks — with automatic block placement applied.

Pair with transpiles for boolean smoke tests and stan_instantiate to compile the generated code via BridgeStan.

source
StanBlocks.stan_model Function
julia
stan_model(slic::SlicModel) -> StanModel

Trace slic end-to-end (forward / backward / distribute passes), returning the inferred StanModel — a fully resolved representation of the Stan blocks plus the data dictionary.

Tracing is the expensive step. A StanModel is cheap to re-data: call model(; new_kwargs...) to swap the data dict without re-tracing. Use this in preference to repeatedly calling stan_instantiate on the original SlicModel.

Errors during tracing are wrapped in a StanBlocksError tagged with phase = :transpile.

source
StanBlocks.stan_instantiate Function
julia
stan_instantiate(model; kwargs...) -> StanProblem

Exported alias for StanBlocks.instantiate. Compiles model (a SlicModel or StanModel) via BridgeStan and returns a StanLogDensityProblems.StanProblem implementing the LogDensityProblems interface. See instantiate for the full kwarg list.

source
StanBlocks.instantiate Function
julia
instantiate(model; nan_on_error=true, make_args=["STAN_THREADS=true"], warn=false, path=…) -> StanProblem
stan_instantiate(model; ...) -> StanProblem

Compile model (a SlicModel or StanModel) via BridgeStan and return a StanLogDensityProblems.StanProblem. The returned value implements the LogDensityProblems interface — call LogDensityProblems.dimension, logdensity, and logdensity_and_gradient on it.

stan_instantiate is an exported alias for instantiate.

Keyword arguments

  • path::AbstractString — where to write the .stan file. Defaults to "tmp/<hash>.stan", so identical generated code is cached on disk.

  • nan_on_error::Bool = true — make BridgeStan return NaN instead of throwing on evaluation failures.

  • make_args::Vector{String} = ["STAN_THREADS=true"] — extra arguments forwarded to Stan's make.

  • warn::Bool = false — forwarded to BridgeStan.

Errors during compilation are wrapped in a StanBlocksError tagged with phase = :compile.

source

Smoke Tests

StanBlocks.transpiles Function
julia
transpiles(model; re=true) -> Bool

Return true if model (a SlicModel or StanModel) successfully transpiles to Stan source via stan_code, false otherwise.

This is the fast smoke-test for a model — it exercises the full SLIC tracing pipeline but stops short of invoking stanc / BridgeStan.

Set re=false to swallow the transpilation error and just return false (useful for batch regression dashboards); the default re=true rethrows so failures surface with their normal StanBlocksError trace.

source
StanBlocks.compiles Function
julia
compiles(model; re=true) -> Bool

Return true if model (a SlicModel or StanModel) successfully transpiles and compiles via BridgeStan (i.e. stan_instantiate succeeds), false otherwise.

Strictly stronger than transpiles: a model that transpiles can still fail to compile if stanc rejects the generated Stan or the C++ build fails.

Set re=false to swallow the error and just return false; the default re=true rethrows.

source
StanBlocks.stanc_check Function
julia
stanc_check(stan_code::AbstractString; warn_pedantic=true) -> (ok::Bool, output::String)

Run the stanc compiler on stan_code (written to a temporary file) and return whether it accepted the code plus any compiler output (warnings, errors). Binary discovery: $STANC_PATH env var → BridgeStan's bin/stanc. Errors if neither is available.

source

Types

StanBlocks.SlicModel Type

The AST and the data, pre-tracing. Can be instantiated via stan_instantiate.

The mod field stores the defining module (set automatically by @slic), used for symbol resolution during tracing — functions defined via @deffun in package extensions are found by checking mod before falling back to Main.

Warning:

Repeatedly instantiating SlicModels is inefficient, as the tracing is redone for every instantiation. Instead, get the StanModel first (via model = stan_model(slic_model)) and update its data (via new_model = model(;x=new_x)).

source
StanBlocks.StanModel Type

The inferred Stan model, post-tracing. Can be instantiated via stan_instantiate.

source

Errors

StanBlocks.StanBlocksError Type
julia
StanBlocksError <: Exception

Wraps errors that occur during transpilation, compilation, or evaluation of Stan models.

Fields

  • phase::Symbol: the pipeline stage where the error occurred (:transpile, :compile, or :evaluate)

  • context::String: a description of what was being processed (e.g. "model: eight_schools")

  • cause::Any: the underlying error, typically an (exception, backtrace) tuple

source
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.