Molecule Generation¶
This page describes basic usage of the mlconfgen Python library to generate molecules with the PyTorch backend. The ONNX backend exposes the same interface — see ONNX Inference & Export.
Interactive examples are available in the repository: python_api_demo.ipynb.
Overview¶
Generation is a three-stage pipeline wrapped by a single call:
- EDM sampling: the Equivariant Diffusion Model samples heavy-atom coordinates and element types conditioned on the reference shape.
- Bond prediction: the AdjMatSeer GCN predicts the adjacency matrix (bond orders) from the atom cloud.
- Standardisation: largest-fragment selection, valence check, kekulisation, RDKit sanitisation and optional constrained MMFF94 geometry optimisation. Molecules that fail are dropped.
The result is a list of valid RDKit Mol objects. The number of returned molecules is therefore lower than n_samples.
1. Generation from a Reference Conformer¶
from rdkit import Chem
from mlconfgen import MLConformerGenerator, evaluate_samples
model = MLConformerGenerator(
edm_weights="edm_moi_chembl_15_39.pt",
adj_mat_seer_weights="adj_mat_seer_chembl_15_39.pt",
diffusion_steps=100,
)
reference = Chem.MolFromMolFile("./assets/demo_files/ceyyag.mol")
samples = model.generate_conformers(
reference_conformer=reference,
n_samples=20,
variance=2,
)
print(f"{len(samples)} valid molecules")
The reference must carry a 3D conformer. Hydrogens are stripped automatically; the reference heavy-atom count sets the target size of the generated molecules.
Note: The default weights are trained on molecules with 15–39 heavy atoms. References outside this range still work but generation quality degrades.
2. Generation from an Arbitrary Context¶
Instead of a molecule, pass the three principal moments of inertia directly along with the target number of heavy atoms:
import torch
context = torch.tensor([89.87, 210.78, 217.78]) # principal MOI components
samples = model.generate_conformers(
reference_context=context,
n_atoms=20,
n_samples=20,
)
This is how shape-only references (e.g. a pocket volume) are supplied. The context can be computed for any point cloud with align_mol_to_principal_frame or get_moment_of_inertia_tensor from mlconfgen.utils.
3. Random Generation¶
When no reference is at hand — smoke tests, unconditional sampling, seeding a library — random() draws a context from a bundled set of ChEMBL-derived seeds (random_molecule_seeds.json, ~100 contexts per heavy-atom count from 15 to 39, selected to reproduce the MOI distribution of the training set) and generates molecules for it:
mols = model.random(size=10, seed=42)
| Parameter | Type | Default | Description |
|---|---|---|---|
size |
int | 1 |
Number of molecules to generate. |
seed |
int / None | None |
Seed for the context draw; the same seed picks the same context. |
optimize_geometry |
bool | True |
MMFF94 optimisation during standardisation. |
The picked seed fixes both the shape (context) and the heavy-atom count (n_atoms); generation runs with variance=0, so every molecule in the batch has the same size. The method is available on both backends with the same signature:
from mlconfgen import MLConformerGenerator, MLConformerGeneratorONNX
MLConformerGenerator().random(size=5)
MLConformerGeneratorONNX().random(size=5, seed=7)
seed only controls which context is picked. To make the diffusion itself reproducible, seed the backend RNG as well (torch.manual_seed for PyTorch, np.random.seed for ONNX). The underlying helper is exposed as mlconfgen.utils.random_context(size, seed) and returns {"context": [I1, I2, I3], "n_atoms": n} for use with generate_conformers directly.
4. Evaluating Samples¶
evaluate_samples aligns every sample to the reference and scores shape and chemical similarity:
aligned_reference, results = evaluate_samples(reference, samples)
for item in results:
print(item["shape_tanimoto"], item["chemical_tanimoto"])
aligned_reference: mol block of the reference in its principal inertial frameresults: one dict per sample withmol_block— the sample aligned to the reference frameshape_tanimoto— Shape Tanimoto similarity via Gaussian volume overlap (hydrogens ignored)chemical_tanimoto— Tanimoto similarity of 2-hop 2048-bit Morgan fingerprints
Pass sanitize_ref=False when the reference cannot be sanitised (e.g. a pocket pseudo-molecule); chemical similarity is then reported as 0.
How alignment and the shape score are computed is described in Shape Similarity & Alignment.
5. Lower-Level Access¶
The pipeline stages are exposed individually for custom workflows:
# Prepared context, target size and (optionally) prepared fixed fragment
ref_context, ref_n_atoms, _ = model.prepare_inputs(reference_conformer=reference)
# Stage 1: atom clouds without bonds
edm_mols = model.edm_samples(
reference_context=ref_context,
n_samples=20,
min_n_nodes=ref_n_atoms - 2,
max_n_nodes=ref_n_atoms + 2,
)
# Stage 2: bonds from AdjMatSeer
raw_mols = model.predict_bonds(edm_mols)
# Stage 3: standardise yourself
from mlconfgen.utils import standardize_mol
valid = [m for m in (standardize_mol(x, optimize_geometry=True) for x in raw_mols) if m]
edm_samples(..., raw_output=True) returns the raw (x, h, node_mask, edge_mask) tensors instead of molecules.
Stage 2 can be swapped for a deterministic, rule-based bond perception — see Bond Prediction.
6. Practical Tips¶
- Generation is batched; request
n_samplesin the tens to hundreds rather than looping over single samples. diffusion_steps=100is the quality/speed sweet spot. Values of 20–50 are usable for screening; avoid values below 20.- Expect roughly 50% of samples to survive standardisation with the core AdjMatSeer and ~70% with the distilled one.
- Call
model.generate_conformersundertorch.inference_mode()if you wrap it in your own loop — the method already does so internally.