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

core_concepts.md

256 lines · skills/torchdrug/references/core_concepts.md at 5f2bc03

Open on GitHub
1# Core Concepts and Data Structures
2
3This reference follows the
4[TorchDrug 0.2.1 data API](https://torchdrug.ai/docs/api/data.html),
5[quick start](https://torchdrug.ai/docs/quick_start.html), and
6[notes](https://torchdrug.ai/docs/notes/).
7External-file and checkpoint examples require user-supplied trusted inputs;
8they are illustrative unless listed in [review evidence](review.md).
9

Core Concepts and Data Structures

This reference follows the TorchDrug 0.2.1 data API, quick start, and notes. External-file and checkpoint examples require user-supplied trusted inputs; they are illustrative unless listed in review evidence.

Raw lines 1 to 9
10## Component hierarchy
12TorchDrug separates four concerns:
14- `torchdrug.data`: tensor-backed `Graph`, `Molecule`, `Protein`, and packed
15 variants.
16- `torchdrug.datasets`: downloadable datasets whose samples contain graphs and
17 targets.
18- `torchdrug.models`: reusable graph, sequence, embedding, flow, and
19 self-supervised encoders.
20- `torchdrug.tasks`: objectives that wrap models and implement prediction, loss,
21 and evaluation.
22- `torchdrug.core.Engine`: preprocessing, batching, optimization, checkpointing,
23 and evaluation.
25Keep these layers separate. A model creates representations; a task defines what
26to learn; an engine executes the experiment.

Component hierarchy

TorchDrug separates four concerns:

  • torchdrug.data: tensor-backed Graph, Molecule, Protein, and packed variants.
  • torchdrug.datasets: downloadable datasets whose samples contain graphs and targets.
  • torchdrug.models: reusable graph, sequence, embedding, flow, and self-supervised encoders.
  • torchdrug.tasks: objectives that wrap models and implement prediction, loss, and evaluation.
  • torchdrug.core.Engine: preprocessing, batching, optimization, checkpointing, and evaluation.

Keep these layers separate. A model creates representations; a task defines what to learn; an engine executes the experiment.

Raw lines 10 to 27
28## Graphs and molecules
30```python
31import torchdrug as td
32from torchdrug import data
34edge_list = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]]
35graph = data.Graph(edge_list, num_node=6)
37mol = data.Molecule.from_smiles(
38 "CCOC(=O)N",
39 atom_feature="default",
40 bond_feature="default",
42print(mol.node_feature.shape)
43print(mol.edge_feature.shape)
45node_in, node_out, _ = mol.edge_list.t()
46carbon_edge = (mol.atom_type[node_in] == td.CARBON) | (
47 mol.atom_type[node_out] == td.CARBON
49carbon_subgraph = mol.edge_mask(carbon_edge)
50```
52Molecular bonds are represented by two directed edges. Do not assume a stable
53ordering of those edges.
55Useful conversions:
57- `data.Molecule.from_smiles(smiles)`
58- `data.Molecule.from_molecule(rdkit_mol)`
59- `molecule.to_smiles()`
60- `molecule.to_molecule()`
61- `data.PackedMolecule.from_smiles(smiles_list)`
62- `data.PackedMolecule.from_molecule(rdkit_mols)`
64`PackedMolecule.to_smiles()` and `.to_molecule()` return lists.

Graphs and molecules

import torchdrug as td
from torchdrug import data

edge_list = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]]
graph = data.Graph(edge_list, num_node=6)

mol = data.Molecule.from_smiles(
    "CCOC(=O)N",
    atom_feature="default",
    bond_feature="default",
)
print(mol.node_feature.shape)
print(mol.edge_feature.shape)

node_in, node_out, _ = mol.edge_list.t()
carbon_edge = (mol.atom_type[node_in] == td.CARBON) | (
    mol.atom_type[node_out] == td.CARBON
)
carbon_subgraph = mol.edge_mask(carbon_edge)

Molecular bonds are represented by two directed edges. Do not assume a stable ordering of those edges.

Useful conversions:

  • data.Molecule.from_smiles(smiles)
  • data.Molecule.from_molecule(rdkit_mol)
  • molecule.to_smiles()
  • molecule.to_molecule()
  • data.PackedMolecule.from_smiles(smiles_list)
  • data.PackedMolecule.from_molecule(rdkit_mols)

PackedMolecule.to_smiles() and .to_molecule() return lists.

Raw lines 28 to 65
66## Proteins
68```python
69from torchdrug import data
71sequence_protein = data.Protein.from_sequence(
72 "MKTAYIAKQRQISFVKSHFSRQ",
73 atom_feature=None,
74 bond_feature=None,
75 residue_feature="default",
77structure_protein = data.Protein.from_pdb(
78 "protein.pdb",
79 residue_feature="default",
82print(sequence_protein.to_sequence())
83```
85For sequence-only work, setting `atom_feature=None` and `bond_feature=None`
86avoids constructing unnecessary atom-level features and can substantially reduce
87loading cost.
88The fast path has no bonds: `to_sequence()` consequently inserts dots between
89residues. Preserve the input sequence and chain boundaries separately. Unknown
90symbols become glycine with a warning; reject or explicitly map ambiguous
91residues before construction. See [protein modeling](protein_modeling.md).
93Documented protein constructors and conversions include:
95- `Protein.from_sequence`
96- `Protein.from_pdb`
97- `Protein.from_molecule`
98- `Protein.to_sequence`
99- `Protein.to_pdb`
100- `Protein.to_molecule`
102Protein graph construction is handled by the documented geometry/graph
103construction layers. `Protein` does not provide a `residue_graph()` method in
1040.2.1.

Proteins

from torchdrug import data

sequence_protein = data.Protein.from_sequence(
    "MKTAYIAKQRQISFVKSHFSRQ",
    atom_feature=None,
    bond_feature=None,
    residue_feature="default",
)
structure_protein = data.Protein.from_pdb(
    "protein.pdb",
    residue_feature="default",
)

print(sequence_protein.to_sequence())

For sequence-only work, setting atom_feature=None and bond_feature=None avoids constructing unnecessary atom-level features and can substantially reduce loading cost. The fast path has no bonds: to_sequence() consequently inserts dots between residues. Preserve the input sequence and chain boundaries separately. Unknown symbols become glycine with a warning; reject or explicitly map ambiguous residues before construction. See protein modeling.

Documented protein constructors and conversions include:

  • Protein.from_sequence
  • Protein.from_pdb
  • Protein.from_molecule
  • Protein.to_sequence
  • Protein.to_pdb
  • Protein.to_molecule

Protein graph construction is handled by the documented geometry/graph construction layers. Protein does not provide a residue_graph() method in 0.2.1.

Raw lines 66 to 105
106## Packed graphs and collation
108Graphs of different sizes are packed into a block-diagonal representation:
110```python
111from torchdrug import data
113graphs = [
114 data.Molecule.from_smiles("CCO"),
115 data.Molecule.from_smiles("c1ccccc1"),
117batch = data.Graph.pack(graphs)
118restored = batch.unpack()
119```
121For dataset samples, use:
123```python
124batch = data.graph_collate(samples)
125```
127`graph_collate` recursively collates nested containers and uses `Graph.pack` for
128graph values. Prefer it to PyTorch's default collator for manual inference.
130Packed graph operations include:
132- `subbatch(index)` for selecting graphs
133- `node_mask(index, compact=...)`
134- `edge_mask(index)`
135- `graph_mask(index, compact=...)`
136- `repeat(count)` / `repeat_interleave(repeats)`
137- `unpack()`

Packed graphs and collation

Graphs of different sizes are packed into a block-diagonal representation:

from torchdrug import data

graphs = [
    data.Molecule.from_smiles("CCO"),
    data.Molecule.from_smiles("c1ccccc1"),
]
batch = data.Graph.pack(graphs)
restored = batch.unpack()

For dataset samples, use:

batch = data.graph_collate(samples)

graph_collate recursively collates nested containers and uses Graph.pack for graph values. Prefer it to PyTorch's default collator for manual inference.

Packed graph operations include:

  • subbatch(index) for selecting graphs
  • node_mask(index, compact=...)
  • edge_mask(index)
  • graph_mask(index, compact=...)
  • repeat(count) / repeat_interleave(repeats)
  • unpack()
Raw lines 106 to 138
139## Attributes and references
141TorchDrug graph attributes carry semantic scopes. When adding custom attributes,
142register them in the matching context:
144```python
145with mol.atom():
146 mol.is_carbon = mol.atom_type == td.CARBON
148with mol.edge():
149 mol.is_single_bond = mol.bond_type == td.SINGLE
150```
152Use node, edge, graph, and reference contexts so masking, packing, and device
153transfer update custom values correctly. See
154[Deal with References](https://torchdrug.ai/docs/notes/reference.html).

Attributes and references

TorchDrug graph attributes carry semantic scopes. When adding custom attributes, register them in the matching context:

with mol.atom():
    mol.is_carbon = mol.atom_type == td.CARBON

with mol.edge():
    mol.is_single_bond = mol.bond_type == td.SINGLE

Use node, edge, graph, and reference contexts so masking, packing, and device transfer update custom values correctly. See Deal with References.

Raw lines 139 to 155
156## Model interface
158Graph representation models use this general call shape:
160```python
161output = model(graph, graph.node_feature)
162graph_feature = output["graph_feature"]
163node_feature = output["node_feature"]
164```
166Protein sequence models may return `residue_feature` instead of `node_feature`.
167Inspect the selected model's API page rather than assuming every model returns
168the same keys.
170Most models accept optional `all_loss` and `metric` accumulators:
172```python
173output = model(graph, graph.node_feature, all_loss=all_loss, metric=metric)
174```
176Tasks use those accumulators for auxiliary losses and metrics.

Model interface

Graph representation models use this general call shape:

output = model(graph, graph.node_feature)
graph_feature = output["graph_feature"]
node_feature = output["node_feature"]

Protein sequence models may return residue_feature instead of node_feature. Inspect the selected model's API page rather than assuming every model returns the same keys.

Most models accept optional all_loss and metric accumulators:

output = model(graph, graph.node_feature, all_loss=all_loss, metric=metric)

Tasks use those accumulators for auxiliary losses and metrics.

Raw lines 156 to 177
178## Task and Engine lifecycle
180The normal lifecycle is:
1821. construct model,
1832. construct task,
1843. construct optimizer over `task.parameters()`,
1854. construct `core.Engine`,
1865. call `solver.train()` and `solver.evaluate()`.
188When `Engine` is created, it calls task preprocessing against the supplied
189train/validation/test sets. This matters because tasks may infer target
190statistics or metadata during preprocessing.
192```python
193import torch
195optimizer = torch.optim.Adam(task.parameters(), lr=1e-3)
196solver = core.Engine(
197 task,
198 train_set,
199 valid_set,
200 test_set,
201 optimizer,
202 batch_size=128,
204solver.train(num_epoch=10)
205metrics = solver.evaluate("valid")
206```
208Use `gpus=[0]` for one supported CUDA device. Omit it on CPU. For manual nested
209batches, `torchdrug.utils.cuda(batch)` moves all tensors and graphs together.

Task and Engine lifecycle

The normal lifecycle is:

  1. construct model,
  2. construct task,
  3. construct optimizer over task.parameters(),
  4. construct core.Engine,
  5. call solver.train() and solver.evaluate().

When Engine is created, it calls task preprocessing against the supplied train/validation/test sets. This matters because tasks may infer target statistics or metadata during preprocessing.

import torch

optimizer = torch.optim.Adam(task.parameters(), lr=1e-3)
solver = core.Engine(
    task,
    train_set,
    valid_set,
    test_set,
    optimizer,
    batch_size=128,
)
solver.train(num_epoch=10)
metrics = solver.evaluate("valid")

Use gpus=[0] for one supported CUDA device. Omit it on CPU. For manual nested batches, torchdrug.utils.cuda(batch) moves all tensors and graphs together.

Raw lines 178 to 210
211## Configuration and checkpoints
213`core.Configurable` serializes component constructor configuration:
215```python
216import json
217from torchdrug import core
219with open("solver.json", "w") as fout:
220 json.dump(solver.config_dict(), fout)
221solver.save("solver.pth")
223with open("solver.json") as fin:
224 restored_solver = core.Configurable.load_config_dict(json.load(fin))
225restored_solver.load("solver.pth")
226```
228For transfer learning, a solver checkpoint stores model state under `"model"`:
230```python
231checkpoint = torch.load("pretrained.pth", map_location="cpu")["model"]
232transfer = task.load_state_dict(checkpoint, strict=False)
233print(transfer.missing_keys, transfer.unexpected_keys)
234```
236Use `strict=False` only when intentionally transferring a compatible subset, such
237as a pretrained encoder into a property-prediction task.
238Verify that encoder keys actually matched: InfoGraph introduces an additional
239`model.` nesting level compared with AttributeMasking. Never count a successful
240`strict=False` call as proof of weight transfer. PyTorch 2.0 checkpoint loading
241uses pickle; only load files from trusted sources. The special `Retrosynthesis`
242task requires the two individual subtask checkpoints, not a generic combined
243round trip (see [retrosynthesis](retrosynthesis.md#checkpoint-loading)).

Configuration and checkpoints

core.Configurable serializes component constructor configuration:

import json
from torchdrug import core

with open("solver.json", "w") as fout:
    json.dump(solver.config_dict(), fout)
solver.save("solver.pth")

with open("solver.json") as fin:
    restored_solver = core.Configurable.load_config_dict(json.load(fin))
restored_solver.load("solver.pth")

For transfer learning, a solver checkpoint stores model state under "model":

checkpoint = torch.load("pretrained.pth", map_location="cpu")["model"]
transfer = task.load_state_dict(checkpoint, strict=False)
print(transfer.missing_keys, transfer.unexpected_keys)

Use strict=False only when intentionally transferring a compatible subset, such as a pretrained encoder into a property-prediction task. Verify that encoder keys actually matched: InfoGraph introduces an additional model. nesting level compared with AttributeMasking. Never count a successful strict=False call as proof of weight transfer. PyTorch 2.0 checkpoint loading uses pickle; only load files from trusted sources. The special Retrosynthesis task requires the two individual subtask checkpoints, not a generic combined round trip (see retrosynthesis).

Raw lines 211 to 244
245## Feature naming in 0.2.1
247Prefer:
249- `atom_feature`
250- `bond_feature`
251- `residue_feature`
252- `mol_feature`
254The older `node_feature`, `edge_feature`, and `graph_feature` constructor names
255are deprecated aliases where documented. Runtime properties such as
256`dataset.node_feature_dim` and `graph.node_feature` remain valid.

Feature naming in 0.2.1

Prefer:

  • atom_feature
  • bond_feature
  • residue_feature
  • mol_feature

The older node_feature, edge_feature, and graph_feature constructor names are deprecated aliases where documented. Runtime properties such as dataset.node_feature_dim and graph.node_feature remain valid.

Raw lines 245 to 256