Skill Tracer
Skills › Scientific Agent Skills › torchdrug › molecular_generation.md

molecular_generation.md

252 lines · skills/torchdrug/references/molecular_generation.md at 5f2bc03

Open on GitHub
1# Molecular Generation
2
3The official
4[TorchDrug 0.2.1 generation tutorial](https://torchdrug.ai/docs/tutorials/generation.html)
5implements GCPN and GraphAF on ZINC250k. It pretrains with negative
6log-likelihood (NLL), then optionally fine-tunes with proximal policy optimization
7(PPO) for QED or penalized logP.
8Full ZINC250k training and generation are illustrative. Source review and tiny
9CPU checks do not establish validity rates, optimization quality, or convergence.

Molecular Generation

The official TorchDrug 0.2.1 generation tutorial implements GCPN and GraphAF on ZINC250k. It pretrains with negative log-likelihood (NLL), then optionally fine-tunes with proximal policy optimization (PPO) for QED or penalized logP. Full ZINC250k training and generation are illustrative. Source review and tiny CPU checks do not establish validity rates, optimization quality, or convergence.

Raw lines 1 to 10
11## Shared dataset
13```python
14from torchdrug import datasets
16dataset = datasets.ZINC250k(
17 "~/molecule-datasets/",
18 kekulize=True,
19 atom_feature="symbol",
21```
23The tutorial assumes:
25- maximum graph size: 38 atoms
26- 9 atom types
27- 3 bond types
28- `max_edge_unroll=12`
30If using another dataset, recompute these assumptions instead of copying the
31ZINC250k values.

Shared dataset

from torchdrug import datasets

dataset = datasets.ZINC250k(
    "~/molecule-datasets/",
    kekulize=True,
    atom_feature="symbol",
)

The tutorial assumes:

  • maximum graph size: 38 atoms
  • 9 atom types
  • 3 bond types
  • max_edge_unroll=12

If using another dataset, recompute these assumptions instead of copying the ZINC250k values.

Raw lines 11 to 32
35### Pretraining task
37```python
38import torch
39from torchdrug import core, models, tasks
41model = models.RGCN(
42 input_dim=dataset.node_feature_dim,
43 num_relation=dataset.num_bond_type,
44 hidden_dims=[256, 256, 256, 256],
45 batch_norm=False,
47task = tasks.GCPNGeneration(
48 model,
49 dataset.atom_types,
50 max_edge_unroll=12,
51 max_node=38,
52 criterion="nll",
55optimizer = torch.optim.Adam(task.parameters(), lr=1e-3)
56solver = core.Engine(
57 task,
58 dataset,
59 None,
60 None,
61 optimizer,
62 batch_size=128,
63 log_interval=10,
65solver.train(num_epoch=1)
66solver.save("gcpn-zinc250k.pth")
67```
69Use `gpus=(0,)` or `gpus=[0]` only on supported CUDA hardware.

Pretraining task

import torch
from torchdrug import core, models, tasks

model = models.RGCN(
    input_dim=dataset.node_feature_dim,
    num_relation=dataset.num_bond_type,
    hidden_dims=[256, 256, 256, 256],
    batch_norm=False,
)
task = tasks.GCPNGeneration(
    model,
    dataset.atom_types,
    max_edge_unroll=12,
    max_node=38,
    criterion="nll",
)

optimizer = torch.optim.Adam(task.parameters(), lr=1e-3)
solver = core.Engine(
    task,
    dataset,
    None,
    None,
    optimizer,
    batch_size=128,
    log_interval=10,
)
solver.train(num_epoch=1)
solver.save("gcpn-zinc250k.pth")

Use gpus=(0,) or gpus=[0] only on supported CUDA hardware.

Raw lines 35 to 70
71### Generate samples
73```python
74solver.load("gcpn-zinc250k.pth")
75task.eval()
76results = task.generate(num_sample=32, max_resample=5)
77print(results.to_smiles())
78```
80`results` is a packed molecule object. Validate all returned structures before
81downstream use. Set `task.eval()` before generation; both generation methods
82disable gradients internally but do not automatically disable training-mode
83batch normalization. Record requested/returned counts and bounded resampling.

Generate samples

solver.load("gcpn-zinc250k.pth")
task.eval()
results = task.generate(num_sample=32, max_resample=5)
print(results.to_smiles())

results is a packed molecule object. Validate all returned structures before downstream use. Set task.eval() before generation; both generation methods disable gradients internally but do not automatically disable training-mode batch normalization. Record requested/returned counts and bounded resampling.

Raw lines 71 to 84
85### Goal-directed fine-tuning
87The documented optimization tasks are `"qed"` and `"plogp"`. The task does not
88accept an arbitrary `reward_function=` callback in 0.2.1.
90```python
91task = tasks.GCPNGeneration(
92 model,
93 dataset.atom_types,
94 max_edge_unroll=12,
95 max_node=38,
96 task="plogp",
97 criterion="ppo",
98 reward_temperature=1,
99 agent_update_interval=3,
100 gamma=0.9,
103optimizer = torch.optim.Adam(task.parameters(), lr=1e-5)
104solver = core.Engine(
105 task,
106 dataset,
107 None,
108 None,
109 optimizer,
110 batch_size=16,
111 log_interval=10,
113solver.load("gcpn-zinc250k.pth", load_optimizer=False)
114solver.train(num_epoch=10)
115```
117For mixed supervised/RL training, the tutorial also uses:
119```python
120criterion = ("ppo", "nll")
121```
123or a weighted criterion mapping where supported by the task.

Goal-directed fine-tuning

The documented optimization tasks are "qed" and "plogp". The task does not accept an arbitrary reward_function= callback in 0.2.1.

task = tasks.GCPNGeneration(
    model,
    dataset.atom_types,
    max_edge_unroll=12,
    max_node=38,
    task="plogp",
    criterion="ppo",
    reward_temperature=1,
    agent_update_interval=3,
    gamma=0.9,
)

optimizer = torch.optim.Adam(task.parameters(), lr=1e-5)
solver = core.Engine(
    task,
    dataset,
    None,
    None,
    optimizer,
    batch_size=16,
    log_interval=10,
)
solver.load("gcpn-zinc250k.pth", load_optimizer=False)
solver.train(num_epoch=10)

For mixed supervised/RL training, the tutorial also uses:

criterion = ("ppo", "nll")

or a weighted criterion mapping where supported by the task.

Raw lines 85 to 124
125## GraphAF
127GraphAF has three distinct layers:
1291. an `RGCN` representation model,
1302. node and edge flow models exposed as `models.GraphAF`,
1313. `tasks.AutoregressiveGeneration` as the training objective.
133The representation model uses discrete atom-type input:
135```python
136model = models.RGCN(
137 input_dim=dataset.num_atom_type,
138 num_relation=dataset.num_bond_type,
139 hidden_dims=[256, 256, 256],
140 batch_norm=True,
142```
144Create the node and edge priors exactly as shown in the upstream tutorial, then
145construct one flow for nodes and one for edges:
147```python
148from torchdrug.layers import distribution
150num_atom_type = dataset.num_atom_type
151num_bond_type = dataset.num_bond_type + 1 # one extra class for no edge
153node_prior = distribution.IndependentGaussian(
154 torch.zeros(num_atom_type),
155 torch.ones(num_atom_type),
157edge_prior = distribution.IndependentGaussian(
158 torch.zeros(num_bond_type),
159 torch.ones(num_bond_type),
161node_flow = models.GraphAF(
162 model,
163 node_prior,
164 num_layer=12,
166edge_flow = models.GraphAF(
167 model,
168 edge_prior,
169 use_edge=True,
170 num_layer=12,
173task = tasks.AutoregressiveGeneration(
174 node_flow,
175 edge_flow,
176 max_node=38,
177 max_edge_unroll=12,
178 criterion="nll",
180```
182Do not omit the documented prior construction. The node and edge prior shapes
183must match the dataset's atom and bond vocabularies.
185Train and generate through the task:
187```python
188optimizer = torch.optim.Adam(task.parameters(), lr=1e-3)
189solver = core.Engine(
190 task,
191 dataset,
192 None,
193 None,
194 optimizer,
195 batch_size=128,
196 log_interval=10,
198solver.train(num_epoch=10)
199solver.save("graphaf-zinc250k.pth")
201solver.load("graphaf-zinc250k.pth")
202task.eval()
203results = task.generate(num_sample=32)
204print(results.to_smiles())
205```
207For PPO fine-tuning, rebuild `AutoregressiveGeneration` with `task="qed"` or
208`task="plogp"`, a PPO criterion, and the tutorial's reward/baseline settings;
209then load the pretrained checkpoint with `load_optimizer=False`.

GraphAF

GraphAF has three distinct layers:

  1. an RGCN representation model,
  2. node and edge flow models exposed as models.GraphAF,
  3. tasks.AutoregressiveGeneration as the training objective.

The representation model uses discrete atom-type input:

model = models.RGCN(
    input_dim=dataset.num_atom_type,
    num_relation=dataset.num_bond_type,
    hidden_dims=[256, 256, 256],
    batch_norm=True,
)

Create the node and edge priors exactly as shown in the upstream tutorial, then construct one flow for nodes and one for edges:

from torchdrug.layers import distribution

num_atom_type = dataset.num_atom_type
num_bond_type = dataset.num_bond_type + 1  # one extra class for no edge

node_prior = distribution.IndependentGaussian(
    torch.zeros(num_atom_type),
    torch.ones(num_atom_type),
)
edge_prior = distribution.IndependentGaussian(
    torch.zeros(num_bond_type),
    torch.ones(num_bond_type),
)
node_flow = models.GraphAF(
    model,
    node_prior,
    num_layer=12,
)
edge_flow = models.GraphAF(
    model,
    edge_prior,
    use_edge=True,
    num_layer=12,
)

task = tasks.AutoregressiveGeneration(
    node_flow,
    edge_flow,
    max_node=38,
    max_edge_unroll=12,
    criterion="nll",
)

Do not omit the documented prior construction. The node and edge prior shapes must match the dataset's atom and bond vocabularies.

Train and generate through the task:

optimizer = torch.optim.Adam(task.parameters(), lr=1e-3)
solver = core.Engine(
    task,
    dataset,
    None,
    None,
    optimizer,
    batch_size=128,
    log_interval=10,
)
solver.train(num_epoch=10)
solver.save("graphaf-zinc250k.pth")

solver.load("graphaf-zinc250k.pth")
task.eval()
results = task.generate(num_sample=32)
print(results.to_smiles())

For PPO fine-tuning, rebuild AutoregressiveGeneration with task="qed" or task="plogp", a PPO criterion, and the tutorial's reward/baseline settings; then load the pretrained checkpoint with load_optimizer=False.

Raw lines 125 to 210
211## What the API does not provide
213Avoid these unsupported patterns:
215```python
216# Not a TorchDrug 0.2.1 API
217tasks.GCPNGeneration(model, reward_function=my_reward, criterion="ppo")
218```
220TorchDrug 0.2.1's built-in generation task names are limited to QED and penalized
221logP. A custom objective requires extending the task implementation rather than
222passing a callback shown in another library.
224The tutorial does not document generic scaffold-conditioned or
225fragment-conditioned constructors. Do not claim those capabilities without a
226separate implementation.

What the API does not provide

Avoid these unsupported patterns:

# Not a TorchDrug 0.2.1 API
tasks.GCPNGeneration(model, reward_function=my_reward, criterion="ppo")

TorchDrug 0.2.1's built-in generation task names are limited to QED and penalized logP. A custom objective requires extending the task implementation rather than passing a callback shown in another library.

The tutorial does not document generic scaffold-conditioned or fragment-conditioned constructors. Do not claim those capabilities without a separate implementation.

Raw lines 211 to 227
228## Evaluation and safety
230At minimum report:
232- validity
233- uniqueness
234- novelty against the training set
235- duplicate-aware property distributions
236- failure and resampling rates
238Also:
240- canonicalize and sanitize with a chemistry toolkit,
241- reject disconnected or chemically implausible structures as appropriate,
242- screen structural alerts and undesirable substructures,
243- assess synthetic accessibility separately,
244- avoid presenting QED or penalized logP as evidence of efficacy or safety,
245- keep generated structures out of automated synthesis without expert review.

Evaluation and safety

At minimum report:

  • validity
  • uniqueness
  • novelty against the training set
  • duplicate-aware property distributions
  • failure and resampling rates

Also:

  • canonicalize and sanitize with a chemistry toolkit,
  • reject disconnected or chemically implausible structures as appropriate,
  • screen structural alerts and undesirable substructures,
  • assess synthetic accessibility separately,
  • avoid presenting QED or penalized logP as evidence of efficacy or safety,
  • keep generated structures out of automated synthesis without expert review.
Raw lines 228 to 246
247## Source links
249- [Generation tutorial](https://torchdrug.ai/docs/tutorials/generation.html)
250- [Generation benchmark](https://torchdrug.ai/docs/benchmark/generation.html)
251- [Generation task API](https://torchdrug.ai/docs/api/tasks.html#molecule-generation-tasks)
252- [Flow model API](https://torchdrug.ai/docs/api/models.html#normalizing-flows)