rename distance subcommand to phylo

Rename the distance CLI subcommand to phylo across the codebase, documentation, and build configurations. Relocate source files from cmd/distance/ to a dedicated cmd/phylo/ module, update all internal routing references, and adjust benchmark scripts and Makefile targets to reflect the new command name.
This commit is contained in:
Eric Coissac
2026-08-15 10:07:03 +02:00
parent 79346c0c86
commit dd889854cb
19 changed files with 49 additions and 46 deletions
+11 -11
View File
@@ -205,7 +205,7 @@ single-copy at that locus.
## Context, detectability, and a 3-way ordinal distance per pair
Empirical follow-up to the pseudo-alignment idea above: `obikmer distance
Empirical follow-up to the pseudo-alignment idea above: `obikmer phylo
--snp` was run on a real 20-genome benchmark index and the resulting FASTA
fed to `raxml-ng`. Two problems surfaced, both traced back to conflating
distinct notions under one symbol.
@@ -552,7 +552,7 @@ informative across domains with this character type. Exploratory only —
run entirely outside the repo (`/tmp/tnt_run`, TNT installed locally under
`TNT/`), no new Rust code. Kept here as the record of what was learned.
**Pipeline.** `obikmer distance --snp` emits one IUPAC-coded pseudo-alignment
**Pipeline.** `obikmer phylo --snp` emits one IUPAC-coded pseudo-alignment
row per genome (`snp_pseudo_alignment`, one column per family with
`family_size() >= 2`). A small external Python script decodes IUPAC back to
the 16-state bitmask, applies the set-edit-distance formula above, and
@@ -630,7 +630,7 @@ it, no external script needed. `∅` is an ordinary 16th state throughout
(never `-`), specifically to avoid gap-semantics confusion in downstream
tools — see "A concrete Sankoff cost matrix" above for why.
**TNT (`--tnt`).** `write_sankoff_tnt` (`obikmer/src/cmd/distance.rs`)
**TNT (`--tnt`).** `write_sankoff_tnt` (`obikmer/src/cmd/phylo/mod.rs`)
recodes to TNT's own `0-9A-F` xread alphabet (its default reader rejects
the wider IUPAC set otherwise), scales and rounds costs to integers
(`smatrix`/`cost` reject decimals), then re-runs integer Floyd-Warshall on
@@ -902,7 +902,7 @@ sections below, which implement and then substantially revise this.
### `R`/`π` implemented; `--exclude-genome` added; rogue-taxon test negative (2026-08-12)
`write_iqtree` (`obikmer/src/cmd/distance/iqtree.rs`) implements exactly
`write_iqtree` (`obikmer/src/cmd/phylo/iqtree.rs`) implements exactly
the `R = exp(-cost)` / empirical-`π` design above: writes
`<prefix>_iqtree.model` (lower-triangular `R`, PAML order, then `π`) and
`<prefix>_iqtree.fasta` (alignment recoded to the compact `0..k-1`
@@ -949,7 +949,7 @@ tell: with `n_eligible=1`, the ratio can only be exactly `0` or `1`, so
possibly ascertainment bias — see below — or possibly a few genuinely
ultra-conserved loci; not resolved).
**`--exclude-genome LABEL`** (repeatable, `obikmer distance`) added for
**`--exclude-genome LABEL`** (repeatable, `obikmer phylo`) added for
exactly this kind of test: zeroes the excluded genome's row/column in
`RawSnpDistanceOutput` after `raw_snp_distance` runs (a pair with zero
counts is already skipped by `calibrate_p_hat`/`base_pair_tally` — no
@@ -1165,7 +1165,7 @@ silently wrong data for TNT/PhyG, and a hard failure for IQ-TREE's `+ASC`
(verified: excluding 2 taxa on the benchmark left 116,351 such columns —
matches the manual `+ASC` failures hit earlier in this same investigation,
before `--exclude-genome` existed). Fixed in `drop_excluded`
(`obikmer/src/cmd/distance/mod.rs`): after dropping excluded rows,
(`obikmer/src/cmd/phylo/mod.rs`): after dropping excluded rows,
re-scan each column among the *surviving* sequences and drop any that are
now constant. Verified: 908,723 → 792,372 sites after excluding 2 taxa,
zero monomorphic columns remain, `π` recomputed from the corrected
@@ -1582,7 +1582,7 @@ index, not of any single genome — it cannot be computed correctly at
mono-genome build time (a family may gain siblings, or its minorant may
change, once more genomes are merged in later). Computing it eagerly at
every `merge` would also waste work on intermediate merged states nobody
ever queries. Instead: compute it lazily, on first `distance` call against a
ever queries. Instead: compute it lazily, on first `phylo` call against a
given index, and persist the result alongside that index for subsequent
calls — the same lazy-derived-cache pattern `PersistentBitMatrix` already
uses for `Columnar` -> `Packed`. This requires no explicit invalidation for
@@ -1745,7 +1745,7 @@ above) is observed anywhere in the index — fixes both:
own 3 lookups independently," not a strict improvement to adopt by
default.
4. **Trigger and caching** (`obikindex::KmerIndex`/`distance.rs`): compute
lazily on first `distance` call for an SNP-family metric against a given
lazily on first `phylo` call for an SNP-family metric against a given
index; check for an existing annex file first (mirrors
`PersistentBitMatrix::open()`'s auto-detect-and-fall-back,
`bitmatrix.rs:264-287`); if absent, run step 3 and persist; if present,
@@ -1765,7 +1765,7 @@ above) is observed anywhere in the index — fixes both:
slots are never looked up cross-partition during the sweep.
Cost: `3 * N_distinct` existence-only lookups, computed once per index
state and amortised over every subsequent `distance` call that reuses the
state and amortised over every subsequent `phylo` call that reuses the
cached annex — cheaper per-lookup than the sweep itself (hit/miss only, no
column fetch).
@@ -1795,11 +1795,11 @@ intermediate). Two options, to decide:
Keeps one CLI surface (`--metric jukes-cantor`), at the cost of a branch in
`distance()` that ignores the `LayeredStore` it built.
- **(b)** A dedicated pathway (`KmerIndex::snp_distance`) and a distinct CLI
entry, if mixing a cross-partition sweep into the partition-local `distance`
entry, if mixing a cross-partition sweep into the partition-local `phylo`
command is judged architecturally muddy.
Recommendation: (a) for user ergonomics (all pairwise distances under
`distance`, all feeding NJ/UPGMA/`--shared-kmers` unchanged), but compute the
`phylo`, all feeding NJ/UPGMA/`--shared-kmers` unchanged), but compute the
sweep lazily only when an SNP-family metric is requested, so the existing
metrics keep their partition-local fast path untouched.