Slice Sampling

SliceSampling.jl provides a family of derivative-free slice samplers. Once using SliceSampling, they can sample any compiled BUGSModel, so you get gradient-free inference without configuring an AD backend. This is handy for models with awkward geometry, non-differentiable pieces, or as a robust component sampler inside JuliaBUGS.Gibbs.

Slice samplers are used two ways:

  • Standalone — pass a slice sampler to AbstractMCMC.sample to update the whole model.
  • Inside JuliaBUGS.Gibbs — assign a slice sampler to one or more variable groups in the sampler_map; JuliaBUGS drives it through the Gibbs sweep.

Setup

using JuliaBUGS
using JuliaBUGS: Gibbs
using SliceSampling
using AbstractMCMC
using MCMCChains
using OrderedCollections: OrderedDict

model_def = @bugs begin
    mu ~ dnorm(0, 0.0001)
    y ~ dnorm(mu, 1)
end
model = model_def((; y = 1.5))
BUGSModel (parameters are in transformed (unconstrained) space, with dimension 1):

  Model parameters:
    mu

  Variable sizes and types:
    y: type = Float64
    mu: type = Float64

Standalone sampling

Univariate slice samplers such as SliceSteppingOut and SliceDoublingOut update one coordinate at a time. On a single-parameter model you can pass one directly:

chain = AbstractMCMC.sample(
    model,
    SliceSteppingOut(1.0),   # 1.0 is the initial slice window width
    500;
    chain_type = Chains,
    progress = false,
)
summarystats(chain)
Summary Statistics

  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e ⋯
      Symbol   Float64   Float64   Float64       Real    Float64   Float64     ⋯

          mu    1.2611    1.9714    0.1312   426.3167   235.6800    1.0023     ⋯

                                                                1 column omitted

No initial_params is required: the extension implements SliceSampling.initial_sample, which seeds the sampler from the model's current parameter values.

For a model with more than one parameter, wrap a univariate sampler in a multivariate strategy (e.g. RandPermGibbs, which cycles through coordinates in a random order), or use a genuinely multivariate slice sampler:

multi_def = @bugs begin
    a ~ dnorm(0, 1)
    b ~ dnorm(0, 1)
    y ~ dnorm(a + b, 1)
end
multi_model = multi_def((; y = 0.5))

multi_chain = AbstractMCMC.sample(
    multi_model,
    RandPermGibbs(SliceSteppingOut(1.0)),
    500;
    chain_type = Chains,
    progress = false,
)
summarystats(multi_chain)
Summary Statistics

  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e ⋯
      Symbol   Float64   Float64   Float64       Real    Float64   Float64     ⋯

           b    0.2249    0.8074    0.0571   202.1302   245.3813    0.9994     ⋯
           a    0.1198    0.8243    0.0531   241.1146   262.6275    1.0062     ⋯

                                                                1 column omitted

Inside Gibbs

Because each single-site conditional in JuliaBUGS.Gibbs is univariate, a bare univariate slice sampler works per site — no multivariate wrapper needed. Map variable groups to samplers in an OrderedDict:

sampler_map = OrderedDict(
    @varname(a) => SliceSteppingOut(1.0),
    @varname(b) => SliceSteppingOut(1.0),
)
gibbs = Gibbs(multi_model, sampler_map)

gibbs_chain = AbstractMCMC.sample(
    multi_model,
    gibbs,
    500;
    chain_type = Chains,
    progress = false,
)
summarystats(gibbs_chain)
Summary Statistics

  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e ⋯
      Symbol   Float64   Float64   Float64       Real    Float64   Float64     ⋯

           b    0.0753    0.7751    0.0490   249.9038   296.7838    1.0012     ⋯
           a    0.2623    0.7823    0.0463   285.6554   216.9296    0.9983     ⋯

                                                                1 column omitted

Slice samplers can be freely mixed with HMC and AdvancedMH samplers in the same sampler_map. They update continuous blocks. Gibbs updates finite discrete blocks exactly by enumerating and normalizing their full conditional, without invoking SliceSampling or AdvancedMH.

Output formats and statistics

Both chain backends are supported:

  • chain_type = Chains returns an MCMCChains.Chains (shown above).
  • chain_type = VNChain returns a FlexiChains.FlexiChain keyed by VarName (requires using FlexiChains):
using FlexiChains
chain = AbstractMCMC.sample(model, SliceSteppingOut(1.0), 500; chain_type = VNChain, progress = false)

In either case the sampler's own statistics are recorded alongside the draws: the log density lp and num_proposals (the number of proposals the slice sampler evaluated per step). Under MCMCChains these appear as internals; under FlexiChains they are FlexiChains.Extra entries. For a multivariate sampler the per-coordinate num_proposals is flattened into num_proposals[i] columns for MCMCChains.