Amortise the multi-head library re-prediction across an experiment - #86
Conversation
Reported: multi-head re-predicts the retention times for every file, and the wanted behaviour is to predict on the first run and use the per-run LOESS for the rest. That is what `experiment.finetune_scope = first_run_only` already did for the DeepLC fine-tune, and it did not cover multi-head: `process_run` tested `mh_heads > 0` before the shared-library branch, so the shared path was unreachable whenever multi-head was on. Confirmed on a real six-file Astral experiment: `fragment_library_precursors_multihead.parquet` in every one of r0-r5, 362 MB each, `"repredicted": 9403264` in all six summaries. The field is now `experiment.rt_library_scope`, with `finetune_scope` kept as a serde alias, and it governs whichever adaptation is active. Both are the same shape of work -- one full re-prediction of the library against that run's confident seed PSMs -- so one knob amortises both, and the name now says what it governs rather than which mechanism happened to use it first. `process_run` tries the shared library first, whichever mechanism produced it, and multi-head now offers its library for sharing as the fine-tune already did. The first-run-alone gate covers multi-head, resolved the same way the stage resolves it so the decision matches what the run will actually do. The reasoning for per-run was that the calibration is fitted against THIS run's chromatography. True, and equally true of the fine-tune. What a shared library fixes is elution ORDER, which replicate injections on one method share; a per-run LOESS can stretch and bend the axis but cannot reorder two peptides, so a batch that genuinely reorders wants `per_run`, and so does a long batch where drift accumulates. The fine-tune's measured cost of sharing is recorded on the enum: +47% median RT residual across five reusing runs, monotonic in acquisition order. The multi-head equivalent is being measured on the six Astral files now; this commit does not claim it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The A/B the previous commit said it did not claim. Six-file Astral experiment, one binary, configurations differing only in experiment.rt_library_scope: precursors 128,006 -> 126,762 -0.97% peptides 115,924 -> 114,704 -1.05% protein groups 12,201 -> 12,287 +0.70% decoy fraction 0.0100 0.0100 unchanged w_rt, reusing 20.8-23.2 s -> 34.9-37.9 s So it is a trade, not a free win. Sharing gives back 55% of the window narrowing multi-head's gain came from, and the decoy fraction is identical in both arms, so the 1% is lost identifications rather than a moved threshold. What it buys is five full re-predictions of a 9.4M-row library removed: those files finish their chain in 1.9-3.4 minutes instead of the better part of an hour. `first_run_only` stays the default. It is the same default the fine-tune has always had on a considerably worse trade (+47% median RT residual across the reusing runs), and 1% for that much wall clock is the right shipped balance. The number is written down so anyone for whom it is not can set `per_run`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A/B result: sharing costs ~1%, and that is the whole costSix-file Astral experiment, one binary, configurations differing only in
It is a trade, not a free win. Sharing gives back 55% of the window narrowing that multi-head's gain came from — the five reusing runs go from a mean What it buys: five full re-predictions of a 9.4M-row library removed. Those files finish their chain in under four minutes instead of the better part of an hour. Default
Worth noting for later: r0's window is 22.1 s in both arms while the reusing runs sit at 35–38 s, which points at the loss being the width rather than the head choice. A periodic re-adaptation — every k-th run rather than once or every time — would likely recover most of the 1% at a fraction of the cost. Not in this PR. Out of draft. 🤖 Generated with Claude Code |
Reported: multi-head re-predicts the retention times for every file, and the wanted behaviour is to predict on the first run and use the per-run LOESS for the rest.
It was supposed to already do that
experiment.finetune_scope = first_run_onlyhas done exactly this for the DeepLC fine-tune all along. It did not cover multi-head, and not by configuration —process_runtestedmh_heads > 0before the shared-library branch, so the shared path was unreachable whenever multi-head was on.Confirmed on a real six-file Astral experiment (
results3), whose config already setfinetune_scope: first_run_only:fragment_library_precursors_multihead.parquetin every one ofr0–r5, 362 MB each;"repredicted": 9403264in all six summaries — six full re-predictions of the same 9.4M-row library;That is where the measured 1.4×–1.7× wall clock goes.
Change
experiment.finetune_scope→experiment.rt_library_scope, with the old name kept as a serde alias. It now governs whichever adaptation is active, because both are the same shape of work: one full re-prediction of the library against that run's confident seed PSMs. The name says what it governs rather than which mechanism happened to use it first.process_runtries the shared library first, whichever mechanism produced it;The reasoning it overturns
The old comment said multi-head is "per run, always, and never shared: the whole point is that it is fitted against THIS run's chromatography." That is true — and equally true of the fine-tune, which has been shareable all along. What a shared library fixes is elution order, which replicate injections on one LC method share. A per-run LOESS can stretch and bend the axis but cannot reorder two peptides, so a batch that genuinely reorders — different gradients or columns, a method change part-way — wants
per_run, and so does a long batch where drift accumulates.What is not claimed here
The cost of sharing for multi-head is not yet measured. The guide until it is, recorded on the enum, is the fine-tune's measured cost on the 6-run HYE AIF set: the run that owned the fit reached 15.2 s median RT residual, the five reusing it 20.3–25.4 s (+47%), windows widening 145 s → 179–227 s, degradation monotonic in acquisition order.
Reason to expect better here: the six Astral runs' own calibrations are near-identical —
w_rt20.8–23.2 s, residuals 2.30–2.73 s — so one fit plausibly serves all six. That is the hypothesis the A/B is testing, againstresults3(per-run: 128,006 precursors, 115,924 peptides, 12,201 protein groups, decoy fraction 0.0100).Validation
cargo fmt --check, clippy-D warnings,cargo test --workspace285 passed (one new test covering the default, the alias, and that both spellings agree). Config reference regenerated; docs/02, docs/08, CLAUDE.md and CHANGELOG updated.🤖 Generated with Claude Code