apex: apex_sim bench + unify finder + cull weighting, re-tune base profile#70
Merged
Conversation
…blur pass, joint-snap 1) Bench-validated: SUM 267.9 -> 326.2 (+58.3) on apex_sim canonical suite. Rounded exponents (0.5/0.75/1.75) match or beat the Optuna values.
Per-cycle expected-intensity-weighted mean pairwise correlation of center-normalized fragment XIC windows. Runtime cost (18->44 us/run) addressed in a later perf pass.
…, +63.6) Robust per-row matched-filter soft votes summed across fragments; saturating votes make a genuine multi-fragment apex win over tall single-row interferents. Runtime cost (44->91 us/run) addressed in perf pass.
MS1 precursor undetected, fragments intact. Guards against over-reliance on precursor signal in the apex profile.
Replaces 3 full sorts/row with partial selection. Result-identical (same order statistic); SUM 491.3 unchanged on canonical suite.
… 59->51 us/run)
Weighted mean pairwise correlation of unit window vectors has a closed
form (sum_{a<b} w_a w_b <u_a,u_b> = (||W||^2 - sum w_a^2)/2), collapsing
the per-cycle O(rows^2) inner loop to O(rows). Zero-profile cycles skip
the window work entirely. Result-identical: SUM 491.3 unchanged.
…date alloc) Moves cn/active/wacc and support/r/m/tmp into an ApexScratch owned by TraceScorer, cleared+resized per call. Matches the TraceScorerBuffers idiom; removes allocator churn across rayon workers at scale. Result-identical: SUM 491.3 unchanged.
Moves coelution + vote into scoring/apex/{coelution,vote}.rs as pure
kernels over (profile, rows, cfg, scratch), decoupled from the Extraction
data model. ApexConfig/ApexScratch live in apex/mod.rs; apex_finding calls
a single weight_profile orchestrator. Adds 4 unit tests (co-elution vs
scattered, concurrent votes vs tall interferent, disabled no-ops).
Bench parity: SUM 491.3, 51 us/run unchanged.
…, s 0.4) Rounded from raw Optuna values; SUM 491.3 -> 491.6, no regression.
…_loop) Result-identical; SUM 491.6 unchanged.
Fixed count in a wide window understates interferent density and makes apex-finding artificially easy. density_per_cycle > 0 overrides count as round(density * n_cycles * hardness); defaults keep count-based behavior.
apex_jitter=Some(range) draws realized apex = apex_cycle + U(-range,range) + U(-0.5,0.5) per seed (off-grid, moving target). None keeps the historical on-grid fixed apex byte-identical. run_sensitivity scores each run against its realized apex.
roc_auc (average-rank Mann-Whitney) + run_discrimination: matched pairs share a seed; the absent twin zeros BOTH fragments and precursor (true no-signal), so identical noise+interferents isolate the real peak's effect on the Pass-2 score. Measures the score as a signal/noise separator.
…bles Density-scaled interferents + jittered sub-cycle apex at production window sizes. Example prints canonical/broad/narrow recovery tables plus a narrow score-discrimination AUC table. Canonical header kept as '=== summary' so score.sh still parses it.
…0/245) 2.5/cyc (mis-derived from the 40-cyc viewer example) drowned every noisy scenario at realistic window sizes. 0.41/cyc matches the canonical suite's interferent density, isolating window size as the changed variable: broad high-noise recovery 86%(245cyc) -> 54%(1695cyc); narrow AUC 1.0 clean -> 0.68 heavy. hardness=3 remains as an explicit stressor.
Delete score_for_viewer (no callers; viewer uses find_apex directly), its ViewerResult type, and now-orphaned build_broad_extraction. Fix stale docs: apex_profile C(t) formula (cos^0.5*I^0.75 not cos^3*I), drop 'Migration aid'/'replaces LocalizationBuffer' notes. No behavior change.
Pass 2 scored at effective_apex = find_joint_apex(cosine_profile) (the UN-weighted cos^3*I, global argmax, snapped to Pass 1). It relocated onto interferents in narrow windows. Now Pass 2 trusts Pass 1's weighted apex_profile location -- one apex finder, one C(t). Removes find_joint_apex + its tests, and the joint_snap/relocate_apex_via_joint knobs. Bench-neutral: canonical SUM 492.0->492.1; narrow recovery + AUC unchanged on apex_sim (perfectly co-eluting sim can't show the real-window drift the change prevents).
The coelution and cross-row vote weighting passes tripled apex scoring cost (canonical 18->51 us/run, broad 112->320 us/run) while, on the apex_sim suites, buying recovery that the sim itself barely discriminates -- and the sim does not reproduce the imperfect co-elution these passes were partly meant to handle. Remove both passes (coelution.rs, vote.rs, weight_profile, ApexScratch, the coel_/vote_ knobs); apex_profile is now the base composite profile only. Runtime returns to the pre-feature baseline. With the weighting gone, the base-profile knobs were re-tuned standalone (Optuna over the canonical+broad+narrow recovery suites, rounded to robust non-degenerate values): cos_pow 0.5->0.25, i_exp 0.75->1.0, s_ratio 1.75->0.25, blur_passes 1->2. +1.5% summed recovery across all three window sizes at no runtime cost. Tuning is sim-derived and provisional pending a real-data check.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Net change vs main. History is noisy (it builds an apex-profile weighting
experiment and then removes it) -- squash on merge.
What this does
apex_sim test-harness (new). Synthetic DIA extractions fed to the real
TraceScorer. Broad (1695-cyc, production Phase-1) and narrow (150-cyc,
Phase-3) recovery suites, interferent density that scales with the
window (0.41 peaks/cyc anchor from a real run), sub-cycle per-seed apex
jitter, and a ROC-AUC bench separating a real peptide's score from a
pure-noise twin. Window/width/density anchors derived from a real
250225 HeLa run.
Dead code removed. score_for_viewer path, ViewerResult, full_results.rs,
build_broad_extraction, find_joint_apex + its tests. Stale docs fixed
(apex_profile C(t) was documented cos^3*I).
Apex finder unified. Pass-3 score_at now scores at Pass-1's apex cycle
(effective_apex = cycle) instead of relocating via find_joint_apex on a
separate profile. One apex source.
Apex-profile weighting: added, then culled. An intermediate experiment
added ratio-free coelution + cross-row vote weighting to apex_profile.
It tripled apex scoring cost (canonical 18->51 us/run, broad
112->320 us/run) for recovery gains the sim barely discriminates, and
the sim cannot reproduce the imperfect co-elution the passes were partly
meant to handle. Net vs main: the weighting is GONE. apex_profile is the
base composite profile only; runtime is back at the pre-experiment
baseline.
Base profile re-tuned standalone. With the weighting removed, the base
knobs were re-tuned (Optuna over canonical+broad+narrow recovery,
rounded to robust non-degenerate values): cos_pow 0.5->0.25,
i_exp 0.75->1.0, s_ratio 1.75->0.25, blur_passes 1->2. +1.5% summed
recovery across all three window sizes at no runtime cost.
Runtime
canonical us 18 51 18
broad us 112 320 110
narrow us 12 33 12
Validation
tuned base knobs; per-suite canonical/broad/narrow all up, none regressed.
Review notes (fable), open for decision before merge
MEDIUM. Locator/scorer profile split. apex_profile (locator, cos^0.25)
picks the apex cycle; score_at then reads the 11 features off
cosine_profile (scorer, cos^3) AT that cycle. On non-co-eluting real data
the two peaks can sit 1-3 cycles apart, so features get sampled off the
true peak and deflate a genuine peptide's score. base_score is safe (it
re-localizes independently); only the feature multipliers ride the
locator apex. The apex_sim harness co-elutes everything at one cycle, so
it is structurally blind to this -- "unify is bench-neutral" is asserted,
not tested. Fix options: re-localize features in a small +/-window on
cosine_profile around the locator apex, or add a non-co-eluting sim
scenario (fragment apex offset from precursor/interference apex).
LOW. Post-unify, pass1% and pass2% recovery columns are mathematically
identical (Pass 2 no longer relocates). Collapse or relabel.
LOW. run_discrimination's noise twin zeroes the expected (library)
precursor intensity too, not just the observed data, making the AUC
slightly easier than the real signal-vs-noise decision. Fragments are
handled correctly (ratios preserved).
The tuned constants are sim-derived and provisional; cos_pow=0.25 sits
near the search-range floor. A real-data check (bench/wandb_bench.py) is
the right gate before trusting the +1.5%.
Supersedes
Folds in the content of #68 (apex weighting -- now culled) and #69
(apex_sim realism). Both can be closed.