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,506 @@
1
+ """Template preparation and reaction execution for the RBL engine."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import copy
6
+ from collections import Counter
7
+ from typing import Any, Dict, List, Optional, Sequence, Union
8
+
9
+ try:
10
+ from typing import Self
11
+ except ImportError:
12
+ from typing_extensions import Self
13
+
14
+ import networkx as nx
15
+
16
+ from synkit.Chem.utils import reverse_reaction
17
+ from synkit.Rule.syn_rule import SynRule
18
+ from synkit.Synthesis.RBL.validation import (
19
+ FusionIssue,
20
+ FusionIssueCode,
21
+ FusionValidation,
22
+ WildcardRole,
23
+ validate_fusion_rsmi,
24
+ )
25
+ from synkit.Synthesis.RBL.policy import AcceptanceTask
26
+
27
+ ITSLike = Any
28
+
29
+
30
+ class RBLReactionMixin:
31
+ @staticmethod
32
+ def _normalize_duplicate_atom_maps(graph: nx.Graph) -> nx.Graph:
33
+ """Assign deterministic unique AAM labels when fused sources collide."""
34
+ result = graph.copy()
35
+
36
+ def endpoint_maps(value: Any) -> tuple[Any, ...]:
37
+ if isinstance(value, (tuple, list)) and len(value) == 2:
38
+ return tuple(value)
39
+ return (value,)
40
+
41
+ seen: List[set[Any]] = [set(), set()]
42
+ duplicate = False
43
+ for _, data in result.nodes(data=True):
44
+ values = endpoint_maps(data.get("atom_map"))
45
+ for index, value in enumerate(values):
46
+ if value in (None, 0):
47
+ continue
48
+ bucket = seen[min(index, 1)]
49
+ if value in bucket:
50
+ duplicate = True
51
+ bucket.add(value)
52
+ if not duplicate:
53
+ return result
54
+
55
+ for atom_map, node in enumerate(sorted(result.nodes, key=repr), start=1):
56
+ old = result.nodes[node].get("atom_map")
57
+ result.nodes[node]["atom_map"] = (
58
+ (atom_map, atom_map)
59
+ if isinstance(old, (tuple, list)) and len(old) == 2
60
+ else atom_map
61
+ )
62
+ result.graph["atom_maps_normalized_after_fusion"] = True
63
+ return result
64
+
65
+ @staticmethod
66
+ def _identity_standardize(rsmi: str) -> str:
67
+ """Return a whitespace-normalized reaction when no standardizer exists."""
68
+ return rsmi.strip()
69
+
70
+ def _canonical_split(self, rsmi: str) -> Optional[tuple[str, str]]:
71
+ """Canonicalize and split a reaction into its two endpoints."""
72
+ canon = self.standardize_fn(rsmi)
73
+ try:
74
+ reactants, products = canon.split(">>", 1)
75
+ except (AttributeError, ValueError):
76
+ self.logger.debug("Canonical split failed for reaction: %s", rsmi)
77
+ return None
78
+ return reactants, products
79
+
80
+ def _reset_run_state(self) -> None:
81
+ """Reset per-run outputs while retaining reusable configuration."""
82
+ self._forward_its = []
83
+ self._backward_its = []
84
+ self._fused_its = []
85
+ self._fused_rsmis = []
86
+ self._fusion_candidates = []
87
+ self._fusion_search_stats = {}
88
+ self._latest_postprocessed_its = None
89
+ self._latest_output_validation = None
90
+ self._diagnostics = {
91
+ "forward": [],
92
+ "backward": [],
93
+ "quick_check": [],
94
+ "fusion": [],
95
+ }
96
+ self._last_stop_mode = "not_run"
97
+ self._last_stop_reason = "not_run"
98
+ self._last_stop_metadata = {}
99
+ self._active_search_policy = self.search_policy
100
+
101
+ def _record_stop(
102
+ self,
103
+ *,
104
+ mode: str,
105
+ reason: str,
106
+ metadata: Optional[Dict[str, Any]] = None,
107
+ ) -> None:
108
+ """Record the stable termination description for the current run."""
109
+ self._last_stop_mode = mode
110
+ self._last_stop_reason = reason
111
+ self._last_stop_metadata = dict(metadata or {})
112
+
113
+ def _prepare_from_graph(self, template: nx.Graph) -> ITSLike:
114
+ """Internal helper: prepare a template from a NetworkX graph.
115
+
116
+ :param template: ITS-like NetworkX graph.
117
+ :type template: nx.Graph
118
+ :return: Standardized ITS-like object.
119
+ :rtype: ITSLike
120
+ """
121
+ # Compatibility modes historically folded every explicit hydrogen into
122
+ # its heavy-atom ``hcount``. The verified path must retain mapped H
123
+ # identities: they are reaction-rule vertices, not display details.
124
+ temp = (
125
+ self.h_to_implicit_fn(template) if self.implicit_temp else template.copy()
126
+ )
127
+ return self.standardize_h_fn(temp)
128
+
129
+ def _prepare_from_str(self, template: str) -> ITSLike:
130
+ """Internal helper: prepare a template from a reaction SMILES string.
131
+
132
+ :param template: Reaction SMILES.
133
+ :type template: str
134
+ :return: Standardized ITS-like object.
135
+ :rtype: ITSLike
136
+ """
137
+ # Only an implicit-H template is allowed to discard explicit H atoms.
138
+ # In particular, a mapped transfer such as O:3-H:4 -> O:2-H:4 must
139
+ # retain H:4 as a first-class rule vertex in verified mode.
140
+ cleaned = (
141
+ self.remove_explicit_H_fn(template) if self.implicit_temp else template
142
+ )
143
+ temp = self.rsmi_to_its_fn(cleaned, core=True)
144
+ return self.standardize_h_fn(temp)
145
+
146
+ def prepare_template(
147
+ self,
148
+ template: Union[str, nx.Graph, SynRule, ITSLike],
149
+ ) -> Self:
150
+ """Prepare a reaction template into a standardized ITS representation.
151
+
152
+ :param template: Template as reaction SMILES, graph, normalized
153
+ :class:`SynRule`, or ITS-like value.
154
+ :type template: str | nx.Graph | SynRule | ITSLike
155
+ :return: The current engine instance (for chaining).
156
+ :rtype: RBLEngine
157
+ """
158
+ self._template_raw = template
159
+
160
+ if isinstance(template, SynRule):
161
+ self._template_its = template
162
+ elif isinstance(template, nx.Graph):
163
+ self._template_its = self._prepare_from_graph(template)
164
+ elif isinstance(template, str):
165
+ self._template_its = self._prepare_from_str(template)
166
+ else:
167
+ self._template_its = self.standardize_h_fn(template)
168
+
169
+ self.logger.debug("Template prepared: %s", type(self._template_its))
170
+ return self
171
+
172
+ # ------------------------------------------------------------------
173
+ # Reaction application core helpers
174
+ # ------------------------------------------------------------------
175
+
176
+ def _safe_its_to_rsmi(
177
+ self,
178
+ its_graph: ITSLike,
179
+ *,
180
+ fmt: str = "tuple",
181
+ explicit_hydrogen: bool = False,
182
+ ) -> Optional[str]:
183
+ """Safely convert ITS to RSMI, returning ``None`` on failure.
184
+
185
+ :param its_graph: ITS-like graph to convert.
186
+ :type its_graph: ITSLike
187
+ :return: Reaction SMILES or ``None`` on failure.
188
+ :rtype: Optional[str]
189
+ """
190
+ try:
191
+ kwargs: Dict[str, Any] = {"format": fmt}
192
+ if explicit_hydrogen:
193
+ kwargs["explicit_hydrogen"] = True
194
+ return self.its_to_rsmi_fn(its_graph, **kwargs)
195
+ except Exception as exc: # pragma: no cover - defensive
196
+ self.logger.debug("ITS→RSMI conversion failed: %s", exc)
197
+ return None
198
+
199
+ @staticmethod
200
+ def _strip_balanced_isolated_wildcards(rsmi: str) -> str:
201
+ """Remove identical isolated wildcard spectators from both endpoints.
202
+
203
+ Only complete dot-separated fragments of the forms ``*``, ``[*]`` or
204
+ ``[*:n]`` are eligible. The multiset intersection is removed, so an
205
+ unmatched wildcard is preserved for later validation instead of being
206
+ silently discarded.
207
+ """
208
+ if rsmi.count(">>") != 1:
209
+ return rsmi
210
+
211
+ reactants, products = rsmi.split(">>", 1)
212
+ reactant_parts = [part for part in reactants.split(".") if part]
213
+ product_parts = [part for part in products.split(".") if part]
214
+
215
+ def is_isolated_wildcard(fragment: str) -> bool:
216
+ if fragment in {"*", "[*]"}:
217
+ return True
218
+ return (
219
+ fragment.startswith("[*:")
220
+ and fragment.endswith("]")
221
+ and fragment[3:-1].isdigit()
222
+ )
223
+
224
+ common = Counter(
225
+ part for part in reactant_parts if is_isolated_wildcard(part)
226
+ ) & Counter(part for part in product_parts if is_isolated_wildcard(part))
227
+
228
+ def remove_common(parts: List[str]) -> List[str]:
229
+ remaining = common.copy()
230
+ kept: List[str] = []
231
+ for part in parts:
232
+ if remaining[part] > 0:
233
+ remaining[part] -= 1
234
+ else:
235
+ kept.append(part)
236
+ return kept
237
+
238
+ return (
239
+ ".".join(remove_common(reactant_parts))
240
+ + ">>"
241
+ + ".".join(remove_common(product_parts))
242
+ )
243
+
244
+ def _validate_fusion_output(
245
+ self,
246
+ rsmi: str,
247
+ *,
248
+ allow_wildcards: bool,
249
+ source: str,
250
+ context: Optional[Dict[str, Any]] = None,
251
+ ) -> FusionValidation:
252
+ """Apply and record the common endpoint contract for an RBL result."""
253
+ if self._last_reaction is None:
254
+ validation = validate_fusion_rsmi(
255
+ rsmi,
256
+ allow_wildcards=allow_wildcards,
257
+ )
258
+ else:
259
+ # Resolve through the historical facade so monkeypatching and
260
+ # instrumentation of ``rbl_engine.validate_rbl_candidate`` keep
261
+ # working after this method moved into a mixin.
262
+ from synkit.Synthesis.RBL import engine as rbl_engine_module
263
+
264
+ if (
265
+ self._active_search_policy.acceptance_task
266
+ is AcceptanceTask.STRICT_RECONSTRUCTION
267
+ ):
268
+ validation = rbl_engine_module.validate_strict_rbl_candidate(
269
+ self._last_reaction,
270
+ rsmi,
271
+ allow_wildcards=allow_wildcards,
272
+ boundary=self.conservation_boundary,
273
+ environment_delta=self.environment_delta,
274
+ )
275
+ else:
276
+ validation = rbl_engine_module.validate_rbl_candidate(
277
+ self._last_reaction,
278
+ rsmi,
279
+ allow_wildcards=allow_wildcards,
280
+ preserve_sides=self.preserve_original_sides,
281
+ )
282
+ payload = validation.to_dict()
283
+ payload["source"] = source
284
+ payload.update(context or {})
285
+ self._diagnostics["fusion"].append(payload)
286
+ self._latest_output_validation = validation
287
+ return validation
288
+
289
+ def _record_fusion_failure(
290
+ self,
291
+ code: FusionIssueCode,
292
+ message: str,
293
+ *,
294
+ source: str,
295
+ context: Optional[Dict[str, Any]] = None,
296
+ ) -> None:
297
+ """Record a contained construction or serialization failure."""
298
+ issue = FusionIssue(
299
+ code=code,
300
+ stage="fusion",
301
+ message=message,
302
+ context=context or {},
303
+ )
304
+ payload = FusionValidation(valid=False, issues=(issue,)).to_dict()
305
+ payload["source"] = source
306
+ payload.update(context or {})
307
+ self._diagnostics["fusion"].append(payload)
308
+
309
+ def _record_reaction_failure(
310
+ self,
311
+ code: FusionIssueCode,
312
+ message: str,
313
+ *,
314
+ stage: str,
315
+ source: str,
316
+ context: Optional[Dict[str, Any]] = None,
317
+ ) -> None:
318
+ """Record a typed forward/backward application-stage failure."""
319
+ issue = FusionIssue(
320
+ code=code,
321
+ stage=stage,
322
+ message=message,
323
+ context=context or {},
324
+ )
325
+ payload = FusionValidation(valid=False, issues=(issue,)).to_dict()
326
+ payload["stage"] = stage
327
+ payload["source"] = source
328
+ self._diagnostics[stage].append(payload)
329
+
330
+ def _safe_rsmi_to_its(self, rsmi: str) -> Optional[ITSLike]:
331
+ """Safely convert RSMI to ITS, returning ``None`` on failure.
332
+
333
+ :param rsmi: Reaction SMILES to convert.
334
+ :type rsmi: str
335
+ :return: ITS-like object or ``None`` on failure.
336
+ :rtype: Optional[ITSLike]
337
+ """
338
+ try:
339
+ return self.rsmi_to_its_fn(rsmi, format="tuple")
340
+ except Exception as exc: # pragma: no cover - defensive
341
+ self.logger.debug("RSMI→ITS conversion failed: %s", exc)
342
+ return None
343
+
344
+ def _decorate_radical(self, rsmi: str, invert: bool) -> Optional[str]:
345
+ """Apply radical wildcard decoration (and optional inversion).
346
+
347
+ :param rsmi: Reaction SMILES to decorate.
348
+ :type rsmi: str
349
+ :param invert: Whether to reverse the reaction (products↔reactants)
350
+ after decoration.
351
+ :type invert: bool
352
+ :return: Decorated (and possibly inverted) reaction SMILES, or
353
+ ``None`` on failure.
354
+ :rtype: Optional[str]
355
+ """
356
+ rw_adder = self.wildcard_adder_cls()
357
+ try:
358
+ decorated = rw_adder.transform(rsmi)
359
+ except Exception as exc: # pragma: no cover - defensive
360
+ self.logger.debug("Radical wildcard decoration failed: %s", exc)
361
+ return None
362
+
363
+ if invert:
364
+ return reverse_reaction(decorated)
365
+ return decorated
366
+
367
+ def _run_reaction(
368
+ self,
369
+ substrate: Union[str, ITSLike],
370
+ pattern: ITSLike,
371
+ invert: bool,
372
+ ) -> List[ITSLike]:
373
+ """Internal helper: apply template to substrate using :class:`SynReactor`.
374
+
375
+ For each resulting ITS, the method performs:
376
+
377
+ 1. ITS → RSMI
378
+ 2. Radical wildcard decoration
379
+ 3. Optional inversion
380
+ 4. RSMI → ITS
381
+
382
+ Any failing conversions are skipped.
383
+
384
+ :param substrate: Substrate reaction string or ITS-like object.
385
+ :type substrate: str | ITSLike
386
+ :param pattern: Template ITS-like object.
387
+ :type pattern: ITSLike
388
+ :param invert: Whether to run the reactor in inverted mode.
389
+ :type invert: bool
390
+ :return: List of ITS-like objects after decoration.
391
+ :rtype: list[ITSLike]
392
+ """
393
+ self.logger.debug(
394
+ "Running reaction: invert=%s, substrate type=%s",
395
+ invert,
396
+ type(substrate),
397
+ )
398
+
399
+ reactor = self.reactor_cls(
400
+ substrate,
401
+ pattern,
402
+ partial=True,
403
+ implicit_temp=self.implicit_temp,
404
+ explicit_h=self.explicit_h,
405
+ automorphism=False,
406
+ invert=invert,
407
+ embed_threshold=self.embed_threshold,
408
+ electron_diagnostics=self.electron_diagnostics,
409
+ dedup_its=not self.verified_mode,
410
+ )
411
+ stage = "backward" if invert else "forward"
412
+ self._diagnostics[stage].extend(getattr(reactor, "diagnostics", []) or [])
413
+
414
+ out: List[ITSLike] = []
415
+ its_list: Sequence[ITSLike] = getattr(reactor, "its", []) or []
416
+
417
+ for its_graph in its_list:
418
+ rsmi = self._safe_its_to_rsmi(
419
+ its_graph,
420
+ fmt="typesGH",
421
+ explicit_hydrogen=self.explicit_h,
422
+ )
423
+ if rsmi is None:
424
+ self._record_reaction_failure(
425
+ FusionIssueCode.SERIALIZATION_FAILED,
426
+ "Could not serialize a rule-application graph.",
427
+ stage=stage,
428
+ source=f"{stage}_application",
429
+ context={"invert": invert},
430
+ )
431
+ continue
432
+
433
+ rsmi_decorated = self._decorate_radical(rsmi, invert)
434
+ if rsmi_decorated is None:
435
+ self._record_reaction_failure(
436
+ FusionIssueCode.POSTPROCESS_FAILED,
437
+ "Could not decorate a rule-application graph with typed ports.",
438
+ stage=stage,
439
+ source=f"{stage}_application",
440
+ context={"invert": invert},
441
+ )
442
+ continue
443
+
444
+ its_back = self._safe_rsmi_to_its(rsmi_decorated)
445
+ if its_back is not None:
446
+ application_provenance = its_graph.graph.get("application_provenance")
447
+ if application_provenance is not None:
448
+ its_back.graph["application_provenance"] = copy.deepcopy(
449
+ application_provenance
450
+ )
451
+ its_back.graph["application_direction"] = stage
452
+ its_back = self._annotate_wildcard_roles(
453
+ its_back,
454
+ WildcardRole.RADICAL_COMPLETION,
455
+ )
456
+ out.append(its_back)
457
+ else:
458
+ self._record_reaction_failure(
459
+ FusionIssueCode.POSTPROCESS_FAILED,
460
+ "Could not restore a decorated rule-application graph.",
461
+ stage=stage,
462
+ source=f"{stage}_application",
463
+ context={"invert": invert},
464
+ )
465
+
466
+ self.logger.debug("Reaction produced %d ITS graphs", len(out))
467
+ return out
468
+
469
+ def react(
470
+ self,
471
+ substrate: Union[str, ITSLike],
472
+ pattern: Optional[ITSLike] = None,
473
+ invert: bool = False,
474
+ ) -> Self:
475
+ """Public wrapper around :meth:`_run_reaction` that updates engine state.
476
+
477
+ If ``pattern`` is ``None``, the last prepared template
478
+ (:attr:`template_its`) is used.
479
+
480
+ Results are stored in :attr:`forward_its` (for ``invert=False``)
481
+ or :attr:`backward_its` (for ``invert=True``).
482
+
483
+ :param substrate: Substrate reaction string or ITS-like object.
484
+ :type substrate: str | ITSLike
485
+ :param pattern: Optional template ITS; if ``None``, use
486
+ :attr:`template_its`.
487
+ :type pattern: ITSLike or None
488
+ :param invert: If ``True``, store results as backward ITS.
489
+ :type invert: bool
490
+ :return: The current engine instance.
491
+ :rtype: RBLEngine
492
+ :raises ValueError: If no template pattern was provided or prepared.
493
+ """
494
+ if pattern is None:
495
+ pattern = self._template_its
496
+ if pattern is None:
497
+ raise ValueError("No template pattern provided or prepared.")
498
+
499
+ its_list = self._run_reaction(substrate, pattern, invert=invert)
500
+
501
+ if invert:
502
+ self._backward_its = its_list
503
+ else:
504
+ self._forward_its = its_list
505
+
506
+ return self
@@ -0,0 +1,169 @@
1
+ """Read-only run-state views for the RBL engine."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ from synkit.Graph.Fusion import (
8
+ FUSION_PROOF_SCHEMA,
9
+ FusionCandidate,
10
+ graph_identity_digest,
11
+ )
12
+ from synkit.Synthesis.RBL.policy import (
13
+ AcceptanceTask,
14
+ SearchOutcomeStatus,
15
+ )
16
+ from synkit.Synthesis.RBL.proof import RBL_PROOF_SCHEMA
17
+
18
+ ITSLike = Any
19
+ RBL_RESULT_SCHEMA = "synkit.rbl-result/2"
20
+
21
+
22
+ class RBLStateMixin:
23
+ """Expose defensive copies of RBL outputs and diagnostics."""
24
+
25
+ @property
26
+ def template_its(self) -> Optional[ITSLike]:
27
+ """Return the standardized representation of the prepared template."""
28
+ return self._template_its
29
+
30
+ @property
31
+ def forward_its(self) -> List[ITSLike]:
32
+ """Return ITS graphs from the last forward application."""
33
+ return list(self._forward_its)
34
+
35
+ @property
36
+ def backward_its(self) -> List[ITSLike]:
37
+ """Return ITS graphs from the last backward application."""
38
+ return list(self._backward_its)
39
+
40
+ @property
41
+ def fused_its(self) -> List[ITSLike]:
42
+ """Return accepted fused ITS graphs."""
43
+ return list(self._fused_its)
44
+
45
+ @property
46
+ def fused_rsmis(self) -> List[str]:
47
+ """Return accepted fused reaction SMILES."""
48
+ return list(self._fused_rsmis)
49
+
50
+ @property
51
+ def fusion_candidates(self) -> List[FusionCandidate]:
52
+ """Return proof-bearing candidates from the fusion stage."""
53
+ return list(self._fusion_candidates)
54
+
55
+ @property
56
+ def rbl_proofs(self) -> List[Any]:
57
+ """Return independently replayable RBL certificates."""
58
+ return list(self._rbl_proofs)
59
+
60
+ @property
61
+ def last_reaction(self) -> Optional[str]:
62
+ """Return the last processed reaction, if any."""
63
+ return self._last_reaction
64
+
65
+ @property
66
+ def result(self) -> Dict[str, Any]:
67
+ """Return the stable result and termination record for the last run."""
68
+ search = dict(self._fusion_search_stats)
69
+ complete = bool(search.get("complete", False))
70
+ incomplete_reasons = list(search.get("incomplete_reasons", ()))
71
+ if self._fused_rsmis:
72
+ status = SearchOutcomeStatus.FOUND
73
+ elif complete:
74
+ status = SearchOutcomeStatus.PROVED_NONE
75
+ elif search.get("operational_failures", 0) or any(
76
+ issue.get("code")
77
+ in {
78
+ "FUSION_OPERATION_FAILED",
79
+ "FUSION_SERIALIZATION_FAILED",
80
+ }
81
+ for reports in self._diagnostics.values()
82
+ for report in reports
83
+ for issue in report.get("issues", ())
84
+ ) and not search:
85
+ status = SearchOutcomeStatus.ERROR
86
+ else:
87
+ status = SearchOutcomeStatus.INCOMPLETE
88
+
89
+ strict = (
90
+ self._active_search_policy.acceptance_task
91
+ is AcceptanceTask.STRICT_RECONSTRUCTION
92
+ )
93
+ acceptance_policy = (
94
+ {
95
+ "task": "strict_reconstruction",
96
+ "relation": "exact_component_multiset_inclusion",
97
+ "preserve_original_sides": ["reactants", "products"],
98
+ "conservation_boundary": self.conservation_boundary,
99
+ "environment_delta": dict(self.environment_delta),
100
+ "require_mapped_material": True,
101
+ "use_chirality": True,
102
+ }
103
+ if strict
104
+ else {
105
+ "task": "compatibility",
106
+ "preserve_original_sides": list(self.preserve_original_sides),
107
+ "relation": "component_injective_subgraph",
108
+ "use_chirality": True,
109
+ }
110
+ )
111
+ proof_digests_by_outcome: dict[str, list[str]] = {}
112
+ for proof in self._rbl_proofs:
113
+ payload = proof.to_dict()
114
+ proof_digests_by_outcome.setdefault(proof.final_digest, []).append(
115
+ payload["document_digest"]
116
+ )
117
+ outcomes = []
118
+ for index, rsmi in enumerate(self._fused_rsmis):
119
+ signature = (
120
+ graph_identity_digest(self._fused_its[index])
121
+ if index < len(self._fused_its)
122
+ else None
123
+ )
124
+ proof_digests = (
125
+ sorted(proof_digests_by_outcome.get(signature, ()))
126
+ if signature is not None
127
+ else []
128
+ )
129
+ outcomes.append(
130
+ {
131
+ "rsmi": rsmi,
132
+ "canonical_signature": signature,
133
+ "proof_digests": proof_digests,
134
+ "proof_count": len(proof_digests),
135
+ }
136
+ )
137
+ return {
138
+ "schema": RBL_RESULT_SCHEMA,
139
+ "fused_rsmis": list(self._fused_rsmis),
140
+ "mode": self._last_stop_mode,
141
+ "reason": self._last_stop_reason,
142
+ "metadata": dict(self._last_stop_metadata),
143
+ "n_forward_its": len(self._forward_its),
144
+ "n_backward_its": len(self._backward_its),
145
+ "n_fused_its": len(self._fused_its),
146
+ "fusion_proof_schema": FUSION_PROOF_SCHEMA,
147
+ "rbl_proof_schema": RBL_PROOF_SCHEMA,
148
+ "fusion_candidates": [
149
+ candidate.to_dict() for candidate in self._fusion_candidates
150
+ ],
151
+ "rbl_proofs": [proof.to_dict() for proof in self._rbl_proofs],
152
+ "outcomes": outcomes,
153
+ "fusion_search": search,
154
+ "search_status": status.value,
155
+ "complete": complete,
156
+ "reason_incomplete": incomplete_reasons,
157
+ "verified_fusion_mode": self.verified_mode,
158
+ "search_policy": self._active_search_policy.to_dict(),
159
+ "acceptance_policy": acceptance_policy,
160
+ "diagnostics": self.diagnostics,
161
+ }
162
+
163
+ @property
164
+ def diagnostics(self) -> Dict[str, List[Dict[str, Any]]]:
165
+ """Return diagnostics grouped by reactor stage."""
166
+ return {stage: list(reports) for stage, reports in self._diagnostics.items()}
167
+
168
+
169
+ __all__ = ["RBL_RESULT_SCHEMA", "RBLStateMixin"]