hmoe-da / scripts /verify_parity.py
elanschonfeld's picture
initial open-weights release: package + weights + model card
a974167 verified
Raw History Blame
2.37 kB
#!/usr/bin/env python
"""Parity check: standalone hmoe_da vs the study's cached Kamath predictions.
The cached file Kamath_DA_predicted.h5ad holds `pred_subtype` produced by the
original torch/awatramani_lab path with Gad2 EXCLUDED. So the standalone package
run with include_gad2=False must reproduce those labels exactly, and the stored
obsm['X_hmoe_P_sub'] to <1e-5.
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
import scanpy as sc
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from hmoe_da import predict # noqa: E402
REPO = Path(__file__).resolve().parents[2]
RAW = REPO / "Data" / "Human" / "Kamath" / "Human_Kamath_Dopamine.h5ad"
CACHED = REPO / "Data" / "Human" / "Kamath" / "Kamath_DA_predicted.h5ad"
def main() -> int:
raw = sc.read_h5ad(RAW)
cached = sc.read_h5ad(CACHED)
print(f"raw {raw.shape} cached {cached.shape}")
# Align cached rows to raw order by barcode.
idx = cached.obs_names.get_indexer(raw.obs_names)
assert (idx >= 0).all(), "some raw barcodes missing from cached file"
ref_sub = cached.obs["pred_subtype"].values[idx]
ref_P = np.asarray(cached.obsm["X_hmoe_P_sub"])[idx]
ad = predict(raw.copy(), include_gad2=False, verbose=True)
got_sub = ad.obs["hmoe_subtype"].values
got_P = np.asarray(ad.obsm["X_hmoe_P_sub"])
max_dP = float(np.max(np.abs(got_P - ref_P)))
label_match = float(np.mean(got_sub == ref_sub))
n_mismatch = int(np.sum(got_sub != ref_sub))
print(f"\nP_sub max abs diff : {max_dP:.2e}")
print(f"label agreement : {label_match*100:.4f}% ({n_mismatch} mismatched)")
ok = (max_dP < 1e-5) and (n_mismatch == 0)
if not ok and n_mismatch:
# show a few mismatches for diagnosis
mm = np.where(got_sub != ref_sub)[0][:10]
for i in mm:
print(f" cell {i}: got {got_sub[i]!r} vs ref {ref_sub[i]!r} "
f"(dP={np.max(np.abs(got_P[i]-ref_P[i])):.2e})")
# sanity: include_gad2=True should surface some Gad2 calls
ad2 = predict(raw.copy(), include_gad2=True, verbose=False)
n_gad = int((ad2.obs["hmoe_family"] == "Gad2").sum())
print(f"include_gad2=True : {n_gad} cells called Gad2")
print("\nPARITY", "PASS" if ok else "FAIL")
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())