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,561 @@
1
+ """Graph preparation and ITS gluing helpers for SynReactor."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Tuple
6
+
7
+ import networkx as nx
8
+
9
+ from synkit.Graph import add_wildcard_subgraph_for_unmapped
10
+ from synkit.Graph.Hyrogen._misc import h_to_explicit
11
+ from synkit.Graph.ITS.its_construction import ITSConstruction
12
+ from synkit.Graph.ITS.its_decompose import its_decompose
13
+ from synkit.Graph.ITS.its_reverter import ITSReverter
14
+ from synkit.Graph.Matcher.subgraph_matcher import SubgraphSearchEngine
15
+ from synkit.IO.chem_converter import ITSFormat, detect_its_format
16
+ from . import product as _product_state
17
+ from .strategy import Strategy
18
+
19
+ MappingDict = Dict[Any, Any]
20
+ _EMBEDDING_ANCHOR = "_synkit_explicit_h_embedding_anchor"
21
+
22
+
23
+ def _implicit_heavy_hydrogens(
24
+ graph: nx.Graph,
25
+ *,
26
+ preserve_mapped_hydrogens: bool = False,
27
+ ) -> nx.Graph:
28
+ """Convert ordinary heavy-atom-bound explicit H nodes into hcount."""
29
+ removable = []
30
+ for node, attrs in graph.nodes(data=True):
31
+ if attrs.get("element") != "H":
32
+ continue
33
+ neighbors = list(graph.neighbors(node))
34
+ heavy_neighbors = [
35
+ nbr for nbr in neighbors if graph.nodes[nbr].get("element") != "H"
36
+ ]
37
+ if heavy_neighbors and len(heavy_neighbors) == len(neighbors):
38
+ removable.append((node, heavy_neighbors))
39
+
40
+ # Parsed heavy-atom graphs normally contain no removable explicit H.
41
+ # Returning the immutable matching input is then exact and avoids a full
42
+ # graph copy; the copy-on-write branch below preserves the old contract
43
+ # whenever normalization actually changes state.
44
+ if not removable:
45
+ return graph
46
+
47
+ normalized = graph.copy()
48
+
49
+ for h, heavy_neighbors in removable:
50
+ if not normalized.has_node(h):
51
+ continue
52
+ for heavy in heavy_neighbors:
53
+ atom_map = normalized.nodes[h].get("atom_map")
54
+ if preserve_mapped_hydrogens and isinstance(atom_map, int) and atom_map > 0:
55
+ stored = list(normalized.nodes[heavy].get("_implicit_h_atom_maps", ()))
56
+ stored.append(atom_map)
57
+ normalized.nodes[heavy]["_implicit_h_atom_maps"] = tuple(stored)
58
+ normalized.nodes[heavy]["hcount"] = (
59
+ normalized.nodes[heavy].get("hcount", 0) + 1
60
+ )
61
+ normalized.remove_node(h)
62
+ return normalized
63
+
64
+
65
+ def _invert_template(
66
+ tpl: nx.Graph,
67
+ balance_its: bool = True,
68
+ format: ITSFormat | None = None,
69
+ ) -> nx.Graph:
70
+ resolved_format = format or detect_its_format(tpl)
71
+ if resolved_format == "tuple":
72
+ reverter = ITSReverter(tpl)
73
+ left, right = reverter.to_reactant_graph(), reverter.to_product_graph()
74
+ return ITSConstruction().construct(
75
+ right,
76
+ left,
77
+ balance_its=balance_its,
78
+ )
79
+ left, right = its_decompose(tpl)
80
+ return ITSConstruction().ITSGraph(right, left, balance_its=balance_its)
81
+
82
+
83
+ # ==================================================================
84
+ # Aux – glue, explicit‑H, SMARTS
85
+ # ==================================================================
86
+ def _node_glue(
87
+ host_n: Dict[str, Any], pat_n: Dict[str, Any], key: str = "typesGH"
88
+ ) -> None:
89
+ host_r, host_p = host_n[key]
90
+ pat_r, pat_p = pat_n[key]
91
+ delta = pat_r[2] - pat_p[2]
92
+ if pat_r[0] == "*":
93
+ new_r = host_r
94
+ else:
95
+ new_r = host_r[:2] + (host_r[2],) + host_r[3:]
96
+ if pat_p[0] == "*":
97
+ new_p = host_p[:2] + (host_r[2] - delta,) + (host_p[3],) + host_p[4:]
98
+ else:
99
+ new_p = host_p[:2] + (host_r[2] - delta,) + (pat_p[3],) + host_p[4:]
100
+ # if pat_r[0] == '*':
101
+ # host_r[0] = '*'
102
+ # if pat_p[0] == '*':
103
+ # host_p[0] = '*'
104
+ host_n[key] = (new_r, new_p)
105
+
106
+ for key in ("h_pairs", "h_pairs_left", "h_pairs_right", "h_pair_atom_maps"):
107
+ if key in pat_n:
108
+ host_n[key] = pat_n[key]
109
+
110
+
111
+ def _get_explicit_map(
112
+ host: nx.Graph,
113
+ mapping: MappingDict,
114
+ pattern_explicit: nx.Graph | None = None,
115
+ strategy: Strategy = Strategy.ALL,
116
+ embed_threshold: float = None,
117
+ embed_pre_filter: bool = False,
118
+ anchor_embedding: bool = True,
119
+ expanded_host_cache: Dict[frozenset[Any], nx.Graph] | None = None,
120
+ ):
121
+ """Expand implicit H only within, and consistently with, one embedding.
122
+
123
+ In deduplicated mode, ``mapping`` was already accepted against the
124
+ implicit-H pattern, so the explicit-H search needs only extensions of
125
+ that embedding. Unique temporary anchor colours enforce this condition
126
+ inside VF2 and avoid repeating every heavy-atom permutation. Raw mode can
127
+ disable anchoring to retain its historical exhaustive multiplicity.
128
+ """
129
+ expand_nodes = [v for _, v in mapping.items()]
130
+ original_nodes = set(host)
131
+ cache_key = frozenset(expand_nodes)
132
+ cached_host = (
133
+ expanded_host_cache.get(cache_key) if expanded_host_cache is not None else None
134
+ )
135
+ if cached_host is None:
136
+ host_explicit = h_to_explicit(host, expand_nodes)
137
+ for node in set(host_explicit) - original_nodes:
138
+ if not host_explicit.nodes[node].get("_restored_mapped_h"):
139
+ host_explicit.nodes[node]["_pattern_expanded_h"] = True
140
+ if expanded_host_cache is not None:
141
+ expanded_host_cache[cache_key] = host_explicit.copy()
142
+ else:
143
+ host_explicit = cached_host.copy()
144
+ anchored_pattern = (pattern_explicit or nx.Graph()).copy()
145
+ if anchor_embedding:
146
+ for anchor, (pattern_node, host_node) in enumerate(
147
+ sorted(mapping.items(), key=lambda item: repr(item[0])),
148
+ start=1,
149
+ ):
150
+ if pattern_node not in anchored_pattern or host_node not in host_explicit:
151
+ continue
152
+ anchored_pattern.nodes[pattern_node][_EMBEDDING_ANCHOR] = anchor
153
+ host_explicit.nodes[host_node][_EMBEDDING_ANCHOR] = anchor
154
+
155
+ search_host = host_explicit
156
+ missing_pattern_nodes = set(anchored_pattern) - set(mapping)
157
+ local_h_extension = bool(anchor_embedding) and all(
158
+ anchored_pattern.nodes[node].get("element") == "H"
159
+ and all(neighbor in mapping for neighbor in anchored_pattern.neighbors(node))
160
+ and any(neighbor in mapping for neighbor in anchored_pattern.neighbors(node))
161
+ for node in missing_pattern_nodes
162
+ )
163
+ if local_h_extension:
164
+ # Heavy nodes are pointwise anchored. Every omitted pattern H came
165
+ # from implicitization and is adjacent only to those anchors, so no
166
+ # node outside their closed H-neighbourhood can extend this mapping.
167
+ # Restricting VF2 to that induced subgraph preserves the exact
168
+ # extension set while avoiding traversal of unrelated host context.
169
+ local_nodes = set(mapping.values())
170
+ for host_node in tuple(local_nodes):
171
+ local_nodes.update(
172
+ neighbor
173
+ for neighbor in host_explicit.neighbors(host_node)
174
+ if host_explicit.nodes[neighbor].get("element") == "H"
175
+ )
176
+ search_host = host_explicit.subgraph(local_nodes).copy()
177
+
178
+ mappings = SubgraphSearchEngine.find_subgraph_mappings(
179
+ host=search_host,
180
+ pattern=anchored_pattern,
181
+ node_attrs=["element", "charge", _EMBEDDING_ANCHOR],
182
+ edge_attrs=["order"],
183
+ strategy=strategy,
184
+ threshold=embed_threshold,
185
+ pre_filter=embed_pre_filter,
186
+ )
187
+
188
+ # Atom maps are not chemical matching constraints, but when applying a
189
+ # reviewed mapped rule back to its mapped host they are the deterministic
190
+ # tie-breaker between symmetry-equivalent explicit hydrogens.
191
+ mappings.sort(
192
+ key=lambda candidate: _mapping_atom_map_alignment(
193
+ anchored_pattern,
194
+ host_explicit,
195
+ candidate,
196
+ ),
197
+ reverse=True,
198
+ )
199
+ for _, attrs in host_explicit.nodes(data=True):
200
+ attrs.pop(_EMBEDDING_ANCHOR, None)
201
+ return mappings, host_explicit
202
+
203
+
204
+ def _mapping_atom_map_alignment(
205
+ pattern: nx.Graph,
206
+ host: nx.Graph,
207
+ mapping: MappingDict,
208
+ ) -> int:
209
+ """Count positive reactant-side AAM identities in one candidate map."""
210
+
211
+ def reactant_map(value: Any) -> Any:
212
+ if isinstance(value, tuple) and len(value) == 2:
213
+ return value[0]
214
+ return value
215
+
216
+ score = 0
217
+ for pattern_node, host_node in mapping.items():
218
+ pattern_map = reactant_map(pattern.nodes[pattern_node].get("atom_map"))
219
+ host_map = reactant_map(host.nodes[host_node].get("atom_map"))
220
+ if isinstance(pattern_map, int) and pattern_map > 0:
221
+ score += pattern_map == host_map
222
+ return score
223
+
224
+
225
+ def _restore_unmatched_pattern_hydrogens(
226
+ graph: nx.Graph,
227
+ mapping: MappingDict,
228
+ ) -> None:
229
+ """Fold unmatched temporary H expansions back into heavy-atom hcount."""
230
+ matched_nodes = set(mapping.values())
231
+ removable = [
232
+ node
233
+ for node, attrs in graph.nodes(data=True)
234
+ if attrs.get("_pattern_expanded_h") and node not in matched_nodes
235
+ ]
236
+ for hydrogen in removable:
237
+ neighbors = list(graph.neighbors(hydrogen))
238
+ if len(neighbors) != 1:
239
+ continue
240
+ heavy = neighbors[0]
241
+ graph.nodes[heavy]["hcount"] = graph.nodes[heavy].get("hcount", 0) + 1
242
+ types = graph.nodes[heavy].get("typesGH")
243
+ if (
244
+ isinstance(types, tuple)
245
+ and len(types) == 2
246
+ and isinstance(types[0], tuple)
247
+ and len(types[0]) >= 3
248
+ ):
249
+ graph.nodes[heavy]["typesGH"] = (
250
+ types[0][:2] + (types[0][2] + 1,) + types[0][3:],
251
+ types[1],
252
+ )
253
+ graph.remove_node(hydrogen)
254
+
255
+
256
+ def _prepare_rewrite_host(host: nx.Graph, *, electron_aware: bool) -> nx.Graph:
257
+ """Materialize mapping-invariant host state once for a rewrite batch."""
258
+ prepared = host.copy()
259
+ for _, data in prepared.nodes(data=True):
260
+ tpl = (
261
+ data.get("element", "*"),
262
+ data.get("aromatic", False),
263
+ data.get("hcount", 0),
264
+ data.get("charge", 0),
265
+ data.get("neighbors", []),
266
+ )
267
+ data.setdefault("typesGH", (tpl, tpl))
268
+ if electron_aware:
269
+ _product_state._ensure_host_atom_maps(prepared)
270
+ for _, _, data in prepared.edges(data=True):
271
+ order = data.get("order", 1.0)
272
+ data["order"] = (order, order)
273
+ if electron_aware:
274
+ sigma = data.get("sigma_order", 1.0 if order else 0.0)
275
+ pi = data.get("pi_order", max(0.0, float(order) - 1.0))
276
+ data["sigma_order"] = (sigma, sigma)
277
+ data["pi_order"] = (pi, pi)
278
+ data.setdefault("standard_order", 0.0)
279
+ return prepared
280
+
281
+
282
+ def _prepare_rewrite_batch_host(
283
+ host: nx.Graph,
284
+ mappings: List[MappingDict],
285
+ reaction_center: nx.Graph,
286
+ pattern_has_explicit_h: bool,
287
+ electron_aware: bool,
288
+ *,
289
+ seed_structural_signatures: bool = False,
290
+ ) -> Tuple[nx.Graph, bool]:
291
+ """Return a reusable prepared host when every application is total."""
292
+ if pattern_has_explicit_h or any(
293
+ not set(reaction_center).issubset(mapping) for mapping in mappings
294
+ ):
295
+ return host, False
296
+ prepared = _prepare_rewrite_host(host, electron_aware=electron_aware)
297
+ if seed_structural_signatures and len(mappings) >= 2:
298
+ # Exact labels of unchanged context are mapping-invariant. NetworkX
299
+ # copies their immutable values into every application, so materialize
300
+ # them once and invalidate only the local write/electron support below.
301
+ from ..output import structural as _structural
302
+
303
+ prepared.graph["electron_aware_rewrite"] = electron_aware
304
+ prepared.graph[_structural._EXACT_NODE_PALETTE] = {}
305
+ prepared.graph[_structural._EXACT_EDGE_PALETTE] = {}
306
+ prepared.graph[_structural._EXACT_IDENTITY_CACHE] = {}
307
+ _structural._attach_exact_structural_signatures(prepared)
308
+ prepared.graph["_structural_signatures_seeded"] = True
309
+ return prepared, True
310
+
311
+
312
+ def _glue_graph(
313
+ host: nx.Graph,
314
+ rc: nx.Graph,
315
+ mapping: MappingDict,
316
+ pattern_has_explicit_H: bool = False,
317
+ pattern_explicit: nx.Graph | None = None,
318
+ strategy: Strategy = Strategy.ALL,
319
+ embed_threshold: float = None,
320
+ embed_pre_filter: bool = False,
321
+ relative_pi_edges: set[frozenset[Any]] | None = None,
322
+ restore_unmatched_explicit_h: bool = True,
323
+ refresh_electrons: bool = True,
324
+ electron_aware: bool | None = None,
325
+ relative_match_resources: frozenset[str] = frozenset(),
326
+ anchor_explicit_embedding: bool = True,
327
+ host_prepared: bool = False,
328
+ expanded_host_cache: Dict[frozenset[Any], nx.Graph] | None = None,
329
+ consume_prepared_host: bool = False,
330
+ ) -> List[nx.Graph]:
331
+ list_its: List[nx.Graph] = []
332
+ # NetworkX copies node/edge attribute dictionaries. Rewrite values
333
+ # are replaced rather than mutated in place, so recursively copying
334
+ # every tuple and stereo descriptor only adds mapping-sized overhead.
335
+ # A prepared batch host is a private working copy. Its last application
336
+ # can safely take ownership instead of cloning the full molecular graph
337
+ # once more; earlier applications have already received independent
338
+ # copies. Explicit-H expansion may branch and therefore keeps the
339
+ # conservative copy path.
340
+ host_g = (
341
+ host
342
+ if consume_prepared_host and host_prepared and not pattern_has_explicit_H
343
+ else host.copy()
344
+ )
345
+ if electron_aware is None:
346
+ electron_aware = _is_electron_aware_template(rc)
347
+
348
+ def _default_tg(a: Dict[str, Any]) -> Tuple[Tuple[Any, ...], Tuple[Any, ...]]:
349
+ tpl = (
350
+ a.get("element", "*"),
351
+ a.get("aromatic", False),
352
+ a.get("hcount", 0),
353
+ a.get("charge", 0),
354
+ a.get("neighbors", []),
355
+ )
356
+ return tpl, tpl
357
+
358
+ if not host_prepared:
359
+ for _, data in host_g.nodes(data=True):
360
+ data.setdefault("typesGH", _default_tg(data))
361
+ if electron_aware:
362
+ _product_state._ensure_host_atom_maps(host_g)
363
+
364
+ if pattern_has_explicit_H:
365
+ mappings, host_g = _get_explicit_map(
366
+ host_g,
367
+ mapping,
368
+ pattern_explicit,
369
+ strategy,
370
+ embed_threshold,
371
+ embed_pre_filter,
372
+ anchor_explicit_embedding,
373
+ expanded_host_cache,
374
+ )
375
+ if electron_aware:
376
+ _product_state._ensure_host_atom_maps(host_g)
377
+ else:
378
+ mappings = [mapping]
379
+
380
+ relative_pi_centers = {
381
+ node for edge in (relative_pi_edges or set()) for node in edge
382
+ }
383
+ relative_node_resources = (
384
+ frozenset(rc.graph.get("relative_node_resources", ()))
385
+ | relative_match_resources
386
+ )
387
+
388
+ # Iterate over remappings --------------------------------------
389
+ reuse_prepared_host = len(mappings) == 1
390
+ for m in mappings:
391
+
392
+ its = host_g if reuse_prepared_host else host_g.copy()
393
+ if its.graph.get("_structural_signatures_seeded", False):
394
+ # Graph.copy() deliberately shares immutable label values. Dirty
395
+ # supports, in contrast, must be private to this application.
396
+ its.graph["_structural_exact_dirty_nodes"] = set()
397
+ its.graph["_structural_exact_dirty_edges"] = set()
398
+ structural_dirty_nodes = its.graph.get("_structural_exact_dirty_nodes")
399
+ structural_dirty_edges = its.graph.get("_structural_exact_dirty_edges")
400
+ if pattern_has_explicit_H and restore_unmatched_explicit_h:
401
+ _restore_unmatched_pattern_hydrogens(its, m)
402
+ # Materialize wildcard nodes for partial mappings.
403
+ if len(m.keys()) < rc.number_of_nodes():
404
+ its, m = add_wildcard_subgraph_for_unmapped(
405
+ its,
406
+ rc,
407
+ m,
408
+ inplace=True,
409
+ tuple_mode=electron_aware,
410
+ )
411
+
412
+ if not host_prepared:
413
+ for _, _, data in its.edges(data=True):
414
+ o = data.get("order", 1.0)
415
+ data["order"] = (o, o)
416
+ if electron_aware:
417
+ sigma = data.get("sigma_order", 1.0 if o else 0.0)
418
+ pi = data.get("pi_order", max(0.0, float(o) - 1.0))
419
+ data["sigma_order"] = (sigma, sigma)
420
+ data["pi_order"] = (pi, pi)
421
+ data.setdefault("standard_order", 0.0)
422
+
423
+ for _, data in rc.nodes(data=True):
424
+ data.setdefault("typesGH", _default_tg(data))
425
+
426
+ # merge nodes -------------------------------------------
427
+ for rc_n, host_n in m.items():
428
+ if its.has_node(host_n):
429
+ its.nodes[host_n].pop("_structural_exact_node_sig", None)
430
+ if isinstance(structural_dirty_nodes, set):
431
+ structural_dirty_nodes.add(host_n)
432
+ _node_glue(its.nodes[host_n], rc.nodes[rc_n])
433
+ if electron_aware:
434
+ rc_element = rc.nodes[rc_n].get("element")
435
+ wildcard_context = (
436
+ isinstance(rc_element, tuple)
437
+ and len(rc_element) == 2
438
+ and rc_element[0] == rc_element[1] == "*"
439
+ )
440
+ _product_state._pair_electron_aware_node_attrs(
441
+ its.nodes[host_n],
442
+ rc.nodes[rc_n],
443
+ preserve_unchanged_state=(
444
+ rc_n in relative_pi_centers or wildcard_context
445
+ ),
446
+ relative_resources=relative_node_resources,
447
+ )
448
+
449
+ # merge edges (additive order) ---------------------------
450
+ for u, v, rc_attr in rc.edges(data=True):
451
+ hu, hv = m.get(u), m.get(v)
452
+ if hu is None or hv is None:
453
+ continue
454
+ if not its.has_edge(hu, hv):
455
+ its.add_edge(hu, hv, **dict(rc_attr))
456
+ if isinstance(structural_dirty_edges, set):
457
+ structural_dirty_edges.add((hu, hv))
458
+ else:
459
+ host_attr = its[hu][hv]
460
+ host_attr.pop("_structural_exact_edge_sig", None)
461
+ if isinstance(structural_dirty_edges, set):
462
+ structural_dirty_edges.add((hu, hv))
463
+ rc_order = rc_attr.get("order", (0, 0))
464
+ if relative_pi_edges and frozenset((u, v)) in relative_pi_edges:
465
+ # Apply the rule delta to the matched host edge. Thus
466
+ # C=C -> C-C naturally becomes C#C -> C=C on an
467
+ # alkyne, while both still remove exactly one pi bond.
468
+ for key in (
469
+ "order",
470
+ "kekule_order",
471
+ "sigma_order",
472
+ "pi_order",
473
+ ):
474
+ host_value = host_attr.get(key)
475
+ rule_value = rc_attr.get(key)
476
+ if not (
477
+ isinstance(host_value, tuple)
478
+ and len(host_value) == 2
479
+ and isinstance(rule_value, tuple)
480
+ and len(rule_value) == 2
481
+ ):
482
+ continue
483
+ delta = float(rule_value[0]) - float(rule_value[1])
484
+ product_value = float(host_value[0]) - delta
485
+ if product_value.is_integer():
486
+ product_value = int(product_value)
487
+ host_attr[key] = (host_value[0], product_value)
488
+ host_attr["standard_order"] = rc_attr.get("standard_order", 0.0)
489
+ elif rc_order[0] == 0: # additive only on product side
490
+ ho = host_attr["order"]
491
+ host_attr["order"] = (ho[0], round(ho[1] + rc_order[1]))
492
+ if electron_aware:
493
+ host_sigma = host_attr.get("sigma_order", (0.0, 0.0))
494
+ host_pi = host_attr.get("pi_order", (0.0, 0.0))
495
+ rc_sigma = rc_attr.get("sigma_order", (0.0, 0.0))
496
+ rc_pi = rc_attr.get("pi_order", (0.0, 0.0))
497
+ host_attr["sigma_order"] = (
498
+ host_sigma[0],
499
+ host_sigma[1] + rc_sigma[1],
500
+ )
501
+ host_attr["pi_order"] = (
502
+ host_pi[0],
503
+ host_pi[1] + rc_pi[1],
504
+ )
505
+ host_attr["standard_order"] += rc_attr.get("standard_order", 0.0)
506
+ else:
507
+ # Preserve the host reactant phase; independently parsed
508
+ # aromatic graphs may use an equivalent Kekule assignment.
509
+ for key, rule_value in rc_attr.items():
510
+ if isinstance(rule_value, tuple) and len(rule_value) == 2:
511
+ host_value = host_attr.get(key)
512
+ host_left = (
513
+ host_value[0]
514
+ if isinstance(host_value, tuple)
515
+ and len(host_value) == 2
516
+ else host_value
517
+ )
518
+ host_attr[key] = (host_left, rule_value[1])
519
+ else:
520
+ host_attr[key] = rule_value
521
+ its.graph["electron_aware_rewrite"] = electron_aware
522
+ if electron_aware:
523
+ # Retain the exact one-local support of the electron functional:
524
+ # mapped write nodes plus boundary neighbors whose incident edge
525
+ # state actually changes. Consumers that receive an ITS without
526
+ # this proof hint retain the conservative whole-graph path.
527
+ its.graph["_product_refresh_nodes"] = (
528
+ _product_state._electron_refresh_support(
529
+ its,
530
+ m.values(),
531
+ )
532
+ )
533
+ its.graph["_product_electron_fields_current"] = False
534
+ its.graph["_product_kekule_phase_dirty"] = (
535
+ _product_state._product_kekule_phase_is_dirty(its)
536
+ )
537
+ if refresh_electrons:
538
+ _product_state._refresh_product_electron_fields(its)
539
+ list_its.append(its)
540
+ return list_its
541
+
542
+
543
+ def _is_electron_aware_template(rc: nx.Graph) -> bool:
544
+ """Return whether an RC carries paired Lewis-state rewrite data.
545
+
546
+ Explicit hydrogen transfer edges may be folded into ``hcount`` by
547
+ :class:`SynRule`. Radical or lone-pair changes on the remaining nodes
548
+ still require the electron-aware tuple rewrite path in that case.
549
+ """
550
+ edge_state = any(
551
+ "sigma_order" in data and "pi_order" in data
552
+ for _, _, data in rc.edges(data=True)
553
+ )
554
+ node_state = any(
555
+ isinstance(data.get(key), tuple)
556
+ and len(data[key]) == 2
557
+ and data[key][0] != data[key][1]
558
+ for _, data in rc.nodes(data=True)
559
+ for key in ("radical", "lone_pairs", "valence_electrons")
560
+ )
561
+ return edge_state or node_state
@@ -0,0 +1,44 @@
1
+ from enum import Enum
2
+ from typing import Union
3
+
4
+
5
+ class Strategy(str, Enum):
6
+ """Strategy for sub-graph matching/application:
7
+
8
+ - ALL: classic VF2 on the whole graph
9
+ - COMPONENT: component-aware only (no cross-CC backtracking)
10
+ - BACKTRACK: component-aware with backtracking across CCs
11
+ - PARTIAL: partial matching (mcs)
12
+ """
13
+
14
+ ALL = "all"
15
+ COMPONENT = "comp"
16
+ BACKTRACK = "bt"
17
+ PARTIAL = "partial"
18
+
19
+ @classmethod
20
+ def from_string(cls, value: Union[str, "Strategy"]) -> "Strategy":
21
+ """Convert a string or Strategy to a Strategy enum.
22
+
23
+ :param value: The strategy to parse.
24
+ :type value: str or Strategy
25
+
26
+ :return: Parsed Strategy.
27
+ :rtype: Strategy
28
+
29
+ :raises ValueError: If the input is not a valid Strategy.
30
+ """
31
+ if isinstance(value, cls):
32
+ return value
33
+ try:
34
+ return cls(value.lower()) # type: ignore[arg-type]
35
+ except ValueError as e:
36
+ raise ValueError(f"Unknown strategy: {value!r}") from e
37
+
38
+ def __str__(self) -> str:
39
+ """Return the strategy’s canonical code (e.g. 'all', 'comp', 'bt')."""
40
+ return self.value
41
+
42
+ def __repr__(self) -> str:
43
+ """Return the enum-style representation, e.g. 'Strategy.ALL'."""
44
+ return f"{self.__class__.__name__}.{self.name}"
@@ -0,0 +1 @@
1
+ """Reaction-rule matching policies and symmetry reduction."""