Standalone MCMC#
Sequince’s mutation implementations can be run as a standalone Markov chain Monte Carlo sampler, without SMC weighting or resampling. This is useful for ordinary posterior sampling and for testing a mutation configuration before using it to rejuvenate an SMC population.
Creating a sampler#
Use make_mcmc() with a vectorised log-density. Each row of
the particle array is an independent chain:
import numpy as np
from sequince import ParticleState, make_mcmc
def log_prob(particles, context):
return -0.5 * np.sum(particles**2, axis=-1)
rng = np.random.default_rng(42)
initial_state = ParticleState(
particles=rng.normal(size=(32, 2)),
)
mcmc = make_mcmc(
log_prob,
mutation="minipcn",
n_steps=1_000,
)
result = mcmc.run(initial_state, rng=rng)
n_steps is the number of MCMC transitions in the returned chain, not the
number of chains. The number of rows in initial_state.particles determines
the number of chains.
Reading chains and diagnostics#
chain has shape
(n_steps, n_chains, n_dimensions). Use samples to discard burn-in,
thin, and optionally flatten the chain axis:
chain = result.chain
samples = result.samples(burn_in=200, thin=5)
unflattened = result.samples(burn_in=200, thin=5, flat=False)
final_positions = result.final_state.particles
diagnostics = result.diagnostics
The flattened result has shape (retained_steps * n_chains, n_dimensions).
diagnostics contains the final mutation block’s backend-specific values,
such as acceptance information and the number of transitions.
Choosing a mutation backend#
make_mcmc accepts the same named mutation backends used by the standard SMC
helper: "minipcn", "emcee", "blackjax", and
"metropolis-hastings". Backend-specific settings are passed through
mutation_kwargs:
mcmc = make_mcmc(
log_prob,
mutation="metropolis-hastings",
n_steps=1_000,
mutation_kwargs={
"kernel": "gaussian-random-walk",
"kernel_kwargs": {"scale": 0.2},
},
)
See Mutation configuration for available backends, optional dependencies, and
kernel configuration. A configured
MutationStep can also be passed directly to
MarkovChainMonteCarlo; the runner enables chain storage
on that step automatically.
Context and particle transforms#
A target may omit context when it does not need run-level inputs. When it
does, supply values using shared_context just as for SMC:
def log_prob(particles, context):
scale = context.shared["scale"]
return -0.5 * np.sum((particles / scale) ** 2, axis=-1)
result = mcmc.run(
initial_state,
rng=42,
shared_context={"scale": 2.0},
)
Pass a configured ParticleTransform as
particle_transform when the chains should evolve in transformed
coordinates. Stored chains and final particles are returned in the original
particle coordinates.
Checkpointing#
The standalone runner accepts callbacks and checkpoint writers. It executes
one mutation block per call to run; a checkpoint therefore represents the
end of that block. Reconstruct the same mutation configuration, call
resume_from(checkpoint), and pass the returned runtime to run to append
another block to the stored chain. See Checkpointing for formats and RNG
restoration.