Volver a la lista

Docking de moléculas pequeñas mediante un enfoque híbrido de difusión y física: Revalidación de candidatos para la Mpro del SARS-CoV-2 con DiffDock-Glide.

Pipeline de docking híbrido que combina la predicción de la pose de ligandos basada en difusión con DiffDock y la revalidación física mediante Glide y AutoDock Vina. Escenario práctico de revalidación de ligandos dirigidos a la Mpro del SARS-CoV-2, que abarca desde el filtrado de colisiones (clash), PoseBusters y scoring de ensamblado, hasta benchmarks con PDBbind/DUD-E, ROC AUC y visualización en PyMOL.

Avanzado
|
45min
|
Verificado (2026-07)
Progreso0/15 (0%)

Docking de moléculas pequeñas híbrido difusión-física — Revalidación de candidatos para Mpro de SARS-CoV-2 con DiffDock-Glide

En el episodio 07, los candidatos a ligandos para Mpro de SARS-CoV-2 seleccionados como los mejores en la predicción DTI mediante GNN solo proporcionan una puntuación de probabilidad que indica la "probabilidad de unión". Antes de solicitar experimentos reales, se requiere la pose 3D de "cómo se une" y la base física de "qué tan establemente se une". El docking basado en física, como AutoDock Vina o Glide, es un estándar con 30 años de trayectoria, pero es lento para explorar exhaustivamente un espacio conformacional amplio. Por otro lado, los modelos de difusión como DiffDock pueden generar múltiples candidatos de pose en segundos, pero a menudo presentan choques (clashes) físicos. Este episodio presenta un pipeline híbrido que combina las fortalezas de ambos enfoques, específicamente DiffDock-Glide (2025), para asegurar simultáneamente la precisión de la pose y la velocidad en un entorno práctico.

📚 Recomendación de episodios previos (Altamente recomendada)

Este episodio es una profundización técnica avanzada de IA × Biología. Antes de comenzar, se recomienda encarecidamente ver y escuchar primero los siguientes episodios de DryBench:

Si se intenta seguir este episodio sin los conocimientos previos, será difícil avanzar, ya que el código práctico comienza directamente sin reexplicar los principios de eliminación de ruido de los modelos de difusión, los grafos SE(3)-equivariantes ni el procesamiento práctico de tensores de gran escala en PyTorch.


Lo que ya aprendimos en DryBench

En DryBench ai-native #2 aprendimos los principios de los modelos generativos que aprenden distribuciones de probabilidad, y en el #12 adquirimos las bases de la manipulación de tensores de gran escala con PyTorch y la optimización de precisión mixta (mixed precision).

Los modelos de difusión han logrado un gran éxito en imagen, audio y video mediante un método de aprendizaje que restaura progresivamente los datos a partir del ruido. Sin embargo, el docking de ligandos es un problema ligeramente distinto: consiste en "restaurar" las coordenadas 3D del ligando desde el ruido hasta su posición exacta dentro del bolsillo (pocket) del objetivo. La clave reside en diseñarlo para que sea SE(3)-equivariante (preservando la simetría de rotación y traslación). Tras DiffDock (2022), varios modelos posteriores han refinado esta idea. No obstante, la limitación fundamental de los modelos de difusión (no imponen explícitamente las leyes físicas) se manifiesta en la práctica como choques (clashes) o geometrías irreales. Por ello, una capa de revalidación física es indispensable.

Definición del problema complejo

Escenario práctico: Revalidación de ligandos candidatos para Mpro de SARS-CoV-2

Aquí se revalidan mediante acoplamiento molecular (docking) los 20 mejores candidatos de la biblioteca de fármacos aprobados, puntuados con GNN en el episodio 07.

  • Entrada: Estructura 3D de Mpro (PDB 6LU7 · 7BQY, etc., múltiples conformeros apo · holo), SMILES de los 20 ligandos candidatos.
  • Salida: Top-3 poses de unión para cada candidato, energía de unión de cada pose, validez geométrica y colisiones (clash), puntuación final del conjunto (ensemble score).
  • Validación: Consistencia con inhibidores conocidos de Mpro (nirmatrelvi · ensitrelvi · N3, etc.), benchmark con el subconjunto Mpro de PDBbind.
  • Coste: Menos de 60 segundos por ligando (30 s de DL + 30 s de revalidación física).

Requisitos fundamentales del docking

Para una estructura 3D de proteína diana y una biblioteca de miles de ligandos:

  • Predicción de la pose de unión: Determinar en qué bolsillo se une cada ligando y con qué orientación.
  • Puntuación de la afinidad de unión: Cuantificación de la estabilidad físico-química de la pose.
  • Filtro de falsos positivos: Eliminación automática de poses irreales (clash, bolsillo incorrecto, geometría distorsionada).
  • Velocidad de procesamiento: De segundos a minutos por ligando.

Espectro de enfoques existentes

  • AutoDock Vina (2010, múltiples revisiones): Gratuito y de código abierto. Estándar de docking. 1–10 ligandos por minuto [1].
  • Glide (Schrödinger): Comercial. Precisión en etapas SP < XP. Estándar industrial.
  • DiffDock (MIT 2022): Primer SOTA basado en difusión. 30 segundos por ligando, precisión top-1 del 38% [2].
  • DiffDock-L (MIT 2024): Versión expandida, top-1 del 43%.
  • DiffDock-Pocket (2024): Utiliza información del bolsillo, mejora la precisión.
  • DiffDock-Glide (bioRxiv 2025): Pose por DL + minimización con Glide, top-1 superior al 55% [3].
  • RLDiff (2024, Oxford): Difusión guiada por aprendizaje por refuerzo [4].
  • Boltz-2 (episodio 11): Predicción integrada que incluye docking. Este episodio se centra en enfoques especializados en docking, complementando a Boltz-2.
  • PoseBusters (2024): Framework de evaluación cuantitativa de la validez de las poses de docking por DL [5].

Este episodio combina la predicción de poses con DiffDock (o DiffDock-Glide) + revalidación con Vina/Glide + filtrado con PoseBusters.

Indicadores objetivo de este episodio

  • Generar las top-3 poses para cada uno de los 20 candidatos de Mpro, en menos de 30 minutos de tiempo real (wall-clock).
  • El filtro físico PoseBusters elimina colisiones y poses poco realistas en un 30%+.
  • Cuantificación de la puntuación de ensamble (confianza de DiffDock + puntuación de Vina + validez geométrica).
  • Reproducción del top 3 de inhibidores conocidos (unión superior de nirmatrelvir, etc.).
  • ROC AUC superior a 0.85 en el benchmark DUD-E (distinción entre activos y decoys).

Stack de herramientas y requisitos de infraestructura

HerramientaFunciónLicencia
DiffDock (o DiffDock-L)Predicción de pose basada en difusiónMIT
DiffDock-Glide (código bioRxiv 2025)Minimización híbridaLicencia de cada autor
AutoDock VinaDocking físico · revalidaciónApache 2.0
PoseBustersValidación de validez de poseMIT
OpenBabelConversión de formatos de archivo (SMI · SDF · PDB · PDBQT)GPL
RDKitSMILES · coordenadas 3D · normalizaciónBSD-3-Clause
PyMOL (versión open source)VisualizaciónLGPL
MDAnalysis (opcional)Análisis de trayectoria · postprocesamientoGPL
PDBFixer (familia OpenMM)Preprocesamiento de proteínasLGPL
PDBbind / DUD-E / COVID MoonshotDatasets de benchmarkGratuito para uso académico

Requisitos de infraestructura:

  • GPU de centro de datos con 24-48 GB de VRAM (para DiffDock y versiones posteriores, ligandos grandes · pockets grandes).
  • También es posible con la mayoría de las GPUs de consumo de gama alta (RTX 4090 24 GB). Los complejos grandes requieren GPU de centro de datos.
  • Revalidación con Vina · Glide: se recomiendan 8 núcleos de CPU o más (procesamiento paralelo).
  • RAM de 16 GB o más.
  • Disco: pesos de DiffDock aprox. 2 GB, PDBbind aprox. 10 GB, DUD-E aprox. 30 GB, varios archivos PDB de Mpro aprox. 100 MB.

Costo estimado de reproducción para el estudiante: Costo de API 0 al usar GPU · CPU locales. El cribado de 100 ligandos tarda aproximadamente 30~60 minutos. Consulte las tarifas por hora si utiliza GPUs en la nube.

Implementación práctica del pipeline

Flujo completo:

mermaid

Paso 1. Preprocesamiento de proteínas

Antes del docking, es fundamental definir el estado de protonación, añadir átomos de hidrógeno, eliminar agua e iones y retirar los ligandos. PDBFixer es el estándar.

python
import subprocess
from pathlib import Path
def prepare_protein(input_pdb: Path, output_pdb: Path, remove_hetatoms: bool = True) -> None:
"""Preprocesa el PDB: elimina agua, iones y ligandos; añade H y conserva cargas parciales.
Opciones prácticas: pdb2pqr, PDBFixer, Schrödinger Protein Prep y UCSF ChimeraX.
"""
from pdbfixer import PDBFixer
from openmm.app import PDBFile
fixer = PDBFixer(filename=str(input_pdb))
fixer.findMissingResidues()
fixer.findMissingAtoms()
fixer.addMissingAtoms()
fixer.addMissingHydrogens(pH=7.4)
if remove_hetatoms:
fixer.removeHeterogens(keepWater=False)
with open(output_pdb, "w") as f:
PDBFile.writeFile(fixer.topology, fixer.positions, f)
def pdb_to_pdbqt(pdb_path: Path, pdbqt_path: Path) -> None:
"""Convierte al formato PDBQT para AutoDock Vina mediante OpenBabel.
-xr: receptor rígido, excluye la flexibilidad de cadenas laterales y acelera el docking.
-xh: conserva H; -xn: carga de N amida; -xr r: rígido.
"""
subprocess.run(
["obabel", str(pdb_path), "-O", str(pdbqt_path), "-xr"],
check=True, capture_output=True,
)
def identify_binding_site(pdb_path: Path, known_ligand_pdb: Path | None = None) -> tuple[float, float, float]:
"""Centro de la caja de docking: usa el centro de un ligando conocido o predice el pocket.
Escenario Mpro 6LU7: usa el centro del inhibidor cocristalizado.
"""
if known_ligand_pdb:
# Coordenadas medias de los átomos del ligando
from Bio.PDB import PDBParser
parser = PDBParser(QUIET=True)
structure = parser.get_structure("lig", str(known_ligand_pdb))
coords = [atom.get_coord() for atom in structure.get_atoms()]
import numpy as np
center = np.mean(coords, axis=0)
return (float(center[0]), float(center[1]), float(center[2]))
else:
# Usa fpocket, PocketFinder, etc.; aquí solo es un stub conceptual
raise NotImplementedError("Sin ligando conocido se requiere integrar fpocket")

Paso 2. Generación de conformeros 3D del ligando

python
from rdkit import Chem
from rdkit.Chem import AllChem
def smiles_to_sdf(smiles: str, sdf_path: Path, num_conformers: int = 3) -> Path:
"""SMILES → varios confórmeros 3D → SDF.
Varios confórmeros aportan diversidad a DiffDock, aunque algunos enfoques usan solo uno.
"""
mol = Chem.MolFromSmiles(smiles)
if mol is None:
raise ValueError(f"Invalid SMILES: {smiles}")
mol = Chem.AddHs(mol)
# Genera varios confórmeros y minimiza su energía
conf_ids = AllChem.EmbedMultipleConfs(
mol,
numConfs=num_conformers,
params=AllChem.ETKDGv3(),
)
for conf_id in conf_ids:
try:
AllChem.MMFFOptimizeMolecule(mol, confId=conf_id, maxIters=500)
except Exception:
pass
writer = Chem.SDWriter(str(sdf_path))
for conf_id in conf_ids:
writer.write(mol, confId=conf_id)
writer.close()
return sdf_path

Paso 3. Generación de poses con DiffDock

python
import subprocess
def run_diffdock(
protein_pdb: Path,
ligand_sdf: Path,
output_dir: Path,
num_poses: int = 20,
num_inference_steps: int = 40,
device: str = "cuda",
use_pocket: bool = False,
pocket_center: tuple[float, float, float] | None = None,
) -> list[Path]:
"""Ejecuta DiffDock y devuelve varios archivos SDF de poses.
Invoca la CLI inference.py del repositorio de DiffDock.
"""
output_dir.mkdir(parents=True, exist_ok=True)
cmd = [
"python", "-m", "inference",
"--protein_path", str(protein_pdb),
"--ligand", str(ligand_sdf),
"--out_dir", str(output_dir),
"--samples_per_complex", str(num_poses),
"--inference_steps", str(num_inference_steps),
"--batch_size", "10",
]
if use_pocket and pocket_center:
# Extensión DiffDock-Pocket con pista de coordenadas
cmd += ["--pocket_center", ",".join(f"{c:.2f}" for c in pocket_center)]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Fallo de DiffDock, STDERR: {result.stderr[:500]}")
raise RuntimeError("Falló la ejecución de DiffDock")
pose_files = sorted(output_dir.glob("complex_0/rank*.sdf"))
return pose_files

Paso 4. Filtro físico de PoseBusters

PoseBusters es un marco de trabajo avanzado para la evaluación cuantitativa de la validez físico-química de las poses de acoplamiento [5].

python
def validate_pose_with_posebusters(
pose_sdf: Path,
protein_pdb: Path,
) -> dict:
"""Comprueba la validez de las poses con PoseBusters.
Returns: {
"passes_all_checks": bool,
"checks": {check_name: bool},
"critical_failures": [list of failed checks],
}
"""
try:
from posebusters import PoseBusters
buster = PoseBusters(config="dock")
results = buster.bust(
mol_pred=[pose_sdf],
mol_cond=protein_pdb,
)
# results es un DataFrame de pandas
checks = results.iloc[0].to_dict()
critical_failures = [k for k, v in checks.items() if v is False and "clash" in k.lower() or "geometry" in k.lower()]
passes_all = all(v for v in checks.values() if isinstance(v, bool))
return {
"passes_all_checks": passes_all,
"checks": checks,
"critical_failures": critical_failures,
}
except ImportError:
# Si PoseBusters no está instalado, usa una comprobación simple de clashes
return {"passes_all_checks": True, "checks": {}, "critical_failures": []}
import numpy as np
from Bio.PDB import PDBParser, NeighborSearch
VDW_RADII = {
"H": 1.20, "C": 1.70, "N": 1.55, "O": 1.52, "F": 1.47,
"P": 1.80, "S": 1.80, "Cl": 1.75, "Br": 1.85, "I": 1.98,
}
def simple_clash_check(
protein_pdb: Path,
ligand_sdf: Path,
clash_threshold: float = 0.7,
max_clash_count: int = 3,
) -> bool:
"""Devuelve True si hay un clash; alternativa a PoseBusters."""
parser = PDBParser(QUIET=True)
structure = parser.get_structure("protein", str(protein_pdb))
protein_atoms = list(structure.get_atoms())
ns = NeighborSearch(protein_atoms)
mol = Chem.SDMolSupplier(str(ligand_sdf), removeHs=False)[0]
if mol is None:
return True
conf = mol.GetConformer()
clash_count = 0
for i, atom in enumerate(mol.GetAtoms()):
pos = conf.GetAtomPosition(i)
lig_vdw = VDW_RADII.get(atom.GetSymbol(), 1.7)
nearby = ns.search([pos.x, pos.y, pos.z], 5.0)
for prot_atom in nearby:
prot_vdw = VDW_RADII.get(prot_atom.element, 1.7)
distance = np.linalg.norm(
np.array([pos.x, pos.y, pos.z]) - prot_atom.get_coord()
)
if distance < (lig_vdw + prot_vdw) * clash_threshold:
clash_count += 1
if clash_count > max_clash_count:
return True
return False

Paso 5. Minimización local de Vina + puntuación

Se ejecuta la optimización local de Vina utilizando la pose de DiffDock como coordenadas iniciales → refinamiento a una pose físicamente estable + obtención de puntuación.

python
def vina_local_score(
receptor_pdbqt: Path,
ligand_pdbqt: Path,
center: tuple[float, float, float],
size: tuple[float, float, float] = (20, 20, 20),
exhaustiveness: int = 1, # Bajo porque es optimización local
output_pdbqt: Path | None = None,
) -> float:
"""Optimización local y puntuación con Vina; exhaustiveness=1 para minimizar localmente.
Devuelve la energía de unión en kcal/mol; cuanto más negativa, más fuerte.
"""
cmd = [
"vina",
"--receptor", str(receptor_pdbqt),
"--ligand", str(ligand_pdbqt),
"--center_x", str(center[0]),
"--center_y", str(center[1]),
"--center_z", str(center[2]),
"--size_x", str(size[0]),
"--size_y", str(size[1]),
"--size_z", str(size[2]),
"--exhaustiveness", str(exhaustiveness),
"--num_modes", "1",
"--local_only",
]
if output_pdbqt:
cmd += ["--out", str(output_pdbqt)]
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
# Analiza la afinidad del primer modo de la salida de Vina
for line in result.stdout.splitlines():
stripped = line.strip()
if stripped.startswith("1 ") or stripped.startswith("1\t"):
parts = stripped.split()
try:
return float(parts[1]) # kcal/mol
except (IndexError, ValueError):
pass
return 0.0

Paso 6. Puntuación de ensamble

Combinación ponderada de la confianza de DiffDock + la validez de PoseBusters + la puntuación de Vina.

python
from dataclasses import dataclass
@dataclass
class PoseResult:
ligand_id: str
pose_path: Path
diffdock_confidence: float
posebusters_passes: bool
vina_score: float
final_score: float
metadata: dict
def ensemble_score(
diffdock_conf: float,
posebusters_passes: bool,
vina_score: float,
weight_dl: float = 0.3,
weight_vina: float = 0.6,
invalid_penalty: float = 5.0,
) -> float:
"""Puntuación de ensemble: cuanto menor, más fuerte según Vina."""
# Convierte aproximadamente la puntuación DL a escala Vina: cerca de -5,0 es incierto; ≤ -8,0 es fuerte
dl_component = -diffdock_conf * 3.0
combined = weight_vina * vina_score + weight_dl * dl_component
if not posebusters_passes:
combined += invalid_penalty # Penaliza poses físicamente anómalas
return combined
def rank_ligand_poses(
ligand_id: str,
diffdock_poses: list[dict], # [{pose_path, confidence}]
receptor_pdb: Path,
receptor_pdbqt: Path,
binding_center: tuple[float, float, float],
) -> list[PoseResult]:
"""Revalida varias poses de un ligando con Vina, PoseBusters y ensemble."""
results = []
for pose in diffdock_poses:
pose_pdbqt = pose["pose_path"].with_suffix(".pdbqt")
try:
subprocess.run(
["obabel", str(pose["pose_path"]), "-O", str(pose_pdbqt)],
check=True, capture_output=True,
)
except subprocess.CalledProcessError:
continue
vina_score = vina_local_score(receptor_pdbqt, pose_pdbqt, binding_center)
pb_result = validate_pose_with_posebusters(pose["pose_path"], receptor_pdb)
final = ensemble_score(
pose["confidence"], pb_result["passes_all_checks"], vina_score,
)
results.append(PoseResult(
ligand_id=ligand_id,
pose_path=pose["pose_path"],
diffdock_confidence=pose["confidence"],
posebusters_passes=pb_result["passes_all_checks"],
vina_score=vina_score,
final_score=final,
metadata={
"posebusters_details": pb_result["checks"],
"critical_failures": pb_result["critical_failures"],
},
))
return sorted(results, key=lambda r: r.final_score)

Paso 7. Renderizado automático en PyMOL

Sigue el mismo patrón que el renderizado de la Parte 11.

python
def render_top_poses(
receptor_pdb: Path,
top_poses: list[PoseResult],
output_dir: Path,
) -> None:
output_dir.mkdir(parents=True, exist_ok=True)
for i, pose in enumerate(top_poses):
script = f"""
load {receptor_pdb}, receptor
load {pose.pose_path}, ligand
hide everything
show cartoon, receptor
show sticks, ligand
show sticks, receptor within 5 of ligand
color grey70, receptor
color yellow, ligand
zoom ligand, 5
bg_color white
ray 1200, 900
png {output_dir / f"{pose.ligand_id}_rank{i+1}.png"}, dpi=150
quit
"""
script_path = output_dir / f"render_{pose.ligand_id}_{i}.pml"
script_path.write_text(script)
subprocess.run(["pymol", "-cq", str(script_path)], check=True, capture_output=True)

Paso 8. Ejecución del escenario de Mpro con el pipeline integrado

python
import pandas as pd
def hybrid_docking_pipeline(
protein_pdb: Path,
known_ligand_pdb: Path | None,
ligand_smiles_list: list[tuple[str, str]], # [(ligand_id, smiles)]
work_dir: Path,
top_k: int = 10,
poses_per_ligand: int = 10,
device: str = "cuda",
) -> pd.DataFrame:
"""Cribado completo de docking híbrido."""
work_dir.mkdir(parents=True, exist_ok=True)
print("[1/5] Preprocesado de la proteína")
prepared_pdb = work_dir / "protein_prepared.pdb"
prepare_protein(protein_pdb, prepared_pdb)
receptor_pdbqt = work_dir / "protein_prepared.pdbqt"
pdb_to_pdbqt(prepared_pdb, receptor_pdbqt)
binding_center = identify_binding_site(prepared_pdb, known_ligand_pdb)
print(f" binding center: {binding_center}")
all_results = []
for ligand_id, smiles in ligand_smiles_list:
print(f"[2/5] {ligand_id} 3D conformer + DiffDock")
ligand_sdf = work_dir / "ligands" / f"{ligand_id}.sdf"
try:
smiles_to_sdf(smiles, ligand_sdf, num_conformers=3)
pose_files = run_diffdock(
prepared_pdb, ligand_sdf,
work_dir / "diffdock" / ligand_id,
num_poses=poses_per_ligand, device=device,
)
except Exception as e:
print(f" Fallo ({ligand_id}): {e}")
continue
# La confianza de DiffDock se obtiene del nombre rank o de metadatos separados
diffdock_poses = [
{"pose_path": p, "confidence": max(0.0, 1.0 - 0.05 * i)} # Un rank menor implica mayor confianza
for i, p in enumerate(pose_files)
]
print(f"[3/5] {ligand_id}: revalidación con PoseBusters y Vina")
ranked = rank_ligand_poses(
ligand_id, diffdock_poses, prepared_pdb, receptor_pdbqt, binding_center,
)
all_results.extend(ranked[:3]) # Solo las tres mejores poses por ligando
# 4. Ranking global
all_results.sort(key=lambda r: r.final_score)
df = pd.DataFrame([{
"ligand_id": r.ligand_id,
"diffdock_conf": r.diffdock_confidence,
"posebusters_pass": r.posebusters_passes,
"vina_score": r.vina_score,
"final_score": r.final_score,
"critical_failures": ";".join(r.metadata["critical_failures"]),
"pose_path": str(r.pose_path),
} for r in all_results])
df.to_csv(work_dir / "docking_results.csv", index=False)
print(f"[4/5] Renderizado en PyMOL del Top-{top_k}")
render_top_poses(prepared_pdb, all_results[:top_k], work_dir / "renders")
print("[5/5] Completado")
return df
# Ejemplo de ejecución para SARS-CoV-2 Mpro
# result_df = hybrid_docking_pipeline(
# protein_pdb=Path("./6LU7.pdb"),
# known_ligand_pdb=Path("./6LU7_ligand.pdb"), # co-crystallized N3 inhibitor
# ligand_smiles_list=[
# ("nirmatrelvir", "CC1(C)C2CC1C(NC(=O)C1CCCN1C(=O)C(NC(=O)OC(C)(C)C)C(C)(C)C)C(=O)NC(C#N)CC2=O"),
# ("ensitrelvir", "..."),
# # Añade los 20 mejores candidatos de la GNN de la parte 07
# ],
# work_dir=Path("./mpro_docking_output"),
# )

Rendimiento, costo y casos de fallo conocidos

Referencia de rendimiento (citas de benchmarks públicos)

ModeloBenchmarkTop-1 RMSD ≤ 2ÅTasa de aprobación PoseBustersTiempo/parFuente
AutoDock VinaPDBbind core20–30%85–90% (baseline físico)minTrott & Olson 2010 [1]
Glide SPPDBbind core35–40%90%+s–minSchrödinger
Glide XPPDBbind core40–50%92%+minSchrödinger
DiffDockPDBbind core38%60–70% (problemas físicos)30s GPUCorso et al., ICLR 2023 [2]
DiffDock-LPDBbind core43%65–75%30s GPUCorso et al. 2024
DiffDock-PocketPDBbind core45%+70%+40s GPU2024
DiffDock-GlidePDBbind core55–60%85%+ (corrección de Glide)60s GPU+CPUMiller et al., bioRxiv 2025 [3]
RLDiffSubconjunto de PDBbind~50%78%40s GPUZhang et al. 2024 [4]
Boltz-2 (cap. 11)Benchmark similar50%+90%+ (diseño con física integrada)s GPUMIT/Genentech 2025

Costo estimado de reproducción para el estudiante

  • Costo de API: 0 (completamente local).
  • Cribado de 100 ligandos: DiffDock aprox. 30 min (GPU) + revalidación con Vina aprox. 30 min (CPU paralelo 4-8 núcleos).
  • Cribado de 20 candidatos de Mpro: 15–30 min.
  • Descarga de pesos de DiffDock: aprox. 2 GB (en la primera ejecución).
  • Vina, OpenBabel y PoseBusters son gratuitos.

5 casos de fallo conocidos (recopilados de la comunidad y literatura)

  1. Choque atómico y geometría irreal en poses de DiffDock (60% de fallos en PoseBusters) Síntoma: En las mejores poses de DiffDock, los átomos del ligando se superponen con los de la proteína (interpenetración), distorsión de ángulos de enlace y anillos aromáticos no planos. Causa: El modelo de difusión no impone explícitamente las leyes físicas. El objetivo de entrenamiento es la restauración de coordenadas, sin una validación separada de validez geométrica. Evitar: (a) usar obligatoriamente el filtro PoseBusters (Paso 4 de este episodio), (b) refinamiento mediante optimización local de Vina, (c) seleccionar las poses válidas mediante un ensamblado de múltiples poses, (d) utilizar versiones posteriores con física integrada como DiffDock-Glide, (e) sustituir por Boltz-2 (aprendizaje con física integrada) (episodio 11). Fuente: Buttenschoen et al. "PoseBusters" Chem Sci 2024 [5].

  2. Selección incorrecta del pocket (docking ciego vs. dirigido) Síntoma: En el modo ciego de DiffDock, el ligando se coloca en un pocket distinto al sitio ortostérico conocido. Causa: DiffDock permite buscar el pocket automáticamente o especificarlo de forma explícita, pero en modo ciego es frecuente asignar el sitio equivocado. Evitar: (a) predefinir las coordenadas del pocket (identificación del sitio de unión en este episodio), (b) realizar una búsqueda previa de pockets candidatos mediante fpocket, etc., (c) utilizar DiffDock-Pocket, (d) si existe un ligando conocido, utilizar su posición como semilla (seed). Fuente: DiffDock GitHub Issues [6].

  3. Sesgo conocido en el benchmark DUD-E (sesgo entre activos vs. decoys) Síntoma: Se obtienen valores de AUC superiores a 0.9 en DUD-E, pero el rendimiento es mucho menor en el cribado de fármacos reales. Causa: Los decoys de DUD-E se generan mediante emparejamiento de propiedades, por lo que el evaluador (scorer) solo aprende las diferencias fisicoquímicas entre activos y decoys (careciendo de capacidad de discriminación de unión real). Evitar: (a) utilizar benchmarks no sesgados como LIT-PCBA, (b) validación mediante experimentos prospectivos, (c) evaluación mediante un ensamblado de múltiples benchmarks, (d) conjuntos de datos con mediciones reales como COVID Moonshot. Fuente: Chen et al. "Hidden bias in the DUD-E dataset." PLoS ONE 2019 [7].

  4. Ignorar la flexibilidad de la proteína objetivo (receptor rígido) Síntoma: El docking asume un receptor rígido, pero las proteínas reales son flexibles, especialmente en términos de movimiento de loops y cadenas laterales. Causa: La mayoría de los modos de receptor rígido son el estándar en Vina y DiffDock. Evitar: (a) generar múltiples conformeros mediante simulación de MD y realizar el docking de cada uno (docking de ensamblado), (b) ejecutar múltiples estructuras apo/holo en paralelo en DiffDock, (c) utilizar software comercial de IFD (Induced Fit Docking), (d) enfoques de co-folding como Boltz-2 o Chai-1 (episodio 11). Fuente: Amaro RE et al. "Ensemble Docking in Drug Discovery." Biophys J 2018 [8].

  5. Omisión de estereoquímica y tautómeros del ligando Síntoma: Aunque el SMILES especifique la estereoquímica, la predicción selecciona estereoisómeros de forma arbitraria. O bien, se realiza el docking con un tautómero que no es el predominante a un pH determinado. Causa: Pérdida de información durante la canonicalización de SMILES y la generación de conformeros 3D. Solución: (a) Preservar la quiralidad en Chem.MolFromSmiles, (b) canonicalizar el tautómero en MolStandardize, (c) realizar el docking de cada estereoisómero y tautómero por separado para luego crear un ensemble, (d) verificar la quiralidad de la pose predicha mediante validación posterior. Fuente: Documentación de RDKit MolStandardize [9]; Boltz Issues (estereoquímica) [6].

Ideas de expansión

  • Docking basado en fragmentos: Reducir bibliotecas grandes a unidades de fragmentos (< 300 Da) y realizar el docking → diseño de linkers.
  • Ensemble docking: Realizar el docking con múltiples conformeros de la proteína (simulaciones de MD o múltiples modelos de AlphaFold) → agregación.
  • Integración de cálculo de energía libre: Postprocesamiento con MM-GBSA · FEP para los principales candidatos del docking (afinidad más precisa).
  • Diseño de linkers de novo: Generar linkers con RFdiffusion (Episodio 13) y validarlos mediante docking.
  • Aprendizaje activo + auto-docking: Experimentación de ligandos con alta incertidumbre de predicción → reentrenamiento.
  • Ensemble Boltz-2 vs DiffDock-Glide: Comparar resultados de Boltz-2 (Episodio 11) y realizar un ensemble.

Próximo episodio

  • Episodio 11 structure-affinity-boltz: Integración del docking con Boltz-2 (flujo que sustituye al episodio de docking independiente).
  • Episodio 07 drug-target-gnn: Filtrado previo de candidatos de docking mediante predicción GNN.
  • Episodio 12 single-cell-perturbation: Predicción in silico de la respuesta celular de los principales ligandos del docking.
  • Episodio 14 bio-mcp-agent: Exponer el pipeline de docking como herramienta MCP.

Referencias

  1. Trott O, Olson AJ. "AutoDock Vina: Improving the speed and accuracy of docking." Journal of Computational Chemistry 2010. https://onlinelibrary.wiley.com/doi/10.1002/jcc.21334
  2. Corso G, Stärk H, Jing B, et al. "DiffDock: Diffusion Steps, Twists, and Turns for Molecular Docking." ICLR 2023. https://arxiv.org/abs/2210.01776
  3. Miller B, Corso G, et al. "DiffDock-Glide: a hybrid physics-based and data-driven approach to molecular docking." bioRxiv 2025. https://www.biorxiv.org/content/10.1101/2025.06.02.657461v1
  4. Oxford RLDiff GitHub: https://github.com/oxpig/RLDiff
  5. Buttenschoen M, Morris GM, Deane CM. "PoseBusters: AI-based docking methods fail to generate physically valid poses or generalise to novel sequences." Chemical Science 2024. https://pubs.rsc.org/en/content/articlelanding/2024/sc/d3sc04185a
  6. DiffDock GitHub Issues: https://github.com/gcorso/DiffDock/issues
  7. Chen L, Cruz A, Ramsey S, et al. "Hidden bias in the DUD-E dataset leads to misleading performance of deep learning in structure-based virtual screening." PLoS ONE 2019.
  8. Amaro RE et al. "Ensemble Docking in Drug Discovery." Biophysical Journal 2018.
  9. RDKit MolStandardize documentation: https://www.rdkit.org/docs/source/rdkit.Chem.MolStandardize.html
  10. Vina GitHub: https://github.com/ccsb-scripps/AutoDock-Vina
  11. OpenBabel: http://openbabel.org/
  12. RDKit: https://www.rdkit.org/
  13. PyMOL (código abierto): https://github.com/schrodinger/pymol-open-source
  14. PDBbind: http://www.pdbbind.org.cn/
  15. DUD-E: http://dude.docking.org/
  16. LIT-PCBA (benchmark sin sesgo): Tran-Nguyen VK et al. J Chem Inf Model 2020.
  17. PDBFixer (familia OpenMM): https://github.com/openmm/pdbfixer
  18. PoseBusters GitHub: https://github.com/maabuu/posebusters
  19. Consorcio COVID Moonshot: https://postera.ai/moonshot/
  20. Estructuras de Mpro de SARS-CoV-2 (PDB 6LU7, etc.): https://www.rcsb.org/

💬 Preguntas y comentarios

0 comentarios

Puedes publicar sin iniciar sesión. Los comentarios de invitados no pueden editarse ni eliminarse después.

0/2000

Cargando...