Research-oriented Metropolis-Hastings tools in Rust for ordinary numeric states, large combinatorial state spaces, and proposal implementations that need rollback-safe mutation or delayed commits.
This library implements composable Metropolis-Hastings sampling in Rust for workflows where the state space, proposal mechanism, and measurement strategy are application-specific. It is designed for research code where proposal kernels, observables, and scientific validity checks live in domain code, while the sampler owns the transition bookkeeping.
The Metropolis-Hastings contract is explicit: targets return unnormalized natural log weights, proposals describe the same concrete transition they generate, and proposal asymmetry stays in the Hastings correction. The crate is useful for simple numeric examples, spin systems, triangulation moves, and other large combinatorial state spaces where cloning, rollback, or delayed commits matter.
π§ Pre-release (0.x) β This is research software under active development. APIs may change before 1.0.
Use this crate when you want:
- a generic Metropolis-Hastings chain over user-defined state spaces
- by-value, in-place, and delayed-commit proposal APIs
- log-space acceptance calculations with NaN/+infinity checks
- additive target composition for bias potentials, energy/action terms, externally supplied learned regularizers, and other log-weight modifiers
- observable measurement APIs, streaming statistics, and binning-based uncertainty estimates for correlated samples
- trace recording and CSV export for downstream MCMC diagnostics
- thinning helpers for long sampler runs
- optional
serdecheckpointing with validated resume flows - detailed-balance diagnostics for proposal development
This crate provides the sampler mechanics; proposal correctness, ergodicity, convergence assessment, and scientific model choice remain domain-specific responsibilities.
The acceptance rule is the standard Metropolis-Hastings correction:
alpha(x, y) = min(1, exp(log pi(y) - log pi(x) + log q(x | y) - log q(y | x)))
Target<S> supplies log pi(s) up to an additive constant. Proposal implementations either use the default symmetric correction or supply the proposal ratio
for the same concrete transition they generate. For asymmetric combinatorial moves, that usually means accounting for move-kind probabilities, valid-site
counts, reverse-site counts, and invalid-move handling.
Physics actions and externally supplied learned regularizer terms fit the same target interface: implement Target::log_prob as an unnormalized log weight, or
as -E(state) when working in energy/action form. Training learned energies or adaptive proposal policies is outside the current crate scope.
The crate checks local transition mechanics: log-space acceptance, invalid floating-point values, rollback for in-place proposals, delayed commits, counters, checkpoints, and empirical detailed-balance diagnostics for representative discrete transitions. It does not prove that a proposal is ergodic, that a chain has mixed, or that a scientific model is appropriate for a downstream study.
For the detailed contract, see the scientific basis and scope guide.
- Generic
Chain<S>over user-defined state spaces with explicit accepted/rejected counters. - Log-space Metropolis-Hastings acceptance with typed errors for NaN and positive-infinite target or proposal values.
AdditiveTargetfor composing model and bias log-weight terms without mixing them into proposal-ratio corrections.- Three proposal workflows: by-value
Proposal, rollback-safe in-placeProposalMut, and delayed-commitDelayedProposal. Samplerhelpers for repeated and chunked runs, iterator-style sampling, thinning, observations, and counter resets after burn-in.- Streaming
OnlineStatsandBinningAnalysisfor long correlated runs without retaining every sample. TraceRecorderandTracefor numeric observable traces with chain IDs, accept/reject metadata, and CSV export.ChainCheckpointrestore APIs that recompute cached log-probabilities against the resumed target.- Optional
serdesupport for serializing chains, samplers, and portable checkpoints. - Detailed-balance diagnostics for proposal tests on representative discrete transitions.
- π Introduction
- π§ͺ Scientific basis
- β¨ Features
- π Quick start
- π§ Choosing an API
- π¦ Cargo features
- π§ͺ Examples
- π Documentation
- π Reviewer guide
- π§© Ecosystem
- π€ Contributing
- π Citation
- π References
- π€ AI-assisted development
- π License
Add the library to your crate:
cargo add markov-chain-monte-carloEnable checkpoint serialization when needed:
cargo add markov-chain-monte-carlo --features serdeRust 1.97.1 or newer is required.
Minimal by-value Metropolis-Hastings sampler. This example demonstrates the transition mechanics; convergence assessment remains a separate analysis step.
use markov_chain_monte_carlo::prelude::by_value::*;
use rand::{Rng, RngExt, SeedableRng, rngs::StdRng};
#[derive(Clone)]
struct Scalar(f64);
struct Normal;
impl Target<Scalar> for Normal {
fn log_prob(&self, state: &Scalar) -> f64 {
-0.5 * state.0 * state.0
}
}
struct RandomWalk {
width: f64,
}
impl Proposal<Scalar> for RandomWalk {
fn propose<R: Rng + ?Sized>(&self, current: &Scalar, rng: &mut R) -> Scalar {
let delta = rng.random_range(-self.width..self.width);
Scalar(current.0 + delta)
}
}
fn main() -> Result<(), McmcError> {
let mut rng = StdRng::seed_from_u64(42);
let mut chain = Chain::new(Scalar(0.0), &Normal)?;
let proposal = RandomWalk { width: 1.0 };
for _ in 0..1000 {
chain.step(&Normal, &proposal, &mut rng)?;
}
assert!(chain.acceptance_rate() > 0.0);
Ok(())
}- Start with
ProposalandChain::stepwhen state cloning is cheap. - Use
ProposalMutandChain::step_mutwhen cloning state is expensive and rollback is simple; itsInfometadata is returned in structuredSteptelemetry for accepted and rejected proposals.StepOutcome::NoProposalcarries metadata only whenProposalMut::no_proposal_infoprovides it. - Drive
Sampler::step_mutexplicitly when every transition needs metadata. BulkSampler::run_mut*methods deliberately skipInfoconstruction and proposal telemetry hooks, so metadata that would be discarded is not constructed. - Use
DelayedProposalandChain::step_delayedwhen you need to plan and score a concrete move before mutating state. - Use
AdditiveTargetwhen the target log weight is the sum of model, bias, energy, action, or externally supplied regularizer terms. - Use
DelayedSteptelemetry,StepOutcome, andDelayedProposal::no_plan_infowhen delayed proposals need domain-specific per-step records. - Use
Samplerwhen you want ergonomic repeated runs, resumable chunks, iterator-based sampling, or observing helpers. - Parse raw positive thinning counts with
ThinningInterval::new, then reuse the validated interval acrossSampler::*_with_thinningcalls. - Use
Sampler::run_delayed_chunk_observingto record per-step delayed telemetry and post-step state while resuming chunked runs from aChainCheckpoint. - Use
TraceRecorderwhen you need reusable numeric traces with chain IDs, acceptance metadata, target log-probabilities, and CSV export. - Use
verify_detailed_balance*helpers in proposal tests for representative discrete transitions. - Use
OnlineStatsandBinningAnalysiswhen long runs should stream statistics instead of retaining every sample.
When migrating from the previous in-place API, add ProposalMut::Info, implement info (and optionally no_proposal_info), make proposal mutation hooks
accept &mut self, and read the Step returned by Chain::step_mut or Sampler::step_mut instead of a boolean. Rejection must restore both the state and
proposal-internal transition state. Keep telemetry hooks observational because bulk execution may skip them. For earlier thinning and
delayed-telemetry APIs, replace raw thinning usize arguments with a parsed ThinningInterval, handle the underlying sampler or observation error directly
instead of matching ThinningError::Run, and replace Step field reads with the corresponding outcome(), info(), log_prob_before(),
log_prob_after(), and log_alpha() accessors.
serdeβ enableserde::SerializeforChainandSampler, plusChainCheckpointserialization/deserialization for validated resume flows.
Complete runnable examples live in examples/:
examples/normal_1d.rsβ by-value random-walk sampler for a normal targetexamples/ising_1d.rsβ in-place spin-flip proposals for a non-CloneIsing state, with energy/magnetization trace CSV exportexamples/iterator_sampling.rsβSampleras an iteratorexamples/detailed_balance.rsβ by-value, in-place, delayed, and batch detailed-balance checksexamples/delayed_chunked_telemetry.rsβ per-step delayed telemetry and post-step state recorded across resumable chunksexamples/additive_target_bias.rsβ model and bias log-weight terms composed withAdditiveTarget
Run them with:
just examplesFor proposal-specific testing patterns, see the proposal validation guide.
The Ising trace notebook lives at
notebooks/ising_trace_analysis.ipynb. Run
just notebook-check to generate target/ising_1d_trace.csv, validate the source notebook, and write a headlessly executed copy under target/notebooks/.
- docs.rs API documentation
- Reviewer guide
- Changelog
- Scientific basis and scope
- Proposal validation guide
- Roadmap
- Code organization guide
- Rust development workflow
- Release process
- Security policy
For a short reading path through the repository's scientific contract, validation strategy, roadmap boundaries, and reproducible local checks, see
docs/reviewer_guide.md.
This crate is part of a broader Rust ecosystem for computational geometry and simulation:
causal-triangulationsβ CDT physics and simulationdelaunayβ geometric primitives and triangulationsla-stackβ fixed-size linear algebra
The long-term architecture separates:
- Geometry: triangulations and geometric predicates
- Sampling: this crate
- Physics: CDT actions, observables, and domain-specific dynamics
See CONTRIBUTING.md for the full contributor guide (project layout,
development workflow, code style, testing, documentation layout, performance/benchmarking, and the release process). Community expectations live in
CODE_OF_CONDUCT.md. AI assistants should follow
AGENTS.md.
Quick local workflow: run just setup once, then run just check before opening a pull request. For the full command list, run just --list.
If you use this crate in academic work or downstream research software, please cite it using
CITATION.cff or GitHub's "Cite this repository" feature.
For canonical background references for Metropolis-Hastings, MCMC, and the example models, see
REFERENCES.md.
This repository contains an AGENTS.md file, which defines the rules and invariants for AI coding assistants and autonomous agents working on this codebase.
Portions of this library were developed with the assistance of AI tools including ChatGPT, Claude, Codex, and CodeRabbit.
All accepted code and documentation changes are reviewed, edited, and validated by the author.
For tool citation metadata, see the
AI-assisted development tools section of
REFERENCES.md.
This project is licensed under the BSD 3-Clause License.
