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.
- D4CMPP2/_Data/AGENTS.md +24 -0
- D4CMPP2/_Data/Aqsoldb.csv +9291 -0
- D4CMPP2/_Data/BradleyMP.csv +3042 -0
- D4CMPP2/_Data/Lipophilicity.csv +1131 -0
- D4CMPP2/_Data/README.md +8 -0
- D4CMPP2/_Data/__init__.py +26 -0
- D4CMPP2/_Data/optical.csv +20237 -0
- D4CMPP2/_Data/test.csv +190 -0
- D4CMPP2/__init__.py +16 -0
- D4CMPP2/__main__.py +5 -0
- D4CMPP2/_main.py +500 -0
- D4CMPP2/cli.py +7 -0
- D4CMPP2/exceptions.py +53 -0
- D4CMPP2/grid_search.py +259 -0
- D4CMPP2/network_refer.yaml +160 -0
- D4CMPP2/networks/AFP_model.py +72 -0
- D4CMPP2/networks/AFPwithSolv_model.py +72 -0
- D4CMPP2/networks/DMPNN_model.py +90 -0
- D4CMPP2/networks/DMPNNwithSolv_model.py +89 -0
- D4CMPP2/networks/GAT_model.py +48 -0
- D4CMPP2/networks/GATwithSolv_model.py +63 -0
- D4CMPP2/networks/GCN_model.py +113 -0
- D4CMPP2/networks/GCNwithSolv_model.py +103 -0
- D4CMPP2/networks/GC_model.py +122 -0
- D4CMPP2/networks/ISATPM_model.py +14 -0
- D4CMPP2/networks/ISATPN_model.py +199 -0
- D4CMPP2/networks/ISAT_model.py +90 -0
- D4CMPP2/networks/MPNN_model.py +56 -0
- D4CMPP2/networks/MPNNwithSolv_model.py +72 -0
- D4CMPP2/networks/__init__.py +25 -0
- D4CMPP2/networks/base.py +250 -0
- D4CMPP2/networks/registry.py +187 -0
- D4CMPP2/networks/src/AFP.py +118 -0
- D4CMPP2/networks/src/BiDropout.py +29 -0
- D4CMPP2/networks/src/DMPNN.py +35 -0
- D4CMPP2/networks/src/GAT.py +69 -0
- D4CMPP2/networks/src/GC.py +85 -0
- D4CMPP2/networks/src/GCN.py +71 -0
- D4CMPP2/networks/src/ISAT.py +153 -0
- D4CMPP2/networks/src/Linear.py +49 -0
- D4CMPP2/networks/src/MPNN.py +56 -0
- D4CMPP2/networks/src/SolventLayer.py +62 -0
- D4CMPP2/networks/src/__init__.py +0 -0
- D4CMPP2/networks/src/distGCN.py +21 -0
- D4CMPP2/networks/src/pyg_hetero.py +24 -0
- D4CMPP2/optimize.py +472 -0
- D4CMPP2/src/Analyzer/ISAAnalyzer.py +458 -0
- D4CMPP2/src/Analyzer/ISAPNAnalyzer.py +366 -0
- D4CMPP2/src/Analyzer/ISAwSAnalyzer.py +117 -0
- D4CMPP2/src/Analyzer/MolAnalyzer.py +319 -0
- D4CMPP2/src/Analyzer/__init__.py +54 -0
- D4CMPP2/src/Analyzer/core.py +480 -0
- D4CMPP2/src/Analyzer/factory.py +166 -0
- D4CMPP2/src/Analyzer/interpretation.py +232 -0
- D4CMPP2/src/Analyzer/results.py +101 -0
- D4CMPP2/src/DataManager/Dataset/GraphDataset.py +314 -0
- D4CMPP2/src/DataManager/Dataset/ISAGraphDataset.py +384 -0
- D4CMPP2/src/DataManager/Dataset/__init__.py +0 -0
- D4CMPP2/src/DataManager/GraphGenerator/ISAGraphGenerator.py +223 -0
- D4CMPP2/src/DataManager/GraphGenerator/MolGraphGenerator.py +73 -0
- D4CMPP2/src/DataManager/GraphGenerator/__init__.py +14 -0
- D4CMPP2/src/DataManager/ISADataManager.py +67 -0
- D4CMPP2/src/DataManager/MolDataManager.py +735 -0
- D4CMPP2/src/DataManager/__init__.py +14 -0
- D4CMPP2/src/DataManager/contracts.py +179 -0
- D4CMPP2/src/NetworkManager/ISANetworkManager.py +12 -0
- D4CMPP2/src/NetworkManager/NetworkManager.py +520 -0
- D4CMPP2/src/NetworkManager/__init__.py +14 -0
- D4CMPP2/src/PostProcessor.py +160 -0
- D4CMPP2/src/TrainManager/ISATrainManager.py +26 -0
- D4CMPP2/src/TrainManager/TrainManager.py +254 -0
- D4CMPP2/src/TrainManager/__init__.py +14 -0
- D4CMPP2/src/TrainManager/callbacks.py +119 -0
- D4CMPP2/src/__init__.py +0 -0
- D4CMPP2/src/utils/PATH.py +246 -0
- D4CMPP2/src/utils/__init__.py +0 -0
- D4CMPP2/src/utils/argparser.py +56 -0
- D4CMPP2/src/utils/checkpointing.py +90 -0
- D4CMPP2/src/utils/config_resolution.py +123 -0
- D4CMPP2/src/utils/config_validation.py +370 -0
- D4CMPP2/src/utils/csv_validation.py +105 -0
- D4CMPP2/src/utils/data_quality.py +181 -0
- D4CMPP2/src/utils/featureizer.py +202 -0
- D4CMPP2/src/utils/functional_group.csv +169 -0
- D4CMPP2/src/utils/graph_cache.py +213 -0
- D4CMPP2/src/utils/leaderboard.py +212 -0
- D4CMPP2/src/utils/metrics.py +31 -0
- D4CMPP2/src/utils/module_loader.py +147 -0
- D4CMPP2/src/utils/output.py +80 -0
- D4CMPP2/src/utils/reproducibility.py +70 -0
- D4CMPP2/src/utils/run_manifest.py +175 -0
- D4CMPP2/src/utils/scaler.py +40 -0
- D4CMPP2/src/utils/sculptor.py +713 -0
- D4CMPP2/src/utils/splitting.py +250 -0
- D4CMPP2/src/utils/supportfile_saver.py +94 -0
- D4CMPP2/src/utils/tools.py +156 -0
- D4CMPP2/src/utils/transfer_learning.py +111 -0
- d4cmpp2-0.4.0.dist-info/METADATA +420 -0
- d4cmpp2-0.4.0.dist-info/RECORD +103 -0
- d4cmpp2-0.4.0.dist-info/WHEEL +5 -0
- d4cmpp2-0.4.0.dist-info/entry_points.txt +2 -0
- d4cmpp2-0.4.0.dist-info/licenses/LICENSE +21 -0
- 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
|