scPTR / analyses /deep /16_gpu_scalability.py
bryan7264's picture
Add files using upload-large-folder tool
925ee3b verified
Raw
History Blame Contribute Delete
5.95 kB
#!/usr/bin/env python
"""GPU scalability: full-genome DeepPTR with CUDA.
Demonstrates that DeepPTR scales to full gene sets when GPU is available,
comparing runtime and quality vs the 300-gene CPU subset.
"""
from _common import *
OUT = output_dir("16_gpu_scalability")
def main():
set_figure_style()
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Device: {device}")
if device == "cpu":
print(" [WARN] No GPU available. Running reduced comparison.")
# Load and preprocess
adata_raw = scptr.datasets.pancreas()
scptr.pp.filter_genes(adata_raw)
scptr.pp.normalize_layers(adata_raw)
scptr.pp.neighbors(adata_raw, n_neighbors=30)
scptr.pp.smooth_layers(adata_raw)
scptr.tl.estimate_beta(adata_raw)
_, hl_human = load_halflife_refs()
results = {}
# ── CPU 300 genes (baseline) ──────────────────────────────────────
print(f"\n{'=' * 60}\nCPU: 300 genes\n{'=' * 60}")
adata_300 = select_top_genes(adata_raw, n_top=300)
from scipy.sparse import issparse
for key in ("spliced", "unspliced"):
if key in adata_300.layers and issparse(adata_300.layers[key]):
adata_300.layers[key] = np.asarray(adata_300.layers[key].todense())
torch.set_num_threads(4)
t0 = _time.time() if 'time' not in dir() else __import__('time').time()
import time as _time
t0 = _time.time()
scptr.deep.fit_deepptr(adata_300, device="cpu", verbose=True, **DEEP_HP)
t_cpu_300 = _time.time() - t0
r_300, n_300 = halflife_spearman(adata_300, hl_human)
print(f" Time: {t_cpu_300:.1f}s, HL r={r_300:.4f} (n={n_300})")
results["cpu_300"] = {"time": t_cpu_300, "r": r_300, "n_genes": 300, "n_hl": n_300}
# ── GPU scaling experiments ───────────────────────────────────────
gene_counts = [500, 1000, 2000]
if device == "cpu":
gene_counts = [500] # Reduced for CPU-only
for n_genes in gene_counts:
if n_genes > adata_raw.n_vars:
continue
label = f"{device}_{n_genes}"
print(f"\n{'=' * 60}\n{device.upper()}: {n_genes} genes\n{'=' * 60}")
adata_n = select_top_genes(adata_raw, n_top=n_genes)
for key in ("spliced", "unspliced"):
if key in adata_n.layers and issparse(adata_n.layers[key]):
adata_n.layers[key] = np.asarray(adata_n.layers[key].todense())
hp = dict(DEEP_HP)
hp["device"] = device
if n_genes > 1000:
hp["d_hidden"] = 64 # Scale up for more genes
torch.set_num_threads(4)
t0 = _time.time()
try:
scptr.deep.fit_deepptr(adata_n, verbose=True, **hp)
elapsed = _time.time() - t0
r_n, n_n = halflife_spearman(adata_n, hl_human)
print(f" Time: {elapsed:.1f}s, HL r={r_n:.4f} (n={n_n})")
results[label] = {"time": elapsed, "r": r_n, "n_genes": n_genes, "n_hl": n_n}
except Exception as e:
print(f" FAILED: {e}")
results[label] = {"error": str(e), "n_genes": n_genes}
# ── Full genome attempt ───────────────────────────────────────────
if device == "cuda":
n_full = adata_raw.n_vars
print(f"\n{'=' * 60}\nGPU: Full genome ({n_full} genes)\n{'=' * 60}")
adata_full = adata_raw.copy()
for key in ("spliced", "unspliced"):
if key in adata_full.layers and issparse(adata_full.layers[key]):
adata_full.layers[key] = np.asarray(adata_full.layers[key].todense())
hp = dict(DEEP_HP)
hp["device"] = "cuda"
hp["d_hidden"] = 128
hp["batch_size"] = 256
t0 = _time.time()
try:
scptr.deep.fit_deepptr(adata_full, verbose=True, **hp)
elapsed = _time.time() - t0
r_full, n_full_hl = halflife_spearman(adata_full, hl_human)
print(f" Time: {elapsed:.1f}s, HL r={r_full:.4f} (n={n_full_hl})")
results[f"gpu_full_{n_full}"] = {
"time": elapsed, "r": r_full, "n_genes": n_full, "n_hl": n_full_hl
}
except Exception as e:
print(f" FAILED: {e}")
results[f"gpu_full_{n_full}"] = {"error": str(e), "n_genes": n_full}
# ── Summary ───────────────────────────────────────────────────────
print(f"\n{'=' * 60}")
print("SCALABILITY SUMMARY")
print("=" * 60)
print(f" {'Config':<25} {'Genes':>8} {'Time':>10} {'HL r':>10} {'HL n':>8}")
for label, d in results.items():
if "error" in d:
print(f" {label:<25} {d['n_genes']:>8} {'FAIL':>10}")
else:
print(f" {label:<25} {d['n_genes']:>8} {d['time']:>9.1f}s {d['r']:>10.4f} {d['n_hl']:>8}")
save_json(results, "gpu_scalability", OUT)
# Figure
configs = [k for k in results if "error" not in results[k]]
if len(configs) > 1:
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
genes = [results[k]["n_genes"] for k in configs]
times = [results[k]["time"] for k in configs]
rs = [abs(results[k]["r"]) for k in configs]
axes[0].plot(genes, times, "o-", color="steelblue")
axes[0].set_xlabel("Number of genes")
axes[0].set_ylabel("Runtime (seconds)")
axes[0].set_title("Scalability")
axes[1].plot(genes, rs, "o-", color="darkorange")
axes[1].set_xlabel("Number of genes")
axes[1].set_ylabel("|r| with half-life")
axes[1].set_title("Quality vs gene count")
fig.tight_layout()
save_fig(fig, "gpu_scalability", OUT)
if __name__ == "__main__":
main()