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,307 @@
1
+ import os
2
+ import numpy as np
3
+ from tqdm import tqdm
4
+ from typing import Optional, Union, Dict, Any, Tuple, List, Callable, Literal, Type
5
+ import warnings
6
+ from dataclasses import dataclass, field
7
+
8
+ import torch
9
+ from torch_geometric.loader import DataLoader
10
+ from torch_geometric.data import Data
11
+
12
+ from .model import GNN
13
+ from ..constant import GNN_ENCODER_MODELS, GNN_ENCODER_READOUTS, GNN_ENCODER_PARAMS
14
+ from ...base import BaseMolecularEncoder
15
+ from ...utils import graph_from_smiles
16
+
17
+ ALLOWABLE_ENCODER_MODELS = GNN_ENCODER_MODELS
18
+ ALLOWABLE_ENCODER_READOUTS = GNN_ENCODER_READOUTS
19
+
20
+ @dataclass
21
+ class MoamaMolecularEncoder(BaseMolecularEncoder):
22
+ """This encoder implements Motif-aware Attribute Masking for Molecular Graph Pre-training.
23
+
24
+ References
25
+ ----------
26
+ - Paper: https://openreview.net/forum?id=uqPnesiGGi
27
+ - Code: https://github.com/einae-nd/MoAMa-dev
28
+
29
+ Parameters
30
+ ----------
31
+ mask_rate : float, default=0.15
32
+ Fraction of nodes to mask in each graph
33
+ lw_rec : float, default=0.5
34
+ Weight balancing between reconstruction loss and fingerprint loss.
35
+ Higher values emphasize reconstruction, lower values emphasize fingerprint matching.
36
+ num_layer : int, default=5
37
+ Number of GNN layers.
38
+ hidden_size : int, default=300
39
+ Dimension of hidden node features.
40
+ drop_ratio : float, default=0.5
41
+ Dropout probability.
42
+ norm_layer : str, default="batch_norm"
43
+ Type of normalization layer to use. One of ["batch_norm", "layer_norm", "instance_norm", "graph_norm", "size_norm", "pair_norm"].
44
+ encoder_type : str, default="gin-virtual"
45
+ Type of GNN architecture to use. One of ["gin-virtual", "gcn-virtual", "gin", "gcn"].
46
+ readout : str, default="sum"
47
+ Method for aggregating node features to obtain graph-level representations. One of ["sum", "mean", "max"].
48
+ batch_size : int, default=128
49
+ Number of samples per batch for training.
50
+ epochs : int, default=500
51
+ Maximum number of training epochs.
52
+ learning_rate : float, default=0.001
53
+ Learning rate for optimizer.
54
+ grad_clip_value : float, optional
55
+ Maximum norm of gradients for gradient clipping.
56
+ weight_decay : float, default=0.0
57
+ L2 regularization strength.
58
+ use_lr_scheduler : bool, default=False
59
+ Whether to use a learning rate scheduler.
60
+ scheduler_factor : float, default=0.5
61
+ Factor by which to reduce the learning rate when plateau is detected.
62
+ scheduler_patience : int, default=5
63
+ Number of epochs with no improvement after which learning rate will be reduced.
64
+ verbose : bool, default=False
65
+ Whether to print progress information during training.
66
+ model_name : str, default="MoamaMolecularEncoder"
67
+ Name of the encoder model.
68
+ """
69
+ # Pretraining task parameters
70
+ mask_rate: float = 0.15
71
+ lw_rec: float = 0.5
72
+
73
+ # Model parameters
74
+ num_layer: int = 5
75
+ hidden_size: int = 300
76
+ drop_ratio: float = 0.5
77
+ norm_layer: str = "batch_norm"
78
+
79
+ encoder_type: str = "gin-virtual"
80
+ readout: str = "sum"
81
+
82
+ # Training parameters
83
+ batch_size: int = 128
84
+ epochs: int = 500
85
+ learning_rate: float = 0.001
86
+ grad_clip_value: Optional[float] = None
87
+ weight_decay: float = 0.0
88
+
89
+ # Scheduler parameters
90
+ use_lr_scheduler: bool = False
91
+ scheduler_factor: float = 0.5
92
+ scheduler_patience: int = 5
93
+
94
+ # Other parameters
95
+ verbose: bool = False
96
+ model_name: str = "MoamaMolecularEncoder"
97
+
98
+ # Non-init fields
99
+ fitting_loss: List[float] = field(default_factory=list, init=False)
100
+ fitting_epoch: int = field(default=0, init=False)
101
+ model_class: Type[GNN] = field(default=GNN, init=False)
102
+
103
+ def __post_init__(self):
104
+ """Initialize the model after dataclass initialization."""
105
+ super().__post_init__()
106
+ if self.encoder_type not in ALLOWABLE_ENCODER_MODELS:
107
+ raise ValueError(f"Invalid encoder_model: {self.encoder_type}. Currently only {ALLOWABLE_ENCODER_MODELS} are supported.")
108
+ if self.readout not in ALLOWABLE_ENCODER_READOUTS:
109
+ raise ValueError(f"Invalid encoder_readout: {self.readout}. Currently only {ALLOWABLE_ENCODER_READOUTS} are supported.")
110
+
111
+ @staticmethod
112
+ def _get_param_names() -> List[str]:
113
+ """Get parameter names for the estimator.
114
+
115
+ Returns
116
+ -------
117
+ List[str]
118
+ List of parameter names that can be used for model configuration.
119
+ """
120
+ return ["mask_rate", "lw_rec"] + GNN_ENCODER_PARAMS.copy()
121
+
122
+ def _get_model_params(self, checkpoint: Optional[Dict] = None) -> Dict[str, Any]:
123
+ params = {
124
+ "num_layer": self.num_layer,
125
+ "hidden_size": self.hidden_size,
126
+ "encoder_type": self.encoder_type,
127
+ "drop_ratio": self.drop_ratio,
128
+ "norm_layer": self.norm_layer,
129
+ "readout": self.readout,
130
+ "mask_rate": self.mask_rate,
131
+ "lw_rec": self.lw_rec
132
+ }
133
+
134
+ if checkpoint is not None:
135
+ if "hyperparameters" not in checkpoint:
136
+ raise ValueError("Checkpoint missing 'hyperparameters' key")
137
+ params = {k: checkpoint["hyperparameters"].get(k, v) for k, v in params.items()}
138
+
139
+ return params
140
+
141
+ def _convert_to_pytorch_data(self, X):
142
+ """Convert numpy arrays to PyTorch Geometric data format.
143
+ """
144
+ if self.verbose:
145
+ iterator = tqdm(enumerate(X), desc="Converting molecules to graphs", total=len(X))
146
+ else:
147
+ iterator = enumerate(X)
148
+
149
+ pyg_graph_list = []
150
+ for idx, smiles_or_mol in iterator:
151
+ graph = graph_from_smiles(smiles_or_mol, None)
152
+ g = Data()
153
+ g.num_nodes = graph["num_nodes"]
154
+ g.edge_index = torch.from_numpy(graph["edge_index"])
155
+
156
+ del graph["num_nodes"]
157
+ del graph["edge_index"]
158
+
159
+ if graph["edge_feat"] is not None:
160
+ g.edge_attr = torch.from_numpy(graph["edge_feat"])
161
+ del graph["edge_feat"]
162
+
163
+ if graph["node_feat"] is not None:
164
+ g.x = torch.from_numpy(graph["node_feat"])
165
+ del graph["node_feat"]
166
+
167
+ pyg_graph_list.append(g)
168
+
169
+ return pyg_graph_list
170
+
171
+ def _setup_optimizers(self) -> Tuple[torch.optim.Optimizer, Optional[Any]]:
172
+ """Setup optimization components including optimizer and learning rate scheduler.
173
+ """
174
+
175
+ optimizer = torch.optim.Adam(self.model.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
176
+ scheduler = None
177
+ if self.use_lr_scheduler:
178
+ scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(
179
+ optimizer,
180
+ mode="min",
181
+ factor=self.scheduler_factor,
182
+ patience=self.scheduler_patience,
183
+ min_lr=1e-6,
184
+ cooldown=0,
185
+ eps=1e-8,
186
+ )
187
+
188
+ return optimizer, scheduler
189
+
190
+ def fit(
191
+ self,
192
+ X_train: List[str],
193
+ ) -> "MoamaMolecularEncoder":
194
+ """Fit the model to the training data with optional validation set.
195
+
196
+ Parameters
197
+ ----------
198
+ X_train : List[str]
199
+ Training set input molecular structures as SMILES strings
200
+ Returns
201
+ -------
202
+ self : MoamaMolecularEncoder
203
+ Fitted estimator
204
+ """
205
+
206
+ self._initialize_model(self.model_class)
207
+ self.model.initialize_parameters()
208
+ optimizer, scheduler = self._setup_optimizers()
209
+
210
+ # Prepare datasets and loaders
211
+ smiles = X_train
212
+ X_train, _ = self._validate_inputs(X_train, return_rdkit_mol=True)
213
+ train_dataset = self._convert_to_pytorch_data(X_train)
214
+ for i in range(len(X_train)):
215
+ train_dataset[i].smiles = smiles[i]
216
+ train_loader = DataLoader(
217
+ train_dataset,
218
+ batch_size=self.batch_size,
219
+ shuffle=True,
220
+ num_workers=0
221
+ )
222
+ self.fitting_loss = []
223
+
224
+ for epoch in range(self.epochs):
225
+ # Training phase
226
+ train_losses = self._train_epoch(train_loader, optimizer, epoch)
227
+ self.fitting_loss.append(np.mean(train_losses))
228
+ if scheduler:
229
+ scheduler.step(np.mean(train_losses))
230
+
231
+ self.fitting_epoch = epoch
232
+ self.is_fitted_ = True
233
+ return self
234
+
235
+ def _train_epoch(self, train_loader, optimizer, epoch):
236
+ """Training logic for one epoch.
237
+
238
+ Args:
239
+ train_loader: DataLoader containing training data
240
+ optimizer: Optimizer instance for model parameter updates
241
+
242
+ Returns:
243
+ list: List of loss values for each training step
244
+ """
245
+ self.model.train()
246
+ losses = []
247
+
248
+ iterator = (
249
+ tqdm(train_loader, desc="Training", leave=False)
250
+ if self.verbose
251
+ else train_loader
252
+ )
253
+
254
+ for batch in iterator:
255
+ batch = batch.to(self.device)
256
+ optimizer.zero_grad()
257
+ loss = self.model.compute_loss(batch)
258
+ loss.backward()
259
+ if self.grad_clip_value is not None:
260
+ torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.grad_clip_value)
261
+ optimizer.step()
262
+ losses.append(loss.item())
263
+
264
+ # Update progress bar if using tqdm
265
+ if self.verbose:
266
+ iterator.set_postfix({"Epoch": f"{epoch}", "Loss": f"{loss.item():.4f}"})
267
+
268
+ return losses
269
+
270
+ def encode(self, X: List[str], return_type: Literal["np", "pt"] = "pt") -> Union[np.ndarray, torch.Tensor]:
271
+ """Encode molecules into vector representations.
272
+
273
+ Parameters
274
+ ----------
275
+ X : List[str]
276
+ List of SMILES strings
277
+ return_type : Literal["np", "pt"], default="pt"
278
+ Return type of the representations
279
+
280
+ Returns
281
+ -------
282
+ representations : ndarray or torch.Tensor
283
+ Molecular representations
284
+ """
285
+ self._check_is_fitted()
286
+
287
+ # Convert to PyTorch Geometric format and create loader
288
+ X, _ = self._validate_inputs(X, return_rdkit_mol=True)
289
+ dataset = self._convert_to_pytorch_data(X)
290
+ loader = DataLoader(dataset, batch_size=self.batch_size, shuffle=False)
291
+
292
+ if self.model is None:
293
+ raise RuntimeError("Model not initialized")
294
+
295
+ # Generate encodings
296
+ self.model = self.model.to(self.device)
297
+ self.model.eval()
298
+ encodings = []
299
+ with torch.no_grad():
300
+ for batch in tqdm(loader, disable=not self.verbose):
301
+ batch = batch.to(self.device)
302
+ out = self.model(batch)
303
+ encodings.append(out["graph"].cpu())
304
+
305
+ # Concatenate and convert to requested format
306
+ encodings = torch.cat(encodings, dim=0)
307
+ return encodings if return_type == "pt" else encodings.numpy()
@@ -0,0 +1,143 @@
1
+ import torch
2
+ import random
3
+ import torch.nn as nn
4
+ from rdkit import Chem, DataStructs
5
+ from rdkit.Chem import BRICS
6
+
7
+ def get_fingerprint_loss(smiles, h_rep):
8
+ cos = nn.CosineSimilarity(dim=0, eps=1e-6)
9
+ fingerprint_list = [Chem.RDKFingerprint(Chem.MolFromSmiles(x)) for x in smiles]
10
+ fingerprint_loss = 0
11
+
12
+ for i in range(len(h_rep)):
13
+ for j in range(len(h_rep)-i-1):
14
+ finger_sim = DataStructs.FingerprintSimilarity(fingerprint_list[i], fingerprint_list[i+j+1]) #* 10
15
+ emb_sim = (cos(h_rep[i], h_rep[i+j+1]) + 1)/2
16
+ fingerprint_loss += (finger_sim - emb_sim)**2
17
+
18
+ return fingerprint_loss
19
+
20
+ def get_mask_indices(batched_data, mask_rate=0.15):
21
+ masked_node_indices_full = list()
22
+ offset = 0
23
+
24
+ for i in range(len(batched_data)):
25
+ masked_node_indices = list()
26
+ data = batched_data[i]
27
+ mol = Chem.MolFromSmiles(data.smiles)
28
+ motifs_list, edges = get_motifs_edges(mol)
29
+ random.shuffle(motifs_list)
30
+ num_atoms = mol.GetNumAtoms()
31
+ sample_size = int(num_atoms * mask_rate + 1)
32
+
33
+ for motif in motifs_list:
34
+ edge_index_mask = sum(data.edge_index[0]==x for x in motif).bool()
35
+ neigh = torch.masked_select(data.edge_index[1], edge_index_mask)
36
+
37
+ if any(x in neigh for x in masked_node_indices):
38
+ continue
39
+
40
+ if len(masked_node_indices) + len(motif) > sample_size:
41
+ break
42
+ if len(motif) > 9:
43
+ del motif[5::6]
44
+ for atom in motif:
45
+ masked_node_indices.append(atom + offset)
46
+
47
+ atom_candidates = [x for x in range(0, num_atoms) if x not in masked_node_indices]
48
+ random.shuffle(atom_candidates)
49
+ if len(masked_node_indices) < sample_size:
50
+ for atom in atom_candidates[:sample_size-len(masked_node_indices)]:
51
+ masked_node_indices.append(atom + offset)
52
+
53
+ offset += num_atoms
54
+
55
+ masked_node_indices_full += masked_node_indices
56
+
57
+ return masked_node_indices_full
58
+
59
+ def get_motifs_edges(data):
60
+ Chem.SanitizeMol(data)
61
+ motifs, edges = brics_decomp(data)
62
+ return motifs, edges
63
+
64
+ def brics_decomp(mol):
65
+ n_atoms = mol.GetNumAtoms()
66
+ if n_atoms == 1:
67
+ return [[0]], []
68
+
69
+ cliques = []
70
+ breaks = []
71
+ for bond in mol.GetBonds():
72
+ a1 = bond.GetBeginAtom().GetIdx()
73
+ a2 = bond.GetEndAtom().GetIdx()
74
+ cliques.append([a1, a2])
75
+
76
+ res = list(BRICS.FindBRICSBonds(mol))
77
+ if len(res) == 0:
78
+ return [list(range(n_atoms))], []
79
+ else:
80
+ for bond in res:
81
+ if [bond[0][0], bond[0][1]] in cliques:
82
+ cliques.remove([bond[0][0], bond[0][1]])
83
+ else:
84
+ cliques.remove([bond[0][1], bond[0][0]])
85
+ cliques.append([bond[0][0]])
86
+ cliques.append([bond[0][1]])
87
+
88
+ # break bonds between rings and non-ring atoms
89
+ for c in cliques:
90
+ if len(c) > 1:
91
+ if mol.GetAtomWithIdx(c[0]).IsInRing() and not mol.GetAtomWithIdx(c[1]).IsInRing():
92
+ cliques.remove(c)
93
+ cliques.append([c[1]])
94
+ breaks.append(c)
95
+ if mol.GetAtomWithIdx(c[1]).IsInRing() and not mol.GetAtomWithIdx(c[0]).IsInRing():
96
+ cliques.remove(c)
97
+ cliques.append([c[0]])
98
+ breaks.append(c)
99
+
100
+ # select atoms at intersections as motif
101
+ for atom in mol.GetAtoms():
102
+ if len(atom.GetNeighbors()) > 2 and not atom.IsInRing():
103
+ cliques.append([atom.GetIdx()])
104
+ for nei in atom.GetNeighbors():
105
+ if [nei.GetIdx(), atom.GetIdx()] in cliques:
106
+ cliques.remove([nei.GetIdx(), atom.GetIdx()])
107
+ breaks.append([nei.GetIdx(), atom.GetIdx()])
108
+ elif [atom.GetIdx(), nei.GetIdx()] in cliques:
109
+ cliques.remove([atom.GetIdx(), nei.GetIdx()])
110
+ breaks.append([atom.GetIdx(), nei.GetIdx()])
111
+ cliques.append([nei.GetIdx()])
112
+
113
+ # merge cliques
114
+ for c in range(len(cliques) - 1):
115
+ if c >= len(cliques):
116
+ break
117
+ for k in range(c + 1, len(cliques)):
118
+ if k >= len(cliques):
119
+ break
120
+ if len(set(cliques[c]) & set(cliques[k])) > 0:
121
+ cliques[c] = list(set(cliques[c]) | set(cliques[k]))
122
+ cliques[k] = []
123
+ cliques = [c for c in cliques if len(c) > 0]
124
+ cliques = [c for c in cliques if len(c) > 0]
125
+
126
+ # edges
127
+ edges = []
128
+ for bond in res:
129
+ for c in range(len(cliques)):
130
+ if bond[0][0] in cliques[c]:
131
+ c1 = c
132
+ if bond[0][1] in cliques[c]:
133
+ c2 = c
134
+ edges.append((c1, c2))
135
+ for bond in breaks:
136
+ for c in range(len(cliques)):
137
+ if bond[0] in cliques[c]:
138
+ c1 = c
139
+ if bond[1] in cliques[c]:
140
+ c2 = c
141
+ edges.append((c1, c2))
142
+
143
+ return cliques, edges
@@ -0,0 +1,3 @@
1
+ from .modeling_pretrained import HFPretrainedMolecularEncoder
2
+
3
+ __all__ = ['HFPretrainedMolecularEncoder']