User guide#

The Sequential Monte Carlo (SMC) loop in sequince is built from a set of composable steps. Each step inspects the current ParticleState, optionally consumes observations and shared configuration, and returns an updated state. At the end of each loop iteration, a set of callbacks are executed that can perform operations like logging, diagnostics, and plotting. The SequentialMonteCarlo class drives the loop by sequentially executing the configured steps and callbacks at every iteration.

Configuring the runner#

The runner accepts a steps sequence containing callables that conform to the SMCStep protocol defined in sequince.steps.base. Similarly, the runner accepts a sequence of optional callbacks that adhere to the SMCCallback protocol in sequince.callbacks. The quickest way to build a useful pipeline is to reuse the pre-built helper:

from sequince import make_standard_bayesian_smc
from sequince.callbackss import TracePlot
smc, posterior, shared_context = make_standard_bayesian_smc(
    log_prior,
    log_likelihood,
    mutation="emcee",
    callbacks = [TracePlot()],  # Optional
)

make_standard_bayesian_smc wires together AdaptiveTemperedWeighting, SystematicResampling, and a mutation step chosen via the mutation argument. Pass mutation="minipcn" to opt into the MiniPCNMutation transition kernel or mutation="none" to skip rejuvenation.

BlackJAX mutation#

Install the optional BlackJAX integration with uv add "sequince[blackjax]". BlackJAX mutation requires JAX-backed particles and JAX-compatible target functions. NUTS is the default algorithm:

import jax.numpy as jnp

smc, posterior, metadata = make_standard_bayesian_smc(
    log_prior,
    log_likelihood,
    mutation="blackjax",
    mutation_n_steps=20,
    mutation_kwargs={
        "algorithm": "nuts",
        "algorithm_kwargs": {
            "step_size": 0.1,
            "inverse_mass_matrix": jnp.ones(n_dimensions),
        },
    },
)

algorithm may also be "hmc", "random-walk", or a callable that constructs a BlackJAX sampling algorithm from the current scalar log-density. Acceptance and divergence diagnostics are returned in the mutation step output under "acceptance" and "divergence". Per-iteration step outputs are available through SMCResult.outputs.

If you need full control, construct SequentialMonteCarlo manually by instantiating step and callback objects and supplying them in the desired order:

from sequince import SequentialMonteCarlo
from sequince.steps import (
    AdaptiveTemperedWeighting,
    SystematicResampling,
    EmceeMutation,
)
from sequince.callbacks import TracePlot

smc = SequentialMonteCarlo(
    steps=[
        AdaptiveTemperedWeighting(posterior, target_ess_ratio=0.8),
        SystematicResampling(always_resample=True),
        EmceeMutation(posterior, n_steps=10),
    ],
    callbacks=[TracePlot()],  # Optional
)

Working with particle state#

ParticleState stores the particle locations, log-weights, accumulated log-evidence, and auxiliary metadata. A few convenience methods are helpful when implementing new steps:

ParticleState.normalize()

Returns a fresh state with renormalised weights while keeping the original untouched.

ParticleState.resample(indices)

Constructs a new state from ancestor indices, resetting the weights to a uniform distribution.

ParticleState.with_updates(**kwargs)

Produces a copy with selected fields replaced.

When possible, treat the state as immutable and return a new instance instead of mutating in place. This keeps the iteration trace recorded in SMCResults consistent and easier to debug.

Incorporating observations#

The runner accepts an optional iterable of observations. Each iteration pulls the next observation (if available) and exposes it through the StepContext supplied to steps. This allows for particle filtering style updates where likelihood evaluations depend on time indexed data.

Custom steps and callbacks#

To implement bespoke step logic, derive from SMCStep. Override the apply method to perform work and return (state, context, output). StepContext.shared contains read-only user data, while current cross-step coordination values live in StepContext.metadata. Update metadata with context.with_metadata(...) and return the replacement context. Historical diagnostics belong in the explicit step output; the runner stores ordered per-iteration output tuples on SMCResult.

Similarly, custom callbacks can be implemented by sub-classing SMCCallback and overriding the apply method Callbacks are provided with the current state, context, and result history. An observational callback returns None; a callback that updates metadata or stop controls returns a replacement context.

See sequince.steps and sequince.callbacks for a catalogue of the built-in components.