MolMiner-graphs 0.0.1__tar.gz

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 (33) hide show
  1. molminer_graphs-0.0.1/PKG-INFO +58 -0
  2. molminer_graphs-0.0.1/README.md +28 -0
  3. molminer_graphs-0.0.1/mminer_graphs/.DS_Store +0 -0
  4. molminer_graphs-0.0.1/mminer_graphs/__init__.py +13 -0
  5. molminer_graphs-0.0.1/mminer_graphs/graph/__init__.py +10 -0
  6. molminer_graphs-0.0.1/mminer_graphs/graph/graph.py +277 -0
  7. molminer_graphs-0.0.1/mminer_graphs/graph/graph_cluster.py +450 -0
  8. molminer_graphs-0.0.1/mminer_graphs/graph/graph_structure.py +465 -0
  9. molminer_graphs-0.0.1/mminer_graphs/graph/graph_utils.py +220 -0
  10. molminer_graphs-0.0.1/mminer_graphs/hyperparams/__init__.py +7 -0
  11. molminer_graphs-0.0.1/mminer_graphs/hyperparams/mlx_opt.py +469 -0
  12. molminer_graphs-0.0.1/mminer_graphs/hyperparams/opt_plot.py +213 -0
  13. molminer_graphs-0.0.1/mminer_graphs/hyperparams/torch_opt.py +372 -0
  14. molminer_graphs-0.0.1/mminer_graphs/mlx_models/__init__.py +10 -0
  15. molminer_graphs-0.0.1/mminer_graphs/mlx_models/crossval.py +236 -0
  16. molminer_graphs-0.0.1/mminer_graphs/mlx_models/mlx_latent_space.py +427 -0
  17. molminer_graphs-0.0.1/mminer_graphs/mlx_models/mlx_modeling.py +2237 -0
  18. molminer_graphs-0.0.1/mminer_graphs/mlx_models/mlx_summary.py +149 -0
  19. molminer_graphs-0.0.1/mminer_graphs/mlx_models/mlx_utils.py +84 -0
  20. molminer_graphs-0.0.1/mminer_graphs/model/__init__.py +11 -0
  21. molminer_graphs-0.0.1/mminer_graphs/model/crossval.py +222 -0
  22. molminer_graphs-0.0.1/mminer_graphs/model/graph_modeling.py +2150 -0
  23. molminer_graphs-0.0.1/mminer_graphs/model/latent_space.py +438 -0
  24. molminer_graphs-0.0.1/mminer_graphs/model/model_utils.py +194 -0
  25. molminer_graphs-0.0.1/mminer_graphs/plot/__init__.py +9 -0
  26. molminer_graphs-0.0.1/mminer_graphs/plot/mlx_plot.py +750 -0
  27. molminer_graphs-0.0.1/mminer_graphs/plot/plot.py +828 -0
  28. molminer_graphs-0.0.1/mminer_graphs/utils/func_ring.csv +23 -0
  29. molminer_graphs-0.0.1/mminer_graphs/visualizer/__init__.py +7 -0
  30. molminer_graphs-0.0.1/mminer_graphs/visualizer/explainer.py +0 -0
  31. molminer_graphs-0.0.1/mminer_graphs/visualizer/graph_visualizer.py +267 -0
  32. molminer_graphs-0.0.1/pyproject.toml +58 -0
  33. molminer_graphs-0.0.1/pyproject.toml.orig +54 -0
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.3
2
+ Name: MolMiner-graphs
3
+ Version: 0.0.1
4
+ Summary: For Creaming Molecular Graph Models
5
+ Author: Tony Eight Lin
6
+ Author-email: Tony Eight Lin <tonyelin@tmu.edu.tw>
7
+ License: Apache-2.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: matplotlib>=3.10.3
14
+ Requires-Dist: numpy>=2.0
15
+ Requires-Dist: pandas>=2.1
16
+ Requires-Dist: rdkit>=2023.9.5
17
+ Requires-Dist: seaborn>=0.12.2
18
+ Requires-Dist: scikit-learn>=1.6.1
19
+ Requires-Dist: torch>=2.1.0
20
+ Requires-Dist: torchinfo>=1.8.0
21
+ Requires-Dist: torch-geometric>=2.6.1
22
+ Requires-Dist: torchviz>=0.0.3
23
+ Requires-Dist: tabulate>=0.9.0
24
+ Requires-Dist: optuna>=4.9.0
25
+ Requires-Dist: mlx>=0.31.1 ; extra == 'mlx'
26
+ Requires-Dist: mlx-graphs>=0.0.9 ; extra == 'mlx'
27
+ Requires-Python: >=3.11
28
+ Provides-Extra: mlx
29
+ Description-Content-Type: text/markdown
30
+
31
+ # MolMiner - Graphs
32
+ [![Molecular Miner - Graphs](https://img.shields.io/pypi/v/MolMiner-graphs.svg?label=MolMiner-Graphs&style=flat)]()
33
+ [![Python Versions](https://img.shields.io/badge/python-3.11+-blue.svg?logo=python&logoColor=white)](https://pypi.org/project/mlx-mol-cluster/)
34
+ [![pytorch](https://img.shields.io/badge/PyTorch-2.1.0+-EE4C2C.svg?style=flat&logo=pytorch)](https://pytorch.org)
35
+ [![MLX](https://img.shields.io/badge/MLX-0.31.0+-black?logo=apple&labelColor=gray)](https://github.com/ml-explore/mlx)
36
+
37
+ ## Overview
38
+ This repo is a companion to the MolMiner project. It is focused on building deep learning molecular graph models built
39
+ using PyTorch or MLX.
40
+
41
+ ## Install
42
+
43
+ Installation of MolMiner-graphs is done as follows:
44
+
45
+ ```bash
46
+ pip install MolMiner-graphs
47
+ ```
48
+
49
+ By default, this will only install the PyTorch backend. For users interested in MLX, this requires workstations using
50
+ Apple Silicon and can be installed as follows:
51
+ ```bash
52
+ pip install "MolMiner-graphs[mlx]"
53
+ ```
54
+ This will install both PyTorch and MLX on the Apple Silicon workstation.
55
+
56
+ ## Tutorials
57
+
58
+ Tutorials and examples of how to use MolMiner-Graphs can be seen at the [Tutorials](tutorials/) directory.
@@ -0,0 +1,28 @@
1
+ # MolMiner - Graphs
2
+ [![Molecular Miner - Graphs](https://img.shields.io/pypi/v/MolMiner-graphs.svg?label=MolMiner-Graphs&style=flat)]()
3
+ [![Python Versions](https://img.shields.io/badge/python-3.11+-blue.svg?logo=python&logoColor=white)](https://pypi.org/project/mlx-mol-cluster/)
4
+ [![pytorch](https://img.shields.io/badge/PyTorch-2.1.0+-EE4C2C.svg?style=flat&logo=pytorch)](https://pytorch.org)
5
+ [![MLX](https://img.shields.io/badge/MLX-0.31.0+-black?logo=apple&labelColor=gray)](https://github.com/ml-explore/mlx)
6
+
7
+ ## Overview
8
+ This repo is a companion to the MolMiner project. It is focused on building deep learning molecular graph models built
9
+ using PyTorch or MLX.
10
+
11
+ ## Install
12
+
13
+ Installation of MolMiner-graphs is done as follows:
14
+
15
+ ```bash
16
+ pip install MolMiner-graphs
17
+ ```
18
+
19
+ By default, this will only install the PyTorch backend. For users interested in MLX, this requires workstations using
20
+ Apple Silicon and can be installed as follows:
21
+ ```bash
22
+ pip install "MolMiner-graphs[mlx]"
23
+ ```
24
+ This will install both PyTorch and MLX on the Apple Silicon workstation.
25
+
26
+ ## Tutorials
27
+
28
+ Tutorials and examples of how to use MolMiner-Graphs can be seen at the [Tutorials](tutorials/) directory.
@@ -0,0 +1,13 @@
1
+ """
2
+ Initialize submodules
3
+ """
4
+ from .graph import AtomGraph, GLOBAL_VALUES
5
+ from .graph.graph_structure import PharmacophoreGraph, FunctionalGraph
6
+ from .graph.graph_cluster import JunctionTreeGraph, ClusterGraph
7
+ from .visualizer.graph_visualizer import Graph
8
+ from .model.graph_modeling import MolToGraph, Base, GCNNet, GINNet, GINENet, GSAGENet, GATNet, EarlyStopping
9
+ from .model.crossval import CrossVal
10
+ from .plot import Plots
11
+
12
+ __all__ = ["AtomGraph", "GLOBAL_VALUES", "PharmacophoreGraph", "FunctionalGraph", "JunctionTreeGraph", "ClusterGraph",
13
+ "Graph", "MolToGraph", "Plots"]
@@ -0,0 +1,10 @@
1
+ """
2
+ Initialize classes for graph features
3
+ """
4
+ from .graph import MolGraph, AtomGraph, GLOBAL_VALUES
5
+ from .graph_cluster import JunctionTreeGraph, ClusterGraph
6
+ from .graph_structure import PharmacophoreGraph, FunctionalGraph
7
+ from .graph_utils import *
8
+
9
+ __all__ = ["MolGraph", "AtomGraph", "JunctionTreeGraph", "ClusterGraph", "PharmacophoreGraph", "FunctionalGraph",
10
+ "GLOBAL_VALUES"]
@@ -0,0 +1,277 @@
1
+ """
2
+ Convert molecules into graph
3
+ Adapted from MMGX (https://github.com/ohuelab/MMGX.git)
4
+ Paper: "Enhancing property and activity prediction and interpretation using multiple molecular graph representations with MMGX"
5
+ Communications Chemistry (2024), https://doi.org/10.1038/s42004-024-01084-4
6
+ """
7
+
8
+ from mminer_graphs.graph.graph_utils import *
9
+ from rdkit.Chem import rdchem
10
+ from rdkit import Chem
11
+
12
+ __all__ = ["MolGraph", "AtomGraph", "GLOBAL_VALUES"]
13
+
14
+
15
+ # print global definition
16
+ def GLOBAL_VALUES():
17
+ # atom features
18
+ # print('ATOM Features:', ATOM_NUM) #todo add additional charges into list
19
+ print('ATOM Features: 142')
20
+ for (x, item) in enumerate(ATOMS):
21
+ print(f" {x}: {item}")
22
+ # degree
23
+ print(f" Atom Degree: 11")
24
+ print(f" Atom Valence: 7")
25
+ print(f" Formal charge: 5")
26
+ print(f" Hybridization: 6")
27
+ print(f" Aromaticity: 1")
28
+ print(f" Number of Hydrogens: 5")
29
+ print(f" Chirality: 5")
30
+ print(f" Ring status: 1")
31
+ print(f" Mass: 1")
32
+
33
+ # bond features
34
+ print('BOND Features:', BOND_NUM)
35
+ print(' Bond types:')
36
+ for (x, item) in enumerate(BONDS):
37
+ print(f' {x}: {item}')
38
+
39
+ print(' Bond flags:')
40
+ for (x, item) in enumerate(BOND_FEATURES):
41
+ print(f' {x + len(BONDS)}: {item}')
42
+
43
+ print(' Bond stereo:')
44
+ for (x, item) in enumerate(BOND_STEREO):
45
+ print(f' {x + len(BONDS) + len(BOND_FEATURES)}: {item}')
46
+
47
+ print(f' {len(BONDS) + len(BOND_FEATURES) + len(BOND_STEREO)}: stereo_atoms_present')
48
+
49
+ # features for functional graph
50
+ print('FUNCTIONAL Graph Features:', FUNCTIONAL_NUM)
51
+ for x in range(fparams.GetNumFuncGroups()):
52
+ if x == 27:
53
+ print('SWAP WITH 29')
54
+ if x == 29:
55
+ print('SWAP WITH 27')
56
+ print(f"{x}: {fparams.GetFuncGroup(x).GetProp('_Name')} {Chem.MolToSmarts(fparams.GetFuncGroup(x))}")
57
+ # functional graph bond features
58
+ print('FUNC_BOND Graph Bond Features', FUNCBOND_NUM)
59
+ for (x, item) in enumerate(FUNCBOND):
60
+ print(f"{x}: {item}")
61
+
62
+ print('FUNC_RING Functional Graph Rings', FUNCRING_NUM)
63
+ print(func_ring)
64
+
65
+
66
+ class MolGraph:
67
+ def __init__(self, smi=None, with_atom_index=True):
68
+ self.smi = mol_to_smiles(smiles_to_mol(smi, with_atom_index=with_atom_index)) # standardize smi
69
+ self.mol = smiles_to_mol(self.smi)
70
+ self.cliques = None
71
+ self.edges = None
72
+ self.graph_size = 0
73
+ self.node_index = []
74
+ self.node_features = []
75
+ self.edge_index = []
76
+ self.edge_features = []
77
+
78
+ def getMoleculeGraph(self):
79
+ return self.mol, self.smi, self.cliques, self.edges, self.graph_size, \
80
+ self.node_index, self.node_features, self.edge_index, self.edge_features,
81
+
82
+ def __str__(self):
83
+ if not isinstance(self, AtomGraph):
84
+ return "Molecule Graph: " + self.smi + "\n" + \
85
+ "Node Index:" + str(self.node_index) + "\n" + \
86
+ "Edge Index:" + str(self.edge_index) + "\n" + \
87
+ "Cliques:" + str(self.cliques)
88
+ else:
89
+ return "Molecule Graph: " + self.smi + "\n" + \
90
+ "Node Index:" + str(self.node_index) + "\n" + \
91
+ "Edge Index:" + str(self.edge_index)
92
+
93
+
94
+ # ATOM-based
95
+ class AtomGraph(MolGraph):
96
+ def __init__(self, smi, normalize=True, with_atom_index=True):
97
+ super().__init__(smi)
98
+ self.smi = mol_to_smiles(smiles_to_mol(smi, with_atom_index=with_atom_index))
99
+ self.mol = smiles_to_mol(self.smi)
100
+ self.normalize = normalize
101
+ graph_size, node_index, node_attr, edge_index, edge_attr = self.mol_to_atom_graph(self.mol, normalize)
102
+ self.graph_size = graph_size
103
+ self.node_index = node_index
104
+ self.node_features = node_attr
105
+ self.edge_index = edge_index
106
+ self.edge_features = edge_attr
107
+ # atom graph only
108
+ self.cliques = [[x] for x in node_index]
109
+ self.edges = edge_index
110
+
111
+ def mol_to_atom_graph(self, mol=None, normalize=True):
112
+ """
113
+ Convert molecules into graph embeddings. Will run default graph featurization in this order - graph_size,
114
+ node_index, node_attr, edge_index, edge_attr.
115
+ :param mol: Chem.rdchem.Mol
116
+ A molecule rendered in RDKit for graph featurization.
117
+ :param normalize: bool
118
+ Convert vector into a distibution where values add up to 1.
119
+ :return:
120
+ """
121
+ if mol is None:
122
+ mol = self.mol
123
+
124
+ graph_size = mol.GetNumAtoms()
125
+
126
+ node_attr = []
127
+ node_index = []
128
+ for atom in mol.GetAtoms():
129
+ # print(atom.GetAtomicNum())
130
+ node_feature = self._get_atom_feats(atom, chirality=True)
131
+ node_attr.append(list(node_feature))
132
+ node_index.append(atom.GetIdx())
133
+
134
+ edge_attr = []
135
+ edge_index = []
136
+ # bidirectional edges
137
+ for bond in mol.GetBonds():
138
+ edge_feature = self._get_bond_feats(bond)
139
+ # from A -> B
140
+ edge_attr.append(list(edge_feature))
141
+ edge_index.append((bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()))
142
+
143
+ # from B -> A
144
+ edge_attr.append(list(edge_feature))
145
+ edge_index.append((bond.GetEndAtomIdx(), bond.GetBeginAtomIdx()))
146
+
147
+ return graph_size, node_index, node_attr, edge_index, edge_attr
148
+
149
+ @staticmethod
150
+ def _get_atom_feats(atom, chirality: bool = True):
151
+ """
152
+ Get atom features (Total: 142). The features are ordered as follows:
153
+ - atomic number (100)
154
+ - degree (11)
155
+ - implicit valence (7)
156
+ - formal charge (5)
157
+ - hybridization (6)
158
+ - aromaticity (1)
159
+ - number of hydrogens (5)
160
+ - chirality (5)
161
+ - ring status (1)
162
+ - mass (1)
163
+ """
164
+ atom_feats = []
165
+
166
+ # atomic number
167
+ atom_feats += one_hot_encode(atom.GetAtomicNum(), ATOMIC_NUM)
168
+
169
+ # atom degree (number of directly bonded atoms)
170
+ atom_feats += one_hot_encode(atom.GetDegree(), list(range(11)))
171
+
172
+ # implicit valence
173
+ atom_feats += one_hot_encode(atom.GetValence(Chem.ValenceType.IMPLICIT), list(range(7)))
174
+
175
+ # formal charge
176
+ formal_charges = [-2, -1, 0, 1, 2]
177
+ atom_feats += one_hot_encode(atom.GetFormalCharge(), formal_charges)
178
+
179
+ # hybridization
180
+ hybridizations = [
181
+ Chem.rdchem.HybridizationType.SP,
182
+ Chem.rdchem.HybridizationType.SP2,
183
+ Chem.rdchem.HybridizationType.SP3,
184
+ Chem.rdchem.HybridizationType.SP3D,
185
+ Chem.rdchem.HybridizationType.SP3D2,
186
+ 'Unknown'
187
+ ]
188
+ atom_feats += one_hot_encode(atom.GetHybridization(), hybridizations)
189
+
190
+ # aromatic
191
+ atom_feats += [int(atom.GetIsAromatic())]
192
+
193
+ # number of Hs
194
+ atom_feats += one_hot_encode(atom.GetTotalNumHs(), list(range(5)))
195
+
196
+ # chirality
197
+ if chirality:
198
+ try:
199
+ chiral_tags = [
200
+ rdchem.ChiralType.CHI_UNSPECIFIED,
201
+ rdchem.ChiralType.CHI_TETRAHEDRAL_CW,
202
+ rdchem.ChiralType.CHI_TETRAHEDRAL_CCW,
203
+ rdchem.ChiralType.CHI_OTHER
204
+ ]
205
+ atom_feats += one_hot_encode(atom.GetChiralTag(), chiral_tags)
206
+ except:
207
+ atom_feats += [0, 0]
208
+ atom_feats += [int(atom.HasProp('_ChiralityPossible'))]
209
+
210
+ # check if atom is in ring
211
+ atom_feats += [np.multiply(atom.IsInRing(), 1)]
212
+
213
+ # atom mass
214
+ atom_feats += [atom.GetMass() / 100] # normalize atomic mass between 0 and 1
215
+
216
+ return np.array(atom_feats, dtype=np.float32)
217
+
218
+ @staticmethod
219
+ def _get_bond_feats(bond, chirality: bool = True):
220
+ """
221
+ Get bond features (Total: 11). The features are ordered as follows:
222
+ - numm bond (1)
223
+ - bond type (4)
224
+ - conjugated (1)
225
+ - aromatic (1)
226
+ - ring (1)
227
+ - stereo info (7)
228
+ - stereo atom present (1)
229
+ """
230
+ # bond features
231
+ bond_feats = []
232
+
233
+ # null bond
234
+ bond_feats += [int(bond is None)]
235
+ if bond is None:
236
+ return np.zeros(16, dtype=np.float32)
237
+
238
+ # bond info
239
+ bond_type = [
240
+ Chem.rdchem.BondType.SINGLE,
241
+ Chem.rdchem.BondType.DOUBLE,
242
+ Chem.rdchem.BondType.TRIPLE,
243
+ Chem.rdchem.BondType.AROMATIC,
244
+ ]
245
+ bond_feats += one_hot_encode(bond.GetBondType(), bond_type)
246
+
247
+ # bond conjugation and ring status
248
+ bond_feats += [int(bond.GetIsConjugated())]
249
+ bond_feats += [int(bond.GetIsAromatic())]
250
+ bond_feats += [int(bond.IsInRing())]
251
+
252
+ # stereo info
253
+ stereo_feats = [
254
+ Chem.rdchem.BondStereo.STEREONONE,
255
+ Chem.rdchem.BondStereo.STEREOANY,
256
+ Chem.rdchem.BondStereo.STEREOZ,
257
+ Chem.rdchem.BondStereo.STEREOE,
258
+ Chem.rdchem.BondStereo.STEREOCIS,
259
+ Chem.rdchem.BondStereo.STEREOTRANS,
260
+ 'Unknown'
261
+ ]
262
+ bond_feats += one_hot_encode(bond.GetStereo(), stereo_feats)
263
+
264
+ if chirality:
265
+ try:
266
+ stereo_atoms = bond.GetStereoAtoms()
267
+ bond_feats.append(len(stereo_atoms) > 0)
268
+ except Exception:
269
+ bond_feats.append(False)
270
+
271
+ return np.array(bond_feats, dtype=np.float32)
272
+
273
+
274
+ if __name__ == "__main__":
275
+ import doctest
276
+
277
+ doctest.testmod()