Score sequence-free libraries with library-wide chemistry fallbacks - #146
Merged
Merged
Conversation
Apex-finder bench
Sensitivity + timing across canonical scenarioscommit 7ff2817 |
There was a problem hiding this comment.
🟡 Changes recommended
Moderate issues remain in raw RT semantics, empty-arena decoy handling, and Phase 6 timing.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR adds sequence-free library scoring with chemistry fallbacks, raw-score output, and opaque string-label support.
Changes:
- Gates isotope features and mass-shift decoys library-wide.
- Adds raw scoring without calibration, rescoring, competition, or q-values.
- Supports string-labelled libraries with optional intensity sidecars.
File summaries
| File | Summary |
|---|---|
rust/timsseek/src/scoring/timings.rs |
Adds raw-score performance reporting. |
rust/timsseek/src/scoring/plan.rs |
Resolves isotope capability and feature selection. |
rust/timsseek/src/scoring/pipeline.rs |
Supports raw extraction and optional calibration. |
rust/timsseek/src/scoring/parquet_writer.rs |
Writes raw-mode schemas and metadata. |
rust/timsseek/src/scoring/blocks/lazy.rs |
Separates isotope score fields. |
rust/timsseek/src/ml/qvalues.rs |
Applies scoring-plan feature projections. |
rust/timsseek/src/data_sources/reference_library.rs |
Supports opaque labels and intensity sidecars. |
rust/timsseek_cli/tests/build_library.rs |
Updates target-table matching tests. |
rust/timsseek_cli/src/processing.rs |
Adds raw execution and decoy-free handling. |
rust/timsquery/src/traits/fragment_label.rs |
Defines fragment and decoy-shift capabilities. |
rust/timsquery/src/serde/library_file.rs |
Supports string-label intensity sidecars. |
rust/timsquery/src/models/target_columns.rs |
Gates generated decoys by library capability. |
rust/timsquery/src/models/query_handle.rs |
Updates opaque-label behavior. |
rust/timsquery/src/models/capabilities.rs |
Clarifies capability semantics. |
rust/timsquery_cli/src/commands.rs |
Handles extended table variants. |
docs/development.md |
Documents fallback and raw scoring modes. |
Review details
Suppressed comments (1)
rust/timsseek/src/scoring/pipeline.rs:744
- The raw branch now sets
calibrated_rt_secondstoNaN, butCandidateMetadatastill documents this field as equal tolibrary_rtwhen no calibration is applied. SinceRt::computealso derivescalibrated_delta_rtfrom that value, update the public contract to distinguish raw mode's unavailable, unit-agnostic calibration rather than leaving consumers with contradictory semantics.
source_id: identity.source_id,
library_rt: query.rt_seconds(),
calibrated_rt_seconds: f32::NAN,
ref_mobility_ook0: query.mobility_ook0(),
ref_precursor_mz: query.mono_precursor_mz(),
- Files reviewed: 16/16 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| writer.add_raw(row).map_err(io_error)?; | ||
| } | ||
| } | ||
| writer.close().map_err(io_error)?; |
Comment on lines
+170
to
+176
| if !matches!( | ||
| speclib.geometry().capabilities().decoys, | ||
| timsquery::models::capabilities::DecoyStrategy::MassShift { .. } | ||
| ) && speclib.geometry().n_stored_decoys() == 0 | ||
| { | ||
| return execute_raw_pipeline(speclib, pipeline, options); | ||
| } |
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.
Sequence-free libraries can now reach common spectrum scoring without treating unknown fragment keys as chemical annotations. A library with unannotated peaks and no decoys automatically uses raw scoring; it no longer generates charge-assumed mass-shift decoys or attempts FDR estimation.
For example, an mzSpecLib spectrum containing precursor m/z/charge and reference peaks, but no sequence and only
?peak annotations, now yields raw spectrum scores with nullable sequence and no q-value column. ProgrammaticTargetTable::Strlibraries can also supply reference intensities; even a string key"y3"remains opaque.if-missing/never.result_mode=raw; the run report records per-fileraw_scores=trueand the library-widescoring_plan(pluscalibration_scoring_planfor a separate calibration library). Plans include the requested decoy policy, resolved strategy, stored-decoy count and retained resolution reason. The report borrows the same plans used by scoring/Parquet, without copying isotope buffers.Validation: 452 unit tests passed (one additional ignored), eight query contract tests, workspace Clippy with dashboard, serial CLI check, and
task fmt.Real-data checks use the supplied 948,957-spectrum / 27,287,703-peak HeLa library and Astral mzML:
Degraded output sequences are null; IDs/names survive. The sequence-only fixture uses explicit
0@annotations so fragments do not reference a deleted analyte; it retains fragment-isotope scoring. Fixtures/results are local and gitignored; the original library/raw file are untouched.Concrete mzSpecLib degradation example
This is spectrum 1 from the original HeLa library (entry body; the unchanged library header is omitted):
no-analytes)<Analyte=1>block: ProForma ion notation (MS:1003270), stripped sequence (MS:1000888), residue count (MS:1003043), theoretical neutral mass (MS:1003637), protein accession (MS:1000885)b2→0@b2,y1→0@y1, etc. This removes the implicit reference to deleted analyte 1 while retaining ion series/ordinal/charge information.no-chemistry)?for every peakraw-1000)no-chemistry; additionally exclude decoy spectra and take the first 1,000 retained targets?peak annotationsFor example, the first peak changes as follows (columns are m/z, reference intensity, annotation):
MS:1003061|library spectrum name=AAAAC[UNIMOD:4]LDK/2remains unchanged in every case. It is an opaque entry name, not a source of sequence or charge; charge remains separately supplied byMS:1000041. The full-library degradations also retain all supplied decoy spectra, their decoy declarations and pairing IDs. Header provenance remains unchanged. This entry supplies no separate molecular-formula field to remove.Spectrum-name hash stress test
Replaced only
MS:1003061values withsha256(original_name.encode("utf-8")).hexdigest()in the fully degraded library and the target-only raw fixture. Example:This hashes
AAAAC[UNIMOD:4]LDK/2without its trailing newline. All 948,957 names became distinct hashes. A line-by-line comparison verified every other input line unchanged: spectrum keys, charge, coordinates, peaks/intensities, decoy declarations, pairing IDs and header provenance.discriminant_scoreandqvalueis identical, including charge and declared group IDs.canonicalize_and_shufflesorts by source ID before seeded shuffling into folds. mzSpecLib spectrum names supply those IDs, so hashing changes fold assignments. The 1% FDR count changes 27,972 → 28,137; q-filtered outputs contain 103,481 versus 102,332 rows (36,094 original-only, 34,945 hashed-only). This test establishes chemistry/charge independence from names, not invariance of learned scores to renamed identities.Local generators, line-verification manifests and result comparisons are in
.scratch/97-sequence-free/; no source library or production code changed for this test.This does not validate decoy strategies for new analyte classes or add arbitrary adduct/negative-ion handling.
forceretains its existing reader behavior of dropping supplied decoys before sealing; if generation is then unavailable, the retained rows use raw scoring. Supervised calibration retains its existing RT-input contract.Closes #97; follows #142, #144 and #145. Sidecar ownership cleanup in #118 remains separate.