#!/usr/bin/env python3 """Per-stage timing of `simurg genotype` for one sample (plan ยง19 measurements). python bench/profile_genotype.py BPM EGT GRN_IDAT [--repeat N] """ from __future__ import annotations import argparse import datetime import time from dataclasses import replace from pathlib import Path import numpy as np from simurg.formats.bpm import read_bpm from simurg.formats.egt import read_egt from simurg.formats.gtc import write_gtc from simurg.formats.idat import read_idat from simurg.genotype.baf_lrr import baf_lrr, logr_dev from simurg.genotype.clusters import align_clusters from simurg.genotype.gencall import call_genotypes from simurg.genotype.sample import genotype_sample, manifest_context, params_for_preset from simurg.genotype.sex import estimate_sex from simurg.signal.normalization.apply import normalize, polar from simurg.signal.normalization.fit import fit_transforms from simurg.signal.raw_xy import LocusMapCache, assemble_raw_xy def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("bpm") ap.add_argument("egt") ap.add_argument("grn") ap.add_argument("--repeat", type=int, default=3) ap.add_argument("--out", default="/tmp/claude-1000/profile.gtc") a = ap.parse_args() m = read_bpm(a.bpm) ctx = manifest_context(m, align_clusters(read_egt(a.egt), m)) lite = m.lite() ctx = replace(ctx, manifest=lite, cache=LocusMapCache(lite)) params = params_for_preset("current") red_path = a.grn.replace("_Grn", "_Red") t: dict[str, list[float]] = {} def lap(name: str, t0: float) -> float: now = time.perf_counter() t.setdefault(name, []).append(now - t0) return now for _ in range(a.repeat): t0 = time.perf_counter() grn, red = read_idat(a.grn), read_idat(red_path) t0 = lap("read_idat x2", t0) raw = assemble_raw_xy(grn, red, lite, cache=ctx.cache) t0 = lap("assemble_raw_xy", t0) xf = fit_transforms(raw.raw_x, raw.raw_y, ctx.norm_bins, params.norm_version) t0 = lap("fit_transforms", t0) nx, ny = normalize(raw.raw_x, raw.raw_y, ctx.lookup, xf) th, r = polar(nx, ny) t0 = lap("normalize+polar", t0) z = (raw.raw_x == 0) & (raw.raw_y == 0) thc = np.where(z, np.float32(np.nan), th).astype(np.float32) rc = np.where(z, np.float32(np.nan), r).astype(np.float32) calls = call_genotypes(thc, rc, z, ctx.clusters, lite.intensity_only, params.cutoff) t0 = lap("call_genotypes", t0) estimate_sex(calls.genotypes, r, ctx.clusters.gentrain, ctx.clusters.present, ctx.sex_classes, params.sex) t0 = lap("estimate_sex", t0) _, lrr = baf_lrr(thc, rc, z, ctx.clusters, lite.intensity_only) logr_dev(lrr, ctx.autosomal, lite.intensity_only, ctx.clusters.present) t0 = lap("baf_lrr+logr_dev", t0) g = genotype_sample(grn, red, ctx, params, datetime.datetime(2026, 1, 1)) t0 = lap("genotype_sample (whole)", t0) write_gtc(g, Path(a.out)) t0 = lap("write_gtc", t0) print(f"{'stage':28s} {'median s':>9s}") for k, v in t.items(): print(f"{k:28s} {sorted(v)[len(v) // 2]:9.3f}") if __name__ == "__main__": main()