LudwigO's picture
add chemprop files
5716801
Raw
History Blame Contribute Delete
4.13 kB
from dataclasses import dataclass, field, InitVar
from typing import Iterable, NamedTuple, Sequence
import numpy as np
import torch
from torch import Tensor
from chemprop.data.datasets import Datum
from chemprop.data.molgraph import MolGraph
@dataclass(repr=False, eq=False, slots=True)
class BatchMolGraph:
"""A :class:`BatchMolGraph` represents a batch of individual :class:`MolGraph`\s.
It has all the attributes of a ``MolGraph`` with the addition of the ``batch`` attribute. This
class is intended for use with data loading, so it uses :obj:`~torch.Tensor`\s to store data
"""
mgs: InitVar[Sequence[MolGraph]]
"""A list of individual :class:`MolGraph`\s to be batched together"""
V: Tensor = field(init=False)
"""the atom feature matrix"""
E: Tensor = field(init=False)
"""the bond feature matrix"""
edge_index: Tensor = field(init=False)
"""an tensor of shape ``2 x E`` containing the edges of the graph in COO format"""
rev_edge_index: Tensor = field(init=False)
"""A tensor of shape ``E`` that maps from an edge index to the index of the source of the
reverse edge in the ``edge_index`` attribute."""
batch: Tensor = field(init=False)
"""the index of the parent :class:`MolGraph` in the batched graph"""
__size: int = field(init=False)
def __post_init__(self, mgs: Sequence[MolGraph]):
self.__size = len(mgs)
Vs = []
Es = []
edge_indexes = []
rev_edge_indexes = []
batch_indexes = []
num_nodes = 0
num_edges = 0
for i, mg in enumerate(mgs):
Vs.append(mg.V)
Es.append(mg.E)
edge_indexes.append(mg.edge_index + num_nodes)
rev_edge_indexes.append(mg.rev_edge_index + num_edges)
batch_indexes.append([i] * len(mg.V))
num_nodes += mg.V.shape[0]
num_edges += mg.edge_index.shape[1]
self.V = torch.from_numpy(np.concatenate(Vs)).float()
self.E = torch.from_numpy(np.concatenate(Es)).float()
self.edge_index = torch.from_numpy(np.hstack(edge_indexes)).long()
self.rev_edge_index = torch.from_numpy(np.concatenate(rev_edge_indexes)).long()
self.batch = torch.tensor(np.concatenate(batch_indexes)).long()
def __len__(self) -> int:
"""the number of individual :class:`MolGraph`\s in this batch"""
return self.__size
def to(self, device: str | torch.device):
self.V = self.V.to(device)
self.E = self.E.to(device)
self.edge_index = self.edge_index.to(device)
self.rev_edge_index = self.rev_edge_index.to(device)
self.batch = self.batch.to(device)
class TrainingBatch(NamedTuple):
bmg: BatchMolGraph
V_d: Tensor | None
X_d: Tensor | None
Y: Tensor | None
w: Tensor
lt_mask: Tensor | None
gt_mask: Tensor | None
def collate_batch(batch: Iterable[Datum]) -> TrainingBatch:
mgs, V_ds, x_ds, ys, weights, lt_masks, gt_masks = zip(*batch)
return TrainingBatch(
BatchMolGraph(mgs),
None if V_ds[0] is None else torch.from_numpy(np.concatenate(V_ds)).float(),
None if x_ds[0] is None else torch.from_numpy(np.array(x_ds)).float(),
None if ys[0] is None else torch.from_numpy(np.array(ys)).float(),
torch.tensor(weights, dtype=torch.float).unsqueeze(1),
None if lt_masks[0] is None else torch.from_numpy(np.array(lt_masks)),
None if gt_masks[0] is None else torch.from_numpy(np.array(gt_masks)),
)
class MulticomponentTrainingBatch(NamedTuple):
bmgs: list[BatchMolGraph]
V_ds: list[Tensor | None]
X_d: Tensor | None
Y: Tensor | None
w: Tensor
lt_mask: Tensor | None
gt_mask: Tensor | None
def collate_multicomponent(batches: Iterable[Iterable[Datum]]) -> MulticomponentTrainingBatch:
tbs = [collate_batch(batch) for batch in zip(*batches)]
return MulticomponentTrainingBatch(
[tb.bmg for tb in tbs],
[tb.V_d for tb in tbs],
tbs[0].X_d,
tbs[0].Y,
tbs[0].w,
tbs[0].lt_mask,
tbs[0].gt_mask,
)