JaCK & Jill
The content on this page was written by AI under human supervision.
JaCK & Jill is the Python package phyloexact (import phyloexact as px): JaCK for the exact Jukes–Cantor and Kimura computations, Jill for the interval and sampling routines. Given a small alignment, a tree topology and a substitution model, it computes the tree's Bayesian evidence and says which kind of answer it could give: an exact fraction, a proven interval, an estimate with a measured error, or a proof that two trees tie. Every answer carries a machine-readable certificate that a separate routine in the package rechecks against the data.
What it does
Bayesian phylogenetics weighs one tree or one substitution model against another by its evidencemarginal likelihood: the probability of the alignment under a tree and a model, integrated over the branch lengths with a prior; a ratio of two evidences is a Bayes factor. Programs in everyday use estimate the evidence by sampling, without a proven error bound. For four taxa under the Jukes–Cantor model (JC69, all substitutions equally likely) with the package's prior it is a rational number, returned as a Python Fraction:
$$Z(T)=\int_{(0,1)^5} P(D\mid T,x)\,dx_1\cdots dx_5,\qquad x_e=e^{-4t_e/3}.$$
The prior is uniform in each $x_e$, which is an exponential prior of rate $4/3$ on each branch length $t_e$. Under JC69 the likelihood is a polynomial in the $x_e$ with rational coefficients, so $Z$ is rational and a Bayes factor is a ratio of integers. Kimura's two-parameter model (K2P) is exact too on shorter alignments, and protein sequences enter through a four-letter recoding ('JC-SR4').
The package calls the kind of answer its register. R0 is a tie proof: if a permutation of the taxa leaves the site-pattern counts unchanged and maps one topology onto the other, the two evidences are equal under the usual models (JC69, K2P, GTR, mixtures) with any independent branch prior. Nothing is integrated, and every comparison checks for this first. R1 is the exact fraction, with a check that its denominator divides a bound known in advance ($256^N\,\mathrm{lcm}(1,\ldots,N+1)^5$ for $N$ columns under JC69). R2 is an interval with exact rational endpoints proven to contain $Z$, by quadrature in ball arithmetic (needs python-flint); where only the upper end is proven the label is R2-one-sided. R3 is an estimate by importance sampling with scrambled Sobol points, available at any alignment length for JC69, HKY85, GTR with gamma rates and your own log-likelihood (px.PlugIn). Its error figure comes from a calibration record of measured deviations from exact values on small alignments, identified by checksum in the certificate: an indicative accuracy, not a bound. The bundled record was measured on JC69 quartets; HKY85 and GTR estimates reuse it and say so in the certificate, and a model without a usable record, such as general Markov ('GM'), is refused with a message instead of being estimated without one.
register="auto" returns the strongest answer available, based on an import-time probe of the installed libraries and GPU that is recorded in every certificate. Forcing a register that cannot be met raises px.RegisterUnavailable, whose reason states the limit and whose unlock names the smallest change that would satisfy the request; nothing weaker is substituted silently. On a CPU, JC69 is exact for quartets up to 150 usable columns and K2P up to about 35; past 150 columns exact JC69 needs a CUDA GPU with torch, and only for alignments inside a measured size range that the refusal message states. px.verify_certificate rechecks a certificate against the data, recomputing the value where that is cheap and saying what it skipped. It catches mistakes, stale installs and corruption, and is not designed to stop someone who can rewrite both the certificate and the package.
Further calls compare and grade models and check their adequacy (see Routines). px.hill works at genome length. It runs window scans and computes an exact upper bound on the likelihood that any independent-sites model can reach on an alignment of any size. It also checks published likelihood tables against that bound and writes weighted-quartet input files for the species-tree programs wASTRAL and wQFM. px.quintet ranks the 15 rooted five-taxon trees by maximum likelihood with proven bounds, abstaining rather than guessing.
Limits. The package scores a given topology and does not search trees. Exact values and intervals are four-taxon (the quintet engine aside); beyond that it offers tie proofs for up to 32 taxa and the JC69 estimate. HKY85 and GTR have irrational transition probabilities, so no exact value exists and a forced R1 says so. Columns with gaps or ambiguity codes are dropped. The weight files certify inputs to a species-tree program, never the tree.
Examples
Run the self-test suite. We want to know a fresh copy works on this machine before trusting its numbers.
python3 -m phyloexact.validate
The suite prints the capability probe, one line per test with PASS or FAIL and its time, and an overall verdict, and writes VALIDATION_REPORT.json. The tests recompute known exact values on the bundled fixtures, make deliberate edits to certificates and reference files that the verifiers must reject, and confirm that infeasible requests are refused with the right message; a missing optional library is reported as a skip. No GPU or external data is needed, and any failure makes the exit code nonzero.
The smallest exact case, from the suite's JC69 test: one column in which all four taxa carry the same base, then twenty such columns with the exact register forced.
from fractions import Fraction as F
from phyloexact import Dataset, evidence
rep = evidence(Dataset.from_counts({"AAAA": 1}), "12|34", "JC69")
rep.register == "R1" and rep.value == F(103, 4096) # True
rep = evidence(Dataset.from_counts({"AAAA": 20}), "12|34", "JC69",
register="R1")
c = rep.certificate
c["denominator_bound"]["divides"] # True
len(c["engine"]["sha256"]) == 64 # True
A constant column has evidence exactly $103/4096$ on 12|34; the same test checks AGCT ($7/4096$) and AACG ($15/4096$). rep.logZ is the floating-point logarithm. In the certificate, denominator_bound records that the denominator divides the bound above, and engine holds the SHA-256 of the exact engine's source, which the verifier compares with the file on disk.
A quartet from a FASTA file, using lines from the package guide's quickstart: one evidence, the tie-proof certificate, a topology comparison, a between-model Bayes factor and a certificate check.
import phyloexact as px
ds = px.Dataset.from_fasta("quartet.fasta", taxa=["a", "b", "c", "d"])
rep = px.evidence(ds, topology="12|34", model="JC69", register="auto")
rep.value # exact Fraction (R1) or float logZ view (R2/R3)
rep.register # register actually delivered, e.g. 'R1'
rep.certificate # machine-checkable dict
px.decidability(ds) # R0 certificate directly (always available)
px.compare(ds, "13|24", "14|23", model="GTR+G") # forced tie => R0, no arithmetic
px.compare_models(ds, "12|34", models=("K2P", "JC69")) # exact between-model BF
px.verify_certificate(rep.certificate, dataset=ds) # machine-check any cert
taxa= fixes the order, so "12|34" pairs a with b; "13|24" and "14|23" are the other two quartet trees. For a transfer-RNA-length alignment rep.register is 'R1' and rep.value a Fraction with hundreds of digits; on a long one auto gives 'R3' and a floating-point log evidence. px.compare returns a dictionary whose verdict is 'forced-tie' (with log_bf exactly 0 and the symmetry that proves it) or 'computed' (with log_bf, and bf_exact when both sides are exact). px.compare_models returns a ModelComparisonReport whose bf is an exact ratio when both models ran exactly, and px.verify_certificate returns ok and a list of checks marked pass, fail or skipped.
Routines
Data
px.Dataset.from_fasta(path, taxa=None, alphabet=None),.from_rows(rows, taxa=None, alphabet=None),.from_counts(counts, taxa=None)— an alignment from a file, a{name: sequence}mapping, or site-pattern counts keyed byACGTpatterns or the 15 pattern-class labels ('xxxy'style);taxa=selects and orders records;alphabet=states'nucleotide'or'protein'instead of detecting it from the letters; duplicate names and unequal lengths raise.Dataset.N,.dropped,.sha256,.stats(topology),.raw_pattern_counts(),.fold_counts()— usable and dropped columns, the data hash, the size summary, the pattern tables.
Evidence and model checking
px.evidence(ds, topology="12|34", model="JC69", register="auto", *, prior=None, budget=100000, seed=0, subset=None, topology_prior=None)— one evidence;EvidenceReportwith.value,.logZ,.register,.certificate,.meta; raisespx.RegisterUnavailable(.reason,.unlock).topologyis a quartet key, a Newick string over the taxa names, or"all"for the sum over the three quartet topologies (aTopologySumReportwith.posterior,.winnerand.interval;topology_prior=weights them);subset=picks four taxa from a larger dataset;prior=px.BetaPrior(a, b)sets a Beta prior on the estimate route for JC69 and K2P; under JC69 the estimate route also accepts five or more taxa.px.decidability(ds, label="", topology=None)— theR0certificate for 4 to 32 taxa: symmetry group of the pattern counts and the topology pairs forced to tie (every orbit up to seven taxa; from eight on, passtopology=as a Newick string to get that topology's orbit).px.compare(ds, topoA, topoB, model="JC69", register="auto")— two topologies, given as split keys or Newick strings over the taxa names (4 to 32 taxa): tie check first, else both evidences and the log Bayes factor (beyond four taxa the untied case is computed only under JC69, as estimates).px.compare_models(ds, topology, models=("JC69", "K2P"), register="auto", allow_mixed_registers=False)— Bayes factor between models as aModelComparisonReport(.bf,.log_bf,.register,.certificate); an exact and an estimated side combine only if allowed, and then carry the weaker label.px.grade_model(ds, rep, convention="ambient", alpha=1)— how far the model's evidence falls below the ceiling $\prod_k (n_k/N)^{n_k}$ (pattern counts $n_k$) that bounds every independent-sites model, in nats per site, with two Dirichlet reference values; a compound certificate.px.check_adequacy(data, topology="12|34", model="JC69", register="auto", budget=None, seed=None, workers=None, cache_dir=None),px.prior_predictive(model, topology)— posterior-predictive check on pattern-class frequencies (datais aDataset, counts mapping or FASTA path; the estimate route's budget defaults to 20000;cache_dir=makes the exact and interval routes resumable), and the exact prior-predictive class probabilities.px.pail_evidence(ds, model="JC69", register="auto"),px.pail_row(ds, models=("JC69", "K2P"), register="auto", allow_mixed_registers=False),px.pail_aggregate(rows, labels=None)— tree-free model choice: one model's evidence averaged over the three topologies (PailReport), one window's between-model differences (PailRowReport), and the summary over windows.px.PlugIn(loglik_fn, d, name="plugin")— your own vectorized log-likelihood on thed-dimensional unit cube as a model (estimate register only); three worked plug-ins (a covarion switch, a two-profile mixture, a multispecies-coalescent quartet) are underexamples/, e.g.python3 examples/covarion_plugin.py.
Certificates and environment
px.verify_certificate(cert, dataset=None, recompute="auto", strict=False)— recheck an evidence, comparison, topology-sum, adequacy or grade certificate;dataset=is aDataset, a FASTA path or a mapping;recompute=Truereruns values past the cheap envelope andstrict=Trueturns any skipped check into a failure; returnsok,register,checks,n_performed,n_skipped.px.capabilities()— the import-time probe.px.validate();python -m phyloexact.validate [--gates V1,V6] [--report PATH] [--pin-root DIR] [--parity] [--full] [--tree-sha]— the self-test suite from Python (returnsTruewhen every test passes) or the shell; the options run a subset, move the report, read reference files fromDIRfirst (also$PHYLOEXACT_PIN_ROOT), add the slower tier that recomputes the exact values quoted in the paper (--fullfor every one), or print the checksum of the delivered files and exit.
Alignment scale (px.hill)
px.hill.sup_ceiling_from_counts(counts),pattern_counts(seqs, order, missing="drop"),pattern_counts_from_fasta(path),sup_ceiling(pc),sup_ceiling_partitioned(seqs, order, parts),two_integer_log_ceiling(N, K)— the exact ceiling (Fractionplus certificate) for a count table, for any number of sequences, per partition, or from site and pattern counts alone.px.hill.grade(path, package=None);python -m phyloexact.hill.grader FILE [--package iqtree|paup|mrbayes|auto] [--json OUT]— test a published likelihood table against that ceiling and its own numbers.python -m phyloexact.hill.scan --windows windows_W.npy --out OUTDIR [--budget 20000] [--procs 1] [--force-rescan](or--rows windows.json) — window scan, one JSON line per window plusMANIFEST.json; a rerun on the same input skips finished windows, an--outholding another input's rows is refused unless--force-rescan, and provably tied windows are markedtiedwith no winner.px.hill.emit_weights(scan_path, manifest_path, taxa, outdir, label);python -m phyloexact.hill.weights --scan SCAN_OUT.jsonl --manifest MANIFEST.json --taxa A,B,C,D --out OUTDIR— wASTRAL and wQFM files with sidecar certificates.px.hill.verify_weight_certificate(cert_path, check_sources=True, scan=None, manifest=None, taxa=None, weight_file=None);python -m phyloexact.hill.verify_cert FILE.certificate.json --scan SCAN_OUT.jsonl --taxa A,B,C,D(or--manifest MANIFEST.json)[--weights FILE] [--no-sources] [--json OUT]— recheck a sidecar by re-rendering the weight file you hold from the scan output you hold under your four leaf names; exit 0 verified, 1 failed, 2 unreadable, 3 format checks only (no scan supplied, or--no-sources), 4 format checks only (no taxa supplied).python -m phyloexact.hill.l4_check [FILE],python -m phyloexact.hill.bench run --case CASE.json— a score-gap certificate for a returned species tree from exact scores and per-quartet bounds, and the large exact-JC69 GPU benchmark (BenchUnavailable, exit 2, without CUDA).
Five taxa (px.quintet)
px.quintet.engine()— the exact interval engine; itsrank_topologies(counter, cap=...)bounds each of the 15 trees' evidence on a short window.px.quintet.adjudicate(fasta, name_map=None, retry=True, **kw)— the maximum-likelihood verdict for five records: a report whoseverdictisCERTIFIED_ML_WINNER,CERTIFIED_TOP_SETorABSTAIN, withleader,survivingand thepolicyused. The records are namedH,OT,CO,RN,OG, orname_map={"H": "taxonA", ...}maps those seats to your record names. The default pass allows 25000 evaluations per rival and 100000 in total, with one retry at 75000 and 300000 when it abstains with two to four trees surviving; passingcap=ortotal_budget=runs a single pass with those limits (nstarts=,seed=andfast=are passed through). The compiledfastbnb.sois used when it loads on this CPU; otherwise a slower numpy path gives the same verdicts.px.quintet.a3()— the ranking module itself, for callers who want its functions directly.px.quintet.vendor_path(fname),px.quintet.PROVENANCE— engine files and checksums. The bundledfastbnb.sois checksummed, so never rebuild it in place:build_fastbnb.sh OUT.sobuilds the kernel to a path of yours (setFASTBNB_MARCH=nativeon a CPU without AVX2 or a non-x86-64 machine; keep-ffp-contract=off, no fast-math), then point$PHYLOEXACT_FASTBNB_SOat it and rerun the self-tests.
Used on this site
- Phylogenetics — the package described in the second paper there; its exact values are the reference for that page's benchmark of evidence estimators.
Requirements and source
Python 3.10 or later with numpy, scipy, sympy and mpmath; optional python-flint (intervals), numba (compiled sampling kernels), torch with CUDA (large exact JC69). Install with pip install -e . from the root of a jackandjill-dev checkout or put that directory on PYTHONPATH; the package reads checksummed reference files beside it, so it is not built as a wheel, and files under pins/ and phyloexact/quintet/_vendor/ must not be edited. Self-tests: python3 -m phyloexact.validate. Code: BootLoops' jackandjill-dev repository (GitHub organization BootLoops-ai, beside the bootloops-dev toolkit repository rather than inside it), released under the MIT license. The supplementary material on the phylogenetics page is the full manual: every call with its options, returned objects and refusals, the models available for each kind of answer, and cost planning. The package README marks a few interfaces as experimental (the pail_* calls, non-default priors and plug-in models, the genome-scan command-line tools, the quintet tuning options, the estimate route of the adequacy check): they are tested, but their signatures may change in a later release. Authors: Matthew D. Schwartz, Scott V. Edwards, Paul O. Lewis and Claude (Anthropic).