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,1059 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, Optional, Pattern, Sequence
4
+ import re
5
+
6
+ import networkx as nx
7
+ from rdkit import Chem
8
+ from rdkit.Chem import rdChemReactions
9
+
10
+ from synkit.IO.debug import setup_logging
11
+ from synkit.IO.mol_to_graph import MolToGraph
12
+ from synkit.IO.graph_to_mol import GraphToMol
13
+ from synkit.IO.nx_to_gml import NXToGML
14
+ from synkit.IO.gml_to_nx import GMLToNX
15
+ from synkit.Graph.ITS.its_construction import ITSConstruction
16
+ from synkit.Graph.ITS.its_decompose import get_rc, its_decompose
17
+ from synkit.Graph.ITS.rc_extractor import RCExtractor
18
+ from synkit.Graph.ITS.its_reverter import ITSReverter
19
+
20
+ _BRACKET_DIGIT_PATTERN: Pattern[str] = re.compile(r"\[([^\]]*?)\](\d+)")
21
+ _BRACKET_MAP_PATTERN: Pattern[str] = re.compile(r"\[([^\]]+):(\d+)\]")
22
+
23
+ ITSFormat = Literal["typesGH", "tuple"]
24
+
25
+ DEFAULT_NODE_ATTRS = (
26
+ "element",
27
+ "aromatic",
28
+ "hcount",
29
+ "charge",
30
+ "radical",
31
+ "lone_pairs",
32
+ "valence_electrons",
33
+ "neighbors",
34
+ "atom_map",
35
+ )
36
+
37
+ DEFAULT_EDGE_ATTRS = (
38
+ "order",
39
+ "kekule_order",
40
+ "sigma_order",
41
+ "pi_order",
42
+ "aromatic",
43
+ )
44
+
45
+ logger = setup_logging()
46
+
47
+
48
+ def _resolve_graph_attrs(
49
+ node_attrs: Optional[Sequence[str]] = None,
50
+ edge_attrs: Optional[Sequence[str]] = None,
51
+ ) -> tuple[list[str], list[str]]:
52
+ """
53
+ Resolve node and edge attribute lists.
54
+
55
+ :param node_attrs: Optional node attribute names.
56
+ :type node_attrs: Optional[Sequence[str]]
57
+ :param edge_attrs: Optional edge attribute names.
58
+ :type edge_attrs: Optional[Sequence[str]]
59
+ :return: Normalized node and edge attribute lists.
60
+ :rtype: tuple[list[str], list[str]]
61
+ """
62
+ resolved_node_attrs = list(node_attrs or DEFAULT_NODE_ATTRS)
63
+ resolved_edge_attrs = list(edge_attrs or DEFAULT_EDGE_ATTRS)
64
+ return resolved_node_attrs, resolved_edge_attrs
65
+
66
+
67
+ def _validate_its_format(format: str) -> ITSFormat:
68
+ """
69
+ Validate ITS serialization format.
70
+
71
+ :param format: Requested ITS format.
72
+ :type format: str
73
+ :return: Validated ITS format.
74
+ :rtype: ITSFormat
75
+ :raises ValueError: If the format is unsupported.
76
+ """
77
+ valid_formats = ("typesGH", "tuple")
78
+ if format not in valid_formats:
79
+ raise ValueError(
80
+ f"Unsupported format: {format!r}. " f"Expected one of {valid_formats}."
81
+ )
82
+ return format
83
+
84
+
85
+ def detect_its_format(graph: nx.Graph) -> ITSFormat:
86
+ """
87
+ Detect the ITS storage representation used by a graph.
88
+
89
+ Legacy ITS graphs keep scalar node attributes and store side-specific
90
+ values only in ``typesGH``. Tuple ITS graphs store direct paired node and
91
+ edge attributes such as ``element=("C", "C")`` or
92
+ ``sigma_order=(1.0, 1.0)``.
93
+
94
+ :param graph: ITS-like graph to inspect.
95
+ :type graph: nx.Graph
96
+ :return: Detected ITS format.
97
+ :rtype: ITSFormat
98
+ """
99
+ tuple_node_keys = (
100
+ "element",
101
+ "aromatic",
102
+ "hcount",
103
+ "charge",
104
+ "radical",
105
+ "lone_pairs",
106
+ "valence_electrons",
107
+ )
108
+ tuple_edge_keys = ("kekule_order", "sigma_order", "pi_order")
109
+
110
+ for _, attrs in graph.nodes(data=True):
111
+ if any(_is_pair(attrs.get(key)) for key in tuple_node_keys):
112
+ return "tuple"
113
+
114
+ for _, _, attrs in graph.edges(data=True):
115
+ if any(_is_pair(attrs.get(key)) for key in tuple_edge_keys):
116
+ return "tuple"
117
+
118
+ return "typesGH"
119
+
120
+
121
+ def _split_rsmi(rsmi: str) -> tuple[str, str]:
122
+ """
123
+ Split a reaction SMILES string into reactant and product parts.
124
+
125
+ :param rsmi: Reaction SMILES string in ``reactants>>products`` format.
126
+ :type rsmi: str
127
+ :return: Tuple of reactant SMILES and product SMILES.
128
+ :rtype: tuple[str, str]
129
+ :raises ValueError: If the string is not a valid reaction SMILES.
130
+ """
131
+ parts = rsmi.split(">>")
132
+ if len(parts) != 2:
133
+ raise ValueError(f"Invalid RSMI format: {rsmi}")
134
+ return parts[0], parts[1]
135
+
136
+
137
+ def _require_graph(graph: Optional[nx.Graph], label: str) -> nx.Graph:
138
+ """
139
+ Require a graph object to be present.
140
+
141
+ :param graph: Graph to validate.
142
+ :type graph: Optional[nx.Graph]
143
+ :param label: Human-readable graph label.
144
+ :type label: str
145
+ :return: Validated graph.
146
+ :rtype: nx.Graph
147
+ :raises ValueError: If the graph is ``None``.
148
+ """
149
+ if graph is None:
150
+ raise ValueError(f"Failed to build {label} graph.")
151
+ return graph
152
+
153
+
154
+ def _is_pair(value: object) -> bool:
155
+ """
156
+ Check whether a value is a 2-item pair.
157
+
158
+ :param value: Value to inspect.
159
+ :type value: object
160
+ :return: ``True`` if the value is a tuple or list of length 2.
161
+ :rtype: bool
162
+ """
163
+ return isinstance(value, (tuple, list)) and len(value) == 2
164
+
165
+
166
+ def _extract_scalar(value: object) -> object:
167
+ """
168
+ Extract a representative scalar value.
169
+
170
+ If the input is a paired value, the first entry is returned.
171
+
172
+ :param value: Input value.
173
+ :type value: object
174
+ :return: Scalar-like representative value.
175
+ :rtype: object
176
+ """
177
+ if _is_pair(value):
178
+ return value[0]
179
+ return value
180
+
181
+
182
+ def _local_charge_model_consistent(graph: nx.Graph, node: object) -> bool:
183
+ """Test the local ``VE - NBE - B`` formal-charge identity."""
184
+ if node not in graph:
185
+ return False
186
+ attrs = graph.nodes[node]
187
+ required = ("valence_electrons", "lone_pairs", "radical", "hcount", "charge")
188
+ if any(attrs.get(name) is None for name in required):
189
+ return False
190
+
191
+ bond_sum = 0.0
192
+ for _, _, edge_attrs in graph.edges(node, data=True):
193
+ sigma = edge_attrs.get("sigma_order")
194
+ pi = edge_attrs.get("pi_order")
195
+ if sigma is not None and pi is not None:
196
+ bond_sum += float(sigma) + float(pi)
197
+ continue
198
+ order = edge_attrs.get("kekule_order", edge_attrs.get("order"))
199
+ if order is None:
200
+ return False
201
+ bond_sum += float(order)
202
+
203
+ modeled_charge = (
204
+ float(attrs["valence_electrons"])
205
+ - 2.0 * float(attrs["lone_pairs"])
206
+ - float(attrs["radical"])
207
+ - float(attrs["hcount"])
208
+ - bond_sum
209
+ )
210
+ return modeled_charge == float(attrs["charge"])
211
+
212
+
213
+ def _extract_atom_map(attrs: dict) -> Optional[int]:
214
+ """
215
+ Extract atom-map value from a node attribute dictionary.
216
+
217
+ :param attrs: Node attribute dictionary.
218
+ :type attrs: dict
219
+ :return: Atom-map number if available.
220
+ :rtype: Optional[int]
221
+ """
222
+ value = _extract_scalar(attrs.get("atom_map"))
223
+ if value is None:
224
+ return None
225
+ try:
226
+ return int(value)
227
+ except (TypeError, ValueError):
228
+ return None
229
+
230
+
231
+ def _is_hydrogen_node(attrs: dict) -> bool:
232
+ """
233
+ Determine whether a node represents hydrogen.
234
+
235
+ Supports both scalar and paired ``element`` attributes.
236
+
237
+ :param attrs: Node attribute dictionary.
238
+ :type attrs: dict
239
+ :return: ``True`` if the node corresponds to hydrogen.
240
+ :rtype: bool
241
+ """
242
+ value = attrs.get("element")
243
+ if _is_pair(value):
244
+ return any(item == "H" for item in value)
245
+ return value == "H"
246
+
247
+
248
+ def _build_its_for_rsmi(
249
+ reactant_graph: nx.Graph,
250
+ product_graph: nx.Graph,
251
+ format: ITSFormat,
252
+ ) -> nx.Graph:
253
+ """
254
+ Build an ITS graph for downstream RSMI generation.
255
+
256
+ :param reactant_graph: Reactant graph.
257
+ :type reactant_graph: nx.Graph
258
+ :param product_graph: Product graph.
259
+ :type product_graph: nx.Graph
260
+ :param format: ITS format.
261
+ :type format: ITSFormat
262
+ :return: Constructed ITS graph.
263
+ :rtype: nx.Graph
264
+ """
265
+ if format == "typesGH":
266
+ return ITSConstruction().ITSGraph(reactant_graph, product_graph)
267
+
268
+ return ITSConstruction().construct(
269
+ reactant_graph,
270
+ product_graph,
271
+ node_attrs=list(DEFAULT_NODE_ATTRS),
272
+ edge_attrs=list(DEFAULT_EDGE_ATTRS),
273
+ )
274
+
275
+
276
+ def _extract_rc_graph(its: nx.Graph, format: ITSFormat) -> nx.Graph:
277
+ """
278
+ Extract the reaction-center graph from an ITS graph.
279
+
280
+ :param its: ITS graph.
281
+ :type its: nx.Graph
282
+ :param format: ITS format.
283
+ :type format: ITSFormat
284
+ :return: Reaction-center graph.
285
+ :rtype: nx.Graph
286
+ """
287
+ if format == "typesGH":
288
+ return get_rc(its)
289
+
290
+ return RCExtractor(preserve_full_attrs=True).extract(its)
291
+
292
+
293
+ def _get_preserved_hydrogen_maps(
294
+ its: nx.Graph,
295
+ format: ITSFormat,
296
+ ) -> list[int]:
297
+ """
298
+ Collect atom maps of hydrogens that cannot be implicitized.
299
+
300
+ Reaction-centre hydrogens retain explicit identity. Hydrogen-only
301
+ components also remain explicit because there is no heavy-atom neighbour
302
+ onto which their multiplicity can be projected.
303
+
304
+ :param its: ITS graph.
305
+ :type its: nx.Graph
306
+ :param format: ITS format.
307
+ :type format: ITSFormat
308
+ :return: Sorted unique hydrogen atom-map numbers.
309
+ :rtype: list[int]
310
+ """
311
+ rc_graph = _extract_rc_graph(its, format)
312
+ atom_maps: set[int] = set()
313
+
314
+ for _, attrs in rc_graph.nodes(data=True):
315
+ if not _is_hydrogen_node(attrs):
316
+ continue
317
+ atom_map = _extract_atom_map(attrs)
318
+ if atom_map is not None:
319
+ atom_maps.add(atom_map)
320
+
321
+ for node, attrs in its.nodes(data=True):
322
+ if not _is_hydrogen_node(attrs) or any(
323
+ not _is_hydrogen_node(its.nodes[neighbor])
324
+ for neighbor in its.neighbors(node)
325
+ ):
326
+ continue
327
+ atom_map = _extract_atom_map(attrs)
328
+ if atom_map is not None:
329
+ atom_maps.add(atom_map)
330
+
331
+ return sorted(atom_maps)
332
+
333
+
334
+ def _decompose_its(
335
+ its: nx.Graph,
336
+ format: ITSFormat,
337
+ ) -> tuple[nx.Graph, nx.Graph]:
338
+ """
339
+ Convert an ITS graph back to reactant and product graphs.
340
+
341
+ :param its: ITS graph.
342
+ :type its: nx.Graph
343
+ :param format: ITS format.
344
+ :type format: ITSFormat
345
+ :return: Reactant and product graphs.
346
+ :rtype: tuple[nx.Graph, nx.Graph]
347
+ """
348
+ if format == "typesGH":
349
+ return its_decompose(its)
350
+
351
+ reverter = ITSReverter(its)
352
+ return reverter.to_reactant_graph(), reverter.to_product_graph()
353
+
354
+
355
+ def smiles_to_graph(
356
+ smiles: str,
357
+ drop_non_aam: bool = False,
358
+ sanitize: bool = True,
359
+ use_index_as_atom_map: bool = False,
360
+ node_attrs: Optional[Sequence[str]] = None,
361
+ edge_attrs: Optional[Sequence[str]] = None,
362
+ include_stereo_descriptors: bool = True,
363
+ ) -> Optional[nx.Graph]:
364
+ """
365
+ Convert a SMILES string to a molecular graph.
366
+
367
+ :param smiles: SMILES representation of the molecule.
368
+ :type smiles: str
369
+ :param drop_non_aam: If ``True``, drop atoms without atom-map labels.
370
+ :type drop_non_aam: bool
371
+ :param sanitize: If ``True``, sanitize the RDKit molecule.
372
+ :type sanitize: bool
373
+ :param use_index_as_atom_map: If ``True``, overwrite atom-map labels
374
+ using atom indices.
375
+ :type use_index_as_atom_map: bool
376
+ :param node_attrs: Node attributes to export into the graph.
377
+ :type node_attrs: Optional[Sequence[str]]
378
+ :param edge_attrs: Edge attributes to export into the graph.
379
+ :type edge_attrs: Optional[Sequence[str]]
380
+ :param include_stereo_descriptors: Whether to construct the graph-level
381
+ relative-stereochemistry registry.
382
+ :type include_stereo_descriptors: bool
383
+ :return: Molecular graph or ``None`` on failure.
384
+ :rtype: Optional[nx.Graph]
385
+ """
386
+ resolved_node_attrs, resolved_edge_attrs = _resolve_graph_attrs(
387
+ node_attrs=node_attrs,
388
+ edge_attrs=edge_attrs,
389
+ )
390
+
391
+ try:
392
+ mol = Chem.MolFromSmiles(smiles, sanitize=False)
393
+ if mol is None:
394
+ logger.warning("Failed to parse SMILES: %s", smiles)
395
+ return None
396
+
397
+ if sanitize:
398
+ try:
399
+ Chem.SanitizeMol(mol)
400
+ except Exception as exc:
401
+ logger.error(
402
+ "Sanitization failed for SMILES %s: %s",
403
+ smiles,
404
+ exc,
405
+ )
406
+ return None
407
+
408
+ graph = MolToGraph(
409
+ node_attrs=resolved_node_attrs,
410
+ edge_attrs=resolved_edge_attrs,
411
+ include_stereo_descriptors=include_stereo_descriptors,
412
+ ).transform(
413
+ mol,
414
+ drop_non_aam=drop_non_aam,
415
+ use_index_as_atom_map=use_index_as_atom_map,
416
+ )
417
+ if graph is None:
418
+ logger.warning(
419
+ "Failed to convert molecule to graph for SMILES: %s",
420
+ smiles,
421
+ )
422
+ return graph
423
+ except Exception as exc:
424
+ logger.error(
425
+ "Unhandled exception while converting SMILES to graph: %s; %s",
426
+ smiles,
427
+ exc,
428
+ )
429
+ return None
430
+
431
+
432
+ def rsmi_to_graph(
433
+ rsmi: str,
434
+ drop_non_aam: bool = True,
435
+ sanitize: bool = True,
436
+ use_index_as_atom_map: bool = True,
437
+ node_attrs: Optional[Sequence[str]] = None,
438
+ edge_attrs: Optional[Sequence[str]] = None,
439
+ include_stereo_descriptors: bool = True,
440
+ ) -> tuple[Optional[nx.Graph], Optional[nx.Graph]]:
441
+ """
442
+ Convert a reaction SMILES into reactant and product graphs.
443
+
444
+ :param rsmi: Reaction SMILES string in ``reactants>>products`` format.
445
+ :type rsmi: str
446
+ :param drop_non_aam: If ``True``, drop atoms lacking atom maps.
447
+ :type drop_non_aam: bool
448
+ :param sanitize: If ``True``, sanitize molecules during conversion.
449
+ :type sanitize: bool
450
+ :param use_index_as_atom_map: If ``True``, overwrite atom-map labels
451
+ using atom indices.
452
+ :type use_index_as_atom_map: bool
453
+ :param node_attrs: Node attributes to export into the graphs.
454
+ :type node_attrs: Optional[Sequence[str]]
455
+ :param edge_attrs: Edge attributes to export into the graphs.
456
+ :type edge_attrs: Optional[Sequence[str]]
457
+ :param include_stereo_descriptors: Whether to construct graph-level
458
+ relative-stereochemistry registries.
459
+ :type include_stereo_descriptors: bool
460
+ :return: Tuple of reactant and product graphs.
461
+ :rtype: tuple[Optional[nx.Graph], Optional[nx.Graph]]
462
+ """
463
+ resolved_node_attrs, resolved_edge_attrs = _resolve_graph_attrs(
464
+ node_attrs=node_attrs,
465
+ edge_attrs=edge_attrs,
466
+ )
467
+
468
+ try:
469
+ reactants_smiles, products_smiles = _split_rsmi(rsmi)
470
+ except ValueError:
471
+ logger.error("Invalid RSMI format: %s", rsmi)
472
+ return None, None
473
+
474
+ reactant_graph = smiles_to_graph(
475
+ smiles=reactants_smiles,
476
+ drop_non_aam=drop_non_aam,
477
+ sanitize=sanitize,
478
+ use_index_as_atom_map=use_index_as_atom_map,
479
+ node_attrs=resolved_node_attrs,
480
+ edge_attrs=resolved_edge_attrs,
481
+ include_stereo_descriptors=include_stereo_descriptors,
482
+ )
483
+ product_graph = smiles_to_graph(
484
+ smiles=products_smiles,
485
+ drop_non_aam=drop_non_aam,
486
+ sanitize=sanitize,
487
+ use_index_as_atom_map=use_index_as_atom_map,
488
+ node_attrs=resolved_node_attrs,
489
+ edge_attrs=resolved_edge_attrs,
490
+ include_stereo_descriptors=include_stereo_descriptors,
491
+ )
492
+ return reactant_graph, product_graph
493
+
494
+
495
+ def graph_to_smi(
496
+ graph: nx.Graph,
497
+ sanitize: bool = True,
498
+ preserve_atom_maps: Optional[Sequence[int]] = None,
499
+ prefer_kekule_order: bool = True,
500
+ ) -> Optional[str]:
501
+ """
502
+ Convert a molecular graph to a SMILES string.
503
+
504
+ :param graph: Molecular graph.
505
+ :type graph: nx.Graph
506
+ :param sanitize: If ``True``, sanitize the generated molecule.
507
+ :type sanitize: bool
508
+ :param preserve_atom_maps: Atom-map numbers whose hydrogens should
509
+ remain explicit.
510
+ :type preserve_atom_maps: Optional[Sequence[int]]
511
+ :param prefer_kekule_order: Whether retained Kekule/sigma-pi edge fields
512
+ should take precedence over aromatic presentation order.
513
+ :type prefer_kekule_order: bool
514
+ :return: SMILES string or ``None`` on failure.
515
+ :rtype: Optional[str]
516
+ """
517
+ try:
518
+ preserved = list(preserve_atom_maps or [])
519
+ if not preserved:
520
+ mol = GraphToMol().graph_to_mol(
521
+ graph,
522
+ sanitize=sanitize,
523
+ use_h_count=True,
524
+ prefer_kekule_order=prefer_kekule_order,
525
+ )
526
+ else:
527
+ from synkit.Graph.Hyrogen._misc import implicit_hydrogen
528
+
529
+ graph_imp = implicit_hydrogen(graph, set(preserved))
530
+ mol = GraphToMol().graph_to_mol(
531
+ graph_imp,
532
+ sanitize=sanitize,
533
+ use_h_count=True,
534
+ prefer_kekule_order=prefer_kekule_order,
535
+ )
536
+
537
+ if mol is None:
538
+ return None
539
+ return Chem.MolToSmiles(mol)
540
+ except Exception as exc:
541
+ logger.debug("Error generating SMILES: %s", exc)
542
+ return None
543
+
544
+
545
+ def graph_to_rsmi(
546
+ r: nx.Graph,
547
+ p: nx.Graph,
548
+ its: Optional[nx.Graph] = None,
549
+ sanitize: bool = True,
550
+ explicit_hydrogen: bool = False,
551
+ preserve_hydrogen_maps: Optional[Sequence[int]] = None,
552
+ ) -> Optional[str]:
553
+ """Convert reactant and product graphs into a reaction SMILES string.
554
+
555
+ :param r: Graph representing the reactants.
556
+ :type r: networkx.Graph
557
+ :param p: Graph representing the products.
558
+ :type p: networkx.Graph
559
+ :param its: Imaginary transition state graph. If None, it will be
560
+ constructed.
561
+ :type its: networkx.Graph or None
562
+ :param sanitize: Whether to sanitize molecules during conversion.
563
+ :type sanitize: bool
564
+ :param explicit_hydrogen: Whether to preserve explicit hydrogens in
565
+ the SMILES.
566
+ :type explicit_hydrogen: bool
567
+ :param preserve_hydrogen_maps: Precomputed atom maps of reaction-centre
568
+ hydrogens that must remain explicit. When omitted, derive them from
569
+ ``its`` for backward compatibility.
570
+ :type preserve_hydrogen_maps: Optional[Sequence[int]]
571
+ :return: Reaction SMILES string in 'reactants>>products' format or
572
+ None on failure.
573
+ :rtype: str or None
574
+ """
575
+ try:
576
+ if explicit_hydrogen:
577
+ r_smiles = graph_to_smi(r, sanitize=sanitize)
578
+ p_smiles = graph_to_smi(p, sanitize=sanitize)
579
+ else:
580
+ if preserve_hydrogen_maps is None:
581
+ if its is None:
582
+ its = ITSConstruction().ITSGraph(r, p)
583
+ rc = get_rc(its)
584
+ list_hydrogen = [
585
+ d["atom_map"]
586
+ for _, d in rc.nodes(data=True)
587
+ if d.get("element") == "H"
588
+ ]
589
+ else:
590
+ list_hydrogen = list(preserve_hydrogen_maps)
591
+ r_smiles = graph_to_smi(
592
+ r,
593
+ sanitize=sanitize,
594
+ preserve_atom_maps=list_hydrogen,
595
+ )
596
+ p_smiles = graph_to_smi(
597
+ p,
598
+ sanitize=sanitize,
599
+ preserve_atom_maps=list_hydrogen,
600
+ )
601
+
602
+ if r_smiles is None or p_smiles is None:
603
+ return None
604
+
605
+ return f"{r_smiles}>>{p_smiles}"
606
+ except Exception as e:
607
+ logger.debug(f"Error in generating reaction SMILES: {str(e)}")
608
+ return None
609
+
610
+
611
+ def rsmi_to_its(
612
+ rsmi: str,
613
+ drop_non_aam: bool = True,
614
+ sanitize: bool = True,
615
+ use_index_as_atom_map: bool = True,
616
+ core: bool = False,
617
+ node_attrs: Optional[Sequence[str]] = None,
618
+ edge_attrs: Optional[Sequence[str]] = None,
619
+ explicit_hydrogen: bool = False,
620
+ format: ITSFormat = "typesGH",
621
+ include_context_edges: bool = True,
622
+ ) -> nx.Graph:
623
+ """
624
+ Convert a reaction SMILES into an ITS graph.
625
+
626
+ Supported formats:
627
+
628
+ - ``"typesGH"``: legacy ITS representation
629
+ - ``"tuple"``: paired-attribute ITS representation
630
+
631
+ :param rsmi: Reaction SMILES string.
632
+ :type rsmi: str
633
+ :param drop_non_aam: If ``True``, discard fragments lacking atom maps.
634
+ :type drop_non_aam: bool
635
+ :param sanitize: If ``True``, sanitize molecules during conversion.
636
+ :type sanitize: bool
637
+ :param use_index_as_atom_map: If ``True``, overwrite atom maps using
638
+ atom indices.
639
+ :type use_index_as_atom_map: bool
640
+ :param core: If ``True``, return only the reaction-center graph.
641
+ :type core: bool
642
+ :param node_attrs: Node attributes to include in graph construction.
643
+ :type node_attrs: Optional[Sequence[str]]
644
+ :param edge_attrs: Edge attributes to include in graph construction.
645
+ :type edge_attrs: Optional[Sequence[str]]
646
+ :param explicit_hydrogen: If ``True``, convert implicit hydrogens to
647
+ explicit nodes for the selected ITS format.
648
+ :type explicit_hydrogen: bool
649
+ :param format: ITS format.
650
+ :type format: ITSFormat
651
+ :param include_context_edges: For tuple-format reaction centers, include
652
+ unchanged edges between reaction-center nodes when ``True``. Set to
653
+ ``False`` for a minimal changed-edge reaction center.
654
+ :type include_context_edges: bool
655
+ :return: ITS graph or RC graph.
656
+ :rtype: nx.Graph
657
+ :raises ValueError: If graph construction fails.
658
+ """
659
+ validated_format = _validate_its_format(format)
660
+ resolved_node_attrs, resolved_edge_attrs = _resolve_graph_attrs(
661
+ node_attrs=node_attrs,
662
+ edge_attrs=edge_attrs,
663
+ )
664
+
665
+ reactant_graph, product_graph = rsmi_to_graph(
666
+ rsmi=rsmi,
667
+ drop_non_aam=drop_non_aam,
668
+ sanitize=sanitize,
669
+ use_index_as_atom_map=use_index_as_atom_map,
670
+ node_attrs=resolved_node_attrs,
671
+ edge_attrs=resolved_edge_attrs,
672
+ )
673
+
674
+ reactant_graph = _require_graph(reactant_graph, "reactant")
675
+ product_graph = _require_graph(product_graph, "product")
676
+
677
+ if validated_format == "typesGH":
678
+ its_graph = ITSConstruction().ITSGraph(reactant_graph, product_graph)
679
+ if explicit_hydrogen:
680
+ from synkit.Graph.Hyrogen._misc import h_to_explicit
681
+
682
+ its_graph = h_to_explicit(its_graph, None, True)
683
+ if core:
684
+ its_graph = get_rc(its_graph)
685
+ return its_graph
686
+
687
+ its_graph = ITSConstruction().construct(
688
+ reactant_graph,
689
+ product_graph,
690
+ node_attrs=resolved_node_attrs,
691
+ edge_attrs=resolved_edge_attrs,
692
+ )
693
+ for node in its_graph:
694
+ its_graph.nodes[node]["charge_model_consistent"] = (
695
+ _local_charge_model_consistent(reactant_graph, node),
696
+ _local_charge_model_consistent(product_graph, node),
697
+ )
698
+ if explicit_hydrogen:
699
+ from synkit.Graph.Hyrogen._misc import h_to_explicit
700
+
701
+ its_graph = h_to_explicit(its_graph, None, True)
702
+ if core:
703
+ its_graph = RCExtractor(
704
+ node_attrs=resolved_node_attrs,
705
+ edge_attrs=resolved_edge_attrs,
706
+ preserve_full_attrs=False,
707
+ ).extract(
708
+ its_graph,
709
+ include_context_edges=include_context_edges,
710
+ )
711
+ return its_graph
712
+
713
+
714
+ def its_to_rsmi(
715
+ its: nx.Graph,
716
+ sanitize: bool = True,
717
+ explicit_hydrogen: bool = False,
718
+ clean_wildcards: bool = False,
719
+ format: ITSFormat = "typesGH",
720
+ ) -> str:
721
+ """
722
+ Convert an ITS graph into a reaction SMILES (rSMI) string.
723
+
724
+ This function decomposes or reverts the ITS graph into reactant and
725
+ product graphs depending on the selected ITS format, then serializes
726
+ them into a reaction SMILES string.
727
+
728
+ :param its: ITS graph to convert back into reaction SMILES.
729
+ :type its: nx.Graph
730
+ :param sanitize: If ``True``, sanitize graphs before SMILES generation.
731
+ :type sanitize: bool
732
+ :param explicit_hydrogen: If ``True``, include explicit hydrogens in
733
+ the generated SMILES.
734
+ :type explicit_hydrogen: bool
735
+ :param clean_wildcards: If ``True``, clean wildcard radicals in the
736
+ generated reaction SMILES.
737
+ :type clean_wildcards: bool
738
+ :param format: ITS format. Supported values are ``"typesGH"`` and
739
+ ``"tuple"``.
740
+ :type format: ITSFormat
741
+ :return: Reaction SMILES string.
742
+ :rtype: str
743
+ :raises ValueError: If the ITS format is unsupported.
744
+ """
745
+ validated_format = _validate_its_format(format)
746
+ reactant_graph, product_graph = _decompose_its(its, validated_format)
747
+
748
+ if validated_format == "tuple":
749
+ preserved_hydrogens = (
750
+ []
751
+ if explicit_hydrogen
752
+ else _get_preserved_hydrogen_maps(its, validated_format)
753
+ )
754
+ reactant_smiles = graph_to_smi(
755
+ reactant_graph,
756
+ sanitize=sanitize,
757
+ preserve_atom_maps=preserved_hydrogens,
758
+ )
759
+ try:
760
+ if explicit_hydrogen:
761
+ product = product_graph
762
+ else:
763
+ from synkit.Graph.Hyrogen._misc import implicit_hydrogen
764
+
765
+ product = implicit_hydrogen(
766
+ product_graph,
767
+ set(preserved_hydrogens),
768
+ )
769
+ product_smiles = graph_to_smi(
770
+ product,
771
+ sanitize=sanitize,
772
+ preserve_atom_maps=preserved_hydrogens,
773
+ )
774
+ except Exception as exc:
775
+ logger.debug("Error generating tuple product SMILES: %s", exc)
776
+ product_smiles = None
777
+ rsmi = (
778
+ f"{reactant_smiles}>>{product_smiles}"
779
+ if reactant_smiles is not None and product_smiles is not None
780
+ else None
781
+ )
782
+ else:
783
+ rsmi = graph_to_rsmi(
784
+ reactant_graph,
785
+ product_graph,
786
+ its,
787
+ sanitize,
788
+ explicit_hydrogen,
789
+ )
790
+
791
+ if rsmi is None:
792
+ raise ValueError("Failed to convert ITS graph to reaction SMILES.")
793
+
794
+ if clean_wildcards:
795
+ from synkit.Chem.Reaction.radical_wildcard import clean_wc
796
+
797
+ rsmi = clean_wc(rsmi)
798
+
799
+ return rsmi
800
+
801
+
802
+ def smart_to_gml(
803
+ smart: str,
804
+ core: bool = True,
805
+ sanitize: bool = True,
806
+ rule_name: str = "rule",
807
+ reindex: bool = False,
808
+ explicit_hydrogen: bool = False,
809
+ useSmiles: bool = True,
810
+ ) -> str:
811
+ """
812
+ Convert a reaction SMARTS or SMILES string into GML.
813
+
814
+ This function uses the legacy ITS/GML pipeline.
815
+
816
+ :param smart: Reaction SMARTS or SMILES string.
817
+ :type smart: str
818
+ :param core: If ``True``, export only the reaction core.
819
+ :type core: bool
820
+ :param sanitize: If ``True``, sanitize molecules during conversion.
821
+ :type sanitize: bool
822
+ :param rule_name: Rule name stored in the GML output.
823
+ :type rule_name: str
824
+ :param reindex: If ``True``, reindex graph nodes before export.
825
+ :type reindex: bool
826
+ :param explicit_hydrogen: If ``True``, include explicit hydrogens.
827
+ :type explicit_hydrogen: bool
828
+ :param useSmiles: If ``True``, treat input as reaction SMILES.
829
+ Otherwise, treat it as reaction SMARTS.
830
+ :type useSmiles: bool
831
+ :return: GML representation of the reaction rule.
832
+ :rtype: str
833
+ :raises ValueError: If graph construction fails.
834
+ """
835
+ rsmi = smart if useSmiles else rsmarts_to_rsmi(smart)
836
+
837
+ reactant_graph, product_graph = rsmi_to_graph(
838
+ rsmi=rsmi,
839
+ sanitize=sanitize,
840
+ )
841
+ reactant_graph = _require_graph(reactant_graph, "reactant")
842
+ product_graph = _require_graph(product_graph, "product")
843
+
844
+ its_graph = ITSConstruction().ITSGraph(reactant_graph, product_graph)
845
+ if core:
846
+ its_graph = get_rc(its_graph)
847
+ reactant_graph, product_graph = its_decompose(its_graph)
848
+
849
+ return NXToGML().transform(
850
+ (reactant_graph, product_graph, its_graph),
851
+ reindex=reindex,
852
+ rule_name=rule_name,
853
+ explicit_hydrogen=explicit_hydrogen,
854
+ )
855
+
856
+
857
+ def gml_to_smart(
858
+ gml: str,
859
+ sanitize: bool = True,
860
+ explicit_hydrogen: bool = False,
861
+ useSmiles: bool = True,
862
+ ) -> str:
863
+ """
864
+ Convert a GML reaction rule back to reaction SMILES or SMARTS.
865
+
866
+ :param gml: GML string.
867
+ :type gml: str
868
+ :param sanitize: If ``True``, sanitize during SMILES generation.
869
+ :type sanitize: bool
870
+ :param explicit_hydrogen: If ``True``, keep explicit hydrogens.
871
+ :type explicit_hydrogen: bool
872
+ :param useSmiles: If ``True``, return reaction SMILES. Otherwise,
873
+ return reaction SMARTS.
874
+ :type useSmiles: bool
875
+ :return: Reaction SMILES or SMARTS string.
876
+ :rtype: str
877
+ :raises ValueError: If conversion fails.
878
+ """
879
+ reactant_graph, product_graph, rc_graph = GMLToNX(gml).transform()
880
+ rsmi = graph_to_rsmi(
881
+ r=reactant_graph,
882
+ p=product_graph,
883
+ its=rc_graph,
884
+ sanitize=sanitize,
885
+ explicit_hydrogen=explicit_hydrogen,
886
+ )
887
+ if rsmi is None:
888
+ raise ValueError("Failed to convert GML to reaction SMILES.")
889
+
890
+ if useSmiles:
891
+ return rsmi
892
+ return rsmi_to_rsmarts(rsmi)
893
+
894
+
895
+ def its_to_gml(
896
+ its: nx.Graph,
897
+ core: bool = True,
898
+ rule_name: str = "rule",
899
+ reindex: bool = True,
900
+ explicit_hydrogen: bool = False,
901
+ format: ITSFormat = "typesGH",
902
+ ) -> str:
903
+ """
904
+ Convert an ITS graph to GML format.
905
+
906
+ :param its: ITS graph.
907
+ :type its: nx.Graph
908
+ :param core: If ``True``, export the reaction-center graph.
909
+ :type core: bool
910
+ :param rule_name: Rule name stored in the GML output.
911
+ :type rule_name: str
912
+ :param reindex: If ``True``, reindex graph nodes before export.
913
+ :type reindex: bool
914
+ :param explicit_hydrogen: If ``True``, include explicit hydrogens.
915
+ :type explicit_hydrogen: bool
916
+ :param format: ITS format.
917
+ :type format: ITSFormat
918
+ :return: GML representation of the reaction.
919
+ :rtype: str
920
+ """
921
+ validated_format = _validate_its_format(format)
922
+ exported_its = _extract_rc_graph(its, validated_format) if core else its
923
+ reactant_graph, product_graph = _decompose_its(exported_its, validated_format)
924
+
925
+ return NXToGML().transform(
926
+ (reactant_graph, product_graph, exported_its),
927
+ reindex=reindex,
928
+ rule_name=rule_name,
929
+ explicit_hydrogen=explicit_hydrogen,
930
+ )
931
+
932
+
933
+ def gml_to_its(gml: str) -> nx.Graph:
934
+ """
935
+ Convert a GML reaction rule back into an ITS graph.
936
+
937
+ :param gml: GML string.
938
+ :type gml: str
939
+ :return: ITS graph.
940
+ :rtype: nx.Graph
941
+ """
942
+ _, _, its_graph = GMLToNX(gml).transform()
943
+ return its_graph
944
+
945
+
946
+ def rsmi_to_rsmarts(rsmi: str) -> str:
947
+ """
948
+ Convert mapped reaction SMILES to reaction SMARTS.
949
+
950
+ :param rsmi: Reaction SMILES input.
951
+ :type rsmi: str
952
+ :return: Reaction SMARTS string.
953
+ :rtype: str
954
+ :raises ValueError: If conversion fails.
955
+ """
956
+ try:
957
+ rxn = rdChemReactions.ReactionFromSmarts(rsmi, useSmiles=True)
958
+ return rdChemReactions.ReactionToSmarts(rxn)
959
+ except Exception as exc:
960
+ raise ValueError(f"Failed to convert RSMI to RSMARTS: {exc}") from exc
961
+
962
+
963
+ def rsmarts_to_rsmi(rsmarts: str) -> str:
964
+ """
965
+ Convert reaction SMARTS to reaction SMILES.
966
+
967
+ :param rsmarts: Reaction SMARTS input.
968
+ :type rsmarts: str
969
+ :return: Reaction SMILES string.
970
+ :rtype: str
971
+ :raises ValueError: If conversion fails.
972
+ """
973
+ try:
974
+ rxn = rdChemReactions.ReactionFromSmarts(rsmarts, useSmiles=False)
975
+ return rdChemReactions.ReactionToSmiles(rxn)
976
+ except Exception as exc:
977
+ raise ValueError(f"Failed to convert RSMARTS to RSMI: {exc}") from exc
978
+
979
+
980
+ def dfs_to_smiles(dfs: str, keep_map: bool = True) -> str:
981
+ """Convert DFS-style annotated SMILES to normal SMILES form.
982
+
983
+ The conversion replaces ``[]`` with ``[*]`` and converts bracketed tokens
984
+ followed by digits, such as ``[H]12``, into atom-mapped tokens
985
+ ``[H:12]`` when ``keep_map=True``.
986
+ When ``keep_map=False``, it removes the trailing digits instead. Tokens
987
+ already containing ``:`` inside brackets are preserved.
988
+
989
+ :param dfs: DFS-style SMILES or reaction SMILES.
990
+ :type dfs: str
991
+ :param keep_map: Whether to keep atom maps.
992
+ :type keep_map: bool
993
+ :return: Converted SMILES string.
994
+ :rtype: str
995
+ """
996
+ if not isinstance(dfs, str):
997
+ raise ValueError("dfs must be a string")
998
+
999
+ normalized = dfs.replace("[]", "[*]")
1000
+
1001
+ def _replace(match: re.Match[str]) -> str:
1002
+ inner = match.group(1)
1003
+ digits = match.group(2)
1004
+
1005
+ if ":" in inner:
1006
+ return match.group(0)
1007
+ if inner == "":
1008
+ inner = "*"
1009
+
1010
+ if keep_map:
1011
+ return f"[{inner}:{digits}]"
1012
+ return f"[{inner}]"
1013
+
1014
+ return _BRACKET_DIGIT_PATTERN.sub(_replace, normalized)
1015
+
1016
+
1017
+ def smiles_to_dfs(smiles: str) -> str:
1018
+ """
1019
+ Convert SMILES with atom maps into DFS-style notation.
1020
+
1021
+ Rules:
1022
+ - ``[X:123]`` becomes ``[X]123``
1023
+ - ``[*:3]`` becomes ``[]3``
1024
+ - unmapped tokens remain unchanged
1025
+ - remaining ``[*]`` is normalized back to ``[]``
1026
+
1027
+ :param smiles: SMILES or reaction SMILES.
1028
+ :type smiles: str
1029
+ :return: DFS-style string.
1030
+ :rtype: str
1031
+ """
1032
+ if not isinstance(smiles, str):
1033
+ raise ValueError("smiles must be a string")
1034
+
1035
+ def _replace(match: re.Match[str]) -> str:
1036
+ inner = match.group(1)
1037
+ number = match.group(2)
1038
+ if inner == "*":
1039
+ return f"[]{number}"
1040
+ return f"[{inner}]{number}"
1041
+
1042
+ dfs = _BRACKET_MAP_PATTERN.sub(_replace, smiles)
1043
+ return dfs.replace("[*]", "[]")
1044
+
1045
+
1046
+ def normalize_dfs_for_compare(dfs: str) -> str:
1047
+ """
1048
+ Normalize DFS-style strings for comparison.
1049
+
1050
+ :param dfs: DFS-style string.
1051
+ :type dfs: str
1052
+ :return: Normalized comparison string.
1053
+ :rtype: str
1054
+ """
1055
+ if not isinstance(dfs, str):
1056
+ raise ValueError("dfs must be a string")
1057
+
1058
+ normalized = dfs.replace("[*]", "[]")
1059
+ return re.sub(r"\s+", "", normalized)