| |
| """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.") |
|
|
| |
| 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 = {} |
|
|
| |
| 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} |
|
|
| |
| gene_counts = [500, 1000, 2000] |
| if device == "cpu": |
| gene_counts = [500] |
|
|
| 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 |
|
|
| 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} |
|
|
| |
| 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} |
|
|
| |
| 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) |
|
|
| |
| 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() |
|
|