Pipeline de découverte de médicaments
Cribler les candidats médicamenteux de bout en bout en utilisant trois NIMs BioNeMo en séquence :
Étape 1: GenMol → Étape 2: DiffDock → Étape 3: Boltz2
(Générer mols) (Docking sur cible) (Prédire affinité)
Aperçu
Ce pipeline est utilisé pour :
- Découverte de hits de novo : générer des molécules de type médicament et les cribler contre une cible
- Optimisation de leads : partir d'un scaffold connu et générer des analogs améliorés, puis docking et notation
- Virtual screening : docking d'une librairie de candidats et filtrage par confiance de docking + affinité
Avant de commencer
Confirmez avec l'utilisateur :
- Protéine cible : fichier PDB ou séquence de la cible de liaison
- Point de départ : de novo (pas de scaffold) ou décoration de scaffold (noyau connu) ?
- Notation : drug-likeness (QED) ou lipophilicité (LogP) ?
- Mode API : hébergé ou Docker local ?
Pour Docker local, ne supposez pas que tous les NIMs s'exécutent sur localhost:8000 en même temps. Exécutez soit un conteneur à la fois et transmettez les fichiers/résultats entre les étapes, soit lancez chaque NIM sur un port hôte distinct et définissez les URLs par étape.
Étape 1 : Générer des molécules avec GenMol
GenMol nécessite une entrée en notation SAFE (pas du SMILES brut). Utilisez le package safe-mol.
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"}
# Génération de novo (sans scaffold) :
safe_input = "[*{20-30}]"
# Décoration de scaffold (scaffold connu) :
# scaffold_smiles = "c1ccccc1"
# safe_input = sf.encode(scaffold_smiles) + ".[*{5-10}]"
payload = {
"smiles": safe_input, # le champ s'appelle 'smiles' mais prend la notation SAFE
"num_molecules": 30, # demandez plus pour compenser le filtrage post-génération
"scoring": "QED", # QED ou LogP
"unique": True,
"temperature": "1.0", # NOTE : doit être une chaîne, pas un flottant
"noise": "1.0", # NOTE : doit être une chaîne, pas un flottant
}
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}")
Étape 2 : Docker des molécules avec DiffDock
Préparez la protéine et effectuez le docking de chaque candidat :
# Charger la protéine (enregistrements ATOM uniquement)
receptor_pdb_raw = Path("target.pdb").read_text()
receptor_pdb = "\n".join(line for line in receptor_pdb_raw.splitlines()
if line.startswith("ATOM"))
if HOSTED:
diffdock_url = "https://health.api.nvidia.com/v1/biology/mit/diffdock"
else:
diffdock_url = "http://localhost:8000/molecular-docking/diffdock/generate"
docking_results = []
for i, mol in enumerate(top_20):
payload = {
"protein": receptor_pdb,
"ligand": mol["smiles"],
"ligand_file_type": "txt", # "txt" pour entrée SMILES
"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] # pose rang 1
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}")
# Classez par confiance de docking
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}")
Étape 3 : Prédire l'affinité de liaison avec Boltz2
Pour les meilleurs candidats de docking, prédisez l'affinité de liaison basée sur la structure :
if HOSTED:
boltz_url = "https://health.api.nvidia.com/v1/biology/mit/boltz2/predict"
else:
boltz_url = "http://localhost:8000/biology/mit/boltz2/predict"
# Utilisez la séquence de protéine cible (pas PDB)
target_sequence = "<YOUR_TARGET_PROTEIN_SEQUENCE>"
affinity_results = []
for d in docking_results[:5]: # notez les 5 meilleurs résultats de docking
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}")
# Classement final par pIC50
affinity_results.sort(key=lambda x: x["pic50"], reverse=True)
Interprétation des résultats
- Score QED GenMol : 0–1 ; >0,5 est de type médicament
- Confiance DiffDock : supérieur = prédiction de pose de liaison plus fiable
- Boltz2 pIC50 : -log10(IC50) prédit ; >6 = submicromolaire, >8 = très puissant
- P(bind) : probabilité de liaison binaire ; >0,7 = liant probable
Référence rapide — dépendances de skill
| Étape | Skill | Endpoint clé |
|---|---|---|
| Génération de molécules | genmol-nim |
/biology/nvidia/genmol/generate |
| Docking | diffdock-nim |
/molecular-docking/diffdock/generate |
| Prédiction d'affinité | boltz2-nim |
/biology/mit/boltz2/predict |