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,89 @@
|
|
|
1
|
+
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
from D4CMPP2.networks.base import (
|
|
4
|
+
InputContract,
|
|
5
|
+
MolecularNetwork,
|
|
6
|
+
STANDARD_SOLVENT_HYPERPARAMETERS,
|
|
7
|
+
STANDARD_SOLVENT_OPTIMIZATION_SPACE,
|
|
8
|
+
)
|
|
9
|
+
from D4CMPP2.networks.src.GCN import graph_sum_pool
|
|
10
|
+
|
|
11
|
+
from D4CMPP2.networks.src.SolventLayer import SolventLayer
|
|
12
|
+
from D4CMPP2.networks.src.DMPNN import DMPNNLayer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SolventDMPNN(MolecularNetwork):
|
|
16
|
+
"""Directed-edge compound encoder coupled to a solvent graph branch."""
|
|
17
|
+
|
|
18
|
+
model_name = "dmpnn_solvent"
|
|
19
|
+
required_config = ("node_dim", "edge_dim", "target_dim")
|
|
20
|
+
input_contract = InputContract(
|
|
21
|
+
required=(
|
|
22
|
+
"compound_graphs",
|
|
23
|
+
"compound_node_feature",
|
|
24
|
+
"compound_edge_feature",
|
|
25
|
+
"solvent_graphs",
|
|
26
|
+
"solvent_node_feature",
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
hyperparameters = STANDARD_SOLVENT_HYPERPARAMETERS
|
|
30
|
+
default_optimization_space = STANDARD_SOLVENT_OPTIMIZATION_SPACE
|
|
31
|
+
|
|
32
|
+
def __init__(self, config):
|
|
33
|
+
super().__init__(config)
|
|
34
|
+
hidden_dim = self.config["hidden_dim"]
|
|
35
|
+
conv_layers = self.config["conv_layers"]
|
|
36
|
+
dropout = self.config["dropout"]
|
|
37
|
+
|
|
38
|
+
self.embedding_node_lin = nn.Linear(self.config["node_dim"], hidden_dim, bias=True)
|
|
39
|
+
self.embedding_edge_lin = nn.Linear(self.config["edge_dim"], hidden_dim, bias=True)
|
|
40
|
+
self.init_h_func = nn.Sequential(
|
|
41
|
+
nn.Linear(2 * hidden_dim, hidden_dim, bias=True),
|
|
42
|
+
nn.LeakyReLU()
|
|
43
|
+
)
|
|
44
|
+
self.W_a = nn.Sequential(
|
|
45
|
+
nn.Linear(2 * hidden_dim, hidden_dim, bias=True),
|
|
46
|
+
nn.LeakyReLU()
|
|
47
|
+
)
|
|
48
|
+
self.dropout_layer = nn.Dropout(dropout)
|
|
49
|
+
self.DMPNNLayer = nn.ModuleList([DMPNNLayer(hidden_dim,hidden_dim,hidden_dim,nn.LeakyReLU(),dropout) for _ in range(conv_layers)])
|
|
50
|
+
self.SolvLayer = SolventLayer(self.config)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def forward(self, **kwargs):
|
|
54
|
+
graph = kwargs.get('compound_graphs', kwargs.get('graph'))
|
|
55
|
+
node_feats = kwargs.get('compound_node_feature', kwargs.get('node_feats'))
|
|
56
|
+
edge_feats = kwargs.get('compound_edge_feature', kwargs.get('edge_feats'))
|
|
57
|
+
solv_graph = kwargs.get('solvent_graphs', kwargs.get('solv_graph'))
|
|
58
|
+
solv_node_feats = kwargs.get('solvent_node_feature', kwargs.get('solv_node_feats'))
|
|
59
|
+
missing = [
|
|
60
|
+
name
|
|
61
|
+
for name, value in {
|
|
62
|
+
"compound_graphs": graph,
|
|
63
|
+
"compound_node_feature": node_feats,
|
|
64
|
+
"compound_edge_feature": edge_feats,
|
|
65
|
+
"solvent_graphs": solv_graph,
|
|
66
|
+
"solvent_node_feature": solv_node_feats,
|
|
67
|
+
}.items()
|
|
68
|
+
if value is None
|
|
69
|
+
]
|
|
70
|
+
if missing:
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"SolventDMPNN input is missing required fields {missing!r}."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
node = self.embedding_node_lin(node_feats)
|
|
76
|
+
edge = self.embedding_edge_lin(edge_feats)
|
|
77
|
+
|
|
78
|
+
direct_feats = None
|
|
79
|
+
backward_feats = None
|
|
80
|
+
for layer in self.DMPNNLayer:
|
|
81
|
+
hidden_feats, direct_feats, backward_feats = layer(graph, node, edge, direct_feats, backward_feats)
|
|
82
|
+
|
|
83
|
+
graph_feats = graph_sum_pool(graph, hidden_feats)
|
|
84
|
+
output = self.SolvLayer(graph_feats, solv_graph, solv_node_feats)
|
|
85
|
+
return output
|
|
86
|
+
|
|
87
|
+
network = SolventDMPNN
|
|
88
|
+
|
|
89
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
from D4CMPP2.networks.base import (
|
|
3
|
+
InputContract,
|
|
4
|
+
MolecularNetwork,
|
|
5
|
+
STANDARD_GRAPH_HYPERPARAMETERS,
|
|
6
|
+
STANDARD_GRAPH_OPTIMIZATION_SPACE,
|
|
7
|
+
)
|
|
8
|
+
from D4CMPP2.networks.src.GAT import GATs
|
|
9
|
+
from D4CMPP2.networks.src.GCN import graph_sum_pool
|
|
10
|
+
from D4CMPP2.networks.src.Linear import Linears
|
|
11
|
+
|
|
12
|
+
class GAT(MolecularNetwork):
|
|
13
|
+
"""Graph-attention network with graph-sum readout."""
|
|
14
|
+
|
|
15
|
+
model_name = "gat"
|
|
16
|
+
input_contract = InputContract(
|
|
17
|
+
required=("compound_graphs", "compound_node_feature")
|
|
18
|
+
)
|
|
19
|
+
hyperparameters = STANDARD_GRAPH_HYPERPARAMETERS
|
|
20
|
+
default_optimization_space = STANDARD_GRAPH_OPTIMIZATION_SPACE
|
|
21
|
+
|
|
22
|
+
def __init__(self, config):
|
|
23
|
+
super().__init__(config)
|
|
24
|
+
|
|
25
|
+
hidden_dim = self.config["hidden_dim"]
|
|
26
|
+
gcn_layers = self.config["conv_layers"]
|
|
27
|
+
dropout = self.config["dropout"]
|
|
28
|
+
linear_layers = self.config["linear_layers"]
|
|
29
|
+
target_dim = self.config["target_dim"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
self.node_embedding = nn.Linear(self.config["node_dim"], hidden_dim)
|
|
33
|
+
|
|
34
|
+
self.GATs = GATs(hidden_dim, hidden_dim, hidden_dim, nn.ReLU(), gcn_layers, dropout, False, True) # in_feats, hidden_feats, out_feats, activation, n_layers, dropout=0.2, batch_norm=False, residual_sum = False):
|
|
35
|
+
self.Linears = Linears(hidden_dim,target_dim, nn.ReLU(), linear_layers, dropout, False, False, True) # in_feats, out_feats, activation, n_layers, dropout=0.2, batch_norm=False, residual_sum = False):
|
|
36
|
+
|
|
37
|
+
def forward(self, **kwargs):
|
|
38
|
+
self.validate_input(kwargs)
|
|
39
|
+
graph = kwargs["compound_graphs"]
|
|
40
|
+
node_feats = kwargs["compound_node_feature"]
|
|
41
|
+
|
|
42
|
+
h = self.node_embedding(node_feats)
|
|
43
|
+
h = self.GATs(graph, h)
|
|
44
|
+
h = graph_sum_pool(graph, h)
|
|
45
|
+
h = self.Linears(h)
|
|
46
|
+
return h
|
|
47
|
+
|
|
48
|
+
network = GAT
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
from D4CMPP2.networks.base import (
|
|
3
|
+
InputContract,
|
|
4
|
+
MolecularNetwork,
|
|
5
|
+
STANDARD_SOLVENT_HYPERPARAMETERS,
|
|
6
|
+
STANDARD_SOLVENT_OPTIMIZATION_SPACE,
|
|
7
|
+
)
|
|
8
|
+
from D4CMPP2.networks.src.GAT import GATs
|
|
9
|
+
from D4CMPP2.networks.src.GCN import graph_sum_pool
|
|
10
|
+
from D4CMPP2.networks.src.SolventLayer import SolventLayer
|
|
11
|
+
|
|
12
|
+
class SolventGAT(MolecularNetwork):
|
|
13
|
+
"""Graph-attention compound encoder coupled to a solvent branch."""
|
|
14
|
+
|
|
15
|
+
model_name = "gat_solvent"
|
|
16
|
+
input_contract = InputContract(
|
|
17
|
+
required=(
|
|
18
|
+
"compound_graphs",
|
|
19
|
+
"compound_node_feature",
|
|
20
|
+
"solvent_graphs",
|
|
21
|
+
"solvent_node_feature",
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
hyperparameters = STANDARD_SOLVENT_HYPERPARAMETERS
|
|
25
|
+
default_optimization_space = STANDARD_SOLVENT_OPTIMIZATION_SPACE
|
|
26
|
+
|
|
27
|
+
def __init__(self, config):
|
|
28
|
+
super().__init__(config)
|
|
29
|
+
|
|
30
|
+
hidden_dim = self.config["hidden_dim"]
|
|
31
|
+
gcn_layers = self.config["conv_layers"]
|
|
32
|
+
dropout = self.config["dropout"]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
self.node_embedding = nn.Linear(self.config["node_dim"], hidden_dim)
|
|
36
|
+
|
|
37
|
+
self.GATs = GATs(hidden_dim, hidden_dim, hidden_dim, nn.ReLU(), gcn_layers, dropout, False, True)
|
|
38
|
+
self.SolventLayer = SolventLayer(self.config)
|
|
39
|
+
|
|
40
|
+
def forward(self, **kwargs):
|
|
41
|
+
graph = kwargs.get('compound_graphs', kwargs.get('graph'))
|
|
42
|
+
node_feats = kwargs.get('compound_node_feature', kwargs.get('node_feats'))
|
|
43
|
+
solv_graph = kwargs.get('solvent_graphs', kwargs.get('solv_graph'))
|
|
44
|
+
solv_node_feats = kwargs.get('solvent_node_feature', kwargs.get('solv_node_feats'))
|
|
45
|
+
values = {
|
|
46
|
+
"compound_graphs": graph,
|
|
47
|
+
"compound_node_feature": node_feats,
|
|
48
|
+
"solvent_graphs": solv_graph,
|
|
49
|
+
"solvent_node_feature": solv_node_feats,
|
|
50
|
+
}
|
|
51
|
+
missing = [name for name, value in values.items() if value is None]
|
|
52
|
+
if missing:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"SolventGAT input is missing required fields {missing!r}."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
h = self.node_embedding(node_feats)
|
|
58
|
+
h = self.GATs(graph, h)
|
|
59
|
+
h = graph_sum_pool(graph, h)
|
|
60
|
+
h = self.SolventLayer(h,solv_graph,solv_node_feats)
|
|
61
|
+
return h
|
|
62
|
+
|
|
63
|
+
network = SolventGAT
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
|
|
3
|
+
from D4CMPP2.networks.base import (
|
|
4
|
+
Hyperparameter,
|
|
5
|
+
InputContract,
|
|
6
|
+
MolecularNetwork,
|
|
7
|
+
)
|
|
8
|
+
from D4CMPP2.networks.src.GCN import GCNs, graph_sum_pool
|
|
9
|
+
from D4CMPP2.networks.src.Linear import Linears
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GCN(MolecularNetwork):
|
|
13
|
+
"""Graph convolution using transformed self-plus-neighbor sums."""
|
|
14
|
+
|
|
15
|
+
model_name = "gcn"
|
|
16
|
+
input_contract = InputContract(
|
|
17
|
+
required=("compound_graphs", "compound_node_feature"),
|
|
18
|
+
)
|
|
19
|
+
hyperparameters = {
|
|
20
|
+
"hidden_dim": Hyperparameter(
|
|
21
|
+
"int",
|
|
22
|
+
default=64,
|
|
23
|
+
low=1,
|
|
24
|
+
search_low=16,
|
|
25
|
+
search_high=256,
|
|
26
|
+
step=16,
|
|
27
|
+
grid=(32, 64, 128, 256),
|
|
28
|
+
description="Hidden node and graph representation width.",
|
|
29
|
+
),
|
|
30
|
+
"conv_layers": Hyperparameter(
|
|
31
|
+
"int",
|
|
32
|
+
default=4,
|
|
33
|
+
low=1,
|
|
34
|
+
search_low=1,
|
|
35
|
+
search_high=8,
|
|
36
|
+
step=1,
|
|
37
|
+
grid=(2, 4, 6, 8),
|
|
38
|
+
description="Number of graph convolution layers.",
|
|
39
|
+
),
|
|
40
|
+
"linear_layers": Hyperparameter(
|
|
41
|
+
"int",
|
|
42
|
+
default=2,
|
|
43
|
+
low=1,
|
|
44
|
+
search_low=1,
|
|
45
|
+
search_high=5,
|
|
46
|
+
step=1,
|
|
47
|
+
grid=(1, 2, 3, 4),
|
|
48
|
+
description="Number of prediction-head linear layers.",
|
|
49
|
+
),
|
|
50
|
+
"dropout": Hyperparameter(
|
|
51
|
+
"float",
|
|
52
|
+
default=0.2,
|
|
53
|
+
low=0.0,
|
|
54
|
+
high=0.5,
|
|
55
|
+
search_low=0.0,
|
|
56
|
+
search_high=0.5,
|
|
57
|
+
grid=(0.0, 0.1, 0.2, 0.3, 0.5),
|
|
58
|
+
description="Dropout probability.",
|
|
59
|
+
),
|
|
60
|
+
}
|
|
61
|
+
default_optimization_space = (
|
|
62
|
+
"hidden_dim",
|
|
63
|
+
"conv_layers",
|
|
64
|
+
"linear_layers",
|
|
65
|
+
"dropout",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def __init__(self, config):
|
|
69
|
+
super().__init__(config)
|
|
70
|
+
hidden_dim = self.config["hidden_dim"]
|
|
71
|
+
self.node_embedding = nn.Linear(self.config["node_dim"], hidden_dim)
|
|
72
|
+
# Keep the established module registration names so saved state dictionaries
|
|
73
|
+
# remain readable while the public class/config contract is redesigned.
|
|
74
|
+
self.GCNs = GCNs(
|
|
75
|
+
hidden_dim,
|
|
76
|
+
hidden_dim,
|
|
77
|
+
hidden_dim,
|
|
78
|
+
nn.ReLU(),
|
|
79
|
+
self.config["conv_layers"],
|
|
80
|
+
self.config["dropout"],
|
|
81
|
+
False,
|
|
82
|
+
True,
|
|
83
|
+
)
|
|
84
|
+
self.Linears = Linears(
|
|
85
|
+
hidden_dim,
|
|
86
|
+
self.config["target_dim"],
|
|
87
|
+
nn.ReLU(),
|
|
88
|
+
self.config["linear_layers"],
|
|
89
|
+
self.config["dropout"],
|
|
90
|
+
False,
|
|
91
|
+
False,
|
|
92
|
+
True,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def convolutions(self):
|
|
97
|
+
return self.GCNs
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def prediction_head(self):
|
|
101
|
+
return self.Linears
|
|
102
|
+
|
|
103
|
+
def forward(self, **batch):
|
|
104
|
+
self.validate_input(batch)
|
|
105
|
+
graph = batch["compound_graphs"]
|
|
106
|
+
node = self.node_embedding(batch["compound_node_feature"])
|
|
107
|
+
node = self.GCNs(graph, node)
|
|
108
|
+
graph_feature = graph_sum_pool(graph, node)
|
|
109
|
+
return self.Linears(graph_feature)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# Staged bridge for code paths not yet migrated to the typed registry.
|
|
113
|
+
network = GCN
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
import torch
|
|
3
|
+
from D4CMPP2.networks.base import (
|
|
4
|
+
Hyperparameter,
|
|
5
|
+
InputContract,
|
|
6
|
+
MolecularNetwork,
|
|
7
|
+
STANDARD_SOLVENT_HYPERPARAMETERS,
|
|
8
|
+
STANDARD_SOLVENT_OPTIMIZATION_SPACE,
|
|
9
|
+
)
|
|
10
|
+
from D4CMPP2.networks.src.GCN import GCNs, graph_sum_pool
|
|
11
|
+
from D4CMPP2.networks.src.Linear import Linears
|
|
12
|
+
|
|
13
|
+
class SolventGCN(MolecularNetwork):
|
|
14
|
+
"""Two-branch GCN for compound-solvent property prediction."""
|
|
15
|
+
|
|
16
|
+
model_name = "gcn_solvent"
|
|
17
|
+
input_contract = InputContract(
|
|
18
|
+
required=(
|
|
19
|
+
"compound_graphs",
|
|
20
|
+
"compound_node_feature",
|
|
21
|
+
"solvent_graphs",
|
|
22
|
+
"solvent_node_feature",
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
hyperparameters = {
|
|
26
|
+
**STANDARD_SOLVENT_HYPERPARAMETERS,
|
|
27
|
+
"fusion_dim": Hyperparameter(
|
|
28
|
+
"int", default=16, low=1, search_low=8, search_high=128,
|
|
29
|
+
step=8, grid=(16, 32, 64),
|
|
30
|
+
description="Width of the fused compound-solvent representation.",
|
|
31
|
+
),
|
|
32
|
+
}
|
|
33
|
+
default_optimization_space = (
|
|
34
|
+
*STANDARD_SOLVENT_OPTIMIZATION_SPACE,
|
|
35
|
+
"fusion_dim",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def __init__(self, config):
|
|
39
|
+
super().__init__(config)
|
|
40
|
+
|
|
41
|
+
hidden_dim = self.config["hidden_dim"]
|
|
42
|
+
conv_layers = self.config["conv_layers"]
|
|
43
|
+
linear_layers = self.config["linear_layers"]
|
|
44
|
+
dropout = self.config["dropout"]
|
|
45
|
+
solvent_hidden_dim = self.config["solvent_hidden_dim"]
|
|
46
|
+
solvent_conv_layers = self.config["solvent_conv_layers"]
|
|
47
|
+
solvent_linear_layers = self.config["solvent_linear_layers"]
|
|
48
|
+
solvent_dropout = self.config["solvent_dropout"]
|
|
49
|
+
fusion_dim = self.config["fusion_dim"]
|
|
50
|
+
self.node_embedding = nn.Linear(self.config["node_dim"], hidden_dim)
|
|
51
|
+
self.node_embedding_solv = nn.Linear(
|
|
52
|
+
self.config["node_dim"], solvent_hidden_dim
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self.GCNs = GCNs(hidden_dim, hidden_dim, hidden_dim, nn.ReLU(), conv_layers, dropout, False, True)
|
|
56
|
+
self.GCNs_solv = GCNs(solvent_hidden_dim, solvent_hidden_dim, solvent_hidden_dim, nn.ReLU(), solvent_conv_layers, solvent_dropout, False, True)
|
|
57
|
+
self.Linears1 = Linears(hidden_dim,hidden_dim, nn.ReLU(), 2, dropout, False, False)
|
|
58
|
+
self.Linears2 = Linears(solvent_hidden_dim,solvent_hidden_dim, nn.ReLU(), solvent_linear_layers, solvent_dropout, False, False)
|
|
59
|
+
self.Linears3 = Linears(hidden_dim+solvent_hidden_dim,fusion_dim, nn.ReLU(), linear_layers, dropout, False, False)
|
|
60
|
+
|
|
61
|
+
self.output = nn.Linear(fusion_dim, self.config["target_dim"])
|
|
62
|
+
|
|
63
|
+
def forward(self, **kwargs):
|
|
64
|
+
batch = {
|
|
65
|
+
"compound_graphs": kwargs.get(
|
|
66
|
+
"compound_graphs", kwargs.get("graph")
|
|
67
|
+
),
|
|
68
|
+
"compound_node_feature": kwargs.get(
|
|
69
|
+
"compound_node_feature", kwargs.get("node_feats")
|
|
70
|
+
),
|
|
71
|
+
"solvent_graphs": kwargs.get(
|
|
72
|
+
"solvent_graphs", kwargs.get("solv_graph")
|
|
73
|
+
),
|
|
74
|
+
"solvent_node_feature": kwargs.get(
|
|
75
|
+
"solvent_node_feature", kwargs.get("solv_node_feats")
|
|
76
|
+
),
|
|
77
|
+
}
|
|
78
|
+
missing = [name for name, value in batch.items() if value is None]
|
|
79
|
+
if missing:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"SolventGCN input is missing required fields {missing!r}."
|
|
82
|
+
)
|
|
83
|
+
graph = batch["compound_graphs"]
|
|
84
|
+
node_feats = batch["compound_node_feature"]
|
|
85
|
+
solv_graph = batch["solvent_graphs"]
|
|
86
|
+
solv_node_feats = batch["solvent_node_feature"]
|
|
87
|
+
|
|
88
|
+
h = self.node_embedding(node_feats)
|
|
89
|
+
h = self.GCNs(graph, h)
|
|
90
|
+
h = graph_sum_pool(graph, h)
|
|
91
|
+
|
|
92
|
+
h_solv = self.node_embedding_solv(solv_node_feats)
|
|
93
|
+
h_solv = self.GCNs_solv(solv_graph, h_solv)
|
|
94
|
+
h_solv = graph_sum_pool(solv_graph, h_solv)
|
|
95
|
+
|
|
96
|
+
h = self.Linears1(h)
|
|
97
|
+
h_solv = self.Linears2(h_solv)
|
|
98
|
+
h = torch.cat([h,h_solv],axis=1)
|
|
99
|
+
h = self.Linears3(h)
|
|
100
|
+
h = self.output(h)
|
|
101
|
+
return h
|
|
102
|
+
|
|
103
|
+
network = SolventGCN
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import torch.nn as nn
|
|
2
|
+
|
|
3
|
+
from D4CMPP2.networks.base import (
|
|
4
|
+
Hyperparameter,
|
|
5
|
+
InputContract,
|
|
6
|
+
MolecularNetwork,
|
|
7
|
+
STANDARD_GRAPH_HYPERPARAMETERS,
|
|
8
|
+
STANDARD_GRAPH_OPTIMIZATION_SPACE,
|
|
9
|
+
)
|
|
10
|
+
from D4CMPP2.networks.src.GC import GCconvolution
|
|
11
|
+
from D4CMPP2.networks.src.GCN import graph_sum_pool
|
|
12
|
+
from D4CMPP2.networks.src.pyg_hetero import relation_graph
|
|
13
|
+
|
|
14
|
+
class GroupContributionNetwork(MolecularNetwork):
|
|
15
|
+
"""Group-contribution network over the ISA heterogeneous graph."""
|
|
16
|
+
|
|
17
|
+
model_name = "group_contribution"
|
|
18
|
+
required_config = ("node_dim", "edge_dim", "target_dim")
|
|
19
|
+
input_contract = InputContract(
|
|
20
|
+
required=(
|
|
21
|
+
"compound_graphs",
|
|
22
|
+
"compound_r_node",
|
|
23
|
+
"compound_i_node",
|
|
24
|
+
"compound_r2r_edge",
|
|
25
|
+
"compound_d2d_edge",
|
|
26
|
+
),
|
|
27
|
+
optional=("get_score",),
|
|
28
|
+
)
|
|
29
|
+
hyperparameters = {
|
|
30
|
+
**{
|
|
31
|
+
name: field
|
|
32
|
+
for name, field in STANDARD_GRAPH_HYPERPARAMETERS.items()
|
|
33
|
+
if name != "linear_layers"
|
|
34
|
+
},
|
|
35
|
+
"activation": Hyperparameter(
|
|
36
|
+
"categorical",
|
|
37
|
+
default="Tanh",
|
|
38
|
+
values=("Tanh", "Sigmoid", "ReLU", "Identity"),
|
|
39
|
+
grid=("Tanh", "Sigmoid", "ReLU"),
|
|
40
|
+
description="Activation applied to the normalized contribution sum.",
|
|
41
|
+
),
|
|
42
|
+
}
|
|
43
|
+
default_optimization_space = (
|
|
44
|
+
*(
|
|
45
|
+
name
|
|
46
|
+
for name in STANDARD_GRAPH_OPTIMIZATION_SPACE
|
|
47
|
+
if name != "linear_layers"
|
|
48
|
+
),
|
|
49
|
+
"activation",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def __init__(self, config):
|
|
53
|
+
if "activation" not in config and "func_f" in config:
|
|
54
|
+
config = {**config, "activation": config["func_f"]}
|
|
55
|
+
super().__init__(config)
|
|
56
|
+
if self.config["target_dim"] != 1:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
"GroupContributionNetwork requires target_dim=1 because its "
|
|
59
|
+
"contribution head produces one scalar per molecule."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
hidden_dim = self.config["hidden_dim"]
|
|
63
|
+
gcn_layers = self.config["conv_layers"]
|
|
64
|
+
dropout = self.config["dropout"]
|
|
65
|
+
|
|
66
|
+
self.embedding_rnode_lin = nn.Sequential(
|
|
67
|
+
nn.Linear(self.config["node_dim"], hidden_dim, bias=False)
|
|
68
|
+
)
|
|
69
|
+
self.embedding_inode_lin = nn.Sequential(
|
|
70
|
+
nn.Linear(1, hidden_dim, bias=False)
|
|
71
|
+
)
|
|
72
|
+
self.embedding_edge_lin = nn.Sequential(
|
|
73
|
+
nn.Linear(self.config["edge_dim"], hidden_dim, bias=False)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
self.GCconv = GCconvolution(hidden_dim, hidden_dim, hidden_dim, nn.LeakyReLU(), gcn_layers, dropout, False, True, 0.1)
|
|
77
|
+
|
|
78
|
+
self.linear = nn.Linear(1,1)
|
|
79
|
+
self.normalize = nn.LayerNorm(1)
|
|
80
|
+
self.func_f = getattr(nn, self.config["activation"])()
|
|
81
|
+
|
|
82
|
+
def forward(self, **kargs):
|
|
83
|
+
graph = kargs.get('compound_graphs', kargs.get('graph'))
|
|
84
|
+
r_node = kargs.get('compound_r_node', kargs.get('r_node'))
|
|
85
|
+
i_node = kargs.get('compound_i_node', kargs.get('i_node'))
|
|
86
|
+
r_edge = kargs.get('compound_r2r_edge', kargs.get('r_edge'))
|
|
87
|
+
d_edge = kargs.get('compound_d2d_edge', kargs.get('d_edge'))
|
|
88
|
+
missing = [
|
|
89
|
+
name
|
|
90
|
+
for name, value in {
|
|
91
|
+
"compound_graphs": graph,
|
|
92
|
+
"compound_r_node": r_node,
|
|
93
|
+
"compound_i_node": i_node,
|
|
94
|
+
"compound_r2r_edge": r_edge,
|
|
95
|
+
"compound_d2d_edge": d_edge,
|
|
96
|
+
}.items()
|
|
97
|
+
if value is None
|
|
98
|
+
]
|
|
99
|
+
if missing:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"GroupContributionNetwork input is missing required fields {missing!r}."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
r_node = r_node.float()
|
|
105
|
+
r_node = self.embedding_rnode_lin(r_node)
|
|
106
|
+
i_node = i_node.float()
|
|
107
|
+
i_node = self.embedding_inode_lin(i_node)
|
|
108
|
+
r_edge = r_edge.float()
|
|
109
|
+
r_edge = self.embedding_edge_lin(r_edge)
|
|
110
|
+
|
|
111
|
+
score = self.GCconv(graph, r_node, r_edge, i_node, d_edge)
|
|
112
|
+
h = graph_sum_pool(relation_graph(graph, 'd_nd', 'd2d'), score)
|
|
113
|
+
h = self.normalize(h)
|
|
114
|
+
h = self.func_f(h)
|
|
115
|
+
h = self.linear(h)
|
|
116
|
+
|
|
117
|
+
if kargs.get('get_score',False):
|
|
118
|
+
return {'prediction':h, 'positive':score}
|
|
119
|
+
|
|
120
|
+
return h
|
|
121
|
+
|
|
122
|
+
network = GroupContributionNetwork
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Deprecated compatibility wrapper for the former ISATPM module name."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
warnings.warn(
|
|
6
|
+
"D4CMPP2.networks.ISATPM_model is deprecated; use "
|
|
7
|
+
"D4CMPP2.networks.ISATPN_model instead.",
|
|
8
|
+
FutureWarning,
|
|
9
|
+
stacklevel=2,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from D4CMPP2.networks.ISATPN_model import ISATPN, network
|
|
13
|
+
|
|
14
|
+
__all__ = ["ISATPN", "network"]
|