drug-discovery-pipeline

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Drug Discovery Pipeline

药物发现流程

Screen drug candidates end-to-end using three BioNeMo NIMs in sequence:
Step 1: GenMol    →  Step 2: DiffDock  →  Step 3: Boltz2
(Generate mols)      (Dock to target)      (Predict affinity)

依次使用三个BioNeMo NIMs端到端筛选候选药物:
Step 1: GenMol    →  Step 2: DiffDock  →  Step 3: Boltz2
(Generate mols)      (Dock to target)      (Predict affinity)

Overview

概述

This pipeline is used for:
  • De novo hit discovery: generate drug-like molecules and screen them against a target
  • Lead optimization: start from a known scaffold and generate improved analogs, then dock and score
  • Virtual screening: dock a library of candidates and filter by docking confidence + affinity

本流程适用于:
  • 全新命中发现:生成类药物分子并针对靶点进行筛选
  • 先导化合物优化:从已知骨架出发生成改良类似物,然后进行对接和评分
  • 虚拟筛选:对接候选化合物库,并根据对接置信度和亲和力进行筛选

Before you start

开始之前

Confirm with the user:
  1. Target protein: PDB file or sequence of the binding target
  2. Starting point: de novo (no scaffold) or scaffold decoration (known core)?
  3. Scoring: drug-likeness (QED) or lipophilicity (LogP)?
  4. API mode: hosted or local Docker?
For local Docker, do not assume all NIMs are running on
localhost:8000
at the same time. Either run one container at a time and hand files/results between steps, or start each NIM on a distinct host port and set the per-step URLs.

请与用户确认以下信息:
  1. 靶点蛋白质:结合靶点的PDB文件或序列
  2. 起始方式:全新生成(无骨架)还是骨架修饰(已知核心)?
  3. 评分标准:类药性(QED)还是亲脂性(LogP)?
  4. API模式:托管式还是本地Docker?
对于本地Docker模式,请勿假设所有NIMs同时运行在
localhost:8000
。可以每次运行一个容器并在步骤间传递文件/结果,或者在不同的主机端口启动每个NIM并设置各步骤的URL。

Step 1: Generate molecules with GenMol

步骤1:使用GenMol生成分子

GenMol requires SAFE notation input (not raw SMILES). Use the
safe-mol
package.
python
import requests, json, os
import safe as sf                          # pip install safe-mol
from pathlib import Path

NGC_API_KEY = os.getenv("NGC_API_KEY")
HOSTED = True

if HOSTED:
    genmol_url = "https://health.api.nvidia.com/v1/biology/nvidia/genmol/generate"
    headers = {"Content-Type": "application/json",
               "Authorization": f"Bearer {NGC_API_KEY}"}
else:
    genmol_url = "http://localhost:8000/generate"
    headers = {"Content-Type": "application/json"}
GenMol需要SAFE符号输入(而非原始SMILES)。请使用
safe-mol
包。
python
import requests, json, os
import safe as sf                          # pip install safe-mol
from pathlib import Path

NGC_API_KEY = os.getenv("NGC_API_KEY")
HOSTED = True

if HOSTED:
    genmol_url = "https://health.api.nvidia.com/v1/biology/nvidia/genmol/generate"
    headers = {"Content-Type": "application/json",
               "Authorization": f"Bearer {NGC_API_KEY}"}
else:
    genmol_url = "http://localhost:8000/generate"
    headers = {"Content-Type": "application/json"}

De novo generation (no scaffold):

De novo generation (no scaffold):

safe_input = "[*{20-30}]"
safe_input = "[*{20-30}]"

Scaffold decoration (known core):

Scaffold decoration (known core):

scaffold_smiles = "c1ccccc1"

scaffold_smiles = "c1ccccc1"

safe_input = sf.encode(scaffold_smiles) + ".[*{5-10}]"

safe_input = sf.encode(scaffold_smiles) + ".[*{5-10}]"

payload = { "smiles": safe_input, # field is named 'smiles' but takes SAFE notation "num_molecules": 30, # request more to compensate for post-generation filtering "scoring": "QED", # QED or LogP "unique": True, "temperature": "1.0", # NOTE: must be string, not float "noise": "1.0", # NOTE: must be string, not float }
r = requests.post(genmol_url, headers=headers, json=payload) r.raise_for_status() molecules = r.json()["molecules"] molecules_sorted = sorted(molecules, key=lambda x: x["score"], reverse=True) top_20 = molecules_sorted[:20]
print(f"Generated {len(molecules)} valid molecules (requested 30)") print("Top 5 by QED score:") for m in top_20[:5]: print(f" {m['smiles'][:50]} score={m['score']:.4f}")

---
payload = { "smiles": safe_input, # field is named 'smiles' but takes SAFE notation "num_molecules": 30, # request more to compensate for post-generation filtering "scoring": "QED", # QED or LogP "unique": True, "temperature": "1.0", # NOTE: must be string, not float "noise": "1.0", # NOTE: must be string, not float }
r = requests.post(genmol_url, headers=headers, json=payload) r.raise_for_status() molecules = r.json()["molecules"] molecules_sorted = sorted(molecules, key=lambda x: x["score"], reverse=True) top_20 = molecules_sorted[:20]
print(f"Generated {len(molecules)} valid molecules (requested 30)") print("Top 5 by QED score:") for m in top_20[:5]: print(f" {m['smiles'][:50]} score={m['score']:.4f}")

---

Step 2: Dock molecules with DiffDock

步骤2:使用DiffDock进行分子对接

Prepare the protein and dock each candidate:
python
undefined
准备蛋白质并对接每个候选分子:
python
undefined

Load protein (ATOM records only)

Load protein (ATOM records only)

receptor_pdb_raw = Path("target.pdb").read_text() receptor_pdb = "\n".join(line for line in receptor_pdb_raw.splitlines() if line.startswith("ATOM"))
docking_results = []
for i, mol in enumerate(top_20): payload = { "protein": receptor_pdb, "ligand": mol["smiles"], "ligand_file_type": "txt", # "txt" for SMILES input "num_poses": 5, "time_divisions": 20, "steps": 18, "save_trajectory": False, }
r = requests.post(diffdock_url, headers=headers, json=payload)
r.raise_for_status()
result = r.json()

best_conf = result["position_confidence"][0]  # rank 1 pose
best_pose = result["ligand_positions"][0]

docking_results.append({
    "smiles": mol["smiles"],
    "qed_score": mol["score"],
    "docking_confidence": best_conf,
    "best_pose_sdf": best_pose,
})
print(f"  Mol {i+1:2d}: QED={mol['score']:.3f}  docking_conf={best_conf:.4f}")
receptor_pdb_raw = Path("target.pdb").read_text() receptor_pdb = "\n".join(line for line in receptor_pdb_raw.splitlines() if line.startswith("ATOM"))
docking_results = []
for i, mol in enumerate(top_20): payload = { "protein": receptor_pdb, "ligand": mol["smiles"], "ligand_file_type": "txt", # "txt" for SMILES input "num_poses": 5, "time_divisions": 20, "steps": 18, "save_trajectory": False, }
r = requests.post(diffdock_url, headers=headers, json=payload)
r.raise_for_status()
result = r.json()

best_conf = result["position_confidence"][0]  # rank 1 pose
best_pose = result["ligand_positions"][0]

docking_results.append({
    "smiles": mol["smiles"],
    "qed_score": mol["score"],
    "docking_confidence": best_conf,
    "best_pose_sdf": best_pose,
})
print(f"  Mol {i+1:2d}: QED={mol['score']:.3f}  docking_conf={best_conf:.4f}")

Rank by docking confidence

Rank by docking confidence

docking_results.sort(key=lambda x: x["docking_confidence"], reverse=True) print(f"\nTop 3 by docking confidence:") for d in docking_results[:3]: print(f" {d['smiles'][:50]} conf={d['docking_confidence']:.4f}")

---
docking_results.sort(key=lambda x: x["docking_confidence"], reverse=True) print(f"\nTop 3 by docking confidence:") for d in docking_results[:3]: print(f" {d['smiles'][:50]} conf={d['docking_confidence']:.4f}")

---

Step 3: Predict binding affinity with Boltz2

步骤3:使用Boltz2预测结合亲和力

For the top docking candidates, predict structure-based binding affinity:
python
if HOSTED:
    boltz_url = "https://health.api.nvidia.com/v1/biology/mit/boltz2/predict"
else:
    boltz_url = "http://localhost:8000/biology/mit/boltz2/predict"
针对排名靠前的对接候选分子,基于结构预测结合亲和力:
python
if HOSTED:
    boltz_url = "https://health.api.nvidia.com/v1/biology/mit/boltz2/predict"
else:
    boltz_url = "http://localhost:8000/biology/mit/boltz2/predict"

Use the target protein sequence (not PDB)

Use the target protein sequence (not PDB)

target_sequence = "<YOUR_TARGET_PROTEIN_SEQUENCE>"
affinity_results = [] for d in docking_results[:5]: # score top 5 docking hits payload = { "polymers": [ {"id": "A", "molecule_type": "protein", "sequence": target_sequence} ], "ligands": [ {"id": "L1", "smiles": d["smiles"], "predict_affinity": True} ], "recycling_steps": 3, "sampling_steps": 50, "diffusion_samples": 1, "output_format": "mmcif", }
r = requests.post(boltz_url, headers=headers, json=payload)
r.raise_for_status()
result = r.json()

aff = result["affinities"]["L1"]
pic50 = aff["affinity_pic50"][0]
prob_binding = aff["affinity_probability_binary"][0]

affinity_results.append({
    **d,
    "pic50": pic50,
    "probability_binding": prob_binding,
})
print(f"  {d['smiles'][:40]}  pIC50={pic50:.2f}  P(bind)={prob_binding:.3f}")
target_sequence = "<YOUR_TARGET_PROTEIN_SEQUENCE>"
affinity_results = [] for d in docking_results[:5]: # score top 5 docking hits payload = { "polymers": [ {"id": "A", "molecule_type": "protein", "sequence": target_sequence} ], "ligands": [ {"id": "L1", "smiles": d["smiles"], "predict_affinity": True} ], "recycling_steps": 3, "sampling_steps": 50, "diffusion_samples": 1, "output_format": "mmcif", }
r = requests.post(boltz_url, headers=headers, json=payload)
r.raise_for_status()
result = r.json()

aff = result["affinities"]["L1"]
pic50 = aff["affinity_pic50"][0]
prob_binding = aff["affinity_probability_binary"][0]

affinity_results.append({
    **d,
    "pic50": pic50,
    "probability_binding": prob_binding,
})
print(f"  {d['smiles'][:40]}  pIC50={pic50:.2f}  P(bind)={prob_binding:.3f}")

Final ranking by pIC50

Final ranking by pIC50

affinity_results.sort(key=lambda x: x["pic50"], reverse=True)

---
affinity_results.sort(key=lambda x: x["pic50"], reverse=True)

---

Interpreting results

结果解读

  • GenMol QED score: 0–1; >0.5 is drug-like
  • DiffDock confidence: higher = more reliable binding pose prediction
  • Boltz2 pIC50: predicted -log10(IC50); >6 = sub-micromolar, >8 = very potent
  • P(bind): probability of binary binding; >0.7 = likely binder

  • GenMol QED评分:0–1;大于0.5表示具有类药性
  • DiffDock置信度:数值越高,结合构象预测越可靠
  • Boltz2 pIC50:预测的-log10(IC50);大于6表示亚微摩尔级活性,大于8表示活性极强
  • P(bind):二元结合概率;大于0.7表示很可能是结合剂

Quick reference — skill dependencies

快速参考 — 技能依赖

StepSkillKey endpoint
Molecule generation
genmol-nim
/biology/nvidia/genmol/generate
Docking
diffdock-nim
/molecular-docking/diffdock/generate
Affinity prediction
boltz2-nim
/biology/mit/boltz2/predict
步骤技能关键端点
分子生成
genmol-nim
/biology/nvidia/genmol/generate
分子对接
diffdock-nim
/molecular-docking/diffdock/generate
亲和力预测
boltz2-nim
/biology/mit/boltz2/predict