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,167 @@
1
+ import torch
2
+ from torch.nn import Parameter
3
+ import torch.nn.functional as F
4
+
5
+ from .utils import mask_adjs, pow_tensor, mask_x
6
+ from .layers import DenseGCNConv, MLP, AttentionLayer
7
+
8
+ # def node_feature_to_matrix(x):
9
+ # """
10
+ # :param x: BS x N x F
11
+ # :return:
12
+ # x_pair: BS x N x N x 2F
13
+ # """
14
+ # x_b = x.unsqueeze(-2).expand(x.size(0), x.size(1), x.size(1), -1) # BS x N x N x F
15
+ # x_pair = torch.cat([x_b, x_b.transpose(1, 2)], dim=-1) # BS x N x N x 2F
16
+ # return x_pair
17
+
18
+ # class BaselineNetworkLayer(torch.nn.Module):
19
+
20
+ # def __init__(self, num_linears, conv_input_dim, conv_output_dim, input_dim, output_dim, batch_norm=False):
21
+
22
+ # super(BaselineNetworkLayer, self).__init__()
23
+
24
+ # self.convs = torch.nn.ModuleList()
25
+ # for _ in range(input_dim):
26
+ # self.convs.append(DenseGCNConv(conv_input_dim, conv_output_dim))
27
+ # self.hidden_dim = max(input_dim, output_dim)
28
+ # self.mlp_in_dim = input_dim + 2*conv_output_dim
29
+ # self.mlp = MLP(num_linears, self.mlp_in_dim, self.hidden_dim, output_dim,
30
+ # use_bn=False, activate_func=F.elu)
31
+ # self.multi_channel = MLP(2, input_dim*conv_output_dim, self.hidden_dim, conv_output_dim,
32
+ # use_bn=False, activate_func=F.elu)
33
+
34
+ # def forward(self, x, adj, flags):
35
+
36
+ # x_list = []
37
+ # for _ in range(len(self.convs)):
38
+ # _x = self.convs[_](x, adj[:,_,:,:])
39
+ # x_list.append(_x)
40
+ # x_out = mask_x(self.multi_channel(torch.cat(x_list, dim=-1)) , flags)
41
+ # x_out = torch.tanh(x_out)
42
+
43
+ # x_matrix = node_feature_to_matrix(x_out)
44
+ # mlp_in = torch.cat([x_matrix, adj.permute(0,2,3,1)], dim=-1)
45
+ # shape = mlp_in.shape
46
+ # mlp_out = self.mlp(mlp_in.view(-1, shape[-1]))
47
+ # _adj = mlp_out.view(shape[0], shape[1], shape[2], -1).permute(0,3,1,2)
48
+ # _adj = _adj + _adj.transpose(-1,-2)
49
+ # adj_out = mask_adjs(_adj, flags)
50
+
51
+ # return x_out, adj_out
52
+
53
+
54
+ # class BaselineNetwork(torch.nn.Module):
55
+
56
+ # def __init__(self, max_feat_num, max_node_num, nhid, num_layers, num_linears,
57
+ # c_init, c_hid, c_final, adim, num_heads=4, conv='GCN'):
58
+
59
+ # super(BaselineNetwork, self).__init__()
60
+
61
+ # self.nfeat = max_feat_num
62
+ # self.max_node_num = max_node_num
63
+ # self.nhid = nhid
64
+ # self.num_layers = num_layers
65
+ # self.num_linears = num_linears
66
+ # self.c_init = c_init
67
+ # self.c_hid = c_hid
68
+ # self.c_final = c_final
69
+
70
+ # self.layers = torch.nn.ModuleList()
71
+ # for _ in range(self.num_layers):
72
+ # if _==0:
73
+ # self.layers.append(BaselineNetworkLayer(self.num_linears, self.nfeat, self.nhid, self.c_init, self.c_hid))
74
+
75
+ # elif _==self.num_layers-1:
76
+ # self.layers.append(BaselineNetworkLayer(self.num_linears, self.nhid, self.nhid, self.c_hid, self.c_final))
77
+
78
+ # else:
79
+ # self.layers.append(BaselineNetworkLayer(self.num_linears, self.nhid, self.nhid, self.c_hid, self.c_hid))
80
+
81
+ # self.fdim = self.c_hid*(self.num_layers-1) + self.c_final + self.c_init
82
+ # self.final = MLP(num_layers=3, input_dim=self.fdim, hidden_dim=2*self.fdim, output_dim=1,
83
+ # use_bn=False, activate_func=F.elu)
84
+ # self.mask = torch.ones([self.max_node_num, self.max_node_num]) - torch.eye(self.max_node_num)
85
+ # self.mask.unsqueeze_(0)
86
+
87
+ # def forward(self, x, adj, flags=None):
88
+
89
+ # adjc = pow_tensor(adj, self.c_init)
90
+
91
+ # adj_list = [adjc]
92
+ # for _ in range(self.num_layers):
93
+
94
+ # x, adjc = self.layers[_](x, adjc, flags)
95
+ # adj_list.append(adjc)
96
+
97
+ # adjs = torch.cat(adj_list, dim=1).permute(0,2,3,1)
98
+ # out_shape = adjs.shape[:-1] # B x N x N
99
+ # score = self.final(adjs).view(*out_shape)
100
+
101
+ # self.mask = self.mask.to(score.device)
102
+ # score = score * self.mask
103
+
104
+ # score = mask_adjs(score, flags)
105
+
106
+ # return score
107
+
108
+
109
+ # class ScoreNetworkA(BaselineNetwork):
110
+ # def __init__(self, max_feat_num, max_node_num, nhid, num_layers, num_linears,
111
+ # c_init, c_hid, c_final, adim, num_heads=4, conv='GCN'):
112
+ # super(ScoreNetworkA, self).__init__(max_feat_num, max_node_num, nhid, num_layers, num_linears,
113
+ # c_init, c_hid, c_final, adim, num_heads=4, conv='GCN')
114
+ class ScoreNetworkA(torch.nn.Module):
115
+ def __init__(self, max_feat_num, max_node_num, nhid, num_layers, num_linears,
116
+ c_init, c_hid, c_final, adim, num_heads=4, conv='GCN'):
117
+ super(ScoreNetworkA, self).__init__()
118
+
119
+ self.adim = adim
120
+ self.num_heads = num_heads
121
+ self.conv = conv
122
+ self.num_layers = num_layers
123
+ self.num_linears = num_linears
124
+ self.nfeat = max_feat_num
125
+ self.nhid = nhid
126
+ self.c_init = c_init
127
+ self.c_hid = c_hid
128
+ self.c_final = c_final
129
+ self.max_node_num = max_node_num
130
+
131
+ self.layers = torch.nn.ModuleList()
132
+ for i in range(self.num_layers):
133
+ if i==0:
134
+ self.layers.append(AttentionLayer(self.num_linears, self.nfeat, self.nhid, self.nhid, self.c_init,
135
+ self.c_hid, self.num_heads, self.conv))
136
+ elif i==self.num_layers-1:
137
+ self.layers.append(AttentionLayer(self.num_linears, self.nhid, self.adim, self.nhid, self.c_hid,
138
+ self.c_final, self.num_heads, self.conv))
139
+ else:
140
+ self.layers.append(AttentionLayer(self.num_linears, self.nhid, self.adim, self.nhid, self.c_hid,
141
+ self.c_hid, self.num_heads, self.conv))
142
+
143
+ self.fdim = self.c_hid*(self.num_layers-1) + self.c_final + self.c_init
144
+ self.final = MLP(num_layers=3, input_dim=self.fdim, hidden_dim=2*self.fdim, output_dim=1,
145
+ use_bn=False, activate_func=F.elu)
146
+ self.mask = torch.ones([self.max_node_num, self.max_node_num]) - torch.eye(self.max_node_num)
147
+ self.mask.unsqueeze_(0)
148
+
149
+ def forward(self, x, adj, flags):
150
+
151
+ adjc = pow_tensor(adj, self.c_init)
152
+
153
+ adj_list = [adjc]
154
+ for i in range(self.num_layers):
155
+ x, adjc = self.layers[i](x, adjc, flags)
156
+ adj_list.append(adjc)
157
+
158
+ adjs = torch.cat(adj_list, dim=1).permute(0,2,3,1)
159
+ out_shape = adjs.shape[:-1] # B x N x N
160
+ score = self.final(adjs).view(*out_shape)
161
+
162
+ self.mask = self.mask.to(score.device)
163
+ score = score * self.mask
164
+
165
+ score = mask_adjs(score, flags)
166
+
167
+ return score
@@ -0,0 +1,85 @@
1
+ import torch
2
+ import torch.nn.functional as F
3
+
4
+ from .layers import DenseGCNConv, MLP, AttentionLayer
5
+ from .utils import mask_x, pow_tensor
6
+
7
+ class ScoreNetworkX(torch.nn.Module):
8
+ def __init__(self, max_feat_num, depth, nhid):
9
+ super(ScoreNetworkX, self).__init__()
10
+
11
+ self.nfeat = max_feat_num
12
+ self.depth = depth
13
+ self.nhid = nhid
14
+
15
+ self.layers = torch.nn.ModuleList()
16
+ for _ in range(self.depth):
17
+ if _ == 0:
18
+ self.layers.append(DenseGCNConv(self.nfeat, self.nhid))
19
+ else:
20
+ self.layers.append(DenseGCNConv(self.nhid, self.nhid))
21
+
22
+ self.fdim = self.nfeat + self.depth * self.nhid
23
+ self.final = MLP(num_layers=3, input_dim=self.fdim, hidden_dim=2*self.fdim, output_dim=self.nfeat,
24
+ use_bn=False, activate_func=F.elu)
25
+
26
+ self.activation = torch.tanh
27
+
28
+ def forward(self, x, adj, flags):
29
+
30
+ x_list = [x]
31
+ for _ in range(self.depth):
32
+ x = self.layers[_](x, adj)
33
+ x = self.activation(x)
34
+ x_list.append(x)
35
+
36
+ xs = torch.cat(x_list, dim=-1) # B x N x (F + num_layers x H)
37
+ out_shape = (adj.shape[0], adj.shape[1], -1)
38
+ x = self.final(xs).view(*out_shape)
39
+
40
+ x = mask_x(x, flags)
41
+
42
+ return x
43
+
44
+
45
+ # class ScoreNetworkX_GMH(torch.nn.Module):
46
+ # def __init__(self, max_feat_num, depth, nhid, num_linears,
47
+ # c_init, c_hid, c_final, adim, num_heads=4, conv='GCN'):
48
+ # super().__init__()
49
+
50
+ # self.depth = depth
51
+ # self.c_init = c_init
52
+
53
+ # self.layers = torch.nn.ModuleList()
54
+ # for _ in range(self.depth):
55
+ # if _ == 0:
56
+ # self.layers.append(AttentionLayer(num_linears, max_feat_num, nhid, nhid, c_init,
57
+ # c_hid, num_heads, conv))
58
+ # elif _ == self.depth - 1:
59
+ # self.layers.append(AttentionLayer(num_linears, nhid, adim, nhid, c_hid,
60
+ # c_final, num_heads, conv))
61
+ # else:
62
+ # self.layers.append(AttentionLayer(num_linears, nhid, adim, nhid, c_hid,
63
+ # c_hid, num_heads, conv))
64
+
65
+ # fdim = max_feat_num + depth * nhid
66
+ # self.final = MLP(num_layers=3, input_dim=fdim, hidden_dim=2*fdim, output_dim=max_feat_num,
67
+ # use_bn=False, activate_func=F.elu)
68
+
69
+ # self.activation = torch.tanh
70
+
71
+ # def forward(self, x, adj, flags):
72
+ # adjc = pow_tensor(adj, self.c_init)
73
+
74
+ # x_list = [x]
75
+ # for _ in range(self.depth):
76
+ # x, adjc = self.layers[_](x, adjc, flags)
77
+ # x = self.activation(x)
78
+ # x_list.append(x)
79
+
80
+ # xs = torch.cat(x_list, dim=-1) # B x N x (F + num_layers x H)
81
+ # out_shape = (adj.shape[0], adj.shape[1], -1)
82
+ # x = self.final(xs).view(*out_shape)
83
+ # x = mask_x(x, flags)
84
+
85
+ # return x
@@ -0,0 +1,240 @@
1
+ import json
2
+ import numpy as np
3
+ import torch
4
+ from torch_geometric.utils import to_dense_adj, to_dense_batch, remove_self_loops
5
+
6
+ from rdkit import Chem
7
+
8
+ def mask_x(x, flags):
9
+ if flags is None:
10
+ flags = torch.ones((x.shape[0], x.shape[1]), device=x.device)
11
+ return x * flags[:,:,None]
12
+
13
+ # -------- Mask batch of adjacency matrices with 0-1 flags tensor --------
14
+ def mask_adjs(adjs, flags):
15
+ """
16
+ :param adjs: B x N x N or B x C x N x N
17
+ :param flags: B x N
18
+ :return:
19
+ """
20
+ if flags is None:
21
+ flags = torch.ones((adjs.shape[0], adjs.shape[-1]), device=adjs.device)
22
+
23
+ if len(adjs.shape) == 4:
24
+ flags = flags.unsqueeze(1) # B x 1 x N
25
+ adjs = adjs * flags.unsqueeze(-1)
26
+ adjs = adjs * flags.unsqueeze(-2)
27
+ return adjs
28
+
29
+ def quantize_mol(adjs):
30
+ adjs[adjs >= 3.5] = 4
31
+ adjs[torch.bitwise_and(adjs >= 2.5, adjs < 3.5)] = 3
32
+ adjs[torch.bitwise_and(adjs >= 1.5, adjs < 2.5)] = 2
33
+ adjs[torch.bitwise_and(adjs >= 0.5, adjs < 1.5)] = 1
34
+ adjs[adjs < 0.5] = 0
35
+ adjs[torch.isnan(adjs)] = 0
36
+ return adjs.long()
37
+
38
+ # -------- Generate noise --------
39
+ def gen_noise(x, flags, sym=True):
40
+ z = torch.randn_like(x)
41
+ if sym:
42
+ z = z.triu(1)
43
+ z = z + z.transpose(-1, -2)
44
+ z = mask_adjs(z, flags)
45
+ else:
46
+ z = mask_x(z, flags)
47
+ return z
48
+
49
+ # -------- Create higher order adjacency matrices --------
50
+ def pow_tensor(x, cnum):
51
+ # x : B x N x N
52
+ x_ = x.clone()
53
+ xc = [x.unsqueeze(1)]
54
+ for _ in range(cnum-1):
55
+ x_ = torch.bmm(x_, x)
56
+ xc.append(x_.unsqueeze(1))
57
+ xc = torch.cat(xc, dim=1)
58
+
59
+ return xc
60
+
61
+ # -------- Common utils --------
62
+ def to_dense(x, edge_index, edge_attr, batch, max_num_nodes):
63
+ X, node_mask = to_dense_batch(x=x, batch=batch, max_num_nodes=max_num_nodes)
64
+ edge_index, edge_attr = remove_self_loops(edge_index, edge_attr)
65
+ E = to_dense_adj(
66
+ edge_index=edge_index,
67
+ batch=batch,
68
+ edge_attr=edge_attr,
69
+ max_num_nodes=max_num_nodes,
70
+ )
71
+ return X, E, node_mask
72
+
73
+ # class PlaceHolder:
74
+ # def __init__(self, X, E, y):
75
+ # self.X = X
76
+ # self.E = E
77
+ # self.y = y
78
+
79
+ # def type_as(self, x: torch.Tensor, categorical: bool = False):
80
+ # """ Changes the device and dtype of X, E, y. """
81
+ # self.X = self.X.type_as(x)
82
+ # self.E = self.E.type_as(x)
83
+ # if categorical:
84
+ # self.y = self.y.type_as(x)
85
+ # return self
86
+
87
+ # def mask(self, node_mask, collapse=False):
88
+ # x_mask = node_mask.unsqueeze(-1) # bs, n, 1
89
+ # e_mask1 = x_mask.unsqueeze(2) # bs, n, 1, 1
90
+ # e_mask2 = x_mask.unsqueeze(1) # bs, 1, n, 1
91
+
92
+ # if collapse:
93
+ # self.X = torch.argmax(self.X, dim=-1)
94
+ # self.E = torch.argmax(self.E, dim=-1)
95
+
96
+ # self.X[node_mask == 0] = - 1
97
+ # self.E[(e_mask1 * e_mask2).squeeze(-1) == 0] = - 1
98
+ # else:
99
+ # self.X = self.X * x_mask
100
+ # self.E = self.E * e_mask1 * e_mask2
101
+ # assert torch.allclose(self.E, torch.transpose(self.E, 1, 2))
102
+ # return self
103
+
104
+ def compute_dataset_info(smiles_or_mol_list, cache_path=None):
105
+ pt = Chem.GetPeriodicTable()
106
+ atom_name_list = []
107
+ atom_count_list = []
108
+ for i in range(2, 119):
109
+ atom_name_list.append(pt.GetElementSymbol(i))
110
+ atom_count_list.append(0)
111
+ atom_name_list.append('*')
112
+ atom_count_list.append(0)
113
+
114
+ bond_count_list = [0, 0, 0, 0, 0]
115
+ bond_type_to_index = {Chem.BondType.SINGLE: 1, Chem.BondType.DOUBLE: 2, Chem.BondType.TRIPLE: 3, Chem.BondType.AROMATIC: 4}
116
+
117
+ n_atom_list = []
118
+ n_bond_list = []
119
+ n_atoms_per_mol_count = {}
120
+ max_node = 0
121
+ for i, sms_or_mol in enumerate(smiles_or_mol_list):
122
+ if isinstance(sms_or_mol, str):
123
+ mol = Chem.MolFromSmiles(sms_or_mol)
124
+ else:
125
+ mol = sms_or_mol
126
+
127
+ n_atom = mol.GetNumHeavyAtoms()
128
+ n_bond = mol.GetNumBonds()
129
+ max_node = max(max_node, n_atom)
130
+
131
+ n_atom_list.append(n_atom)
132
+ n_bond_list.append(n_bond)
133
+
134
+ # Count atoms per molecule size
135
+ if n_atom in n_atoms_per_mol_count:
136
+ n_atoms_per_mol_count[n_atom] += 1
137
+ else:
138
+ n_atoms_per_mol_count[n_atom] = 1
139
+
140
+ cur_atom_count_arr = np.zeros(118)
141
+ for atom in mol.GetAtoms():
142
+ symbol = atom.GetSymbol()
143
+ if symbol == 'H':
144
+ continue
145
+ elif symbol == '*':
146
+ atom_count_list[-1] += 1
147
+ cur_atom_count_arr[-1] += 1
148
+ else:
149
+ atom_count_list[atom.GetAtomicNum()-2] += 1
150
+ cur_atom_count_arr[atom.GetAtomicNum()-2] += 1
151
+
152
+ tansition_E_temp = np.zeros((118, 118, 5))
153
+ for bond in mol.GetBonds():
154
+ start_atom, end_atom = bond.GetBeginAtom(), bond.GetEndAtom()
155
+ if start_atom.GetSymbol() == 'H' or end_atom.GetSymbol() == 'H':
156
+ continue
157
+
158
+ if start_atom.GetSymbol() == '*':
159
+ start_index = 117
160
+ else:
161
+ start_index = start_atom.GetAtomicNum() - 2
162
+ if end_atom.GetSymbol() == '*':
163
+ end_index = 117
164
+ else:
165
+ end_index = end_atom.GetAtomicNum() - 2
166
+
167
+ bond_type = bond.GetBondType()
168
+ bond_index = bond_type_to_index[bond_type]
169
+ bond_count_list[bond_index] += 2
170
+
171
+ # Create n_atoms_per_mol array with proper size
172
+ n_atoms_per_mol = [0] * (max_node + 1)
173
+ for n_atom, count in n_atoms_per_mol_count.items():
174
+ n_atoms_per_mol[n_atom] = count
175
+
176
+ n_atoms_per_mol = np.array(n_atoms_per_mol) / np.sum(n_atoms_per_mol)
177
+ n_atoms_per_mol = n_atoms_per_mol.tolist()
178
+
179
+ atom_count_list = np.array(atom_count_list) / np.sum(atom_count_list)
180
+ active_atoms = np.array(atom_name_list)[atom_count_list > 0]
181
+ active_atoms = active_atoms.tolist()
182
+ atom_count_list = atom_count_list.tolist()
183
+
184
+ bond_count_list = np.array(bond_count_list) / np.sum(bond_count_list)
185
+ bond_count_list = bond_count_list.tolist()
186
+
187
+
188
+ node_types = torch.Tensor(atom_count_list)
189
+ edge_types = torch.Tensor(bond_count_list)
190
+ active_index = (node_types > 0).nonzero().squeeze()
191
+
192
+ x_margins = node_types.float() / torch.sum(node_types)
193
+ x_margins = x_margins[active_index]
194
+ e_margins = edge_types.float() / torch.sum(edge_types)
195
+
196
+ num_nodes_dist = DistributionNodes(torch.Tensor(n_atoms_per_mol))
197
+
198
+ meta_dict = {
199
+ 'active_index': active_index,
200
+ 'x_margins': x_margins,
201
+ 'e_margins': e_margins,
202
+ 'atom_decoder': active_atoms,
203
+ 'num_nodes_dist': num_nodes_dist,
204
+ 'max_node': max_node,
205
+ }
206
+
207
+ if cache_path is not None:
208
+ with open(cache_path, "w") as f:
209
+ json.dump(meta_dict, f)
210
+
211
+ return meta_dict
212
+
213
+ class DistributionNodes:
214
+ def __init__(self, histogram):
215
+ """ Compute the distribution of the number of nodes in the dataset, and sample from this distribution.
216
+ historgram: dict. The keys are num_nodes, the values are counts
217
+ """
218
+
219
+ if type(histogram) == dict:
220
+ max_n_nodes = max(histogram.keys())
221
+ prob = torch.zeros(max_n_nodes + 1)
222
+ for num_nodes, count in histogram.items():
223
+ prob[num_nodes] = count
224
+ else:
225
+ prob = histogram
226
+
227
+ self.prob = prob / prob.sum()
228
+ self.m = torch.distributions.Categorical(prob)
229
+
230
+ def sample_n(self, n_samples, device):
231
+ idx = self.m.sample((n_samples,))
232
+ return idx.to(device)
233
+
234
+ def log_prob(self, batch_n_nodes):
235
+ assert len(batch_n_nodes.size()) == 1
236
+ p = self.prob.to(batch_n_nodes.device)
237
+
238
+ probas = p[batch_n_nodes]
239
+ log_p = torch.log(probas + 1e-30)
240
+ return log_p
@@ -0,0 +1,3 @@
1
+ from .modeling_graph_dit import GraphDITMolecularGenerator
2
+
3
+ __all__ = ['GraphDITMolecularGenerator']
@@ -0,0 +1,188 @@
1
+ import torch
2
+ import numpy as np
3
+ from torch.nn import functional as F
4
+ from .utils import PlaceHolder
5
+
6
+ def cosine_beta_schedule_discrete(timesteps, s=0.008):
7
+ """ Cosine schedule as proposed in https://openreview.net/forum?id=-NEXDKk8gZ. """
8
+ steps = timesteps + 2
9
+ x = np.linspace(0, steps, steps)
10
+
11
+ alphas_cumprod = np.cos(0.5 * np.pi * ((x / steps) + s) / (1 + s)) ** 2
12
+ alphas_cumprod = alphas_cumprod / alphas_cumprod[0]
13
+ alphas = (alphas_cumprod[1:] / alphas_cumprod[:-1])
14
+ betas = 1 - alphas
15
+ return betas.squeeze()
16
+
17
+ class NoiseScheduleDiscrete(torch.nn.Module):
18
+ def __init__(self, timesteps):
19
+ super(NoiseScheduleDiscrete, self).__init__()
20
+ self.timesteps = timesteps
21
+
22
+ betas = cosine_beta_schedule_discrete(timesteps)
23
+ self.register_buffer('betas', torch.from_numpy(betas).float())
24
+
25
+ # 0.9999
26
+ self.alphas = 1 - torch.clamp(self.betas, min=0, max=1)
27
+
28
+ log_alpha = torch.log(self.alphas)
29
+ log_alpha_bar = torch.cumsum(log_alpha, dim=0)
30
+ self.alphas_bar = torch.exp(log_alpha_bar)
31
+
32
+ def forward(self, t_normalized=None, t_int=None):
33
+ assert int(t_normalized is None) + int(t_int is None) == 1
34
+ if t_int is None:
35
+ t_int = torch.round(t_normalized * self.timesteps)
36
+ return self.betas[t_int.long()]
37
+
38
+ def get_alpha_bar(self, t_normalized=None, t_int=None):
39
+ assert int(t_normalized is None) + int(t_int is None) == 1
40
+ if t_int is None:
41
+ t_int = torch.round(t_normalized * self.timesteps)
42
+ self.alphas_bar = self.alphas_bar.to(t_int.device)
43
+ return self.alphas_bar[t_int.long()]
44
+
45
+ class MarginalTransition:
46
+ def __init__(self, x_marginals, e_marginals, xe_conditions, ex_conditions, n_nodes):
47
+ self.X_classes = len(x_marginals)
48
+ self.E_classes = len(e_marginals)
49
+ self.x_marginals = x_marginals # Dx
50
+ self.e_marginals = e_marginals # Dx, De
51
+ self.xe_conditions = xe_conditions
52
+
53
+ self.u_x = x_marginals.unsqueeze(0).expand(self.X_classes, -1).unsqueeze(0) # 1, Dx, Dx
54
+ self.u_e = e_marginals.unsqueeze(0).expand(self.E_classes, -1).unsqueeze(0) # 1, De, De
55
+ self.u_xe = xe_conditions.unsqueeze(0) # 1, Dx, De
56
+ self.u_ex = ex_conditions.unsqueeze(0) # 1, De, Dx
57
+ self.u = self.get_union_transition(self.u_x, self.u_e, self.u_xe, self.u_ex, n_nodes) # 1, Dx + n*De, Dx + n*De
58
+
59
+ def get_union_transition(self, u_x, u_e, u_xe, u_ex, n_nodes):
60
+ u_e = u_e.repeat(1, n_nodes, n_nodes) # (1, n*de, n*de)
61
+ u_xe = u_xe.repeat(1, 1, n_nodes) # (1, dx, n*de)
62
+ u_ex = u_ex.repeat(1, n_nodes, 1) # (1, n*de, dx)
63
+ u0 = torch.cat([u_x, u_xe], dim=2) # (1, dx, dx + n*de)
64
+ u1 = torch.cat([u_ex, u_e], dim=2) # (1, n*de, dx + n*de)
65
+ u = torch.cat([u0, u1], dim=1) # (1, dx + n*de, dx + n*de)
66
+ return u
67
+
68
+ def index_edge_margin(self, X, q_e, n_bond=5):
69
+ # q_e: (bs, dx, de) --> (bs, n, de)
70
+ bs, n, n_atom = X.shape
71
+ node_indices = X.argmax(-1) # (bs, n)
72
+ ind = node_indices[ :, :, None].expand(bs, n, n_bond)
73
+ q_e = torch.gather(q_e, 1, ind)
74
+ return q_e
75
+
76
+ def get_Qt(self, beta_t, device):
77
+ """ Returns one-step transition matrices for X and E, from step t - 1 to step t.
78
+ Qt = (1 - beta_t) * I + beta_t / K
79
+ beta_t: (bs)
80
+ returns: q (bs, d0, d0)
81
+ """
82
+ bs = beta_t.size(0)
83
+ d0 = self.u.size(-1)
84
+ self.u = self.u.to(device)
85
+ u = self.u.expand(bs, d0, d0)
86
+
87
+ beta_t = beta_t.to(device)
88
+ beta_t = beta_t.view(bs, 1, 1)
89
+ q = beta_t * u + (1 - beta_t) * torch.eye(d0, device=device).unsqueeze(0)
90
+
91
+ return PlaceHolder(X=q, E=None, y=None)
92
+
93
+ def get_Qt_bar(self, alpha_bar_t, device):
94
+ """ Returns t-step transition matrices for X and E, from step 0 to step t.
95
+ Qt = prod(1 - beta_t) * I + (1 - prod(1 - beta_t)) * K
96
+ alpha_bar_t: (bs, 1) roduct of the (1 - beta_t) for each time step from 0 to t.
97
+ returns: q (bs, d0, d0)
98
+ """
99
+ bs = alpha_bar_t.size(0)
100
+ d0 = self.u.size(-1)
101
+ alpha_bar_t = alpha_bar_t.to(device)
102
+ alpha_bar_t = alpha_bar_t.view(bs, 1, 1)
103
+ self.u = self.u.to(device)
104
+ q = alpha_bar_t * torch.eye(d0, device=device).unsqueeze(0) + (1 - alpha_bar_t) * self.u
105
+
106
+ return PlaceHolder(X=q, E=None, y=None)
107
+
108
+ def sample_discrete_features(probX, probE, node_mask):
109
+ """ Sample features from multinomial distribution with given probabilities (probX, probE, proby)
110
+ :param probX: bs, n, dx_out node features
111
+ :param probE: bs, n, n, de_out edge features
112
+ :param proby: bs, dy_out global features.
113
+ """
114
+ bs, n, _ = probX.shape
115
+
116
+ # Noise X
117
+ # The masked rows should define probability distributions as well
118
+ probX[~node_mask] = 1 / probX.shape[-1]
119
+
120
+ # Flatten the probability tensor to sample with multinomial
121
+ probX = probX.reshape(bs * n, -1) # (bs * n, dx_out)
122
+
123
+ # Sample X
124
+ probX = probX + 1e-12
125
+ probX = probX / probX.sum(dim=-1, keepdim=True)
126
+ X_t = probX.multinomial(1) # (bs * n, 1)
127
+ X_t = X_t.reshape(bs, n) # (bs, n)
128
+
129
+ # Noise E
130
+ # The masked rows should define probability distributions as well
131
+ inverse_edge_mask = ~(node_mask.unsqueeze(1) * node_mask.unsqueeze(2))
132
+ diag_mask = torch.eye(n).unsqueeze(0).expand(bs, -1, -1)
133
+
134
+ probE[inverse_edge_mask] = 1 / probE.shape[-1]
135
+ probE[diag_mask.bool()] = 1 / probE.shape[-1]
136
+ probE = probE.reshape(bs * n * n, -1) # (bs * n * n, de_out)
137
+ probE = probE + 1e-12
138
+ probE = probE / probE.sum(dim=-1, keepdim=True)
139
+
140
+ # Sample E
141
+ E_t = probE.multinomial(1).reshape(bs, n, n) # (bs, n, n)
142
+ E_t = torch.triu(E_t, diagonal=1)
143
+ E_t = (E_t + torch.transpose(E_t, 1, 2))
144
+
145
+ return PlaceHolder(X=X_t, E=E_t, y=None)
146
+
147
+ def sample_discrete_feature_noise(limit_dist, node_mask):
148
+ """Sample from the limit distribution of the diffusion process"""
149
+ bs, n_max = node_mask.shape
150
+ x_limit = limit_dist.X[None, None, :].expand(bs, n_max, -1)
151
+ U_X = x_limit.flatten(end_dim=-2).multinomial(1).reshape(bs, n_max)
152
+ U_X = F.one_hot(U_X.long(), num_classes=x_limit.shape[-1]).type_as(x_limit)
153
+
154
+ e_limit = limit_dist.E[None, None, None, :].expand(bs, n_max, n_max, -1)
155
+ U_E = e_limit.flatten(end_dim=-2).multinomial(1).reshape(bs, n_max, n_max)
156
+ U_E = F.one_hot(U_E.long(), num_classes=e_limit.shape[-1]).type_as(x_limit)
157
+
158
+ U_X = U_X.to(node_mask.device)
159
+ U_E = U_E.to(node_mask.device)
160
+
161
+ # Get upper triangular part of edge noise, without main diagonal
162
+ upper_triangular_mask = torch.zeros_like(U_E)
163
+ indices = torch.triu_indices(row=U_E.size(1), col=U_E.size(2), offset=1)
164
+ upper_triangular_mask[:, indices[0], indices[1], :] = 1
165
+
166
+ U_E = U_E * upper_triangular_mask
167
+ U_E = U_E + torch.transpose(U_E, 1, 2)
168
+
169
+ assert (U_E == torch.transpose(U_E, 1, 2)).all()
170
+ return PlaceHolder(X=U_X, E=U_E, y=None).mask(node_mask)
171
+
172
+ def reverse_diffusion(predX_0, X_t, Qt, Qsb, Qtb):
173
+ """M: X or E
174
+ Compute xt @ Qt.T * x0 @ Qsb / x0 @ Qtb @ xt.T for each possible value of x0
175
+ X_t: bs, n, dt or bs, n, n, dt
176
+ Qt: bs, d_t-1, dt
177
+ Qsb: bs, d0, d_t-1
178
+ Qtb: bs, d0, dt.
179
+ """
180
+ Qt_T = Qt.transpose(-1, -2) # bs, N, dt
181
+ assert Qt.dim() == 3
182
+ left_term = X_t @ Qt_T # bs, N, d_t-1
183
+ right_term = predX_0 @ Qsb
184
+ numerator = left_term * right_term # bs, N, d_t-1
185
+
186
+ denominator = Qtb @ X_t.transpose(-1, -2) # bs, d0, N
187
+ denominator = denominator.transpose(-1, -2) # bs, N, d0
188
+ return numerator / denominator.clamp_min(1e-5)