From caf59b5658da8b4b5ab5f2458bcb880a8f0b9da7 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Fri, 28 Aug 2026 10:26:18 +0200 Subject: [PATCH] Update documentation for CLI command renames and new commands Renames the reindex command to convert across all usage guides and navigation menus. Adds documentation for the newly introduced name-tree command. Updates flag references, such as replacing --metric with --distance in pack and phylo modules, and adjusts MkDocs configuration to match the revised structure. All changes are strictly limited to documentation and configuration files. --- UserDocMD/architecture.md | 2 +- UserDocMD/formats/index_layout.md | 4 +- UserDocMD/index.md | 5 +- UserDocMD/usage/convert.md | 29 + UserDocMD/usage/estimate.md | 2 +- UserDocMD/usage/index_command.md | 2 +- UserDocMD/usage/name-tree.md | 21 - UserDocMD/usage/pack.md | 4 +- UserDocMD/usage/phylo.md | 310 +++-- UserDocMD/usage/reindex.md | 25 - doc/404.html | 32 +- doc/architecture/index.html | 34 +- doc/formats/index_layout/index.html | 36 +- doc/index.html | 42 +- doc/installation/index.html | 32 +- doc/sitemap.xml.gz | Bin 127 -> 127 bytes doc/theory/encoding/index.html | 32 +- doc/theory/entropy_filter/index.html | 32 +- doc/theory/indexing_architecture/index.html | 32 +- doc/theory/kmers_and_superkmers/index.html | 32 +- doc/theory/minimizer_selection/index.html | 32 +- doc/usage/annotate/index.html | 32 +- doc/usage/{reindex => convert}/index.html | 79 +- doc/usage/dump/index.html | 32 +- doc/usage/estimate/index.html | 36 +- doc/usage/filter/index.html | 32 +- doc/usage/index_command/index.html | 34 +- doc/usage/merge/index.html | 32 +- doc/usage/name-tree/index.html | 1235 ------------------- doc/usage/pack/index.html | 40 +- doc/usage/phylo/index.html | 608 +++++---- doc/usage/predicates/index.html | 32 +- doc/usage/query/index.html | 32 +- doc/usage/select/index.html | 32 +- doc/usage/superkmer/index.html | 32 +- doc/usage/unitig/index.html | 34 +- doc/usage/utils/index.html | 34 +- mkdocs-user.yml | 3 +- 38 files changed, 666 insertions(+), 2433 deletions(-) create mode 100644 UserDocMD/usage/convert.md delete mode 100644 UserDocMD/usage/name-tree.md delete mode 100644 UserDocMD/usage/reindex.md rename doc/usage/{reindex => convert}/index.html (90%) delete mode 100644 doc/usage/name-tree/index.html diff --git a/UserDocMD/architecture.md b/UserDocMD/architecture.md index 7c1da714..a3da8b95 100644 --- a/UserDocMD/architecture.md +++ b/UserDocMD/architecture.md @@ -20,7 +20,7 @@ An index directory is organized as `KmerIndex → partitions → layers`, with a ## Parallel execution and NUMA awareness -Partition-level work (index construction, `merge`, `filter`, `reindex`, `select`, `phylo`'s sibling-annex/Sankoff computations) is dispatched by a partition runner that adapts to the machine's memory topology, detected automatically at startup via hwloc: +Partition-level work (index construction, `merge`, `filter`, `convert`, `select`, `phylo`'s sibling-annex/Sankoff computations) is dispatched by a partition runner that adapts to the machine's memory topology, detected automatically at startup via hwloc: - On a multi-socket / multi-NUMA-node machine, one thread pool is pinned per NUMA node, and each partition is processed entirely by threads pinned to one node — keeping the memory a partition touches local to that node's DRAM. This matters because touching kmer data across NUMA nodes without pinning can degrade throughput by an order of magnitude or more on large multi-socket machines. - On a single-socket machine, Apple Silicon, or if hwloc cannot report NUMA topology, all cores are treated as one node with no pinning and negligible overhead — this is the default behavior on macOS. diff --git a/UserDocMD/formats/index_layout.md b/UserDocMD/formats/index_layout.md index 09633b09..b590364f 100644 --- a/UserDocMD/formats/index_layout.md +++ b/UserDocMD/formats/index_layout.md @@ -19,7 +19,7 @@ Each partition's surviving kmers are mapped to a dense range of integer slots by ## Evidence: exact vs. approximate -Two verification modes are available, selected at build time (`index --approx`) and convertible afterwards ([`reindex`](../usage/reindex.md)): +Two verification modes are available, selected at build time (`index --approx`) and convertible afterwards ([`convert`](../usage/convert.md)): - **Exact** (default): the hashed slot stores a pointer back into the partition's unitig data. At query time the kmer is reconstructed from that location and compared directly to the query. Zero false positives, at the cost of one extra random read per lookup. - **Approximate** (`--approx`): the slot stores a short fingerprint (`--evidence-bits` bits) instead of a pointer; verification is a single fingerprint comparison. This trades a small, bounded false-positive rate ($1/2^b$ per kmer, reduced further to about $1/2^{b \cdot z}$ for a read requiring $z$ consecutive matching kmers via the `-z`/`--findere-z` parameter) for lower memory and disk usage, since no reconstruction index is needed. See [`estimate`](../usage/estimate.md) to explore this trade-off before building. @@ -51,6 +51,6 @@ Two verification modes are available, selected at build time (`index --approx`) A **layer** corresponds to one increment of kmer content added to a partition — most commonly, one [`merge`](../usage/merge.md) operation that introduces kmers not already present in the index. Genomes already present in the index simply gain new columns in the existing layers' count/presence data; only genuinely new kmer content is assembled into a new layer. Because of this, merging cost scales with the novel kmer content being added, not with the accumulated size of the index. A query against an index with several layers checks each layer's MPHF in turn. -Sources merged together must share the same kmer size, minimizer size, partition count, and evidence mode (including matching approximate-mode parameters); mismatches are rejected rather than silently reconciled — [`reindex`](../usage/reindex.md) one of the sources first if needed. +Sources merged together must share the same kmer size, minimizer size, partition count, and evidence mode (including matching approximate-mode parameters); mismatches are rejected rather than silently reconciled — [`convert`](../usage/convert.md) one of the sources first if needed. `obikmer pack` consolidates a partition's per-column files (counts/presence) into a single file, reducing the number of file opens needed at query time. diff --git a/UserDocMD/index.md b/UserDocMD/index.md index 10aa2c2e..aa1fcc7b 100644 --- a/UserDocMD/index.md +++ b/UserDocMD/index.md @@ -24,11 +24,10 @@ All functionality is exposed through a single binary, `obikmer`, organized as su | [`query`](usage/query.md) | Query an index with sequences and annotate matches | | [`dump`](usage/dump.md) | Dump indexed kmers as CSV | | [`annotate`](usage/annotate.md) | Add, update, or dump genome metadata | -| [`phylo`](usage/phylo.md) | Compute pairwise evolutionary-distance proxies, trees, and phylogenetic exports | -| [`name-tree`](usage/name-tree.md) | Translate a TNT/PhyG numeric-label tree export back to real taxon names | +| [`phylo`](usage/phylo.md) | Compute pairwise genome distances, trees, and phylogenetic exports | | [`unitig`](usage/unitig.md) | Dump the unitigs of an index as FASTA | | [`estimate`](usage/estimate.md) | Estimate approximate-index parameters before indexing | -| [`reindex`](usage/reindex.md) | Convert an index's evidence representation (exact ↔ approximate) | +| [`convert`](usage/convert.md) | Convert an index's evidence representation (exact/approximate/hybrid), in place | | [`utils`](usage/utils.md) | Miscellaneous index maintenance and inspection utilities | | [`pack`](usage/pack.md) | Pack per-column matrix files into a single-file format | diff --git a/UserDocMD/usage/convert.md b/UserDocMD/usage/convert.md new file mode 100644 index 00000000..f2a2b44a --- /dev/null +++ b/UserDocMD/usage/convert.md @@ -0,0 +1,29 @@ +# convert + +Convert an existing index's evidence representation in place, between exact, approximate, and hybrid. + +```bash +obikmer convert INDEX (--exact-evidence | --approx-evidence BITS | --hybrid-evidence) [OPTIONS] +``` + +## Arguments + +| Argument | Description | +|---|---| +| `INDEX` | Index directory to convert (modified in place) | + +## Options + +Exactly one of the first three is required: + +| Option | Description | +|---|---| +| `--exact-evidence` | Convert to exact evidence (zero false positives) | +| `--approx-evidence BITS` | Convert to approximate (fingerprint-only) evidence; `BITS` = fingerprint bits per slot (b) | +| `--hybrid-evidence` | Convert to hybrid evidence (both exact and approximate bundles kept) | +| `--evidence-bits BITS` | Fingerprint bits per slot (b) — required with `--hybrid-evidence` when the source index is currently exact; rejected otherwise (the source already fixes `b`) | +| `-z, --findere-z Z` | Findere z parameter: number of consecutive stored kmers that must all match to confirm a hit. This does not shorten the indexed kmer length (fixed forever at `index` build time) — it extends the effective match window: on a k=31 index, `z=2` requires 32 consecutive matching bases, not 30 | +| `--fp FP` | Target false-positive rate per z-window (e.g. `0.01`); derives `b` or `z` when one of them isn't given directly | +| `--block-size N` | Block size for exact evidence's on-disk index (unitigs per block). Ignored when converting to pure approximate evidence. Default `1` | + +See [`index`](index_command.md#exact-vs-approximate-evidence) for the exact/approximate trade-off and the underlying false-positive model, and [`estimate`](estimate.md) to explore parameters beforehand. The index directory is locked for exclusive access during conversion. diff --git a/UserDocMD/usage/estimate.md b/UserDocMD/usage/estimate.md index 64ee85a1..a8f326fb 100644 --- a/UserDocMD/usage/estimate.md +++ b/UserDocMD/usage/estimate.md @@ -15,4 +15,4 @@ obikmer estimate [OPTIONS] | `--evidence-bits` | none | Fingerprint bits per slot (b) | | `--fp` | none | Target false-positive rate per z-window | -Any two of `-z`, `--evidence-bits`, `--fp` may be given; the third is derived using the same model as `index --approx` and `reindex --approx` ($FP = 1 / 2^{b \cdot z}$). The report printed to stdout includes: query $k$, effective indexed $k$ ($k-z+1$), $z$, evidence bits, per-kmer false-positive rate, and per-z-window false-positive rate. +Any two of `-z`, `--evidence-bits`, `--fp` may be given; the third is derived using the same model as `index --approx` and `convert --approx-evidence` ($FP = 1 / 2^{b \cdot z}$). The report printed to stdout includes: query $k$, effective indexed $k$ ($k-z+1$), $z$, evidence bits, per-kmer false-positive rate, and per-z-window false-positive rate. diff --git a/UserDocMD/usage/index_command.md b/UserDocMD/usage/index_command.md index d887e1b6..5c1861a7 100644 --- a/UserDocMD/usage/index_command.md +++ b/UserDocMD/usage/index_command.md @@ -45,6 +45,6 @@ With `--approx`, evidence is stored as a compact **fingerprint** instead, tradin $$FP = \frac{1}{2^{b \cdot z}}$$ -where $b$ is `--evidence-bits` and $z$ is `--findere-z`. Any two of `-z`, `--evidence-bits`, `--fp` can be given and the third is derived; if none are given, defaults are $b=8$, $z=1$ ($FP \approx 1/256$). See [`estimate`](estimate.md) to explore this trade-off before building an index, and [`reindex`](reindex.md) to convert an existing index between the two representations. +where $b$ is `--evidence-bits` and $z$ is `--findere-z`. Any two of `-z`, `--evidence-bits`, `--fp` can be given and the third is derived; if none are given, defaults are $b=8$, $z=1$ ($FP \approx 1/256$). See [`estimate`](estimate.md) to explore this trade-off before building an index, and [`convert`](convert.md) to change an existing index's representation afterwards. `z` must be strictly less than k: the effective indexed kmer length under approximate evidence is k−z+1. diff --git a/UserDocMD/usage/name-tree.md b/UserDocMD/usage/name-tree.md deleted file mode 100644 index 70f2943c..00000000 --- a/UserDocMD/usage/name-tree.md +++ /dev/null @@ -1,21 +0,0 @@ -# name-tree - -Translate a numerically-labelled tree export (TNT, PhyG, or any plain Newick file with bare `1`, `2`, `3`, … leaf labels) back to real taxon names, reading the label order from the FASTA that produced it. - -```bash -obikmer name-tree TREE --fasta FASTA -o OUTPUT -``` - -## Arguments - -| Argument | Description | -|---|---| -| `TREE` | Tree file to translate — a TNT-style NEXUS export (`tree NAME = [&U] ...;`) or a plain Newick file | -| `--fasta` | FASTA file whose record order gives the numeric taxon labels (1-based) — typically the `_sankoff.fasta`/`_snp.fasta` used to produce `TREE` | -| `-o, --output` | Output NEXUS file path | - -## Output - -A NEXUS file with a `taxa` block, a `translate` table (numeric label → taxon name, from `--fasta`'s header order), and every tree found in `TREE`, topology unchanged — readable directly in FigTree, PearTree, `ape` (R), etc. - -`--tnt`'s and `--phyg`'s exports (see [phylo](phylo.md)) both number taxa `1..N` in the same order as the pseudo-alignment FASTA they were built from (`_sankoff.fasta`), so pass that same file as `--fasta` here. diff --git a/UserDocMD/usage/pack.md b/UserDocMD/usage/pack.md index b8eeb6e4..cdffa707 100644 --- a/UserDocMD/usage/pack.md +++ b/UserDocMD/usage/pack.md @@ -24,6 +24,6 @@ The index directory is locked for exclusive access while packing. Matrix data (which genomes carry each kmer, or with what count) is often mostly empty — most kmers are present in only a handful of genomes out of the whole collection. The default (dense) packed format stores one entry per genome for every kmer regardless of how many genomes actually carry it; `--sparse` instead stores each kmer's genome list directly. For presence/absence matrices, identical genome lists shared by many kmers are also deduplicated (common in real data, since kmers from the same conserved region tend to be carried by the same genomes); for count matrices, the genome list is deduplicated the same way but each kmer's actual counts are kept per-kmer, since two kmers sharing the same genome list rarely carry the same counts. -On real genome collections this has measured at roughly 7x smaller on disk than the dense format for presence/absence, and single-kmer lookups (the shape `phylo`'s sibling-annex/entropy/Sankoff computations use) are typically faster too, since the smaller files mean less data to read from disk. The trade-off: reading a whole genome column at once (used by `--metric` distance-matrix computations) is much slower on the sparse format than on the dense one, since there is no native column layout to read sequentially — prefer the dense format (the default, no `--sparse`) for indexes you mainly query with `phylo`'s plain `--metric` distance matrices. +On real genome collections this has measured at roughly 7x smaller on disk than the dense format for presence/absence, and single-kmer lookups (the shape `phylo`'s sibling-annex/entropy/Sankoff computations use) are typically faster too, since the smaller files mean less data to read from disk. The trade-off: reading a whole genome column at once (used by `--distance` matrix computations) is much slower on the sparse format than on the dense one, since there is no native column layout to read sequentially — prefer the dense format (the default, no `--sparse`) for indexes you mainly query with `phylo`'s `--distance` matrices. -`--sparse` applies to both presence/absence and count matrices — a count index (`--metric` distance matrices included) is packed sparse the same as a presence index. +`--sparse` applies to both presence/absence and count matrices — a count index (`--distance` matrix computations included) is packed sparse the same as a presence index. diff --git a/UserDocMD/usage/phylo.md b/UserDocMD/usage/phylo.md index 7da90cc9..f933ddd4 100644 --- a/UserDocMD/usage/phylo.md +++ b/UserDocMD/usage/phylo.md @@ -1,6 +1,6 @@ # phylo -Compute pairwise evolutionary-distance proxies between the genomes stored in an index — a plain distance matrix, optionally trees (NJ/UPGMA), and optionally a central-position SNP model with exports for external phylogenetic tools (TNT, PhyG, IQ-TREE). +Compute pairwise distances between the genomes stored in an index, optionally build trees (NJ/UPGMA) from them, and optionally calibrate a 16-state parsimony model for a central-position SNP character with exports for external phylogenetic tools (TNT, PhyG, IQ-TREE). ```bash obikmer phylo INDEX [OPTIONS] @@ -12,133 +12,217 @@ obikmer phylo INDEX [OPTIONS] |---|---| | `INDEX` | Index directory | -## Distance matrix +## Distance matrix (`--distance`) | Option | Default | Description | |---|---|---| -| `--metric` | `jaccard` | One of `jaccard`, `mash`, `hamming`, `bray-curtis`, `relfreq-bray-curtis`, `euclidean`, `relfreq-euclidean`, `hellinger`, `hellinger-euclidean` | -| `--presence-threshold` | `1` | Minimum count for a kmer to be considered present, for Jaccard/Mash on a count index | -| `--shared-kmers` | off | Also write the shared-kmer count matrix | +| `--distance` | `jaccard` | See the two tables below for the full list of accepted values | +| `--gamma-shape ALPHA` | none | Rate-heterogeneity correction, for `snp-*` values that support it (see below). No effect on the other values; rejected if given together with a value that doesn't support it | +| `--presence-threshold` | `1` | Minimum count for a kmer to be considered present, for `jaccard`/`mash` on a count index | +| `--csv` | off | Write the matrix as plain CSV instead of the default relaxed-PHYLIP format | +| `--shared-kmers` | off | Also write the shared-kmer count matrix. Only valid with a whole-index metric, not a `snp-*` value | | `--nj` | off | Compute and write a Neighbor-Joining tree (Newick) | | `--upgma` | off | Compute and write a UPGMA tree (Newick) | -| `-o, --output` | none (stdout) | Output file prefix; without it, the distance matrix is printed to stdout as CSV | +| `-o, --output` | none (stdout) | Output file prefix | -`hamming` requires a presence/absence index. All other metrics work on either index type; on a presence index, `jaccard`/`mash`/`hamming` are the only ones available. +Every value routes to one of two independent computations: -### Metric definitions +### Whole-index metrics -- **jaccard**: $D = 1 - \dfrac{|A \cap B|}{|A \cup B|}$ over the sets of kmers present in each genome. -- **mash**: derived from the Jaccard distance via $D = -\dfrac{1}{k} \ln\!\left(\dfrac{2J}{1+J}\right)$ where $J = 1 - D_{\text{jaccard}}$ and $k$ is the index's kmer size; clamped to 1.0 when $J \le 0$. -- **hamming**: number of kmer positions where presence differs between the two genomes (presence index only, not normalized): $D = \sum_i \mathbb{1}[a_i \ne b_i]$. -- **bray-curtis**: $D = 1 - \dfrac{2 \sum_i \min(c_i^A, c_i^B)}{\sum_i c_i^A + \sum_i c_i^B}$ on raw per-kmer counts. -- **relfreq-bray-curtis**: the same formula computed on per-genome relative frequencies $p_i = c_i / \sum_j c_j$ instead of raw counts. -- **euclidean**: $D = \sqrt{\sum_i (c_i^A - c_i^B)^2}$ on raw counts. -- **relfreq-euclidean**: the same formula on relative frequencies. -- **hellinger**: $D = \dfrac{1}{\sqrt{2}} \sqrt{\sum_i \left(\sqrt{p_i^A} - \sqrt{p_i^B}\right)^2}$ on relative frequencies, bounded in $[0, 1]$. -- **hellinger-euclidean**: the unnormalized variant, $D = \sqrt{2} \times D_{\text{hellinger}}$. +| Value | Definition | +|---|---| +| `jaccard` | $D = 1 - \dfrac{\lvert A \cap B \rvert}{\lvert A \cup B \rvert}$ over the sets of kmers present in each genome | +| `mash` | derived from the Jaccard distance via $D = -\dfrac{1}{k} \ln\!\left(\dfrac{2J}{1+J}\right)$ where $J = 1 - D_{\text{jaccard}}$ and $k$ is the index's kmer size; clamped to 1.0 when $J \le 0$ | +| `hamming` | number of kmer positions where presence differs between the two genomes (presence index only, not normalized): $D = \sum_i \mathbb{1}[a_i \ne b_i]$ | +| `bray-curtis` | $D = 1 - \dfrac{2 \sum_i \min(c_i^A, c_i^B)}{\sum_i c_i^A + \sum_i c_i^B}$ on raw per-kmer counts | +| `relfreq-bray-curtis` | the same formula computed on per-genome relative frequencies $p_i = c_i / \sum_j c_j$ instead of raw counts | +| `euclidean` | $D = \sqrt{\sum_i (c_i^A - c_i^B)^2}$ on raw counts | +| `relfreq-euclidean` | the same formula on relative frequencies | +| `hellinger` | $D = \dfrac{1}{\sqrt{2}} \sqrt{\sum_i \left(\sqrt{p_i^A} - \sqrt{p_i^B}\right)^2}$ on relative frequencies, bounded in $[0, 1]$ | +| `hellinger-euclidean` | the unnormalized variant, $D = \sqrt{2} \times D_{\text{hellinger}}$ | + +`hamming` requires a presence/absence index; the others work on either index type. + +### `snp-*` corrections + +Computed from the central-position SNP model (see "Central-position SNP model" below): a family is the set of up to 4 kmers sharing identical flanking sequence and differing only at the central base. These values require the sibling annex (`--sibling-annex`, below) and are, by default, computed exhaustively over every non-monomorphic family in the index; add `--subsample N` to bound the computation to approximately `N` families instead (see "Sampling at scale" below — the same flag `--pseudo-alignment`/`--sankoff` use, but optional here). + +For a genome pair, let $L$ be its total number of eligible loci (both genomes single-copy at that family), $p$ the raw proportion of substitutions among those loci, $P$/$Q$ the transition/transversion proportions, $Q_1$/$Q_2$ Kimura's two transversion categories (A↔C & G↔T vs. A↔T & C↔G), $P_1$/$P_2$ the purine (A↔G) / pyrimidine (C↔T) transition proportions, and $\pi_A,\pi_C,\pi_G,\pi_T$ the pair's pooled base frequencies. + +**`snp-raw`** + +$$d = p$$ + +**`snp-jc`** + +$$d = -\frac{3}{4}\ln\!\left(1-\frac{4p}{3}\right)$$ + +**`snp-k2p`** + +$$ +\begin{aligned} +a_1 &= 1-2P-Q \\ +a_2 &= 1-2Q \\ +d &= -\frac{1}{2}\ln a_1-\frac{1}{4}\ln a_2 +\end{aligned} +$$ + +**`snp-k81`** + +$$ +\begin{aligned} +a_1 &= 1-2P-2Q_1 \\ +a_2 &= 1-2P-2Q_2 \\ +a_3 &= 1-2Q_1-2Q_2 \\ +d &= -\frac{1}{4}\left(\ln a_1+\ln a_2+\ln a_3\right) +\end{aligned} +$$ + +**`snp-f81`** + +$$ +\begin{aligned} +E &= 1-\left(\pi_A^2+\pi_C^2+\pi_G^2+\pi_T^2\right) \\ +d &= -E\ln\!\left(1-\frac{p}{E}\right) +\end{aligned} +$$ + +**`snp-t92`** + +$$ +\begin{aligned} +g &= \pi_C+\pi_G \\ +w &= 2g(1-g) \\ +a_1 &= 1-\frac{P}{w}-Q \\ +a_2 &= 1-2Q \\ +d &= -w\ln a_1-\frac{1}{2}(1-w)\ln a_2 +\end{aligned} +$$ + +**`snp-tn93`** + +$$ +\begin{aligned} +g_R &= \pi_A+\pi_G \\ +g_Y &= \pi_C+\pi_T \\ +k_1 &= \frac{2\pi_A\pi_G}{g_R} \\ +k_2 &= \frac{2\pi_C\pi_T}{g_Y} \\ +k_3 &= 2\left(g_Rg_Y-\frac{\pi_A\pi_G\,g_Y}{g_R}-\frac{\pi_C\pi_T\,g_R}{g_Y}\right) \\ +w_1 &= 1-\frac{P_1}{k_1}-\frac{Q}{2g_R} \\ +w_2 &= 1-\frac{P_2}{k_2}-\frac{Q}{2g_Y} \\ +w_3 &= 1-\frac{Q}{2g_Rg_Y} \\ +d &= -k_1\ln w_1-k_2\ln w_2-k_3\ln w_3 +\end{aligned} +$$ + +**`snp-tv`** — transversions only, deliberately uncorrected: + +$$d = Q$$ + +`--gamma-shape ALPHA` applies to every value above except `snp-raw` and `snp-tv`: each $-\ln(x)$ term in the formulas above is replaced by $\alpha\left(x^{-1/\alpha}-1\right)$ (the same weight, same $x$). + +### Output + +Without `-o`, the matrix goes to stdout in relaxed-PHYLIP format (`n` on the first line, then one `labelvalue...` row per genome). With `--csv`, the format is instead a header row `genome,,,...` followed by one `