P3: normalization fit (1.2.0 and 1.1.2), apply, polar, simurg normalize
ci / test (3.12) (push) Failing after 8s
ci / test (3.13) (push) Failing after 10s

- signal/normalization/numerics.py: vendor percentile, trimmean, MAD sigma
  (old/new), bisquare robust fits, float32/float64 typing and sequential
  sums reproduced exactly (ported from gtc2vcf, MIT)
- signal/normalization/fit.py: outlier removal, offset, rotation, shear,
  scale, nn12/rr12, single-channel model for small Infinium I bins,
  exact and binned nearest-neighbour sweeps; vendor quirks marked QUIRK
- signal/normalization/apply.py: affine transform and theta/R
- pipeline/normalize.py + CLI `simurg normalize`
- oracle GTCs rebuilt with -ffp-contract=off (aarch64 gcc fuses a*b+c by
  default, shifting the last bits); the old FMA oracle differed by <=4 ULP

Result: transforms bit-identical to the oracle for all 36 GSA samples x
48 bins, for both normalization versions (3456/3456 bins, 0 ULP).

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-27 22:51:06 +01:00
1 parent 5c93e194e0
commit 314fdc40d1
12 files changed
+1097 -14

No files matched your search

+23 -1
View File
@@ -1,6 +1,7 @@
"""``simurg`` command-line interface (spec §2, plan §14).
Available: ``version``, ``inspect`` (P1), ``ingest`` (P2). Later phases add the other commands of CLI-001.
Available: ``version``, ``inspect`` (P1), ``ingest`` (P2), ``normalize`` (P3).
Later phases add the other commands of CLI-001.
"""
from __future__ import annotations
@@ -252,5 +253,26 @@ def ingest_cmd(
run_guarded(rep, lambda: run_ingest(opts, rep))
# --------------------------------------------------------------------------------------------------------
@app.command("normalize")
def normalize_cmd(
indir: Annotated[Path, typer.Option("--in", help="Output folder of `simurg ingest`.")],
norm_version: Annotated[str, typer.Option("--norm-version", help="1.2.0 (current) or 1.1.2 (older)")] = "1.2.0",
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,
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:
"""Fit per-bin normalization transforms and write normalized X/Y, theta and R (phase P3)."""
from simurg.pipeline.normalize import NormalizeOptions, run_normalize
rep = Reporter(
command="normalize", level="debug" if debug else "info", json_stderr=log_json, debug=debug, quiet=quiet
)
opts = NormalizeOptions(indir=indir, version=norm_version, threads=threads, force=force, argv=sys.argv[1:])
run_guarded(rep, lambda: run_normalize(opts, rep))
if __name__ == "__main__": # pragma: no cover
app()