chython 2.19__cp313-cp313-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 +35 -0
- chython/_functions.py +95 -0
- chython/_java.py +57 -0
- chython/algorithms/__init__.py +18 -0
- chython/algorithms/_isomorphism.cp313-win_amd64.pyd +0 -0
- chython/algorithms/_isomorphism.pyx +189 -0
- chython/algorithms/_rings.cp313-win_amd64.pyd +0 -0
- chython/algorithms/_rings.pyx +478 -0
- chython/algorithms/aromatics/__init__.py +27 -0
- chython/algorithms/aromatics/_rules.py +124 -0
- chython/algorithms/aromatics/kekule.py +532 -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 +222 -0
- chython/algorithms/calculate2d/__init__.py +23 -0
- chython/algorithms/calculate2d/clean2d.js +1 -0
- chython/algorithms/calculate2d/molecule.py +191 -0
- chython/algorithms/calculate2d/reaction.py +75 -0
- chython/algorithms/conformers.py +106 -0
- chython/algorithms/depict.py +596 -0
- chython/algorithms/fingerprints/__init__.py +43 -0
- chython/algorithms/fingerprints/linear.py +202 -0
- chython/algorithms/fingerprints/morgan.py +132 -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/groups/__init__.py +212 -0
- chython/algorithms/groups/_functional.py +290 -0
- chython/algorithms/groups/_oxidations.py +70 -0
- chython/algorithms/groups/_protective.py +964 -0
- chython/algorithms/groups/_reactions.py +809 -0
- chython/algorithms/groups/_reductions.py +88 -0
- chython/algorithms/groups/_transformations.py +278 -0
- chython/algorithms/groups/test/__init__.py +18 -0
- chython/algorithms/groups/test/test_deprotection.py +108 -0
- chython/algorithms/groups/test/test_reactions.py +377 -0
- chython/algorithms/isomorphism.py +664 -0
- chython/algorithms/mapping/__init__.py +188 -0
- chython/algorithms/mapping/_groups.py +94 -0
- chython/algorithms/mapping/_reactions.py +72 -0
- chython/algorithms/mapping/attention.py +266 -0
- chython/algorithms/mapping/reconstruct.py +346 -0
- chython/algorithms/mcs.py +201 -0
- chython/algorithms/morgan.py +77 -0
- chython/algorithms/rings.py +178 -0
- chython/algorithms/smiles.py +597 -0
- chython/algorithms/standardize/__init__.py +30 -0
- chython/algorithms/standardize/_groups.py +958 -0
- chython/algorithms/standardize/_isomers.py +330 -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 +475 -0
- chython/algorithms/standardize/reaction.py +384 -0
- chython/algorithms/standardize/resonance.py +191 -0
- chython/algorithms/standardize/salts.py +128 -0
- chython/algorithms/standardize/saturation.py +373 -0
- chython/algorithms/standardize/test/__init__.py +18 -0
- chython/algorithms/standardize/test/test_canonicalize.py +66 -0
- chython/algorithms/standardize/test/test_groups.py +146 -0
- chython/algorithms/standardize/test/test_isomers.py +198 -0
- chython/algorithms/stereo.py +1215 -0
- chython/algorithms/tautomers/__init__.py +167 -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 +208 -0
- chython/algorithms/tautomers/heteroarenes.py +103 -0
- chython/algorithms/tautomers/keto_enol.py +143 -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 +350 -0
- chython/containers/__init__.py +48 -0
- chython/containers/_pack_v2.cp313-win_amd64.pyd +0 -0
- chython/containers/_pack_v2.pyx +271 -0
- chython/containers/_unpack_v0v2.cp313-win_amd64.pyd +0 -0
- chython/containers/_unpack_v0v2.pyx +302 -0
- chython/containers/bonds.py +255 -0
- chython/containers/cgr.py +115 -0
- chython/containers/chimera.py +63 -0
- chython/containers/graph.py +199 -0
- chython/containers/molecule.py +817 -0
- chython/containers/query.py +71 -0
- chython/containers/rdkit.py +183 -0
- chython/containers/reaction.py +324 -0
- chython/containers/test/__init__.py +18 -0
- chython/containers/test/test_molecule.py +117 -0
- chython/containers/test/test_pack.py +60 -0
- chython/containers/test/test_rdkit.py +70 -0
- chython/containers/test/test_reaction.py +40 -0
- chython/exceptions.py +114 -0
- chython/files/MRVrw.py +532 -0
- chython/files/PDBrw.py +255 -0
- chython/files/RDFrw.py +309 -0
- chython/files/SDFrw.py +221 -0
- chython/files/__init__.py +30 -0
- chython/files/_convert.py +207 -0
- chython/files/_mapping.py +117 -0
- chython/files/_xyz.cp313-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 +260 -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 +554 -0
- chython/files/mdl/__init__.py +25 -0
- chython/files/mdl/emol.py +251 -0
- chython/files/mdl/erxn.py +67 -0
- chython/files/mdl/mol.py +186 -0
- chython/files/mdl/read.py +223 -0
- chython/files/mdl/rxn.py +67 -0
- chython/files/mdl/stereo.py +63 -0
- chython/files/mdl/write.py +201 -0
- chython/files/opsin.py +35 -0
- chython/files/test/__init__.py +0 -0
- chython/files/test/test_inchi.py +27 -0
- chython/files/test/test_mrv.py +47 -0
- chython/files/test/test_sdf_rdf.py +64 -0
- chython/files/test/test_xyz.py +20 -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 +451 -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/periodictable/test/__init__.py +0 -0
- chython/periodictable/test/test_element.py +100 -0
- chython/reactor/__init__.py +24 -0
- chython/reactor/base.py +222 -0
- chython/reactor/reactor.py +177 -0
- chython/reactor/test/__init__.py +18 -0
- chython/reactor/test/test_reactor.py +40 -0
- chython/reactor/test/test_transformer.py +78 -0
- chython/reactor/transformer.py +72 -0
- chython/utils/__init__.py +50 -0
- chython/utils/free_wilson.py +114 -0
- chython/utils/grid.py +130 -0
- chython/utils/retro.py +139 -0
- chython-2.19.dist-info/METADATA +105 -0
- chython-2.19.dist-info/RECORD +175 -0
- chython-2.19.dist-info/WHEEL +4 -0
- chython-2.19.dist-info/licenses/LICENSE +165 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 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 lazy_object_proxy import Proxy
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _make_reactor(rxn_name, fg_name, output_fg, product_smarts):
|
|
23
|
+
from ._functional import rules
|
|
24
|
+
from ...reactor import Reactor
|
|
25
|
+
from ... import smarts
|
|
26
|
+
|
|
27
|
+
q = rules[fg_name]
|
|
28
|
+
product = smarts(product_smarts)
|
|
29
|
+
return rxn_name, fg_name, output_fg, Reactor((q,), (product,),
|
|
30
|
+
delete_atoms=True, one_shot=True, ignore_pyrrole_hydrogen=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _rules():
|
|
34
|
+
rules = []
|
|
35
|
+
|
|
36
|
+
# aldehyde → primary_alcohol (NaBH4, LiAlH4)
|
|
37
|
+
rules.append(_make_reactor('aldehyde_to_alcohol', 'aldehyde', 'primary_alcohol', '[A:2]-[A:1]'))
|
|
38
|
+
|
|
39
|
+
# ketone → secondary_alcohol (NaBH4, LiAlH4)
|
|
40
|
+
rules.append(_make_reactor('ketone_to_alcohol', 'ketone', 'secondary_alcohol', '[A:2]-[A:1]'))
|
|
41
|
+
|
|
42
|
+
# carboxylic_acid → primary_alcohol (LiAlH4)
|
|
43
|
+
rules.append(_make_reactor('acid_to_alcohol', 'carboxylic_acid', 'primary_alcohol', '[A:2]-[A:1]'))
|
|
44
|
+
|
|
45
|
+
# ester → alcohol (LiAlH4). OR' group is lost.
|
|
46
|
+
rules.append(_make_reactor('ester_to_alcohol', 'ester', 'primary_alcohol', '[A:2]-[A:1]'))
|
|
47
|
+
|
|
48
|
+
# primary_amide → amine (LiAlH4, BH3)
|
|
49
|
+
rules.append(_make_reactor('amide_to_amine', 'primary_amide', 'primary_amine', '[A:1]-[A:2]'))
|
|
50
|
+
|
|
51
|
+
# secondary_amide → amine (LiAlH4, BH3)
|
|
52
|
+
rules.append(_make_reactor('amide_to_amine', 'secondary_amide', 'secondary_amine', '[A:1]-[A:2]'))
|
|
53
|
+
|
|
54
|
+
# nitrile → primary_amine (LiAlH4, H2/cat)
|
|
55
|
+
rules.append(_make_reactor('nitrile_to_amine', 'nitrile', 'primary_amine', '[A:2]-[A:1]'))
|
|
56
|
+
|
|
57
|
+
# nitro → primary_amine (H2/Pd, SnCl2, Fe/HCl)
|
|
58
|
+
rules.append(_make_reactor('nitro_to_amine', 'nitro', 'primary_aniline', '[A:1]'))
|
|
59
|
+
|
|
60
|
+
# azide → primary_amine (PPh3/H2O, H2/Pd)
|
|
61
|
+
rules.append(_make_reactor('azide_to_amine', 'azide', 'primary_amine', '[A:1]'))
|
|
62
|
+
|
|
63
|
+
# alkene hydrogenation: C=C → C-C (H2/Pd, H2/Pt)
|
|
64
|
+
rules.append(_make_reactor('alkene_hydrogenation', 'alkene', None, '[A:1]-[A:2]'))
|
|
65
|
+
rules.append(_make_reactor('alkene_hydrogenation', 'terminal_alkene', None, '[A:1]-[A:2]'))
|
|
66
|
+
rules.append(_make_reactor('alkene_hydrogenation', 'enamine', None, '[A:1]-[A:2]-[A:3]'))
|
|
67
|
+
rules.append(_make_reactor('alkene_hydrogenation', 'enol_ether', None, '[A:1]-[A:2]-[A:3]'))
|
|
68
|
+
|
|
69
|
+
# deoxygenation: alcohol → alkane (Barton-McCombie, Appel+reduction)
|
|
70
|
+
rules.append(_make_reactor('deoxygenation', 'primary_alcohol', None, '[A:2]'))
|
|
71
|
+
rules.append(_make_reactor('deoxygenation', 'secondary_alcohol', None, '[A:2]'))
|
|
72
|
+
|
|
73
|
+
# carbonyl → primary amine (reductive amination with NH3)
|
|
74
|
+
rules.append(_make_reactor('carbonyl_to_amine', 'aldehyde', 'primary_amine', '[A:1]-[N:20]'))
|
|
75
|
+
rules.append(_make_reactor('carbonyl_to_amine', 'ketone', 'primary_amine', '[A:1]-[N:20]'))
|
|
76
|
+
|
|
77
|
+
# reductive dehalogenation: Ar-X → Ar-H (H2/Pd, n-Bu3SnH, Zn/AcOH)
|
|
78
|
+
rules.append(_make_reactor('debromination', 'aryl_bromide', None, '[A:1]'))
|
|
79
|
+
rules.append(_make_reactor('dechlorination', 'aryl_chloride', None, '[A:1]'))
|
|
80
|
+
rules.append(_make_reactor('deiodination', 'aryl_iodide', None, '[A:1]'))
|
|
81
|
+
|
|
82
|
+
return rules
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
rules = Proxy(_rules)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
__all__ = ['rules']
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 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 lazy_object_proxy import Proxy
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _make_reactor(rxn_name, fg_name, output_fg, product_smarts):
|
|
23
|
+
from ._functional import rules
|
|
24
|
+
from ...reactor import Reactor
|
|
25
|
+
from ... import smarts
|
|
26
|
+
|
|
27
|
+
q = rules[fg_name]
|
|
28
|
+
product = smarts(product_smarts)
|
|
29
|
+
return rxn_name, fg_name, output_fg, Reactor((q,), (product,),
|
|
30
|
+
delete_atoms=True, one_shot=True, ignore_pyrrole_hydrogen=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _rules():
|
|
34
|
+
rules = []
|
|
35
|
+
|
|
36
|
+
# 1,3-diketone → isoxazole (+ hydroxylamine, implicit)
|
|
37
|
+
rules.append(_make_reactor('isoxazole', '1_3_diketone', 'isoxazole', '[A:1]:1:[O:20]:[N:21]:[A:3]:[A:5]:1'))
|
|
38
|
+
|
|
39
|
+
# 1,4-diketone → pyridazine (+ hydrazine, implicit)
|
|
40
|
+
rules.append(_make_reactor('pyridazine', '1_4_diketone', 'pyridazine', '[A:1]:1:[N:20]:[N:21]:[A:3]:[A:6]:[A:5]:1'))
|
|
41
|
+
|
|
42
|
+
# Appel: alcohol → alkyl halide (CCl4/CBr4 + PPh3)
|
|
43
|
+
rules.append(_make_reactor('appel', 'primary_alcohol', 'alkyl_bromide', '[Br:20]-[A:2]'))
|
|
44
|
+
rules.append(_make_reactor('appel', 'secondary_alcohol', 'alkyl_bromide', '[Br:20]-[A:2]'))
|
|
45
|
+
rules.append(_make_reactor('appel_chloride', 'primary_alcohol', 'alkyl_chloride', '[Cl:20]-[A:2]'))
|
|
46
|
+
rules.append(_make_reactor('appel_chloride', 'secondary_alcohol', 'alkyl_chloride', '[Cl:20]-[A:2]'))
|
|
47
|
+
|
|
48
|
+
# Miyaura borylation: ArX → ArB(OH)2
|
|
49
|
+
rules.append(_make_reactor('borylation_acid', 'aryl_bromide', 'aryl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
50
|
+
rules.append(_make_reactor('borylation_acid', 'aryl_iodide', 'aryl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
51
|
+
rules.append(_make_reactor('borylation_acid', 'aryl_chloride', 'aryl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
52
|
+
|
|
53
|
+
# Miyaura borylation: ArX → ArBpin (Pd, B2pin2)
|
|
54
|
+
rules.append(_make_reactor('borylation_ester', 'aryl_bromide', 'aryl_boronic_ester',
|
|
55
|
+
'[A:1]-[B:20]1-[O:21]-[C:22]([C:23])([C:24])-[C:25]([C:26])([C:27])-[O:28]-1'))
|
|
56
|
+
rules.append(_make_reactor('borylation_ester', 'aryl_iodide', 'aryl_boronic_ester',
|
|
57
|
+
'[A:1]-[B:20]1-[O:21]-[C:22]([C:23])([C:24])-[C:25]([C:26])([C:27])-[O:28]-1'))
|
|
58
|
+
rules.append(_make_reactor('borylation_ester', 'aryl_chloride', 'aryl_boronic_ester',
|
|
59
|
+
'[A:1]-[B:20]1-[O:21]-[C:22]([C:23])([C:24])-[C:25]([C:26])([C:27])-[O:28]-1'))
|
|
60
|
+
rules.append(_make_reactor('borylation_acid', 'aryl_triflate', 'aryl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
61
|
+
rules.append(_make_reactor('borylation_ester', 'aryl_triflate', 'aryl_boronic_ester',
|
|
62
|
+
'[A:1]-[B:20]1-[O:21]-[C:22]([C:23])([C:24])-[C:25]([C:26])([C:27])-[O:28]-1'))
|
|
63
|
+
|
|
64
|
+
# Nitrile hydrolysis: R-C≡N → R-C(=O)-NH2 (partial, to amide)
|
|
65
|
+
rules.append(_make_reactor('nitrile_hydrolysis', 'nitrile', 'primary_amide', '[A:2]-[A:1]=[O:20]'))
|
|
66
|
+
|
|
67
|
+
# Electrophilic aromatic nitration: Ar-H → Ar-NO2
|
|
68
|
+
rules.append(_make_reactor('nitration', 'arene_ch', 'nitro', '[A:1]-[N;+:20](=[O:21])[O;-:22]'))
|
|
69
|
+
|
|
70
|
+
# Electrophilic aromatic halogenation: Ar-H → Ar-X
|
|
71
|
+
rules.append(_make_reactor('bromination', 'arene_ch', 'aryl_bromide', '[A:1]-[Br:20]'))
|
|
72
|
+
rules.append(_make_reactor('chlorination', 'arene_ch', 'aryl_chloride', '[A:1]-[Cl:20]'))
|
|
73
|
+
rules.append(_make_reactor('iodination', 'arene_ch', 'aryl_iodide', '[A:1]-[I:20]'))
|
|
74
|
+
rules.append(_make_reactor('fluorination', 'arene_ch', 'aryl_fluoride', '[A:1]-[F:20]'))
|
|
75
|
+
|
|
76
|
+
# benzylic halogenation: Ar-CH2R → Ar-CHBrR (NBS, Br2/hv)
|
|
77
|
+
rules.append(_make_reactor('benzylic_bromination', 'benzylic_ch', 'alkyl_bromide', '[A:1](-[Br:20])-[A:2]'))
|
|
78
|
+
|
|
79
|
+
# alpha-halogenation: alpha_ketone → alpha-bromoketone (NBS/Br2)
|
|
80
|
+
rules.append(_make_reactor('alpha_halogenation', 'alpha_ketone', 'alpha_haloketone', '[A:1](=[A:2])-[A:3](-[Br:20])'))
|
|
81
|
+
|
|
82
|
+
# alkyl borylation: R-X → R-B(OH)2
|
|
83
|
+
rules.append(_make_reactor('borylation_acid', 'alkyl_bromide', 'alkyl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
84
|
+
rules.append(_make_reactor('borylation_acid', 'alkyl_iodide', 'alkyl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
85
|
+
rules.append(_make_reactor('borylation_acid', 'alkyl_chloride', 'alkyl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
86
|
+
|
|
87
|
+
# alkyl borylation: R-X → R-Bpin
|
|
88
|
+
rules.append(_make_reactor('borylation_ester', 'alkyl_bromide', 'alkyl_boronic_ester',
|
|
89
|
+
'[A:1]-[B:20]1-[O:21]-[C:22]([C:23])([C:24])-[C:25]([C:26])([C:27])-[O:28]-1'))
|
|
90
|
+
rules.append(_make_reactor('borylation_ester', 'alkyl_iodide', 'alkyl_boronic_ester',
|
|
91
|
+
'[A:1]-[B:20]1-[O:21]-[C:22]([C:23])([C:24])-[C:25]([C:26])([C:27])-[O:28]-1'))
|
|
92
|
+
rules.append(_make_reactor('borylation_ester', 'alkyl_chloride', 'alkyl_boronic_ester',
|
|
93
|
+
'[A:1]-[B:20]1-[O:21]-[C:22]([C:23])([C:24])-[C:25]([C:26])([C:27])-[O:28]-1'))
|
|
94
|
+
|
|
95
|
+
# ester to amide: R-C(=O)-OR' → R-C(=O)-NH2 (NH3)
|
|
96
|
+
rules.append(_make_reactor('ester_to_amide', 'ester', 'primary_amide', '[A:1](=[A:2])-[N:20]'))
|
|
97
|
+
|
|
98
|
+
# ester to hydroxamic acid: R-C(=O)-OR' → R-C(=O)-NH-OH (NH2OH)
|
|
99
|
+
rules.append(_make_reactor('ester_to_hydroxamic_acid', 'ester', 'hydroxamic_acid', '[A:1](=[A:2])-[N:20]-[O:21]'))
|
|
100
|
+
|
|
101
|
+
# amide hydrolysis: R-C(=O)-NHR → R-C(=O)-OH
|
|
102
|
+
rules.append(_make_reactor('amide_hydrolysis', 'primary_amide', 'carboxylic_acid', '[A:2](=[A:3])-[O:20]'))
|
|
103
|
+
rules.append(_make_reactor('amide_hydrolysis', 'secondary_amide', 'carboxylic_acid', '[A:2](=[A:3])-[O:20]'))
|
|
104
|
+
|
|
105
|
+
# cyanation: R-X → R-CN (NaCN/CuCN)
|
|
106
|
+
rules.append(_make_reactor('cyanation', 'alkyl_bromide', 'nitrile', '[A:1]-[C:20]#[N:21]'))
|
|
107
|
+
rules.append(_make_reactor('cyanation', 'alkyl_iodide', 'nitrile', '[A:1]-[C:20]#[N:21]'))
|
|
108
|
+
rules.append(_make_reactor('cyanation', 'alkyl_chloride', 'nitrile', '[A:1]-[C:20]#[N:21]'))
|
|
109
|
+
rules.append(_make_reactor('cyanation', 'aryl_bromide', 'nitrile', '[A:1]-[C:20]#[N:21]'))
|
|
110
|
+
rules.append(_make_reactor('cyanation', 'aryl_iodide', 'nitrile', '[A:1]-[C:20]#[N:21]'))
|
|
111
|
+
rules.append(_make_reactor('cyanation', 'aryl_chloride', 'nitrile', '[A:1]-[C:20]#[N:21]'))
|
|
112
|
+
|
|
113
|
+
# dehydration: R-C(=O)-NH2 → R-C≡N (P2O5/SOCl2)
|
|
114
|
+
rules.append(_make_reactor('dehydration', 'primary_amide', 'nitrile', '[A:2]#[A:1]'))
|
|
115
|
+
|
|
116
|
+
# triflation: phenol/alcohol → triflate (Tf2O)
|
|
117
|
+
rules.append(_make_reactor('triflation', 'phenol', 'aryl_triflate', '[A:2]-[A:1]-[S:20](=[O:21])(=[O:22])-[C:23]([F:24])([F:25])[F:26]'))
|
|
118
|
+
rules.append(_make_reactor('triflation', 'primary_alcohol', 'alkyl_triflate', '[A:2]-[A:1]-[S:20](=[O:21])(=[O:22])-[C:23]([F:24])([F:25])[F:26]'))
|
|
119
|
+
rules.append(_make_reactor('triflation', 'secondary_alcohol', 'alkyl_triflate', '[A:2]-[A:1]-[S:20](=[O:21])(=[O:22])-[C:23]([F:24])([F:25])[F:26]'))
|
|
120
|
+
|
|
121
|
+
# nitrile full hydrolysis: R-C≡N → R-COOH
|
|
122
|
+
rules.append(_make_reactor('nitrile_to_acid', 'nitrile', 'carboxylic_acid', '[A:1](=[O:20])-[O:21]'))
|
|
123
|
+
|
|
124
|
+
# tertiary alcohol dehydration: R3C-OH → R2C=CR (loss of water)
|
|
125
|
+
rules.append(_make_reactor('alcohol_dehydration', 'tertiary_alcohol_with_alpha_h', 'alkene', '[A:2]=[A:3]'))
|
|
126
|
+
|
|
127
|
+
# oximation: ketone/aldehyde → oxime (+ NH2OH implicit)
|
|
128
|
+
rules.append(_make_reactor('oximation', 'ketone', 'oxime', '[O:20]-[N:21]=[A:1]'))
|
|
129
|
+
rules.append(_make_reactor('oximation', 'aldehyde', 'oxime', '[O:20]-[N:21]=[A:1]'))
|
|
130
|
+
|
|
131
|
+
# acid chloride formation: RCOOH → RCOCl (SOCl2, oxalyl chloride)
|
|
132
|
+
rules.append(_make_reactor('acid_chlorination', 'carboxylic_acid', 'acyl_chloride', '[Cl:20]-[A:1]=[A:2]'))
|
|
133
|
+
|
|
134
|
+
# mesylation: ROH → ROMs (MsCl, Et3N)
|
|
135
|
+
rules.append(_make_reactor('mesylation', 'primary_alcohol', 'alkyl_mesylate', '[A:2]-[A:1]-[S:20](=[O:21])(=[O:22])-[C:23]'))
|
|
136
|
+
rules.append(_make_reactor('mesylation', 'secondary_alcohol', 'alkyl_mesylate', '[A:2]-[A:1]-[S:20](=[O:21])(=[O:22])-[C:23]'))
|
|
137
|
+
|
|
138
|
+
# tosylation: ROH → ROTs (TsCl, pyridine)
|
|
139
|
+
rules.append(_make_reactor('tosylation', 'primary_alcohol', 'alkyl_tosylate',
|
|
140
|
+
'[A:2]-[A:1]-[S:20](=[O:21])(=[O:22])-[C:23]:1:[C:24]:[C:25]:[C:26](-[C:27]):[C:28]:[C:29]:1'))
|
|
141
|
+
rules.append(_make_reactor('tosylation', 'secondary_alcohol', 'alkyl_tosylate',
|
|
142
|
+
'[A:2]-[A:1]-[S:20](=[O:21])(=[O:22])-[C:23]:1:[C:24]:[C:25]:[C:26](-[C:27]):[C:28]:[C:29]:1'))
|
|
143
|
+
|
|
144
|
+
# halide hydrolysis: ArX → ArOH (NaOH/H2O, Cu cat, high T)
|
|
145
|
+
rules.append(_make_reactor('halide_hydrolysis', 'aryl_chloride', 'phenol', '[A:1]-[O:20]'))
|
|
146
|
+
rules.append(_make_reactor('halide_hydrolysis', 'aryl_bromide', 'phenol', '[A:1]-[O:20]'))
|
|
147
|
+
rules.append(_make_reactor('halide_hydrolysis', 'aryl_iodide', 'phenol', '[A:1]-[O:20]'))
|
|
148
|
+
rules.append(_make_reactor('halide_hydrolysis', 'aryl_fluoride', 'phenol', '[A:1]-[O:20]'))
|
|
149
|
+
|
|
150
|
+
# alkyl halide hydrolysis: RX → ROH (SN2, NaOH/H2O)
|
|
151
|
+
rules.append(_make_reactor('halide_hydrolysis', 'alkyl_chloride', 'primary_alcohol', '[A:1]-[O:20]'))
|
|
152
|
+
rules.append(_make_reactor('halide_hydrolysis', 'alkyl_bromide', 'primary_alcohol', '[A:1]-[O:20]'))
|
|
153
|
+
rules.append(_make_reactor('halide_hydrolysis', 'alkyl_iodide', 'primary_alcohol', '[A:1]-[O:20]'))
|
|
154
|
+
|
|
155
|
+
# DAST fluorination: ROH → RF (DAST, DeoxoFluor)
|
|
156
|
+
rules.append(_make_reactor('fluorination', 'primary_alcohol', 'alkyl_fluoride', '[F:20]-[A:2]'))
|
|
157
|
+
rules.append(_make_reactor('fluorination', 'secondary_alcohol', 'alkyl_fluoride', '[F:20]-[A:2]'))
|
|
158
|
+
rules.append(_make_reactor('fluorination', 'tertiary_alcohol', 'alkyl_fluoride', '[F:20]-[A:2]'))
|
|
159
|
+
|
|
160
|
+
# Appel iodination: ROH → RI (I2/PPh3)
|
|
161
|
+
rules.append(_make_reactor('appel_iodide', 'primary_alcohol', 'alkyl_iodide', '[I:20]-[A:2]'))
|
|
162
|
+
rules.append(_make_reactor('appel_iodide', 'secondary_alcohol', 'alkyl_iodide', '[I:20]-[A:2]'))
|
|
163
|
+
|
|
164
|
+
# Hydroxy to chloro: ROH → RCl (SOCl2, PCl5 — broader than Appel)
|
|
165
|
+
# phenol → ArCl (PCl5, POCl3)
|
|
166
|
+
rules.append(_make_reactor('hydroxy_to_chloro', 'phenol', 'aryl_chloride', '[Cl:20]-[A:2]'))
|
|
167
|
+
# tertiary_alcohol → RCl
|
|
168
|
+
rules.append(_make_reactor('hydroxy_to_chloro', 'tertiary_alcohol', 'alkyl_chloride', '[Cl:20]-[A:2]'))
|
|
169
|
+
|
|
170
|
+
# acid fluoride formation: RCOOH → RCOF (cyanuric fluoride, DAST)
|
|
171
|
+
rules.append(_make_reactor('acid_fluorination', 'carboxylic_acid', 'acyl_fluoride', '[F:20]-[A:1]=[A:2]'))
|
|
172
|
+
|
|
173
|
+
# Sandmeyer: ArNH2 → ArX (NaNO2/HX, CuX)
|
|
174
|
+
rules.append(_make_reactor('sandmeyer_bromination', 'primary_aniline', 'aryl_bromide', '[Br:20]-[A:2]'))
|
|
175
|
+
rules.append(_make_reactor('sandmeyer_iodination', 'primary_aniline', 'aryl_iodide', '[I:20]-[A:2]'))
|
|
176
|
+
rules.append(_make_reactor('sandmeyer_chlorination', 'primary_aniline', 'aryl_chloride', '[Cl:20]-[A:2]'))
|
|
177
|
+
|
|
178
|
+
# Oxo to difluoro: C=O → CF2 (DAST, DeoxoFluor)
|
|
179
|
+
rules.append(_make_reactor('oxo_to_difluoro', 'ketone', None, '[A:1](-[F:20])-[F:21]'))
|
|
180
|
+
rules.append(_make_reactor('oxo_to_difluoro', 'aldehyde', None, '[A:1](-[F:20])-[F:21]'))
|
|
181
|
+
|
|
182
|
+
# Oxo to thioxo: C=O → C=S (Lawesson's reagent, P4S10)
|
|
183
|
+
rules.append(_make_reactor('oxo_to_thioxo', 'primary_amide', 'thioamide', '[S:20]=[A:2]-[A:1]'))
|
|
184
|
+
rules.append(_make_reactor('oxo_to_thioxo', 'secondary_amide', None, '[S:20]=[A:2]-[A:1]'))
|
|
185
|
+
rules.append(_make_reactor('oxo_to_thioxo', 'ketone', None, '[S:20]=[A:1]'))
|
|
186
|
+
|
|
187
|
+
# Amino to isocyanate: ArNH2/RNH2 → ArNCO (triphosgene/COCl2)
|
|
188
|
+
# primary_aniline: [N:1][C;a:2] → need N=C=O, keep Ar bond
|
|
189
|
+
rules.append(_make_reactor('amino_to_isocyanate', 'primary_aniline', 'isocyanate', '[A:2]-[A:1]=[C:20]=[O:21]'))
|
|
190
|
+
rules.append(_make_reactor('amino_to_isocyanate', 'primary_amine', 'isocyanate', '[A:2]-[A:1]=[C:20]=[O:21]'))
|
|
191
|
+
|
|
192
|
+
# Amino to isothiocyanate: ArNH2/RNH2 → ArNCS (CS2/DCC, thiophosgene)
|
|
193
|
+
rules.append(_make_reactor('amino_to_isothiocyanate', 'primary_aniline', None, '[A:2]-[A:1]=[C:20]=[S:21]'))
|
|
194
|
+
rules.append(_make_reactor('amino_to_isothiocyanate', 'primary_amine', None, '[A:2]-[A:1]=[C:20]=[S:21]'))
|
|
195
|
+
|
|
196
|
+
# Amino to azide: ArNH2 → ArN3 (diazotization + NaN3)
|
|
197
|
+
rules.append(_make_reactor('amino_to_azide', 'primary_aniline', 'azide', '[A:2]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
198
|
+
rules.append(_make_reactor('amino_to_azide', 'primary_amine', 'azide', '[A:2]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
199
|
+
|
|
200
|
+
# Amino to hydroxy: ArNH2 → ArOH (diazotization + H2O)
|
|
201
|
+
rules.append(_make_reactor('amino_to_hydroxy', 'primary_aniline', 'phenol', '[A:2]-[O:20]'))
|
|
202
|
+
rules.append(_make_reactor('amino_to_hydroxy', 'primary_amine', 'primary_alcohol', '[A:2]-[O:20]'))
|
|
203
|
+
|
|
204
|
+
# Bpin hydrolysis: ArBpin → ArB(OH)2 (NaIO4, HCl, diethanolamine)
|
|
205
|
+
rules.append(_make_reactor('bpin_hydrolysis', 'aryl_boronic_ester', 'aryl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
206
|
+
rules.append(_make_reactor('bpin_hydrolysis', 'alkyl_boronic_ester', 'alkyl_boronic_acid', '[A:1]-[B:20](-[O:21])-[O:22]'))
|
|
207
|
+
|
|
208
|
+
# Ester hydrolysis: RCOOR' → RCOOH (LiOH, NaOH, HCl)
|
|
209
|
+
rules.append(_make_reactor('ester_hydrolysis', 'ester', 'carboxylic_acid', '[A:1](=[A:2])-[O:20]'))
|
|
210
|
+
|
|
211
|
+
# Carboxy to amide: RCOOH → RCONH2 (EDC/HOBt+NH3, or CDI+NH3)
|
|
212
|
+
rules.append(_make_reactor('acid_to_amide', 'carboxylic_acid', 'primary_amide', '[N:20]-[A:1]=[A:2]'))
|
|
213
|
+
|
|
214
|
+
# Acyl chloride to acid: RCOCl → RCOOH (H2O)
|
|
215
|
+
rules.append(_make_reactor('acyl_chloride_hydrolysis', 'acyl_chloride', 'carboxylic_acid', '[O:20]-[A:1]=[A:2]'))
|
|
216
|
+
rules.append(_make_reactor('acyl_chloride_hydrolysis', 'acyl_fluoride', 'carboxylic_acid', '[O:20]-[A:1]=[A:2]'))
|
|
217
|
+
|
|
218
|
+
# Acyl chloride to amide: RCOCl → RCONH2 (NH3)
|
|
219
|
+
rules.append(_make_reactor('acyl_chloride_to_amide', 'acyl_chloride', 'primary_amide', '[N:20]-[A:1]=[A:2]'))
|
|
220
|
+
rules.append(_make_reactor('acyl_chloride_to_amide', 'acyl_fluoride', 'primary_amide', '[N:20]-[A:1]=[A:2]'))
|
|
221
|
+
|
|
222
|
+
# Halide to methoxy: ArX/RX → ArOMe/ROMe (NaOMe, SNAr)
|
|
223
|
+
rules.append(_make_reactor('halide_to_methoxy', 'aryl_fluoride', None, '[A:1]-[O:20]-[C:21]'))
|
|
224
|
+
rules.append(_make_reactor('halide_to_methoxy', 'aryl_chloride', None, '[A:1]-[O:20]-[C:21]'))
|
|
225
|
+
rules.append(_make_reactor('halide_to_methoxy', 'aryl_bromide', None, '[A:1]-[O:20]-[C:21]'))
|
|
226
|
+
rules.append(_make_reactor('halide_to_methoxy', 'alkyl_chloride', None, '[A:1]-[O:20]-[C:21]'))
|
|
227
|
+
rules.append(_make_reactor('halide_to_methoxy', 'alkyl_bromide', None, '[A:1]-[O:20]-[C:21]'))
|
|
228
|
+
rules.append(_make_reactor('halide_to_methoxy', 'alkyl_iodide', None, '[A:1]-[O:20]-[C:21]'))
|
|
229
|
+
|
|
230
|
+
# Halide to azide: RX → RN3 (NaN3)
|
|
231
|
+
rules.append(_make_reactor('azidation', 'alkyl_chloride', 'azide', '[A:1]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
232
|
+
rules.append(_make_reactor('azidation', 'alkyl_bromide', 'azide', '[A:1]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
233
|
+
rules.append(_make_reactor('azidation', 'alkyl_iodide', 'azide', '[A:1]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
234
|
+
rules.append(_make_reactor('azidation', 'aryl_chloride', 'azide', '[A:1]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
235
|
+
rules.append(_make_reactor('azidation', 'aryl_bromide', 'azide', '[A:1]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
236
|
+
rules.append(_make_reactor('azidation', 'aryl_fluoride', 'azide', '[A:1]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
237
|
+
|
|
238
|
+
# Hydroxy to azide: ROH → RN3 (DPPA/Mitsunobu or Ms→N3)
|
|
239
|
+
rules.append(_make_reactor('azidation', 'primary_alcohol', 'azide', '[A:2]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
240
|
+
rules.append(_make_reactor('azidation', 'secondary_alcohol', 'azide', '[A:2]-[N:20]=[N;+:21]=[N;-:22]'))
|
|
241
|
+
|
|
242
|
+
# Halide exchange (Finkelstein): RCl/RBr → RI (NaI/acetone)
|
|
243
|
+
rules.append(_make_reactor('finkelstein', 'alkyl_chloride', 'alkyl_iodide', '[A:1]-[I:20]'))
|
|
244
|
+
rules.append(_make_reactor('finkelstein', 'alkyl_bromide', 'alkyl_iodide', '[A:1]-[I:20]'))
|
|
245
|
+
rules.append(_make_reactor('finkelstein', 'aryl_chloride', 'aryl_iodide', '[A:1]-[I:20]'))
|
|
246
|
+
rules.append(_make_reactor('finkelstein', 'aryl_bromide', 'aryl_iodide', '[A:1]-[I:20]'))
|
|
247
|
+
|
|
248
|
+
# Halex fluorination: ArCl/ArBr → ArF (KF, CsF, spray-dried KF)
|
|
249
|
+
rules.append(_make_reactor('halex', 'aryl_chloride', 'aryl_fluoride', '[A:1]-[F:20]'))
|
|
250
|
+
rules.append(_make_reactor('halex', 'aryl_bromide', 'aryl_fluoride', '[A:1]-[F:20]'))
|
|
251
|
+
|
|
252
|
+
# Halide to chloride: ArBr/ArI → ArCl; RI → RCl
|
|
253
|
+
rules.append(_make_reactor('halide_to_chloride', 'aryl_bromide', 'aryl_chloride', '[A:1]-[Cl:20]'))
|
|
254
|
+
rules.append(_make_reactor('halide_to_chloride', 'aryl_iodide', 'aryl_chloride', '[A:1]-[Cl:20]'))
|
|
255
|
+
rules.append(_make_reactor('halide_to_chloride', 'alkyl_iodide', 'alkyl_chloride', '[A:1]-[Cl:20]'))
|
|
256
|
+
|
|
257
|
+
# Halide to bromide: ArCl → ArBr (not common but exists)
|
|
258
|
+
rules.append(_make_reactor('halide_to_bromide', 'aryl_chloride', 'aryl_bromide', '[A:1]-[Br:20]'))
|
|
259
|
+
|
|
260
|
+
# Halide to thiol: RX → RSH (NaSH, thiourea hydrolysis)
|
|
261
|
+
rules.append(_make_reactor('halide_to_thiol', 'aryl_chloride', None, '[A:1]-[S:20]'))
|
|
262
|
+
rules.append(_make_reactor('halide_to_thiol', 'aryl_bromide', None, '[A:1]-[S:20]'))
|
|
263
|
+
rules.append(_make_reactor('halide_to_thiol', 'alkyl_bromide', 'thiol', '[A:1]-[S:20]'))
|
|
264
|
+
rules.append(_make_reactor('halide_to_thiol', 'alkyl_chloride', 'thiol', '[A:1]-[S:20]'))
|
|
265
|
+
|
|
266
|
+
# Halide to thioether: RX → RSMe (NaSMe)
|
|
267
|
+
rules.append(_make_reactor('halide_to_thioether', 'aryl_chloride', 'thioether', '[A:1]-[S:20]-[C:21]'))
|
|
268
|
+
rules.append(_make_reactor('halide_to_thioether', 'aryl_bromide', 'thioether', '[A:1]-[S:20]-[C:21]'))
|
|
269
|
+
rules.append(_make_reactor('halide_to_thioether', 'alkyl_bromide', 'thioether', '[A:1]-[S:20]-[C:21]'))
|
|
270
|
+
rules.append(_make_reactor('halide_to_thioether', 'alkyl_chloride', 'thioether', '[A:1]-[S:20]-[C:21]'))
|
|
271
|
+
|
|
272
|
+
return rules
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
rules = Proxy(_rules)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
__all__ = ['rules']
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 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
|
+
#
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2022-2026 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
|
+
import pytest
|
|
20
|
+
from chython import smiles
|
|
21
|
+
from chython.algorithms.groups._protective import rules
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_test_cases = [(name, *rule) for name, rule in rules.items()]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.mark.parametrize('name,q,keep,add,test_smi,cleaved_smi,decoys', _test_cases, ids=[x[0] for x in _test_cases])
|
|
28
|
+
def test_deprotection(name, q, keep, add, test_smi, cleaved_smi, decoys):
|
|
29
|
+
t = smiles(test_smi)
|
|
30
|
+
t.canonicalize()
|
|
31
|
+
# query matches protected form
|
|
32
|
+
assert q < t, f'query does not match protected form'
|
|
33
|
+
# deprotection produces expected product
|
|
34
|
+
a = smiles(cleaved_smi)
|
|
35
|
+
a.canonicalize()
|
|
36
|
+
t.remove_protection(name)
|
|
37
|
+
assert t == a, f'deprotection gave {t}, expected {a}'
|
|
38
|
+
# decoys are not matched
|
|
39
|
+
for d in decoys:
|
|
40
|
+
d = smiles(d)
|
|
41
|
+
d.canonicalize()
|
|
42
|
+
assert not q < d, f'query matches decoy {d}'
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_no_false_matches_from_ordering():
|
|
46
|
+
"""Ensure general patterns come after specific ones to prevent false atom claims.
|
|
47
|
+
|
|
48
|
+
If pattern B matches within the protected molecule of pattern A, then A must come
|
|
49
|
+
BEFORE B in the rules dict (so A claims atoms first). This test fails when B comes
|
|
50
|
+
before A - indicating rules dict needs reordering.
|
|
51
|
+
"""
|
|
52
|
+
names = list(rules.keys())
|
|
53
|
+
for name_a, (_, _, _, test_smi, *_) in rules.items():
|
|
54
|
+
mol = smiles(test_smi)
|
|
55
|
+
mol.canonicalize()
|
|
56
|
+
idx_a = names.index(name_a)
|
|
57
|
+
for name_b in names[:idx_a]: # patterns that come BEFORE name_a
|
|
58
|
+
q_b = rules[name_b][0]
|
|
59
|
+
if next(q_b.get_mapping(mol), None) is not None:
|
|
60
|
+
pytest.fail(
|
|
61
|
+
f"Pattern '{name_b}' (idx {names.index(name_b)}) matches within "
|
|
62
|
+
f"protected molecule of '{name_a}' (idx {idx_a}), but comes before it. "
|
|
63
|
+
f"Move '{name_b}' after '{name_a}' in _protective.py rules dict."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_protective_groups_no_overlap():
|
|
68
|
+
"""Ensure protective_groups doesn't report false positives due to sub-pattern overlap.
|
|
69
|
+
|
|
70
|
+
Checks that deletable atoms (atoms not in keep list) of different detected PGs
|
|
71
|
+
don't overlap - matching the logic in the protective_groups property.
|
|
72
|
+
"""
|
|
73
|
+
for name, (q, keep, add, test_smi, *_) in rules.items():
|
|
74
|
+
mol = smiles(test_smi)
|
|
75
|
+
mol.canonicalize()
|
|
76
|
+
pgs = mol.protective_groups
|
|
77
|
+
assert name in pgs, f'{name} not detected in its own protected SMILES'
|
|
78
|
+
# verify deletable atoms of detected PGs don't overlap
|
|
79
|
+
for other in pgs:
|
|
80
|
+
if other == name:
|
|
81
|
+
continue
|
|
82
|
+
q_other, keep_other = rules[other][0], rules[other][1]
|
|
83
|
+
q_self = rules[name][0]
|
|
84
|
+
self_atoms = set()
|
|
85
|
+
for mp in q_self.get_mapping(mol, automorphism_filter=False):
|
|
86
|
+
self_atoms.update(m for n, m in mp.items() if n not in keep)
|
|
87
|
+
for mp in q_other.get_mapping(mol, automorphism_filter=False):
|
|
88
|
+
other_atoms = {m for n, m in mp.items() if n not in keep_other}
|
|
89
|
+
assert not self_atoms.intersection(other_atoms), (
|
|
90
|
+
f"'{other}' deletable atoms overlap with '{name}' on '{test_smi}'. "
|
|
91
|
+
f"Reorder rules in _protective.py so '{name}' comes before '{other}'."
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.mark.parametrize('smi,expected', [
|
|
96
|
+
('CC(C)(C)OC(=O)N(C(=O)OC(C)(C)C)c1ccccc1', {'amine_boc': 2}),
|
|
97
|
+
('CC(C)(C)OC(=O)Nc1ccc(OC(C)(C)C)cc1', {'amine_boc': 1, 'hydroxyl_tbu': 1}),
|
|
98
|
+
('c1ccc(COC(=O)Nc2ccc(OCc3ccccc3)cc2)cc1', {'amine_cbz': 1, 'hydroxyl_benzyl': 1}),
|
|
99
|
+
('CC(C)OCOCc1ccccc1', {'hydroxyl_bom': 1}),
|
|
100
|
+
('C(OCOC)(OCOC)C', {'hydroxyl_mom': 2}),
|
|
101
|
+
('OC(CO[Si](C)(C)C)O[Si](C)(C)C', {'hydroxyl_tms': 2}),
|
|
102
|
+
], ids=['double_boc', 'boc_and_tbu', 'cbz_and_benzyl', 'bom_no_benzyl', 'double_mom', 'double_tms'])
|
|
103
|
+
def test_multiple_pg_on_molecule(smi, expected):
|
|
104
|
+
"""Test correct detection of multiple PGs and no false positives from sub-pattern overlap."""
|
|
105
|
+
mol = smiles(smi)
|
|
106
|
+
mol.canonicalize()
|
|
107
|
+
pgs = mol.protective_groups
|
|
108
|
+
assert pgs == expected, f'got {pgs}'
|