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,796 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Dict, Iterable, List, Mapping, Optional, Set, Tuple
5
+
6
+ import json
7
+
8
+ from ._adapter import tokenize_syncrn_incidence
9
+
10
+ Multiset = Dict[str, int]
11
+
12
+
13
+ @dataclass
14
+ class ReachabilityConfig:
15
+ """Configuration for forward reachability traversal.
16
+
17
+ This configuration controls how many propagation layers are explored and
18
+ whether the traversal should terminate as soon as no newly reachable
19
+ species are discovered.
20
+
21
+ :param max_layers:
22
+ Maximum number of forward layers to compute.
23
+ :type max_layers: int
24
+ :param stop_when_no_new_species:
25
+ If ``True``, stop once a layer produces no newly reachable species,
26
+ even if reactions are still enabled.
27
+ :type stop_when_no_new_species: bool
28
+
29
+ .. rubric:: Example
30
+
31
+ .. code-block:: python
32
+
33
+ cfg = ReachabilityConfig(
34
+ max_layers=100,
35
+ stop_when_no_new_species=True,
36
+ )
37
+ """
38
+
39
+ max_layers: int = 10_000
40
+ stop_when_no_new_species: bool = True
41
+
42
+
43
+ @dataclass
44
+ class ReachabilityLayer:
45
+ """One forward reachability layer.
46
+
47
+ A layer summarizes what became newly active at a given traversal depth.
48
+ In set semantics, a reaction is considered enabled when all of its
49
+ reactant species are already reachable. In multiset semantics, a reaction
50
+ is enabled when the current marking contains sufficient multiplicity for
51
+ each reactant.
52
+
53
+ :param depth:
54
+ Layer index, starting at ``1`` for the first propagation step.
55
+ :type depth: int
56
+ :param newly_enabled_reactions:
57
+ Reaction identifiers that became enabled at this layer.
58
+ :type newly_enabled_reactions: List[str]
59
+ :param newly_reachable_species:
60
+ Species that became reachable for the first time at this layer.
61
+ :type newly_reachable_species: List[str]
62
+ :param all_reachable_species:
63
+ Complete set of reachable species after this layer is applied.
64
+ :type all_reachable_species: List[str]
65
+
66
+ .. rubric:: Example
67
+
68
+ .. code-block:: python
69
+
70
+ layer = ReachabilityLayer(
71
+ depth=2,
72
+ newly_enabled_reactions=["r3", "r4"],
73
+ newly_reachable_species=["E", "F"],
74
+ all_reachable_species=["A", "B", "C", "D", "E", "F"],
75
+ )
76
+ """
77
+
78
+ depth: int
79
+ newly_enabled_reactions: List[str]
80
+ newly_reachable_species: List[str]
81
+ all_reachable_species: List[str]
82
+
83
+
84
+ @dataclass
85
+ class ReachabilityResult:
86
+ """Container for forward reachability results.
87
+
88
+ This object stores the initial support, the layered traversal trace, and
89
+ the first depth at which each species or reaction became reachable or
90
+ enabled.
91
+
92
+ :param initial_species:
93
+ Initial reachable species support used to seed the traversal.
94
+ :type initial_species: List[str]
95
+ :param layers:
96
+ Ordered list of reachability layers.
97
+ :type layers: List[ReachabilityLayer]
98
+ :param species_first_depth:
99
+ Mapping from species identifier to the first layer depth at which that
100
+ species became reachable. Initial species are assigned depth ``0``.
101
+ :type species_first_depth: Dict[str, int]
102
+ :param reaction_first_depth:
103
+ Mapping from reaction identifier to the first layer depth at which that
104
+ reaction became enabled.
105
+ :type reaction_first_depth: Dict[str, int]
106
+
107
+ .. rubric:: Example
108
+
109
+ .. code-block:: python
110
+
111
+ result = ReachabilityResult(
112
+ initial_species=["A", "B"],
113
+ layers=[],
114
+ species_first_depth={"A": 0, "B": 0},
115
+ reaction_first_depth={},
116
+ )
117
+ """
118
+
119
+ initial_species: List[str]
120
+ layers: List[ReachabilityLayer]
121
+ species_first_depth: Dict[str, int]
122
+ reaction_first_depth: Dict[str, int]
123
+
124
+
125
+ class PathwayReachability:
126
+ """Forward reachability utilities for SynCRN pathway analysis.
127
+
128
+ This class provides layered forward propagation over a reaction hypergraph.
129
+ It supports two related semantics:
130
+
131
+ - **Set semantics**:
132
+ a reaction is enabled once all reactant species are present in the
133
+ reachable set; stoichiometric multiplicities are ignored for enabling.
134
+ - **Multiset semantics**:
135
+ a reaction is enabled only when the current marking contains enough
136
+ copies of each reactant species to satisfy stoichiometric coefficients.
137
+
138
+ The class can be loaded either from tokenized ``vertices`` / ``edges``
139
+ data or directly from a SynCRN-like object via :meth:`load_syncrn`.
140
+
141
+ Internally, reactions are stored as a mapping
142
+
143
+ ``reaction_id -> (tail_multiset, head_multiset)``
144
+
145
+ where the tail is the reactant multiset and the head is the product
146
+ multiset.
147
+
148
+ :param config:
149
+ Optional reachability configuration. If omitted, a default
150
+ :class:`ReachabilityConfig` is used.
151
+ :type config: Optional[ReachabilityConfig]
152
+
153
+ .. rubric:: Example
154
+
155
+ .. code-block:: python
156
+
157
+ rr = PathwayReachability()
158
+ rr.load_hypergraph(
159
+ vertices=["A", "B", "C", "D"],
160
+ edges={
161
+ "r1": ({"A": 1, "B": 1}, {"C": 1}),
162
+ "r2": ({"C": 1}, {"D": 1}),
163
+ },
164
+ )
165
+
166
+ result = rr.compute_layers_set(initial_species=["A", "B"])
167
+ print(result.species_first_depth)
168
+ # {'A': 0, 'B': 0, 'C': 1, 'D': 2}
169
+ """
170
+
171
+ def __init__(self, config: Optional[ReachabilityConfig] = None) -> None:
172
+ """
173
+ Initialize an empty reachability engine.
174
+
175
+ :param config:
176
+ Optional reachability configuration.
177
+ :type config: Optional[ReachabilityConfig]
178
+ """
179
+ self.vertices: Set[str] = set()
180
+ self.edges: Dict[str, Tuple[Multiset, Multiset]] = {}
181
+ self.species_token_to_id: Dict[str, str] = {}
182
+ self.reaction_token_to_id: Dict[str, str] = {}
183
+ self.species_id_to_token: Dict[str, str] = {}
184
+ self.reaction_id_to_token: Dict[str, str] = {}
185
+ self._config = config or ReachabilityConfig()
186
+
187
+ # ------------------------------------------------------------------
188
+ # data loading
189
+ # ------------------------------------------------------------------
190
+
191
+ def load_hypergraph(
192
+ self,
193
+ vertices: Iterable[str],
194
+ edges: Mapping[str, Tuple[Mapping[str, int], Mapping[str, int]]],
195
+ ) -> "PathwayReachability":
196
+ """Load a tokenized reaction hypergraph directly.
197
+
198
+ Each edge must map a reaction identifier to a pair
199
+ ``(tail_multiset, head_multiset)``, where both multisets map species
200
+ identifiers to strictly positive stoichiometric coefficients.
201
+
202
+ Non-positive coefficients are discarded during normalization.
203
+
204
+ :param vertices:
205
+ Iterable of species identifiers.
206
+ :type vertices: Iterable[str]
207
+ :param edges:
208
+ Mapping from reaction identifier to
209
+ ``(reactant_multiset, product_multiset)``.
210
+ :type edges: Mapping[str, Tuple[Mapping[str, int], Mapping[str, int]]]
211
+ :return:
212
+ The current instance, to allow fluent chaining.
213
+ :rtype: PathwayReachability
214
+
215
+ .. rubric:: Example
216
+
217
+ .. code-block:: python
218
+
219
+ rr = PathwayReachability().load_hypergraph(
220
+ vertices=["A", "B", "C"],
221
+ edges={
222
+ "r1": ({"A": 1}, {"B": 1}),
223
+ "r2": ({"B": 1}, {"C": 1}),
224
+ },
225
+ )
226
+ """
227
+ self.vertices = set(str(v) for v in vertices)
228
+ self.edges = {
229
+ str(eid): (
230
+ {str(s): int(v) for s, v in tail.items() if int(v) > 0},
231
+ {str(s): int(v) for s, v in head.items() if int(v) > 0},
232
+ )
233
+ for eid, (tail, head) in edges.items()
234
+ }
235
+ self.species_token_to_id = {v: v for v in self.vertices}
236
+ self.reaction_token_to_id = {eid: eid for eid in self.edges}
237
+ self.species_id_to_token = {v: v for v in self.vertices}
238
+ self.reaction_id_to_token = {eid: eid for eid in self.edges}
239
+ return self
240
+
241
+ def load_syncrn(
242
+ self,
243
+ crn: object,
244
+ *,
245
+ species: str = "label",
246
+ reaction: str = "id",
247
+ ) -> "PathwayReachability":
248
+ """Load reachability data from a SynCRN-like object.
249
+
250
+ This method delegates tokenization to
251
+ :func:`tokenize_syncrn_incidence`, then stores both the tokenized
252
+ hypergraph and the forward/backward token-id lookup tables.
253
+
254
+ :param crn:
255
+ SynCRN-like object to tokenize.
256
+ :type crn: object
257
+ :param species:
258
+ Species attribute used during tokenization, such as ``"label"`` or
259
+ another species node attribute supported by the adapter.
260
+ :type species: str
261
+ :param reaction:
262
+ Reaction attribute used during tokenization, such as ``"id"``.
263
+ :type reaction: str
264
+ :return:
265
+ The current instance, to allow fluent chaining.
266
+ :rtype: PathwayReachability
267
+
268
+ .. rubric:: Example
269
+
270
+ .. code-block:: python
271
+
272
+ rr = PathwayReachability().load_syncrn(
273
+ syncrn_object,
274
+ species="label",
275
+ reaction="id",
276
+ )
277
+ """
278
+ vertices, edges, species_token_to_id, reaction_token_to_id, _ = (
279
+ tokenize_syncrn_incidence(
280
+ crn,
281
+ species=species,
282
+ reaction=reaction,
283
+ )
284
+ )
285
+ self.load_hypergraph(vertices, edges)
286
+ self.species_token_to_id = species_token_to_id
287
+ self.reaction_token_to_id = reaction_token_to_id
288
+ self.species_id_to_token = {
289
+ sid: tok for tok, sid in species_token_to_id.items()
290
+ }
291
+ self.reaction_id_to_token = {
292
+ rid: tok for tok, rid in reaction_token_to_id.items()
293
+ }
294
+ return self
295
+
296
+ # ------------------------------------------------------------------
297
+ # small helpers
298
+ # ------------------------------------------------------------------
299
+
300
+ @staticmethod
301
+ def _normalize_marking(marking: Mapping[str, int]) -> Dict[str, int]:
302
+ """Normalize a marking into a clean positive multiset.
303
+
304
+ Species with non-positive counts are removed.
305
+
306
+ :param marking:
307
+ Input species-count mapping.
308
+ :type marking: Mapping[str, int]
309
+ :return:
310
+ Normalized marking containing only strictly positive counts.
311
+ :rtype: Dict[str, int]
312
+ """
313
+ return {str(s): int(v) for s, v in marking.items() if int(v) > 0}
314
+
315
+ @staticmethod
316
+ def _support(marking: Mapping[str, int]) -> Set[str]:
317
+ """Return the support of a marking.
318
+
319
+ The support is the set of species whose count is strictly positive.
320
+
321
+ :param marking:
322
+ Input species-count mapping.
323
+ :type marking: Mapping[str, int]
324
+ :return:
325
+ Set of species present with positive multiplicity.
326
+ :rtype: Set[str]
327
+ """
328
+ return {s for s, v in marking.items() if int(v) > 0}
329
+
330
+ def _enabled_reactions_for_set(self, reachable: Set[str]) -> List[str]:
331
+ """Return reactions enabled under set semantics.
332
+
333
+ A reaction is enabled if all reactant species are already members of
334
+ the reachable set. Stoichiometric coefficients are ignored for the
335
+ enabling condition.
336
+
337
+ :param reachable:
338
+ Current reachable species set.
339
+ :type reachable: Set[str]
340
+ :return:
341
+ List of enabled reaction identifiers.
342
+ :rtype: List[str]
343
+ """
344
+ enabled: List[str] = []
345
+ for eid, (tail, _) in self.edges.items():
346
+ if set(tail.keys()).issubset(reachable):
347
+ enabled.append(eid)
348
+ return enabled
349
+
350
+ def _enabled_reactions_for_marking(self, marking: Mapping[str, int]) -> List[str]:
351
+ """Return reactions enabled under multiset semantics.
352
+
353
+ A reaction is enabled if the marking contains at least the required
354
+ stoichiometric coefficient for every reactant species.
355
+
356
+ :param marking:
357
+ Current species marking.
358
+ :type marking: Mapping[str, int]
359
+ :return:
360
+ List of enabled reaction identifiers.
361
+ :rtype: List[str]
362
+ """
363
+ enabled: List[str] = []
364
+ for eid, (tail, _) in self.edges.items():
365
+ if all(int(marking.get(s, 0)) >= int(coeff) for s, coeff in tail.items()):
366
+ enabled.append(eid)
367
+ return enabled
368
+
369
+ def _fire_reactions_once_in_batch(
370
+ self,
371
+ marking: Mapping[str, int],
372
+ reaction_ids: Iterable[str],
373
+ ) -> Dict[str, int]:
374
+ """Fire a batch of reactions once each from the given marking.
375
+
376
+ Reactants are consumed and products are produced exactly once for each
377
+ reaction in ``reaction_ids``. The resulting marking is normalized so
378
+ that non-positive counts are removed.
379
+
380
+ :param marking:
381
+ Input marking before firing.
382
+ :type marking: Mapping[str, int]
383
+ :param reaction_ids:
384
+ Iterable of reaction identifiers to fire once.
385
+ :type reaction_ids: Iterable[str]
386
+ :return:
387
+ Updated normalized marking after the batch firing.
388
+ :rtype: Dict[str, int]
389
+ """
390
+ nxt = dict(marking)
391
+ for eid in reaction_ids:
392
+ tail, head = self.edges[eid]
393
+ for s, coeff in tail.items():
394
+ nxt[s] = nxt.get(s, 0) - int(coeff)
395
+ for s, coeff in head.items():
396
+ nxt[s] = nxt.get(s, 0) + int(coeff)
397
+ return self._normalize_marking(nxt)
398
+
399
+ @staticmethod
400
+ def _update_first_depth_maps(
401
+ *,
402
+ depth: int,
403
+ enabled_now: Iterable[str],
404
+ new_species: Set[str],
405
+ seen_reactions: Set[str],
406
+ reaction_first_depth: Dict[str, int],
407
+ species_first_depth: Dict[str, int],
408
+ ) -> None:
409
+ """
410
+ Update first-seen depth maps for reactions and species.
411
+
412
+ Reactions are assigned a first depth only once, when first observed as
413
+ enabled. Species are assigned a first depth when they first appear in
414
+ the newly reached set.
415
+
416
+ :param depth:
417
+ Current traversal depth.
418
+ :type depth: int
419
+ :param enabled_now:
420
+ Reactions enabled at the current depth.
421
+ :type enabled_now: Iterable[str]
422
+ :param new_species:
423
+ Species that became newly reachable at the current depth.
424
+ :type new_species: Set[str]
425
+ :param seen_reactions:
426
+ Mutable set of reactions already assigned a first depth.
427
+ :type seen_reactions: Set[str]
428
+ :param reaction_first_depth:
429
+ Output mapping from reaction identifier to first enabling depth.
430
+ :type reaction_first_depth: Dict[str, int]
431
+ :param species_first_depth:
432
+ Output mapping from species identifier to first reachability depth.
433
+ :type species_first_depth: Dict[str, int]
434
+ """
435
+ for eid in enabled_now:
436
+ if eid not in seen_reactions:
437
+ reaction_first_depth[eid] = depth
438
+ seen_reactions.add(eid)
439
+ for s in sorted(new_species):
440
+ species_first_depth[s] = depth
441
+
442
+ # ------------------------------------------------------------------
443
+ # main computations
444
+ # ------------------------------------------------------------------
445
+
446
+ def compute_layers_set(
447
+ self,
448
+ initial_species: Iterable[str],
449
+ max_layers: Optional[int] = None,
450
+ ) -> ReachabilityResult:
451
+ """Compute layered forward reachability in set semantics.
452
+
453
+ In this mode, only species presence matters. Stoichiometric
454
+ multiplicities are ignored when deciding whether a reaction is enabled.
455
+
456
+ The algorithm proceeds layer by layer:
457
+ 1. find all reactions whose reactants are already reachable,
458
+ 2. keep only reactions not previously seen as enabled,
459
+ 3. collect all products they produce,
460
+ 4. add any newly produced species to the reachable set.
461
+
462
+ :param initial_species:
463
+ Initial set of reachable species.
464
+ :type initial_species: Iterable[str]
465
+ :param max_layers:
466
+ Optional layer cap overriding the instance configuration.
467
+ :type max_layers: Optional[int]
468
+ :return:
469
+ Reachability result containing the full layered traversal trace.
470
+ :rtype: ReachabilityResult
471
+
472
+ .. rubric:: Example
473
+
474
+ .. code-block:: python
475
+
476
+ rr = PathwayReachability().load_hypergraph(
477
+ vertices=["A", "B", "C", "D"],
478
+ edges={
479
+ "r1": ({"A": 1, "B": 1}, {"C": 1}),
480
+ "r2": ({"C": 1}, {"D": 1}),
481
+ },
482
+ )
483
+
484
+ result = rr.compute_layers_set(initial_species=["A", "B"])
485
+
486
+ for layer in result.layers:
487
+ print(layer.depth, layer.newly_reachable_species)
488
+
489
+ # 1 ['C']
490
+ # 2 ['D']
491
+ """
492
+ max_layers = max_layers if max_layers is not None else self._config.max_layers
493
+ reachable: Set[str] = {str(x) for x in initial_species}
494
+ seen_reactions: Set[str] = set()
495
+
496
+ species_first_depth: Dict[str, int] = {s: 0 for s in reachable}
497
+ reaction_first_depth: Dict[str, int] = {}
498
+ layers: List[ReachabilityLayer] = []
499
+
500
+ for depth in range(1, max_layers + 1):
501
+ newly_enabled = [
502
+ eid
503
+ for eid in self._enabled_reactions_for_set(reachable)
504
+ if eid not in seen_reactions
505
+ ]
506
+ produced_this_round: Set[str] = set()
507
+ for eid in newly_enabled:
508
+ _, head = self.edges[eid]
509
+ produced_this_round.update(head.keys())
510
+ new_species = produced_this_round - reachable
511
+
512
+ if not newly_enabled and not new_species:
513
+ break
514
+
515
+ self._update_first_depth_maps(
516
+ depth=depth,
517
+ enabled_now=newly_enabled,
518
+ new_species=new_species,
519
+ seen_reactions=seen_reactions,
520
+ reaction_first_depth=reaction_first_depth,
521
+ species_first_depth=species_first_depth,
522
+ )
523
+ reachable.update(new_species)
524
+
525
+ layers.append(
526
+ ReachabilityLayer(
527
+ depth=depth,
528
+ newly_enabled_reactions=sorted(newly_enabled),
529
+ newly_reachable_species=sorted(new_species),
530
+ all_reachable_species=sorted(reachable),
531
+ )
532
+ )
533
+
534
+ if self._config.stop_when_no_new_species and not new_species:
535
+ break
536
+
537
+ return ReachabilityResult(
538
+ initial_species=sorted(set(str(x) for x in initial_species)),
539
+ layers=layers,
540
+ species_first_depth=species_first_depth,
541
+ reaction_first_depth=reaction_first_depth,
542
+ )
543
+
544
+ def compute_layers_multiset(
545
+ self,
546
+ initial_marking: Mapping[str, int],
547
+ max_layers: Optional[int] = None,
548
+ ) -> ReachabilityResult:
549
+ """Compute layered forward reachability in multiset semantics.
550
+
551
+ In this mode, species counts matter. A reaction is enabled only if the
552
+ current marking contains enough multiplicity for every reactant. At
553
+ each layer, all currently enabled reactions are fired once in batch.
554
+
555
+ This procedure is useful when approximate token-flow behavior is
556
+ desired, but it should not be confused with exhaustive Petri-net
557
+ reachability analysis over all possible firing sequences.
558
+
559
+ :param initial_marking:
560
+ Initial species marking.
561
+ :type initial_marking: Mapping[str, int]
562
+ :param max_layers:
563
+ Optional layer cap overriding the instance configuration.
564
+ :type max_layers: Optional[int]
565
+ :return:
566
+ Reachability result containing the full layered traversal trace.
567
+ :rtype: ReachabilityResult
568
+
569
+ .. rubric:: Example
570
+
571
+ .. code-block:: python
572
+
573
+ rr = PathwayReachability().load_hypergraph(
574
+ vertices=["A", "B", "C"],
575
+ edges={
576
+ "r1": ({"A": 2}, {"B": 1}),
577
+ "r2": ({"B": 1}, {"C": 1}),
578
+ },
579
+ )
580
+
581
+ result = rr.compute_layers_multiset(initial_marking={"A": 2})
582
+
583
+ print(result.species_first_depth)
584
+ # {'A': 0, 'B': 1, 'C': 2}
585
+ """
586
+ max_layers = max_layers if max_layers is not None else self._config.max_layers
587
+ marking = self._normalize_marking(initial_marking)
588
+ support = self._support(marking)
589
+
590
+ species_first_depth: Dict[str, int] = {s: 0 for s in support}
591
+ reaction_first_depth: Dict[str, int] = {}
592
+ layers: List[ReachabilityLayer] = []
593
+ seen_reactions: Set[str] = set()
594
+
595
+ for depth in range(1, max_layers + 1):
596
+ enabled_now = self._enabled_reactions_for_marking(marking)
597
+ if not enabled_now:
598
+ break
599
+
600
+ next_marking = self._fire_reactions_once_in_batch(marking, enabled_now)
601
+ next_support = self._support(next_marking)
602
+ new_species = next_support - support
603
+
604
+ self._update_first_depth_maps(
605
+ depth=depth,
606
+ enabled_now=enabled_now,
607
+ new_species=new_species,
608
+ seen_reactions=seen_reactions,
609
+ reaction_first_depth=reaction_first_depth,
610
+ species_first_depth=species_first_depth,
611
+ )
612
+
613
+ layers.append(
614
+ ReachabilityLayer(
615
+ depth=depth,
616
+ newly_enabled_reactions=sorted(enabled_now),
617
+ newly_reachable_species=sorted(new_species),
618
+ all_reachable_species=sorted(next_support),
619
+ )
620
+ )
621
+
622
+ marking = next_marking
623
+ support = next_support
624
+
625
+ if self._config.stop_when_no_new_species and not new_species:
626
+ break
627
+
628
+ return ReachabilityResult(
629
+ initial_species=sorted(
630
+ self._support(self._normalize_marking(initial_marking))
631
+ ),
632
+ layers=layers,
633
+ species_first_depth=species_first_depth,
634
+ reaction_first_depth=reaction_first_depth,
635
+ )
636
+
637
+ # ------------------------------------------------------------------
638
+ # export helpers
639
+ # ------------------------------------------------------------------
640
+
641
+ def export_layers_json(
642
+ self, result: ReachabilityResult, fn: str
643
+ ) -> "PathwayReachability":
644
+ """Export a reachability result to a JSON file.
645
+
646
+ The output contains the initial species set, first-depth maps, and the
647
+ full list of layered traversal records.
648
+
649
+ :param result:
650
+ Reachability result to serialize.
651
+ :type result: ReachabilityResult
652
+ :param fn:
653
+ Output JSON filename.
654
+ :type fn: str
655
+ :return:
656
+ The current instance, to allow fluent chaining.
657
+ :rtype: PathwayReachability
658
+ :raises OSError:
659
+ Raised if the target file cannot be written.
660
+
661
+ .. rubric:: Example
662
+
663
+ .. code-block:: python
664
+
665
+ rr.export_layers_json(result, "reachability_layers.json")
666
+ """
667
+ data = {
668
+ "initial_species": result.initial_species,
669
+ "species_first_depth": result.species_first_depth,
670
+ "reaction_first_depth": result.reaction_first_depth,
671
+ "layers": [
672
+ {
673
+ "depth": layer.depth,
674
+ "newly_enabled_reactions": layer.newly_enabled_reactions,
675
+ "newly_reachable_species": layer.newly_reachable_species,
676
+ "all_reachable_species": layer.all_reachable_species,
677
+ }
678
+ for layer in result.layers
679
+ ],
680
+ }
681
+ with open(fn, "w") as fh:
682
+ json.dump(data, fh, indent=2)
683
+ return self
684
+
685
+ def __repr__(self) -> str: # pragma: no cover - simple repr
686
+ """Return a compact developer-friendly representation.
687
+
688
+ :return:
689
+ String representation including the number of vertices and edges.
690
+ :rtype: str
691
+ """
692
+ return f"<PathwayReachability vertices={len(self.vertices)} edges={len(self.edges)}>"
693
+
694
+
695
+ def syncrn_to_reachability_inputs(
696
+ crn: object,
697
+ *,
698
+ species: str = "label",
699
+ reaction: str = "id",
700
+ ) -> Tuple[List[str], Dict[str, Tuple[Dict[str, int], Dict[str, int]]]]:
701
+ """Convert a SynCRN-like object into tokenized reachability inputs.
702
+
703
+ This is a lightweight adapter returning only the tokenized species list and
704
+ reaction hyperedges needed by :class:`PathwayReachability`.
705
+
706
+ :param crn:
707
+ SynCRN-like object to tokenize.
708
+ :type crn: object
709
+ :param species:
710
+ Species attribute used during tokenization.
711
+ :type species: str
712
+ :param reaction:
713
+ Reaction attribute used during tokenization.
714
+ :type reaction: str
715
+ :return:
716
+ Pair ``(vertices, edges)`` where ``vertices`` is the species token list
717
+ and ``edges`` maps reaction token to ``(tail_multiset, head_multiset)``.
718
+ :rtype: Tuple[List[str], Dict[str, Tuple[Dict[str, int], Dict[str, int]]]]
719
+
720
+ .. rubric:: Example
721
+
722
+ .. code-block:: python
723
+
724
+ vertices, edges = syncrn_to_reachability_inputs(
725
+ syncrn_object,
726
+ species="label",
727
+ reaction="id",
728
+ )
729
+ """
730
+ vertices, edges, _, _, _ = tokenize_syncrn_incidence(
731
+ crn, species=species, reaction=reaction
732
+ )
733
+ return vertices, edges
734
+
735
+
736
+ def run_reachability_from_syncrn(
737
+ crn: object,
738
+ initial_species: Iterable[str],
739
+ *,
740
+ species: str = "label",
741
+ reaction: str = "id",
742
+ verbose: bool = True,
743
+ ) -> Tuple[PathwayReachability, ReachabilityResult]:
744
+ """Run layered qualitative reachability directly from a SynCRN-like object.
745
+
746
+ This convenience function constructs a :class:`PathwayReachability`
747
+ instance, loads the tokenized SynCRN representation, computes set-based
748
+ layered reachability, and optionally prints a human-readable traversal
749
+ summary.
750
+
751
+ :param crn:
752
+ SynCRN-like object to analyze.
753
+ :type crn: object
754
+ :param initial_species:
755
+ Initial reachable species set.
756
+ :type initial_species: Iterable[str]
757
+ :param species:
758
+ Species attribute used during tokenization.
759
+ :type species: str
760
+ :param reaction:
761
+ Reaction attribute used during tokenization.
762
+ :type reaction: str
763
+ :param verbose:
764
+ If ``True``, print the tokenized edges and each traversal layer.
765
+ :type verbose: bool
766
+ :return:
767
+ Pair ``(reachability_engine, result)``.
768
+ :rtype: Tuple[PathwayReachability, ReachabilityResult]
769
+
770
+ .. rubric:: Example
771
+
772
+ .. code-block:: python
773
+
774
+ rr, result = run_reachability_from_syncrn(
775
+ syncrn_object,
776
+ initial_species=["A", "B"],
777
+ species="label",
778
+ reaction="id",
779
+ verbose=True,
780
+ )
781
+ """
782
+ rr = PathwayReachability().load_syncrn(crn, species=species, reaction=reaction)
783
+ result = rr.compute_layers_set(initial_species=initial_species)
784
+
785
+ if verbose:
786
+ print("Edges:")
787
+ for eid, (t, h) in rr.edges.items():
788
+ print(f" {eid}: {t} >> {h}")
789
+ print("Initial species:", sorted(set(str(x) for x in initial_species)))
790
+ for layer in result.layers:
791
+ print(f"Layer {layer.depth}:")
792
+ print(" Newly enabled reactions:", layer.newly_enabled_reactions)
793
+ print(" Newly reachable species:", layer.newly_reachable_species)
794
+ print(" All reachable species:", layer.all_reachable_species)
795
+
796
+ return rr, result