Skip to content

Improve benchmark result normalization and generic reporting - #1

Closed
nej1gotnochill wants to merge 32 commits into
mainfrom
feat/openswath-normalization-and-cleanup
Closed

nej1gotnochill wants to merge 32 commits into
mainfrom
feat/openswath-normalization-and-cleanup

Conversation

@nej1gotnochill

@nej1gotnochill nej1gotnochill commented Sep 3, 2026 •

Copy link
Copy Markdown
Collaborator
  • Add normalize_openswath: extract OpenSwath normalization from runner script into report_generate.py as a proper CLI subcommand
  • Remove tool_versions from canonical v2 result format (normalize_smoke, normalize_openswath, _promote_v1); identity.software.version remains the source for primary software version
  • Add CI normalization steps: Smoke and OpenSwath results are now normalized into the canonical v2 format after benchmark execution
  • Add 4 tests for normalize_openswath: basic, load_results integration, missing files graceful handling, required stage failure
  • Preserve raw benchmark output for debugging alongside canonical results
  • Generic renderer remains benchmark-agnostic; no schema migration introduced

Summary by CodeRabbit

  • New Features

    • Added a ProSE and PeptDeep entrapment benchmark with baseline comparisons, performance metrics, and standardized v2 results.
    • Added automated ONNX-enabled build, validation, packaging, and artifact uploads.
    • Added tools for reproducible benchmark inputs and combined target/entrapment datasets.
  • Bug Fixes

    • Improved handling of incomplete, empty, or invalid OpenSwath stage data.
  • Documentation

    • Added design documentation for the ProSE and PeptDeep benchmark.
  • Tests

    • Added coverage for OpenSwath and ProSE/PeptDeep normalization and failure scenarios.

- Copy OpenSwathWorkflow_1 test fixtures from OpenMS repo (7 peptides, 5 SWATH windows)
- Add run_openswath_benchmark.sh following smoke benchmark conventions
- Include correctness metrics: feature count, overall quality, intensity sum, QC JSON
- Update PROVENANCE.md with OpenSwath fixture provenance

This benchmark exercises DIA proteomics (complementing DDA smoke benchmark),
covering SWATH data reading, chromatogram extraction, MRM scoring, and feature picking.

Fixtures: 320 KB total, deterministic, no external dependencies.
Expected runtime: 5-15s in CI.
- Add run-openswath-benchmark job alongside smoke benchmark
- Uses same OpenMS artifact from build-openms job
- Installs runtime dependencies and sets up library paths
- Uploads results as separate artifact

This enables CI validation of the OpenSwath DIA benchmark prototype.
- Update normalize_smoke and normalize_proteobench to produce v2 output
- Add v1->v2 auto-promotion in load_results
- Support both v1 (openms/tools) and v2 (benchmark-specific) directory layouts
- Add comprehensive test suite for v2 schema changes
- Remove duplicate v1 fields from normalize_smoke and normalize_proteobench output
- Renderer reads exclusively from v2 fields (identity, run, performance, metrics)
- Dynamic stage discovery: no hard-coded stage names (decoy_database, comet, percolator)
- Dynamic metric comparison: discovers shared metrics between runs
- Optional sections: handles missing stages/build/correctness/tool gracefully
- No benchmark-specific if/elif branches in renderer
- Add comprehensive test suite (11 tests) covering all benchmark types
- Add v2 data access helpers (_identity, _run, _performance, _metrics, etc.)
- Fix tool result labels to use _run_id() instead of raw get("run_id")
- Remove _v1_compat from promote output (clean v2 only)

The renderer now works with Smoke, ProteoBench, OpenSwath, and any future
benchmark type without code changes.
The comparison table lost all stage and build metrics when the renderer
was refactored to use _metrics() only. Smoke comparison showed only
"verdict" instead of stage wall times, build time, and stage statuses.

Add _flat_compare() that generically flattens a result's metrics,
performance.build, performance.stages, and correctness into a single
comparable dict — without duplicating data in the stored schema.

Also fix stage regression detection which was silently broken because
stage.*.status keys never appeared in the comparison dict.

Tests: 16/16 pass (5 new tests for _flat_compare, Smoke comparison,
stage regression detection, and ProteoBench no-spurious-keys).
Add JSON definition files for Smoke, OpenSwath, and ProteoBench
benchmarks. Each definition contains id, name, description, runner
path, fixtures, and normalization function. Also add a lightweight
validation script that checks referenced files exist and functions
are defined.

These definition files provide discovery and documentation benefits
without hiding execution logic. The runner scripts and normalization
functions remain unchanged. The renderer remains benchmark-agnostic.
- Add normalize_openswath: extract OpenSwath normalization from runner script
  into report_generate.py as a proper CLI subcommand
- Remove tool_versions from canonical v2 result format (normalize_smoke,
  normalize_openswath, _promote_v1); identity.software.version remains
  the source for primary software version
- Add CI normalization steps: Smoke and OpenSwath results are now normalized
  into the canonical v2 format after benchmark execution
- Add 4 tests for normalize_openswath: basic, load_results integration,
  missing files graceful handling, required stage failure
- Preserve raw benchmark output for debugging alongside canonical results
- Generic renderer remains benchmark-agnostic; no schema migration introduced
Copilot AI lite review requested due to automatic review settings September 3, 2026 21:01
@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

Important

Review skipped

The saved review history does not include the base for the last reviewed commit. This saved history cannot establish the base for an incremental review. Comment @coderabbitai full review to establish a new review baseline. No full review was started, and the last reviewed checkpoint was preserved.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: ac852e7b-37a1-42d4-826f-b13c4164bef2

📥 Commits

Reviewing files that changed from the base of the PR and between c3aa03d and 14b2802.

📒 Files selected for processing (1)
  • .github/workflows/benchmark.yml

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change adds a ProSE and PeptDeep entrapment benchmark with input preparation, metric extraction, execution, v2 normalization, ONNX-enabled CI jobs, output validation, and tests. It also extends OpenSwath normalization coverage.

Changes

ProSE and PeptDeep benchmark flow

Layer / File(s) Summary
Benchmark inputs and contracts
PROSE_PEPTDEEP_DESIGN.md, benchmark/definitions/prose_peptdeep.json, benchmark/make_entrapment_fasta.py, benchmark/prepare_prose_inputs.sh, .gitignore
The benchmark definition and design specify baseline and PeptDeep arms. Preparation tools generate an entrapment FASTA, merged mzML inputs, and provenance hashes. Generated inputs are ignored.
Benchmark execution and metrics
benchmark/prose_metrics.py, benchmark/run_prose_benchmark.sh
The runner executes both arms, records timing and memory data, parses idXML results, classifies target and entrapment PSMs, checks q-values and PeptDeep features, writes prose.json, and returns a verdict.
v2 result normalization
benchmark/report/report_generate.py
The report generator normalizes ProSE, PeptDeep, and OpenSwath results to v2. It removes legacy tool_versions propagation and registers the normalization commands.
ONNX build and workflow integration
.github/workflows/benchmark.yml
CI pins dependency retrieval, builds ONNX-enabled OpenMS targets, packages models and runtime libraries, runs the benchmark, validates outputs, normalizes results, and uploads artifacts.
Benchmark and normalization validation
benchmark/test_prose_metrics.py, benchmark/report/test_generic_renderer.py
Tests cover metric parsing, classification, q-value fallback, feature validation, runner failures, ProSE normalization, result discovery, and OpenSwath normalization edge cases.

Sequence Diagram(s)

sequenceDiagram
  participant CI as Benchmark workflow
  participant Build as ONNX-enabled OpenMS build
  participant Runner as ProSE and PeptDeep runner
  participant Metrics as ProSE metrics
  participant Normalizer as v2 normalizer
  participant Artifact as Results artifact
  CI->>Build: build targets and package models and runtime libraries
  Build-->>CI: provide ONNX benchmark artifact
  CI->>Runner: run baseline and PeptDeep benchmark arms
  Runner->>Metrics: parse idXML and validate benchmark features
  Metrics-->>Runner: return metrics and verdict
  Runner-->>CI: write prose.json
  CI->>Normalizer: normalize ProSE and PeptDeep results
  Normalizer-->>Artifact: write and upload v2 results
Loading

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The OpenSwath normalization currently can emit an incorrect passing verdict when stages.tsv is missing (and has fragile TSV numeric parsing), and the workflow’s artifact-size output may be blank unless forced to a numeric default.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR refactors OpenSwath result normalization into report_generate.py as a first-class CLI subcommand, aligns CI to normalize benchmark outputs into the canonical v2 result layout after execution, and adds test coverage to ensure the normalized OpenSwath output integrates cleanly with the generic renderer.

Changes:

  • Added normalize openswath CLI subcommand to convert raw OpenSwath benchmark outputs into v2 run results, and removed tool_versions from v2 outputs/promotion.
  • Extended CI workflow to normalize smoke and OpenSwath results into v2 immediately after benchmarks run.
  • Added 4 tests validating OpenSwath normalization output structure, renderer loading, missing optional files handling, and required-stage failure verdict.
File summaries
File Description
benchmark/report/test_generic_renderer.py Adds OpenSwath normalization test coverage and verifies v2 loading behavior.
benchmark/report/report_generate.py Implements normalize openswath and removes v1 tool_versions carryover to keep v2 canonical.
.github/workflows/benchmark.yml Adds post-benchmark normalization steps (smoke + OpenSwath) and computes artifact size for smoke normalization.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread benchmark/report/report_generate.py Outdated
Comment on lines +869 to +885
stages_path = os.path.join(results_dir, "stages.tsv")
if os.path.exists(stages_path):
with open(stages_path) as fh:
for row in csv.reader(fh, delimiter="\t"):
if not row:
continue
name, required, rc, wall, cpu, peak, status, reason = (row + [""] * 8)[:8]
stages.append({
"name": name,
"required": required == "true",
"exit_code": int(rc),
"wall_time_s": float(wall),
"cpu_time_s": float(cpu),
"peak_rss_kb": int(peak),
"status": status,
"reason": reason,
})
Comment thread .github/workflows/benchmark.yml Outdated
- name: Compute artifact size
id: artifact-size
shell: bash
run: echo "bytes=$(du -sb openms/build/bin openms/build/lib openms/share/OpenMS openms/_thirdparty 2>/dev/null | awk '{s+=$1} END{print s}')" >> "$GITHUB_OUTPUT"
Comment thread benchmark/report/report_generate.py Outdated
normalize proteobench <local-proteobench.json> --label LABEL [--out PATH]
Convert a local ProteoBench scoring result into a tool result.

normalize openswath --run-id ID [--cache cold|warm|none] [--results-dir DIR] [--out PATH]
Fix missing/empty stages producing incorrect pass verdicts via all([]),
make TSV numeric parsing robust to empty/malformed values, and default
CI artifact size to 0 when du fails. Add 4 regression tests.
Two arms over the same mzML/FASTA/config: baseline (ProSE -> Percolator)
vs ProSE with PeptDeep MS2+RT prediction features (PR #9975). Extraction
of q-values, target/entrapment classification and unique-peptide counting
lives in benchmark/prose_metrics.py (shared with unit tests); the runner
follows the OpenSwath runner conventions (/usr/bin/time -v per stage).
The peptdeep arm fails the verdict unless all five PeptDeep features land
in the idXML, so an ONNX-less build cannot silently pass as baseline.
Includes the v2 normalization subcommand, benchmark definition, design
report and synthetic-idXML unit tests; no dataset is vendored yet.
…ndling

- Add make_entrapment_fasta.py (deterministic P02769 target + ENTRAPMENT_
  E. coli K-12 targets, rev_ decoys excluded) and prepare_prose_inputs.sh
  (FileMerger BSA1+2+3 merge into git-ignored benchmark/generated/prose/)
- Fix peptdeep:enable CLI handling: OpenMS maps the string param to a
  no-value flag, so the baseline arm must omit it and the peptdeep arm
  must pass it bare
- Record a clean verdict=fail prose.json instead of crashing when a search
  produces no PeptideIdentification elements
- Document dataset provenance in the definition note; include pending
  design-doc, normalizer, and prose_metrics test updates

Validated against OpenMS PR #9975 (e4b9609c95): both arms pass, Percolator
runs in both, all five PeptDeep features present, target-vs-entrapment
separation clean (67 BSA vs 1 entrapment vs 2 decoy at q<=0.05).
@nej1gotnochill

Copy link
Copy Markdown
Collaborator Author

Added an opt-in ONNX-enabled CI path for the ProSE + PeptDeep benchmark.

Added:

build-openms-onnx job with pinned ONNX Runtime and model SHA256 verification
run-prose-peptdeep-benchmark job using the generated entrapment FASTA and merged BSA mzML inputs
validation that both ProSE arms execute, Percolator produces q-values, entrapment accessions survive, and all five PeptDeep features are present
benchmark normalization and artifact upload

Validation:

Existing benchmark jobs remain unchanged
14/14 local CI/workflow validation checks pass
benchmark tests and definition validation pass
generated FASTA is byte-identical to the Day-31 reference
generated mzML contains 4,812 spectra

The CI benchmark intentionally does not gate on 1% FDR because the current small BSA fixture is pipeline-validation-only; Day-32 showed that apparent 1% gains from duplicated spectra were scientifically invalid.

The new workflow is currently workflow_dispatch only, so it can be validated without affecting normal CI.

Reproducible preparation path for the scientific benchmark dataset
(PXD028735 ProteoBench QE HF-X HYE DDA, CC0): pinned archive.openms.de
downloads with byte-size + sha256 fail-closed verification, deterministic
target + ENTRAPMENT_-prefixed S. cellulosum FASTA assembly, and a
PROVENANCE.txt record. The FASTA generator refuses decoy-marked sources
(including the SoCe file's _rev suffix decoys, which a rev_-prefix check
misses) - ProSE generates decoys via -Search:decoys auto. Validated
against the unmodified runner: verdict pass, both arms, Percolator
q-values, five-feature coverage 79,113/79,113, +5.18% target PSMs at
1% FDR with PeptDeep on prepared inputs.
@nej1gotnochill

Copy link
Copy Markdown
Collaborator Author

Update: ProteoBench external comparison

Added a standalone ProteoBench comparison tool in 333e71d:

  • vendored the 46 submissions from Proteobench/Results_quant_ion_DDA at pinned commit 206f2410bec7c3343471d8fb0fc7f24e516fd196
  • preserved the upstream JSONs byte-verbatim and added per-file SHA256 provenance
  • added parameter-based compatibility filtering with explicit unknown/insufficient metadata handling
  • extracts ProteoBench N=1/3/6 metrics, with N=3 as the contextual headline
  • includes our existing Exp-1/Exp-2 ProteoBench-scorer results separately
  • generates standalone machine-readable and HTML comparison output
  • added 34 focused tests

I intentionally kept this separate from report_generate.py and the evolving benchmark result schema, so the external ProteoBench view does not get mixed into OpenMS internal regression reporting.

The full snapshot gives 46 submissions: 31 compatible, 13 incompatible, and 2 with insufficient/unusable metadata under the documented filter.

The comparison is contextual rather than a leaderboard. Runtime/memory and historical FASTA provenance are not treated as directly comparable.

Related: OpenMS Issue #8788 and OpenMS PR #9975.

Rewrite the README as a project-level entry point following the main
OpenMS README's information architecture: CI badge, project overview,
table of contents, Features, per-benchmark sections (smoke, OpenSwath
DIA, ProteoBench reference results), a layered architecture diagram,
local run commands, results/reporting with schema v2 and v1 promotion,
the three CI jobs with ccache and artifacts, a Development section on
benchmark definitions and adding a benchmark, Testing, an updated
repository structure, present-tense scope, and Resources.
The referenced notebook repository is personal and not publicly
visible, so it does not belong in the public README.
- add `normalize openswath` subcommand: maps raw CI openswath.json into the
  common v2 schema (stages, correctness, verdict, identity), failing loudly
  on non-finite stage metrics
- store the first OpenSwath baseline from real CI run 32995654115
  (build time and artifact size measured from the same CI run)
- point the OpenSwath definition's normalization at the real function so
  validate_definitions.py verifies the wiring
- history table gains a generic Benchmark column (two benchmarks now share
  the results tree)
- 3 new behavioral tests (19 total): normalization contract, non-finite
  rejection, two-run baseline comparison end-to-end

OpenSwath now participates in the same lifecycle as smoke:
runner -> raw JSON -> normalize -> stored v2 baseline -> generic renderer.
Store the second real OpenSwath run (CI run 35456974761, OpenMS
95fb768fa79298ca70ebadbf7fa08a8f54724ae4) alongside the existing
baseline (run 32995654115, f1768367fa66f7901b4fa78a9ebece64b2ce9024),
so the generic renderer now performs a real version-to-version
comparison: correctness identical, no regression detected. Fix the
comparison-card template line that emitted a trailing-whitespace line
when no cache note is present; regenerated report.html is now
git-diff-check clean.
OpenMS moved its CI dependency model to vcpkg ("Only use dependencies
from vcpkg"): newer SHAs no longer install libraries via their
deps-ubuntu.sh, so the classic build fails at CMake configure. Detect
the dependency model from the checked-out SHA itself (deps-script
content + vcpkg mechanism presence, no hardcoded boundary), and for
vcpkg-era SHAs initialize the vcpkg submodule, bootstrap it with the
same upstream mechanism (lukka/run-vcpkg), provide a manifest-hash-keyed
binary cache, and configure with OPENMS_USE_VCPKG=ON and the upstream
linux triplet. Pre-vcpkg SHAs keep the existing path unchanged; the
build/ output layout and all benchmark build flags are preserved, so
the runners need no changes. A SHA with neither working model fails
loudly.
Post-vcpkg OpenMS builds link TOPP tools against dynamic vcpkg
libraries under build/vcpkg_installed/x64-linux-dynamic/lib, which
did not cross the artifact boundary: Smoke and OpenSwath failed at
their first TOPP invocation with exit 127 (loader cannot resolve
libboost/libxerces shared objects). Upload the vcpkg runtime lib
directory with the binary artifact and add it to LD_LIBRARY_PATH in
both benchmark jobs, preserving the existing build/lib path.
…ization-and-cleanup

# Conflicts:
#	.github/workflows/benchmark.yml
#	benchmark/report/test_generic_renderer.py
Static stdlib-only generator reusing the ProteoBench comparison layer:
a pinned reference view over the vendored snapshot (default) and an
optional latest view from a CI shallow clone of upstream HEAD, with
local Exp-1/Exp-2 results rendered alongside external submissions.
The Phase-2 port made the vcpkg runtime-lib operand unconditional in
the du -sb artifact-size steps, so on legacy (pre-vcpkg) builds du
exited 2 on the missing directory and pipefail failed the benchmark
jobs after they had already passed. Make the operand conditional on
the directory existing.
OpenMS SHAs in the vcpkg model install the wnetalign headers
(pylmcf/wnet/wnetalign) and the onnxruntime overlay port only through
VCPKG_MANIFEST_FEATURES, matching upstream's linux-x64-ci preset.
Without them the plain build fails at the CMake generate step
(PYLMCF/WNET/WNETALIGN_INC NOTFOUND, seen in CI run 35700644257) and
the ONNX build cannot satisfy find_package(onnxruntime CONFIG
REQUIRED), which the vcpkg branch of OpenMS' CMakeLists requires
instead of the module/tarball path used in legacy mode.
The onnxruntime vcpkg overlay port builds ORT from source (dbg+rel),
which exhausted the runner disk (0 MB during the debug build) and the
90-minute job budget. Drop the onnxruntime manifest feature and resolve
find_package(onnxruntime CONFIG REQUIRED) against the pinned, SHA256-
verified prebuilt release archive instead, staged at
$RUNNER_TEMP/ort-prefix so the generated config package's lib64 /
include-onnxruntime layout resolves real paths.
Baseline selection (issue #2, task 1):
- pick_baseline now prefers the latest stored run of the same benchmark
  whose OpenMS version is an ancestor of the current run's version,
  resolved via the GitHub compare API (compare(base...head) status
  "ahead"), with per-render caching and graceful fallback to the
  previous timestamp rule when ancestry is unknown, offline, or
  rate-limited (403/429 disables further API use).
- render gains --baseline (explicit run id | OpenMS SHA | result path)
  and --no-sha-baseline.

Normalize + render in CI (issue #2, task 2):
- all five normalizer invocations record real --run-at timestamps;
  smoke/openswath derive cache state from the actual vcpkg cache
  restore instead of hardcoding "none" (new build-openms output
  vcpkg_cache_restored).
- new render-report job merges all benchmark-* result artifacts,
  renders report.html, uploads it, and writes the verdict into the
  job summary.

Cold-cache acceleration:
- both vcpkg build jobs seed .vcpkg-cache from upstream's published
  binary cache at archive.openms.de on Actions-cache miss (same
  manifest hash: hashFiles over vcpkg.json + vcpkg-configuration.json
  reproduces the Linux-X64-g++-vcpkg-0fa92544... tree), cutting
  from-source provisioning from ~90 min to minutes. Misses and
  unreachable archives fall back to source builds.

Tests: 6 new renderer tests (28/28); proteobench + prose suites and
4/4 definitions still pass.
@nej1gotnochill nej1gotnochill mentioned this pull request Sep 25, 2026
5 tasks done
@nej1gotnochill
nej1gotnochill force-pushed the feat/openswath-normalization-and-cleanup branch from b332c52 to 8f291ce Compare September 25, 2026 15:40
@aliviahossain

Copy link
Copy Markdown
Collaborator

Validated SHA ancestor baseline selection in CI with OpenMS e4b9609 … against baseline run 31881021123 at f1768367 ….

Screenshot 2026-09-27 070016 Screenshot 2026-09-27 070048 Screenshot 2026-09-27 070103 Screenshot 2026-09-27 070115 Screenshot 2026-09-27 070130 Screenshot 2026-09-27 070147 Screenshot 2026-09-27 070205

@nej1gotnochill

nej1gotnochill commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Really great work ! Good to see that you validated the SHA ancestor baseline selection in CI with e4b9609 using the stored f1768367 baseline from run 31881021123. I appreciate that this moved beyond just changing the renderer logic and actually demonstrated that the historical baseline is being selected correctly in a real CI run. That makes the change much more convincing and useful for the large dataset and report history work which is ongoing. Thank you !

@timosachsenberg
timosachsenberg added this pull request to stack #8 October 1, 2026 09:43
@nej1gotnochill

Copy link
Copy Markdown
Collaborator Author

Closing this PR as the work has been absorbed into main through #4

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.

3 participants