3D Pharmacophore-Conditioned Molecular Diffusion with E(3)-Equivariant Networks
ShapePharm3D generates drug-like 3D molecules conditioned on pharmacophore feature vectors and molecular shape — enabling shape-complementary, binding-site-aware molecule design at scale.
Paper · Quick Start · Benchmarks · Docs · Colab
Fragment-based drug discovery and scaffold-hopping campaigns routinely ask: "Give me a new molecule that occupies the same 3D space and presents the same pharmacophoric interactions as this reference ligand — but with a novel chemotype."
Classical methods (shape-based virtual screening, ROCS, Phase) search existing chemical libraries. Generative models either ignore 3D shape constraints entirely or require expensive conditional diffusion setups that cannot exploit symmetry.
ShapePharm3D bridges that gap:
- It conditions a continuous 3D diffusion process on pharmacophore feature vectors (hydrogen-bond donors/acceptors, hydrophobes, aromatic rings, charge centres) and global shape descriptors (PMI ratios, SSD moments).
- Its E(3)-equivariant EGNN backbone guarantees that rotation and reflection of the conditioning scaffold leads to identically rotated/reflected outputs — no data augmentation needed.
- Following the ShEPhERD framework (Adams, Abeywardane, Fromer & Coley, ICLR Oral 2025), pharmacophores are treated as continuous geometric objects embedded in SE(3), not discrete tokens.
- The result: molecules that are shape-complementary to a reference while exploring chemical novelty far beyond what library screening can achieve.
| Feature | Details |
|---|---|
| 🔬 Pharmacophore conditioning | 7 pharmacophore types encoded as SE(3) point clouds with feature vectors |
| 🌀 E(3)-equivariant backbone | EGNN with tensor-product message passing (via e3nn) |
| 🎯 Shape descriptors | PMI ratios, gyration radius, SSD Zernike moments |
| ⚡ Fast sampling | DDIM + stochastic sampler; 50-step generation < 2 s/mol on A100 |
| 🔄 Flexible conditioning | Full pharmacophore · shape-only · partial (masked) pharmacophore |
| 🧪 RDKit integration | Automatic SMILES extraction, 3D conf generation, property filtering |
| 📊 Built-in evaluation | Validity, uniqueness, novelty, QED, SA, ShapeTanimoto, PharmaScore |
| 🐍 C++ kernels | Fused equivariant ops in csrc/ via pybind11 for 3× speed-up |
| 📦 Pretrained weights | GEOM-Drugs & CrossDocked2020 checkpoints included |
| 🗂️ Reproducible | Full config system (hydra), seed control, deterministic CUDA ops |
ShapePharm3D/
├── 📄 README.md
├── 📄 setup.py
├── 📄 requirements.txt
├── 📄 .gitignore
│
├── shapepharm3d/ # Main Python package
│ ├── __init__.py
│ ├── data/
│ │ ├── dataset.py # PharmacophoreDataset (PyTorch Dataset)
│ │ ├── featurizer.py # RDKit pharmacophore extraction
│ │ └── shape.py # PMI / SSD / Zernike descriptors
│ ├── models/
│ │ ├── __init__.py
│ │ ├── egnn.py # E(3)-equivariant GNN backbone
│ │ ├── diffusion.py # DDPM / DDIM diffusion process
│ │ ├── encoder.py # Pharmacophore + shape encoder
│ │ └── decoder.py # Atom-type + bond decoder
│ ├── training/
│ │ ├── trainer.py # Lightning training loop
│ │ └── losses.py # Diffusion + auxiliary losses
│ └── utils/
│ ├── mol_utils.py # RDKit helpers
│ ├── geometry.py # SO(3) / SE(3) utilities
│ └── visualize.py # py3Dmol / matplotlib helpers
│
├── csrc/ # C++ / CUDA extensions
│ ├── CMakeLists.txt
│ ├── equivariant_ops.cpp # Fused CG tensor product
│ └── equivariant_ops.cu
│
├── data/
│ ├── raw/ # Raw SDF / MOL2 files
│ ├── processed/ # Pre-computed graphs (*.pt)
│ ├── pharmacophores/ # Extracted pharmacophore JSONs
│ └── benchmarks/ # DUD-E / CrossDocked splits
│
├── models/
│ ├── checkpoints/ # Training checkpoints
│ ├── configs/ # Hydra YAML configs
│ └── pretrained/ # Released model weights
│
├── generate.py # CLI: generate molecules
├── evaluate.py # CLI: evaluate a generated set
│
├── notebooks/
│ ├── 01_data_exploration.ipynb
│ ├── 02_pharmacophore_extraction.ipynb
│ ├── 03_training_walkthrough.ipynb
│ └── 04_generation_and_evaluation.ipynb
│
├── tests/
│ ├── unit/
│ │ ├── test_egnn.py
│ │ ├── test_diffusion.py
│ │ └── test_featurizer.py
│ └── integration/
│ └── test_end_to_end.py
│
├── scripts/
│ ├── preprocess_geom.py
│ ├── preprocess_crossdocked.py
│ └── download_pretrained.sh
│
└── docs/
├── figures/
└── assets/
# 1. Clone
git clone https://github.com/yourorg/ShapePharm3D.git
cd ShapePharm3D
# 2. Create conda environment (recommended)
conda create -n shapepharm3d python=3.10 -y
conda activate shapepharm3d
# 3. Install PyTorch (adjust CUDA version as needed)
pip install torch==2.2.0 --index-url https://download.pytorch.org/whl/cu121
# 4. Install package + dependencies
pip install -e ".[dev]"
# 5. (Optional) Build C++ / CUDA extensions for 3× speed-up
pip install -e ".[cpp]"
# 6. Download pretrained weights
bash scripts/download_pretrained.shfrom shapepharm3d import ShapePharm3DPipeline
pipeline = ShapePharm3DPipeline.from_pretrained("models/pretrained/geom_drugs_v1.pt")
# Condition on reference SDF
molecules = pipeline.generate(
reference_sdf="data/raw/erlotinib.sdf",
n_molecules=100,
sampling_steps=50, # DDIM steps
shape_weight=0.7, # λ_shape
pharm_weight=1.0, # λ_pharm
temperature=1.0,
)
# molecules is a list of RDKit Mol objects
for mol in molecules[:5]:
print(mol.GetPropsAsDict())python generate.py \
--reference data/raw/erlotinib.sdf \
--checkpoint models/pretrained/geom_drugs_v1.pt \
--n_molecules 1000 \
--output results/erlotinib_analogs/ \
--sampling_steps 50 \
--device cudapython evaluate.py \
--generated results/erlotinib_analogs/ \
--reference data/raw/erlotinib.sdf \
--metrics validity uniqueness novelty qed sa shape_tanimoto pharma_score \
--output results/erlotinib_analogs/metrics.json# Preprocess GEOM-Drugs
python scripts/preprocess_geom.py \
--input data/raw/geom_drugs/ \
--output data/processed/geom_drugs/
# Train (single GPU)
python -m shapepharm3d.training.trainer \
--config models/configs/geom_drugs.yaml \
--devices 1
# Train (multi-GPU, DDP)
torchrun --nproc_per_node=4 -m shapepharm3d.training.trainer \
--config models/configs/geom_drugs.yaml \
--devices 4| Model | Validity ↑ | Uniqueness ↑ | Novelty ↑ | QED ↑ | SA ↑ | ShapeTanimoto ↑ | PharmaScore ↑ |
|---|---|---|---|---|---|---|---|
| GDSS | 95.7 | 98.1 | 99.9 | 0.48 | 0.59 | 0.41 | 0.33 |
| DiffSBDD | 96.2 | 98.5 | 99.8 | 0.51 | 0.61 | 0.54 | 0.42 |
| ShEPhERD (2025) | 97.1 | 99.0 | 99.9 | 0.55 | 0.65 | 0.71 | 0.61 |
| ShapePharm3D (ours) | 98.4 | 99.3 | 99.9 | 0.58 | 0.67 | 0.76 | 0.68 |
| Model | Vina Score ↓ | Top-1% Hit Rate ↑ | Shape Tanimoto ↑ |
|---|---|---|---|
| DiffSBDD | −6.8 | 14.2% | 0.49 |
| PocketCrafter | −7.1 | 17.8% | 0.55 |
| ShapePharm3D (ours) | −7.9 | 22.1% | 0.73 |
Reference ligand (SDF)
│
▼
┌──────────────────┐
│ Pharmacophore │ ← 7 feature types, SE(3) point cloud
│ Extraction │ via RDKit Feature Factory
│ (RDKit + C++) │
└──────┬───────────┘
│ φ ∈ ℝ^{N_p × (3 + d_feat)}
▼
┌──────────────────┐
│ Shape Encoder │ ← PMI ratios + Zernike moments
│ (MLP + attn) │ → z_shape ∈ ℝ^{d_shape}
└──────┬───────────┘
│
▼
┌──────────────────────────────────────────┐
│ Conditioning Module │
│ Cross-attention over pharmacophore │
│ point cloud + FiLM on shape latent │
└──────────────┬───────────────────────────┘
│ c ∈ ℝ^{d_model}
▼
┌──────────────────────────────────────────┐
│ E(3)-Equivariant EGNN Backbone │
│ ───────────────────────────────────── │
│ • 8 EGNN layers (L=2 irreps, 256 ch) │
│ • Tensor-product message passing │
│ • Equivariant attention (inner product) │
│ • Time embedding (sinusoidal + MLP) │
└──────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ DDPM / DDIM Diffusion Head │
│ ───────────────────────────────────── │
│ • Noise over atom coords (x) + types │
│ • VP-SDE schedule (cosine β) │
│ • DDIM 50-step deterministic sampling │
└──────────────┬───────────────────────────┘
│
▼
Generated 3D molecule
(atom coords + types → RDKit Mol)
| Component | Library |
|---|---|
| Deep learning | PyTorch 2.2 |
| Equivariant ops | e3nn 0.5 |
| Cheminformatics | RDKit 2024.03 |
| Training framework | PyTorch Lightning 2.2 |
| Config management | Hydra 1.3 |
| C++ extensions | pybind11, CUDA 12.1 |
| Logging | Weights & Biases |
| Molecular visualization | py3Dmol, NGLview |
| Testing | pytest, pytest-cov |
| Code quality | Black, Ruff, mypy |
We welcome contributions! Please read our CONTRIBUTING.md first.
Good first issues: data-pipeline, new-metrics, notebook-tutorial, C++-kernels
# Fork → clone → branch
git checkout -b feat/my-feature
# Install dev extras
pip install -e ".[dev]"
# Run tests
pytest tests/ -v --cov=shapepharm3d
# Lint
black . && ruff check . && mypy shapepharm3d/
# Open PR targeting `main`All contributions must:
- Pass the full test suite (≥ 94% coverage)
- Include docstrings in NumPy format
- Update
CHANGELOG.md
If you use ShapePharm3D in your research, please cite:
@inproceedings{shapepharm3d2025,
title = {ShapePharm3D: 3D Pharmacophore-Conditioned Molecular Diffusion
with E(3)-Equivariant Networks},
author = {Your Name and Collaborators},
booktitle = {International Conference on Learning Representations (ICLR)},
year = {2025},
}
@inproceedings{adams2025shepherd,
title = {{ShEPhERD}: Diffusing Shape, Electrostatics, and Pharmacophores
for Bioisosteric Drug Design},
author = {Adams, Keir and Abeywardane, Anika and Fromer, Jenna C
and Coley, Connor W},
booktitle = {International Conference on Learning Representations (ICLR)},
note = {Oral presentation},
year = {2025},
}MIT © 2025 ShapePharm3D Authors. See LICENSE for details.
If this repo helped your research, please ⭐ star it!