Skip to content

[1/5] Transmission leak (--transmission-leak) - #141

Open
m-murphy wants to merge 13 commits into
masterfrom
stack/1-leak
Open

m-murphy wants to merge 13 commits into
masterfrom
stack/1-leak

Conversation

@m-murphy

@m-murphy m-murphy commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Summary

An optional transmission leak ε: each Parent set's Parent pool at a Locus is mixed with the Allele frequencies, so a child Allele no parent carries is improbable instead of impossible.

 pool(parent set, locus)
-  alleles the parents carry
+  (1 - ε) · alleles the parents carry + ε · Allele frequencies
  • --transmission-leak <ε> (default 0, the model without a leak) is part of the Run recipe and the manifest (transmission_leak). A manifest without it reads as 0.
  • When leaky, the Transmission memo and term also follow the Allele frequencies.
  • pt_read() refuses to pool Runs whose transmission_leak differs, as it does for null_model.
  • Derivation, implementation, tests and cost: section 6 of doc/marginal-latent-parent.md.

Evidence

  • Before: 0 is the only model, so a child Allele no parent carries gives the Parent set a likelihood of 0.
    After: engine tests 298 pass, 1 skipped (OpenMP); plasmotrackr tests 3724 pass; golden copies in sync. The leaky locus term matches strain-by-strain draws, and the leaky F-table path matches brute force over every latent-parent genome. Cached terms follow allele-frequency changes, and still match a fresh memo after the frequency ids are forgotten. A leak outside [0, 1), or a leaky term without frequencies, throws in release builds too. The ε = 0 golden Stores are unchanged apart from the new manifest key.

Merge Danger

Door: two-way

The default is 0, so existing Runs behave as before.

Blast Radius: small

New manifest key; readers that ignore unknown keys are unaffected. checkpoint::restore() now takes the leak with no default.

…to the parent pool

With leak e > 0 the pool on the child's alleles is (1 - e) p + e pi, so a child
allele no parent carries is improbable rather than impossible. f is computed by
the generating function m! [t^m] prod_{j in C} (e^{p_j t} - 1), which has no
cancellation. The F table takes the frequencies, needs no mask, and includes
U = empty for the latent parent alone. e = 0 takes the old paths unchanged.

Brute-force tests against a plain strain-by-strain reference for observed
parents, observed parents with L, and L alone.
With a leak, locus terms and F tables are keyed by frequency id too, parent-set
sums are no longer memoised by genome, and entries without the Latent parent
are summed at recorded frequency ids and swapped on a frequency change, like
the latent entries. The State carries the leak and the Model hands it to the
memo. A ModelCacheConsistencyTest case at leak 0.03 checks every parent set
against the plain computation, and that a frequency change dirties every term.
Default 0, refused outside [0, 1). Recorded as transmission_leak in the Run
manifest; a manifest without it reads as 0. Resuming needs the same value; a
Run started from a checkpoint takes the value given.
… cost

Section 6 of doc/marginal-latent-parent.md.
The Run manifest now carries the leak; the draws and tables are unchanged.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant