Skip to content

Release v1.21.0 — per-variant allele fraction#403

Merged
joshfactorial merged 16 commits into
mainfrom
develop
Jul 18, 2026
Merged

Release v1.21.0 — per-variant allele fraction#403
joshfactorial merged 16 commits into
mainfrom
develop

Conversation

@joshfactorial

Copy link
Copy Markdown
Collaborator

Release v1.21.0 to main. develop is ahead of main by the work below and has no divergence
to reconcile (main carries no unique content). fmt clean, full suite green (26 binaries),
binary reports 1.21.0.

Headline — per-variant allele fraction (#398 / #399)

gen-reads now honors an optional per-variant allele_fraction from the input VCF (INFO/AF,
else FORMAT/AD), so an input VCF can drive a continuous allele-frequency spectrum instead of
only {0.5, 1.0}. Backward-compatible — variants without an allele fraction are byte-identical
to prior output. Validated on real SCN Pool-seq: simulated-vs-real per-site AF r = 0.98 at 150×,
unbiased. See CHANGELOG for detail.

Also included

After merge

  1. Tag v1.21.0 on main (git tag v1.21.0 <merge sha> && git push origin v1.21.0).
  2. Bump the conda recipe to 1.21.0 + sha256 (PR to develop).

joshfactorial and others added 16 commits July 13, 2026 09:45
Match the released v1.20.1 tag (RNG out-of-range fix).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore(conda-recipe): bump to v1.20.1
Update the SCN status doc to reflect completion: SV + CNV models built for both
strains and rendered end-to-end (loop closed), the MM26-vs-PA3/BD3 contrast with its
confound caveat, the RDCN→INFO/CN CNV format gap + fix, and the v1.20.1 RNG release.
Refreshed tooling table, Delta ops notes (strain-override + verify-the-statistic), and
the open items (common-reference comparison, Phase 2, harness #395, CNV biology, long reads).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per-strain sim-vs-truth fidelity is the validation goal and stands on its
own regardless of reference; the strain contrast + common-reference run are
a biological virulence claim on top of the validated tool, not a validation
gap. Reframe "Open / next steps" to separate tooling/validation items that
can proceed (Phase 2 AF replay, harness #395) from biology extensions parked
pending colleague feedback (common-ref comparison, CNV sanity, long reads).

Co-Authored-By: Claude <noreply@anthropic.com>
Trace the alt-fraction mechanism end-to-end on develop. Corrects stale line
numbers and identifies the real lever: the per-read alt/ref branch in
generate_read (fastq_tools.rs:462), which drives both emitted reads and the
AdCounter (so golden AF tracks it for free). mutate_position is a secondary
SV-junction-only path. Input-VCF variants confirmed to flow through the main
path via variant_map. Scope reduced to: add Variant.allele_fraction, populate
in from_file (INFO/AF or FORMAT/AD), generalize the :462 branch; no golden-VCF
output change needed. Notes overlap with parked polyploidy #266.

Co-Authored-By: Claude <noreply@anthropic.com>
Add an optional `allele_fraction: Option<f64>` on `Variant`, parsed from an
input VCF (INFO/AF, else FORMAT/AD alt/(ref+alt)) and honored by the per-read
alt/ref decision in generate_read (fastq_tools.rs) and the SV-junction path
(mutated_map.rs). When present, the alt allele is emitted on that fraction of
overlapping reads instead of the {homozygous=1.0, het=0.5} default — letting an
input VCF drive a continuous AF spectrum (pooled/somatic data). AD is used over
GT for pooled data, where GT is not diploid-meaningful.

The golden VCF's measured AF (GT:AD:DP:AF) tracks the requested fraction for
free, since the same branch increments the AdCounter — no output change needed.
Enabling change for SCN Phase 2 (realism epic #311).

Determinism: the None (no-AF) path keeps the exact same RNG draw and
short-circuit, so default runs stay byte-identical (model_parity + full suite
green). Variant drops its unused Eq/Hash derives (f64 is only PartialEq); it's
never a map key or set member — compare_vcfs hashes a separate VariantKey.

Tests: parse (INFO/AF, AD, multiallelic, out-of-range, absent) + from_file
population; a generate_read counter test asserting the AD split tracks f; and
allele_fraction_e2e.rs driving the real binary end-to-end (input AF 0.25/0.75 →
golden AF matches, clear of the 0.5 default).

Co-Authored-By: Claude <noreply@anthropic.com>
#398)

Adds the Phase 2 real-data validation for per-variant allele_fraction:

- scn_af_compare.py — dependency-free per-site AF correlation of two VCFs
  (truth/input vs simulated golden). Matches sites by CHROM/POS/REF/ALT; reads
  AF from FORMAT/AF, INFO/AF, or FORMAT/AD; reports Pearson/Spearman, MAE/RMSE
  overall and per AF-decile, with a --min-depth gate. Mirrors sbs96_compare.py.
- run_scn_af_validation.sh — SLURM driver: derive per-site AF from the staged
  pool VCF's AD (SNVs only), run gen-reads with it as input_vcf at mutation_rate=0
  / sv_rate_scale=0 (only supplied sites placed), then compare golden AF vs pool AF.

Untestable from the workstation (Delta filesystem); scn_af_compare.py verified
locally on synthetic VCFs (r=1.0, per-decile MAE, depth gating). Driver has
explicit preflights (FORMAT/AD present, non-zero site counts, banner) per the
verify-the-statistic-not-the-PASS rule.

Co-Authored-By: Claude <noreply@anthropic.com>
stage_scn.sh: add `-a FORMAT/AD` to the bcftools mpileup call so staged pool
VCFs carry per-sample allele depths (additive; needed to derive per-site AF).

run_scn_af_validation.sh: when the pool VCF predates that change and lacks AD,
re-derive AD from the staged BAM at just the SNV sites (-R restricted) instead
of forcing a full re-stage. POOL_AF is both the gen-reads input and the truth,
so the comparison stays self-consistent either way.

Co-Authored-By: Claude <noreply@anthropic.com>
The first Delta run (job 20270457) returned r=0.61 with the "truth" piled at
0.5 — a false negative. Root cause: the harness ran `bcftools +fill-tags -t AF`,
whose AF tag is computed from GT, so a diploid-called pool collapses every het to
0.5 and hom to 1.0 — not the pooled allele fraction. Both gen-reads and the
comparison then used 0.5, so we tested "reproduce 0.5", not the real spectrum.
(The feature itself was correct: hom sites reproduced at exactly 1.0, hets
scattered around 0.5 at textbook 50x binomial noise.)

Fix: pass the AD-carrying VCF through with NO INFO/AF. Both gen-reads (from_file)
and scn_af_compare.py derive the fraction from FORMAT/AD = alt/(ref+alt), which is
the real pool spectrum. Added a pre-run sanity histogram of the AD-derived truth
AF so a collapsed spectrum surfaces instead of hiding behind exit 0.

Co-Authored-By: Claude <noreply@anthropic.com>
docs(scn): status — track complete (SV/CNV/render/contrast)
feat(gen-reads): per-variant allele_fraction from input VCF (#398)
Bump workspace version 1.20.1 → 1.21.0 and add the CHANGELOG entry for the
allele_fraction feature (#398/#399), which merged to develop without a version
bump — leaving develop colliding with the already-released 1.20.1. Minor bump:
the feature is backward-compatible (variants without an allele fraction are
byte-identical to prior output).

No code changes — version + CHANGELOG + lockfile only.

Co-Authored-By: Claude <noreply@anthropic.com>
Mechanical `cargo fmt --all` across the workspace — no logic changes. develop
had drifted from rustfmt's output (no enforcement existed), so a local `cargo fmt`
would rewrite ~38 files and bury real diffs; this one-time sweep resets the tree
to the canonical format. Adds rustfmt.toml (edition 2024) to pin the style.

Verify: `cargo fmt --all -- --check` is clean; full test suite green (26 binaries).

Co-Authored-By: Claude <noreply@anthropic.com>
Adds a `fmt` job running `cargo fmt --all -- --check` so formatting drift fails
CI instead of accumulating silently (the reason the sweep above was needed).

Co-Authored-By: Claude <noreply@anthropic.com>
chore(release): v1.21.0 — per-variant allele fraction
chore: normalize formatting (cargo fmt) + CI fmt gate
@joshfactorial
joshfactorial merged commit 689de92 into main Jul 18, 2026
3 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.

1 participant