| |
| """Full-genome DeepPTR on GPU: eliminate the 300-gene limitation. |
| |
| Runs DeepPTR on ALL genes (after filtering) using CUDA, comparing: |
| - 300 genes CPU (previous baseline) |
| - 500, 1000, 2000, ALL genes on GPU |
| - Half-life correlation, runtime, and gene coverage at each scale |
| """ |
| from _common import * |
| import time as _time |
|
|
| OUT = output_dir("24_fullgenome_gpu") |
|
|
| |
| DEVICE = "cpu" |
|
|
|
|
| def run_at_scale(adata_base, n_genes, device, label): |
| """Run DeepPTR at a given gene count.""" |
| from scipy.sparse import issparse |
|
|
| if n_genes >= adata_base.n_vars: |
| adata = adata_base.copy() |
| actual_n = adata.n_vars |
| else: |
| adata = select_top_genes(adata_base, n_top=n_genes) |
| actual_n = n_genes |
|
|
| for key in ("spliced", "unspliced"): |
| if key in adata.layers and issparse(adata.layers[key]): |
| adata.layers[key] = np.asarray(adata.layers[key].todense()) |
|
|
| |
| d_hidden = 48 if actual_n <= 500 else 64 if actual_n <= 2000 else 128 |
|
|
| hp = dict(DEEP_HP) |
| hp["device"] = device |
| hp["d_hidden"] = d_hidden |
| hp["n_posterior_samples"] = 15 |
|
|
| torch.set_num_threads(4) |
| t0 = _time.time() |
| try: |
| model, history = scptr.deep.fit_deepptr(adata, verbose=True, **hp) |
| elapsed = _time.time() - t0 |
| n_epochs = len(history.train_loss) |
| return adata, elapsed, n_epochs |
| except Exception as e: |
| print(f" FAILED: {e}") |
| return None, _time.time() - t0, 0 |
|
|
|
|
| def main(): |
| set_figure_style() |
| _, hl_human = load_halflife_refs() |
| hl_mouse, _ = load_halflife_refs() |
|
|
| all_results = {} |
|
|
| for ds_name, loader, ck in DATASETS: |
| print(f"\n{'#' * 60}") |
| print(f"# {ds_name.upper()}: Full-genome GPU scaling") |
| print(f"{'#' * 60}") |
|
|
| |
| adata_base = loader() |
| scptr.pp.filter_genes(adata_base) |
| scptr.pp.normalize_layers(adata_base) |
| scptr.pp.neighbors(adata_base, n_neighbors=30) |
| scptr.pp.smooth_layers(adata_base) |
| scptr.tl.estimate_beta(adata_base) |
|
|
| total_genes = adata_base.n_vars |
| print(f" Total genes after filtering: {total_genes}") |
|
|
| |
| scales = [300, 500, 1000, 2000] |
| scales = [s for s in scales if s <= total_genes] |
|
|
| ds_results = [] |
|
|
| for n_g in scales: |
| label = f"{n_g} genes" if n_g < total_genes else f"ALL ({total_genes})" |
| device = "cpu" if n_g <= 300 else DEVICE |
| print(f"\n --- {label} on {device} ---") |
|
|
| adata_fit, elapsed, n_epochs = run_at_scale(adata_base, n_g, device, label) |
|
|
| if adata_fit is not None: |
| |
| r_m, n_m = halflife_spearman(adata_fit, hl_mouse) |
| r_h, n_h = halflife_spearman(adata_fit, hl_human) |
| print(f" Time: {elapsed:.1f}s, epochs: {n_epochs}") |
| print(f" HL mouse: r={r_m:.4f} (n={n_m})") |
| print(f" HL human: r={r_h:.4f} (n={n_h})") |
|
|
| ds_results.append({ |
| "n_genes": n_g if n_g < total_genes else total_genes, |
| "label": label, |
| "device": device, |
| "time_s": elapsed, |
| "n_epochs": n_epochs, |
| "hl_mouse_r": float(r_m), |
| "hl_mouse_n": n_m, |
| "hl_human_r": float(r_h), |
| "hl_human_n": n_h, |
| }) |
| else: |
| ds_results.append({ |
| "n_genes": n_g if n_g < total_genes else total_genes, |
| "label": label, |
| "device": device, |
| "error": True, |
| "time_s": elapsed, |
| }) |
|
|
| |
| scptr.tl.estimate_gamma(adata_base) |
| r_an_m, n_an_m = halflife_spearman(adata_base, hl_mouse) |
| r_an_h, n_an_h = halflife_spearman(adata_base, hl_human) |
| print(f"\n Analytical (all {total_genes} genes): mouse={r_an_m:.4f}(n={n_an_m}), human={r_an_h:.4f}(n={n_an_h})") |
|
|
| ds_results.append({ |
| "n_genes": total_genes, "label": "Analytical (all)", |
| "device": "cpu", "hl_mouse_r": float(r_an_m), "hl_human_r": float(r_an_h), |
| "hl_mouse_n": n_an_m, "hl_human_n": n_an_h, |
| }) |
|
|
| all_results[ds_name] = ds_results |
|
|
| |
| print(f"\n {'Config':<25} {'Device':>6} {'Time':>8} {'HL mouse':>10} {'HL human':>10} {'n_HL':>6}") |
| print(" " + "-" * 70) |
| for r in ds_results: |
| if "error" in r: |
| print(f" {r['label']:<25} {r['device']:>6} {'FAIL':>8}") |
| else: |
| t_s = f"{r.get('time_s', 0):.0f}s" if 'time_s' in r else "—" |
| print(f" {r['label']:<25} {r['device']:>6} {t_s:>8} {r['hl_mouse_r']:>10.4f} {r['hl_human_r']:>10.4f} {r.get('hl_human_n', ''):>6}") |
|
|
| |
| valid = [r for r in ds_results if "error" not in r and "time_s" in r] |
| if len(valid) > 1: |
| fig, axes = plt.subplots(1, 2, figsize=(12, 5)) |
|
|
| genes = [r["n_genes"] for r in valid if r["label"] != "Analytical (all)"] |
| times = [r["time_s"] for r in valid if r["label"] != "Analytical (all)"] |
| rs = [abs(r["hl_human_r"]) for r in valid if r["label"] != "Analytical (all)"] |
|
|
| if genes: |
| 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(f"{ds_name}: Scalability") |
| axes[0].set_xscale("log") |
|
|
| axes[1].plot(genes, rs, "o-", color="darkorange", label="DeepPTR") |
| axes[1].axhline(abs(r_an_h), color="red", ls="--", label=f"Analytical={abs(r_an_h):.3f}") |
| axes[1].set_xlabel("Number of genes") |
| axes[1].set_ylabel("|r| with half-life (human)") |
| axes[1].set_title(f"{ds_name}: Quality vs scale") |
| axes[1].set_xscale("log") |
| axes[1].legend() |
|
|
| fig.tight_layout() |
| save_fig(fig, f"{ds_name}_fullgenome", OUT) |
|
|
| save_json(all_results, "fullgenome_gpu", OUT) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|