Skip to content

Preserve library RT coordinates and calibrate search dimensions independently - #148

Merged
jspaezp merged 5 commits into
mainfrom
feat/library-rt-coordinates
Sep 25, 2026
Merged

jspaezp merged 5 commits into
mainfrom
feat/library-rt-coordinates

Conversation

@jspaezp

@jspaezp jspaezp commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Library RT was treated as acquisition seconds, and a failed RT fit prevented m/z and mobility calibration. This change preserves the source RT domain and calibrates searchable dimensions independently.

  • One RT descriptor per library: absent, seconds, normalized index with optional scale, or unspecified. Measured minutes convert to seconds; normalized values retain negative values and zero. Mixed availability or axes are rejected.
  • mzSpecLib reads effective attributes and grouped units through rustyms PR #6, pinned at c033092. JSON loading and prediction preserve explicit axes and declared scales.
  • Source geometry exposes LibraryRT<f32>. A borrowed ExtractionQuery carries separately resolved ObservedRTSeconds<f32> bounds. Collectors own reusable buffers and retain no library lifetime. Index calls accept PeakTolerance; RT is resolved once, preserving existing tolerance arithmetic.
  • Isotope extraction shifts the collector's fragment buffers directly. No owned target materialization or library-axis clone per scored candidate. Python query adapters also borrow their input geometry.
  • Missing/failed RT fits search the full acquisition and still measure m/z and supported mobility at score-selected apexes, without ridge filtering. Unmeasured dimensions retain configured tolerances. Missing RT preserves decoy competition, rescoring and q-values; it does not select raw mode. Prescore heaps remain bounded and mergeable.
  • The viewer uses calibrated acquisition coordinates or full-run extraction without an RT fit, including libraries measured in seconds. Direct-query CLI/Python seconds inputs explicitly denote acquisition coordinates. Existing CLI output fields remain compatible.
  • Results format 5 includes library-axis metadata; calibration format v4 supports partial calibration. Older calibration files must be regenerated. FAIMS/absent mobility never supplies sentinel calibration measurements.

Validation:

  • 142 query/reader, 201 scoring, 108 search CLI, 12 query CLI, 10 viewer and 17 calibrt unit tests passed. Existing ignored tests remain ignored. Carafe, library-building integration and doctests passed.
  • Tests cover source/acquisition type separation, clipping, explicit full-run selection, invalid RT rejection, collector lifetime independence, source RT remaining unchanged, index use of resolved bounds, and buffer reuse.
  • Workspace/all-target Clippy passed with -D warnings -A clippy::too_many_arguments; the argument-count exception covers existing CLI/viewer/Python signatures. Dashboard feature checks, task fmt and git diff --check passed.
  • Release search on the supplied Astral HeLa mzML, with 948,957 library entries and 474,327 supplied decoys, completed for all four RT variants. All retain rescoring and finite output scores/q-values. Missing/failed RT preserves m/z calibration, leaves RT predictions/residuals unavailable and has zero RT-boundary skips. Absent RT omits RT features; constant RT produces non-finite features that rescoring culls. Python bindings were compile-checked, not exercised in a Python runtime.
Library RT Targets at q ≤ 1% RT behavior
Intact 28,159 Fitted
Removed 23,754 Full run; m/z calibrated
Shuffled, seed 20260923 11,013 Broad spurious curve accepted
Constant zero 21,300 ZeroRange; full run with m/z calibration

Shuffled RT exposes an existing estimator limitation: the fitter accepts a monotonic path through noise. Its ridge half-width averages 411 s versus 96 s for intact RT. Fit-quality rejection remains separate work; no scientific threshold is added here. These estimated q-value counts on one acquisition do not establish independent FDR accuracy or the 1,000-file storage requirement.

Closes #115.

Follow-up work:

Comment thread rust/calibrt/src/lib.rs Outdated
pub rt_range_seconds: [f64; 2],
/// Input-axis descriptor supplied by the library consumer. The fitter maps
/// numerical library coordinates to observed seconds without interpreting units.
pub library_rt_axis: serde_json::Value,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for sure type this ...

Comment thread rust/calibrt/src/lib.rs Outdated
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Apex-finder bench

cargo run -p apex_sim --release --example bench -- 1000 2

Sensitivity + timing across canonical scenarios
=== summary (n=1000, tol=±2 cycles) ===
  scenario                      pass2%  pass1%   medErr    us/run
  clean                          100.0   100.0        0     27.39
  moderate_noise                  92.7    92.7        0     26.72
  high_noise+interference         59.3    59.3        1     26.73
  heavy_interference               7.4     7.4       88     26.88
  mismatched_library              59.8    59.8        1     26.61
  absent_top_fragment             16.0    16.0       65     26.65
  absent_precursor                59.3    59.3        1     26.71

=== broad apex-finding (n=500, tol=±2 cycles) ===
  scenario                      pass2%  pass1%   medErr    us/run
  broad_clean                    100.0   100.0        0    162.39
  broad_moderate_noise            50.6    50.6        1    158.24
  broad_high_noise+interf         27.6    27.6      308    159.42
  broad_hard_3x_density            0.8     0.8      487    158.27
  broad_mismatched_library        41.0    41.0      161    158.98
  broad_measured_density          49.6    49.6       13    187.94

=== narrow recovery (n=1000, tol=±2 cycles) ===
  scenario                      pass2%  pass1%   medErr    us/run
  narrow_clean                   100.0   100.0        0     18.66
  narrow_moderate_noise           81.3    81.3        0     18.29
  narrow_high_noise+interf        65.8    65.8        1     18.22
  narrow_hard_3x_density          10.9    10.9       34     18.29
  narrow_mismatched_library       62.8    62.8        1     18.31
  narrow_measured_density         83.6    83.6        0     20.89

=== narrow score discrimination (AUC, n_seed_pairs=1000) ===
  scenario                         AUC   med+signal    med-noise
  narrow_clean                   1.000     4.329e14      7.199e7
  narrow_moderate_noise          0.870      4.054e9      3.054e8
  narrow_high_noise+interf       0.830      2.761e9      3.602e8
  narrow_hard_3x_density         0.684      3.644e9      1.244e9
  narrow_mismatched_library      0.842      2.172e9      3.575e8
  narrow_measured_density        0.858      4.341e9      2.562e8

commit 245d852

@jspaezp
jspaezp merged commit 40fbf57 into main Sep 25, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rt_seconds names a unit the value often is not in: measured time and normalized index share one f32

1 participant