Spaces:
Sleeping
Sleeping
| import torch | |
| import numpy as np | |
| from rdkit import Chem | |
| from rdkit.Chem.Descriptors import ExactMolWt | |
| from torch_geometric.data import Data as GraphData | |
| ALLOWED_ATOMIC_NUMS = {6, 7, 8, 9, 15, 16, 17, 35} | |
| MIN_NUM_ATOMS = 3 | |
| MAX_NUM_ATOMS = 60 | |
| def get_node_feats(mol: Chem.Mol, atom: Chem.Atom): | |
| ''' | |
| :param mol: The mol from which the node features will be computed. Currently not used | |
| :param atom: The atom from which the node features will be computed | |
| :return: | |
| ''' | |
| atom_num = atom.GetAtomicNum() | |
| valence = atom.GetTotalValence() | |
| charge = atom.GetFormalCharge() | |
| degree = atom.GetDegree() | |
| is_aromatic = atom.GetIsAromatic() | |
| return [float(elem) for elem in [atom_num, valence, charge, degree, is_aromatic]] | |
| # return [float(elem) for elem in [atom_num, valence, charge, is_aromatic]] | |
| # results = one_of_k_encoding_unk( | |
| # atom.GetSymbol(), | |
| # [ | |
| # 'B', | |
| # 'C', | |
| # 'N', | |
| # 'O', | |
| # 'F', | |
| # 'Si', | |
| # 'P', | |
| # 'S', | |
| # 'Cl', | |
| # 'As', | |
| # 'Se', | |
| # 'Br', | |
| # 'Te', | |
| # 'I', | |
| # 'At', | |
| # 'other' | |
| # ]) + one_of_k_encoding(atom.GetDegree(), | |
| # [0, 1, 2, 3, 4, 5]) + \ | |
| # [atom.GetFormalCharge(), atom.GetNumRadicalElectrons()] + \ | |
| # one_of_k_encoding_unk(atom.GetHybridization(), [ | |
| # Chem.rdchem.HybridizationType.SP, Chem.rdchem.HybridizationType.SP2, | |
| # Chem.rdchem.HybridizationType.SP3, Chem.rdchem.HybridizationType. | |
| # SP3D, Chem.rdchem.HybridizationType.SP3D2, 'other' | |
| # ]) + [atom.GetIsAromatic()] | |
| def get_bond_feats(mol: Chem.Mol, bond: Chem.Bond): | |
| ''' | |
| :param mol: The mol from which the edge features will be computed. Currently not used | |
| :param bond: The bond from which the edge features will be computed | |
| :return: | |
| ''' | |
| if bond.GetIsAromatic(): | |
| bond_num = 1.5 | |
| else: | |
| bond_num = bond.GetBondType() | |
| bond_num = float(bond_num) | |
| if bond_num > 3: | |
| bond_num = 4 | |
| # return [bond_num ] | |
| bondInfo = [bond_num, int(bond.GetIsAromatic()), int(bond.GetIsConjugated()), int(bond.IsInRing()) ] | |
| # bondInfo += [["STEREONONE", "STEREOANY", "STEREOZ", "STEREOE"].index(str(bond.GetStereo()))] | |
| return bondInfo | |
| def smiles_to_graph(smi): | |
| mol = Chem.MolFromSmiles(smi) | |
| if mol is None: | |
| return None | |
| # remove stereo information, such as inward and outward edges | |
| Chem.RemoveStereochemistry(mol) | |
| nodes = [] | |
| edges_index, edges_attr = [], [] | |
| cur_atom_id = 0 | |
| rdkit_atomId_to_atom_id = {} | |
| for bond in mol.GetBonds(): | |
| atom1_idx = bond.GetBeginAtomIdx() | |
| atom2_idx = bond.GetEndAtomIdx() | |
| atom1 = mol.GetAtomWithIdx(atom1_idx) | |
| atom2 = mol.GetAtomWithIdx(atom2_idx) | |
| atomicNum1 = atom1.GetAtomicNum() | |
| atomicNum2 = atom2.GetAtomicNum() | |
| if atomicNum1 == 0 or atomicNum2 == 0: | |
| continue | |
| if atomicNum1 not in ALLOWED_ATOMIC_NUMS or atomicNum2 not in ALLOWED_ATOMIC_NUMS: | |
| return None | |
| if atom1_idx not in rdkit_atomId_to_atom_id: | |
| rdkit_atomId_to_atom_id[atom1_idx] = cur_atom_id | |
| nodes.append( get_node_feats(mol, atom1) ) | |
| cur_atom_id += 1 | |
| if atom2_idx not in rdkit_atomId_to_atom_id: | |
| rdkit_atomId_to_atom_id[atom2_idx] = cur_atom_id | |
| nodes.append( get_node_feats(mol, atom2) ) | |
| cur_atom_id += 1 | |
| idx1, idx2 = rdkit_atomId_to_atom_id[atom1_idx], rdkit_atomId_to_atom_id[atom2_idx] | |
| # print(atom1_idx, idx1, atom2_idx, idx2) | |
| edges_index.append([idx1, idx2]) | |
| edges_index.append([idx2, idx1]) | |
| bond_feats = get_bond_feats(mol, bond) | |
| edges_attr.extend([bond_feats, bond_feats]) | |
| x = np.array(nodes, dtype=np.float32) | |
| if x.shape[0] < MIN_NUM_ATOMS or x.shape[0] > MAX_NUM_ATOMS: | |
| return None | |
| edges_index = np.array( edges_index).T.copy().astype( np.int64 ) | |
| edges_attr = np.array(edges_attr, dtype=np.float32) | |
| graph_dict = dict(x=x, edge_index=edges_index, edge_attr=edges_attr) | |
| graph = GraphData(**{key: torch.tensor(val) for key, val in graph_dict.items()} ) | |
| return graph | |
| def fromPerGramToPerMMolPrice(price, smi): | |
| mol = Chem.MolFromSmiles(smi) | |
| if mol is None: | |
| return None | |
| return price * ExactMolWt(mol) / 1000 | |
| def compute_nodes_degree(graphs, max_degree=7): | |
| from torch_geometric.utils import degree | |
| import torch | |
| deg = torch.zeros(max_degree, dtype=torch.long) | |
| if graphs is None: | |
| return deg | |
| for data in graphs: | |
| d = degree(data.edge_index[1], num_nodes=data.num_nodes, dtype=torch.long) | |
| deg += torch.bincount(d, minlength=deg.numel())[:deg.numel()] | |
| return deg | |
| def test(smi = "C1=CC=C(C=C1)CC(C(=O)O)N"): | |
| mol = Chem.MolFromSmiles(smi) | |
| # print(mol) | |
| graph = smiles_to_graph(smi) | |
| print(graph.keys) | |
| print(graph.x) | |
| print(graph.edge_index) | |
| print( graph.edge_attr ) | |
| print( graph.x.shape, graph.edge_index.shape, graph.edge_attr.shape) | |
| for idx in range(mol.GetNumAtoms()): | |
| mol.GetAtomWithIdx(idx).SetProp('molAtomMapNumber', str(mol.GetAtomWithIdx(idx).GetIdx())) | |
| from matplotlib import pyplot as plt; from rdkit.Chem import Draw; plt.imshow(Draw.MolsToGridImage([mol], molsPerRow=2)); plt.show() | |
| if __name__ == "__main__": | |
| smis_list = ["[C@H](C)1CCCO1", "O[C@@H](N)C", "C1=CC=C(C=C1)CC(C(=O)O)N"] | |
| for smi in smis_list: | |
| test(smi) |