syntheogenesis / scripts /run_benchmarks.py
Tengo Gzirishvili
Admin endpoints to run the real validation + seed against bundled DMS
598a072
Raw
History Blame Contribute Delete
3.01 kB
#!/usr/bin/env python3
"""Produce the REAL validation numbers — the receipts behind the glass-box claim.
Runs the engine's zero-shot ESM-2 ranking against published deep-mutational-
scanning (DMS) datasets and writes dee/data/benchmarks.json (Spearman ρ +
top-decile precision per dataset). Run this where torch + the ESM weights
exist (the HF Space, or any box with the model); it is the ONLY thing that
should ever populate benchmarks.json — the app never hand-enters numbers.
Input: a manifest JSON, a list of assays:
[
{"name": "...", "protein": "GENE_ORG", "sequence": "MSK...",
"csv": "path/to/dms.csv", "source": "doi:..."},
...
]
The CSV is ProteinGym-style (a 'mutant'/'mutation' column, a 'DMS_score'/
'score' column). Multi-mutants ('A1C:D5E') are scored as the sum of their
single-site ΔLLs (the same additive assumption the design engine makes — so
this validates exactly what we ship).
Usage:
python scripts/run_benchmarks.py manifest.json [--model small] [--out dee/data/benchmarks.json]
"""
import argparse
import datetime as dt
import json
import sys
from pathlib import Path
# Make `dee` importable when run from the repo root.
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from dee.core import benchmark as bm # noqa: E402
from dee.core.dms_seed import parse_proteingym_csv # noqa: E402
def main():
ap = argparse.ArgumentParser()
ap.add_argument("manifest")
ap.add_argument("--model", default="small")
ap.add_argument("--out", default=str(Path(__file__).resolve().parent.parent / "dee" / "data" / "benchmarks.json"))
args = ap.parse_args()
from dee.core import scoring
scorer = scoring.get_scorer(args.model)
assays = json.loads(Path(args.manifest).read_text(encoding="utf-8"))
results = []
for a in assays:
recs = parse_proteingym_csv(Path(a["csv"]).read_text(encoding="utf-8"))
if not recs:
print(f" skip {a.get('name')}: no parseable records")
continue
labels = [lab for (lab, _v) in recs]
measured = [v for (_lab, v) in recs]
scores_df = scorer.score_all_substitutions(a["sequence"])
predicted = bm.predict_additive(scores_df, labels)
r = bm.evaluate_dataset(a.get("name", a.get("protein", "?")),
a.get("protein", ""), predicted, measured,
source=a.get("source", ""))
results.append(r)
print(f" {r.name:24s} n={r.n:6d} rho={r.spearman} top10p={r.top_decile_precision}")
out = {
"generated_at": dt.datetime.now(dt.timezone.utc).isoformat(),
"model": args.model,
"summary": bm.summarize(results),
"datasets": [r.as_dict() for r in results],
}
Path(args.out).write_text(json.dumps(out, indent=2), encoding="utf-8")
print(f"\nWrote {len(results)} dataset result(s) -> {args.out}")
print(f"Summary: {out['summary']}")
if __name__ == "__main__":
main()