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,212 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014-2025 Ramil Nugmanov <nougmanoff@protonmail.com>
4
+ # Copyright 2019 Adelia Fatykhova <adelik21979@gmail.com>
5
+ # This file is part of chython.
6
+ #
7
+ # chython is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published by
9
+ # the Free Software Foundation; either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with this program; if not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+ from typing import Union
21
+ from ..containers import MoleculeContainer, QueryContainer
22
+ from ..containers.bonds import Bond
23
+ from ..periodictable import Element, AnyElement, QueryElement
24
+
25
+
26
+ class BaseReactor:
27
+ def __init__(self, pattern, replacement, delete_atoms, fix_rings, fix_tautomers):
28
+ if isinstance(replacement, QueryContainer):
29
+ for n, a in replacement.atoms():
30
+ if not isinstance(a, (AnyElement, QueryElement)):
31
+ raise TypeError('Unsupported query atom type')
32
+ elif len(a.implicit_hydrogens) > 1:
33
+ raise ValueError('Query element in patch has more than one implicit hydrogen clause')
34
+ for *_, b in replacement.bonds():
35
+ if len(b.order) > 1:
36
+ raise ValueError('Variable bond in replacement')
37
+
38
+ self._to_delete = {n for n, a in pattern.atoms() if not a.masked} - set(replacement) if delete_atoms else ()
39
+ self._replacement = replacement
40
+ self._fix_rings = fix_rings
41
+ self._fix_tautomers = fix_tautomers
42
+
43
+ def _get_deleted(self, structure, mapping):
44
+ if not self._to_delete:
45
+ return set()
46
+
47
+ bonds = structure._bonds
48
+ to_delete = {mapping[x] for x in self._to_delete}
49
+ # if deleted atoms have another path to remain fragment, the path is preserved
50
+ remain = set(mapping.values()).difference(to_delete)
51
+ delete, global_seen = set(), set()
52
+ for x in to_delete:
53
+ for n in bonds[x]:
54
+ if n in global_seen or n in remain:
55
+ continue
56
+ seen = {n}
57
+ global_seen.add(n)
58
+ stack = [x for x in bonds[n] if x not in global_seen]
59
+ while stack:
60
+ current = stack.pop()
61
+ if current in remain:
62
+ break
63
+ if current in to_delete:
64
+ continue
65
+ seen.add(current)
66
+ global_seen.add(current)
67
+ stack.extend([x for x in bonds[current] if x not in global_seen])
68
+ else:
69
+ delete.update(seen)
70
+
71
+ to_delete.update(delete)
72
+ return to_delete
73
+
74
+ def _patcher(self, structure: MoleculeContainer, mapping):
75
+ satoms = structure._atoms
76
+ sbonds = structure._bonds
77
+
78
+ to_delete = self._get_deleted(structure, mapping)
79
+ new = structure.__class__()
80
+ natoms = new._atoms
81
+ nbonds = new._bonds
82
+ max_atom = max(satoms)
83
+ stereo_atoms = []
84
+ stereo_bonds = []
85
+
86
+ # let's preserve connectivity order from replacement to keep stereo signs as is.
87
+ # stereo labels from original structure will be recalculated after full molecule construction.
88
+ for n, ra in self._replacement.atoms():
89
+ if isinstance(ra, AnyElement):
90
+ if m := mapping.get(n):
91
+ # keep matched atom type and isotope
92
+ sa = satoms[m]
93
+ a = sa.copy()
94
+ a.charge = ra.charge
95
+ a.is_radical = ra.is_radical
96
+ if ra.stereo is not None: # override stereo
97
+ a._stereo = ra.stereo
98
+ elif sa.stereo is not None: # keep original stereo
99
+ stereo_atoms.append(m) # mark for stereo fix
100
+ else:
101
+ raise ValueError("AnyElement doesn't match to pattern")
102
+ else: # QueryElement or Element
103
+ ra: Union[QueryElement, Element] # typehint
104
+ e = Element.from_atomic_number(ra.atomic_number)
105
+ a = e(ra.isotope, charge=ra.charge, is_radical=ra.is_radical)
106
+ if not (m := mapping.get(n)): # new atom
107
+ m = max_atom + 1
108
+ mapping[n] = max_atom = m
109
+ a._stereo = ra.stereo # keep stereo from patch for new atoms
110
+ if isinstance(ra, Element):
111
+ a._implicit_hydrogens = ra.implicit_hydrogens # keep H count from patch
112
+ a.xy = ra.xy # keep coordinates from patch
113
+ elif ra.implicit_hydrogens: # keep H count from patch
114
+ a._implicit_hydrogens = ra.implicit_hydrogens[0]
115
+ else: # existing atoms
116
+ sa = satoms[m]
117
+ a.xy = sa.xy # preserve existing coordinates
118
+ if ra.stereo is not None:
119
+ a._stereo = ra.stereo
120
+ elif sa.stereo is not None: # keep original stereo
121
+ stereo_atoms.append(m)
122
+ natoms[m] = a
123
+ nbonds[m] = {}
124
+
125
+ # preserve connectivity order
126
+ for n, bs in self._replacement._bonds.items():
127
+ n = mapping[n]
128
+ for m, rb in bs.items():
129
+ m = mapping[m]
130
+ if n in nbonds[m]: # back-link
131
+ nbonds[n][m] = nbonds[m][n]
132
+ else:
133
+ nbonds[n][m] = b = Bond(int(rb))
134
+ if rb.stereo is not None: # override stereo
135
+ b._stereo = rb.stereo
136
+ # check bond exists in source and has stereo label and the same order
137
+ elif (sbn := sbonds.get(n)) is None or (sb := sbn.get(m)) is None or sb.stereo is None or sb != b:
138
+ continue
139
+ else: # original structure has stereo bond
140
+ stereo_bonds.append((n, m))
141
+
142
+ patched_atoms = set(new)
143
+ for n, sa in satoms.items(): # add unmatched or masked atoms
144
+ if n not in patched_atoms and n not in to_delete:
145
+ natoms[n] = a = sa.copy(hydrogens=True)
146
+ nbonds[n] = {}
147
+ if sa.stereo is not None:
148
+ # in case of allenes label can disappear/change, thus, requires recalculation
149
+ # for tetrahedrons label can be stored as is
150
+ if n in structure.stereogenic_tetrahedrons:
151
+ a._stereo = sa.stereo
152
+ else:
153
+ stereo_atoms.append(n)
154
+
155
+ for n, bs in sbonds.items(): # preserve connectivity order for keeping stereo labels as is
156
+ if n in to_delete: # atoms for removing
157
+ continue
158
+ for m, b in bs.items():
159
+ # ignore deleted atoms and patch atoms
160
+ if m in to_delete or n in patched_atoms and m in patched_atoms:
161
+ continue
162
+ elif n in nbonds[m]: # back-link
163
+ nbonds[n][m] = nbonds[m][n]
164
+ else:
165
+ nbonds[n][m] = b.copy()
166
+ if b.stereo is not None:
167
+ # stereo label should be recalculated
168
+ stereo_bonds.append((n, m))
169
+
170
+ for n, a in new.atoms():
171
+ if a.implicit_hydrogens is None:
172
+ new.calc_implicit(n)
173
+ new.calc_labels()
174
+
175
+ # translate stereo sign from old order to new order
176
+ for n in stereo_atoms:
177
+ if n in new.stereogenic_tetrahedrons:
178
+ if sbonds[n].keys() == nbonds[n].keys():
179
+ # flush stereo from reaction center. should be explicitly set in replacement.
180
+ s = new._translate_tetrahedron_sign(n, structure.stereogenic_tetrahedrons[n], satoms[n].stereo)
181
+ natoms[n]._stereo = s
182
+ elif n in new.stereogenic_allenes:
183
+ if set(new.stereogenic_allenes[n]) == set(structure.stereogenic_allenes[n]):
184
+ # flush stereo for changed allene substituents
185
+ s = new._translate_allene_sign(n, *structure.stereogenic_allenes[n][:2], satoms[n].stereo)
186
+ natoms[n]._stereo = s
187
+ # else: ignore label
188
+
189
+ for n, m in stereo_bonds:
190
+ # check if bond is center of cumulene
191
+ if (n12 := new._stereo_cis_trans_terminals.get(n, True)) != new._stereo_cis_trans_terminals.get(m, False):
192
+ continue
193
+ s12 = structure._stereo_cis_trans_terminals[n]
194
+ # check if cumulene terminals are the same
195
+ if set(n12) != set(s12):
196
+ continue
197
+ if set(new.stereogenic_cis_trans[n12]) == set(env := structure.stereogenic_cis_trans[s12]):
198
+ # connected to cumulenes atoms should be the same
199
+ s = new._translate_cis_trans_sign(*n12, *env[:2], sbonds[n][m].stereo)
200
+ nbonds[n][m]._stereo = s
201
+ # else: ignore label
202
+
203
+ if self._fix_rings:
204
+ new.kekule() # keeps stereo as is
205
+ if not new.thiele(fix_tautomers=self._fix_tautomers): # fixes stereo if any ring aromatized
206
+ new.fix_stereo()
207
+ else:
208
+ new.fix_stereo()
209
+ return new
210
+
211
+
212
+ __all__ = ['BaseReactor']