Predict the minimal, selective set of drugs that kills a cancer transcriptome — by mining the Tahoe-100M single-cell drug-perturbation atlas (~95 M cells, 50 cancer lines, 379 drugs × 3 doses).
Given a query tumour transcriptome S, OncoDrugFinder finds the Tahoe cell line whose control state is most similar, reads a death score off the perturbation data, and searches for the fewest drugs that drive S to a lethal state without being a non-specific poison.
The death score is constructed (Tahoe has no viability readout) and validated against DepMap PRISM; combinations are predicted (Tahoe has no combinations) and validated against NCI-ALMANAC. Every limitation is quantified below, not hidden.
Across all 50 lines × 378 drugs × 3 doses (62,455 conditions), the death score ranks DepMap PRISM's genuinely-killed drugs to the top at AUROC ≈ 0.62 — beating a depletion-only baseline.
The most broadly-lethal agents are textbook cytotoxics — Dinaciclib (CDK, lethal in all 50 lines), Afatinib (EGFR), Sapanisertib (mTOR), Decitabine (DNMT) — a strong biological sanity check.
The original intuition: map a query cell to its nearest control line, find a drug that kills that line; if none does, hop to the post-drug state and repeat — shortest path to death. Tahoe breaks four naive assumptions; each fix is a module:
| Naive assumption | Reality in Tahoe | Fix |
|---|---|---|
| "We know drug B kills line A" | no viability readout | construct a death score = cell-count depletion + transcriptional collapse (death.py), validate vs PRISM |
| "Don't kill healthy cells" | no normal cells | selectivity across cancer lineages (selectivity.py) |
| "Re-run on the post-drug state" | single drug, single 24 h | Track A graph search + Track B latent-shift composition (graph_search.py, shift_model.py) |
| "Compare to controls" | 95 M raw-count cells | pseudobulk → PCA latent space (embedding.py) |
- Track A — graph search: the original nearest-neighbour shortest-path idea, implemented faithfully. Interpretable baseline.
- Track B — latent-shift model: each
(line, drug)is a shift vector in latent space; a combination is the sum of shifts, decoded to a death score and composed by Bliss independence. Principled; generalises to unseen combos.
Killing other cancer lineages is acceptable (they are cancers too — a multi-cancer-lethal drug is a feature). The only thing to avoid is a non-specific pan-toxin that kills every line (and so, presumably, normal tissue). So selectivity uses a soft, non-linear pan-toxicity guard on the average off-target death:
selectivity = death(target) − λ · pan(combo)²
pan(combo) = max(0, mean_off_target_death − death_threshold) / (1 − death_threshold) ∈ [0,1]
The penalty is ~0 while a drug kills only some other cancers and rises steeply only as it approaches killing them all. On-target death dominates the ranking; pan-toxic anthracyclines are correctly rejected.
The latent space recovers tissue biology (conditions cluster by lineage), so "nearest line" is meaningful:
pip install -e ".[dev]" # core deps; no internet or GPU needed
tahoe-combo demo # full pipeline on synthetic data -> "ALL CHECKS PASSED"
pytest -q # 11 tests: units + e2e + reproducibilitydemo runs the entire pipeline on a self-contained synthetic world with planted ground truth,
so every check should pass. A failure there is a code bug, not biology — your debugging anchor.
| Notebook | What it shows |
|---|---|
OncoDrugFinder_LUAD.ipynb |
★ Featured, current. Self-contained lung-adenocarcinoma case study (6 KRAS-mutant LUAD lines): death + PRISM validation, latent space, selective combinations (current scoring), NCI-ALMANAC validation. |
OncoDrugFinder_walkthrough.ipynb |
Every pipeline stage explained on the synthetic ground-truth world. |
OncoDrugFinder_real_Tahoe100M.ipynb |
Earlier 6-line real-data deep dive (13 figures). |
OncoDrugFinder_full_atlas.ipynb |
Scaling to the full atlas (death score for all 50 lines). |
The LUAD notebook uses the current pan-toxicity-guarded selectivity; the earlier real-data notebooks predate that refinement and are kept as exploratory tours.
The pipeline has two layers with very different costs:
- Death score needs only per-cell metadata → covers the entire atlas in ~60 s:
python scripts/build_atlas_death.py # -> data/atlas_death.csv (PRISM AUROC ≈ 0.62) - Combination engines need per-condition expression. The sequential pseudobulk of all 314 GB is
~24 h on a modest box; a parallel, plate-sharded builder (validated bit-identical to the trusted
loader) does it in ~100 min:
python scripts/build_full_panel_parallel.py # -> full_panel.pkl (16 GB, local disk) python scripts/run_full_analysis.py # pipeline + combo validation + latent -> outputs_final/ python scripts/make_figures.py --all --outputs outputs_final
See RUNBOOK_full_atlas.md for the RAM sizing table and gotchas, and
FINAL_RESULTS.md for the consolidated results write-up.
src/tahoe_combo/ pipeline package (loaders, embedding, death, shift_model, graph_search, selectivity, validation)
scripts/ build_atlas_death · build_full_panel_parallel · run_full_analysis · make_figures · build_*_notebook
tests/ pytest unit + end-to-end + reproducibility tests
*.ipynb executed presentation notebooks (see table above)
figures/ publication figure pack (full-atlas death + multi-lineage combinations)
figures_luad/ lung-adenocarcinoma figure pack
data/ small validation assets (atlas_death, drug_moa, combo_truth, PRISM) — large .pkl panels are NOT committed
outputs_interim/ 10-line multi-lineage combination results
outputs_luad/ lung-adenocarcinoma results
config.yaml all tunable parameters (CLI flags override)
PLAN.md scientific rationale + feasibility analysis
The raw Tahoe-100M atlas (314 GB) and the multi-GB pseudobulk panels (*.pkl) are not committed
(GitHub's 100 MB limit). They are regenerated from the raw atlas with scripts/build_*.py. The
committed data/ assets (full-atlas death score + PRISM + NCI-ALMANAC truth + drug MoA) are enough to
reproduce every validation figure, and tahoe-combo demo runs with no data at all.
- No death label in Tahoe — we construct one and validate it (top-hit AUROC ≈ 0.62). Fine-grained ranking is weaker (24 h transcriptome vs multi-day assay).
- No healthy cells — selectivity is across cancer lineages, not normal-tissue safety.
- No combinations in Tahoe — predicted by latent additivity;
leave_drug_outmeasures additivity, and NCI-ALMANAC provides external validation. ComboScore measures synergy, not absolute lethality — a stated mismatch with our lethality prediction. - Cell lines ≠ patient tumours — a prioritisation tool, not a clinical predictor.
MIT — see LICENSE. Built for the Broad Institute computational hackathon using Tahoe-100M (Vevo Therapeutics / Arc Institute). DepMap PRISM and NCI-ALMANAC used for external validation.


