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,554 @@
1
+ """Reaction-SMILES interface for full-space exact mapping and symmetry classes.
2
+
3
+ ``map_reaction`` defaults to Python propagation and assignment-bounded search
4
+ (PABS). It does not run SLAP or extract a reduced uncertainty kernel. The
5
+ optional C++ backend performs a separate full-space integer-shell search.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import math
12
+ import time
13
+ from collections import Counter
14
+ from dataclasses import asdict, dataclass
15
+ from numbers import Real
16
+
17
+ import numpy as np
18
+ from rdkit import Chem
19
+
20
+ from .solvers.native.reaction_text import split_reaction_fields
21
+ from .solvers.exact.distance_search.records import normalize_distance_target
22
+ from .solvers.exact.hydrogen import enumerate_minimal_hydrogen_transfers
23
+ from .solvers.exact.pabs.propagation import PropagationConfig
24
+ from .solvers.exact.search import enumerate_pabs_mappings
25
+ from .graph.labeled_graph import LabeledGraph
26
+ from .reporting.spectrum import _attributed_its_graph, _exact_code
27
+ from .solvers.slap.lap import _adjacency_and_elements
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class ReactionMapping:
32
+ """One exact symmetry class representative, including any missing atoms."""
33
+
34
+ mapped_reaction: str
35
+ mapping: tuple[int, ...]
36
+ class_id: str
37
+ distance: float
38
+ hydrogen_distance: int | None
39
+ reactant_only_maps: tuple[int, ...]
40
+ product_only_maps: tuple[int, ...]
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class ReactionMappingResult:
45
+ """User-facing mappings with independent search/classification completion."""
46
+
47
+ reactions: tuple[ReactionMapping, ...]
48
+ complete: bool
49
+ search_complete: bool
50
+ classification_complete: bool
51
+ hydrogen_complete: bool
52
+ minimum_cost: float | None
53
+ incomplete_reason: str | None
54
+ backend: str
55
+ hydrogen: str
56
+ objective: str
57
+ balance: str
58
+ agents: str
59
+ labeled_mapping_count: int | None
60
+ labeled_mapping_count_scope: str
61
+ minimum_cost_scope: str
62
+ elapsed_seconds: float
63
+ search_result: object
64
+
65
+ def as_dict(self):
66
+ """Export public metadata and mapped reactions without solver internals."""
67
+ from dataclasses import fields
68
+
69
+ return {
70
+ field.name: (
71
+ [asdict(item) for item in self.reactions] if field.name == "reactions" else getattr(self, field.name)
72
+ )
73
+ for field in fields(self)
74
+ if field.name != "search_result"
75
+ }
76
+
77
+ @property
78
+ def mapped_reactions(self):
79
+ return tuple(item.mapped_reaction for item in self.reactions)
80
+
81
+
82
+ def _parse(reaction, hydrogen):
83
+ if not isinstance(reaction, str):
84
+ raise TypeError("reaction must be reaction SMILES")
85
+ parts = split_reaction_fields(
86
+ reaction,
87
+ error_message="Expected reactants>agents>products or reactants>>products",
88
+ )
89
+ if not parts[0] or not parts[2]:
90
+ raise ValueError("Expected reactants>agents>products or reactants>>products")
91
+ params = Chem.SmilesParserParams()
92
+ params.removeHs = False
93
+ endpoints = []
94
+ for side in (parts[0], parts[2]):
95
+ mol = Chem.MolFromSmiles(side, params)
96
+ if mol is None:
97
+ raise ValueError("Could not parse reaction endpoint")
98
+ for atom in mol.GetAtoms():
99
+ if atom.GetAtomicNum() == 0:
100
+ raise ValueError("Wildcard atoms require a concrete element")
101
+ atom.SetAtomMapNum(0)
102
+ if hydrogen == "explicit":
103
+ mol = Chem.AddHs(mol)
104
+ else:
105
+ mol = Chem.RemoveHs(mol)
106
+ if any(atom.GetAtomicNum() == 1 for atom in mol.GetAtoms()):
107
+ raise ValueError("Free or isotopic H requires hydrogen='explicit'")
108
+ Chem.AssignStereochemistry(mol, cleanIt=True, force=True)
109
+ endpoints.append(mol)
110
+ return endpoints, parts[1]
111
+
112
+
113
+ def _identity(atom):
114
+ return atom.GetAtomicNum(), atom.GetIsotope()
115
+
116
+
117
+ def _graphs(mols, balance, objective):
118
+ inventories = [Counter(_identity(atom) for atom in mol.GetAtoms()) for mol in mols]
119
+ if balance == "strict" and inventories[0] != inventories[1]:
120
+ raise ValueError("Unbalanced atom inventories; select balance='dummy' explicitly")
121
+ universe = inventories[0] | inventories[1]
122
+ graphs = []
123
+ for mol, inventory in zip(mols, inventories):
124
+ atoms = list(mol.GetAtoms())
125
+ labels = [_identity(atom) for atom in atoms]
126
+ dummy_labels = sorted((universe - inventory).elements())
127
+ labels.extend(dummy_labels)
128
+ adjacency = {i: {} for i in range(len(labels))}
129
+ for bond in mol.GetBonds():
130
+ i, j = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
131
+ adjacency[i][j] = adjacency[j][i] = bond.GetBondTypeAsDouble()
132
+ properties = {
133
+ "charge": [atom.GetFormalCharge() for atom in atoms],
134
+ "radical": [atom.GetNumRadicalElectrons() for atom in atoms],
135
+ "hcount": [atom.GetTotalNumHs() for atom in atoms],
136
+ "stereo": [atom.GetProp("_CIPCode") if atom.HasProp("_CIPCode") else "" for atom in atoms],
137
+ "dummy": [False] * len(atoms),
138
+ # Fix stereobond endpoints individually in the pruning subgroup.
139
+ # The output equivalence also retains E/Z bond annotations; the
140
+ # matrix-only automorphism engine cannot otherwise see them.
141
+ "_stereo_anchor": [0] * len(atoms),
142
+ }
143
+ for bond in mol.GetBonds():
144
+ if bond.GetStereo() != Chem.BondStereo.STEREONONE:
145
+ for index in (bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()):
146
+ properties["_stereo_anchor"][index] = index + 1
147
+ for name, values in properties.items():
148
+ values.extend([True if name == "dummy" else "" if name == "stereo" else 0] * len(dummy_labels))
149
+ if objective == "lewis":
150
+ from synkit.IO.mol_to_graph import MolToGraph
151
+
152
+ # A uniquely colored anchor converts unary electron costs into
153
+ # ordinary edges. Its bijection is forced by atom compatibility.
154
+ electrons = [2 * MolToGraph.estimate_lone_pairs(atom) + atom.GetNumRadicalElectrons() for atom in atoms]
155
+ electrons.extend([0] * len(dummy_labels))
156
+ anchor = len(labels)
157
+ labels.append((0, -1))
158
+ adjacency[anchor] = {}
159
+ for i, count in enumerate(electrons):
160
+ if count:
161
+ adjacency[i][anchor] = adjacency[anchor][i] = count / 2
162
+ for name, values in properties.items():
163
+ values.append(True if name == "dummy" else "" if name == "stereo" else 0)
164
+ graph = LabeledGraph(adjacency, labels)
165
+ graph.props.update(properties)
166
+ graphs.append(graph)
167
+ return graphs
168
+
169
+
170
+ def _render(mols, mapping):
171
+ mapped = [Chem.Mol(mol) for mol in mols]
172
+ product_numbers = {image: atom + 1 for atom, image in enumerate(mapping)}
173
+ for atom in mapped[0].GetAtoms():
174
+ atom.SetAtomMapNum(atom.GetIdx() + 1)
175
+ for atom in mapped[1].GetAtoms():
176
+ atom.SetAtomMapNum(product_numbers[atom.GetIdx()])
177
+ nr, np = (mol.GetNumAtoms() for mol in mols)
178
+ reactant_only = tuple(i + 1 for i, j in enumerate(mapping[:nr]) if j >= np)
179
+ product_only = tuple(i + 1 for i, j in enumerate(mapping) if i >= nr and j < np)
180
+ return (
181
+ ">>".join(Chem.MolToSmiles(mol, canonical=True) for mol in mapped),
182
+ reactant_only,
183
+ product_only,
184
+ )
185
+
186
+
187
+ def _lift_mapping(mols, heavy_mapping, plan):
188
+ explicit = [Chem.AddHs(mol) for mol in mols]
189
+ children = []
190
+ for mol in explicit:
191
+ children.append(
192
+ {
193
+ atom.GetIdx(): [neighbor.GetIdx() for neighbor in atom.GetNeighbors() if neighbor.GetAtomicNum() == 1]
194
+ for atom in mol.GetAtoms()
195
+ if atom.GetAtomicNum() != 1
196
+ }
197
+ )
198
+ mapping = [-1] * explicit[0].GetNumAtoms()
199
+ mapping[: len(heavy_mapping)] = heavy_mapping
200
+
201
+ def transfer(source, target, count):
202
+ r = children[0][source]
203
+ p = children[1][heavy_mapping[target]]
204
+ for i, j in zip(r[:count], p[:count]):
205
+ mapping[i] = j
206
+ del r[:count]
207
+ del p[:count]
208
+
209
+ for i, count in enumerate(plan.preserved):
210
+ transfer(i, i, count)
211
+ for source, target, count in plan.transfers:
212
+ transfer(source, target, count)
213
+ if sorted(mapping) != list(range(len(mapping))):
214
+ raise RuntimeError("Hydrogen flow did not produce a full bijection")
215
+ return explicit, tuple(mapping)
216
+
217
+
218
+ def map_reaction(
219
+ reaction,
220
+ *,
221
+ backend="python",
222
+ library_path=None,
223
+ CD="minimal",
224
+ CD_mode="exact",
225
+ hydrogen=None,
226
+ objective="lewis",
227
+ balance="strict",
228
+ binary=False,
229
+ time_limit_seconds=10.0,
230
+ max_mappings=100_000,
231
+ max_bijections=None,
232
+ max_hydrogen_plans=10_000,
233
+ classification_max_search_nodes=100_000,
234
+ search_config=None,
235
+ ):
236
+ """Return symmetry-distinct mapped reactions from unmapped reaction SMILES.
237
+
238
+ ``backend='python'`` uses exact propagation and linear-assignment bounds,
239
+ called PABS (Propagation and Assignment-Bounded Search) by this package.
240
+ ``backend='cpp'`` uses the optional compiled full-space search. This entry
241
+ point does not run the SLAP heuristic or extract its uncertainty kernel.
242
+ It searches compatible assignments and then classifies the resulting ITSs.
243
+ Inspect ``complete`` because deadlines or output limits can interrupt it.
244
+
245
+ With CD omitted, prove the minimum and enumerate every minimum-distance
246
+ class. Numeric CD selects exactly that distance; the native backend also
247
+ accepts ``CD_mode='at_most'`` to enumerate every class at distance <= CD.
248
+
249
+ The default ``objective='lewis'`` uses explicit H and weighted bonds. Its
250
+ normalized distance is bond-order distance plus half the L1 difference of
251
+ estimated nonbonding electron counts (twice lone pairs plus radicals).
252
+ Select ``objective='bond'`` for bond-only distance and implicit heavy H.
253
+ ``hydrogen=None`` selects explicit H for Lewis and heavy atoms for bond CD.
254
+
255
+ Hydrogen modes: ``heavy`` searches heavy bonds; ``explicit`` searches all
256
+ H bonds jointly; ``compressed`` returns minimum-H lifts conditional on each
257
+ selected heavy mapping (not a global full-H optimum).
258
+
259
+ ``balance='dummy'`` pads missing element/isotope inventories with isolated
260
+ zero-state placeholders. Output contains only real endpoint atoms; map IDs
261
+ present on one side are reported explicitly. Agents are excluded from search
262
+ and returned separately. Existing atom-map numbers are discarded, not fixed.
263
+ ``search_config`` optionally supplies ``PropagationConfig`` controls for the
264
+ Python PABS backend, including experimental separator and factor-spectrum
265
+ bounds.
266
+
267
+ Classes use exact colored ITS canonicalization, equivalent to the two-sided
268
+ automorphism action beta*f*alpha^-1. Incomplete canonicalization never merges
269
+ unverified classes. The deadline is shared by search, H lifting and dedup.
270
+ """
271
+ return _map_reaction_with_search(
272
+ reaction,
273
+ search_function=enumerate_pabs_mappings,
274
+ backend=backend,
275
+ library_path=library_path,
276
+ CD=CD,
277
+ CD_mode=CD_mode,
278
+ hydrogen=hydrogen,
279
+ objective=objective,
280
+ balance=balance,
281
+ binary=binary,
282
+ time_limit_seconds=time_limit_seconds,
283
+ max_mappings=max_mappings,
284
+ max_bijections=max_bijections,
285
+ max_hydrogen_plans=max_hydrogen_plans,
286
+ classification_max_search_nodes=classification_max_search_nodes,
287
+ search_config=search_config,
288
+ )
289
+
290
+
291
+ # Parsing, callbacks and rendering share one search budget and result contract.
292
+ def _map_reaction_with_search( # noqa: C901
293
+ reaction,
294
+ *,
295
+ search_function,
296
+ backend="python",
297
+ library_path=None,
298
+ CD="minimal",
299
+ CD_mode="exact",
300
+ hydrogen=None,
301
+ objective="lewis",
302
+ balance="strict",
303
+ binary=False,
304
+ time_limit_seconds=10.0,
305
+ max_mappings=100_000,
306
+ max_bijections=None,
307
+ max_hydrogen_plans=10_000,
308
+ classification_max_search_nodes=100_000,
309
+ search_config=None,
310
+ native_classification_library=None,
311
+ ):
312
+ """Share parsing, hydrogen lifting, rendering and classification across solvers."""
313
+ started = time.perf_counter()
314
+ CD = normalize_distance_target(CD)
315
+ if CD_mode not in {"exact", "at_most"}:
316
+ raise ValueError("CD_mode must be 'exact' or 'at_most'")
317
+ if CD_mode == "at_most" and (backend != "cpp" or CD == "minimal"):
318
+ raise ValueError("CD_mode='at_most' requires native search and numeric CD")
319
+ if hydrogen is None:
320
+ hydrogen = "explicit" if objective == "lewis" else "heavy"
321
+ if CD_mode == "at_most" and hydrogen == "compressed":
322
+ raise ValueError(
323
+ "CD_mode='at_most' requires explicit or heavy hydrogens; "
324
+ "compressed lifts have a separate conditional distance"
325
+ )
326
+ if hydrogen not in {"heavy", "explicit", "compressed"}:
327
+ raise ValueError("hydrogen must be 'heavy', 'explicit', or 'compressed'")
328
+ if objective not in {"bond", "lewis"} or balance not in {"strict", "dummy"}:
329
+ raise ValueError("objective must be 'bond' or 'lewis'; balance must be 'strict' or 'dummy'")
330
+ if objective == "lewis" and (hydrogen != "explicit" or binary):
331
+ raise ValueError("Lewis objective requires hydrogen='explicit' and binary=False")
332
+ if search_config is not None and not isinstance(search_config, PropagationConfig):
333
+ raise TypeError("search_config must be a PropagationConfig or None")
334
+ if search_config is not None and backend != "python":
335
+ raise ValueError("search_config is currently supported by backend='python'")
336
+ if hydrogen == "compressed" and balance != "strict":
337
+ raise ValueError("Compressed H requires strict balance; use explicit H with dummy balance")
338
+ if time_limit_seconds is not None and (
339
+ isinstance(time_limit_seconds, bool)
340
+ or not isinstance(time_limit_seconds, Real)
341
+ or not math.isfinite(time_limit_seconds)
342
+ or time_limit_seconds < 0
343
+ ):
344
+ raise ValueError("time_limit_seconds must be finite and non-negative")
345
+ for name, value in (
346
+ ("max_hydrogen_plans", max_hydrogen_plans),
347
+ ("classification_max_search_nodes", classification_max_search_nodes),
348
+ ):
349
+ if isinstance(value, bool) or not isinstance(value, int) or value < 1:
350
+ raise ValueError(f"{name} must be a positive integer")
351
+ deadline = None if time_limit_seconds is None else started + time_limit_seconds
352
+
353
+ def remaining():
354
+ return None if deadline is None else max(0, deadline - time.perf_counter())
355
+
356
+ mols, agents = _parse(reaction, hydrogen)
357
+ graphs = _graphs(mols, balance, objective)
358
+ native_classifier = None
359
+ native_class_registry = {}
360
+ if native_classification_library is not None:
361
+ if hydrogen == "compressed":
362
+ raise ValueError("Native class pipeline currently requires explicit or heavy H")
363
+ from .solvers.native.classification import NativeITSBatchClassifier
364
+
365
+ native_classifier = NativeITSBatchClassifier.from_endpoint_graphs(
366
+ mols, graphs, library_path=native_classification_library, binary=binary
367
+ )
368
+ native_classifier.frontend_max_canonical_nodes = classification_max_search_nodes
369
+ hcounts = [[atom.GetTotalNumHs() for atom in mol.GetAtoms()] for mol in mols]
370
+ if hydrogen == "compressed" and sum(hcounts[0]) != sum(hcounts[1]):
371
+ raise ValueError("Compressed H requires equal H inventories; use explicit H with dummy balance")
372
+ records = {}
373
+ canonical_cache = {}
374
+ classification_complete = hydrogen_complete = True
375
+ failure = None
376
+
377
+ def classify(endpoint_mols, endpoint_graphs, mapping, cost, hcost=None):
378
+ nonlocal classification_complete, failure
379
+ if not classification_complete:
380
+ return
381
+ if deadline is not None and time.perf_counter() >= deadline:
382
+ classification_complete = False
383
+ failure = "classification:time_limit"
384
+ return
385
+ if native_classifier is not None:
386
+ native_record = native_class_registry.pop(tuple(mapping), None)
387
+ if native_record is None:
388
+ raise RuntimeError("Native class callback lacks an exact class record")
389
+ code = native_record.key
390
+ if code not in records:
391
+ rendered, left_only, right_only = _render(endpoint_mols, mapping)
392
+ records[code] = ReactionMapping(
393
+ rendered,
394
+ tuple(mapping),
395
+ native_record.identifier,
396
+ float(cost),
397
+ hcost,
398
+ left_only,
399
+ right_only,
400
+ )
401
+ return
402
+ a, labels = _adjacency_and_elements(endpoint_graphs[0], binary)
403
+ b, _ = _adjacency_and_elements(endpoint_graphs[1], binary)
404
+ properties = {
405
+ name: (endpoint_graphs[0].props[name], endpoint_graphs[1].props[name])
406
+ for name in ("charge", "radical", "hcount", "stereo", "dummy")
407
+ }
408
+ # _attributed_its_graph expects product already in reactant coordinates.
409
+ images = np.asarray(mapping, dtype=int)
410
+ its = _attributed_its_graph(a, b[images[:, None], images[None, :]], labels, properties, mapping)
411
+ # Retain endpoint E/Z annotations in the exact equivalence relation.
412
+ for i, j, attrs in its.edges(data=True):
413
+ stereo = []
414
+ for mol, left, right in (
415
+ (endpoint_mols[0], i, j),
416
+ (endpoint_mols[1], mapping[i], mapping[j]),
417
+ ):
418
+ bond = mol.GetBondBetweenAtoms(left, right) if max(left, right) < mol.GetNumAtoms() else None
419
+ stereo.append(str(bond.GetStereo()) if bond is not None else "")
420
+ attrs["color"] += tuple(stereo)
421
+ # Identical fully colored indexed graphs need no second proof. This
422
+ # cache never treats a hash or WL fingerprint as class equality.
423
+ signature = (
424
+ tuple((i, attrs["color"]) for i, attrs in its.nodes(data=True)),
425
+ tuple((i, j, attrs["color"]) for i, j, attrs in its.edges(data=True)),
426
+ )
427
+ code = canonical_cache.get(signature)
428
+ reason = None
429
+ if code is None:
430
+ code, reason = _exact_code(
431
+ its,
432
+ timeout_seconds=remaining(),
433
+ max_search_nodes=classification_max_search_nodes,
434
+ )
435
+ if code is None:
436
+ classification_complete = False
437
+ failure = f"classification:{reason}"
438
+ return
439
+ if len(canonical_cache) < 256:
440
+ canonical_cache[signature] = code
441
+ if code not in records:
442
+ rendered, left_only, right_only = _render(endpoint_mols, mapping)
443
+ records[code] = ReactionMapping(
444
+ rendered,
445
+ tuple(mapping),
446
+ hashlib.sha256(repr(code).encode()).hexdigest(),
447
+ float(cost),
448
+ hcost,
449
+ left_only,
450
+ right_only,
451
+ )
452
+
453
+ def receive(mapping, cost):
454
+ nonlocal hydrogen_complete, failure
455
+ if hydrogen == "compressed":
456
+ plans = enumerate_minimal_hydrogen_transfers(
457
+ *hcounts,
458
+ mapping,
459
+ max_plans=max_hydrogen_plans,
460
+ time_limit_seconds=remaining(),
461
+ )
462
+ if not plans.complete:
463
+ hydrogen_complete = False
464
+ failure = f"hydrogen:{plans.truncation_reason}"
465
+ for plan in plans.plans:
466
+ explicit, lifted = _lift_mapping(mols, mapping, plan)
467
+ classify(
468
+ explicit,
469
+ _graphs(explicit, "strict", "bond"),
470
+ lifted,
471
+ cost + plan.distance,
472
+ plan.distance,
473
+ )
474
+ else:
475
+ classify(mols, graphs, mapping, cost)
476
+
477
+ search_options = {} if search_config is None else {"config": search_config}
478
+ if native_classifier is not None:
479
+ search_options.update(
480
+ _native_classifier=native_classifier,
481
+ _native_class_registry=native_class_registry,
482
+ )
483
+ if backend == "cpp":
484
+ search_options.update(
485
+ CD_mode=CD_mode,
486
+ symmetry=True,
487
+ node_properties=(
488
+ "charge",
489
+ "radical",
490
+ "hcount",
491
+ "stereo",
492
+ "dummy",
493
+ "_stereo_anchor",
494
+ ),
495
+ )
496
+ search = search_function(
497
+ graphs,
498
+ backend=backend,
499
+ library_path=library_path,
500
+ CD=CD,
501
+ binary=binary,
502
+ max_bijections=max_bijections,
503
+ max_mappings=max_mappings,
504
+ time_limit_seconds=remaining(),
505
+ collect_mappings=False,
506
+ mapping_callback=receive,
507
+ compute_minimum_cost=(CD == "minimal"),
508
+ **search_options,
509
+ )
510
+ native_details = search.backend_statistics or {}
511
+ hydrogen_complete = hydrogen_complete and bool(native_details.get("hydrogen_complete", True))
512
+ if native_classifier is not None:
513
+ classification_complete = classification_complete and bool(native_details.get("classification_complete", False))
514
+ if not classification_complete:
515
+ reason = native_details.get("classification_reason") or search.truncation_reason or "incomplete"
516
+ failure = f"classification:{reason}"
517
+ if not hydrogen_complete and failure is None:
518
+ failure = f"hydrogen:{search.truncation_reason or 'incomplete'}"
519
+ objective_name = (
520
+ "heavy_cd_with_conditional_minimum_h_lifts"
521
+ if hydrogen == "compressed"
522
+ else (
523
+ "lewis_bond_plus_nonbonding_electron_distance"
524
+ if objective == "lewis"
525
+ else ("explicit_h_bond_distance" if hydrogen == "explicit" else "heavy_bond_distance")
526
+ )
527
+ )
528
+ labeled_count = getattr(search, "selected_labeled_mapping_count", search.selected_mapping_count)
529
+ count_scope = (
530
+ "heavy_mapping" if hydrogen == "compressed" else "augmented_mapping" if balance == "dummy" else "mapping"
531
+ )
532
+ count_scope += "_ball" if CD_mode == "at_most" else "_shell"
533
+ return ReactionMappingResult(
534
+ reactions=tuple(sorted(records.values(), key=lambda item: item.class_id)),
535
+ complete=search.complete and classification_complete and hydrogen_complete,
536
+ search_complete=search.complete,
537
+ classification_complete=classification_complete,
538
+ hydrogen_complete=hydrogen_complete,
539
+ minimum_cost=search.minimum_cost,
540
+ incomplete_reason=failure or search.truncation_reason,
541
+ backend=search.backend,
542
+ hydrogen=hydrogen,
543
+ objective=objective_name,
544
+ balance=balance,
545
+ agents=agents,
546
+ labeled_mapping_count=labeled_count,
547
+ labeled_mapping_count_scope=("not_computed_symmetry_pruned_search" if labeled_count is None else count_scope),
548
+ minimum_cost_scope=("heavy_bond_distance" if hydrogen == "compressed" else objective_name),
549
+ elapsed_seconds=time.perf_counter() - started,
550
+ search_result=search,
551
+ )
552
+
553
+
554
+ __all__ = ["ReactionMapping", "ReactionMappingResult", "map_reaction"]
@@ -0,0 +1 @@
1
+ """Exact-shell analysis, ITS spectra, and structural alternatives."""