oncodsl / scripts /run_h2.py
govindbalki's picture
Upload folder using huggingface_hub
0fff343 verified
Raw
History Blame Contribute Delete
3.89 kB
"""Run the H2 GP engine end-to-end and persist artefacts.
This is the biology-aware orchestrator: it loads the processed cohort,
restricts to the usable MSI-H vs MSS samples, anonymises the expression
matrix via the airgap, then hands a binary y plus an opaque-ID matrix to
the engine. The engine never sees the cohort labels' string values or any
gene symbols.
Usage:
python -m scripts.run_h2 [--seed 42] [--population 150] [--generations 30]
Outputs:
data/processed/h2/evolution_log.json (anonymised, opaque IDs only)
data/processed/h2/result.json (anonymised, opaque IDs only)
"""
from __future__ import annotations
import argparse
import json
import sys
from airgap import anonymise
from data_pipeline import schema
from dsl import Load
from engine import run_gp_pipeline
from validate.h1 import POSITIVE_LABEL, usable_msi_cohort
OUT_DIR = schema.PROCESSED_DIR / "h2"
EVOLUTION_PATH = OUT_DIR / "evolution_log.json"
RESULT_PATH = OUT_DIR / "result.json"
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description="Run the H2 GP discovery pipeline.")
ap.add_argument("--seed", type=int, default=42)
ap.add_argument("--population", type=int, default=150)
ap.add_argument("--generations", type=int, default=30)
ap.add_argument("--prefilter-n", type=int, default=2000)
ap.add_argument("--permutations", type=int, default=200)
args = ap.parse_args(argv)
print("=" * 75)
print("OncoDSL H2 — blind discovery via genetic programming")
print("=" * 75)
print(f"Seed : {args.seed}")
print(f"Population size : {args.population}")
print(f"Generations : {args.generations}")
print(f"Prefilter top N : {args.prefilter_n}")
print(f"Permutations : {args.permutations}")
print()
print("[1/3] Loading + anonymising cohort ...")
cohort = usable_msi_cohort(Load("processed"))
matrix = anonymise(cohort.expression)
y = (cohort.labels["msi_status"] == POSITIVE_LABEL).astype(int).values
print(f" {len(cohort.sample_ids)} samples, "
f"{matrix.shape[1]} anonymised feature IDs "
f"(MSI-H {int(y.sum())} / MSS {int((1 - y).sum())}).")
print()
print("[2/3] Running prefilter + GP + baseline + permutation null ...")
evolution_log, result = run_gp_pipeline(
matrix, y,
seed=args.seed,
prefilter_n=args.prefilter_n,
population_size=args.population,
n_generations=args.generations,
n_permutations=args.permutations,
)
print()
print("[3/3] Writing artefacts ...")
OUT_DIR.mkdir(parents=True, exist_ok=True)
EVOLUTION_PATH.write_text(json.dumps(evolution_log, indent=2))
RESULT_PATH.write_text(json.dumps(result, indent=2))
print(f" wrote {EVOLUTION_PATH}")
print(f" wrote {RESULT_PATH}")
win = result["winning"]
base = result["baseline"]
perm = result["permutation_summary"]
print()
print("-" * 75)
print("WINNER (anonymised)")
print("-" * 75)
print(f" program : {win['program_repr']}")
print(f" feature sets : {win['feature_sets']}")
print(f" n_genes : {len(win['gene_ids'])}")
print(f" CV fitness : {win['cv_fitness']:.4f}")
print(f" HELD-OUT AUROC : {win['holdout_auroc']:.4f}")
print(f" Permutation p : {win['permutation_p']:.4f}")
print()
print(f"BASELINE ({len(base['gene_ids'])} top-prefilter genes) "
f"AUROC: {base['holdout_auroc']:.4f}")
print(f"NULL mean AUROC: {perm['null_auroc_mean']:.4f}, "
f"p95: {perm['null_auroc_p95']:.4f}")
print("-" * 75)
print(
"Start the API to serve these artefacts:\n"
" uvicorn api.app:app --reload\n"
"Then open the H2 tab in the Streamlit viewer."
)
return 0
if __name__ == "__main__":
sys.exit(main())