Skip to content

Correct Summary population and deterministic reporting - #699

Open
SuhasSrinivasan wants to merge 3 commits into
nanoporetech:masterfrom
SuhasSrinivasan:codex/fix-summary-output-order
Open

Correct Summary population and deterministic reporting#699
SuhasSrinivasan wants to merge 3 commits into
nanoporetech:masterfrom
SuhasSrinivasan:codex/fix-summary-output-order

Conversation

@SuhasSrinivasan

@SuhasSrinivasan SuhasSrinivasan commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review status: Author-reviewed and ready for ONT review.

Summary

This PR consolidates the related Summary population and reporting corrections:

  • indexed summary --cpg excludes unmapped records, matching the documented explicit --motif CG 0 behavior;
  • bases and modification codes are emitted in deterministic biological/code order;
  • categories seen only among filtered calls receive rows so category totals reconcile with pass/fail/all totals;
  • zero category denominators render as 0, and sampling fractions are logged in percent units.

The three commits are confined to Summary input eligibility, aggregation, and report rendering. Sampling decisions and threshold calculations are unchanged.

Severity

High — scientific counts, reporting completeness, and reproducibility.

The CpG shorthand can silently include an ineligible unmapped population, while filtered-only biological states can disappear despite contributing to totals. Hash-dependent ordering also prevents byte-stable reports.

Bugs and corrected behavior

Case Previous behavior Corrected behavior
Indexed mixed mapped/unmapped BAM with --cpg Unmapped calls could enter the reference-CpG histogram Result equals explicit --motif CG 0 and mapped-only control
Filtered-only canonical/modification state State contributed to totals but had no category row Row is emitted with pass 0 and exact fail/all values
Opposite map insertion orders Base/code rows could differ in order Byte-identical A/C/G/T, canonical-first, letter-code, then numeric-ChEBI order
Empty category denominator Could render a nonfinite fraction Renders numeric zero
Sampling fraction 0.1 log Displayed as 0.1% Displays 10%; sampled population is unchanged

Implementation

  • Treat --cpg as a resolved motif restriction in the indexed-path unmapped-record guard.
  • Build the output state union from passing, filtered, and observed states.
  • Sort canonical bases A/C/G/T; place canonical rows first; sort letter codes lexically and ChEBI codes numerically.
  • Guard category denominators and convert a unit fraction to percent only at logging time.

Testing

Tested exact revision: 413cffd1bc1451901a5d58c3b63acfafb3210b5b
Tree: c40550d5f5c780fc67354b63cbe4bf02a9b78a00
Platform/toolchain: macOS arm64; rustc/cargo 1.90.0; rust-htslib 0.46.0 and hts-sys 2.2.0.

  • Full serial workspace gate: 186 passed, 14 ignored, 0 failed.
  • Focused Summary units: 6 passed.
  • Summary CLI integrations: 5 passed.
  • Parent-red CpG fixture: one appended valid unmapped modified-C record changes upstream total_reads_used from 10 to 11; fixed output remains total_reads_used=10, count_reads_C=10, and C_total_mod_calls=77.
  • Larger installed-versus-fixed fixture: 20 reads / 186 C calls becomes the correct 10 reads / 77 C calls, equal to both explicit-motif and mapped-only controls.
  • Filtered-state matrix covers canonical, letter-code, and numeric-ChEBI rows with exact pass/fail/all reconciliation.
  • Table and TSV output are byte-identical across opposite insertion orders, repeated processes, and threads 1/2/4.
  • The seed-42, fraction-0.1 control samples the same ten reads before and after; only the log changes to 10%.
  • git diff --check, changed-file rustfmt --check, clean-worktree, ancestry, patch-ID, and Cargo.lock checks passed.

Bioinformatics review independently verified the reference-CpG population, category conservation, code ordering, denominator handling, and sampling invariance. No blocker remained.

Output compatibility

  • Affected --cpg runs lose only invalid unmapped contributions.
  • Affected reports gain previously omitted filtered-only rows and deterministic ordering.
  • Counts for correct existing populations, thresholds, sampling decisions, CLI options, and output schema are unchanged.

Reviewer guide

  1. Review the one-condition --cpg unmapped guard and its mapped-only/explicit-motif controls.
  2. Review the state-union and ordering helpers in writers.rs.
  3. Review filtered-only and zero-denominator tests.
  4. Review the log-only percentage conversion.
  5. Run cargo test -p mod_kit summary -- --test-threads=1 and cargo test -p modkit --test test_summary -- --test-threads=1 as focused canaries.

Non-goals

  • No mapped motif-lookup change.
  • No fractional/fixed-count sampling, automatic-threshold, or probability change.
  • No redefinition of modification classes or unrelated unmapped-record modes.
  • No global ordering contract for other commands.

@SuhasSrinivasan SuhasSrinivasan changed the title Reconcile and stabilize summary output Correct Summary population and deterministic reporting Aug 11, 2026
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.

1 participant