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,360 @@
1
+ import math
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+ from torch.nn.modules.dropout import Dropout
6
+ from torch.nn.modules.linear import Linear
7
+ from torch.nn.modules.normalization import LayerNorm
8
+ from torch.nn import functional as F
9
+ from torch import Tensor
10
+
11
+ from .utils import PlaceHolder
12
+ from .diffusion import assert_correctly_masked
13
+
14
+ class GraphTransformer(nn.Module):
15
+ def __init__(self, num_layer: int, input_dim_X: int, input_dim_E: int, input_dim_y: int, hidden_size_X: int, hidden_size_E: int, hidden_size_y: int, n_head: int,dropout: float):
16
+ super().__init__()
17
+ self.num_layer = num_layer
18
+ self.out_dim_X = input_dim_X
19
+ self.out_dim_E = input_dim_E
20
+ self.out_dim_y = input_dim_y
21
+ act_fn_in = nn.ReLU()
22
+ act_fn_out = nn.ReLU()
23
+
24
+ self.mlp_in_X = nn.Sequential(nn.Linear(input_dim_X, hidden_size_X), act_fn_in,
25
+ nn.Linear(hidden_size_X, hidden_size_X), act_fn_in)
26
+
27
+ self.mlp_in_E = nn.Sequential(nn.Linear(input_dim_E, hidden_size_E), act_fn_in,
28
+ nn.Linear(hidden_size_E, hidden_size_E), act_fn_in)
29
+
30
+ self.mlp_in_y = nn.Sequential(nn.Linear(input_dim_y, hidden_size_y), act_fn_in,
31
+ nn.Linear(hidden_size_y, hidden_size_y), act_fn_in)
32
+
33
+ self.tf_layers = nn.ModuleList([XEyTransformerLayer(dx=hidden_size_X,
34
+ de=hidden_size_E,
35
+ dy=hidden_size_y,
36
+ n_head=n_head,
37
+ dim_ffX=hidden_size_X,
38
+ dim_ffE=hidden_size_E,
39
+ dropout=dropout)
40
+ for i in range(num_layer)])
41
+
42
+ self.mlp_out_X = nn.Sequential(nn.Linear(hidden_size_X, hidden_size_X), act_fn_out,
43
+ nn.Linear(hidden_size_X, self.out_dim_X))
44
+
45
+ self.mlp_out_E = nn.Sequential(nn.Linear(hidden_size_E, hidden_size_E), act_fn_out,
46
+ nn.Linear(hidden_size_E, self.out_dim_E))
47
+
48
+ self.mlp_out_y = nn.Sequential(nn.Linear(hidden_size_y, hidden_size_y), act_fn_out,
49
+ nn.Linear(hidden_size_y, self.out_dim_y))
50
+
51
+ def initialize_parameters(self, seed=None):
52
+ if seed is not None:
53
+ torch.manual_seed(seed)
54
+
55
+ # Initialize transformer layers:
56
+ def _basic_init(module):
57
+ if isinstance(module, nn.Linear):
58
+ torch.nn.init.xavier_uniform_(module.weight)
59
+ if module.bias is not None:
60
+ nn.init.constant_(module.bias, 0)
61
+
62
+ self.apply(_basic_init)
63
+
64
+ def compute_loss(self, noisy_data, true_X, true_E, lw_X, lw_E):
65
+ pred = self.forward(noisy_data)
66
+
67
+ # Reshape predictions and targets
68
+ true_X = torch.reshape(true_X, (-1, true_X.size(-1))) # (bs * n, dx)
69
+ true_E = torch.reshape(true_E, (-1, true_E.size(-1))) # (bs * n * n, de)
70
+ masked_pred_X = torch.reshape(pred.X, (-1, pred.X.size(-1))) # (bs * n, dx)
71
+ masked_pred_E = torch.reshape(pred.E, (-1, pred.E.size(-1))) # (bs * n * n, de)
72
+
73
+ # Remove masked rows
74
+ mask_X = (true_X != 0.).any(dim=-1)
75
+ mask_E = (true_E != 0.).any(dim=-1)
76
+
77
+ flat_true_X = true_X[mask_X, :]
78
+ flat_pred_X = masked_pred_X[mask_X, :]
79
+ flat_true_E = true_E[mask_E, :]
80
+ flat_pred_E = masked_pred_E[mask_E, :]
81
+
82
+ # Calculate node and edge losses using cross entropy
83
+ loss_X = F.cross_entropy(flat_pred_X, torch.argmax(flat_true_X, dim=-1)) if true_X.numel() > 0 else 0.0
84
+ loss_E = F.cross_entropy(flat_pred_E, torch.argmax(flat_true_E, dim=-1)) if true_E.numel() > 0 else 0.0
85
+ loss = lw_X * loss_X + lw_E * loss_E
86
+
87
+ return loss, loss_X, loss_E
88
+
89
+ def forward(self, noisy_data):
90
+ X, E, y, node_mask = noisy_data['X_t'].float(), noisy_data['E_t'].float(), noisy_data['y_t'].float(), noisy_data['node_mask']
91
+ bs, n = X.shape[0], X.shape[1]
92
+
93
+ diag_mask = torch.eye(n)
94
+ diag_mask = ~diag_mask.type_as(E).bool()
95
+ diag_mask = diag_mask.unsqueeze(0).unsqueeze(-1).expand(bs, -1, -1, -1)
96
+
97
+ X_to_out = X[..., :self.out_dim_X]
98
+ E_to_out = E[..., :self.out_dim_E]
99
+ y_to_out = y[..., :self.out_dim_y]
100
+
101
+ new_E = self.mlp_in_E(E)
102
+ new_E = (new_E + new_E.transpose(1, 2)) / 2
103
+ after_in = PlaceHolder(X=self.mlp_in_X(X), E=new_E, y=self.mlp_in_y(y)).mask(node_mask)
104
+ X, E, y = after_in.X, after_in.E, after_in.y
105
+
106
+ for layer in self.tf_layers:
107
+ X, E, y = layer(X, E, y, node_mask)
108
+
109
+ X = self.mlp_out_X(X)
110
+ E = self.mlp_out_E(E)
111
+ y = self.mlp_out_y(y)
112
+
113
+ X = (X + X_to_out)
114
+ E = (E + E_to_out) * diag_mask
115
+ y = y + y_to_out
116
+
117
+ E = 1/2 * (E + torch.transpose(E, 1, 2))
118
+
119
+ return PlaceHolder(X=X, E=E, y=y).mask(node_mask)
120
+
121
+ class XEyTransformerLayer(nn.Module):
122
+ """ Transformer that updates node, edge and global features
123
+ d_x: node features
124
+ d_e: edge features
125
+ dz : global features
126
+ n_head: the number of heads in the multi_head_attention
127
+ dim_feedforward: the dimension of the feedforward network model after self-attention
128
+ dropout: dropout probablility. 0 to disable
129
+ layer_norm_eps: eps value in layer normalizations.
130
+ """
131
+ def __init__(self, dx: int, de: int, dy: int, n_head: int, dim_ffX: int = 2048,
132
+ dim_ffE: int = 128, dim_ffy: int = 2048, dropout: float = 0.1,
133
+ layer_norm_eps: float = 1e-5, device=None, dtype=None) -> None:
134
+ kw = {'device': device, 'dtype': dtype}
135
+ super().__init__()
136
+
137
+ self.self_attn = NodeEdgeBlock(dx, de, dy, n_head, **kw)
138
+
139
+ self.linX1 = Linear(dx, dim_ffX, **kw)
140
+ self.linX2 = Linear(dim_ffX, dx, **kw)
141
+ self.normX1 = LayerNorm(dx, eps=layer_norm_eps, **kw)
142
+ self.normX2 = LayerNorm(dx, eps=layer_norm_eps, **kw)
143
+ self.dropoutX1 = Dropout(dropout)
144
+ self.dropoutX2 = Dropout(dropout)
145
+ self.dropoutX3 = Dropout(dropout)
146
+
147
+ self.linE1 = Linear(de, dim_ffE, **kw)
148
+ self.linE2 = Linear(dim_ffE, de, **kw)
149
+ self.normE1 = LayerNorm(de, eps=layer_norm_eps, **kw)
150
+ self.normE2 = LayerNorm(de, eps=layer_norm_eps, **kw)
151
+ self.dropoutE1 = Dropout(dropout)
152
+ self.dropoutE2 = Dropout(dropout)
153
+ self.dropoutE3 = Dropout(dropout)
154
+
155
+ self.lin_y1 = Linear(dy, dim_ffy, **kw)
156
+ self.lin_y2 = Linear(dim_ffy, dy, **kw)
157
+ self.norm_y1 = LayerNorm(dy, eps=layer_norm_eps, **kw)
158
+ self.norm_y2 = LayerNorm(dy, eps=layer_norm_eps, **kw)
159
+ self.dropout_y1 = Dropout(dropout)
160
+ self.dropout_y2 = Dropout(dropout)
161
+ self.dropout_y3 = Dropout(dropout)
162
+
163
+ self.activation = F.relu
164
+
165
+ def forward(self, X: Tensor, E: Tensor, y, node_mask: Tensor):
166
+ """ Pass the input through the encoder layer.
167
+ X: (bs, n, d)
168
+ E: (bs, n, n, d)
169
+ y: (bs, dy)
170
+ node_mask: (bs, n) Mask for the src keys per batch (optional)
171
+ Output: newX, newE, new_y with the same shape.
172
+ """
173
+
174
+ newX, newE, new_y = self.self_attn(X, E, y, node_mask=node_mask)
175
+
176
+ newX_d = self.dropoutX1(newX)
177
+ X = self.normX1(X + newX_d)
178
+
179
+ newE_d = self.dropoutE1(newE)
180
+ E = self.normE1(E + newE_d)
181
+
182
+ new_y_d = self.dropout_y1(new_y)
183
+ y = self.norm_y1(y + new_y_d)
184
+
185
+ ff_outputX = self.linX2(self.dropoutX2(self.activation(self.linX1(X))))
186
+ ff_outputX = self.dropoutX3(ff_outputX)
187
+ X = self.normX2(X + ff_outputX)
188
+
189
+ ff_outputE = self.linE2(self.dropoutE2(self.activation(self.linE1(E))))
190
+ ff_outputE = self.dropoutE3(ff_outputE)
191
+ E = self.normE2(E + ff_outputE)
192
+
193
+ ff_output_y = self.lin_y2(self.dropout_y2(self.activation(self.lin_y1(y))))
194
+ ff_output_y = self.dropout_y3(ff_output_y)
195
+ y = self.norm_y2(y + ff_output_y)
196
+
197
+ return X, E, y
198
+
199
+
200
+ class NodeEdgeBlock(nn.Module):
201
+ """ Self attention layer that also updates the representations on the edges. """
202
+ def __init__(self, dx, de, dy, n_head, **kwargs):
203
+ super().__init__()
204
+ assert dx % n_head == 0, f"dx: {dx} -- nhead: {n_head}"
205
+ self.dx = dx
206
+ self.de = de
207
+ self.dy = dy
208
+ self.df = int(dx / n_head)
209
+ self.n_head = n_head
210
+
211
+ # Attention
212
+ self.q = Linear(dx, dx)
213
+ self.k = Linear(dx, dx)
214
+ self.v = Linear(dx, dx)
215
+
216
+ # FiLM E to X
217
+ self.e_add = Linear(de, dx)
218
+ self.e_mul = Linear(de, dx)
219
+
220
+ # FiLM y to E
221
+ self.y_e_mul = Linear(dy, dx) # Warning: here it's dx and not de
222
+ self.y_e_add = Linear(dy, dx)
223
+
224
+ # FiLM y to X
225
+ self.y_x_mul = Linear(dy, dx)
226
+ self.y_x_add = Linear(dy, dx)
227
+
228
+ # Process y
229
+ self.y_y = Linear(dy, dy)
230
+ self.x_y = Xtoy(dx, dy)
231
+ self.e_y = Etoy(de, dy)
232
+
233
+ # Output layers
234
+ self.x_out = Linear(dx, dx)
235
+ self.e_out = Linear(dx, de)
236
+ self.y_out = nn.Sequential(nn.Linear(dy, dy), nn.ReLU(), nn.Linear(dy, dy))
237
+
238
+ def forward(self, X, E, y, node_mask):
239
+ """
240
+ :param X: bs, n, d node features
241
+ :param E: bs, n, n, d edge features
242
+ :param y: bs, dz global features
243
+ :param node_mask: bs, n
244
+ :return: newX, newE, new_y with the same shape.
245
+ """
246
+ bs, n, _ = X.shape
247
+ x_mask = node_mask.unsqueeze(-1) # bs, n, 1
248
+ e_mask1 = x_mask.unsqueeze(2) # bs, n, 1, 1
249
+ e_mask2 = x_mask.unsqueeze(1) # bs, 1, n, 1
250
+
251
+ # 1. Map X to keys and queries
252
+ Q = self.q(X) * x_mask # (bs, n, dx)
253
+ K = self.k(X) * x_mask # (bs, n, dx)
254
+ assert_correctly_masked(Q, x_mask)
255
+ # 2. Reshape to (bs, n, n_head, df) with dx = n_head * df
256
+
257
+ Q = Q.reshape((Q.size(0), Q.size(1), self.n_head, self.df))
258
+ K = K.reshape((K.size(0), K.size(1), self.n_head, self.df))
259
+
260
+ Q = Q.unsqueeze(2) # (bs, 1, n, n_head, df)
261
+ K = K.unsqueeze(1) # (bs, n, 1, n head, df)
262
+
263
+ # Compute unnormalized attentions. Y is (bs, n, n, n_head, df)
264
+ Y = Q * K
265
+ Y = Y / math.sqrt(Y.size(-1))
266
+ assert_correctly_masked(Y, (e_mask1 * e_mask2).unsqueeze(-1))
267
+
268
+ E1 = self.e_mul(E) * e_mask1 * e_mask2 # bs, n, n, dx
269
+ E1 = E1.reshape((E.size(0), E.size(1), E.size(2), self.n_head, self.df))
270
+
271
+ E2 = self.e_add(E) * e_mask1 * e_mask2 # bs, n, n, dx
272
+ E2 = E2.reshape((E.size(0), E.size(1), E.size(2), self.n_head, self.df))
273
+
274
+ # Incorporate edge features to the self attention scores.
275
+ Y = Y * (E1 + 1) + E2 # (bs, n, n, n_head, df)
276
+
277
+ # Incorporate y to E
278
+ newE = Y.flatten(start_dim=3) # bs, n, n, dx
279
+ ye1 = self.y_e_add(y).unsqueeze(1).unsqueeze(1) # bs, 1, 1, de
280
+ ye2 = self.y_e_mul(y).unsqueeze(1).unsqueeze(1)
281
+ newE = ye1 + (ye2 + 1) * newE
282
+
283
+ # Output E
284
+ newE = self.e_out(newE) * e_mask1 * e_mask2 # bs, n, n, de
285
+ assert_correctly_masked(newE, e_mask1 * e_mask2)
286
+
287
+ # Compute attentions. attn is still (bs, n, n, n_head, df)
288
+ softmax_mask = e_mask2.expand(-1, n, -1, self.n_head) # bs, 1, n, 1
289
+ attn = masked_softmax(Y, softmax_mask, dim=2) # bs, n, n, n_head
290
+
291
+ V = self.v(X) * x_mask # bs, n, dx
292
+ V = V.reshape((V.size(0), V.size(1), self.n_head, self.df))
293
+ V = V.unsqueeze(1) # (bs, 1, n, n_head, df)
294
+
295
+ # Compute weighted values
296
+ weighted_V = attn * V
297
+ weighted_V = weighted_V.sum(dim=2)
298
+
299
+ # Send output to input dim
300
+ weighted_V = weighted_V.flatten(start_dim=2) # bs, n, dx
301
+
302
+ # Incorporate y to X
303
+ yx1 = self.y_x_add(y).unsqueeze(1)
304
+ yx2 = self.y_x_mul(y).unsqueeze(1)
305
+ newX = yx1 + (yx2 + 1) * weighted_V
306
+
307
+ # Output X
308
+ newX = self.x_out(newX) * x_mask
309
+ assert_correctly_masked(newX, x_mask)
310
+
311
+ # Process y based on X axnd E
312
+ y = self.y_y(y)
313
+ e_y = self.e_y(E)
314
+ x_y = self.x_y(X)
315
+ new_y = y + x_y + e_y
316
+ new_y = self.y_out(new_y) # bs, dy
317
+
318
+ return newX, newE, new_y
319
+
320
+ class Xtoy(nn.Module):
321
+ def __init__(self, dx, dy):
322
+ """ Map node features to global features """
323
+ super().__init__()
324
+ self.lin = nn.Linear(4 * dx, dy)
325
+
326
+ def forward(self, X):
327
+ """ X: bs, n, dx. """
328
+ m = X.mean(dim=1)
329
+ mi = X.min(dim=1)[0]
330
+ ma = X.max(dim=1)[0]
331
+ std = X.std(dim=1)
332
+ z = torch.hstack((m, mi, ma, std))
333
+ out = self.lin(z)
334
+ return out
335
+
336
+
337
+ class Etoy(nn.Module):
338
+ def __init__(self, d, dy):
339
+ """ Map edge features to global features. """
340
+ super().__init__()
341
+ self.lin = nn.Linear(4 * d, dy)
342
+
343
+ def forward(self, E):
344
+ """ E: bs, n, n, de
345
+ Features relative to the diagonal of E could potentially be added.
346
+ """
347
+ m = E.mean(dim=(1, 2))
348
+ mi = E.min(dim=2)[0].min(dim=1)[0]
349
+ ma = E.max(dim=2)[0].max(dim=1)[0]
350
+ std = torch.std(E, dim=(1, 2))
351
+ z = torch.hstack((m, mi, ma, std))
352
+ out = self.lin(z)
353
+ return out
354
+
355
+ def masked_softmax(x, mask, **kwargs):
356
+ if mask.sum() == 0:
357
+ return x
358
+ x_masked = x.clone()
359
+ x_masked[mask == 0] = -float("inf")
360
+ return torch.softmax(x_masked, **kwargs)
@@ -0,0 +1,196 @@
1
+ import torch
2
+ import json
3
+ from rdkit import Chem
4
+ import numpy as np
5
+ from torch_geometric.utils import to_dense_adj, to_dense_batch, remove_self_loops
6
+
7
+ def to_dense(x, edge_index, edge_attr, batch, max_num_nodes=None):
8
+ X, node_mask = to_dense_batch(x=x, batch=batch, max_num_nodes=max_num_nodes)
9
+ edge_index, edge_attr = remove_self_loops(edge_index, edge_attr)
10
+ if max_num_nodes is None:
11
+ max_num_nodes = X.size(1)
12
+ E = to_dense_adj(edge_index=edge_index, batch=batch, edge_attr=edge_attr, max_num_nodes=max_num_nodes)
13
+ E = encode_no_edge(E)
14
+ return PlaceHolder(X=X, E=E, y=None), node_mask
15
+
16
+ def encode_no_edge(E):
17
+ assert len(E.shape) == 4
18
+ if E.shape[-1] == 0:
19
+ return E
20
+ no_edge = torch.sum(E, dim=3) == 0
21
+ first_elt = E[:, :, :, 0]
22
+ first_elt[no_edge] = 1
23
+ E[:, :, :, 0] = first_elt
24
+ diag = torch.eye(E.shape[1], dtype=torch.bool).unsqueeze(0).expand(E.shape[0], -1, -1)
25
+ E[diag] = 0
26
+ return E
27
+
28
+ class PlaceHolder:
29
+ def __init__(self, X, E, y):
30
+ self.X = X
31
+ self.E = E
32
+ self.y = y
33
+
34
+ def type_as(self, x: torch.Tensor, categorical: bool = False):
35
+ """ Changes the device and dtype of X, E, y. """
36
+ self.X = self.X.type_as(x)
37
+ self.E = self.E.type_as(x)
38
+ if categorical:
39
+ self.y = self.y.type_as(x)
40
+ return self
41
+
42
+ def mask(self, node_mask, collapse=False):
43
+ x_mask = node_mask.unsqueeze(-1) # bs, n, 1
44
+ e_mask1 = x_mask.unsqueeze(2) # bs, n, 1, 1
45
+ e_mask2 = x_mask.unsqueeze(1) # bs, 1, n, 1
46
+
47
+ if collapse:
48
+ self.X = torch.argmax(self.X, dim=-1)
49
+ self.E = torch.argmax(self.E, dim=-1)
50
+
51
+ self.X[node_mask == 0] = - 1
52
+ self.E[(e_mask1 * e_mask2).squeeze(-1) == 0] = - 1
53
+ else:
54
+ self.X = self.X * x_mask
55
+ self.E = self.E * e_mask1 * e_mask2
56
+ assert torch.allclose(self.E, torch.transpose(self.E, 1, 2))
57
+ return self
58
+
59
+ def compute_dataset_info(smiles_or_mol_list, cache_path=None):
60
+ pt = Chem.GetPeriodicTable()
61
+ atom_name_list = []
62
+ atom_count_list = []
63
+ for i in range(2, 119):
64
+ atom_name_list.append(pt.GetElementSymbol(i))
65
+ atom_count_list.append(0)
66
+ atom_name_list.append('*')
67
+ atom_count_list.append(0)
68
+
69
+ bond_count_list = [0, 0, 0, 0, 0]
70
+ bond_type_to_index = {Chem.BondType.SINGLE: 1, Chem.BondType.DOUBLE: 2, Chem.BondType.TRIPLE: 3, Chem.BondType.AROMATIC: 4}
71
+ tansition_E = np.zeros((118, 118, 5))
72
+
73
+ n_atom_list = []
74
+ n_bond_list = []
75
+ n_atoms_per_mol_count = {}
76
+ max_node = 0
77
+ for i, sms_or_mol in enumerate(smiles_or_mol_list):
78
+ if isinstance(sms_or_mol, str):
79
+ mol = Chem.MolFromSmiles(sms_or_mol)
80
+ else:
81
+ mol = sms_or_mol
82
+
83
+ n_atom = mol.GetNumHeavyAtoms()
84
+ n_bond = mol.GetNumBonds()
85
+ max_node = max(max_node, n_atom)
86
+
87
+ n_atom_list.append(n_atom)
88
+ n_bond_list.append(n_bond)
89
+
90
+ # Count atoms per molecule size
91
+ if n_atom in n_atoms_per_mol_count:
92
+ n_atoms_per_mol_count[n_atom] += 1
93
+ else:
94
+ n_atoms_per_mol_count[n_atom] = 1
95
+
96
+ cur_atom_count_arr = np.zeros(118)
97
+ for atom in mol.GetAtoms():
98
+ symbol = atom.GetSymbol()
99
+ if symbol == 'H':
100
+ continue
101
+ elif symbol == '*':
102
+ atom_count_list[-1] += 1
103
+ cur_atom_count_arr[-1] += 1
104
+ else:
105
+ atom_count_list[atom.GetAtomicNum()-2] += 1
106
+ cur_atom_count_arr[atom.GetAtomicNum()-2] += 1
107
+
108
+ tansition_E_temp = np.zeros((118, 118, 5))
109
+ for bond in mol.GetBonds():
110
+ start_atom, end_atom = bond.GetBeginAtom(), bond.GetEndAtom()
111
+ if start_atom.GetSymbol() == 'H' or end_atom.GetSymbol() == 'H':
112
+ continue
113
+
114
+ if start_atom.GetSymbol() == '*':
115
+ start_index = 117
116
+ else:
117
+ start_index = start_atom.GetAtomicNum() - 2
118
+ if end_atom.GetSymbol() == '*':
119
+ end_index = 117
120
+ else:
121
+ end_index = end_atom.GetAtomicNum() - 2
122
+
123
+ bond_type = bond.GetBondType()
124
+ bond_index = bond_type_to_index[bond_type]
125
+ bond_count_list[bond_index] += 2
126
+
127
+ # Create n_atoms_per_mol array with proper size
128
+ n_atoms_per_mol = [0] * (max_node + 1)
129
+ for n_atom, count in n_atoms_per_mol_count.items():
130
+ n_atoms_per_mol[n_atom] = count
131
+
132
+ n_atoms_per_mol = np.array(n_atoms_per_mol) / np.sum(n_atoms_per_mol)
133
+ n_atoms_per_mol = n_atoms_per_mol.tolist()
134
+
135
+ atom_count_list = np.array(atom_count_list) / np.sum(atom_count_list)
136
+ active_atoms = np.array(atom_name_list)[atom_count_list > 0]
137
+ active_atoms = active_atoms.tolist()
138
+ atom_count_list = atom_count_list.tolist()
139
+
140
+ bond_count_list = np.array(bond_count_list) / np.sum(bond_count_list)
141
+ bond_count_list = bond_count_list.tolist()
142
+
143
+
144
+ node_types = torch.Tensor(atom_count_list)
145
+ edge_types = torch.Tensor(bond_count_list)
146
+ active_index = (node_types > 0).nonzero().squeeze()
147
+
148
+ x_margins = node_types.float() / torch.sum(node_types)
149
+ x_margins = x_margins[active_index]
150
+ e_margins = edge_types.float() / torch.sum(edge_types)
151
+
152
+ num_nodes_dist = DistributionNodes(torch.Tensor(n_atoms_per_mol))
153
+
154
+ meta_dict = {
155
+ 'active_index': active_index,
156
+ 'x_margins': x_margins,
157
+ 'e_margins': e_margins,
158
+ 'atom_decoder': active_atoms,
159
+ 'num_nodes_dist': num_nodes_dist,
160
+ 'max_node': max_node,
161
+ }
162
+
163
+ if cache_path is not None:
164
+ with open(cache_path, "w") as f:
165
+ json.dump(meta_dict, f)
166
+
167
+ return meta_dict
168
+
169
+ class DistributionNodes:
170
+ def __init__(self, histogram):
171
+ """ Compute the distribution of the number of nodes in the dataset, and sample from this distribution.
172
+ historgram: dict. The keys are num_nodes, the values are counts
173
+ """
174
+
175
+ if type(histogram) == dict:
176
+ max_n_nodes = max(histogram.keys())
177
+ prob = torch.zeros(max_n_nodes + 1)
178
+ for num_nodes, count in histogram.items():
179
+ prob[num_nodes] = count
180
+ else:
181
+ prob = histogram
182
+
183
+ self.prob = prob / prob.sum()
184
+ self.m = torch.distributions.Categorical(prob)
185
+
186
+ def sample_n(self, n_samples, device):
187
+ idx = self.m.sample((n_samples,))
188
+ return idx.to(device)
189
+
190
+ def log_prob(self, batch_n_nodes):
191
+ assert len(batch_n_nodes.size()) == 1
192
+ p = self.prob.to(batch_n_nodes.device)
193
+
194
+ probas = p[batch_n_nodes]
195
+ log_p = torch.log(probas + 1e-30)
196
+ return log_p
@@ -0,0 +1,3 @@
1
+ from .modeling_gdss import GDSSMolecularGenerator
2
+
3
+ __all__ = ['GDSSMolecularGenerator']