feat: add multi-genome SNP pseudo-alignment and CLI export
Release / create-release (push) Successful in 5m58s
Release / build-macos-arm64 (push) Successful in 2m47s
Release / build-linux-x86_64 (push) Successful in 8m50s
CI / build (pull_request) Canceled after 5m42s

Introduces a `SnpAlignment` struct and helper methods to construct per-genome SNP pseudo-alignments from sibling k-mer data, filtering monomorphic families and encoding bases as IUPAC ambiguity codes. Exposes the type at the crate root for simplified imports. Adds a `--snp` CLI flag to compute and export these alignments as an IUPAC-coded FASTA file. Updates theory documentation to propose a multi-genome framing approach for joint phylogenetic inference, resolving pairwise correspondence ambiguities through positional homology and partial coverage thresholds. Bumps crate version to 1.1.40.
This commit is contained in:
Eric Coissac
2026-08-10 22:38:38 +02:00
parent 49f329edd5
commit f5e508ed33
6 changed files with 282 additions and 14 deletions
+57
View File
@@ -146,6 +146,63 @@ A once multiplicity > 1 on either side. Any pairing rule invents a
correspondence the data cannot support. Multiplicity > 1 is treated as
non-identifiable, not as a puzzle to solve with a heuristic.
## Multi-genome framing: family as pseudo-alignment column
**Idea.** Instead of resolving locus eligibility and correspondence one
genome pair at a time, treat a family as a column of a pseudo multiple
alignment across *all* genomes simultaneously: for each family, each genome
has either a net single-copy state (`A`/`C`/`G`/`T`, when the genome carries
exactly one of the 4 forms) or "missing" (`?`, multi-copy or absent). Flank
conservation (the `2m` bases fixed by construction) supplies positional
homology for free — the same role a real MSA would play, without alignment
software, gap penalties, or progressive-alignment approximations. Stacking
one such column per family, genomes as rows, produces a genuine SNP
pseudo-alignment matrix, not just a bag of pairwise distances.
**Precedent.** This is the same principle behind reference-free
k-mer-based phylogenomics tools — SKA (Split K-mer Analysis, Harris 2018) and
kSNP: split the k-mer around a variable center, use flank identity to call
homologous columns across arbitrarily many genomes with no reference and no
MSA step, then feed the resulting pseudo-alignment to standard phylogenetic
tools. Landing on the same design independently is a good sign, not a
coincidence.
**Resolves the pairwise-correspondence problem, properly.** The "Rejected:
parsimony-based multiset pairing" case above failed because, with only two
genomes' cardinalities to look at, there is no external constraint to justify
picking one correspondence between leftover alleles over another — `min(a,b)`
is a lower bound dressed up as a point estimate (see the follow-up discussion
on Felsenstein-style parsimony inconsistency: minimum-event explanations are
systematically biased low whenever homoplasy/multiplicity is real, not
noise-cancelling). With `N` genomes and many families jointly, the same
question can be answered the way real phylogenetics answers it: ancestral
state reconstruction / ML mapping over a tree estimated from the whole
column set. The tree supplies the missing constraint that two isolated
columns cannot — this is the principled way out, not a heuristic replacement
for one.
**Relation to what's already implemented.** `KmerIndex::raw_snp_distance`
already computes, internally, per family, exactly this row — `single_form:
Vec<Option<u8>>`, one entry per genome, `None` where ambiguous/absent —
before immediately collapsing it into pairwise `snp[i,j]`/`shared[i,j]`
tallies. The pivot this section proposes is small at the implementation
level: stop collapsing early, and surface the per-family row as a first-class
artifact (a `families x genomes` matrix). Pairwise raw p-distance becomes one
projection of that matrix (what's computed today), not the primary object;
downstream, the matrix itself could feed real phylogenetic tools (parsimony/
ML, e.g. RAxML/IQ-TREE-style) instead of only NJ/UPGMA on a homemade
pairwise-distance matrix.
**Caveat: column completeness shrinks with `N`.** The probability that a
family's flanks stay intact simultaneously across all `N` genomes decays with
`N` (same ascertainment-bias mechanism as Bias 1 above, compounded over more
genomes) — fully-resolved columns (no `?` anywhere) become rare as more
genomes are added. Same missing-data situation any real multi-species
alignment faces, and phylogenetic tools already handle it well; the practical
implication is that columns should be allowed partial coverage (>=2 resolved
genomes, not unanimous) rather than requiring every genome to be net
single-copy at that locus.
## Heterozygosity, ploidy, and consensus-assembly inputs
A within-genome multiplicity signal (more than one of the 4 central forms