torch-molecule 0.1.0.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. torch_molecule/__init__.py +70 -0
  2. torch_molecule/base/__init__.py +8 -0
  3. torch_molecule/base/base.py +396 -0
  4. torch_molecule/base/encoder.py +44 -0
  5. torch_molecule/base/generator.py +24 -0
  6. torch_molecule/base/old_encoder.py +322 -0
  7. torch_molecule/base/old_predictor.py +904 -0
  8. torch_molecule/base/old_predictor2.py +436 -0
  9. torch_molecule/base/predictor.py +112 -0
  10. torch_molecule/encoder/attrmask/__init__.py +3 -0
  11. torch_molecule/encoder/attrmask/model.py +113 -0
  12. torch_molecule/encoder/attrmask/modeling_attrmask.py +304 -0
  13. torch_molecule/encoder/constant.py +13 -0
  14. torch_molecule/encoder/contextpred/__init__.py +3 -0
  15. torch_molecule/encoder/contextpred/model.py +207 -0
  16. torch_molecule/encoder/contextpred/modeling_contextpred.py +299 -0
  17. torch_molecule/encoder/contextpred/utils.py +206 -0
  18. torch_molecule/encoder/edgepred/__init__.py +3 -0
  19. torch_molecule/encoder/edgepred/model.py +104 -0
  20. torch_molecule/encoder/edgepred/modeling_edgepred.py +285 -0
  21. torch_molecule/encoder/graphmae/__init__.py +3 -0
  22. torch_molecule/encoder/graphmae/dataloader.py +206 -0
  23. torch_molecule/encoder/graphmae/model.py +137 -0
  24. torch_molecule/encoder/graphmae/modeling_graphmae.py +323 -0
  25. torch_molecule/encoder/infograph/__init__.py +3 -0
  26. torch_molecule/encoder/infograph/loss_function.py +128 -0
  27. torch_molecule/encoder/infograph/model.py +92 -0
  28. torch_molecule/encoder/infograph/modeling_infograph.py +301 -0
  29. torch_molecule/encoder/infograph/module.py +31 -0
  30. torch_molecule/encoder/moama/__init__.py +3 -0
  31. torch_molecule/encoder/moama/model.py +131 -0
  32. torch_molecule/encoder/moama/modeling_moama.py +307 -0
  33. torch_molecule/encoder/moama/utils.py +143 -0
  34. torch_molecule/encoder/pretrained/__init__.py +3 -0
  35. torch_molecule/encoder/pretrained/modeling_pretrained.py +274 -0
  36. torch_molecule/encoder/supervised/__init__.py +3 -0
  37. torch_molecule/encoder/supervised/model.py +97 -0
  38. torch_molecule/encoder/supervised/modeling_supervised.py +354 -0
  39. torch_molecule/generator/digress/__init__.py +3 -0
  40. torch_molecule/generator/digress/diffusion.py +194 -0
  41. torch_molecule/generator/digress/modeling_digress.py +503 -0
  42. torch_molecule/generator/digress/transformer.py +360 -0
  43. torch_molecule/generator/digress/utils.py +196 -0
  44. torch_molecule/generator/gdss/__init__.py +3 -0
  45. torch_molecule/generator/gdss/layers.py +257 -0
  46. torch_molecule/generator/gdss/model.py +204 -0
  47. torch_molecule/generator/gdss/modeling_gdss.py +474 -0
  48. torch_molecule/generator/gdss/sde.py +334 -0
  49. torch_molecule/generator/gdss/solver.py +227 -0
  50. torch_molecule/generator/gdss/toremove/ScoreNetworkA.py +167 -0
  51. torch_molecule/generator/gdss/toremove/ScoreNetworkX.py +85 -0
  52. torch_molecule/generator/gdss/utils.py +240 -0
  53. torch_molecule/generator/graph_dit/__init__.py +3 -0
  54. torch_molecule/generator/graph_dit/diffusion.py +188 -0
  55. torch_molecule/generator/graph_dit/modeling_graph_dit.py +581 -0
  56. torch_molecule/generator/graph_dit/transformer.py +207 -0
  57. torch_molecule/generator/graph_dit/utils.py +220 -0
  58. torch_molecule/generator/graph_ga/__init__.py +3 -0
  59. torch_molecule/generator/graph_ga/crossover.py +182 -0
  60. torch_molecule/generator/graph_ga/modeling_graph_ga.py +310 -0
  61. torch_molecule/generator/graph_ga/mutate.py +136 -0
  62. torch_molecule/generator/graph_ga/oracle.py +95 -0
  63. torch_molecule/generator/jtvae/__init__.py +3 -0
  64. torch_molecule/generator/jtvae/jtnn/chemutils.py +450 -0
  65. torch_molecule/generator/jtvae/jtnn/datautils.py +127 -0
  66. torch_molecule/generator/jtvae/jtnn/jtmpn.py +140 -0
  67. torch_molecule/generator/jtvae/jtnn/jtnn_dec.py +350 -0
  68. torch_molecule/generator/jtvae/jtnn/jtnn_enc.py +131 -0
  69. torch_molecule/generator/jtvae/jtnn/mol_tree.py +111 -0
  70. torch_molecule/generator/jtvae/jtnn/mpn.py +129 -0
  71. torch_molecule/generator/jtvae/jtnn/nnutils.py +66 -0
  72. torch_molecule/generator/jtvae/jtnn/vocab.py +31 -0
  73. torch_molecule/generator/jtvae/jtnn_vae.py +246 -0
  74. torch_molecule/generator/jtvae/modeling_jtvae.py +247 -0
  75. torch_molecule/generator/lstm/__init__.py +3 -0
  76. torch_molecule/generator/lstm/action_sampler.py +91 -0
  77. torch_molecule/generator/lstm/lstm.py +59 -0
  78. torch_molecule/generator/lstm/modeling_lstm.py +263 -0
  79. torch_molecule/generator/lstm/smiles_char_dict.py +123 -0
  80. torch_molecule/generator/lstm/utils.py +62 -0
  81. torch_molecule/generator/molgpt/__init__.py +3 -0
  82. torch_molecule/generator/molgpt/dataset.py +87 -0
  83. torch_molecule/generator/molgpt/gpt.py +251 -0
  84. torch_molecule/generator/molgpt/modeling_molgpt.py +427 -0
  85. torch_molecule/generator/pretrained/modeling_pretrained.py +1 -0
  86. torch_molecule/nn/__init__.py +16 -0
  87. torch_molecule/nn/attention.py +63 -0
  88. torch_molecule/nn/embedder.py +183 -0
  89. torch_molecule/nn/gnn.py +631 -0
  90. torch_molecule/nn/mlp.py +34 -0
  91. torch_molecule/predictor/bfgnn/__init__.py +3 -0
  92. torch_molecule/predictor/bfgnn/model.py +116 -0
  93. torch_molecule/predictor/bfgnn/modeling_bfgnn.py +81 -0
  94. torch_molecule/predictor/dir/__init__.py +3 -0
  95. torch_molecule/predictor/dir/model.py +235 -0
  96. torch_molecule/predictor/dir/modeling_dir.py +161 -0
  97. torch_molecule/predictor/dir/utils.py +40 -0
  98. torch_molecule/predictor/gnn/__init__.py +3 -0
  99. torch_molecule/predictor/gnn/model.py +114 -0
  100. torch_molecule/predictor/gnn/modeling_gnn.py +706 -0
  101. torch_molecule/predictor/gnn/old_modeling_gnn.py +707 -0
  102. torch_molecule/predictor/grea/__init__.py +3 -0
  103. torch_molecule/predictor/grea/model.py +190 -0
  104. torch_molecule/predictor/grea/modeling_grea.py +107 -0
  105. torch_molecule/predictor/grea/old1_features.py +229 -0
  106. torch_molecule/predictor/grea/old1_modeling_grea.py +493 -0
  107. torch_molecule/predictor/grea/utils.py +97 -0
  108. torch_molecule/predictor/grin/__init__.py +3 -0
  109. torch_molecule/predictor/grin/model.py +116 -0
  110. torch_molecule/predictor/grin/modeling_grin.py +91 -0
  111. torch_molecule/predictor/irm/__init__.py +3 -0
  112. torch_molecule/predictor/irm/model.py +127 -0
  113. torch_molecule/predictor/irm/modeling_irm.py +179 -0
  114. torch_molecule/predictor/lstm/__init__.py +3 -0
  115. torch_molecule/predictor/lstm/model.py +145 -0
  116. torch_molecule/predictor/lstm/modeling_lstm.py +627 -0
  117. torch_molecule/predictor/lstm/token_from_smiles.py +85 -0
  118. torch_molecule/predictor/old_modeling_graph.py +650 -0
  119. torch_molecule/predictor/rpgnn/__init__.py +3 -0
  120. torch_molecule/predictor/rpgnn/model.py +185 -0
  121. torch_molecule/predictor/rpgnn/modeling_rpgnn.py +76 -0
  122. torch_molecule/predictor/sgir/__init__.py +3 -0
  123. torch_molecule/predictor/sgir/modeling_sgir.py +260 -0
  124. torch_molecule/predictor/sgir/strategy.py +266 -0
  125. torch_molecule/predictor/smiles_transformer/__init__.py +3 -0
  126. torch_molecule/predictor/smiles_transformer/model.py +424 -0
  127. torch_molecule/predictor/smiles_transformer/modeling_transformer.py +252 -0
  128. torch_molecule/predictor/ssr/__init__.py +3 -0
  129. torch_molecule/predictor/ssr/model.py +283 -0
  130. torch_molecule/predictor/ssr/modeling_ssr.py +267 -0
  131. torch_molecule/predictor/ssr/utils.py +185 -0
  132. torch_molecule/utils/__init__.py +31 -0
  133. torch_molecule/utils/checker.py +158 -0
  134. torch_molecule/utils/checkpoint.py +294 -0
  135. torch_molecule/utils/format.py +140 -0
  136. torch_molecule/utils/generic/metrics.py +530 -0
  137. torch_molecule/utils/generic/pseudo_tasks.py +11 -0
  138. torch_molecule/utils/generic/weights.py +30 -0
  139. torch_molecule/utils/graph/features.py +178 -0
  140. torch_molecule/utils/graph/graph_from_smiles.py +134 -0
  141. torch_molecule/utils/graph/graph_to_smiles.py +312 -0
  142. torch_molecule/utils/hf.py +232 -0
  143. torch_molecule/utils/search.py +64 -0
  144. torch_molecule-0.1.0.dev2.dist-info/METADATA +243 -0
  145. torch_molecule-0.1.0.dev2.dist-info/RECORD +148 -0
  146. torch_molecule-0.1.0.dev2.dist-info/WHEEL +5 -0
  147. torch_molecule-0.1.0.dev2.dist-info/licenses/LICENSE +21 -0
  148. torch_molecule-0.1.0.dev2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,301 @@
1
+ import numpy as np
2
+ from tqdm import tqdm
3
+ from typing import Optional, Union, Dict, Any, Tuple, List, Literal, Type
4
+
5
+ from dataclasses import dataclass, field
6
+
7
+ import torch
8
+ from torch_geometric.loader import DataLoader
9
+ from torch_geometric.data import Data
10
+
11
+ from .model import GNN
12
+ from ..constant import GNN_ENCODER_MODELS, GNN_ENCODER_READOUTS, GNN_ENCODER_PARAMS
13
+ from ...base import BaseMolecularEncoder
14
+ from ...utils import graph_from_smiles
15
+
16
+ ALLOWABLE_ENCODER_MODELS = GNN_ENCODER_MODELS
17
+ ALLOWABLE_ENCODER_READOUTS = GNN_ENCODER_READOUTS
18
+
19
+ @dataclass
20
+ class InfoGraphMolecularEncoder(BaseMolecularEncoder):
21
+ """This encoder implements a InfoGraph for molecular representation learning.
22
+
23
+ InfoGraph: Unsupervised and Semi-supervised Graph-Level Representation Learning via Mutual Information Maximization (ICLR 2020)
24
+
25
+ References
26
+ ----------
27
+ - Paper: https://arxiv.org/abs/1908.01000
28
+ - Code: https://github.com/sunfanyunn/InfoGraph/tree/master/unsupervised
29
+
30
+ Parameters
31
+ ----------
32
+ lw_prior : float, default=0.
33
+ Weight for prior loss term.
34
+ embedding_dim : int, default=160
35
+ Dimension of final graph embedding. Must be divisible by num_layer.
36
+ num_layer : int, default=5
37
+ Number of GNN layers.
38
+ drop_ratio : float, default=0.5
39
+ Dropout probability.
40
+ norm_layer : str, default="batch_norm"
41
+ Type of normalization layer to use. One of ["batch_norm", "layer_norm", "instance_norm", "graph_norm", "size_norm", "pair_norm"].
42
+ encoder_type : str, default="gin-virtual"
43
+ Type of GNN architecture to use. One of ["gin-virtual", "gcn-virtual", "gin", "gcn"].
44
+ readout : str, default="sum"
45
+ Method for aggregating node features to obtain graph-level representations. One of ["sum", "mean", "max"].
46
+ batch_size : int, default=128
47
+ Number of samples per batch for training.
48
+ epochs : int, default=500
49
+ Maximum number of training epochs.
50
+ learning_rate : float, default=0.001
51
+ Learning rate for optimizer.
52
+ grad_clip_value : float, optional
53
+ Maximum norm of gradients for gradient clipping.
54
+ weight_decay : float, default=0.0
55
+ L2 regularization strength.
56
+ use_lr_scheduler : bool, default=False
57
+ Whether to use a learning rate scheduler.
58
+ scheduler_factor : float, default=0.5
59
+ Factor by which to reduce the learning rate when plateau is detected.
60
+ scheduler_patience : int, default=5
61
+ Number of epochs with no improvement after which learning rate will be reduced.
62
+ verbose : bool, default=False
63
+ Whether to print progress information during training.
64
+ model_name : str, default="InfographMolecularEncoder"
65
+ Name of the encoder model.
66
+ """
67
+ # Task related parameters
68
+ lw_prior : float = 0.
69
+ embedding_dim: int = 160
70
+
71
+ # Model parameters
72
+ num_layer: int = 5
73
+ drop_ratio: float = 0.5
74
+ norm_layer: str = "batch_norm"
75
+ encoder_type: str = "gin-virtual"
76
+ readout: str = "sum"
77
+
78
+ # Training parameters
79
+ batch_size: int = 128
80
+ epochs: int = 500
81
+ learning_rate: float = 0.001
82
+ grad_clip_value: Optional[float] = None
83
+ weight_decay: float = 0.0
84
+
85
+ # Scheduler parameters
86
+ use_lr_scheduler: bool = False
87
+ scheduler_factor: float = 0.5
88
+ scheduler_patience: int = 5
89
+
90
+ # Other parameters
91
+ verbose: bool = False
92
+ model_name: str = "InfographMolecularEncoder"
93
+
94
+ # Non-init fields
95
+ fitting_loss: List[float] = field(default_factory=list, init=False)
96
+ fitting_epoch: int = field(default=0, init=False)
97
+ model_class: Type[GNN] = field(default=GNN, init=False)
98
+
99
+ def __post_init__(self):
100
+ """Initialize the model after dataclass initialization."""
101
+ super().__post_init__()
102
+ if self.encoder_type not in ALLOWABLE_ENCODER_MODELS:
103
+ raise ValueError(f"Invalid encoder_model: {self.encoder_type}. Currently only {ALLOWABLE_ENCODER_MODELS} are supported.")
104
+ if self.readout not in ALLOWABLE_ENCODER_READOUTS:
105
+ raise ValueError(f"Invalid encoder_readout: {self.readout}. Currently only {ALLOWABLE_ENCODER_READOUTS} are supported.")
106
+
107
+ if self.lw_prior == 0:
108
+ self.use_prior = False
109
+ else:
110
+ self.use_prior = True
111
+
112
+ assert self.embedding_dim % self.num_layer == 0, "embedding_dim must be divisible by num_layer for InfographMolecularEncoder"
113
+
114
+ @staticmethod
115
+ def _get_param_names() -> List[str]:
116
+ """Get parameter names for the estimator.
117
+
118
+ Returns
119
+ -------
120
+ List[str]
121
+ List of parameter names that can be used for model configuration.
122
+ """
123
+ params = GNN_ENCODER_PARAMS.copy()
124
+ params.remove("hidden_size")
125
+ params = params + ["embedding_dim", 'use_prior', 'lw_prior']
126
+ return params
127
+
128
+
129
+ def _get_model_params(self, checkpoint: Optional[Dict] = None) -> Dict[str, Any]:
130
+ params = [
131
+ "num_layer", "embedding_dim", "drop_ratio", "norm_layer", "encoder_type", "readout", "use_prior",
132
+ ]
133
+ if checkpoint is not None:
134
+ if "hyperparameters" not in checkpoint:
135
+ raise ValueError("Checkpoint missing 'hyperparameters' key")
136
+ return {k: checkpoint["hyperparameters"][k] for k in params}
137
+
138
+ return {k: getattr(self, k) for k in params}
139
+
140
+ def _convert_to_pytorch_data(self, X):
141
+ """Convert numpy arrays to PyTorch Geometric data format.
142
+ """
143
+ if self.verbose:
144
+ iterator = tqdm(enumerate(X), desc="Converting molecules to graphs", total=len(X))
145
+ else:
146
+ iterator = enumerate(X)
147
+
148
+ pyg_graph_list = []
149
+ for idx, smiles_or_mol in iterator:
150
+ graph = graph_from_smiles(smiles_or_mol, None)
151
+ g = Data()
152
+ g.num_nodes = graph["num_nodes"]
153
+ g.edge_index = torch.from_numpy(graph["edge_index"])
154
+
155
+ del graph["num_nodes"]
156
+ del graph["edge_index"]
157
+
158
+ if graph["edge_feat"] is not None:
159
+ g.edge_attr = torch.from_numpy(graph["edge_feat"])
160
+ del graph["edge_feat"]
161
+
162
+ if graph["node_feat"] is not None:
163
+ g.x = torch.from_numpy(graph["node_feat"])
164
+ del graph["node_feat"]
165
+
166
+ pyg_graph_list.append(g)
167
+
168
+ return pyg_graph_list
169
+
170
+ def _setup_optimizers(self) -> Tuple[torch.optim.Optimizer, Optional[Any]]:
171
+ """Setup optimization components including optimizer and learning rate scheduler.
172
+ """
173
+ optimizer = torch.optim.Adam(self.model.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
174
+ scheduler = None
175
+ if self.use_lr_scheduler:
176
+ scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
177
+ optimizer,
178
+ mode="min",
179
+ factor=self.scheduler_factor,
180
+ patience=self.scheduler_patience,
181
+ min_lr=1e-6,
182
+ cooldown=0,
183
+ eps=1e-8,
184
+ )
185
+ return optimizer, scheduler
186
+
187
+ def fit(
188
+ self,
189
+ X_train: List[str],
190
+ ) -> "InfographMolecularEncoder":
191
+ """Fit the model to the training data with optional validation set.
192
+
193
+ Parameters
194
+ ----------
195
+ X_train : List[str]
196
+ Training set input molecular structures as SMILES strings
197
+ Returns
198
+ -------
199
+ self : InfographMolecularEncoder
200
+ Fitted estimator
201
+ """
202
+
203
+ self._initialize_model(self.model_class)
204
+ self.model.initialize_parameters()
205
+ optimizer, scheduler = self._setup_optimizers()
206
+
207
+ # Prepare datasets and loaders
208
+ X_train, _ = self._validate_inputs(X_train, return_rdkit_mol=True)
209
+ train_dataset = self._convert_to_pytorch_data(X_train)
210
+ train_loader = DataLoader(
211
+ train_dataset,
212
+ batch_size=self.batch_size,
213
+ shuffle=True,
214
+ num_workers=0
215
+ )
216
+ self.fitting_loss = []
217
+
218
+ for epoch in range(self.epochs):
219
+ # Training phase
220
+ train_losses = self._train_epoch(train_loader, optimizer, epoch)
221
+ self.fitting_loss.append(np.mean(train_losses).item())
222
+ if scheduler:
223
+ scheduler.step(np.mean(train_losses).item())
224
+
225
+ self.fitting_epoch = epoch
226
+ self.is_fitted_ = True
227
+ return self
228
+
229
+ def _train_epoch(self, train_loader, optimizer, epoch):
230
+ """Training logic for one epoch.
231
+
232
+ Args:
233
+ train_loader: DataLoader containing training data
234
+ optimizer: Optimizer instance for model parameter updates
235
+
236
+ Returns:
237
+ list: List of loss values for each training step
238
+ """
239
+ self.model.train()
240
+ losses = []
241
+
242
+ iterator = (
243
+ tqdm(train_loader, desc="Training", leave=False)
244
+ if self.verbose
245
+ else train_loader
246
+ )
247
+
248
+ for batch in iterator:
249
+ batch = batch.to(self.device)
250
+ optimizer.zero_grad()
251
+ local_global_loss, prior_loss = self.model.compute_loss(batch, self.lw_prior)
252
+ loss = local_global_loss + prior_loss
253
+ loss.backward()
254
+ if self.grad_clip_value is not None:
255
+ torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.grad_clip_value)
256
+ optimizer.step()
257
+ losses.append(loss.item())
258
+
259
+ if self.verbose:
260
+ iterator.set_postfix({"Epoch": f"{epoch}", "Loss": f"{loss.item():.4f}", "Local/Global": f"{local_global_loss.item():.4f}", "Prior": f"{prior_loss.item():.4f}"})
261
+
262
+ return losses
263
+
264
+ def encode(self, X: List[str], return_type: Literal["np", "pt"] = "pt") -> Union[np.ndarray, torch.Tensor]:
265
+ """Encode molecules into vector representations.
266
+
267
+ Parameters
268
+ ----------
269
+ X : List[str]
270
+ List of SMILES strings
271
+ return_type : Literal["np", "pt"], default="pt"
272
+ Return type of the representations
273
+
274
+ Returns
275
+ -------
276
+ representations : ndarray or torch.Tensor
277
+ Molecular representations
278
+ """
279
+ self._check_is_fitted()
280
+
281
+ # Convert to PyTorch Geometric format and create loader
282
+ X, _ = self._validate_inputs(X, return_rdkit_mol=True)
283
+ dataset = self._convert_to_pytorch_data(X)
284
+ loader = DataLoader(dataset, batch_size=self.batch_size, shuffle=False)
285
+
286
+ if self.model is None:
287
+ raise RuntimeError("Model not initialized")
288
+
289
+ # Generate encodings
290
+ self.model = self.model.to(self.device)
291
+ self.model.eval()
292
+ encodings = []
293
+ with torch.no_grad():
294
+ for batch in tqdm(loader, disable=not self.verbose):
295
+ batch = batch.to(self.device)
296
+ out = self.model(batch)
297
+ encodings.append(out["graph"].cpu())
298
+
299
+ # Concatenate and convert to requested format
300
+ encodings = torch.cat(encodings, dim=0)
301
+ return encodings if return_type == "pt" else encodings.numpy()
@@ -0,0 +1,31 @@
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+
5
+ class PriorDiscriminator(nn.Module):
6
+ def __init__(self, input_dim):
7
+ super().__init__()
8
+ self.l0 = nn.Linear(input_dim, input_dim)
9
+ self.l1 = nn.Linear(input_dim, input_dim)
10
+ self.l2 = nn.Linear(input_dim, 1)
11
+
12
+ def forward(self, x):
13
+ h = F.relu(self.l0(x))
14
+ h = F.relu(self.l1(h))
15
+ return torch.sigmoid(self.l2(h))
16
+
17
+ class FF(nn.Module):
18
+ def __init__(self, input_dim):
19
+ super().__init__()
20
+ self.block = nn.Sequential(
21
+ nn.Linear(input_dim, input_dim),
22
+ nn.ReLU(),
23
+ nn.Linear(input_dim, input_dim),
24
+ nn.ReLU(),
25
+ nn.Linear(input_dim, input_dim),
26
+ nn.ReLU()
27
+ )
28
+ self.linear_shortcut = nn.Linear(input_dim, input_dim)
29
+
30
+ def forward(self, x):
31
+ return self.block(x) + self.linear_shortcut(x)
@@ -0,0 +1,3 @@
1
+ from .modeling_moama import MoamaMolecularEncoder
2
+
3
+ __all__ = ['MoamaMolecularEncoder']
@@ -0,0 +1,131 @@
1
+ import torch
2
+ import torch.nn as nn
3
+ from torch_geometric.nn import global_add_pool, global_mean_pool, global_max_pool
4
+
5
+ from ...nn import GNN_node, GNN_node_Virtualnode, GCNConv, GINConv
6
+ from ...utils import init_weights
7
+
8
+ from .utils import get_mask_indices, get_fingerprint_loss
9
+
10
+ class_criterion = torch.nn.CrossEntropyLoss()
11
+
12
+ class GNN(nn.Module):
13
+ def __init__(
14
+ self,
15
+ num_layer,
16
+ hidden_size,
17
+ drop_ratio=0.5,
18
+ norm_layer="batch_norm",
19
+ encoder_type="gin-virtual",
20
+ readout="max",
21
+ mask_rate=0.15,
22
+ lw_rec=0.5
23
+ ):
24
+ super(GNN, self).__init__()
25
+ gnn_name = encoder_type.split("-")[0]
26
+ self.num_atom_type = 119
27
+ self.hidden_size = hidden_size
28
+ self.mask_rate = mask_rate
29
+ self.lw_rec = lw_rec
30
+
31
+ encoder_params = {
32
+ "num_layer": num_layer,
33
+ "hidden_size": hidden_size,
34
+ "JK": "last",
35
+ "drop_ratio": drop_ratio,
36
+ "residual": True,
37
+ "gnn_name": gnn_name,
38
+ "norm_layer": norm_layer
39
+ }
40
+
41
+ # Choose encoder type based on encoder_model
42
+ encoder_class = GNN_node_Virtualnode if "virtual" in encoder_type else GNN_node
43
+ self.graph_encoder = encoder_class(**encoder_params)
44
+ pooling_funcs = {
45
+ "sum": global_add_pool,
46
+ "mean": global_mean_pool,
47
+ "max": global_max_pool
48
+ }
49
+ self.pool = pooling_funcs.get(readout)
50
+ if self.pool is None:
51
+ raise ValueError(f"Invalid graph pooling type {readout}.")
52
+
53
+ self.predictor = GNN_Decoder(hidden_size, self.num_atom_type)
54
+
55
+ def initialize_parameters(self, seed=None):
56
+ """
57
+ Randomly initialize all model parameters using the init_weights function.
58
+
59
+ Args:
60
+ seed (int, optional): Random seed for reproducibility. Defaults to None.
61
+ """
62
+ if seed is not None:
63
+ torch.manual_seed(seed)
64
+
65
+ # Initialize the main components
66
+ init_weights(self.graph_encoder)
67
+ init_weights(self.predictor)
68
+
69
+ # Reset all parameters using PyTorch Geometric's reset function
70
+ def reset_parameters(module):
71
+ if hasattr(module, 'reset_parameters'):
72
+ module.reset_parameters()
73
+ elif hasattr(module, 'weight') and hasattr(module.weight, 'data'):
74
+ init_weights(module)
75
+
76
+ self.apply(reset_parameters)
77
+
78
+ def compute_loss(self, batched_data):
79
+ masked_node_indices = get_mask_indices(batched_data, self.mask_rate)
80
+ batched_data.masked_node_indices = torch.tensor(masked_node_indices)
81
+ batched_data.y = batched_data.x[masked_node_indices][:, 0]
82
+
83
+ # mask nodes' features
84
+ for node_idx in masked_node_indices:
85
+ batched_data.x[node_idx] = torch.tensor([self.num_atom_type - 1] + [0] * (batched_data.x.shape[1] - 1))
86
+
87
+ # generate predictions
88
+ h_node, _ = self.graph_encoder(batched_data)
89
+ h_rep = self.pool(h_node, batched_data.batch)
90
+ batched_data.x = h_node
91
+ prediction_class = self.predictor(batched_data)[masked_node_indices]
92
+
93
+ # target_class = batched_data.y.to(torch.float32)
94
+ loss_class = class_criterion(prediction_class.to(torch.float32), batched_data.y.long())
95
+
96
+ fingerprint_loss = get_fingerprint_loss(batched_data.smiles, h_rep)
97
+
98
+ loss = self.lw_rec * loss_class + (1 - self.lw_rec) * fingerprint_loss
99
+
100
+ return loss
101
+
102
+ def forward(self, batched_data):
103
+ h_node, _ = self.graph_encoder(batched_data)
104
+ h_rep = self.pool(h_node, batched_data.batch)
105
+ return {"graph": h_rep, "node": h_node}
106
+
107
+ class GNN_Decoder(torch.nn.Module):
108
+ def __init__(self, hidden_size, out_dim, JK = "last", gnn_name = "gin"):
109
+ super().__init__()
110
+ if gnn_name == 'gin':
111
+ self.conv = GINConv(hidden_size)
112
+ elif gnn_name == 'gcn':
113
+ self.conv = GCNConv(hidden_size)
114
+ else:
115
+ raise ValueError('Undefined GNN type called {}'.format(gnn_name))
116
+ self.dec_token = torch.nn.Parameter(torch.zeros([1, hidden_size]))
117
+ self.enc_to_dec = torch.nn.Linear(hidden_size, hidden_size, bias=False)
118
+ self.activation = torch.nn.PReLU()
119
+ self.out = torch.nn.Linear(hidden_size, out_dim)
120
+
121
+ def forward(self, batched_data):
122
+ x, edge_index, edge_attr, batch = batched_data.x, batched_data.edge_index, batched_data.edge_attr, batched_data.batch
123
+ masked_node_indices = batched_data.masked_node_indices
124
+
125
+ x = self.activation(x)
126
+ x = self.enc_to_dec(x)
127
+ x[masked_node_indices] = self.dec_token.detach().expand(len(masked_node_indices), -1)
128
+
129
+ x = self.conv(x, edge_index, edge_attr)
130
+ out = self.out(x)
131
+ return out