Skip to content

Feature stats dev - #1

Merged
adamfreedman merged 9 commits into
mainfrom
feature_stats_dev
Aug 12, 2026
Merged

Feature stats dev#1
adamfreedman merged 9 commits into
mainfrom
feature_stats_dev

Conversation

@adamfreedman

Copy link
Copy Markdown
Contributor

added sundry feature-level stats and tests

adamfreedman and others added 9 commits July 21, 2026 13:48
Extend AnnotationQC with two QC metric families beyond BUSCO completeness:

- Structural/nesting/length/UTR stats via AGAT agat_sp_statistics.pl --yaml,
  parsed into a tidy long table results/qc/annotation_stats.tsv
  (rules/agat_stats.smk, scripts/parse_agat_stats.py, envs/pyutils.yml).
- Coding integrity via psauron: annotation-level score (% of CDS scored as
  real protein) plus the saved per-transcript scoring CSV; CDS extracted with
  gffread -x as a separate rule so it does not re-trigger compleasm
  (rules/psauron.smk, scripts/parse_psauron.py, envs/psauron.yml).

Hardening for portability: every python-invoking rule runs with
PYTHONNOUSERSITE=1 and AGAT with 'env -u PERL5LIB', keeping the conda envs
hermetic against leaked ~/.local / ~/apps/perl packages.

rule all now targets all three QC tables; profiles/slurm gains resources for
the new rules; .gitignore excludes AGAT's runtime log dirs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add config-driven GPU support for the psauron rule:
- config.yaml: psauron_use_gpu (default false), psauron_gpu_partition, psauron_gpus
- psauron.smk: conditional rule -- the GPU branch uses envs/psauron_gpu.yml
  (CUDA torch), submits to the configured GPU partition via Snakemake's 'gpu'
  resource (--gpus=N), and lets psauron auto-detect the GPU (no -c); the CPU
  branch is unchanged.
- envs/psauron_gpu.yml: CUDA pytorch build (numpy<2, scipy<1.15, setuptools<81);
  documents exporting CONDA_OVERRIDE_CUDA to build on a CPU submit node.
- profiles/slurm: psauron resources now live on the rule (they are conditional),
  so removed from the profile.
- README: GPU usage section.

CPU-only users leave psauron_use_gpu: false and never build the CUDA env.

Verified on Cannon: a GPU run scored the bullfrog draft's CDS on a gpu-partition
node (holygpu8a22502) in <3 min (psauron score 92.9%).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every annotation is validated before any metric runs; the workflow halts
loudly on failure (no misleading numbers), and the metrics run on the ORIGINAL
annotation gated by a pass-marker -- nothing is rewritten (AGAT's standardizer
silently drops edge-case features, so it is not used to produce the file the
metrics consume).

Gate (workflow/rules/validate.smk):
- gff_syntax_check: gt gff3validator (genometools) for strict GFF3 syntax.
- gff_validate (scripts/validate_gff.py): custom checks vs the genome -- every
  seqid present in the genome FASTA; feature coords within 1..sequence_length;
  CDS phase valid (0/1/2); required feature types (CDS/exon/mRNA) present.
  Writes a per-annotation report + a validation_summary.tsv row; exits non-zero
  with a documented explanation on any failure.
- Optional non-fatal AGAT advisory (config agat_advisory, default off).

Also:
- metric rules (extract_sequences, extract_cds, agat_statistics) now depend on
  the {aid}.validated pass-marker and run on the original annotation.
- fail-loud guards: extraction fails on empty output; busco/psauron parsers
  fail on N=0 / no rows.
- wildcard_constraints on {aid} to avoid output-pattern ambiguity.
- new envs: genometools; pyfaidx added to pyutils.

Verified on Cannon: raw bullfrog + mouse both pass gt and all custom checks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the 'Input validation' section (gt gff3validator + custom genome checks,
fail-loud, metrics run on the original), the agat_advisory config option, and
validation_summary.tsv to the outputs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ation

Modeled on the scRNAseq-preprocessing repo: a fast, hermetic default suite plus
opt-in heavy checks, wired into GitHub Actions.

Workflow:
- workflow/rules/common.smk: validate_config() + validate_annotations() run at
  load time, raising a clear ValueError on a malformed config/samplesheet.
- channel_priority: strict added to all workflow/envs/*.yml.

Tests (tests/, pytest):
- Fast (always-on, hermetic): snakemake --lint parses; -np dry-run builds the
  DAG with all rules + the four QC targets; config & sample-sheet validation
  reject malformed inputs; static env checks (channels / channel_priority /
  non-empty deps, and every conda: reference resolves) + container declarations
  must be docker:// and pinned.
- Opt-in (conftest flags): --run-conda-validation (solve each env + smoke-check
  its key tool), --run-container-validation (pull declared images; none yet),
  --run-workflow (end-to-end on the local mouse chr1 subset).
- Tiny committed dataset tests/testdata/tiny.{fa,gff3}; the chr1 E2E input is
  local-only (gitignored, too big to commit).

CI: .github/workflows/tests.yml (fast suite on push/PR; conda-validation matrix
on manual dispatch). environment.yml runner env. tests/README.md.

Verified on Cannon: fast suite 15 passed / 3 skipped; gffread conda-validation passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All rule outputs now live under RESULTS = config.get("results_dir", "results")
instead of a hardcoded results/ prefix. Backward-compatible (default unchanged),
so a test run can redirect outputs to its own dir without clobbering a real
results/ tree — needed for the golden-output E2E test.

Verified: fast suite 15 passed / 3 skipped (default path unchanged); a
results_dir override redirects every output as expected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The opt-in end-to-end test now runs the pipeline on the chr1 subset into an
isolated results_dir and compares all four QC tables against committed golden
references, instead of only checking they are non-empty.

- tests/reference_outputs/*.tsv: golden QC tables generated from the mouse chr1
  subset (eukaryota lineage, CPU psauron).
- tests/_helpers.py: assert_table_matches_golden() -- order-independent row match;
  exact for deterministic columns, per-column tolerance for the model-derived
  psauron score (+/-2.0) and protein-CDS count (+/-50).
- tests/test_workflow_run.py: E2E compares vs golden (isolated results_dir, never
  touches a real results/); plus always-on test_golden_outputs_wellformed guard.
- tests/update_golden.py: regenerate the golden references after an intended change.
- tests/README.md + .gitignore updated (ignore the golden-run scratch dir).

Verified: fast suite 16 passed / 3 skipped; golden run rc=0 (psauron 98.7%,
6133 CDS; BUSCO deterministic on the chr1 subset).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
agat_sp_statistics.pl emits some symmetric metrics (e.g. the count named
"Number A..B overlapping") with a run-to-run-variable A..B direction, because
Perl randomizes hash-key iteration order per process. That made
annotation_stats.tsv non-reproducible and broke the golden-output E2E comparison.

Pin it with PERL_HASH_SEED=0 + PERL_PERTURB_KEYS=0 in the agat_statistics rule
(also makes real QC runs reproducible). Verified: two runs now produce
byte-identical annotation_stats, matching the committed golden. Widened the
golden-run scratch gitignore glob.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A stray trailing tab in annotations.tsv gave the row one more field than
the header, so pandas read the extra empty field as an index column and
shifted every value one column left: annotation_id became the annotation
path and genome_path became nan.

validate_annotations() already caught the resulting empty genome_path,
but the message named the symptom rather than the cause. Read the sheet
with index_col=False so the shift cannot happen, and point at the extra
field in the empty-value message for any other way a row goes long.

Adds a regression test (verified to fail without index_col=False).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@adamfreedman
adamfreedman merged commit 0ca8e62 into main Aug 12, 2026
4 checks passed
@adamfreedman
adamfreedman deleted the feature_stats_dev branch August 12, 2026 20:34
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