Download scripts/verify_parity.py from elanschonfeld/hmoe-da: direct link, hf CLI and curl.
- Browser
- Download file 2.37 kB
-
https://huggingface.co/elanschonfeld/hmoe-da/resolve/main/scripts/verify_parity.py
- Command line
-
hf download hf://elanschonfeld/hmoe-da/scripts/verify_parity.py
-
curl -L -o verify_parity.py https://huggingface.co/elanschonfeld/hmoe-da/resolve/main/scripts/verify_parity.py
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()) | |