Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

COMET

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.


Headline result — the constructed death score works at whole-atlas scale

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.

Death score vs PRISM

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 idea (and what Tahoe forces us to fix)

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)

Two combination engines (run both, compare)

  • 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.

Selectivity = kill the target without non-specific toxicity

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.

Selectivity

The latent space recovers tissue biology (conditions cluster by lineage), so "nearest line" is meaningful:

Latent space


Quickstart (runs fully offline on synthetic ground truth)

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 + reproducibility

demo 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.


Notebooks

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.


Scaling to the whole atlas

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.


Repository layout

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

Data note

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.


Honest limitations

  1. 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).
  2. No healthy cells — selectivity is across cancer lineages, not normal-tissue safety.
  3. No combinations in Tahoe — predicted by latent additivity; leave_drug_out measures additivity, and NCI-ALMANAC provides external validation. ComboScore measures synergy, not absolute lethality — a stated mismatch with our lethality prediction.
  4. Cell lines ≠ patient tumours — a prioritisation tool, not a clinical predictor.

License

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.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages