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.
Files changed (171) hide show
  1. chython/__init__.py +31 -0
  2. chython/_functions.py +69 -0
  3. chython/algorithms/__init__.py +18 -0
  4. chython/algorithms/_isomorphism.cp312-win_amd64.pyd +0 -0
  5. chython/algorithms/_isomorphism.pyx +189 -0
  6. chython/algorithms/aromatics/__init__.py +27 -0
  7. chython/algorithms/aromatics/_rules.py +124 -0
  8. chython/algorithms/aromatics/kekule.py +530 -0
  9. chython/algorithms/aromatics/test/__init__.py +18 -0
  10. chython/algorithms/aromatics/test/test_kekule.py +160 -0
  11. chython/algorithms/aromatics/test/test_thiele.py +263 -0
  12. chython/algorithms/aromatics/thiele.py +226 -0
  13. chython/algorithms/calculate2d/__init__.py +23 -0
  14. chython/algorithms/calculate2d/clean2d.js +1 -0
  15. chython/algorithms/calculate2d/molecule.py +149 -0
  16. chython/algorithms/calculate2d/reaction.py +80 -0
  17. chython/algorithms/depict.py +523 -0
  18. chython/algorithms/fingerprints/__init__.py +48 -0
  19. chython/algorithms/fingerprints/linear.py +208 -0
  20. chython/algorithms/fingerprints/morgan.py +137 -0
  21. chython/algorithms/fingerprints/test/__init__.py +18 -0
  22. chython/algorithms/fingerprints/test/test_linear.py +127 -0
  23. chython/algorithms/fingerprints/test/test_morgan.py +79 -0
  24. chython/algorithms/isomorphism.py +667 -0
  25. chython/algorithms/mapping/__init__.py +28 -0
  26. chython/algorithms/mapping/_groups.py +94 -0
  27. chython/algorithms/mapping/_reactions.py +72 -0
  28. chython/algorithms/mapping/attention.py +216 -0
  29. chython/algorithms/mapping/fixmapper.py +84 -0
  30. chython/algorithms/mapping/groups.py +137 -0
  31. chython/algorithms/mcs.py +202 -0
  32. chython/algorithms/morgan.py +82 -0
  33. chython/algorithms/rings.py +551 -0
  34. chython/algorithms/smiles.py +556 -0
  35. chython/algorithms/standardize/__init__.py +30 -0
  36. chython/algorithms/standardize/_charged.py +115 -0
  37. chython/algorithms/standardize/_groups.py +926 -0
  38. chython/algorithms/standardize/_metal_organics.py +189 -0
  39. chython/algorithms/standardize/_reagents.py +44 -0
  40. chython/algorithms/standardize/_salts.py +68 -0
  41. chython/algorithms/standardize/molecule.py +479 -0
  42. chython/algorithms/standardize/reaction.py +429 -0
  43. chython/algorithms/standardize/resonance.py +196 -0
  44. chython/algorithms/standardize/salts.py +128 -0
  45. chython/algorithms/standardize/saturation.py +378 -0
  46. chython/algorithms/standardize/test/__init__.py +18 -0
  47. chython/algorithms/standardize/test/test_groups.py +118 -0
  48. chython/algorithms/stereo.py +1222 -0
  49. chython/algorithms/tautomers/__init__.py +171 -0
  50. chython/algorithms/tautomers/_acid.py +59 -0
  51. chython/algorithms/tautomers/_base.py +116 -0
  52. chython/algorithms/tautomers/_keto_enol.py +107 -0
  53. chython/algorithms/tautomers/acid_base.py +214 -0
  54. chython/algorithms/tautomers/heteroarenes.py +108 -0
  55. chython/algorithms/tautomers/keto_enol.py +148 -0
  56. chython/algorithms/tautomers/test/__init__.py +18 -0
  57. chython/algorithms/tautomers/test/test_tautomers.py +80 -0
  58. chython/algorithms/test/__init__.py +18 -0
  59. chython/algorithms/test/test_isomorphism.py +58 -0
  60. chython/algorithms/test/test_smiles.py +76 -0
  61. chython/algorithms/x3dom.py +355 -0
  62. chython/containers/__init__.py +43 -0
  63. chython/containers/_pack_v2.cp312-win_amd64.pyd +0 -0
  64. chython/containers/_pack_v2.pyx +271 -0
  65. chython/containers/_unpack_v0v2.cp312-win_amd64.pyd +0 -0
  66. chython/containers/_unpack_v0v2.pyx +300 -0
  67. chython/containers/bonds.py +255 -0
  68. chython/containers/cgr.py +115 -0
  69. chython/containers/graph.py +200 -0
  70. chython/containers/molecule.py +695 -0
  71. chython/containers/query.py +66 -0
  72. chython/containers/reaction.py +324 -0
  73. chython/exceptions.py +114 -0
  74. chython/files/MRVrw.py +523 -0
  75. chython/files/PDBrw.py +257 -0
  76. chython/files/RDFrw.py +298 -0
  77. chython/files/SDFrw.py +221 -0
  78. chython/files/__init__.py +29 -0
  79. chython/files/_convert.py +207 -0
  80. chython/files/_mapping.py +117 -0
  81. chython/files/_xyz.cp312-win_amd64.pyd +0 -0
  82. chython/files/_xyz.pyx +74 -0
  83. chython/files/daylight/__init__.py +24 -0
  84. chython/files/daylight/parser.py +157 -0
  85. chython/files/daylight/smarts.py +107 -0
  86. chython/files/daylight/smiles.py +245 -0
  87. chython/files/daylight/test/__init__.py +18 -0
  88. chython/files/daylight/test/test_daylight_smarts.py +289 -0
  89. chython/files/daylight/test/test_daylight_smiles.py +101 -0
  90. chython/files/daylight/test/test_parser.py +118 -0
  91. chython/files/daylight/test/test_tokenize.py +77 -0
  92. chython/files/daylight/tokenize.py +376 -0
  93. chython/files/libinchi/__init__.py +22 -0
  94. chython/files/libinchi/libinchi.dll +0 -0
  95. chython/files/libinchi/wrapper.py +555 -0
  96. chython/files/mdl/__init__.py +25 -0
  97. chython/files/mdl/emol.py +209 -0
  98. chython/files/mdl/erxn.py +67 -0
  99. chython/files/mdl/mol.py +144 -0
  100. chython/files/mdl/read.py +225 -0
  101. chython/files/mdl/rxn.py +67 -0
  102. chython/files/mdl/stereo.py +63 -0
  103. chython/files/mdl/write.py +165 -0
  104. chython/files/xyz.py +78 -0
  105. chython/periodictable/__init__.py +69 -0
  106. chython/periodictable/base/__init__.py +25 -0
  107. chython/periodictable/base/dynamic.py +149 -0
  108. chython/periodictable/base/element.py +452 -0
  109. chython/periodictable/base/groups.py +90 -0
  110. chython/periodictable/base/periods.py +46 -0
  111. chython/periodictable/base/query.py +473 -0
  112. chython/periodictable/base/vector.py +119 -0
  113. chython/periodictable/groupI.py +252 -0
  114. chython/periodictable/groupII.py +223 -0
  115. chython/periodictable/groupIII.py +1154 -0
  116. chython/periodictable/groupIV.py +210 -0
  117. chython/periodictable/groupIX.py +196 -0
  118. chython/periodictable/groupV.py +192 -0
  119. chython/periodictable/groupVI.py +183 -0
  120. chython/periodictable/groupVII.py +162 -0
  121. chython/periodictable/groupVIII.py +161 -0
  122. chython/periodictable/groupX.py +166 -0
  123. chython/periodictable/groupXI.py +164 -0
  124. chython/periodictable/groupXII.py +158 -0
  125. chython/periodictable/groupXIII.py +239 -0
  126. chython/periodictable/groupXIV.py +262 -0
  127. chython/periodictable/groupXV.py +276 -0
  128. chython/periodictable/groupXVI.py +457 -0
  129. chython/periodictable/groupXVII.py +324 -0
  130. chython/periodictable/groupXVIII.py +260 -0
  131. chython/reactor/__init__.py +24 -0
  132. chython/reactor/base.py +212 -0
  133. chython/reactor/deprotection.py +565 -0
  134. chython/reactor/groups.py +128 -0
  135. chython/reactor/reactions/__init__.py +157 -0
  136. chython/reactor/reactions/_amidation.py +58 -0
  137. chython/reactor/reactions/_amine_isocyanate.py +52 -0
  138. chython/reactor/reactions/_buchwald_hartwig.py +50 -0
  139. chython/reactor/reactions/_esterification.py +49 -0
  140. chython/reactor/reactions/_macmillan.py +43 -0
  141. chython/reactor/reactions/_reductive_amination.py +50 -0
  142. chython/reactor/reactions/_sonogashira.py +49 -0
  143. chython/reactor/reactions/_sulfonamidation.py +52 -0
  144. chython/reactor/reactions/_suzuki_miyaura.py +88 -0
  145. chython/reactor/reactions/ufe.py +99 -0
  146. chython/reactor/reactor.py +170 -0
  147. chython/reactor/retro/__init__.py +77 -0
  148. chython/reactor/retro/_amidation.py +39 -0
  149. chython/reactor/retro/_aryl_amination.py +32 -0
  150. chython/reactor/retro/_mitsunobu.py +49 -0
  151. chython/reactor/retro/_sonogashira.py +49 -0
  152. chython/reactor/retro/_suzuki_miyaura.py +54 -0
  153. chython/reactor/scaffold.py +122 -0
  154. chython/reactor/test/__init__.py +18 -0
  155. chython/reactor/test/test_deprotection.py +57 -0
  156. chython/reactor/test/test_reactor.py +40 -0
  157. chython/reactor/test/test_scaffold.py +57 -0
  158. chython/reactor/test/test_transformer.py +40 -0
  159. chython/reactor/transformer.py +62 -0
  160. chython/utils/__init__.py +57 -0
  161. chython/utils/free_wilson.py +114 -0
  162. chython/utils/grid.py +130 -0
  163. chython/utils/rdkit.py +165 -0
  164. chython/utils/retro.py +139 -0
  165. chython/utils/svg.py +57 -0
  166. chython/utils/test/__init__.py +18 -0
  167. chython/utils/test/test_rdkit.py +71 -0
  168. chython-2.4.dist-info/LICENSE +165 -0
  169. chython-2.4.dist-info/METADATA +101 -0
  170. chython-2.4.dist-info/RECORD +171 -0
  171. 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']