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,652 @@
1
+ """Wildcard handling and fast matching for the RBL engine."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Mapping, Optional, Sequence, Union
6
+
7
+ import networkx as nx
8
+
9
+ from synkit.Graph.Fusion import graphs_exactly_equivalent
10
+ from synkit.Rule.syn_rule import SynRule
11
+ from synkit.Synthesis.RBL.validation import WildcardRole
12
+
13
+ ITSLike = Any
14
+ Port = tuple[str, Any, Any, tuple[Any, ...]]
15
+
16
+
17
+ def _typed_leaf(
18
+ graph: nx.Graph,
19
+ node: Any,
20
+ *,
21
+ element_key: str,
22
+ wildcard_values: tuple[Any, Any],
23
+ ) -> bool:
24
+ data = graph.nodes[node]
25
+ neighbours = list(graph.neighbors(node))
26
+ return (
27
+ data.get(element_key) in wildcard_values
28
+ and data.get("wildcard_role")
29
+ in {
30
+ WildcardRole.ATTACHMENT_PORT.value,
31
+ WildcardRole.RADICAL_COMPLETION.value,
32
+ }
33
+ and len(neighbours) == 1
34
+ and data.get("owner") == neighbours[0]
35
+ )
36
+
37
+
38
+ def _edge_signature(
39
+ graph: nx.Graph,
40
+ left: Any,
41
+ right: Any,
42
+ edge_attrs: Sequence[str],
43
+ ) -> tuple[Any, ...]:
44
+ attributes = graph.edges[left, right]
45
+ return tuple(attributes.get(key) for key in edge_attrs)
46
+
47
+
48
+ def _collect_ports(
49
+ forward: nx.Graph,
50
+ backward: nx.Graph,
51
+ base: Mapping[Any, Any],
52
+ *,
53
+ element_key: str,
54
+ wildcard_values: tuple[Any, Any],
55
+ edge_attrs: Sequence[str],
56
+ ) -> list[Port]:
57
+ """Collect typed leaves whose owner already belongs to the overlap."""
58
+ ports: list[Port] = []
59
+ for direction, graph, used, owners in (
60
+ ("forward", forward, set(base), base),
61
+ (
62
+ "backward",
63
+ backward,
64
+ set(base.values()),
65
+ {right: left for left, right in base.items()},
66
+ ),
67
+ ):
68
+ for port in sorted(graph.nodes, key=repr):
69
+ if port in used or not _typed_leaf(
70
+ graph,
71
+ port,
72
+ element_key=element_key,
73
+ wildcard_values=wildcard_values,
74
+ ):
75
+ continue
76
+ owner = next(iter(graph.neighbors(port)))
77
+ if owner in owners:
78
+ ports.append(
79
+ (
80
+ direction,
81
+ port,
82
+ owner,
83
+ _edge_signature(graph, port, owner, edge_attrs),
84
+ )
85
+ )
86
+ return ports
87
+
88
+
89
+ def _port_extensions(
90
+ port_info: Port,
91
+ current: Mapping[Any, Any],
92
+ forward: nx.Graph,
93
+ backward: nx.Graph,
94
+ *,
95
+ element_key: str,
96
+ wildcard_values: tuple[Any, Any],
97
+ edge_attrs: Sequence[str],
98
+ ) -> list[tuple[Any, Any]]:
99
+ """Return injective left-to-right pairs that can resolve one port."""
100
+ direction, port, owner, signature = port_info
101
+ if direction == "forward":
102
+ source, opposite = backward, current.get(owner)
103
+ used = set(current.values())
104
+ else:
105
+ source = forward
106
+ opposite = {right: left for left, right in current.items()}.get(owner)
107
+ used = set(current)
108
+ if opposite is None:
109
+ return []
110
+ candidates = [
111
+ node
112
+ for node in source.neighbors(opposite)
113
+ if node not in used
114
+ and source.nodes[node].get(element_key) not in wildcard_values
115
+ and _edge_signature(source, opposite, node, edge_attrs) == signature
116
+ ]
117
+ ordered = sorted(candidates, key=repr)
118
+ if direction == "forward":
119
+ return [(port, candidate) for candidate in ordered]
120
+ return [(candidate, port) for candidate in ordered]
121
+
122
+
123
+ def _port_assignment_edges(
124
+ ports: Sequence[Port],
125
+ base: Mapping[Any, Any],
126
+ forward: nx.Graph,
127
+ backward: nx.Graph,
128
+ *,
129
+ element_key: str,
130
+ wildcard_values: tuple[Any, Any],
131
+ edge_attrs: Sequence[str],
132
+ ) -> tuple[tuple[Any, Any], ...]:
133
+ """Build the exact bipartite compatibility graph for port assignment."""
134
+ edges: set[tuple[Any, Any]] = set()
135
+ for port in ports:
136
+ edges.update(
137
+ _port_extensions(
138
+ port,
139
+ base,
140
+ forward,
141
+ backward,
142
+ element_key=element_key,
143
+ wildcard_values=wildcard_values,
144
+ edge_attrs=edge_attrs,
145
+ )
146
+ )
147
+ return tuple(sorted(edges, key=lambda edge: (repr(edge[0]), repr(edge[1]))))
148
+
149
+
150
+ def _maximum_matching_size(
151
+ choices: Mapping[Any, Sequence[Any]],
152
+ remaining_left: Sequence[Any],
153
+ used_right: set[Any],
154
+ ) -> int:
155
+ graph = nx.Graph()
156
+ tagged_left = [("left", node) for node in remaining_left]
157
+ graph.add_nodes_from(tagged_left, bipartite=0)
158
+ for left in remaining_left:
159
+ for right in choices.get(left, ()):
160
+ if right not in used_right:
161
+ graph.add_node(("right", right), bipartite=1)
162
+ graph.add_edge(("left", left), ("right", right))
163
+ if not graph.edges:
164
+ return 0
165
+ matching = nx.algorithms.bipartite.maximum_matching(
166
+ graph,
167
+ top_nodes=set(tagged_left),
168
+ )
169
+ return len(matching) // 2
170
+
171
+
172
+ def _enumerate_port_matchings(
173
+ base: Mapping[Any, Any],
174
+ edges: Sequence[tuple[Any, Any]],
175
+ *,
176
+ maximum_only: bool,
177
+ ) -> List[Dict[Any, Any]]:
178
+ """Enumerate exact bipartite matchings, with optimality pruning if asked."""
179
+ choices: dict[Any, list[Any]] = {}
180
+ for left, right in edges:
181
+ choices.setdefault(left, []).append(right)
182
+ left_nodes = tuple(
183
+ sorted(choices, key=lambda node: (len(choices[node]), repr(node)))
184
+ )
185
+ optimum = (
186
+ _maximum_matching_size(choices, left_nodes, set(base.values()))
187
+ if maximum_only
188
+ else None
189
+ )
190
+ completed: List[Dict[Any, Any]] = []
191
+
192
+ def visit(
193
+ index: int,
194
+ current: Dict[Any, Any],
195
+ used_right: set[Any],
196
+ added: int,
197
+ ) -> None:
198
+ if index == len(left_nodes):
199
+ if optimum is None or added == optimum:
200
+ completed.append(dict(current))
201
+ return
202
+ if optimum is not None:
203
+ upper = added + _maximum_matching_size(
204
+ choices,
205
+ left_nodes[index:],
206
+ used_right,
207
+ )
208
+ if upper < optimum:
209
+ return
210
+ left = left_nodes[index]
211
+ for right in choices[left]:
212
+ if right in used_right:
213
+ continue
214
+ current[left] = right
215
+ used_right.add(right)
216
+ visit(index + 1, current, used_right, added + 1)
217
+ used_right.remove(right)
218
+ del current[left]
219
+ if optimum is None or added + _maximum_matching_size(
220
+ choices,
221
+ left_nodes[index + 1 :],
222
+ used_right,
223
+ ) >= optimum:
224
+ visit(index + 1, current, used_right, added)
225
+
226
+ visit(0, dict(base), set(base.values()), 0)
227
+ return _all_unique_mappings(completed)
228
+
229
+
230
+ def _all_unique_mappings(
231
+ completed: Sequence[Dict[Any, Any]],
232
+ ) -> List[Dict[Any, Any]]:
233
+ unique: Dict[tuple[tuple[str, str], ...], Dict[Any, Any]] = {}
234
+ for candidate in completed:
235
+ key = tuple(
236
+ sorted((repr(left), repr(right)) for left, right in candidate.items())
237
+ )
238
+ unique.setdefault(key, candidate)
239
+ return sorted(
240
+ unique.values(),
241
+ key=lambda mapping: (
242
+ -len(mapping),
243
+ repr(tuple(sorted(mapping.items(), key=repr))),
244
+ ),
245
+ )
246
+
247
+
248
+ class RBLMatchingMixin:
249
+ def _has_wildcard_nodes(self, G: ITSLike) -> bool:
250
+ """Check whether an ITS graph contains any wildcard atoms.
251
+
252
+ For now this assumes a NetworkX-style graph with node attributes.
253
+
254
+ :param G: ITS graph to inspect.
255
+ :type G: ITSLike
256
+ :return: ``True`` if any node has ``element_key == wildcard_element``.
257
+ :rtype: bool
258
+ """
259
+ if not isinstance(G, nx.Graph):
260
+ # For non-graph ITS types we conservatively return True
261
+ # so that the fast path is not applied.
262
+ return True
263
+
264
+ wildcard = self.wildcard_element
265
+ scalar_wildcard = wildcard[0] if isinstance(wildcard, tuple) else wildcard
266
+ element_key = self.element_key
267
+
268
+ for _, data in G.nodes(data=True):
269
+ if data.get(element_key) in (wildcard, scalar_wildcard):
270
+ return True
271
+ return False
272
+
273
+ def _annotate_wildcard_roles(
274
+ self,
275
+ graph: ITSLike,
276
+ role: WildcardRole,
277
+ ) -> ITSLike:
278
+ """Declare the role of otherwise-untyped wildcards at an RBL boundary."""
279
+ if not isinstance(graph, nx.Graph):
280
+ return graph
281
+ scalar_wildcard = (
282
+ self.wildcard_element[0]
283
+ if isinstance(self.wildcard_element, tuple)
284
+ else self.wildcard_element
285
+ )
286
+ for _, data in graph.nodes(data=True):
287
+ if data.get(self.element_key) in (
288
+ self.wildcard_element,
289
+ scalar_wildcard,
290
+ ):
291
+ data.setdefault("wildcard_role", role.value)
292
+ # A degree-one radical completion is an attachment port owned by its
293
+ # sole neighbour. Recording that incidence lets the verified fusion
294
+ # interface prove any later wildcard-to-concrete substitution.
295
+ for node, data in graph.nodes(data=True):
296
+ if data.get(self.element_key) not in (
297
+ self.wildcard_element,
298
+ scalar_wildcard,
299
+ ):
300
+ continue
301
+ neighbours = list(graph.neighbors(node))
302
+ if len(neighbours) == 1:
303
+ data.setdefault("owner", neighbours[0])
304
+ return graph
305
+
306
+ def _complete_typed_wildcard_ports(
307
+ self,
308
+ forward: nx.Graph,
309
+ backward: nx.Graph,
310
+ mapping: Mapping[Any, Any],
311
+ *,
312
+ maximum_only: bool = True,
313
+ ) -> List[Dict[Any, Any]]:
314
+ """Maximally extend an MCS overlap through typed leaf ports.
315
+
316
+ MCS compares concrete node labels and therefore cannot identify a
317
+ wildcard leaf with the concrete substituent supplied by the other
318
+ partial graph. For every typed degree-one wildcard whose owner is
319
+ already in the overlap, enumerate injective owner-adjacent concrete
320
+ substitutions with the same edge label. Only extensions with the
321
+ maximum number of resolved ports are returned; the categorical
322
+ interface performs the full constraint audit afterward.
323
+ """
324
+ scalar_wildcard = (
325
+ self.wildcard_element[0]
326
+ if isinstance(self.wildcard_element, tuple)
327
+ else self.wildcard_element
328
+ )
329
+ wildcard_values = (self.wildcard_element, scalar_wildcard)
330
+ base = dict(mapping)
331
+ ports = _collect_ports(
332
+ forward,
333
+ backward,
334
+ base,
335
+ element_key=self.element_key,
336
+ wildcard_values=wildcard_values,
337
+ edge_attrs=self.edge_attrs,
338
+ )
339
+ if not ports:
340
+ return [base]
341
+ edges = _port_assignment_edges(
342
+ ports,
343
+ base,
344
+ forward,
345
+ backward,
346
+ element_key=self.element_key,
347
+ wildcard_values=wildcard_values,
348
+ edge_attrs=self.edge_attrs,
349
+ )
350
+ return _enumerate_port_matchings(
351
+ base,
352
+ edges,
353
+ maximum_only=maximum_only,
354
+ )
355
+
356
+ def replace_wildcard_with_H(self, G: nx.Graph) -> nx.Graph:
357
+ """Replace wildcard atoms in an ITS graph with hydrogen.
358
+
359
+ This updates node-level attributes:
360
+
361
+ * ``node[element_key]``
362
+ * ``typesGH`` (if present, element field only)
363
+ * ``neighbors`` lists (string-based)
364
+
365
+ Edge structure and other attributes are not touched.
366
+
367
+ :param G: ITS graph to modify in-place.
368
+ :type G: nx.Graph
369
+ :return: The same graph instance, for convenience.
370
+ :rtype: nx.Graph
371
+ """
372
+ wildcard = self.wildcard_element
373
+ scalar_wildcard = wildcard[0] if isinstance(wildcard, tuple) else wildcard
374
+ element_key = self.element_key
375
+
376
+ wildcard_nodes = [
377
+ n
378
+ for n, d in G.nodes(data=True)
379
+ if d.get(element_key) in (wildcard, scalar_wildcard)
380
+ ]
381
+ if not wildcard_nodes:
382
+ return G
383
+
384
+ for n in wildcard_nodes:
385
+ data = G.nodes[n]
386
+ data[element_key] = (
387
+ ("H", "H") if isinstance(data.get(element_key), tuple) else "H"
388
+ )
389
+ # The role is evidence for the one-time materialization, not a
390
+ # persistent atom property of the resulting hydrogen.
391
+ data.pop("wildcard_role", None)
392
+
393
+ if "typesGH" in data and isinstance(data["typesGH"], tuple):
394
+ gh1, gh2 = data["typesGH"]
395
+ gh1 = ("H",) + tuple(gh1[1:])
396
+ gh2 = ("H",) + tuple(gh2[1:])
397
+ data["typesGH"] = (gh1, gh2)
398
+
399
+ for _, d in G.nodes(data=True):
400
+ if "neighbors" not in d:
401
+ continue
402
+ neighbors = d["neighbors"]
403
+ if isinstance(neighbors, tuple) and len(neighbors) == 2:
404
+ d["neighbors"] = (
405
+ [("H" if x == scalar_wildcard else x) for x in neighbors[0]],
406
+ [("H" if x == scalar_wildcard else x) for x in neighbors[1]],
407
+ )
408
+ else:
409
+ d["neighbors"] = [
410
+ ("H" if x == scalar_wildcard else x) for x in neighbors
411
+ ]
412
+
413
+ return G
414
+
415
+ # ------------------------------------------------------------------
416
+ # Matcher construction
417
+ # ------------------------------------------------------------------
418
+
419
+ def _build_matcher(self) -> Any:
420
+ """Construct a matcher instance using engine configuration.
421
+
422
+ Assumes :attr:`matcher_cls` is API-compatible with :class:`MCSMatcher`
423
+ or :class:`ApproxMCSMatcher`.
424
+
425
+ :return: Matcher instance.
426
+ :rtype: Any
427
+ """
428
+ node_defaults: List[Any] = []
429
+ for attr in self.node_attrs:
430
+ if attr == "element":
431
+ node_defaults.append(self.wildcard_element)
432
+ elif attr == "aromatic":
433
+ node_defaults.append((False, False))
434
+ elif attr == "charge":
435
+ node_defaults.append((0, 0))
436
+ else:
437
+ node_defaults.append(self.wildcard_element)
438
+
439
+ matcher = self.matcher_cls(
440
+ node_attrs=self.node_attrs,
441
+ node_defaults=node_defaults,
442
+ edge_attrs=self.edge_attrs,
443
+ prune_wc=self.prune_wc,
444
+ prune_automorphisms=self.prune_automorphisms,
445
+ wildcard_element=self.wildcard_element,
446
+ element_key=self.element_key,
447
+ )
448
+ return matcher
449
+
450
+ # ------------------------------------------------------------------
451
+ # Quick-check logic (pre-pipeline early-stop)
452
+ # ------------------------------------------------------------------
453
+
454
+ def _quick_check(
455
+ self,
456
+ rsmi: str,
457
+ template: Union[str, nx.Graph, SynRule, ITSLike],
458
+ ) -> Optional[str]:
459
+ """Fast pre-check used when early-stop or fast-paths-only logic is active.
460
+
461
+ Logic:
462
+
463
+ 1. Canonicalize the input reaction using :attr:`standardize_fn`.
464
+ 2. Split into reactants/products (``r``, ``p``).
465
+ 3. Prepare the template ITS (mirroring normal preparation).
466
+ 4. Run :class:`SynReactor` with ``partial=False``.
467
+ 5. For each candidate solution in ``reactor.smarts``, canonicalize it
468
+ and check whether the product side contains the canonicalized
469
+ product ``p``. The first such solution is returned.
470
+
471
+ If a match is found, :meth:`_record_stop` is called with mode
472
+ ``"quick_check"`` and the corresponding reason.
473
+
474
+ :param rsmi: Input reaction SMILES.
475
+ :type rsmi: str
476
+ :param template: Template as reaction SMILES, graph, normalized
477
+ :class:`SynRule`, or ITS-like value.
478
+ :type template: str | nx.Graph | SynRule | ITSLike
479
+ :return: Matching solution string or ``None`` if no match is found.
480
+ :rtype: Optional[str]
481
+ """
482
+ split = self._canonical_split(rsmi)
483
+ if split is None:
484
+ return None
485
+ r_canon, p_canon = split
486
+
487
+ if isinstance(template, SynRule):
488
+ temp_its = template
489
+ elif isinstance(template, nx.Graph):
490
+ temp_its = template
491
+ elif isinstance(template, str):
492
+ temp_its = self._prepare_from_str(template)
493
+ else:
494
+ temp_its = self.standardize_h_fn(template)
495
+
496
+ reactor = self.reactor_cls(
497
+ r_canon,
498
+ temp_its,
499
+ partial=False,
500
+ implicit_temp=self.implicit_temp,
501
+ explicit_h=self.explicit_h,
502
+ automorphism=False,
503
+ invert=False,
504
+ embed_threshold=self.embed_threshold,
505
+ electron_diagnostics=self.electron_diagnostics,
506
+ )
507
+ self._diagnostics["quick_check"].extend(
508
+ getattr(reactor, "diagnostics", []) or []
509
+ )
510
+
511
+ sols: Sequence[str] = getattr(reactor, "smarts", []) or []
512
+ if not sols:
513
+ return None
514
+
515
+ canon_sols = [self.standardize_fn(sol) for sol in sols]
516
+ for idx, rxn in enumerate(canon_sols):
517
+ split_sol = self._canonical_split(rxn)
518
+ if split_sol is None:
519
+ continue
520
+ _, prod = split_sol
521
+ if p_canon in prod:
522
+ self.logger.debug("Quick-check succeeded with solution index %d.", idx)
523
+ self._record_stop(
524
+ mode="quick_check",
525
+ reason="quick_check_match",
526
+ metadata={"solution_index": idx, "n_solutions": len(sols)},
527
+ )
528
+ return sols[idx]
529
+
530
+ return None
531
+
532
+ # ------------------------------------------------------------------
533
+ # Early-stop pruning on wildcard-free ITS
534
+ # ------------------------------------------------------------------
535
+
536
+ def _early_stop_on_nonwildcard(
537
+ self,
538
+ fw_its: Sequence[ITSLike],
539
+ bw_its: Sequence[ITSLike],
540
+ *,
541
+ replace_wc: bool,
542
+ stop_first: bool = True,
543
+ ) -> bool:
544
+ """Try an early-stop path based on ITS graphs that contain no wildcard
545
+ atoms, using the same endpoint-preservation proof as every other mode.
546
+
547
+ Rationale
548
+ ---------
549
+ For many reactions, some forward or backward ITS graphs are already
550
+ fully resolved (i.e. they do not contain any wildcard atoms).
551
+ In early-stop mode, we can treat such graphs as "good enough" and
552
+ directly post-process them without running expensive MCS/fusion,
553
+ provided that the resulting reaction is consistent with the
554
+ original one on the appropriate side.
555
+
556
+ Strategy
557
+ --------
558
+ 1. Collect all forward ITS without wildcard atoms.
559
+ 2. Collect all backward ITS without wildcard atoms.
560
+ 3. Iterate through these candidates (forward first, then backward),
561
+ and for each:
562
+ a. Post-process via :meth:`_postprocess_single`.
563
+ b. Require chemical validation and component-injective embeddings
564
+ of both original endpoints in the candidate endpoints.
565
+ c. On the first proven candidate, record an early-stop and return.
566
+ 4. If no candidate yields a valid fused RSMI, return ``False`` and
567
+ fall back to full fusion (unless fast-path-only mode is active).
568
+
569
+ :param fw_its: Forward ITS graphs.
570
+ :type fw_its: Sequence[ITSLike]
571
+ :param bw_its: Backward ITS graphs.
572
+ :type bw_its: Sequence[ITSLike]
573
+ :param replace_wc: Whether to replace wildcard atoms with H during
574
+ post-processing.
575
+ :type replace_wc: bool
576
+ :param stop_first: Return immediately after the first accepted direct
577
+ candidate. When false, collect every distinct direct candidate
578
+ for a wider search.
579
+ :type stop_first: bool
580
+ :return: ``True`` if a non-wildcard solution was found, ``False``
581
+ otherwise.
582
+ :rtype: bool
583
+ """
584
+ if (not fw_its and not bw_its) or self._last_reaction is None:
585
+ return False
586
+
587
+ candidates: List[tuple[str, ITSLike]] = []
588
+ for g in fw_its:
589
+ if not self._has_wildcard_nodes(g):
590
+ candidates.append(("fw", g))
591
+ for g in bw_its:
592
+ if not self._has_wildcard_nodes(g):
593
+ candidates.append(("bw", g))
594
+
595
+ if not candidates:
596
+ return False
597
+
598
+ rw_adder = self.wildcard_adder_cls()
599
+ fused_graphs: List[ITSLike] = list(self._fused_its)
600
+ fused_rsmis: List[str] = list(self._fused_rsmis)
601
+ found_nonwildcard = False
602
+
603
+ for side, graph in candidates:
604
+ rsmi_final = self._postprocess_single(
605
+ graph,
606
+ replace_wc=replace_wc,
607
+ rw_adder=rw_adder,
608
+ diagnostic_context={"candidate_source": side},
609
+ )
610
+ if rsmi_final is None:
611
+ continue
612
+
613
+ final_graph = self._latest_postprocessed_its
614
+ if not isinstance(final_graph, nx.Graph):
615
+ final_graph = graph
616
+ if any(
617
+ graphs_exactly_equivalent(final_graph, previous)
618
+ for previous in fused_graphs
619
+ ):
620
+ continue
621
+
622
+ found_nonwildcard = True
623
+ fused_graphs.append(final_graph)
624
+ fused_rsmis.append(rsmi_final)
625
+
626
+ self._fused_its = fused_graphs
627
+ self._fused_rsmis = fused_rsmis
628
+ self._record_stop(
629
+ mode="early_stop",
630
+ reason="early_stop_nonwildcard_its",
631
+ metadata={
632
+ "source": side,
633
+ "n_fw": len(fw_its),
634
+ "n_bw": len(bw_its),
635
+ "n_candidates": len(candidates),
636
+ "n_fused_rsmis": len(fused_rsmis),
637
+ "early_stop": stop_first,
638
+ },
639
+ )
640
+ self.logger.debug(
641
+ "Early-stop on non-wildcard ITS: source=%s, "
642
+ "n_candidates=%d, fused_rsmis=%d",
643
+ side,
644
+ len(candidates),
645
+ len(fused_rsmis),
646
+ )
647
+ if stop_first:
648
+ return True
649
+
650
+ # All candidates failed verification or post-processing;
651
+ # fall back to full fusion (unless fast-path-only mode is active).
652
+ return found_nonwildcard