Concept - Marginalising Latent Discrete Counts for HMC
Summary
Hamiltonian Monte Carlo cannot sample integer latents directly. For occupancy, N-mixture and latent-class models the discrete variable (presence zi, abundance Ni, class ci) has a small or truncatable support, so it is summed out of the likelihood in log space and, if needed, recovered afterwards from its conditional posterior.
Overview
Every model in this cluster has a likelihood of the form p(y∣θ)=∑zp(z∣θ)p(y∣z,θ) with independent units. Removing z turns a mixed discrete-continuous posterior into a smooth continuous one that NUTS can handle, usually improves effective sample size, and makes the model compatible with pm.CustomDist / pm.Potential or pymc_extras.marginalize (as in PyMC example E28 and E109, where classes are “summed out”).
Main Content
Log-space marginalisation
For unit i with finite support Z: ℓi(θ)=logsumexpz∈Z[logp(z∣θ)+logp(yi∣z,θ)]. Use logsumexp (never exponentiate and add) to avoid underflow in products over visits.
Let μ=λi. The neglected tail is P(Ni>Nmax)⋅(likelihood bound ≤1), so the marginal likelihood error per site is at most P(Pois(μ)>Nmax). Choose Nmax so this is below tolerance at the largest plausible μ (e.g. Nmax=maxijyij+ several multiples of μ+ margin), and re-run with a larger Nmax as a check: if estimates drift, the truncation (or an unidentified N; see Barker 2018 - Poisson Alternative and Model Discrimination) is driving the result. The same issue arises with the dark-figure grid in Madigan-York (Silverman 2020 - Madigan-York and Dirichlet Process Benchmarks).
Recovering the latent values. After sampling θ, draw zi from p(zi∣yi,θ)∝p(zi∣θ)p(yi∣zi,θ): for occupancy, P(zi=1∣all zeros)=ψ∏(1−p)/[ψ∏(1−p)+1−ψ]; for detected sites zi=1. This is how finite-sample occupancy (the number of occupied sites among those surveyed) is reported: sum the sampled zi (E28).
Pitfalls. (1) Label switching in latent classes: mixture components are exchangeable, so chains can swap labels; constrain by ordering (e.g. λj1 increasing) or a better-than-chance (Youden) constraint for diagnostic raters (E69). (2) Weak identification: marginalisation does not create information; a flat marginal likelihood in (N,p) gives a ridge that HMC will show as divergences or high R^ (Aleshin-Guendel 2020 - Latent Class Identifiability Theorem and LCMCR). (3) Existing vault note on the sampling trouble discrete latents cause: Sampling Problems with Latent Variables - No Vehicles in the Park.
Examples
N-mixture marginal in numpy and in PyMC (untested PyMC)
import numpy as npfrom scipy.special import logsumexpfrom scipy.stats import poisson, binomdef nmix_loglik(y, lam, p, Nmax=200): # y: (J,) counts at one site N = np.arange(y.max(), Nmax+1) lp = poisson.logpmf(N, lam) + sum(binom.logpmf(yj, N, p) for yj in y) return logsumexp(lp)print(nmix_loglik(np.array([3, 5, 2]), lam=10, p=0.4))
PyMC 5:
import pymc as pm, pytensor.tensor as pt# y: (I, J) ints; Nmax intN = pt.arange(Nmax + 1)[None, :] # (1, Nmax+1)with pm.Model() as nm: lam = pm.Gamma("lam", 2, 0.1); p = pm.Beta("p", 2, 2) logp_N = pm.logp(pm.Poisson.dist(lam), N) # (1, Nmax+1) ll = 0 for j in range(y.shape[1]): ll += pm.logp(pm.Binomial.dist(n=N, p=p), y[:, [j]]) # (I, Nmax+1); -inf where N<y pm.Potential("lik", pt.sum(pm.math.logsumexp(logp_N + ll, axis=1)))
Alternative: declare N = pm.Poisson("N", ...) and let pymc_extras.marginalize(model, ["N"]) generate the sum automatically (as E28 checks against its hand-written version).
Why it matters for marketing models
Many marketing models have discrete latents: customer segments (latent class), churn state (alive/dead in BG/NBD), conversion-attribution paths. The same recipe applies: sum the segment out, keep the segment-posterior as a derived quantity. See Monsters and Mixtures for mixture likelihoods and Hierarchical Models for the continuous-latent analogue.