synkit 1.6.5__py3-none-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 (636) hide show
  1. synkit/CRN/Benchmark/__init__.py +53 -0
  2. synkit/CRN/Benchmark/crosschecks.py +13 -0
  3. synkit/CRN/Benchmark/kegg.py +12 -0
  4. synkit/CRN/Benchmark/networks.py +13 -0
  5. synkit/CRN/Benchmark/scaling.py +13 -0
  6. synkit/CRN/Benchmark/validate.py +13 -0
  7. synkit/CRN/Construct/__init__.py +16 -0
  8. synkit/CRN/Construct/abstract.py +668 -0
  9. synkit/CRN/Construct/arity.py +36 -0
  10. synkit/CRN/Construct/builder.py +619 -0
  11. synkit/CRN/Construct/derivation.py +63 -0
  12. synkit/CRN/Construct/flattener.py +100 -0
  13. synkit/CRN/Construct/keys.py +14 -0
  14. synkit/CRN/Construct/mixtures.py +117 -0
  15. synkit/CRN/Construct/rdkit_worker.py +69 -0
  16. synkit/CRN/Construct/smiles.py +99 -0
  17. synkit/CRN/Construct/state.py +31 -0
  18. synkit/CRN/Construct/strategy.py +75 -0
  19. synkit/CRN/Construct/worker.py +42 -0
  20. synkit/CRN/IO/__init__.py +36 -0
  21. synkit/CRN/IO/sbml.py +925 -0
  22. synkit/CRN/Pathway/__init__.py +72 -0
  23. synkit/CRN/Pathway/_adapter.py +333 -0
  24. synkit/CRN/Pathway/pathfinder.py +500 -0
  25. synkit/CRN/Pathway/reachability.py +796 -0
  26. synkit/CRN/Pathway/realizability.py +1322 -0
  27. synkit/CRN/Petrinet/__init__.py +93 -0
  28. synkit/CRN/Petrinet/analyzer.py +486 -0
  29. synkit/CRN/Petrinet/minimal_semiflows.py +436 -0
  30. synkit/CRN/Petrinet/net.py +1041 -0
  31. synkit/CRN/Petrinet/persistence.py +97 -0
  32. synkit/CRN/Petrinet/semiflows.py +483 -0
  33. synkit/CRN/Petrinet/siphon_search.py +402 -0
  34. synkit/CRN/Petrinet/structure.py +286 -0
  35. synkit/CRN/Props/__init__.py +130 -0
  36. synkit/CRN/Props/_rank.py +29 -0
  37. synkit/CRN/Props/deficiency.py +953 -0
  38. synkit/CRN/Props/dynamics.py +832 -0
  39. synkit/CRN/Props/helper.py +151 -0
  40. synkit/CRN/Props/stoich.py +751 -0
  41. synkit/CRN/Props/thermo.py +584 -0
  42. synkit/CRN/Query/__init__.py +66 -0
  43. synkit/CRN/Query/cache.py +41 -0
  44. synkit/CRN/Query/kegg_api.py +99 -0
  45. synkit/CRN/Query/kegg_extract.py +714 -0
  46. synkit/CRN/Query/kegg_impute.py +707 -0
  47. synkit/CRN/Query/kegg_parse.py +480 -0
  48. synkit/CRN/Query/to_syncrn.py +290 -0
  49. synkit/CRN/README.md +234 -0
  50. synkit/CRN/Structure/__init__.py +12 -0
  51. synkit/CRN/Structure/_graph_io.py +713 -0
  52. synkit/CRN/Structure/_matrices.py +88 -0
  53. synkit/CRN/Structure/_parse.py +544 -0
  54. synkit/CRN/Structure/reaction.py +237 -0
  55. synkit/CRN/Structure/rule.py +55 -0
  56. synkit/CRN/Structure/species.py +46 -0
  57. synkit/CRN/Structure/syncrn.py +929 -0
  58. synkit/CRN/Symmetry/__init__.py +20 -0
  59. synkit/CRN/Symmetry/_common.py +1059 -0
  60. synkit/CRN/Symmetry/_ir.py +940 -0
  61. synkit/CRN/Symmetry/automorphism.py +472 -0
  62. synkit/CRN/Symmetry/canon.py +441 -0
  63. synkit/CRN/Symmetry/isomorphism.py +412 -0
  64. synkit/CRN/Symmetry/symmetry.py +61 -0
  65. synkit/CRN/Symmetry/wl_canon.py +967 -0
  66. synkit/CRN/Visualize/__init__.py +11 -0
  67. synkit/CRN/Visualize/labels.py +113 -0
  68. synkit/CRN/Visualize/layout.py +708 -0
  69. synkit/CRN/Visualize/palette.py +292 -0
  70. synkit/CRN/Visualize/validation.py +122 -0
  71. synkit/CRN/Visualize/vis.py +1152 -0
  72. synkit/CRN/__init__.py +233 -0
  73. synkit/CRN/_build.py +50 -0
  74. synkit/CRN/_native_build.py +175 -0
  75. synkit/CRN/analysis.py +629 -0
  76. synkit/CRN/backend.py +74 -0
  77. synkit/CRN/cpp/CMakeLists.txt +58 -0
  78. synkit/CRN/cpp/algebra.cpp +46 -0
  79. synkit/CRN/cpp/closure.cpp +29 -0
  80. synkit/CRN/cpp/exact.h +55 -0
  81. synkit/CRN/cpp/pathway.cpp +46 -0
  82. synkit/CRN/cpp/semiflows.cpp +209 -0
  83. synkit/CRN/cpp/structural.cpp +240 -0
  84. synkit/CRN/cpp/symmetry.cpp +45 -0
  85. synkit/CRN/cpp/write_manifest.cmake +28 -0
  86. synkit/CRN/kinds.py +137 -0
  87. synkit/CRN/lib/synkit_crn.dll +0 -0
  88. synkit/CRN/lib/synkit_crn.json +33 -0
  89. synkit/CRN/native.py +388 -0
  90. synkit/CRN/native_analysis.py +459 -0
  91. synkit/Chem/Cluster/__init__.py +0 -0
  92. synkit/Chem/Cluster/butina.py +140 -0
  93. synkit/Chem/Fingerprint/__init__.py +0 -0
  94. synkit/Chem/Fingerprint/fp_calculator.py +155 -0
  95. synkit/Chem/Fingerprint/smiles_featurizer.py +258 -0
  96. synkit/Chem/Fingerprint/transformation_fp.py +139 -0
  97. synkit/Chem/Mapper/README.rst +43 -0
  98. synkit/Chem/Mapper/__init__.py +255 -0
  99. synkit/Chem/Mapper/_compat.py +130 -0
  100. synkit/Chem/Mapper/adapters/__init__.py +1 -0
  101. synkit/Chem/Mapper/adapters/prediction.py +126 -0
  102. synkit/Chem/Mapper/adapters/template.py +412 -0
  103. synkit/Chem/Mapper/chem/__init__.py +52 -0
  104. synkit/Chem/Mapper/chem/aam.py +673 -0
  105. synkit/Chem/Mapper/chem/blind.py +160 -0
  106. synkit/Chem/Mapper/chem/its.py +309 -0
  107. synkit/Chem/Mapper/chem/smiles.py +564 -0
  108. synkit/Chem/Mapper/graph/__init__.py +66 -0
  109. synkit/Chem/Mapper/graph/automorphism.py +395 -0
  110. synkit/Chem/Mapper/graph/blockcut.py +98 -0
  111. synkit/Chem/Mapper/graph/labeled_graph.py +104 -0
  112. synkit/Chem/Mapper/graph/refine.py +176 -0
  113. synkit/Chem/Mapper/graph/synkit_adapter.py +110 -0
  114. synkit/Chem/Mapper/io/__init__.py +13 -0
  115. synkit/Chem/Mapper/io/idxmapstr.py +82 -0
  116. synkit/Chem/Mapper/mapper.py +685 -0
  117. synkit/Chem/Mapper/reaction_search.py +554 -0
  118. synkit/Chem/Mapper/reporting/__init__.py +1 -0
  119. synkit/Chem/Mapper/reporting/alternatives.py +451 -0
  120. synkit/Chem/Mapper/reporting/analysis.py +681 -0
  121. synkit/Chem/Mapper/reporting/native_analysis.py +485 -0
  122. synkit/Chem/Mapper/reporting/spectrum.py +642 -0
  123. synkit/Chem/Mapper/solvers/__init__.py +1 -0
  124. synkit/Chem/Mapper/solvers/exact/__init__.py +141 -0
  125. synkit/Chem/Mapper/solvers/exact/assignment/__init__.py +1 -0
  126. synkit/Chem/Mapper/solvers/exact/assignment/assignment_certificate.py +113 -0
  127. synkit/Chem/Mapper/solvers/exact/assignment/assignment_domains.py +162 -0
  128. synkit/Chem/Mapper/solvers/exact/assignment/assignment_verification.py +58 -0
  129. synkit/Chem/Mapper/solvers/exact/assignment/incremental_assignment.py +247 -0
  130. synkit/Chem/Mapper/solvers/exact/certificate.py +128 -0
  131. synkit/Chem/Mapper/solvers/exact/core.py +183 -0
  132. synkit/Chem/Mapper/solvers/exact/distance_search/__init__.py +1 -0
  133. synkit/Chem/Mapper/solvers/exact/distance_search/bounds.py +319 -0
  134. synkit/Chem/Mapper/solvers/exact/distance_search/exhaustive.py +179 -0
  135. synkit/Chem/Mapper/solvers/exact/distance_search/inputs.py +283 -0
  136. synkit/Chem/Mapper/solvers/exact/distance_search/records.py +242 -0
  137. synkit/Chem/Mapper/solvers/exact/distance_search/search.py +1025 -0
  138. synkit/Chem/Mapper/solvers/exact/distance_search/verify.py +378 -0
  139. synkit/Chem/Mapper/solvers/exact/edit_support.py +375 -0
  140. synkit/Chem/Mapper/solvers/exact/hybrid.py +224 -0
  141. synkit/Chem/Mapper/solvers/exact/hydrogen.py +264 -0
  142. synkit/Chem/Mapper/solvers/exact/kernel_solvers/__init__.py +1 -0
  143. synkit/Chem/Mapper/solvers/exact/kernel_solvers/branching.py +499 -0
  144. synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumerate.py +1050 -0
  145. synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumeration_result.py +30 -0
  146. synkit/Chem/Mapper/solvers/exact/kernel_solvers/kernel.py +256 -0
  147. synkit/Chem/Mapper/solvers/exact/kernel_solvers/milp.py +194 -0
  148. synkit/Chem/Mapper/solvers/exact/native/__init__.py +1 -0
  149. synkit/Chem/Mapper/solvers/exact/native/build.py +154 -0
  150. synkit/Chem/Mapper/solvers/exact/native/candidates.py +593 -0
  151. synkit/Chem/Mapper/solvers/exact/native/canonical.py +396 -0
  152. synkit/Chem/Mapper/solvers/exact/native/disk_frontier.py +415 -0
  153. synkit/Chem/Mapper/solvers/exact/native/frontier.py +390 -0
  154. synkit/Chem/Mapper/solvers/exact/native/hydrogen_quotient.py +213 -0
  155. synkit/Chem/Mapper/solvers/exact/native/its.py +528 -0
  156. synkit/Chem/Mapper/solvers/exact/native/native_distance.cpp +2803 -0
  157. synkit/Chem/Mapper/solvers/exact/native/orbit_aggregation.py +328 -0
  158. synkit/Chem/Mapper/solvers/exact/native/parallel.py +205 -0
  159. synkit/Chem/Mapper/solvers/exact/native/pmcd.py +203 -0
  160. synkit/Chem/Mapper/solvers/exact/native/runtime.py +40 -0
  161. synkit/Chem/Mapper/solvers/exact/native/search.py +294 -0
  162. synkit/Chem/Mapper/solvers/exact/native/separator.py +224 -0
  163. synkit/Chem/Mapper/solvers/exact/native_build.py +12 -0
  164. synkit/Chem/Mapper/solvers/exact/pabs/__init__.py +1 -0
  165. synkit/Chem/Mapper/solvers/exact/pabs/cost_lattice.py +43 -0
  166. synkit/Chem/Mapper/solvers/exact/pabs/factor_spectrum.py +110 -0
  167. synkit/Chem/Mapper/solvers/exact/pabs/multi_shell.py +368 -0
  168. synkit/Chem/Mapper/solvers/exact/pabs/propagation.py +679 -0
  169. synkit/Chem/Mapper/solvers/exact/pabs/propagation_bounds.py +436 -0
  170. synkit/Chem/Mapper/solvers/exact/pabs/propagation_limits.py +13 -0
  171. synkit/Chem/Mapper/solvers/exact/pabs/propagation_search.py +856 -0
  172. synkit/Chem/Mapper/solvers/exact/pabs/propagation_symmetry.py +66 -0
  173. synkit/Chem/Mapper/solvers/exact/pabs/residual_typed_bonds.py +136 -0
  174. synkit/Chem/Mapper/solvers/exact/pabs/reward_frontier.py +201 -0
  175. synkit/Chem/Mapper/solvers/exact/pabs/separator_bound.py +258 -0
  176. synkit/Chem/Mapper/solvers/exact/pabs/separator_spectrum.py +308 -0
  177. synkit/Chem/Mapper/solvers/exact/pabs/sparse_reward_bound.py +207 -0
  178. synkit/Chem/Mapper/solvers/exact/pabs/suffix_spectrum.py +299 -0
  179. synkit/Chem/Mapper/solvers/exact/search.py +55 -0
  180. synkit/Chem/Mapper/solvers/exact/seeds/__init__.py +1 -0
  181. synkit/Chem/Mapper/solvers/exact/seeds/seed.py +104 -0
  182. synkit/Chem/Mapper/solvers/exact/seeds/seed_fragments.py +148 -0
  183. synkit/Chem/Mapper/solvers/exact/seeds/seed_relaxation.py +181 -0
  184. synkit/Chem/Mapper/solvers/exact/symmetry.py +223 -0
  185. synkit/Chem/Mapper/solvers/native/__init__.py +4 -0
  186. synkit/Chem/Mapper/solvers/native/assignment.py +99 -0
  187. synkit/Chem/Mapper/solvers/native/astar.py +22 -0
  188. synkit/Chem/Mapper/solvers/native/build.py +385 -0
  189. synkit/Chem/Mapper/solvers/native/classification.py +641 -0
  190. synkit/Chem/Mapper/solvers/native/core.py +378 -0
  191. synkit/Chem/Mapper/solvers/native/cpp/CMakeLists.txt +120 -0
  192. synkit/Chem/Mapper/solvers/native/cpp/examples/map_reaction.cpp +29 -0
  193. synkit/Chem/Mapper/solvers/native/cpp/examples/slap_graph.cpp +63 -0
  194. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/assignment/assignment.h +18 -0
  195. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/astar.h +18 -0
  196. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/bound.h +33 -0
  197. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/pmcd_witness.h +16 -0
  198. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/runtime.h +130 -0
  199. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/hydrogen/hydrogen.h +73 -0
  200. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mapper.h +175 -0
  201. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mces/mces.h +9 -0
  202. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/pabs/seed.h +34 -0
  203. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/reaction/reaction.h +30 -0
  204. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/sepdp/sepdp.h +21 -0
  205. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/slap.h +72 -0
  206. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/solvers/solvers.h +16 -0
  207. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/classification.h +140 -0
  208. synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/cpabs.h +33 -0
  209. synkit/Chem/Mapper/solvers/native/cpp/src/assignment/abb.cpp +302 -0
  210. synkit/Chem/Mapper/solvers/native/cpp/src/astar/CMakeLists.txt +12 -0
  211. synkit/Chem/Mapper/solvers/native/cpp/src/astar/README.md +47 -0
  212. synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar.cpp +228 -0
  213. synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar_graph.cpp +37 -0
  214. synkit/Chem/Mapper/solvers/native/cpp/src/astar/bound.cpp +212 -0
  215. synkit/Chem/Mapper/solvers/native/cpp/src/core/dispatch.cpp +272 -0
  216. synkit/Chem/Mapper/solvers/native/cpp/src/core/pmcd_witness.cpp +36 -0
  217. synkit/Chem/Mapper/solvers/native/cpp/src/hydrogen/hydrogen.cpp +380 -0
  218. synkit/Chem/Mapper/solvers/native/cpp/src/mces/README.md +57 -0
  219. synkit/Chem/Mapper/solvers/native/cpp/src/mces/mces.cpp +287 -0
  220. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/bridge.cpp +334 -0
  221. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/chemical_floor.inc +181 -0
  222. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/endpoint_symmetry.inc +104 -0
  223. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/exact_helpers.h +20 -0
  224. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_assignment_certificate.h +165 -0
  225. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_certificate.h +125 -0
  226. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_model.h +365 -0
  227. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_provider.inc +319 -0
  228. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_rounding.inc +170 -0
  229. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_test_hooks.h +342 -0
  230. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/objective_reduction.inc +137 -0
  231. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/overlap_dual.inc +351 -0
  232. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/profile_seed.inc +214 -0
  233. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/seed_heuristics.inc +640 -0
  234. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_embedding.cpp +430 -0
  235. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_root_lp.h +176 -0
  236. synkit/Chem/Mapper/solvers/native/cpp/src/pabs/test_hooks.inc +262 -0
  237. synkit/Chem/Mapper/solvers/native/cpp/src/reaction/reaction.cpp +611 -0
  238. synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/README.md +61 -0
  239. synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/sepdp.cpp +253 -0
  240. synkit/Chem/Mapper/solvers/native/cpp/src/slap.cpp +810 -0
  241. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/formulation.h +138 -0
  242. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/maxsat.cpp +526 -0
  243. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/ortools.cpp +475 -0
  244. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/seed.h +80 -0
  245. synkit/Chem/Mapper/solvers/native/cpp/src/solvers/weighted_cnf.h +256 -0
  246. synkit/Chem/Mapper/solvers/native/cpp/src/supporting/README.md +43 -0
  247. synkit/Chem/Mapper/solvers/native/cpp/src/supporting/dfs.cpp +123 -0
  248. synkit/Chem/Mapper/solvers/native/cpp/src/supporting/edit_support.cpp +224 -0
  249. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification.cpp +553 -0
  250. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_pipeline.cpp +124 -0
  251. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_stream.h +13 -0
  252. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/cpabs.cpp +455 -0
  253. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/edge_deck.cpp +743 -0
  254. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
  255. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
  256. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naugraph.c +732 -0
  257. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.c +94 -0
  258. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.h +49 -0
  259. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.c +1771 -0
  260. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.h +133 -0
  261. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nautil.c +750 -0
  262. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naututil.h +355 -0
  263. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.c +1256 -0
  264. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.h +1544 -0
  265. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.c +1131 -0
  266. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.h +70 -0
  267. synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/sorttemplates.c +580 -0
  268. synkit/Chem/Mapper/solvers/native/hydrogen.py +711 -0
  269. synkit/Chem/Mapper/solvers/native/implicit.py +506 -0
  270. synkit/Chem/Mapper/solvers/native/implicit_certificate.py +187 -0
  271. synkit/Chem/Mapper/solvers/native/implicit_preparation.py +309 -0
  272. synkit/Chem/Mapper/solvers/native/lib/synkit_mapper.dll +0 -0
  273. synkit/Chem/Mapper/solvers/native/mces.py +16 -0
  274. synkit/Chem/Mapper/solvers/native/reaction.py +179 -0
  275. synkit/Chem/Mapper/solvers/native/reaction_build.py +277 -0
  276. synkit/Chem/Mapper/solvers/native/reaction_text.py +26 -0
  277. synkit/Chem/Mapper/solvers/native/runtime.py +242 -0
  278. synkit/Chem/Mapper/solvers/native/sepdp.py +47 -0
  279. synkit/Chem/Mapper/solvers/native/slap.py +266 -0
  280. synkit/Chem/Mapper/solvers/native/solvers.py +67 -0
  281. synkit/Chem/Mapper/solvers/native/stereo.py +86 -0
  282. synkit/Chem/Mapper/solvers/native/supporting.py +34 -0
  283. synkit/Chem/Mapper/solvers/native/symmetry.py +91 -0
  284. synkit/Chem/Mapper/solvers/slap/__init__.py +29 -0
  285. synkit/Chem/Mapper/solvers/slap/lap.py +174 -0
  286. synkit/Chem/Mapper/solvers/slap/sequential.py +696 -0
  287. synkit/Chem/Mapper/validation/__init__.py +1 -0
  288. synkit/Chem/Mapper/validation/aam_validator.py +442 -0
  289. synkit/Chem/Mapper/validation/annotations.py +274 -0
  290. synkit/Chem/Mapper/validation/bonds.py +124 -0
  291. synkit/Chem/Mapper/validation/cohorts.py +72 -0
  292. synkit/Chem/Mapper/validation/identifiability.py +150 -0
  293. synkit/Chem/Mapper/validation/numerical_scope.py +36 -0
  294. synkit/Chem/Mapper/validation/orbits.py +137 -0
  295. synkit/Chem/Molecule/__init__.py +0 -0
  296. synkit/Chem/Molecule/_chirality_loci.py +165 -0
  297. synkit/Chem/Molecule/_chirality_types.py +231 -0
  298. synkit/Chem/Molecule/_helical_loci.py +167 -0
  299. synkit/Chem/Molecule/_stereo_axis_evidence.py +202 -0
  300. synkit/Chem/Molecule/_stereo_element_detection.py +193 -0
  301. synkit/Chem/Molecule/_stereo_orientation_constraints.py +511 -0
  302. synkit/Chem/Molecule/_tetrahedral_carriers.py +63 -0
  303. synkit/Chem/Molecule/atom_features.py +379 -0
  304. synkit/Chem/Molecule/chirality.py +1041 -0
  305. synkit/Chem/Molecule/cip_assignment.py +569 -0
  306. synkit/Chem/Molecule/cip_ranking.py +843 -0
  307. synkit/Chem/Molecule/coordinate_stereo.py +503 -0
  308. synkit/Chem/Molecule/derived_stereo_naming.py +165 -0
  309. synkit/Chem/Molecule/descriptors.py +452 -0
  310. synkit/Chem/Molecule/formula.py +249 -0
  311. synkit/Chem/Molecule/global_stereo.py +175 -0
  312. synkit/Chem/Molecule/graph_annotator.py +300 -0
  313. synkit/Chem/Molecule/standardize.py +817 -0
  314. synkit/Chem/Molecule/stereo_evidence.py +179 -0
  315. synkit/Chem/Molecule/stereo_perception.py +1032 -0
  316. synkit/Chem/Molecule/valence.py +123 -0
  317. synkit/Chem/Reaction/Mapper/__init__.py +44 -0
  318. synkit/Chem/Reaction/__init__.py +20 -0
  319. synkit/Chem/Reaction/aam_validator.py +5 -0
  320. synkit/Chem/Reaction/balance_check.py +138 -0
  321. synkit/Chem/Reaction/canon_rsmi.py +249 -0
  322. synkit/Chem/Reaction/cleaning.py +58 -0
  323. synkit/Chem/Reaction/deionize.py +199 -0
  324. synkit/Chem/Reaction/explicit_h_audit.py +232 -0
  325. synkit/Chem/Reaction/fix_aam.py +64 -0
  326. synkit/Chem/Reaction/neutralize.py +207 -0
  327. synkit/Chem/Reaction/radical_wildcard.py +224 -0
  328. synkit/Chem/Reaction/standardize.py +203 -0
  329. synkit/Chem/Reaction/tautomerize.py +205 -0
  330. synkit/Chem/__init__.py +1 -0
  331. synkit/Chem/utils.py +315 -0
  332. synkit/Data/__init__.py +0 -0
  333. synkit/Data/aldol.json.gz +1 -0
  334. synkit/Data/gen_partial_aam.py +147 -0
  335. synkit/Data/paracetamol.json.gz +1 -0
  336. synkit/Graph/Canon/__init__.py +16 -0
  337. synkit/Graph/Canon/_native/__init__.py +13 -0
  338. synkit/Graph/Canon/_native/build.py +93 -0
  339. synkit/Graph/Canon/_native/lib/synkit_canon.dll +0 -0
  340. synkit/Graph/Canon/_native/prepare.py +49 -0
  341. synkit/Graph/Canon/_native/runtime.py +140 -0
  342. synkit/Graph/Canon/_native/search.py +137 -0
  343. synkit/Graph/Canon/canon_algs.py +228 -0
  344. synkit/Graph/Canon/canon_graph.py +531 -0
  345. synkit/Graph/Canon/cpp/CMakeLists.txt +42 -0
  346. synkit/Graph/Canon/cpp/README.md +123 -0
  347. synkit/Graph/Canon/cpp/examples/canonical_graph.cpp +16 -0
  348. synkit/Graph/Canon/cpp/include/synkit_canon/canon.h +85 -0
  349. synkit/Graph/Canon/cpp/src/canonical.cpp +391 -0
  350. synkit/Graph/Canon/cpp/src/symmetry.h +83 -0
  351. synkit/Graph/Canon/cpp/tests/symmetry.cpp +93 -0
  352. synkit/Graph/Canon/exact.py +1020 -0
  353. synkit/Graph/Canon/exact_refinement.py +368 -0
  354. synkit/Graph/Canon/nauty.py +143 -0
  355. synkit/Graph/Context/__init__.py +0 -0
  356. synkit/Graph/Context/hier_context.py +244 -0
  357. synkit/Graph/Context/radius_expand.py +252 -0
  358. synkit/Graph/FG/__init__.py +19 -0
  359. synkit/Graph/FG/api.py +39 -0
  360. synkit/Graph/FG/audit.py +155 -0
  361. synkit/Graph/FG/catalog.py +919 -0
  362. synkit/Graph/FG/detector.py +288 -0
  363. synkit/Graph/FG/model.py +97 -0
  364. synkit/Graph/FG/recognizers.py +274 -0
  365. synkit/Graph/FG/ring_system.py +154 -0
  366. synkit/Graph/Feature/Descriptors/topology.py +857 -0
  367. synkit/Graph/Feature/Fingerprint/__init__.py +0 -0
  368. synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +222 -0
  369. synkit/Graph/Feature/__init__.py +5 -0
  370. synkit/Graph/Feature/graph_descriptors.py +323 -0
  371. synkit/Graph/Feature/graph_fps.py +96 -0
  372. synkit/Graph/Feature/graph_signature.py +239 -0
  373. synkit/Graph/Feature/hash_fps.py +133 -0
  374. synkit/Graph/Feature/morgan_fps.py +88 -0
  375. synkit/Graph/Feature/path_fps.py +81 -0
  376. synkit/Graph/Feature/wl_hash.py +134 -0
  377. synkit/Graph/Fusion/__init__.py +85 -0
  378. synkit/Graph/Fusion/candidate.py +559 -0
  379. synkit/Graph/Fusion/constructor.py +877 -0
  380. synkit/Graph/Fusion/identity.py +184 -0
  381. synkit/Graph/Fusion/interface.py +700 -0
  382. synkit/Graph/Fusion/search.py +241 -0
  383. synkit/Graph/Hyrogen/__init__.py +6 -0
  384. synkit/Graph/Hyrogen/_misc.py +591 -0
  385. synkit/Graph/Hyrogen/hcomplete.py +995 -0
  386. synkit/Graph/Hyrogen/hcomplete_legacy.py +368 -0
  387. synkit/Graph/Hyrogen/hcompletion_algorithms.py +713 -0
  388. synkit/Graph/Hyrogen/hextend.py +570 -0
  389. synkit/Graph/Hyrogen/hextend_legacy.py +215 -0
  390. synkit/Graph/ITS/__init__.py +4 -0
  391. synkit/Graph/ITS/its_builder.py +120 -0
  392. synkit/Graph/ITS/its_construction.py +719 -0
  393. synkit/Graph/ITS/its_decompose.py +572 -0
  394. synkit/Graph/ITS/its_destruction.py +331 -0
  395. synkit/Graph/ITS/its_expand.py +1251 -0
  396. synkit/Graph/ITS/its_relabel.py +187 -0
  397. synkit/Graph/ITS/its_reverter.py +356 -0
  398. synkit/Graph/ITS/normalize_aam.py +145 -0
  399. synkit/Graph/ITS/partial_its.py +238 -0
  400. synkit/Graph/ITS/rc_extractor.py +656 -0
  401. synkit/Graph/ITS/stereo.py +74 -0
  402. synkit/Graph/MTG/__init__.py +107 -0
  403. synkit/Graph/MTG/gluing.py +451 -0
  404. synkit/Graph/MTG/history.py +928 -0
  405. synkit/Graph/MTG/inventory.py +118 -0
  406. synkit/Graph/MTG/local.py +447 -0
  407. synkit/Graph/MTG/mcs_matcher.py +246 -0
  408. synkit/Graph/MTG/mtg.py +611 -0
  409. synkit/Graph/MTG/mtg_explore.py +74 -0
  410. synkit/Graph/MTG/observation.py +209 -0
  411. synkit/Graph/MTG/process.py +577 -0
  412. synkit/Graph/MTG/queries.py +66 -0
  413. synkit/Graph/MTG/realization.py +95 -0
  414. synkit/Graph/MTG/series_parallel.py +107 -0
  415. synkit/Graph/MTG/utils.py +424 -0
  416. synkit/Graph/Matcher/__init__.py +10 -0
  417. synkit/Graph/Matcher/approx_mcs.py +1264 -0
  418. synkit/Graph/Matcher/auto_est.py +736 -0
  419. synkit/Graph/Matcher/automorphism.py +314 -0
  420. synkit/Graph/Matcher/batch_cluster.py +224 -0
  421. synkit/Graph/Matcher/dedup_matches.py +247 -0
  422. synkit/Graph/Matcher/graph_cluster.py +191 -0
  423. synkit/Graph/Matcher/graph_matcher.py +312 -0
  424. synkit/Graph/Matcher/graph_morphism.py +421 -0
  425. synkit/Graph/Matcher/mcs_matcher.py +804 -0
  426. synkit/Graph/Matcher/multi_turbo_iso.py +186 -0
  427. synkit/Graph/Matcher/orbit.py +284 -0
  428. synkit/Graph/Matcher/partial_matcher.py +694 -0
  429. synkit/Graph/Matcher/sing.py +544 -0
  430. synkit/Graph/Matcher/subgraph_matcher.py +812 -0
  431. synkit/Graph/Matcher/turbo_iso.py +272 -0
  432. synkit/Graph/Matcher/wl_sel.py +537 -0
  433. synkit/Graph/Mech/__init__.py +26 -0
  434. synkit/Graph/Mech/conversion.py +1466 -0
  435. synkit/Graph/Mech/conversion_debug.py +237 -0
  436. synkit/Graph/Mech/electron_accounting.py +200 -0
  437. synkit/Graph/Mech/lwg_editor.py +406 -0
  438. synkit/Graph/Mech/lwg_ops.py +313 -0
  439. synkit/Graph/Morphism/__init__.py +99 -0
  440. synkit/Graph/Morphism/constraints.py +460 -0
  441. synkit/Graph/Morphism/llg.py +999 -0
  442. synkit/Graph/Morphism/morphism.py +349 -0
  443. synkit/Graph/Morphism/stereo.py +258 -0
  444. synkit/Graph/Morphism/stereo_morphism.py +383 -0
  445. synkit/Graph/Morphism/stereo_morphism_helpers.py +957 -0
  446. synkit/Graph/Morphism/stereo_morphism_types.py +117 -0
  447. synkit/Graph/Stereo/__init__.py +376 -0
  448. synkit/Graph/Stereo/_descriptor_core.py +207 -0
  449. synkit/Graph/Stereo/_descriptor_serialization.py +138 -0
  450. synkit/Graph/Stereo/canonical.py +648 -0
  451. synkit/Graph/Stereo/changes.py +964 -0
  452. synkit/Graph/Stereo/composition.py +391 -0
  453. synkit/Graph/Stereo/configured.py +946 -0
  454. synkit/Graph/Stereo/couplings.py +245 -0
  455. synkit/Graph/Stereo/descriptors.py +1030 -0
  456. synkit/Graph/Stereo/enumeration.py +597 -0
  457. synkit/Graph/Stereo/extended_descriptors.py +406 -0
  458. synkit/Graph/Stereo/global_configured.py +152 -0
  459. synkit/Graph/Stereo/global_stereo.py +304 -0
  460. synkit/Graph/Stereo/identity.py +648 -0
  461. synkit/Graph/Stereo/interchange.py +496 -0
  462. synkit/Graph/Stereo/legacy.py +460 -0
  463. synkit/Graph/Stereo/matching.py +899 -0
  464. synkit/Graph/Stereo/native.py +154 -0
  465. synkit/Graph/Stereo/orbits.py +668 -0
  466. synkit/Graph/Stereo/outcomes.py +99 -0
  467. synkit/Graph/Stereo/pairwise.py +350 -0
  468. synkit/Graph/Stereo/rdkit_adapter.py +662 -0
  469. synkit/Graph/Stereo/rdkit_identity.py +118 -0
  470. synkit/Graph/Stereo/rdkit_resonance.py +140 -0
  471. synkit/Graph/Stereo/reaction_interchange.py +266 -0
  472. synkit/Graph/Stereo/semantics.py +296 -0
  473. synkit/Graph/Stereo/supports.py +257 -0
  474. synkit/Graph/Stereo/wire.py +339 -0
  475. synkit/Graph/Wildcard/__init__.py +0 -0
  476. synkit/Graph/Wildcard/fuse_graph.py +156 -0
  477. synkit/Graph/Wildcard/graph_wc.py +303 -0
  478. synkit/Graph/Wildcard/its_merge.py +857 -0
  479. synkit/Graph/Wildcard/radwc.py +114 -0
  480. synkit/Graph/Wildcard/wc_matcher.py +576 -0
  481. synkit/Graph/Wildcard/wildcard.py +225 -0
  482. synkit/Graph/__init__.py +17 -0
  483. synkit/Graph/canon_graph.py +14 -0
  484. synkit/Graph/syn_graph.py +199 -0
  485. synkit/Graph/utils.py +324 -0
  486. synkit/IO/__init__.py +4 -0
  487. synkit/IO/chem_converter.py +1059 -0
  488. synkit/IO/combinatorial/__init__.py +8 -0
  489. synkit/IO/combinatorial/gml_to_graph.py +223 -0
  490. synkit/IO/combinatorial/graph_to_gml.py +280 -0
  491. synkit/IO/combinatorial/graph_to_smarts.py +186 -0
  492. synkit/IO/combinatorial/smarts_expander.py +147 -0
  493. synkit/IO/combinatorial/smarts_generalizer.py +133 -0
  494. synkit/IO/combinatorial/smarts_to_graph.py +179 -0
  495. synkit/IO/conversion.py +42 -0
  496. synkit/IO/data_io.py +269 -0
  497. synkit/IO/data_process.py +143 -0
  498. synkit/IO/debug.py +73 -0
  499. synkit/IO/gml_to_nx.py +150 -0
  500. synkit/IO/graph_to_mol.py +199 -0
  501. synkit/IO/mol_to_graph.py +1105 -0
  502. synkit/IO/mol_to_graph_chemistry.py +545 -0
  503. synkit/IO/nx_to_gml.py +209 -0
  504. synkit/IO/smiles_to_id.py +123 -0
  505. synkit/Mechanism/__init__.py +151 -0
  506. synkit/Mechanism/adapters.py +136 -0
  507. synkit/Mechanism/audit.py +323 -0
  508. synkit/Mechanism/benchmark.py +638 -0
  509. synkit/Mechanism/compatibility.py +13 -0
  510. synkit/Mechanism/drawing.py +85 -0
  511. synkit/Mechanism/electron_models.py +725 -0
  512. synkit/Mechanism/equivalence.py +521 -0
  513. synkit/Mechanism/evidence.py +262 -0
  514. synkit/Mechanism/interchange.py +141 -0
  515. synkit/Mechanism/model.py +918 -0
  516. synkit/Mechanism/neighbor_stereo.py +312 -0
  517. synkit/Mechanism/radical_data.py +740 -0
  518. synkit/Mechanism/replay.py +986 -0
  519. synkit/Mechanism/schema.py +264 -0
  520. synkit/Mechanism/stereo_state.py +416 -0
  521. synkit/Mechanism/symbols.py +91 -0
  522. synkit/Rule/Apply/__init__.py +35 -0
  523. synkit/Rule/Apply/conditions.py +45 -0
  524. synkit/Rule/Apply/dpo.py +998 -0
  525. synkit/Rule/Apply/rule_matcher.py +218 -0
  526. synkit/Rule/Apply/validation.py +354 -0
  527. synkit/Rule/Compose/__init__.py +106 -0
  528. synkit/Rule/Compose/_identity.py +43 -0
  529. synkit/Rule/Compose/composition.py +976 -0
  530. synkit/Rule/Compose/laws.py +439 -0
  531. synkit/Rule/Compose/search.py +771 -0
  532. synkit/Rule/Compose/synrule.py +170 -0
  533. synkit/Rule/Modify/__init__.py +0 -0
  534. synkit/Rule/Modify/implict_rule.py +56 -0
  535. synkit/Rule/Modify/longest_path.py +92 -0
  536. synkit/Rule/Modify/molecule_rule.py +115 -0
  537. synkit/Rule/Modify/prune_templates.py +78 -0
  538. synkit/Rule/Modify/rule_utils.py +194 -0
  539. synkit/Rule/Modify/strip_rule.py +97 -0
  540. synkit/Rule/__init__.py +28 -0
  541. synkit/Rule/generic_stereo.py +884 -0
  542. synkit/Rule/stereo_identity.py +271 -0
  543. synkit/Rule/syn_rule.py +1090 -0
  544. synkit/Synthesis/MSR/__init__.py +0 -0
  545. synkit/Synthesis/MSR/path_finder.py +239 -0
  546. synkit/Synthesis/Metrics/__init__.py +0 -0
  547. synkit/Synthesis/Metrics/_base.py +52 -0
  548. synkit/Synthesis/Metrics/_plot.py +129 -0
  549. synkit/Synthesis/Metrics/_ranking.py +179 -0
  550. synkit/Synthesis/RBL/__init__.py +80 -0
  551. synkit/Synthesis/RBL/engine.py +1249 -0
  552. synkit/Synthesis/RBL/matching.py +652 -0
  553. synkit/Synthesis/RBL/overlap.py +336 -0
  554. synkit/Synthesis/RBL/pipeline.py +376 -0
  555. synkit/Synthesis/RBL/policy.py +139 -0
  556. synkit/Synthesis/RBL/proof.py +370 -0
  557. synkit/Synthesis/RBL/reaction.py +506 -0
  558. synkit/Synthesis/RBL/state.py +169 -0
  559. synkit/Synthesis/RBL/validation.py +1060 -0
  560. synkit/Synthesis/Reactor/__init__.py +60 -0
  561. synkit/Synthesis/Reactor/core/__init__.py +1 -0
  562. synkit/Synthesis/Reactor/core/engine.py +967 -0
  563. synkit/Synthesis/Reactor/core/product.py +621 -0
  564. synkit/Synthesis/Reactor/core/rewrite.py +561 -0
  565. synkit/Synthesis/Reactor/core/strategy.py +44 -0
  566. synkit/Synthesis/Reactor/matching/__init__.py +1 -0
  567. synkit/Synthesis/Reactor/matching/mixin.py +920 -0
  568. synkit/Synthesis/Reactor/matching/policy.py +345 -0
  569. synkit/Synthesis/Reactor/matching/symmetry.py +309 -0
  570. synkit/Synthesis/Reactor/output/__init__.py +1 -0
  571. synkit/Synthesis/Reactor/output/deduplication.py +703 -0
  572. synkit/Synthesis/Reactor/output/policy.py +16 -0
  573. synkit/Synthesis/Reactor/output/serialization.py +485 -0
  574. synkit/Synthesis/Reactor/output/structural.py +893 -0
  575. synkit/Synthesis/Reactor/stereo/__init__.py +1 -0
  576. synkit/Synthesis/Reactor/stereo/assignment.py +35 -0
  577. synkit/Synthesis/Reactor/stereo/mixin.py +780 -0
  578. synkit/Synthesis/Reactor/variants/__init__.py +1 -0
  579. synkit/Synthesis/Reactor/variants/imbalanced.py +180 -0
  580. synkit/Synthesis/Reactor/variants/partial.py +84 -0
  581. synkit/Synthesis/Reactor/workflow/__init__.py +1 -0
  582. synkit/Synthesis/Reactor/workflow/batch.py +550 -0
  583. synkit/Synthesis/Reactor/workflow/benchmark.py +143 -0
  584. synkit/Synthesis/Reactor/workflow/postprocess.py +267 -0
  585. synkit/Synthesis/Reactor/workflow/rule_filter.py +210 -0
  586. synkit/Synthesis/__init__.py +0 -0
  587. synkit/Synthesis/reactor_utils.py +316 -0
  588. synkit/Utils/__init__.py +0 -0
  589. synkit/Utils/utils.py +172 -0
  590. synkit/Vis/__init__.py +91 -0
  591. synkit/Vis/crn/__init__.py +40 -0
  592. synkit/Vis/epd/__init__.py +20 -0
  593. synkit/Vis/epd/arrows.py +425 -0
  594. synkit/Vis/epd/chem.py +197 -0
  595. synkit/Vis/epd/constants.py +83 -0
  596. synkit/Vis/epd/layout.py +283 -0
  597. synkit/Vis/epd/mapping.py +51 -0
  598. synkit/Vis/epd/models.py +136 -0
  599. synkit/Vis/epd/render.py +543 -0
  600. synkit/Vis/epd/utils.py +108 -0
  601. synkit/Vis/epd/visualizer.py +811 -0
  602. synkit/Vis/graph_visualizer.py +375 -0
  603. synkit/Vis/its/__init__.py +19 -0
  604. synkit/Vis/its/drawer.py +615 -0
  605. synkit/Vis/its_drawer.py +35 -0
  606. synkit/Vis/molecule/__init__.py +31 -0
  607. synkit/Vis/molecule/drawer.py +563 -0
  608. synkit/Vis/molecule/reaction.py +285 -0
  609. synkit/Vis/molecule_drawer.py +33 -0
  610. synkit/Vis/mtg/__init__.py +17 -0
  611. synkit/Vis/mtg/drawer.py +769 -0
  612. synkit/Vis/mtg_drawer.py +34 -0
  613. synkit/Vis/pdf_writer.py +146 -0
  614. synkit/Vis/reaction/__init__.py +19 -0
  615. synkit/Vis/reaction/rule.py +141 -0
  616. synkit/Vis/reaction/rxn.py +532 -0
  617. synkit/Vis/reaction_drawer.py +41 -0
  618. synkit/Vis/space/__init__.py +17 -0
  619. synkit/Vis/space/chemical.py +80 -0
  620. synkit/Vis/space/embedding.py +87 -0
  621. synkit/Vis/vis_synedu/Vis/__init__.py +7 -0
  622. synkit/Vis/vis_synedu/Vis/dpo.py +862 -0
  623. synkit/Vis/vis_synedu/rxn_vis.py +377 -0
  624. synkit/Vis/vis_synedu/vis.py +501 -0
  625. synkit/Vis/visual_drawer.py +215 -0
  626. synkit/Vis/visual_model.py +520 -0
  627. synkit/__init__.py +3 -0
  628. synkit/_native.py +91 -0
  629. synkit/examples.py +50 -0
  630. synkit/version.py +23 -0
  631. synkit-1.6.5.dist-info/METADATA +156 -0
  632. synkit-1.6.5.dist-info/RECORD +636 -0
  633. synkit-1.6.5.dist-info/WHEEL +4 -0
  634. synkit-1.6.5.dist-info/licenses/LICENSE +21 -0
  635. synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
  636. synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
@@ -0,0 +1,205 @@
1
+ from typing import List, Dict, Optional
2
+ from rdkit import Chem
3
+ from joblib import Parallel, delayed
4
+
5
+ from synkit.Graph.FG import smiles_to_graph_and_functional_groups
6
+
7
+
8
+ class Tautomerize:
9
+ """Standardize molecules by converting enol and hemiketal tautomers into
10
+ their more stable carbonyl forms, and apply these corrections to individual
11
+ SMILES or collections of reaction data."""
12
+
13
+ @staticmethod
14
+ def standardize_enol(smiles: str, atom_indices: Optional[List[int]] = None) -> str:
15
+ """Convert an enol tautomer into its corresponding carbonyl form.
16
+
17
+ :param smiles: SMILES string of the enol-containing molecule.
18
+ :type smiles: str
19
+ :param atom_indices: List of three atom indices [C1, C2, O]
20
+ defining the enol. If None, defaults to [0, 1, 2].
21
+ :type atom_indices: List[int] or None
22
+ :return: SMILES of the molecule after enol→carbonyl conversion,
23
+ or an error message if the input is invalid or indices fail.
24
+ :rtype: str
25
+ """
26
+ if atom_indices is None:
27
+ atom_indices = [0, 1, 2]
28
+
29
+ mol = Chem.MolFromSmiles(smiles)
30
+ if mol is None:
31
+ return "Invalid SMILES format."
32
+ emol = Chem.EditableMol(mol)
33
+
34
+ try:
35
+ c_idxs = [
36
+ i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "C"
37
+ ]
38
+ c1_idx, c2_idx = c_idxs[:2]
39
+ o_idx = next(
40
+ i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "O"
41
+ )
42
+ except Exception as e:
43
+ return f"Error processing indices: {e}"
44
+
45
+ try:
46
+ emol.RemoveBond(c1_idx, c2_idx)
47
+ emol.RemoveBond(c2_idx, o_idx)
48
+ emol.AddBond(c1_idx, c2_idx, Chem.rdchem.BondType.SINGLE)
49
+ emol.AddBond(c2_idx, o_idx, Chem.rdchem.BondType.DOUBLE)
50
+ new_mol = emol.GetMol()
51
+ Chem.SanitizeMol(new_mol)
52
+ return Chem.MolToSmiles(new_mol)
53
+ except Exception as e:
54
+ return f"Error in modifying molecule: {e}"
55
+
56
+ @staticmethod
57
+ def standardize_hemiketal(smiles: str, atom_indices: List[int]) -> str:
58
+ """Convert a hemiketal tautomer into its corresponding carbonyl form.
59
+
60
+ :param smiles: SMILES string of the hemiketal-containing
61
+ molecule.
62
+ :type smiles: str
63
+ :param atom_indices: List of atom indices [C, O1, O2] defining
64
+ the hemiketal.
65
+ :type atom_indices: List[int]
66
+ :return: SMILES of the molecule after hemiketal→carbonyl
67
+ conversion, or an error message if the input is invalid.
68
+ :rtype: str
69
+ """
70
+ mol = Chem.MolFromSmiles(smiles)
71
+ if mol is None:
72
+ return "Invalid SMILES format."
73
+ emol = Chem.EditableMol(mol)
74
+
75
+ try:
76
+ c_idx = next(
77
+ i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "C"
78
+ )
79
+ o_idxs = [
80
+ i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "O"
81
+ ]
82
+ o1_idx = o_idxs[0]
83
+ except Exception as e:
84
+ return f"Error processing indices: {e}"
85
+
86
+ try:
87
+ emol.RemoveBond(c_idx, o1_idx)
88
+ if len(o_idxs) > 1:
89
+ emol.RemoveBond(c_idx, o_idxs[1])
90
+ emol.AddBond(c_idx, o1_idx, Chem.rdchem.BondType.DOUBLE)
91
+ new_mol = emol.GetMol()
92
+ Chem.SanitizeMol(new_mol)
93
+ return Chem.MolToSmiles(new_mol)
94
+ except Exception as e:
95
+ return f"Error in modifying molecule: {e}"
96
+
97
+ @staticmethod
98
+ def fix_smiles(smiles: str) -> str:
99
+ """Iteratively apply enol and hemiketal standardizations until no
100
+ further changes, then return the canonical SMILES.
101
+
102
+ :param smiles: SMILES string to standardize.
103
+ :type smiles: str
104
+ :return: Canonical SMILES of the standardized molecule.
105
+ :rtype: str
106
+ """
107
+ while True:
108
+ targets = Tautomerize._tautomer_targets(smiles)
109
+ if not targets:
110
+ break
111
+ label, indices = targets[0]
112
+ if label == "hemiketal":
113
+ smiles = Tautomerize.standardize_hemiketal(smiles, indices)
114
+ elif label == "enol":
115
+ smiles = Tautomerize.standardize_enol(smiles, indices)
116
+ return Chem.CanonSmiles(smiles)
117
+
118
+ @staticmethod
119
+ def _tautomer_targets(smiles: str) -> list[tuple[str, List[int]]]:
120
+ """Return RDKit-index targets used by the tautomer repair helpers."""
121
+ mol = Chem.MolFromSmiles(smiles)
122
+ if mol is None:
123
+ return []
124
+ graph, groups = smiles_to_graph_and_functional_groups(smiles)
125
+ node_to_idx = {
126
+ (
127
+ atom.GetAtomMapNum() if atom.GetAtomMapNum() else atom.GetIdx() + 1
128
+ ): atom.GetIdx()
129
+ for atom in mol.GetAtoms()
130
+ }
131
+
132
+ targets = [
133
+ (label, [node_to_idx[node] for node in nodes])
134
+ for label, nodes in groups
135
+ if label in {"hemiketal", "enol"}
136
+ ]
137
+ targets.extend(
138
+ ("hemiketal", [node_to_idx[node] for node in nodes])
139
+ for nodes in Tautomerize._geminal_diol_nodes(graph)
140
+ )
141
+ return targets
142
+
143
+ @staticmethod
144
+ def _geminal_diol_nodes(graph) -> list[tuple[int, ...]]:
145
+ """Legacy tautomerization compatibility for hydrated carbonyls."""
146
+ targets: list[tuple[int, ...]] = []
147
+ for carbon, data in graph.nodes(data=True):
148
+ if data.get("element") != "C":
149
+ continue
150
+ hydroxyls = [
151
+ neighbor
152
+ for neighbor in graph.neighbors(carbon)
153
+ if graph.nodes[neighbor].get("element") == "O"
154
+ and graph.nodes[neighbor].get("hcount", 0) >= 1
155
+ and graph.edges[carbon, neighbor].get("order") == 1.0
156
+ ]
157
+ if len(hydroxyls) >= 2:
158
+ targets.append((carbon, hydroxyls[0], hydroxyls[1]))
159
+ return targets
160
+
161
+ @staticmethod
162
+ def fix_dict(data: Dict[str, str], reaction_column: str) -> Dict[str, str]:
163
+ """Standardize the reactant and product SMILES in a reaction
164
+ dictionary.
165
+
166
+ :param data: Dictionary containing a reaction SMILES under `reaction_column`.
167
+ :type data: Dict[str, str]
168
+ :param reaction_column: Key in `data` where the reaction SMILES is stored.
169
+ :type reaction_column: str
170
+ :return: The same dictionary with standardized reaction SMILES.
171
+ :rtype: Dict[str, str]
172
+ """
173
+ try:
174
+ react, prod = data[reaction_column].split(">>")
175
+ data[reaction_column] = (
176
+ f"{Tautomerize.fix_smiles(react)}>>{Tautomerize.fix_smiles(prod)}"
177
+ )
178
+ except ValueError:
179
+ data[reaction_column] = Tautomerize.fix_smiles(data[reaction_column])
180
+ return data
181
+
182
+ @staticmethod
183
+ def fix_dicts(
184
+ data: List[Dict[str, str]],
185
+ reaction_column: str,
186
+ n_jobs: int = 4,
187
+ verbose: int = 0,
188
+ ) -> List[Dict[str, str]]:
189
+ """Standardize multiple reaction dictionaries in parallel.
190
+
191
+ :param data: List of dictionaries containing reaction SMILES under `reaction_column`.
192
+ :type data: List[Dict[str, str]]
193
+ :param reaction_column: Key in each dictionary for the reaction SMILES.
194
+ :type reaction_column: str
195
+ :param n_jobs: Number of parallel jobs to run. Defaults to 4.
196
+ :type n_jobs: int
197
+ :param verbose: Verbosity level for the joblib Parallel call. Defaults to 0.
198
+ :type verbose: int
199
+ :return: List of dictionaries with standardized SMILES.
200
+ :rtype: List[Dict[str, str]]
201
+ """
202
+ results = Parallel(n_jobs=n_jobs, verbose=verbose)(
203
+ delayed(Tautomerize.fix_dict)(d, reaction_column) for d in data
204
+ )
205
+ return results
@@ -0,0 +1 @@
1
+ # from .Reaction import CanonRSMI, AAMValidator
synkit/Chem/utils.py ADDED
@@ -0,0 +1,315 @@
1
+ from rdkit import Chem
2
+ from rdkit.Chem.MolStandardize import rdMolStandardize
3
+ from rdkit.Chem import rdChemReactions
4
+ import re
5
+ from typing import List, Optional, Tuple, Union
6
+
7
+
8
+ def clean_radical_rsmi(rsmi: str) -> str:
9
+ """Load each side of a reaction SMILES (rSMI) into RDKit, split into disconnected fragments,
10
+ remove any fragment that contains an atom with nonzero radical electrons,
11
+ then reassemble back into a cleaned reaction SMILES.
12
+
13
+ :param rsmi: Reaction SMILES string, e.g.
14
+ 'A>>B.C'
15
+ :type rsmi: str
16
+ :return: Cleaned reaction SMILES with radical-containing fragments removed.
17
+ :rtype: str
18
+
19
+ .. rubric:: Examples
20
+
21
+ >>> clean_radical_rsmi(
22
+ ... 'COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(=O)Nc1cc(OC)cc(C(C)(C)C)c1O'
23
+ ... '>>COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(N)=O.COc1c[c]c(O)c(C(C)(C)C)c1'
24
+ ... )
25
+ 'COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(=O)Nc1cc(OC)cc(C(C)(C)C)c1O'
26
+ '>>COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(N)=O'
27
+ """
28
+ if ">>" not in rsmi:
29
+ return rsmi
30
+
31
+ def _clean_side(side: str) -> str:
32
+ mol = Chem.MolFromSmiles(side)
33
+ if mol is None:
34
+ return ""
35
+ frags = Chem.GetMolFrags(mol, asMols=True)
36
+ kept = []
37
+ for frag in frags:
38
+ if any(atom.GetNumRadicalElectrons() > 0 for atom in frag.GetAtoms()):
39
+ continue
40
+ kept.append(Chem.MolToSmiles(frag, isomericSmiles=True))
41
+ return ".".join(kept)
42
+
43
+ reac, prod = rsmi.split(">>", 1)
44
+ return f"{_clean_side(reac)}>>{_clean_side(prod)}"
45
+
46
+
47
+ def enumerate_tautomers(reaction_smiles: str) -> Optional[List[str]]:
48
+ """Enumerate possible tautomers of reactants while canonicalizing products.
49
+
50
+ :param reaction_smiles: Reaction SMILES in 'reactants>>products'
51
+ format.
52
+ :type reaction_smiles: str
53
+ :return: List of reaction SMILES for each reactant tautomer
54
+ (including the original), or None on error.
55
+ :rtype: Optional[List[str]]
56
+ :raises ValueError: If reactant or product SMILES are invalid.
57
+ """
58
+ try:
59
+ reactants_smiles, products_smiles = reaction_smiles.split(">>")
60
+ reactants_mol = Chem.MolFromSmiles(reactants_smiles)
61
+ products_mol = Chem.MolFromSmiles(products_smiles)
62
+ if reactants_mol is None or products_mol is None:
63
+ raise ValueError("Invalid reactant or product SMILES.")
64
+ enumerator = rdMolStandardize.TautomerEnumerator()
65
+ reactants_tautos = enumerator.Enumerate(reactants_mol) or [reactants_mol]
66
+ prod_can = Chem.MolToSmiles(products_mol, canonical=True)
67
+ rsmi_list = [Chem.MolToSmiles(m) + ">>" + prod_can for m in reactants_tautos]
68
+ rsmi_list.insert(0, reaction_smiles)
69
+ return rsmi_list
70
+ except ValueError:
71
+ raise
72
+ except Exception:
73
+ return None
74
+
75
+
76
+ def mapping_success_rate(list_mapping_data: List[str]) -> float:
77
+ """Calculate percentage of entries containing atom‑mapping annotations.
78
+
79
+ :param list_mapping_data: List of strings to search for mappings.
80
+ :type list_mapping_data: List[str]
81
+ :return: Percentage of entries containing `:<digits>` patterns,
82
+ rounded to two decimals.
83
+ :rtype: float
84
+ :raises ValueError: If input list is empty.
85
+ """
86
+ if not list_mapping_data:
87
+ raise ValueError("The input list is empty, cannot calculate success rate.")
88
+ pattern = re.compile(r":\d+")
89
+ success = sum(1 for entry in list_mapping_data if pattern.search(entry))
90
+ return round(100 * success / len(list_mapping_data), 2)
91
+
92
+
93
+ def count_carbons(smiles: str) -> int:
94
+ """Count the number of carbon atoms in a molecule.
95
+
96
+ :param smiles: SMILES string of the molecule.
97
+ :type smiles: str
98
+ :return: Number of carbon atoms, or raises ValueError if SMILES
99
+ invalid.
100
+ :rtype: int
101
+ :raises ValueError: If the SMILES string is invalid.
102
+ """
103
+ mol = Chem.MolFromSmiles(smiles)
104
+ if mol is None:
105
+ raise ValueError(f"Invalid SMILES string: {smiles}")
106
+ return sum(1 for atom in mol.GetAtoms() if atom.GetSymbol() == "C")
107
+
108
+
109
+ def get_max_fragment(smiles: Union[str, List[str]]) -> str:
110
+ """Return the largest fragment by atom count from SMILES.
111
+
112
+ :param smiles: SMILES string(s), possibly with '.' separators.
113
+ :type smiles: str or List[str]
114
+ :return: SMILES of the fragment with the most atoms, or empty
115
+ string if none valid.
116
+ :rtype: str
117
+ """
118
+ if isinstance(smiles, str):
119
+ fragments = smiles.split(".")
120
+ else:
121
+ fragments = [frag for s in smiles for frag in s.split(".")]
122
+ mols = [Chem.MolFromSmiles(f) for f in fragments if f]
123
+ mols = [m for m in mols if m]
124
+ if not mols:
125
+ return ""
126
+ max_mol = max(mols, key=lambda m: m.GetNumAtoms())
127
+ return Chem.MolToSmiles(max_mol)
128
+
129
+
130
+ def filter_smiles(smiles_list: List[str], target_smiles: str) -> List[str]:
131
+ """Filter SMILES list to those containing carbon and not equal to a target.
132
+
133
+ :param smiles_list: List of SMILES strings to filter.
134
+ :type smiles_list: List[str]
135
+ :param target_smiles: SMILES string to exclude.
136
+ :type target_smiles: str
137
+ :return: Filtered list containing SMILES with at least one carbon atom
138
+ and not matching `target_smiles`.
139
+ :rtype: List[str]
140
+ """
141
+ target_mol = Chem.MolFromSmiles(target_smiles)
142
+ target_can = Chem.MolToSmiles(target_mol) if target_mol else ""
143
+ result: List[str] = []
144
+ for smi in smiles_list:
145
+ mol = Chem.MolFromSmiles(smi)
146
+ if mol and any(atom.GetSymbol() == "C" for atom in mol.GetAtoms()):
147
+ can = Chem.MolToSmiles(mol)
148
+ if can != target_can:
149
+ result.append(smi)
150
+ return result
151
+
152
+
153
+ def remove_atom_mappings(mol: Chem.Mol) -> Chem.Mol:
154
+ """Strip atom‑mapping numbers from a molecule.
155
+
156
+ :param mol: RDKit Mol object.
157
+ :type mol: Chem.Mol
158
+ :return: The same Mol with all atom‑map numbers set to zero.
159
+ :rtype: Chem.Mol
160
+ """
161
+ for atom in mol.GetAtoms():
162
+ atom.SetAtomMapNum(0)
163
+ return mol
164
+
165
+
166
+ def get_sanitized_smiles(smiles_list: List[str]) -> List[str]:
167
+ """Sanitize SMILES list by removing mappings and invalid entries.
168
+
169
+ :param smiles_list: List of SMILES strings to sanitize.
170
+ :type smiles_list: List[str]
171
+ :return: List of sanitized, isomeric SMILES of the largest
172
+ fragments only.
173
+ :rtype: List[str]
174
+ """
175
+ sanitized: List[str] = []
176
+ for smiles in smiles_list:
177
+ if "->" in smiles:
178
+ continue
179
+ mol = Chem.MolFromSmiles(smiles)
180
+ if not mol:
181
+ continue
182
+ mol = remove_atom_mappings(mol)
183
+ try:
184
+ Chem.SanitizeMol(mol)
185
+ sanitized.append(Chem.MolToSmiles(mol, isomericSmiles=True))
186
+ except Exception:
187
+ continue
188
+ # keep only the largest fragment across all
189
+ if sanitized:
190
+ sanitized = [get_max_fragment(sanitized)]
191
+ return sanitized
192
+
193
+
194
+ def remove_duplicates(smiles_list: List[str]) -> List[str]:
195
+ """Remove duplicate strings from a list, preserving first occurrence.
196
+
197
+ :param smiles_list: List of strings (e.g., SMILES) possibly with
198
+ duplicates.
199
+ :type smiles_list: List[str]
200
+ :return: List with duplicates removed in original order.
201
+ :rtype: List[str]
202
+ """
203
+ seen = set()
204
+ unique: List[str] = []
205
+ for s in smiles_list:
206
+ if s not in seen:
207
+ unique.append(s)
208
+ seen.add(s)
209
+ return unique
210
+
211
+
212
+ def process_smiles_list(smiles_list: List[str]) -> List[str]:
213
+ """Split dot‑connected SMILES into individual components.
214
+
215
+ :param smiles_list: List of SMILES strings, some containing '.'
216
+ separators.
217
+ :type smiles_list: List[str]
218
+ :return: Flattened list of component SMILES strings.
219
+ :rtype: List[str]
220
+ """
221
+ new_list: List[str] = []
222
+ for smiles in smiles_list:
223
+ if "." in smiles:
224
+ new_list.extend(smiles.split("."))
225
+ else:
226
+ new_list.append(smiles)
227
+ return new_list
228
+
229
+
230
+ def remove_explicit_H_from_rsmi(rsmi: str) -> str:
231
+ """Remove explicit H atoms from a reaction SMILES, preserving AAM.
232
+
233
+ :param rsmi: Atom‑mapped reaction SMILES with explicit hydrogens.
234
+ :type rsmi: str
235
+ :return: Simplified reaction SMILES with implicit hydrogens.
236
+ :rtype: str
237
+ """
238
+ rxn = rdChemReactions.ReactionFromSmarts(rsmi, useSmiles=True)
239
+
240
+ def cleaned(mols):
241
+ return ".".join(
242
+ Chem.MolToSmiles(Chem.RemoveHs(m), isomericSmiles=True) for m in mols
243
+ )
244
+
245
+ react = cleaned(rxn.GetReactants())
246
+ prod = cleaned(rxn.GetProducts())
247
+ return f"{react}>>{prod}"
248
+
249
+
250
+ def remove_common_reagents(reaction_smiles: str) -> Tuple[Optional[str], Optional[str]]:
251
+ """Remove reagents present on both sides of a reaction SMILES.
252
+
253
+ :param reaction_smiles: Reaction SMILES 'reactants>>products'.
254
+ :type reaction_smiles: str
255
+ :return: Tuple(cleaned_reaction, list_of_removed_reagents or None
256
+ if none found).
257
+ :rtype: Tuple[str, Optional[List[str]]]
258
+ """
259
+ reactants, products = reaction_smiles.split(">>")
260
+ reactant_list = reactants.split(".")
261
+ product_list = products.split(".")
262
+ common_reagents = set(reactant_list) & set(product_list)
263
+
264
+ filtered_reactants = [r for r in reactant_list if r not in common_reagents]
265
+ filtered_products = [p for p in product_list if p not in common_reagents]
266
+ cleaned_reaction_smiles = (
267
+ ".".join(filtered_reactants) + ">>" + ".".join(filtered_products)
268
+ )
269
+
270
+ return cleaned_reaction_smiles
271
+
272
+
273
+ def reverse_reaction(rsmi: str) -> str:
274
+ """Reverse a reaction SMILES.
275
+
276
+ :param rsmi: Reaction SMILES 'reactants>>products'.
277
+ :type rsmi: str
278
+ :return: Reaction SMILES 'products>>reactants'.
279
+ :rtype: str
280
+ """
281
+ parts = rsmi.split(">>")
282
+ return f"{parts[1]}>>{parts[0]}" if len(parts) == 2 else rsmi
283
+
284
+
285
+ def merge_reaction(rsmi_1: str, rsmi_2: str) -> Optional[str]:
286
+ """Merge two reaction SMILES into a single combined reaction.
287
+
288
+ :param rsmi_1: First reaction SMILES.
289
+ :type rsmi_1: str
290
+ :param rsmi_2: Second reaction SMILES.
291
+ :type rsmi_2: str
292
+ :return: Merged reaction SMILES or None if inputs invalid.
293
+ :rtype: Optional[str]
294
+ """
295
+ try:
296
+ r1, p1 = rsmi_1.split(">>")
297
+ r2, p2 = rsmi_2.split(">>")
298
+ except ValueError:
299
+ return None
300
+ if not all([r1, p1, r2, p2]):
301
+ return None
302
+ return f"{r1}.{r2}>>{p1}.{p2}"
303
+
304
+
305
+ def find_longest_fragment(input_list: List[str]) -> Optional[str]:
306
+ """Find the longest string in a list.
307
+
308
+ :param input_list: List of strings to search.
309
+ :type input_list: List[str]
310
+ :return: Longest string or None if list empty.
311
+ :rtype: Optional[str]
312
+ """
313
+ if not input_list:
314
+ return None
315
+ return max(input_list, key=len)
File without changes
@@ -0,0 +1 @@
1
+ [{"R-id": "Mech-1", "reaction": "[CH3:1][CH:2]=[O:3].[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[O:3]([H:7])([H:8])", "mechanisms": [{"mech_name": "Aldol reaction (base cat)", "steps": [{"description": "Base abstracts H from substrate", "smart_string": "[CH:4]([H:7])([H:8])[CH:5]=[O:6].[*-:9]>>[CH-:4]([H:8])[CH:5]=[O:6].[*:9][H:7]", "step": 1, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Base abstracts H from substrate"}, {"description": "Nucleophilic addition fro substrate", "smart_string": "[CH3:1][CH:2]=[O:3].[CH-:4]([H:8])[CH:5]=[O:6]>>[CH3:1][CH:2]([O-:3])[CH:4]([H:8])[CH:5]=[O:6]", "step": 2, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"=\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n ]\n right [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n node [ id 2 label \"O-\" ]\n node [ id 3 label \"C\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]=[O:2].[CH3-:3]>>[CH2:1]([O-:2])[CH3:3]", "step_desc": "Nucleophilic addition fro substrate"}, {"description": "Neutralize substrate", "smart_string": "[CH3:1][CH:2]([O-:3])[CH:4]([H:8])[CH:5]=[O:6].[*:9][H:7]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[*-:9]", "step": 3, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Neutralize substrate"}, {"description": "Base abstracts H from substrate", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[*-:9]>>[CH3:1][CH:2]([O:3][H:7])[CH-:4][CH:5]=[O:6].[*:9][H:8]", "step": 4, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Base abstracts H from substrate"}, {"description": "Elimination Unimolecular Conjugate Base", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH-:4][CH:5]=[O:6]>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[O-:3][H:7]", "step": 5, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n ]\n right [\n edge [ source 1 target 3 label \"=\" ]\n node [ id 2 label \"O-\" ]\n node [ id 3 label \"C\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]([OH:2])[CH2-:3]>>[CH2:1]=[CH2:3].[OH-:2]", "step_desc": "Elimination Unimolecular Conjugate Base"}, {"description": "Neutralize Hydroxide", "smart_string": "[O-:3][H:7].[*:9][H:8]>>[O:3]([H:7])([H:8]).[*-:9]", "step": 6, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Neutralize Hydroxide"}], "cat": "[*-]"}, {"mech_name": "Aldol reaction (neutral cat)", "steps": [{"description": "Tautomerization of substrate", "smart_string": "[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH:4]([H:8])=[CH:5][O:6]([H:7])", "step": 1, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 2 target 3 label \"=\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"C\" ]\n node [ id 3 label \"O\" ]\n ]\n right [\n edge [ source 1 target 2 label \"=\" ]\n edge [ source 2 target 3 label \"-\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH3:1][CH:2]=[O:3]>>[CH2:1]=[CH:2][OH:3]", "step_desc": "Tautomerization of substrate"}, {"description": "Nucleophilic addition from substrate", "smart_string": "[CH3:1][CH:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:7])>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]", "step": 2, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"=\" ]\n edge [ source 3 target 4 label \"=\" ]\n edge [ source 4 target 5 label \"-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C\" ]\n node [ id 4 label \"C\" ]\n node [ id 5 label \"O\" ]\n ]\n right [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n edge [ source 3 target 4 label \"-\" ]\n edge [ source 4 target 5 label \"=\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]=[O:2].[CH2:3]=[CH:4][OH:5]>>[CH2:1]([OH:2])[CH2:3][CH:4]=[O:5]", "step_desc": "Nucleophilic addition from substrate"}, {"description": "Tautomerization of substrate", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:8])", "step": 3, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 2 target 3 label \"=\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"C\" ]\n node [ id 3 label \"O\" ]\n ]\n right [\n edge [ source 1 target 2 label \"=\" ]\n edge [ source 2 target 3 label \"-\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH3:1][CH:2]=[O:3]>>[CH2:1]=[CH:2][OH:3]", "step_desc": "Tautomerization of substrate"}, {"description": "Elimination", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:8])>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[O:3]([H:7])([H:8])", "step": 4, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n edge [ source 3 target 4 label \"=\" ]\n edge [ source 4 target 5 label \"-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C\" ]\n node [ id 4 label \"C\" ]\n node [ id 5 label \"O\" ]\n ]\n right [\n edge [ source 1 target 3 label \"=\" ]\n edge [ source 3 target 4 label \"-\" ]\n edge [ source 4 target 5 label \"=\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]([OH:2])[CH:3]=[CH:4][OH:5]>>[CH2:1]=[CH:3][CH:4]=[O:5].[OH2:2]", "step_desc": "Elimination"}], "cat": ""}, {"mech_name": "Aldol reaction (acid cat)", "steps": [{"description": "Tautomerization of substrate with acid cat", "smart_string": "[CH:4]([H:7])([H:8])[CH:5]=[O:6].[H+:9]>>[CH:4]([H:8])=[CH:5][O:6]([H:9]).[H+:7]", "step": 1}, {"description": "Nucleophilic addition from substrate", "smart_string": "[CH3:1][CH:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:9]).[H+:7]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[H+:9]", "step": 2}, {"description": "Tautomerization of substrate with acid cat", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[H+:9]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:9]).[H+:8]", "step": 3}, {"description": "Elimination", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:9]).[H+:8]>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[H+:9].[O:3]([H:7])([H:8])", "step": 4}], "cat": "[H+]"}]}]
@@ -0,0 +1,147 @@
1
+ from rdkit import Chem
2
+ from typing import Optional
3
+
4
+ from synkit.IO.graph_to_mol import GraphToMol
5
+ from synkit.Graph.ITS.its_construction import ITSConstruction
6
+ from synkit.Graph.ITS.its_decompose import get_rc
7
+ from synkit.IO.chem_converter import smiles_to_graph
8
+
9
+
10
+ def _get_partial_aam(smart: str) -> str:
11
+ """Generate a partial atom‐atom mapping (AAM) SMILES string from a reactant
12
+ SMARTS.
13
+
14
+ This function:
15
+ 1. Parses the forward (“reactant”) and backward (“product”) halves of `smart`.
16
+ 2. Builds RDKit graphs (with atom_map indices) for each side.
17
+ 3. Constructs the integrated transition state (ITS) graph.
18
+ 4. Identifies the reactant core (rc) subgraph.
19
+ 5. Zeroes out `atom_map` on any atom not in the reactant core.
20
+ 6. Converts the modified graphs to RDKit Mol objects (respecting H‐counts).
21
+ 7. Emits SMILES for the “retained” and “partial” graphs, joined with '>>'.
22
+
23
+ :param smart: A reaction SMARTS of the form "R>>P".
24
+ :type smart: str
25
+ :return: An unbalanced, partial‐AAM reaction SMILES "retained>>partial".
26
+ :rtype: str
27
+ :raises RuntimeError:
28
+ If graph decomposition, molecule conversion, or SMILES generation fails.
29
+ """
30
+ # split into reactant/product SMARTS
31
+ r_smi, p_smi = smart.split(">>")
32
+ # build graphs with atom_map indices from the SMILES strings
33
+ r_graph = smiles_to_graph(r_smi, use_index_as_atom_map=True)
34
+ p_graph = smiles_to_graph(p_smi, use_index_as_atom_map=True)
35
+
36
+ # construct and decompose ITS
37
+ its = ITSConstruction.ITSGraph(r_graph, p_graph)
38
+ rc = get_rc(its)
39
+ rc_nodes = set(rc.nodes())
40
+
41
+ # zero out maps for atoms not in the reactant core
42
+ for node in r_graph.nodes():
43
+ if node not in rc_nodes:
44
+ r_graph.nodes[node]["atom_map"] = 0
45
+ for node in p_graph.nodes():
46
+ if node not in rc_nodes:
47
+ p_graph.nodes[node]["atom_map"] = 0
48
+
49
+ converter = GraphToMol()
50
+ try:
51
+ retained_mol = converter.graph_to_mol(r_graph, use_h_count=True)
52
+ partial_mol = converter.graph_to_mol(p_graph, use_h_count=True)
53
+ except Exception as e:
54
+ raise RuntimeError(f"Error converting graphs to molecules: {e}") from e
55
+
56
+ try:
57
+ retained_smiles = Chem.MolToSmiles(retained_mol)
58
+ partial_smiles = Chem.MolToSmiles(partial_mol)
59
+ except Exception as e:
60
+ raise RuntimeError(f"Error generating SMILES: {e}") from e
61
+
62
+ return f"{retained_smiles}>>{partial_smiles}"
63
+
64
+
65
+ def _remove_small_smiles(smiles: str) -> str:
66
+ """Return the canonical SMILES of the largest fragment from an input
67
+ SMILES.
68
+
69
+ This function:
70
+ 1. Parses `smiles` to an RDKit Mol without sanitization.
71
+ 2. Sanitizes the Mol.
72
+ 3. Splits into fragments, picks the one with the most heavy atoms.
73
+ 4. Sanitizes that fragment and returns its canonical SMILES.
74
+
75
+ :param smiles: The input SMILES string.
76
+ :type smiles: str
77
+ :return: Canonical SMILES of the largest fragment.
78
+ :rtype: str
79
+ :raises ValueError:
80
+ - If `smiles` is invalid.
81
+ - If sanitization of the whole or fragment fails.
82
+ - If no fragments are found.
83
+ """
84
+ mol: Optional[Chem.Mol] = Chem.MolFromSmiles(smiles, sanitize=False)
85
+ if mol is None:
86
+ raise ValueError(f"Invalid SMILES string: '{smiles}'")
87
+
88
+ try:
89
+ Chem.SanitizeMol(mol)
90
+ except Exception as e:
91
+ raise ValueError(f"Sanitization failed for '{smiles}': {e}")
92
+
93
+ fragments = Chem.GetMolFrags(mol, asMols=True, sanitizeFrags=False)
94
+ if not fragments:
95
+ raise ValueError(f"No fragments found for SMILES '{smiles}'")
96
+
97
+ largest = max(fragments, key=lambda m: m.GetNumHeavyAtoms())
98
+
99
+ try:
100
+ Chem.SanitizeMol(largest)
101
+ except Exception as e:
102
+ raise ValueError(f"Sanitization failed for largest fragment: {e}")
103
+
104
+ return Chem.MolToSmiles(largest)
105
+
106
+
107
+ def _create_unbalanced_aam(rsmi: str, side: str = "right") -> str:
108
+ """Produce an unbalanced AAM reaction SMILES by keeping only the largest
109
+ fragment on the specified side(s) of the reaction.
110
+
111
+ :param rsmi: A reaction SMILES "reactant>>product".
112
+ :type rsmi: str
113
+ :param side:
114
+ Which side(s) to process:
115
+ - "left" : clean only the reactant side,
116
+ - "right": clean only the product side,
117
+ - "both" : clean both sides.
118
+ :type side: str
119
+ :return: A new reaction SMILES "reactant>>product" with small fragments removed.
120
+ :rtype: str
121
+ :raises ValueError:
122
+ - If `rsmi` doesn’t contain exactly one ">>".
123
+ - If `side` is not one of "left", "right", "both".
124
+ - If fragment processing fails on the chosen side(s).
125
+ """
126
+ parts = [p.strip() for p in rsmi.split(">>")]
127
+ if len(parts) != 2:
128
+ raise ValueError(f"Expected single '>>' in reaction SMILES: '{rsmi}'")
129
+
130
+ r_smi, p_smi = parts
131
+ side_l = side.lower()
132
+ if side_l not in ("left", "right", "both"):
133
+ raise ValueError(f"Invalid side '{side}'; must be 'left', 'right', or 'both'")
134
+
135
+ if side_l in ("left", "both"):
136
+ try:
137
+ r_smi = _remove_small_smiles(r_smi)
138
+ except Exception as e:
139
+ raise ValueError(f"Error processing left side: {e}")
140
+
141
+ if side_l in ("right", "both"):
142
+ try:
143
+ p_smi = _remove_small_smiles(p_smi)
144
+ except Exception as e:
145
+ raise ValueError(f"Error processing right side: {e}")
146
+
147
+ return f"{r_smi}>>{p_smi}"