chython 2.4__cp312-cp312-win_amd64.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.
- chython/__init__.py +31 -0
- chython/_functions.py +69 -0
- chython/algorithms/__init__.py +18 -0
- chython/algorithms/_isomorphism.cp312-win_amd64.pyd +0 -0
- chython/algorithms/_isomorphism.pyx +189 -0
- chython/algorithms/aromatics/__init__.py +27 -0
- chython/algorithms/aromatics/_rules.py +124 -0
- chython/algorithms/aromatics/kekule.py +530 -0
- chython/algorithms/aromatics/test/__init__.py +18 -0
- chython/algorithms/aromatics/test/test_kekule.py +160 -0
- chython/algorithms/aromatics/test/test_thiele.py +263 -0
- chython/algorithms/aromatics/thiele.py +226 -0
- chython/algorithms/calculate2d/__init__.py +23 -0
- chython/algorithms/calculate2d/clean2d.js +1 -0
- chython/algorithms/calculate2d/molecule.py +149 -0
- chython/algorithms/calculate2d/reaction.py +80 -0
- chython/algorithms/depict.py +523 -0
- chython/algorithms/fingerprints/__init__.py +48 -0
- chython/algorithms/fingerprints/linear.py +208 -0
- chython/algorithms/fingerprints/morgan.py +137 -0
- chython/algorithms/fingerprints/test/__init__.py +18 -0
- chython/algorithms/fingerprints/test/test_linear.py +127 -0
- chython/algorithms/fingerprints/test/test_morgan.py +79 -0
- chython/algorithms/isomorphism.py +667 -0
- chython/algorithms/mapping/__init__.py +28 -0
- chython/algorithms/mapping/_groups.py +94 -0
- chython/algorithms/mapping/_reactions.py +72 -0
- chython/algorithms/mapping/attention.py +216 -0
- chython/algorithms/mapping/fixmapper.py +84 -0
- chython/algorithms/mapping/groups.py +137 -0
- chython/algorithms/mcs.py +202 -0
- chython/algorithms/morgan.py +82 -0
- chython/algorithms/rings.py +551 -0
- chython/algorithms/smiles.py +556 -0
- chython/algorithms/standardize/__init__.py +30 -0
- chython/algorithms/standardize/_charged.py +115 -0
- chython/algorithms/standardize/_groups.py +926 -0
- chython/algorithms/standardize/_metal_organics.py +189 -0
- chython/algorithms/standardize/_reagents.py +44 -0
- chython/algorithms/standardize/_salts.py +68 -0
- chython/algorithms/standardize/molecule.py +479 -0
- chython/algorithms/standardize/reaction.py +429 -0
- chython/algorithms/standardize/resonance.py +196 -0
- chython/algorithms/standardize/salts.py +128 -0
- chython/algorithms/standardize/saturation.py +378 -0
- chython/algorithms/standardize/test/__init__.py +18 -0
- chython/algorithms/standardize/test/test_groups.py +118 -0
- chython/algorithms/stereo.py +1222 -0
- chython/algorithms/tautomers/__init__.py +171 -0
- chython/algorithms/tautomers/_acid.py +59 -0
- chython/algorithms/tautomers/_base.py +116 -0
- chython/algorithms/tautomers/_keto_enol.py +107 -0
- chython/algorithms/tautomers/acid_base.py +214 -0
- chython/algorithms/tautomers/heteroarenes.py +108 -0
- chython/algorithms/tautomers/keto_enol.py +148 -0
- chython/algorithms/tautomers/test/__init__.py +18 -0
- chython/algorithms/tautomers/test/test_tautomers.py +80 -0
- chython/algorithms/test/__init__.py +18 -0
- chython/algorithms/test/test_isomorphism.py +58 -0
- chython/algorithms/test/test_smiles.py +76 -0
- chython/algorithms/x3dom.py +355 -0
- chython/containers/__init__.py +43 -0
- chython/containers/_pack_v2.cp312-win_amd64.pyd +0 -0
- chython/containers/_pack_v2.pyx +271 -0
- chython/containers/_unpack_v0v2.cp312-win_amd64.pyd +0 -0
- chython/containers/_unpack_v0v2.pyx +300 -0
- chython/containers/bonds.py +255 -0
- chython/containers/cgr.py +115 -0
- chython/containers/graph.py +200 -0
- chython/containers/molecule.py +695 -0
- chython/containers/query.py +66 -0
- chython/containers/reaction.py +324 -0
- chython/exceptions.py +114 -0
- chython/files/MRVrw.py +523 -0
- chython/files/PDBrw.py +257 -0
- chython/files/RDFrw.py +298 -0
- chython/files/SDFrw.py +221 -0
- chython/files/__init__.py +29 -0
- chython/files/_convert.py +207 -0
- chython/files/_mapping.py +117 -0
- chython/files/_xyz.cp312-win_amd64.pyd +0 -0
- chython/files/_xyz.pyx +74 -0
- chython/files/daylight/__init__.py +24 -0
- chython/files/daylight/parser.py +157 -0
- chython/files/daylight/smarts.py +107 -0
- chython/files/daylight/smiles.py +245 -0
- chython/files/daylight/test/__init__.py +18 -0
- chython/files/daylight/test/test_daylight_smarts.py +289 -0
- chython/files/daylight/test/test_daylight_smiles.py +101 -0
- chython/files/daylight/test/test_parser.py +118 -0
- chython/files/daylight/test/test_tokenize.py +77 -0
- chython/files/daylight/tokenize.py +376 -0
- chython/files/libinchi/__init__.py +22 -0
- chython/files/libinchi/libinchi.dll +0 -0
- chython/files/libinchi/wrapper.py +555 -0
- chython/files/mdl/__init__.py +25 -0
- chython/files/mdl/emol.py +209 -0
- chython/files/mdl/erxn.py +67 -0
- chython/files/mdl/mol.py +144 -0
- chython/files/mdl/read.py +225 -0
- chython/files/mdl/rxn.py +67 -0
- chython/files/mdl/stereo.py +63 -0
- chython/files/mdl/write.py +165 -0
- chython/files/xyz.py +78 -0
- chython/periodictable/__init__.py +69 -0
- chython/periodictable/base/__init__.py +25 -0
- chython/periodictable/base/dynamic.py +149 -0
- chython/periodictable/base/element.py +452 -0
- chython/periodictable/base/groups.py +90 -0
- chython/periodictable/base/periods.py +46 -0
- chython/periodictable/base/query.py +473 -0
- chython/periodictable/base/vector.py +119 -0
- chython/periodictable/groupI.py +252 -0
- chython/periodictable/groupII.py +223 -0
- chython/periodictable/groupIII.py +1154 -0
- chython/periodictable/groupIV.py +210 -0
- chython/periodictable/groupIX.py +196 -0
- chython/periodictable/groupV.py +192 -0
- chython/periodictable/groupVI.py +183 -0
- chython/periodictable/groupVII.py +162 -0
- chython/periodictable/groupVIII.py +161 -0
- chython/periodictable/groupX.py +166 -0
- chython/periodictable/groupXI.py +164 -0
- chython/periodictable/groupXII.py +158 -0
- chython/periodictable/groupXIII.py +239 -0
- chython/periodictable/groupXIV.py +262 -0
- chython/periodictable/groupXV.py +276 -0
- chython/periodictable/groupXVI.py +457 -0
- chython/periodictable/groupXVII.py +324 -0
- chython/periodictable/groupXVIII.py +260 -0
- chython/reactor/__init__.py +24 -0
- chython/reactor/base.py +212 -0
- chython/reactor/deprotection.py +565 -0
- chython/reactor/groups.py +128 -0
- chython/reactor/reactions/__init__.py +157 -0
- chython/reactor/reactions/_amidation.py +58 -0
- chython/reactor/reactions/_amine_isocyanate.py +52 -0
- chython/reactor/reactions/_buchwald_hartwig.py +50 -0
- chython/reactor/reactions/_esterification.py +49 -0
- chython/reactor/reactions/_macmillan.py +43 -0
- chython/reactor/reactions/_reductive_amination.py +50 -0
- chython/reactor/reactions/_sonogashira.py +49 -0
- chython/reactor/reactions/_sulfonamidation.py +52 -0
- chython/reactor/reactions/_suzuki_miyaura.py +88 -0
- chython/reactor/reactions/ufe.py +99 -0
- chython/reactor/reactor.py +170 -0
- chython/reactor/retro/__init__.py +77 -0
- chython/reactor/retro/_amidation.py +39 -0
- chython/reactor/retro/_aryl_amination.py +32 -0
- chython/reactor/retro/_mitsunobu.py +49 -0
- chython/reactor/retro/_sonogashira.py +49 -0
- chython/reactor/retro/_suzuki_miyaura.py +54 -0
- chython/reactor/scaffold.py +122 -0
- chython/reactor/test/__init__.py +18 -0
- chython/reactor/test/test_deprotection.py +57 -0
- chython/reactor/test/test_reactor.py +40 -0
- chython/reactor/test/test_scaffold.py +57 -0
- chython/reactor/test/test_transformer.py +40 -0
- chython/reactor/transformer.py +62 -0
- chython/utils/__init__.py +57 -0
- chython/utils/free_wilson.py +114 -0
- chython/utils/grid.py +130 -0
- chython/utils/rdkit.py +165 -0
- chython/utils/retro.py +139 -0
- chython/utils/svg.py +57 -0
- chython/utils/test/__init__.py +18 -0
- chython/utils/test/test_rdkit.py +71 -0
- chython-2.4.dist-info/LICENSE +165 -0
- chython-2.4.dist-info/METADATA +101 -0
- chython-2.4.dist-info/RECORD +171 -0
- chython-2.4.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2018-2021 Ramil Nugmanov <nougmanoff@protonmail.com>
|
|
4
|
+
# This file is part of chython.
|
|
5
|
+
#
|
|
6
|
+
# chython is free software; you can redistribute it and/or modify
|
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
9
|
+
# (at your option) any later version.
|
|
10
|
+
#
|
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
# GNU Lesser General Public License for more details.
|
|
15
|
+
#
|
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
#
|
|
19
|
+
from collections import defaultdict
|
|
20
|
+
from itertools import product, combinations, islice
|
|
21
|
+
from typing import Dict, Set, Iterator, Tuple
|
|
22
|
+
from ..containers import molecule
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MCS:
|
|
26
|
+
__slots__ = ()
|
|
27
|
+
|
|
28
|
+
def get_mcs_mapping(self, other: 'molecule.MoleculeContainer', /, *, limit=10000) -> Iterator[Dict[int, int]]:
|
|
29
|
+
"""
|
|
30
|
+
Find maximum common substructure. Based on clique searching in product graph.
|
|
31
|
+
|
|
32
|
+
:param limit: limit tested cliques
|
|
33
|
+
"""
|
|
34
|
+
if not isinstance(other, molecule.MoleculeContainer):
|
|
35
|
+
raise TypeError('MoleculeContainer expected')
|
|
36
|
+
|
|
37
|
+
core_product, full_product = self.__get_product(other)
|
|
38
|
+
if not core_product:
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
# search maximum bonded substructures
|
|
42
|
+
hits = []
|
|
43
|
+
max_atoms = 0
|
|
44
|
+
max_bonds = 0
|
|
45
|
+
for mapping in islice(_clique(full_product), limit):
|
|
46
|
+
if len(mapping) < max_atoms:
|
|
47
|
+
continue
|
|
48
|
+
# search bonds count
|
|
49
|
+
bonds = 0
|
|
50
|
+
seen = set()
|
|
51
|
+
for n in mapping:
|
|
52
|
+
seen.add(n)
|
|
53
|
+
for m in core_product[n]:
|
|
54
|
+
if m not in seen and m in mapping:
|
|
55
|
+
bonds += 1
|
|
56
|
+
if bonds > max_bonds:
|
|
57
|
+
max_bonds = bonds
|
|
58
|
+
max_atoms = len(mapping) - 1 # -1 is ad-hoc
|
|
59
|
+
hits = [mapping]
|
|
60
|
+
elif bonds == max_bonds:
|
|
61
|
+
hits.append(mapping)
|
|
62
|
+
|
|
63
|
+
# search maximal components in substructures
|
|
64
|
+
hits2 = []
|
|
65
|
+
max_component = 0
|
|
66
|
+
for mapping in hits:
|
|
67
|
+
# search components
|
|
68
|
+
components = []
|
|
69
|
+
atoms = mapping.copy()
|
|
70
|
+
while atoms:
|
|
71
|
+
n = atoms.pop()
|
|
72
|
+
seen = {n}
|
|
73
|
+
queue = [n]
|
|
74
|
+
component = []
|
|
75
|
+
while queue:
|
|
76
|
+
n = queue.pop(0)
|
|
77
|
+
component.append(n)
|
|
78
|
+
for m in core_product[n]:
|
|
79
|
+
if m not in seen and m in mapping:
|
|
80
|
+
queue.append(m)
|
|
81
|
+
seen.add(m)
|
|
82
|
+
|
|
83
|
+
components.append(component)
|
|
84
|
+
atoms.difference_update(component)
|
|
85
|
+
|
|
86
|
+
# get max component
|
|
87
|
+
component = max(len(x) for x in components)
|
|
88
|
+
if component > max_component:
|
|
89
|
+
max_component = component
|
|
90
|
+
hits2 = [mapping]
|
|
91
|
+
elif component == max_component:
|
|
92
|
+
hits2.append(mapping)
|
|
93
|
+
yield from (dict(x) for x in hits2)
|
|
94
|
+
|
|
95
|
+
def __get_product(self: 'molecule.MoleculeContainer', other: 'molecule.MoleculeContainer'):
|
|
96
|
+
bonds = self._bonds
|
|
97
|
+
o_bonds = other._bonds
|
|
98
|
+
|
|
99
|
+
s_equal = defaultdict(list) # equal self atoms
|
|
100
|
+
for n, atom in self.atoms():
|
|
101
|
+
s_equal[atom].append(n)
|
|
102
|
+
p_equal = defaultdict(list) # equal other atoms
|
|
103
|
+
for n, atom in other.atoms():
|
|
104
|
+
p_equal[atom].append(n)
|
|
105
|
+
|
|
106
|
+
full_product = {}
|
|
107
|
+
core_product = {}
|
|
108
|
+
equal_atoms = {}
|
|
109
|
+
for atom, ns in s_equal.items():
|
|
110
|
+
ms = p_equal[atom]
|
|
111
|
+
if ms:
|
|
112
|
+
for nm in product(ns, ms):
|
|
113
|
+
full_product[nm] = set()
|
|
114
|
+
core_product[nm] = set()
|
|
115
|
+
for n in ns:
|
|
116
|
+
equal_atoms[n] = ms # memory save
|
|
117
|
+
|
|
118
|
+
seen = set()
|
|
119
|
+
for n, o_ns in equal_atoms.items():
|
|
120
|
+
seen.add(n)
|
|
121
|
+
for m, b in bonds[n].items():
|
|
122
|
+
if m in equal_atoms and m not in seen:
|
|
123
|
+
o_ms = equal_atoms[m]
|
|
124
|
+
for o_n in o_ns:
|
|
125
|
+
node1 = (n, o_n)
|
|
126
|
+
fms = full_product[node1]
|
|
127
|
+
cms = core_product[node1]
|
|
128
|
+
for o_m, o_b in o_bonds[o_n].items():
|
|
129
|
+
if o_m in o_ms and b == o_b:
|
|
130
|
+
node2 = (m, o_m)
|
|
131
|
+
full_product[node2].add(node1)
|
|
132
|
+
core_product[node2].add(node1)
|
|
133
|
+
fms.add(node2)
|
|
134
|
+
cms.add(node2)
|
|
135
|
+
|
|
136
|
+
atoms = core_product
|
|
137
|
+
while atoms:
|
|
138
|
+
new_atoms = set()
|
|
139
|
+
for n in atoms:
|
|
140
|
+
core = core_product[n]
|
|
141
|
+
for nm1, nm2 in combinations(full_product[n], 2):
|
|
142
|
+
n1, m1 = nm1
|
|
143
|
+
n2, m2 = nm2
|
|
144
|
+
if n1 == n2 or m1 == m2:
|
|
145
|
+
continue
|
|
146
|
+
if nm1 in full_product[nm2]:
|
|
147
|
+
continue
|
|
148
|
+
if nm1 not in core and nm2 not in core:
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
full_product[nm1].add(nm2)
|
|
152
|
+
full_product[nm2].add(nm1)
|
|
153
|
+
new_atoms.add(nm1)
|
|
154
|
+
new_atoms.add(nm2)
|
|
155
|
+
atoms = new_atoms
|
|
156
|
+
|
|
157
|
+
return core_product, full_product
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _clique(graph) -> Iterator[Set[Tuple[int, int]]]:
|
|
161
|
+
"""
|
|
162
|
+
clique search
|
|
163
|
+
|
|
164
|
+
adopted from networkx algorithms.clique.find_cliques
|
|
165
|
+
"""
|
|
166
|
+
subgraph = {x for x, y in graph.items() if y} # skip isolated nodes
|
|
167
|
+
if not subgraph:
|
|
168
|
+
return # empty or fully disconnected
|
|
169
|
+
elif len(subgraph) == 2: # dimer
|
|
170
|
+
yield set(subgraph)
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
stack = []
|
|
174
|
+
clique_atoms = [None]
|
|
175
|
+
candidates = subgraph.copy()
|
|
176
|
+
roots = candidates - graph[max(subgraph, key=lambda x: len(graph[x]))]
|
|
177
|
+
|
|
178
|
+
while True:
|
|
179
|
+
if roots:
|
|
180
|
+
root = roots.pop()
|
|
181
|
+
candidates.remove(root)
|
|
182
|
+
clique_atoms[-1] = root
|
|
183
|
+
neighbors = graph[root]
|
|
184
|
+
neighbors_subgraph = subgraph & neighbors
|
|
185
|
+
if not neighbors_subgraph:
|
|
186
|
+
yield set(clique_atoms)
|
|
187
|
+
else:
|
|
188
|
+
neighbors_candidates = candidates & neighbors
|
|
189
|
+
if neighbors_candidates:
|
|
190
|
+
stack.append((subgraph, candidates, roots))
|
|
191
|
+
clique_atoms.append(None)
|
|
192
|
+
subgraph = neighbors_subgraph
|
|
193
|
+
candidates = neighbors_candidates
|
|
194
|
+
roots = candidates - graph[max(subgraph, key=lambda x: len(candidates & graph[x]))]
|
|
195
|
+
elif not stack:
|
|
196
|
+
return
|
|
197
|
+
else:
|
|
198
|
+
clique_atoms.pop()
|
|
199
|
+
subgraph, candidates, roots = stack.pop()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
__all__ = ['MCS']
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2017-2024 Ramil Nugmanov <nougmanoff@protonmail.com>
|
|
4
|
+
# This file is part of chython.
|
|
5
|
+
#
|
|
6
|
+
# chython is free software; you can redistribute it and/or modify
|
|
7
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
9
|
+
# (at your option) any later version.
|
|
10
|
+
#
|
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
# GNU Lesser General Public License for more details.
|
|
15
|
+
#
|
|
16
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
#
|
|
19
|
+
from functools import cached_property
|
|
20
|
+
from itertools import groupby
|
|
21
|
+
from logging import getLogger
|
|
22
|
+
from operator import itemgetter
|
|
23
|
+
from typing import Dict, TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
logger = getLogger('chython.morgan')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from chython.containers import MoleculeContainer
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Morgan:
|
|
34
|
+
__slots__ = ()
|
|
35
|
+
|
|
36
|
+
@cached_property
|
|
37
|
+
def atoms_order(self: 'MoleculeContainer') -> Dict[int, int]:
|
|
38
|
+
"""
|
|
39
|
+
Morgan like algorithm for graph nodes ordering
|
|
40
|
+
|
|
41
|
+
:return: dict of atom-order pairs
|
|
42
|
+
"""
|
|
43
|
+
if not self: # for empty containers
|
|
44
|
+
return {}
|
|
45
|
+
elif len(self) == 1: # optimize single atom containers
|
|
46
|
+
return dict.fromkeys(self, 1)
|
|
47
|
+
return _morgan({n: hash(a) for n, a in self.atoms()}, self.int_adjacency)
|
|
48
|
+
|
|
49
|
+
@cached_property
|
|
50
|
+
def int_adjacency(self: 'MoleculeContainer') -> Dict[int, Dict[int, int]]:
|
|
51
|
+
"""
|
|
52
|
+
Adjacency with integer-coded bonds.
|
|
53
|
+
"""
|
|
54
|
+
return {n: {m: hash(b) for m, b in mb.items()} for n, mb in self._bonds.items()}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _morgan(atoms: Dict[int, int], bonds: Dict[int, Dict[int, int]]) -> Dict[int, int]:
|
|
58
|
+
tries = len(atoms) - 1
|
|
59
|
+
numb = len(set(atoms.values()))
|
|
60
|
+
stab = old_numb = 0
|
|
61
|
+
|
|
62
|
+
for _ in range(tries):
|
|
63
|
+
atoms = {n: hash((atoms[n], *(x for x in sorted((atoms[m], b) for m, b in ms.items()) for x in x)))
|
|
64
|
+
for n, ms in bonds.items()}
|
|
65
|
+
old_numb, numb = numb, len(set(atoms.values()))
|
|
66
|
+
if numb == len(atoms): # each atom now unique
|
|
67
|
+
break
|
|
68
|
+
elif numb == old_numb: # not changed. molecules like benzene
|
|
69
|
+
if stab == 3:
|
|
70
|
+
break
|
|
71
|
+
stab += 1
|
|
72
|
+
elif stab: # changed unique atoms number. reset stability check.
|
|
73
|
+
stab = 0
|
|
74
|
+
else:
|
|
75
|
+
if numb < old_numb:
|
|
76
|
+
logger.warning('number of attempts exceeded. uniqueness has decreased.')
|
|
77
|
+
|
|
78
|
+
return {n: i for i, (_, g) in enumerate(groupby(sorted(atoms.items(), key=itemgetter(1)), key=itemgetter(1)),
|
|
79
|
+
start=1) for n, _ in g}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
__all__ = ['Morgan']
|