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   -2.3564   21.5424    5.2915    85.2672    11.6371    1.0104     ⋯

                                                                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.2279    0.7764    0.0459   289.3536   326.5058    1.0117     ⋯
           a    0.0818    0.8130    0.0458   318.6189   314.8640    0.9992     ⋯

                                                                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.1964    0.7793    0.0460   286.8711   325.3525    1.0070     ⋯
           a    0.1261    0.7743    0.0456   289.7106   312.8636    1.0145     ⋯

                                                                1 column omitted

Slice samplers can be freely mixed with other samplers (HMC, IndependentMH, …) in the same sampler_map.

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.