oarfish coverage benchmarking — handoff (2026-08-15)
Written to be picked up cold. It covers what the evaluation panels are, where the data lives, what has been decided and why, and the accuracy leads that were measured but not pursued.
Everything below was verified present on 2026-08-15.
1. Where things stand
dev is at ac0248c and in sync with origin/dev. The coverage-model program
that grew after the logistic model was measured against it on data with exact
read-level truth, and retired: --coverage-model now accepts only
none|logistic.
| change | evidence |
|---|---|
| removed rank blending, dominance pruning | -0.0878 / -0.0437 Spearman, worst family |
| removed alignment calibration, censoring (both had shipped on) | -0.000143 / -0.000233 on exact truth |
removed auto\|adaptive\|endpoint\|hybrid\|degradation + 4 modules |
endpoint -0.0428 (worse than no coverage model at all); auto -0.0004 at +25.9% wall |
--score-prob-denom default 5 → 3 |
improved 32 of 34 samples |
added --model-unannotated-isoforms (genome mode) |
detection precision 1.000; -10.7% error at acted-on loci |
Retired code is preserved, each branch carrying a "do not merge without new evidence" note:
archive/coverage-extras-2026-08-03— the four extrasarchive/coverage-kernels-2026-08-03— the five non-logistic kernelsarchive/junction-endpoint-2026-08-03— splice-junction endpoint termwip/coverage-and-annotation-2026-07-25— in-flight work; three of its four threads were deliberately not ported (endpoint grid geometry, further coverage experiments, evaluation-tier methodology)
History caveat.
fef285b(the WIP commit) is an ancestor ofdevvia the port merge, so those three unported threads read as "already merged" although their content was resolved away.git merge wip/...is a no-op. The archive branches are their real home.
2. The panels
The single most important fact for anyone benchmarking here: the two panels
disagree in sign on essentially every arm. Panel A ranks none above
logistic; Panel B does not. Any conclusion drawn from Panel A alone is
unreliable, and that is how the retired features were selected in the first
place.
Panel B — exact read-level truth. Use this one.
Six transcriptome BAMs, 177,816 RefSeq identifiers, realistic depth
(320–338 reads per expressed transcript). NanoSim read names encode the
generating transcript (NM_015477_2260_aligned_...), so per-read truth is
available, not just per-transcript counts.
| sample | BAM (under oarfish-evaluation-data/sim-panel/) |
--seq-tech |
extra |
|---|---|---|---|
| nanosim-NA12878-cdna | nanosim/NA12878_1DcDNA/cdna_T.bam (11G) |
ont-cdna |
|
| nanosim-NA12878-drna | nanosim/NA12878_directRNA/drna_T.bam (11G) |
ont-drna |
-d fw |
| nanosim-H9-cdna | nanosim/H9_1DcDNA/cdna_T.bam (15G) |
ont-cdna |
|
| nanosim-H9-drna | nanosim/H9_directRNA/drna_T.bam (18G) |
ont-drna |
-d fw |
| tksm-RSII | tksm/RSII_T.bam (14G) |
pac-bio |
|
| tksm-SQ2 | tksm/SQ2_T.bam (13G) |
pac-bio-hifi |
Truth: sim-panel/nanosim/ground_truth/*.csv, sim-panel/tksm/ground_truth/*.csv.
Manifest: rebench-2026-08-03/panelB_manifest.tsv.
Upstream copies (NFS, mounted on this host — no ssh needed):
/fs/cbcb-lab/rob/students/zahra/{nanosim_data,tksm_new_model}.
Truth-format traps. NanoSim truth is headerless and unversioned. TKSM truth
has a Transcript_ID/Count header and versioned accessions. Strip
\.\d+$ on both sides before joining.
Panel A — proxy truth. Treat as a weak comparator.
- 28 LongBench samples,
benchmarks/coverage_ablation_manifest.tsv(8 cell lines x ONT-cDNA/ONT-dRNA/PacBio, plus 3 PacBio 250k). Truth is matched Illumina, quantified against a 252k annotation while the BAMs carry 386k — score only over the shared reference (251,488), never the union. Absolute Spearman is 0.21–0.43; on this panelnonebeatslogisticby +0.0055. - 12 SIRV / spike-in samples,
rebench-2026-08-03/panelA_sirv_manifest.tsv, read mode. SIRV E0 is equal-molar, so truth variance is zero and every rank metric is undefined — reportNA, not0.0. Only the 4 E2 samples support Spearman. The 2independent-simsamples have exact counts but a near-degenerate truth (max 6 reads/transcript, 81% singletons).
Genome-mode inputs
eval/parity/genome.bam(2G) — spliced genome BAM, NanoSim read names, so per-read truth; 1,356,492 reads project under the full annotationeval/GCF.pc_lncrna.matched.gtf,test_data/GCF_000001405.40_GRCh38.p14_genomic.fnatest_data/cdna_ground_truth.csv— the expressed set, for building holdouts- GTFs for junction work:
rebench-2026-08-03/junc/refseq.gtf(RefSeq, matches Panel B) andjunc/gencode.v47.gtf(matches LongBench; reconciles perfectly, 0 length mismatches, vs 4,925 for RefSeq)
/scratch1is node-local and not backed up. Panel B is re-copyable from the NFS paths above; the LongBench BAMs andeval/parity/genome.bamare not known to exist elsewhere.
3. Harness
rebench-2026-08-03/ (also committed under scripts/rebench-2026-08-03/ and
scripts/omission-validation-2026-08-04/):
| file | purpose |
|---|---|
run_arms.py |
run N arms x M samples; alignment and read mode; captures wall/RSS |
metrics.py |
Spearman, Kendall tau-b, Pearson/CCC on log1p, RMSE, MARD |
analyze.py |
per-sample and per-family tables, deltas vs logistic |
make_holdout.py, score_omission.py |
annotation-holdout construction and detection scoring |
coverage-rebenchmark-2026-08-03.tsv |
448-row per-sample deliverable |
juncprobe/ is a Rust harness (built binary present) with phase0, reweight,
em and ceiling modes — ~10 s over a 35M-record BAM, versus ~10 min for the
equivalent Python. Use it for anything that must touch every alignment.
Scoring rules that have already cost time
- Score over a hash-verified identical reference transcriptome.
run_arms.pymd5s each arm's.quantkey set and aborts if two compared arms differ. - Renormalise to library size before comparing counts. A holdout that drops 35% of assigned reads deflates every surviving transcript ~3x. This inverted the sign of a whole validation once; the tell was a baseline signed error of -1.70 where the reference analysis had +0.43. If your baseline's sign disagrees with the reference, stop and reconcile before interpreting.
--seq-techis required even in alignment mode —bulk.rsgated the PacBio kernel on it, and the original harness passed it.- Zero truth variance means undefined, not zero (SIRV E0).
- Arm-vs-arm on one binary is deterministic. The ~0.005 "noise floor" quoted in the older docs is a cross-branch figure for the same nominal model; it does not apply to flag-on/flag-off on one build.
4. Decision rules (pre-registered; keep using them)
From docs/coverage-auto-rebenchmark-plan-2026-08-03.md:
- A feature earns a default only by beating
logistic— notnone— on the union of both panels, with no dataset family regressing more than 0.01 Spearman. - If the bare kernel does not beat
logistic, the kernel should not be a default either. - If the panels disagree, prefer Panel B and report the disagreement.
Two amendments learned the hard way:
- Pre-register a minimum effect size. The junction-endpoint term passed the consistency criterion as literally written (6/6 samples improving) while delivering +0.00026 — about 1/35th of the projected magnitude. Consistency without magnitude is not a bar.
- Rule 5 is not mechanical. When Panel A is strongly and uniformly
opposed rather than merely noisy, that is a conflict to investigate, not a
tie to break by fiat. See the
responsibility-profilesentry below.
5. Where the remaining headroom is
Measured on Panel B, logistic:
| Spearman | headroom | |
|---|---|---|
| as shipped | 0.9220 | — |
| + perfect detection (truth-zeros forced to 0) | 0.9481 | +0.0261 |
| + perfect abundance (expressed set to truth) | 0.9765 | +0.0545 |
1,812 false-positive transcripts carrying 0.156% of the read mass cost 0.026 Spearman — as much as the entire benefit of coverage modelling.
Lead A — a presence/absence component in the EM. Largest known.
Worth up to +0.0261, roughly ten times anything the retired stack delivered, and orthogonal to which coverage model is used. Naive thresholds capture only 10% of it (+0.0026 at a 0.5-read floor) and aggressive ones lose 0.04–0.11, because the classes overlap exactly where the false positives live: of transcripts with est ≤ 1 and no unique read, 65% are truly zero but 35% are real.
The discriminating information exists — false positives have median 1.32 estimated reads versus 30.68 for true positives (23x), and 98.7% have no unique read versus 61.7%. It needs a probabilistic component (zero-inflated or spike-and-slab) emitting a per-transcript presence probability, not a cutoff.
A 0.5-read floor is available today for +0.0026 at no cost, as a stopgap.
Lead B — logistic underperforms on PacBio. Repeatedly observed.
On TKSM SQ2, none (0.9501) beats logistic (0.9479). Across both panels
logistic loses to none on PacBio (-0.0025, 4/13), and the retired auto
and hybrid kernels both beat it on the two TKSM samples. No kernel beat none
consistently, so nothing was kept — but this is the most reproducible anomaly in
the series and the likeliest place a new model finds room. n=2 on exact truth
is the limiting factor: more PacBio samples with exact read-level truth is the
single highest-value data acquisition.
Lead C — responsibility-profiles, unresolved and worth revisiting
On wip/coverage-and-annotation-2026-07-25 as a --coverage-ablation. It
rebuilds the logistic coverage profile from cross-fitted abundance
responsibilities instead of counting every alignment once — the current
construction lets an ambiguous read deposit a full read of coverage on every
candidate it touches (measured 5.09x inflation on H69 cDNA). A real defect.
Measured on logistic:
| dataset | truth | delta | consistency |
|---|---|---|---|
| Panel B (NanoSim/TKSM) | exact | +0.0023 | 6/6 |
| independent-sim | exact counts, different simulator | -0.0013 | 0/2 |
| SIRV-E2 | real reads, known concentrations | -0.0004 | 1/4 |
| Panel A LongBench | matched Illumina | -0.0030 | 0/28 |
Not adopted: one dataset family of four supports it, and the two that are
real with real truth do not. But the defect it fixes is genuine, so a
different correction for the same inflation may still be worth having. Note it
is not redundant with --score-prob-denom 3 — the gain was larger at D=3
than at D=5.
Ideas already tested and killed — do not re-propose without new evidence
| idea | result |
|---|---|
| length / effective-length correction | oracle gain -0.0001 Spearman; the bias is real but rank-preserving |
| ambiguity-gated coverage correction | 0.0000 at every gate threshold |
| splice-junction endpoint term | 9.9x likelihood ratio, +0.025 posterior mass/read, +0.00026 accuracy |
| endpoint grid geometry corrections | +0.000186; the nucleotide-measure variant is harmful (-0.005) |
component-mass-conservation |
byte-identical on 6/6 — never engages |
polya-three-prime |
+0.000108 |
continuous-nested-guard |
helps only the broken pre-demotion baseline |
The junction-endpoint result generalises and is worth internalising. An
information-ceiling analysis found that ~73% of misassigned reads have the true
source and the winning decoy scoring identically. Equal alignment score over
the read's span implies indistinguishable sequence, hence indistinguishable exon
structure — so no annotation-derived per-alignment feature can separate them,
regardless of formulation. The other ~27% already score higher for the true
source and lose to the abundance prior, where annotation is redundant. Reproduce
with juncprobe ceiling before investing in any per-alignment signal.
6. Open items
--model-unannotated-isoformsrecall is ~1% (89 of 9,720 genes) at precision 1.000. Precision is not the weak point; recall is. The gates are--novel-min-misses,--novel-min-locus-reads(default 5) and--novel-require-hits.- The main EM does not converge at the shipped cap — 1,000 evaluations,
converged: false, at threshold 0.001, for every arm includinglogistic, on 177k-transcript data. Surfaced incidentally; affects every configuration and has never been investigated. --coverage-model autonow hard-errors. Nothing in.github/,justfileorscripts/*.shused it, but external workflows and the paper's methods were not checkable from here.- Panel B accuracy validation for the unannotated-isoform feature is genome-mode only; the transcriptome-BAM panel cannot exercise it.
- The original
origin50-style holdout scripts from the July evaluation were not preserved, so its holdouts can only be re-derived from their description, not reproduced exactly.make_holdout.pyre-derives them.