Repository navigation
Conversation
HJCFIT.likelihood.mcmc is the first part of Bayesian inference over the free rate constants, following Epstein, Calderhead, Girolami & Sivilotti (2016) Biophys J 111:333-348: the posterior density the samplers will draw from. - UniformPrior (the paper's) and LogUniformPrior. UniformPrior can be built from the free rates' own limits, and scalcs' default limits are exactly that paper's prior. - LogPosterior: ln prior + ln likelihood over one or more Records. It wraps HJCFitter but never clips a rate to its limits. A point outside the prior is -inf without computing the likelihood, and a likelihood that cannot be computed is -inf and counted. - HJCFitter(solver=...) passes root-finding options (SOLVER_OPTIONS) to every record's Log10Likelihood. On the paper's AChR records, its settings move ln L by 0.38 compared with the defaults. Like the fitter, the module does not import scalcs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mwg_sample is the pilot: Metropolis-within-Gibbs on the logarithm of one rate at a time. The Jacobian of the log walk is applied, and each step size is tuned from its acceptance rate during burn-in. adaptive_sample is adaptive Metropolis (Haario et al. 2001). It uses a small isotropic step until adapt_start, then the running covariance of the chain scaled by 2.38^2/k, mixed with the small step. The mixture can be the paper's weighted sum or the Roberts & Rosenthal (2009) random choice, and an optional initial covariance (e.g. an inverse Hessian) shapes the small step. A global scale is tuned during burn-in only. Both take any callable log density, return a Chain (samples, proposals, acceptance, scales, provenance; .npz save/load), are reproducible from a seed, and report progress through a callback. effective_sample_size is Geyer's initial monotone sequence estimator, truncated at significant_lags as in that paper's Table 2. The tests use targets with known answers. The Jacobian test fails with the Jacobian removed, the means shifting by exactly -sigma^2. The statistical tests also pass for ten other seeds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hessian() takes central second differences with steps relative to each coordinate, because rates span eight decades. One Richardson extrapolation gives O(h^4) and an error estimate for every element. A step that leaves the prior, or where the likelihood fails, raises rather than returning a meaningless number. gaussian_approximation() returns N(mode, (-H)^-1): the basis of maximum-likelihood standard errors, and the red curves of Epstein et al. (2016, Figs 4 and 7). A point that is not a maximum is refused. GaussianApproximation.relative_error shows how well the Hessian was determined. Measured on real data. On the paper's three AChR records, steps from 3e-2 to 1e-3 give identical standard deviations, with a normalised error near 1e-5 at the default 3e-3. On the CH82 sample record, one concentration with eight free rates, one direction is so flat that likelihood noise (about 5e-8 in ln L) decides its curvature. The docs say so, as a matter of identifiability. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Bayesian layer driven by the same file as a fit. - [likelihood] passes root-finding settings (SOLVER_OPTIONS) to every record's likelihood, for `hjcfit fit` and `hjcfit sample` alike. A published value is reproduced only with the settings it was computed with: on the AChR records of Epstein et al. (2016) they move ln L by 0.38. - [mcmc] says how to sample. By default the search runs first and the chains start at the maximum-likelihood estimate, with the inverse Hessian there shaping the first proposals at the optimal scale. start = "guess" is the paper's pilot from the initial guess instead. Also: sampler, n, burnin, prior, mixture, log_space, chains, seed. - runner.sample() runs the chains. It falls back, with a note, when the Hessian cannot be used, and starts extra chains from draws of the Gaussian approximation. Chains run in spawned processes, each rebuilding its posterior from the specification because the C++ likelihood cannot be pickled, and each limited to its share of the OpenMP threads. Reports mean, sd, 95% interval, ESS and split-R-hat per rate, and writes <prefix>.json plus one .npz per chain. - `hjcfit sample SPEC -o PREFIX [--chains N] [--processes P]`. Exits 2 when split-R-hat exceeds 1.1. - mcmc.potential_scale_reduction: split-R-hat (Gelman et al. 2013). Measured, and stated in the docstring rather than assumed: the C++ likelihood takes 57/36/29 ms per AChR evaluation on 1/2/4 OpenMP threads, and 99% of the time is inside it. On a 4-core laptop, four parallel CH82 chains ran 1.3-1.6x faster than four in turn; two threads per worker beat one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
remislp
added a commit
that referenced
this pull request
Sep 26, 2026
1.1.0 published the fitting layer. This publishes a fit written as a file, and Bayesian inference over the same fit. #196 A fit specification (TOML) and the hjcfit command: template, check, fit, show. #197 HJCFIT.likelihood.mcmc: the posterior of a mechanism's rate constants by MCMC, following Epstein et al. (2016). LogPosterior, priors, the pilot and adaptive samplers, Chain, ESS and split-R-hat, and the Gaussian approximation from a numerical Hessian. Also HJCFitter(solver=...), the [likelihood] and [mcmc] tables, and hjcfit sample. 288 tests passing. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bayesian inference over a mechanism's rate constants with the exact HJC missed-events likelihood, following Epstein, Calderhead, Girolami & Sivilotti (2016) Biophys J 111:333–348. It is a new pure-Python module,
HJCFIT.likelihood.mcmc, driven by the same fit specification ashjcfit fit. There are no C++ changes.What's in it
HJCFIT.likelihood.mcmcis duck-typed on a mechanism likeHJCFitter, and does not import scalcs.LogPosterioris ln prior + ln likelihood over one or moreRecords. It wrapsHJCFitterbut never clips a rate to its limits. A point outside the prior gives −∞ without computing the likelihood. A likelihood that can't be computed also gives −∞ and is counted.UniformPrior(the paper's) andLogUniformPrior.UniformPrior.from_mechanism()reads each free rate's limits, and scalcs' default limits are exactly the paper's prior.mwg_sampleis the paper's pilot: Metropolis-within-Gibbs on log rates, with the Jacobian applied and step sizes tuned during burn-in only.adaptive_sampleis adaptive Metropolis (Haario et al. 2001). The mixture can be the paper's weighted sum or the Roberts & Rosenthal (2009) random choice, and an optional initial covariance can shape the first proposals.Chainholds samples, proposals, acceptance and scales, saves to.npz, and hasmode()andkept().effective_sample_size(Geyer's initial monotone sequence, as the paper),significant_lags,autocorrelationand split-R̂ (potential_scale_reduction).gaussian_approximationgives N(mode, (−H)⁻¹) from a numerical Hessian. It uses relative steps and one Richardson extrapolation, and reports an error estimate with every Hessian (relative_error). These are the paper's red curves and the ML standard errors.Fitting layer
HJCFitter(solver=...)passes the root-finding options (SOLVER_OPTIONS) to every record'sLog10Likelihood.Fit specification and command
[likelihood]table: those same options, forfitandsamplealike.[mcmc]table:start = "fit"(the default): search first, then start the chains at the ML estimate, with the Hessian as the first proposal covariance. If the Hessian can't be used, the run says so and falls back to the paper's small step.start = "guess": the paper's pilot from the initial guess.sampler,n,burnin,prior,chains,seed, and a few others.runner.sample(): chains after the first start from draws of the Gaussian approximation.<prefix>.json(summary, spec, provenance) and one.npzper chain.hjcfit sample SPEC [-o PREFIX] [--chains N] [--processes P]exits with status 2 when split-R̂ > 1.1.Why the solver options matter
On the paper's three AChR records, its settings (
nmax=2, tolerances 1e-12) give an ln L 0.38 higher than HJCFIT's defaults at the same rates. With those settings, a scalcs mechanism through HJCFIT reproduces the value from the paper's own MATLAB code (BICME) exactly: 237158.0646.Validation
Two things a reviewer should know
mcmc.pyis added toPYTHON_MODULESinlikelihood/python/CMakeLists.txt.mec.theta_unsqueeze()does not rebuildmec.Q; onlyset_eff()does.HJCFitterandLogPosteriorare safe because they callset_efffor every record. Any new code that readsmec.Qdirectly after setting rates must call it too.🤖 Generated with Claude Code