P4 (WIP): GenCall-equivalent calling, BAF/LRR, sex, GTC v5, simurg genotype
ci / test (3.12) (push) Failing after 8s
ci / test (3.13) (push) Failing after 10s

- genotype/clusters.py: EGT aligned to manifest order (float32 mean R)
- genotype/gencall.py: flair, six-case likelihood, priors/posteriors,
  fuzzy sub-scores and score map, vectorised with the reference's
  float/double evaluation order and libm transcendentals
- genotype/baf_lrr.py, sex.py (rules v1/v2), sample.py (IDAT -> Gtc)
- pipeline/inputs.py shared by ingest and genotype
- pipeline/genotype.py + CLI: GTC per sample, sample_summary.tsv/json
  (OUT-060..063), run manifest
- GTCs from IDATs are byte-identical to the oracle for 36 GSA samples x
  2 normalization versions (NaN sign canonicalised to the x86 pattern)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01814GKVzZVL3FuF3DeiZXhv
This commit is contained in:
kemalandClaude Opus 5.5 committed 2026-09-28 00:07:37 +01:00
1 parent 0cd7eb8aa0
commit febdd121ef
12 files changed
+1270 -53

No files matched your search

+63 -1
View File
@@ -1,6 +1,6 @@
"""``simurg`` command-line interface (spec §2, plan §14).
Available: ``version``, ``inspect`` (P1), ``ingest`` (P2), ``normalize`` (P3).
Available: ``version``, ``inspect`` (P1), ``ingest`` (P2), ``normalize`` (P3), ``genotype`` (P4).
Later phases add the other commands of CLI-001.
"""
@@ -274,5 +274,67 @@ def normalize_cmd(
run_guarded(rep, lambda: run_normalize(opts, rep))
# --------------------------------------------------------------------------------------------------------
@app.command("genotype")
def genotype_cmd(
bpm: Annotated[Path, typer.Option("--bpm", help="BPM manifest (required).")],
egt: Annotated[Path, typer.Option("--egt", help="EGT cluster file (required).")],
out: Annotated[Path, typer.Option("--out", help="Output folder (required).")],
idats: Annotated[Path | None, typer.Option("--idats", help="Folder searched recursively for IDAT pairs.")] = None,
sheet: Annotated[Path | None, typer.Option("--sheet", help="Sample sheet (CSV).")] = None,
csv: Annotated[Path | None, typer.Option("--csv", help="CSV manifest, checked against the BPM.")] = None,
cutoff: Annotated[float, typer.Option("--cutoff", help="GenCall score below which a call is a no-call.")] = 0.15,
norm_version: Annotated[str, typer.Option("--norm-version", help="1.2.0 (current) or 1.1.2")] = "1.2.0",
sex_version: Annotated[int, typer.Option("--sex-version", min=1, max=2, help="Sex estimate rule (1 or 2).")] = 2,
gtc_version: Annotated[int, typer.Option("--gtc-version", help="GTC format version (3 or 5).")] = 5,
autocall_date: Annotated[
str | None, typer.Option("--autocall-date", help="Fixed value for GTC field 201 (reproducible output).")
] = None,
threads: Annotated[int, typer.Option("--threads", min=0, max=1024, help="Worker processes (0 = auto).")] = 0,
force: Annotated[bool, typer.Option("--force", help="Replace existing outputs.")] = False,
layout: Annotated[str, typer.Option("--layout", help="nested|flat")] = "nested",
on_duplicate_idat: Annotated[str, typer.Option("--on-duplicate-idat", help="error|first|newest")] = "error",
log_json: Annotated[bool, typer.Option("--log-json", "--json-log", help="JSON Lines log on stderr.")] = False,
debug: Annotated[bool, typer.Option("--debug", help="Debug logging with tracebacks.")] = False,
quiet: Annotated[bool, typer.Option("--quiet", "--no-progress", help="Suppress info messages.")] = False,
) -> None:
"""IDAT pairs → GTC files and a sample summary (GenCall-equivalent calling; phase P4)."""
from simurg.pipeline.genotype import GenotypeOptions, run_genotype
if layout not in ("nested", "flat"):
raise typer.BadParameter("--layout must be nested or flat")
if gtc_version not in (3, 5):
raise typer.BadParameter("--gtc-version must be 3 or 5")
if norm_version not in ("1.2.0", "1.1.2"):
raise typer.BadParameter("--norm-version must be 1.2.0 or 1.1.2")
rep = Reporter(
command="genotype",
level="debug" if debug else "info",
json_stderr=log_json,
debug=debug,
quiet=quiet,
log_file=out / "logs" / "genotype.log",
)
opts = GenotypeOptions(
out=out,
bpm=bpm,
egt=egt,
idats=idats,
sheet=sheet,
csv=csv,
threads=threads,
force=force,
layout=layout,
on_duplicate_idat=on_duplicate_idat,
cutoff=cutoff,
norm_version=norm_version,
sex_version=sex_version,
gtc_version=gtc_version,
autocall_date=autocall_date,
argv=sys.argv[1:],
)
run_guarded(rep, lambda: run_genotype(opts, rep))
if __name__ == "__main__": # pragma: no cover
app()