Implement gamma shape correction for SNP distance calculation
Introduces support for rate heterogeneity via a Poisson-Gamma mixture model. This includes new command-line options (`--gamma-shape`, `--gamma-shape auto`) to enable automatic estimation of the shape parameter $\alpha$ based on substitution counts across partitions. The correction is applied to the distance metric, with logic to disable the correction if variance checks fail.
This commit is contained in:
@@ -2249,9 +2249,85 @@ F84/TN93 and is included below. `snp-` prefix on every CLI value.
|
||||
**`+Γ` rate-heterogeneity modifier, applicable to `snp-jc`, `snp-k2p`,
|
||||
`snp-k81`, `snp-t92`, `snp-f84`, `snp-hky85`, `snp-tn93`** (not `snp-raw`,
|
||||
nothing to correct; not `snp-logdet`, no standard gamma formulation) — same
|
||||
formula as the base correction, weighted by a shape parameter `α` supplied
|
||||
by the user (`--gamma-shape <alpha>`), not estimated by ML. A modifier on
|
||||
existing values, not a separate enum arm per distance.
|
||||
formula as the base correction, weighted by a shape parameter `α` either
|
||||
supplied by the user (`--gamma-shape <alpha>`) or estimated from the data
|
||||
(`--gamma-shape auto`/`estimate`, method-of-moments — not ML; see
|
||||
"Automatic α estimation" below). A modifier on existing values, not a
|
||||
separate enum arm per distance.
|
||||
|
||||
### Automatic α estimation (`--gamma-shape auto`)
|
||||
|
||||
**Correction (verified against the primary source, 2026-09-11):** Jin &
|
||||
Nei (1990) itself (*"Limitations of the Evolutionary Parsimony Method of
|
||||
Phylogenetic Analysis"*, Mol. Biol. Evol. 7(2):82–102 — the paper this
|
||||
whole `+Γ` correction is cited from, confirmed algebraically to match this
|
||||
codebase's `corrected_log`/`k2p` exactly against their eq. A4, general, and
|
||||
A8, the `a = 1` case) contains **no** data-driven α-estimation procedure.
|
||||
Their own recommendation (p. 98) is a fixed default: *"we suggest that the
|
||||
gamma distance with a = 1 [eq. A8] be used. However, one may choose a
|
||||
different gamma distance, estimating a from data. Wilson et al. (1989)
|
||||
recently used a distance with a = 1/2 for restriction-site data of
|
||||
mitochondrial DNA in hominoids."* — i.e. Jin & Nei explicitly punt
|
||||
data-driven estimation to a *different* paper (Wilson et al. 1989), not
|
||||
read/verified here. The estimator below is therefore **not** "Jin & Nei's
|
||||
method" under any framing — that attribution (present in an earlier
|
||||
revision of this section) was wrong, not just under-cited.
|
||||
|
||||
**Implemented** (`PartitionDispersion`, `obikphylo/src/siblings/algorithms/pairwise.rs`)
|
||||
as an independent method-of-moments estimator, unrelated to any specific
|
||||
published procedure: pools substitution counts by **partition** rather
|
||||
than by genome pair, during the same `reduce_pairwise` pass that builds
|
||||
`PairwiseTally` (no second scan).
|
||||
|
||||
For partition `i`: `n_i` = substitutions pooled over every genome pair,
|
||||
`L_i` = eligible loci pooled over every genome pair, `R_i = n_i / L_i`.
|
||||
Modeling among-site rate heterogeneity the same way as the `+Γ` correction
|
||||
itself (a `Gamma(α, α)`-distributed, mean-1, multiplicative rate shared by
|
||||
every locus in a partition — the classical Poisson-Gamma/negative-binomial
|
||||
mixture, the general identity behind gamma-rate-heterogeneity corrections,
|
||||
also behind Uzzell & Corbin's (1971) original observation that substitution
|
||||
counts across sites/regions are over-dispersed relative to Poisson):
|
||||
|
||||
\[
|
||||
\mathbb{E}[R_i] = \mu \qquad \mathrm{Var}[R_i] = \frac{\mu}{L_i} + \frac{\mu^2}{\alpha}
|
||||
\]
|
||||
|
||||
Weighting each partition's squared deviation by its own `L_i` cancels the
|
||||
Poisson term before attributing what's left to `α`:
|
||||
|
||||
\[
|
||||
\hat\mu = \frac{\sum_i n_i}{\sum_i L_i} \qquad
|
||||
V = \frac{\sum_i L_i (R_i-\hat\mu)^2}{\sum_i L_i} \qquad
|
||||
\hat\alpha = \frac{\hat\mu^2}{V - \hat\mu/\bar L}
|
||||
\]
|
||||
|
||||
where `\bar L` is the mean partition size. Returns "no estimate" (falls
|
||||
back to the uncorrected formula, warns) when fewer than 2 partitions have
|
||||
data, `\hat\mu \le 0`, or `V` doesn't exceed the Poisson floor
|
||||
`\hat\mu/\bar L` — no detectable over-dispersion, `α` would be unbounded.
|
||||
|
||||
**Caveat, stated explicitly rather than left implicit**: unlike every
|
||||
closed-form correction in `snp_distance.rs` (each verified line-by-line
|
||||
against `ape`'s `dist_dna.c`, and now also against Jin & Nei 1990 directly
|
||||
for the base `+Γ` formula), this estimator is derived from first
|
||||
principles (the general Poisson-Gamma/negative-binomial identity) with no
|
||||
primary-source procedure behind it at all — not Jin & Nei's (confirmed
|
||||
above), and Wilson et al. (1989), the paper they point to instead, hasn't
|
||||
been read/verified either. Mathematically self-consistent (re-derived and
|
||||
checked, not guessed), but a from-scratch method, not a literature
|
||||
implementation. If `--gamma-shape` needs a value with a literature
|
||||
pedigree rather than an estimated one, Jin & Nei's own stated default,
|
||||
`α = 1` (`--gamma-shape 1`), is the better-supported choice today.
|
||||
|
||||
Deliberately **not** gated by `--sankoff-ratio-ceiling` the way
|
||||
`base_pair_tally` is (same precedent as `cardinality_tally` — see its own
|
||||
doc comment): that filter excludes individual saturated *pairs* from a
|
||||
composition estimate computed once at the very end, from the complete
|
||||
`PairwiseTally`; the partition axis needed here only exists transiently,
|
||||
one partition at a time, while `PairwiseTally` is still being built — long
|
||||
before any pair's final SNP ratio (and thus its ratio_ceiling eligibility)
|
||||
is known. `--exclude-genome` isn't applied either, matching
|
||||
`reduce_pairwise`'s own raw per-pair fold.
|
||||
|
||||
**Implemented now: `snp-raw`, `snp-jc`, `snp-k2p`, `snp-k81`, `snp-f81`,
|
||||
`snp-t92`, `snp-tn93`, `snp-tv`, all with `+Γ` except `raw`/`tv`** — see
|
||||
|
||||
Reference in New Issue
Block a user