D4CMPP2 0.4.0__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 (103) hide show
  1. D4CMPP2/_Data/AGENTS.md +24 -0
  2. D4CMPP2/_Data/Aqsoldb.csv +9291 -0
  3. D4CMPP2/_Data/BradleyMP.csv +3042 -0
  4. D4CMPP2/_Data/Lipophilicity.csv +1131 -0
  5. D4CMPP2/_Data/README.md +8 -0
  6. D4CMPP2/_Data/__init__.py +26 -0
  7. D4CMPP2/_Data/optical.csv +20237 -0
  8. D4CMPP2/_Data/test.csv +190 -0
  9. D4CMPP2/__init__.py +16 -0
  10. D4CMPP2/__main__.py +5 -0
  11. D4CMPP2/_main.py +500 -0
  12. D4CMPP2/cli.py +7 -0
  13. D4CMPP2/exceptions.py +53 -0
  14. D4CMPP2/grid_search.py +259 -0
  15. D4CMPP2/network_refer.yaml +160 -0
  16. D4CMPP2/networks/AFP_model.py +72 -0
  17. D4CMPP2/networks/AFPwithSolv_model.py +72 -0
  18. D4CMPP2/networks/DMPNN_model.py +90 -0
  19. D4CMPP2/networks/DMPNNwithSolv_model.py +89 -0
  20. D4CMPP2/networks/GAT_model.py +48 -0
  21. D4CMPP2/networks/GATwithSolv_model.py +63 -0
  22. D4CMPP2/networks/GCN_model.py +113 -0
  23. D4CMPP2/networks/GCNwithSolv_model.py +103 -0
  24. D4CMPP2/networks/GC_model.py +122 -0
  25. D4CMPP2/networks/ISATPM_model.py +14 -0
  26. D4CMPP2/networks/ISATPN_model.py +199 -0
  27. D4CMPP2/networks/ISAT_model.py +90 -0
  28. D4CMPP2/networks/MPNN_model.py +56 -0
  29. D4CMPP2/networks/MPNNwithSolv_model.py +72 -0
  30. D4CMPP2/networks/__init__.py +25 -0
  31. D4CMPP2/networks/base.py +250 -0
  32. D4CMPP2/networks/registry.py +187 -0
  33. D4CMPP2/networks/src/AFP.py +118 -0
  34. D4CMPP2/networks/src/BiDropout.py +29 -0
  35. D4CMPP2/networks/src/DMPNN.py +35 -0
  36. D4CMPP2/networks/src/GAT.py +69 -0
  37. D4CMPP2/networks/src/GC.py +85 -0
  38. D4CMPP2/networks/src/GCN.py +71 -0
  39. D4CMPP2/networks/src/ISAT.py +153 -0
  40. D4CMPP2/networks/src/Linear.py +49 -0
  41. D4CMPP2/networks/src/MPNN.py +56 -0
  42. D4CMPP2/networks/src/SolventLayer.py +62 -0
  43. D4CMPP2/networks/src/__init__.py +0 -0
  44. D4CMPP2/networks/src/distGCN.py +21 -0
  45. D4CMPP2/networks/src/pyg_hetero.py +24 -0
  46. D4CMPP2/optimize.py +472 -0
  47. D4CMPP2/src/Analyzer/ISAAnalyzer.py +458 -0
  48. D4CMPP2/src/Analyzer/ISAPNAnalyzer.py +366 -0
  49. D4CMPP2/src/Analyzer/ISAwSAnalyzer.py +117 -0
  50. D4CMPP2/src/Analyzer/MolAnalyzer.py +319 -0
  51. D4CMPP2/src/Analyzer/__init__.py +54 -0
  52. D4CMPP2/src/Analyzer/core.py +480 -0
  53. D4CMPP2/src/Analyzer/factory.py +166 -0
  54. D4CMPP2/src/Analyzer/interpretation.py +232 -0
  55. D4CMPP2/src/Analyzer/results.py +101 -0
  56. D4CMPP2/src/DataManager/Dataset/GraphDataset.py +314 -0
  57. D4CMPP2/src/DataManager/Dataset/ISAGraphDataset.py +384 -0
  58. D4CMPP2/src/DataManager/Dataset/__init__.py +0 -0
  59. D4CMPP2/src/DataManager/GraphGenerator/ISAGraphGenerator.py +223 -0
  60. D4CMPP2/src/DataManager/GraphGenerator/MolGraphGenerator.py +73 -0
  61. D4CMPP2/src/DataManager/GraphGenerator/__init__.py +14 -0
  62. D4CMPP2/src/DataManager/ISADataManager.py +67 -0
  63. D4CMPP2/src/DataManager/MolDataManager.py +735 -0
  64. D4CMPP2/src/DataManager/__init__.py +14 -0
  65. D4CMPP2/src/DataManager/contracts.py +179 -0
  66. D4CMPP2/src/NetworkManager/ISANetworkManager.py +12 -0
  67. D4CMPP2/src/NetworkManager/NetworkManager.py +520 -0
  68. D4CMPP2/src/NetworkManager/__init__.py +14 -0
  69. D4CMPP2/src/PostProcessor.py +160 -0
  70. D4CMPP2/src/TrainManager/ISATrainManager.py +26 -0
  71. D4CMPP2/src/TrainManager/TrainManager.py +254 -0
  72. D4CMPP2/src/TrainManager/__init__.py +14 -0
  73. D4CMPP2/src/TrainManager/callbacks.py +119 -0
  74. D4CMPP2/src/__init__.py +0 -0
  75. D4CMPP2/src/utils/PATH.py +246 -0
  76. D4CMPP2/src/utils/__init__.py +0 -0
  77. D4CMPP2/src/utils/argparser.py +56 -0
  78. D4CMPP2/src/utils/checkpointing.py +90 -0
  79. D4CMPP2/src/utils/config_resolution.py +123 -0
  80. D4CMPP2/src/utils/config_validation.py +370 -0
  81. D4CMPP2/src/utils/csv_validation.py +105 -0
  82. D4CMPP2/src/utils/data_quality.py +181 -0
  83. D4CMPP2/src/utils/featureizer.py +202 -0
  84. D4CMPP2/src/utils/functional_group.csv +169 -0
  85. D4CMPP2/src/utils/graph_cache.py +213 -0
  86. D4CMPP2/src/utils/leaderboard.py +212 -0
  87. D4CMPP2/src/utils/metrics.py +31 -0
  88. D4CMPP2/src/utils/module_loader.py +147 -0
  89. D4CMPP2/src/utils/output.py +80 -0
  90. D4CMPP2/src/utils/reproducibility.py +70 -0
  91. D4CMPP2/src/utils/run_manifest.py +175 -0
  92. D4CMPP2/src/utils/scaler.py +40 -0
  93. D4CMPP2/src/utils/sculptor.py +713 -0
  94. D4CMPP2/src/utils/splitting.py +250 -0
  95. D4CMPP2/src/utils/supportfile_saver.py +94 -0
  96. D4CMPP2/src/utils/tools.py +156 -0
  97. D4CMPP2/src/utils/transfer_learning.py +111 -0
  98. d4cmpp2-0.4.0.dist-info/METADATA +420 -0
  99. d4cmpp2-0.4.0.dist-info/RECORD +103 -0
  100. d4cmpp2-0.4.0.dist-info/WHEEL +5 -0
  101. d4cmpp2-0.4.0.dist-info/entry_points.txt +2 -0
  102. d4cmpp2-0.4.0.dist-info/licenses/LICENSE +21 -0
  103. d4cmpp2-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,187 @@
1
+ """Typed network registry used by training and optimization."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from D4CMPP2.networks.base import MolecularNetwork
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class ModelDefinition:
10
+ name: str
11
+ network: type[MolecularNetwork]
12
+ aliases: tuple[str, ...] = ()
13
+ data_contract: str = "molecule"
14
+
15
+ def __post_init__(self):
16
+ if not isinstance(self.network, type) or not issubclass(
17
+ self.network, MolecularNetwork
18
+ ):
19
+ raise TypeError(
20
+ f"Registered network for {self.name!r} must inherit MolecularNetwork."
21
+ )
22
+ if "<locals>" in self.network.__qualname__:
23
+ raise ValueError(
24
+ f"Network {self.network.__qualname__!r} must be defined at module "
25
+ "scope so saved runs can import it."
26
+ )
27
+ if self.network.model_name != self.name:
28
+ raise ValueError(
29
+ f"Registry name {self.name!r} does not match "
30
+ f"{self.network.__name__}.model_name={self.network.model_name!r}."
31
+ )
32
+ self.network.optimization_space()
33
+ if self.data_contract not in {"molecule", "solvent", "isa"}:
34
+ raise ValueError(
35
+ f"Model {self.name!r} has unknown data_contract "
36
+ f"{self.data_contract!r}. Expected 'molecule', 'solvent', or 'isa'."
37
+ )
38
+
39
+ def training_config(self):
40
+ managers = {
41
+ "molecule": ("MolDataManager", "MolDataManager", "NetworkManager", "NetworkManager", "TrainManager", "Trainer"),
42
+ "solvent": ("MolDataManager", "MolDataManager_withSolv", "NetworkManager", "NetworkManager", "TrainManager", "Trainer"),
43
+ "isa": ("ISADataManager", "ISADataManager", "ISANetworkManager", "ISANetworkManager", "ISATrainManager", "ISATrainer"),
44
+ }
45
+ data_module, data_class, network_module, network_class, train_module, train_class = managers[self.data_contract]
46
+ return {
47
+ "name": self.name,
48
+ "network": self.network.__module__.rsplit(".", 1)[-1],
49
+ "network_id": self.name,
50
+ "data_manager_module": data_module,
51
+ "data_manager_class": data_class,
52
+ "network_manager_module": network_module,
53
+ "network_manager_class": network_class,
54
+ "train_manager_module": train_module,
55
+ "train_manager_class": train_class,
56
+ "description": self.network.__doc__ or self.name,
57
+ "version": "2.0",
58
+ }
59
+
60
+
61
+ _MODELS = {}
62
+
63
+
64
+ def register_model(definition):
65
+ names = (definition.name, *definition.aliases)
66
+ conflicts = [name for name in names if name in _MODELS]
67
+ if conflicts:
68
+ raise ValueError(f"Model names are already registered: {conflicts!r}.")
69
+ for name in names:
70
+ _MODELS[name] = definition
71
+ return definition
72
+
73
+
74
+ def register_network(network, *, aliases=(), data_contract="molecule"):
75
+ """Register a MolecularNetwork subclass for train and optimize entry points."""
76
+ return register_model(
77
+ ModelDefinition(
78
+ network.model_name,
79
+ network,
80
+ aliases=tuple(aliases),
81
+ data_contract=data_contract,
82
+ )
83
+ )
84
+
85
+
86
+ def get_model(name):
87
+ try:
88
+ return _MODELS[name]
89
+ except KeyError as exc:
90
+ raise ValueError(
91
+ f"Unknown model {name!r}. Registered names: {sorted(_MODELS)!r}."
92
+ ) from exc
93
+
94
+
95
+ def registered_models():
96
+ return {
97
+ definition.name: definition
98
+ for definition in _MODELS.values()
99
+ }
100
+
101
+
102
+ def _register_builtins():
103
+ from D4CMPP2.networks.AFP_model import AFP
104
+ from D4CMPP2.networks.AFPwithSolv_model import SolventAFP
105
+ from D4CMPP2.networks.DMPNN_model import DMPNN
106
+ from D4CMPP2.networks.DMPNNwithSolv_model import SolventDMPNN
107
+ from D4CMPP2.networks.GAT_model import GAT
108
+ from D4CMPP2.networks.GATwithSolv_model import SolventGAT
109
+ from D4CMPP2.networks.GCN_model import GCN
110
+ from D4CMPP2.networks.GCNwithSolv_model import SolventGCN
111
+ from D4CMPP2.networks.GC_model import GroupContributionNetwork
112
+ from D4CMPP2.networks.ISAT_model import ISAT
113
+ from D4CMPP2.networks.ISATPN_model import ISATPN
114
+ from D4CMPP2.networks.MPNN_model import MPNN
115
+ from D4CMPP2.networks.MPNNwithSolv_model import SolventMPNN
116
+
117
+ register_model(ModelDefinition("afp", AFP, aliases=("AFP", "AFP_model")))
118
+ register_model(
119
+ ModelDefinition(
120
+ "afp_solvent",
121
+ SolventAFP,
122
+ aliases=("AFPwS", "AFPwithSolv_model"),
123
+ data_contract="solvent",
124
+ )
125
+ )
126
+ register_model(
127
+ ModelDefinition("dmpnn", DMPNN, aliases=("DMPNN", "DMPNN_model"))
128
+ )
129
+ register_model(
130
+ ModelDefinition(
131
+ "dmpnn_solvent",
132
+ SolventDMPNN,
133
+ aliases=("DMPNNwS", "DMPNNwithSolv_model"),
134
+ data_contract="solvent",
135
+ )
136
+ )
137
+ register_model(ModelDefinition("gcn", GCN, aliases=("GCN", "GCN_model")))
138
+ register_model(
139
+ ModelDefinition(
140
+ "gcn_solvent",
141
+ SolventGCN,
142
+ aliases=("GCNwS", "GCNwithSolv_model"),
143
+ data_contract="solvent",
144
+ )
145
+ )
146
+ register_model(
147
+ ModelDefinition(
148
+ "group_contribution",
149
+ GroupContributionNetwork,
150
+ aliases=("GC", "GC_model"),
151
+ data_contract="isa",
152
+ )
153
+ )
154
+ register_model(
155
+ ModelDefinition(
156
+ "isat", ISAT, aliases=("ISAT", "ISAT_model"), data_contract="isa"
157
+ )
158
+ )
159
+ register_model(
160
+ ModelDefinition(
161
+ "isatpn",
162
+ ISATPN,
163
+ aliases=("ISATPN", "ISATPM", "ISATPM_model"),
164
+ data_contract="isa",
165
+ )
166
+ )
167
+ register_model(ModelDefinition("gat", GAT, aliases=("GAT", "GAT_model")))
168
+ register_model(
169
+ ModelDefinition(
170
+ "gat_solvent",
171
+ SolventGAT,
172
+ aliases=("GATwS", "GATwithSolv_model"),
173
+ data_contract="solvent",
174
+ )
175
+ )
176
+ register_model(ModelDefinition("mpnn", MPNN, aliases=("MPNN", "MPNN_model")))
177
+ register_model(
178
+ ModelDefinition(
179
+ "mpnn_solvent",
180
+ SolventMPNN,
181
+ aliases=("MPNNwS", "MPNNwithSolv_model"),
182
+ data_contract="solvent",
183
+ )
184
+ )
185
+
186
+
187
+ _register_builtins()
@@ -0,0 +1,118 @@
1
+
2
+ """
3
+ This codes are modified from the project "GC-GNN" (https://github.com/gsi-lab/GC-GNN)
4
+ The original codes are under the MIT License. (https://github.com/gsi-lab/GC-GNN/blob/main/networks/AttentiveFP.py)
5
+ """
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from D4CMPP2.networks.src.GCN import graph_sum_pool
11
+
12
+
13
+ def _graph_softmax(values, batch, num_graphs):
14
+ maximum = values.new_full((num_graphs, values.shape[1]), -torch.inf)
15
+ maximum.scatter_reduce_(0, batch[:, None].expand_as(values), values, reduce='amax', include_self=True)
16
+ exponentials = torch.exp(values - maximum[batch])
17
+ denominator = values.new_zeros((num_graphs, values.shape[1]))
18
+ denominator.index_add_(0, batch, exponentials)
19
+ return exponentials / denominator[batch]
20
+
21
+ class Atom_AFPLayer(nn.Module):
22
+ def __init__(self, config):
23
+ super().__init__()
24
+ self.hidden_dim = config['hidden_dim']
25
+ self.embedding_edge_lin = nn.Sequential(
26
+ nn.Linear(self.hidden_dim + self.hidden_dim, self.hidden_dim, bias=True),
27
+ nn.Dropout(config['dropout']),
28
+ nn.LeakyReLU()
29
+ )
30
+ self.cal_alignment = nn.Sequential(
31
+ nn.Linear(self.hidden_dim + self.hidden_dim, 1, bias=True),
32
+ )
33
+ self.attend = nn.Sequential(
34
+ nn.Linear(self.hidden_dim, self.hidden_dim, bias=True),
35
+ nn.Dropout(config['dropout']),
36
+ )
37
+ self.GRUCell = nn.GRUCell(self.hidden_dim, self.hidden_dim)
38
+ self.dropout = nn.Dropout(config['dropout'])
39
+ self.reset_parameters()
40
+
41
+ def reset_parameters(self):
42
+ self.embedding_edge_lin[0].reset_parameters()
43
+ self.cal_alignment[0].reset_parameters()
44
+ self.attend[0].reset_parameters()
45
+ self.GRUCell.reset_parameters()
46
+
47
+ def forward(self, graph, node, edge):
48
+ src, dst = graph.edge_index
49
+ neighbor_message = self.embedding_edge_lin(torch.cat([node[src], edge], dim=-1))
50
+ # Retained for state-dict and numerical compatibility although the legacy path did not apply the score.
51
+ self.cal_alignment(torch.cat([neighbor_message, node[dst]], dim=-1))
52
+ messages = self.dropout(self.attend(neighbor_message))
53
+ context = messages.new_zeros(node.shape)
54
+ context.index_add_(0, dst, messages)
55
+ new_node = node + context
56
+ return new_node
57
+
58
+ class Mol_AFPLayer(nn.Module):
59
+ def __init__(self, config):
60
+ super().__init__()
61
+ self.GRUCell = nn.GRUCell(config['hidden_dim'], config['hidden_dim'])
62
+
63
+ self.cal_alignment = nn.Sequential(
64
+ nn.Linear(config['hidden_dim'] + config['hidden_dim'], 1, bias=True),
65
+ nn.LeakyReLU()
66
+ )
67
+ self.attend = nn.Sequential(
68
+ nn.Linear(config['hidden_dim'], config['hidden_dim'], bias=True),
69
+ nn.Dropout(config['dropout']),
70
+ )
71
+ self.dropout = nn.Dropout(config['dropout'])
72
+ self.reset_parameters()
73
+
74
+ def reset_parameters(self):
75
+ self.GRUCell.reset_parameters()
76
+ self.cal_alignment[0].reset_parameters()
77
+ self.attend[0].reset_parameters()
78
+
79
+ def forward(self, graph, super_node, node):
80
+ super_node = F.leaky_relu(super_node)
81
+ batch = getattr(graph, 'batch', node.new_zeros(node.shape[0], dtype=torch.long))
82
+ num_graphs = int(getattr(graph, 'num_graphs', 1))
83
+ score = self.cal_alignment(torch.cat([node, super_node[batch]], dim=1))
84
+ attention_weight = _graph_softmax(score, batch, num_graphs)
85
+ hidden_node = self.attend(self.dropout(node)) * attention_weight
86
+ super_context = hidden_node.new_zeros((num_graphs, hidden_node.shape[1]))
87
+ super_context.index_add_(0, batch, hidden_node)
88
+ super_context = F.elu(super_context)
89
+
90
+ super_node = F.relu(self.GRUCell(super_node, super_context))
91
+ return super_node, attention_weight
92
+
93
+
94
+ class AttentiveFP(nn.Module):
95
+ # Generate Context of each nodes
96
+ def __init__(self, config):
97
+ super().__init__()
98
+ conv_layers = config.get('conv_layers', 4)
99
+ T = config.get("T", conv_layers)
100
+ self.PassingDepth = nn.ModuleList([Atom_AFPLayer(config) for _ in range(conv_layers)])
101
+ self.MultiTimeSteps = nn.ModuleList([Mol_AFPLayer(config) for d in range(T)])
102
+ self.reset_parameters()
103
+
104
+ def reset_parameters(self):
105
+ for l in self.MultiTimeSteps:
106
+ l.reset_parameters()
107
+
108
+ def forward(self, graph, node, edge, **kwargs):
109
+ for i in range(len(self.PassingDepth)):
110
+ node = self.PassingDepth[i](graph, node, edge)
111
+ if kwargs.get('only_atom', False):
112
+ return node, None
113
+ super_node = graph_sum_pool(graph, node)
114
+ for i in range(len(self.MultiTimeSteps)):
115
+ super_node, att_w = self.MultiTimeSteps[i](graph, super_node, node)
116
+ return super_node, att_w
117
+
118
+
@@ -0,0 +1,29 @@
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ class Bi_Dropout(nn.Module):
5
+ def __init__(self, p):
6
+ super(Bi_Dropout, self).__init__()
7
+ self.p = p
8
+ self.drop_p = p
9
+ self.training = True
10
+
11
+ def set_drop_p(self,trainig):
12
+ self.training = trainig
13
+ self.drop_p = torch.rand(1).item()
14
+
15
+ def drop_label(self, score:torch.Tensor,target:torch.Tensor):
16
+ if self.training:
17
+ if self.drop_p<self.p:
18
+ target=nn.ReLU()(target)
19
+ elif self.drop_p>1-self.p:
20
+ target=-nn.ReLU()(-target)
21
+ return score,target
22
+
23
+ def forward(self, x):
24
+ if self.training:
25
+ if self.drop_p<self.p:
26
+ x[:,1]= 0
27
+ elif self.drop_p>1-self.p:
28
+ x[:,0]= 0
29
+ return x
@@ -0,0 +1,35 @@
1
+
2
+ """
3
+ This codes are modified from the project "GC-GNN" (https://github.com/gsi-lab/GC-GNN)
4
+ The original codes are under the MIT License. (https://github.com/gsi-lab/GC-GNN/blob/main/networks/DMPNN.py)
5
+ """
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+
10
+ class DMPNNLayer(nn.Module):
11
+ def __init__(self, in_feats, edge_feats, out_feats, activation, dropout=0.2):
12
+ super(DMPNNLayer, self).__init__()
13
+ self.activation = activation
14
+
15
+ self.W_m = nn.Linear(in_feats+edge_feats, out_feats, bias=True)
16
+ self.dropout_layer = nn.Dropout(dropout)
17
+
18
+
19
+ def forward(self, graph, node_feats, edge_feats, direct_feats=None, backward_feats=None):
20
+ src, dst = graph.edge_index
21
+ if backward_feats is not None and direct_feats is not None:
22
+ direct = self.W_m(torch.cat([edge_feats, direct_feats], 1))
23
+ backward = self.W_m(torch.cat([edge_feats, backward_feats], 1))
24
+ else:
25
+ direct = self.W_m(torch.cat([edge_feats, node_feats[src]], 1))
26
+ backward = self.W_m(torch.cat([edge_feats, node_feats[dst]], 1))
27
+ full_feats = direct.new_zeros((node_feats.shape[0], direct.shape[1]))
28
+ full_feats.index_add_(0, dst, direct)
29
+ new_edge_feats = full_feats[src] - backward
30
+ new_backward_feats = full_feats[dst] - direct
31
+ new_node_feats = full_feats
32
+ if self.activation is not None:
33
+ new_node_feats = self.activation(new_node_feats)
34
+ new_node_feats = self.dropout_layer(new_node_feats)
35
+ return new_node_feats, new_edge_feats, new_backward_feats
@@ -0,0 +1,69 @@
1
+ import torch.nn as nn
2
+ import torch
3
+
4
+ class GAT_layer(nn.Module):
5
+ def __init__(self, in_node_feats, hidden_feats, out_feats, activation, dropout=0.2, batch_norm=False, residual_sum = False):
6
+ super(GAT_layer, self).__init__()
7
+ self.activation = activation
8
+ self.dropout = nn.Dropout(dropout)
9
+
10
+ self.attention_W = nn.Linear(in_node_feats*2, hidden_feats)
11
+ self.attention_a = nn.Linear(hidden_feats,1, bias=False)
12
+ self.linear = nn.Linear(in_node_feats, out_feats)
13
+
14
+ self.batch_norm = batch_norm
15
+ self.residual_sum = residual_sum
16
+ if self.batch_norm:
17
+ self.bn = nn.BatchNorm1d(out_feats)
18
+ if residual_sum:
19
+ if in_node_feats!=out_feats:
20
+ self.residual_layer = nn.Linear(in_node_feats, out_feats)
21
+
22
+ def forward(self, graph, node_feats):
23
+ src, dst = graph.edge_index
24
+ logits = self.attention_a(
25
+ nn.LeakyReLU()(self.attention_W(torch.cat([node_feats[src], node_feats[dst]], dim=1)))
26
+ )
27
+ max_per_dst = logits.new_full((node_feats.shape[0], logits.shape[1]), -torch.inf)
28
+ max_per_dst.scatter_reduce_(
29
+ 0, dst[:, None].expand_as(logits), logits, reduce='amax', include_self=True
30
+ )
31
+ exponentials = torch.exp(logits - max_per_dst[dst])
32
+ denominator = logits.new_zeros((node_feats.shape[0], logits.shape[1]))
33
+ denominator.index_add_(0, dst, exponentials)
34
+ scores = exponentials / denominator[dst]
35
+
36
+ messages = scores * self.linear(node_feats[src])
37
+ h = messages.new_zeros((node_feats.shape[0], messages.shape[1]))
38
+ h.index_add_(0, dst, messages)
39
+ h = nn.LeakyReLU()(h)
40
+ if self.batch_norm:
41
+ h = self.bn(h)
42
+ h = self.activation(h)
43
+ h = self.dropout(h)
44
+ if self.residual_sum:
45
+ if node_feats.shape[1]!=h.shape[1]:
46
+ node_feats = self.residual_layer(node_feats)
47
+ h = h + node_feats
48
+ return h
49
+
50
+ class GATs(nn.Module):
51
+ def __init__(self, in_node_feats, hidden_feats, out_feats, activation, n_layers, dropout=0.2, batch_norm=False, residual_sum = False):
52
+ super(GATs, self).__init__()
53
+
54
+ self.layers = nn.ModuleList()
55
+ for i in range(n_layers):
56
+ if i==0:
57
+ _in_feats = in_node_feats
58
+ else:
59
+ _in_feats = hidden_feats
60
+ if i==n_layers-1:
61
+ _out_feats = out_feats
62
+ else:
63
+ _out_feats = hidden_feats
64
+ self.layers.append(GAT_layer(_in_feats, hidden_feats, _out_feats, activation, dropout, batch_norm, residual_sum))
65
+
66
+ def forward(self, graph, node_feats):
67
+ for layer in self.layers:
68
+ node_feats = layer(graph, node_feats)
69
+ return node_feats
@@ -0,0 +1,85 @@
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+ import numpy as np
5
+
6
+ from .GCN import GCN_layer
7
+ from .MPNN import MPNN_layer
8
+ from .distGCN import distGCN_layer
9
+ from .pyg_hetero import relation_graph, relation_sum
10
+
11
+ class GCconvolution(nn.Module):
12
+ def __init__(self, in_node_feats, in_edge_feats, out_feats, activation, n_layers, dropout=0.2, batch_norm=False, residual_sum = False, alpha=0.1, max_dist = 4):
13
+ super().__init__()
14
+ # Message Passing
15
+ self.r2r = nn.ModuleList([MPNN_layer(in_node_feats, in_edge_feats, out_feats, activation, dropout, batch_norm, residual_sum) for _ in range(n_layers)])
16
+ self.i2i = nn.ModuleList([GCN_layer(out_feats, out_feats, activation, dropout, batch_norm, residual_sum) for _ in range(n_layers)])
17
+
18
+ self.r2i = r2i_layer()
19
+ self.i2d = i2s_layer()
20
+ self.d2d = distGCN_layer(out_feats, max_dist, out_feats, activation, alpha)
21
+
22
+ self.d2score = nn.Sequential(nn.Linear(out_feats, out_feats//2),
23
+ nn.LeakyReLU(),
24
+ nn.Linear(out_feats//2, out_feats//4),
25
+ nn.LeakyReLU(),
26
+ nn.Linear(out_feats//4, 1)
27
+ )
28
+
29
+ def forward(self, graph, r_node, r2r_edge, i_node, d2d_edge):
30
+
31
+ real_graph = relation_graph(graph, 'r_nd', 'r2r')
32
+ image_graph = relation_graph(graph, 'i_nd', 'i2i')
33
+
34
+ for i in range(len(self.r2r)):
35
+ r_node = self.r2r[i](real_graph, r_node, r2r_edge)
36
+ i_node = self.r2i(graph, r_node, i_node)
37
+ i_node = self.i2i[i](image_graph, i_node)
38
+ d_node = self.i2d(graph, i_node)
39
+ score= self.d2score(d_node)
40
+
41
+ return score
42
+
43
+ class GCconvolution2(nn.Module):
44
+ def __init__(self, in_node_feats, in_edge_feats, out_feats, activation, n_layers, dropout=0.2, batch_norm=False, residual_sum = False, alpha=0.1, max_dist = 4):
45
+ super().__init__()
46
+ # Message Passing
47
+ self.r2r = nn.ModuleList([GCN_layer(out_feats, out_feats, activation, dropout, batch_norm, residual_sum) for _ in range(n_layers)])
48
+ self.i2i = nn.ModuleList([GCN_layer(out_feats, out_feats, activation, dropout, batch_norm, residual_sum) for _ in range(n_layers)])
49
+
50
+ self.r2i = r2i_layer()
51
+ self.i2d = i2s_layer()
52
+ self.d2d = distGCN_layer(out_feats, max_dist, out_feats, activation, alpha)
53
+
54
+ self.d2score = nn.Sequential(nn.Linear(out_feats, out_feats//2),
55
+ nn.LeakyReLU(),
56
+ nn.Linear(out_feats//2, out_feats//4),
57
+ nn.LeakyReLU(),
58
+ nn.Linear(out_feats//4, 1)
59
+ )
60
+
61
+ def forward(self, graph, r_node, r2r_edge, i_node, d2d_edge):
62
+
63
+ image_graph = relation_graph(graph, 'i_nd', 'i2i')
64
+
65
+ for i in range(len(self.r2r)):
66
+ r_node = self.r2r[i](image_graph, r_node)
67
+ i_node = self.r2i(graph, r_node, i_node)
68
+ i_node = self.i2i[i](image_graph, i_node)
69
+ d_node = self.i2d(graph, i_node)
70
+ score= self.d2score(d_node)
71
+
72
+ return score
73
+
74
+ class r2i_layer(nn.Module):
75
+ def forward(self,graph, r_node, i_node):
76
+ return i_node+r_node
77
+
78
+ class i2s_layer(nn.Module):
79
+ def forward(self,graph, i_node):
80
+ return relation_sum(graph, 'i_nd', 'i2d', 'd_nd', i_node)
81
+
82
+
83
+ class s2r_Layer(nn.Module):
84
+ def forward(self, graph, node):
85
+ return relation_sum(graph, 'd_nd', 'd2r', 'r_nd', node)
@@ -0,0 +1,71 @@
1
+ import torch.nn as nn
2
+ import torch
3
+
4
+
5
+ class GCN_layer(nn.Module):
6
+ def __init__(self, in_feats, out_feats, activation, dropout=0.2, batch_norm=False, residual_sum = False):
7
+ super(GCN_layer, self).__init__()
8
+ self.activation = activation
9
+ self.dropout = nn.Dropout(dropout)
10
+ self.batch_norm = batch_norm
11
+ self.linear = nn.Linear(in_feats, out_feats)
12
+ self.residual_sum = residual_sum
13
+ if self.batch_norm:
14
+ self.bn = nn.BatchNorm1d(out_feats)
15
+ if residual_sum:
16
+ if in_feats!=out_feats:
17
+ self.residual_layer = nn.Linear(in_feats, out_feats)
18
+
19
+ def forward(self, graph, node_feats):
20
+ h = self.linear(node_feats)
21
+ src, dst = graph.edge_index
22
+ aggregated = torch.zeros_like(h)
23
+ if src.numel() > 0:
24
+ aggregated.index_add_(0, dst, h[src])
25
+ # The established contract adds one self-message per node without removing
26
+ # pre-existing self edges. Existing self edges are already aggregated above.
27
+ h = aggregated + h
28
+ if self.batch_norm:
29
+ h = self.bn(h)
30
+ h = self.activation(h)
31
+ h = self.dropout(h)
32
+ if self.residual_sum:
33
+ if node_feats.shape[1]!=h.shape[1]:
34
+ node_feats = self.residual_layer(node_feats)
35
+ h = h + node_feats
36
+ return h
37
+
38
+ class GCNs(nn.Module):
39
+ def __init__(self, in_feats, hidden_feats, out_feats, activation, n_layers, dropout=0.2, batch_norm=False, residual_sum = False):
40
+ super(GCNs, self).__init__()
41
+
42
+ self.layers = nn.ModuleList()
43
+ for i in range(n_layers):
44
+ if i==0:
45
+ _in_feats = in_feats
46
+ else:
47
+ _in_feats = hidden_feats
48
+ if i==n_layers-1:
49
+ _out_feats = out_feats
50
+ else:
51
+ _out_feats = hidden_feats
52
+ self.layers.append(GCN_layer(_in_feats, _out_feats, activation, dropout, batch_norm, residual_sum))
53
+
54
+ def forward(self, graph, node_feats):
55
+ for layer in self.layers:
56
+ node_feats = layer(graph, node_feats)
57
+ return node_feats
58
+
59
+
60
+ def graph_sum_pool(graph, node_feats):
61
+ """Sum node features per PyG graph using the established readout contract."""
62
+ if hasattr(graph, "batch") and graph.batch is not None:
63
+ batch = graph.batch
64
+ num_graphs = int(graph.num_graphs)
65
+ else:
66
+ batch = torch.zeros(node_feats.shape[0], dtype=torch.long, device=node_feats.device)
67
+ num_graphs = 1
68
+ pooled = node_feats.new_zeros((num_graphs, node_feats.shape[-1]))
69
+ if batch.numel() > 0:
70
+ pooled.index_add_(0, batch, node_feats)
71
+ return pooled