PyBNF convergence diagnostics (pybnf.diagnostics)¶
Convergence diagnostics for MCMC samplers — pure R-hat / ESS math.
Extracted from algorithms/samplers/base.py (M2.2 move 5, ADR-0009): the
convergence diagnostics are substantial, already shared by all four samplers,
project-specific, and oracle-testable, so their pure numerical core lives here
as free functions — a peer of objective.py, navigable, and importable by the
benchmark harness (M2.5) without reaching into samplers/. The
instance-coupled glue (report_convergence_diagnostics /
check_convergence / _write_diagnostics) and the PSet→array bridge
(_param_vec) stay on BayesianAlgorithm, which delegates the math
here.
Implements the rank-normalized split-R-hat and bulk/tail effective sample size of Vehtari, Gelman, Simpson, Carpenter & Bürkner (2021), Bayesian Analysis (the same conventions as Stan / ArviZ).
The data layout throughout: chain_history is a list of num_parallel
per-chain histories, each a list of (n_dim,) parameter vectors in the
sampling space (see BayesianAlgorithm._param_vec). The internal chains
arrays are (n_chains, n_draws, n_dim) (or (n_chains, n_draws) for the
single-parameter ESS kernel).
- pybnf.diagnostics.ess(chain_history, num_parallel)[source]¶
Compute bulk and tail effective sample size per Vehtari et al. (2021).
Bulk ESS: computed on rank-normalized values (same transform as R-hat). Tail ESS: minimum ESS of the 5% and 95% quantile indicators.
Returns (bulk_ess, tail_ess) arrays of shape (n_dim,) or (None, None).
- pybnf.diagnostics.ess_from_chains(chains)[source]¶
Compute effective sample size from an (M, n) array of chains using FFT-based autocovariance and Geyer’s initial positive sequence estimator.
- pybnf.diagnostics.rhat(chain_history, num_parallel)[source]¶
Compute rank-normalized split-R-hat for each parameter (Vehtari, Gelman, Simpson, Carpenter & Burkner, 2021, Bayesian Analysis).
Steps: 1. Split each chain in half (doubles the number of chains, catches within-chain non-stationarity) 2. Rank-normalize across all split chains (replaces values with normal scores of their ranks) 3. Compute R-hat on both the ranked values and folded ranked values (detects scale differences) 4. Return the element-wise maximum
Returns a numpy array of shape (n_dim,) or None if insufficient data.
- pybnf.diagnostics.split_chain_rhat(chains)[source]¶
Compute the potential scale reduction factor R-hat from an array of chains, using the Vehtari et al. (2021) convention R = sqrt(var_plus / W), where var_plus = ((n-1)/n) W + B/n. This is the form paired with the rank normalization and folding done by rhat() (and matches Stan / ArviZ).
It deliberately omits the older Gelman & Rubin (1992) df-style correction sqrt((N+1)/N * (var_plus/W) - (n-1)/(N n)): that factor only inflates R-hat when the chains have not converged (it cancels to ~1 at convergence), making PyBNF report systematically higher R-hat than reference tools on the same chains – harmful for cross-tool comparison – without changing any convergence decision.
chains: (N, n, d) array Returns: (d,) array of R-hat values
- pybnf.diagnostics.split_chains(chain_history, num_parallel)[source]¶
Build the split-chains array used for R-hat and ESS.
Uses the last 50% of each chain, then splits that window in two (doubling the chain count, catching within-chain non-stationarity). Returns an
(2 * num_parallel, half, n_dim)array, orNoneif there is too little history (fewer than 20 recorded steps, or fewer than 5 per split half).