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,929 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Callable, Dict, Hashable, List, Optional, Tuple
5
+
6
+ import networkx as nx
7
+ import numpy as np
8
+
9
+ from .reaction import Reaction
10
+ from .rule import Rule
11
+ from .species import Species
12
+ from ._graph_io import (
13
+ _add_reaction_edges_to_graph,
14
+ _build_reaction_from_graph_node,
15
+ _build_species_table_from_graph,
16
+ _collect_bipartite_nodes,
17
+ _make_internal_id_maps,
18
+ _reaction_node_attrs,
19
+ _resolve_reaction_node_id,
20
+ _resolve_species_node_id,
21
+ _species_node_attrs,
22
+ _validate_bipartite_node_sets,
23
+ )
24
+ from ._matrices import _dense_from_entries, _sparse_from_entries
25
+ from ._parse import (
26
+ ID_STYLES,
27
+ _build_reactions_from_parsed_strings,
28
+ _build_rules_table_from_reaction_strings,
29
+ _build_species_table_from_labels,
30
+ _default_parse_side_text,
31
+ _mint_reaction_string_ids,
32
+ _parse_reaction_string_entry,
33
+ _species_order_from_parsed_reactions,
34
+ )
35
+
36
+ __all__ = ["ID_STYLES", "SynCRN"]
37
+
38
+
39
+ @dataclass
40
+ class SynCRN:
41
+ """Canonical reaction-system object for SynKit-CRN.
42
+
43
+ Official representations exposed by this object
44
+ ------------------------------------------------
45
+ - ``SynCRN``: master reaction-system object
46
+ - ``SynCRN.to_digraph()``: species--reaction bipartite graph view
47
+ - ``SynCRN.to_stoichiometric_matrices()``: matrix view for stoichiometric analysis
48
+ - ``SynCRN.to_petrinet()``: pre/post incidence view for pathways
49
+ - ``SynCRN.to_equations()``: human-readable reaction list
50
+
51
+ Design notes
52
+ ------------
53
+ Input digraph nodes with ``kind="rule"`` are preserved exactly on round-trip
54
+ via ``Reaction.source_kind`` and ``Reaction.source_attrs``. They are also
55
+ normalized as concrete reaction instances for downstream computation.
56
+
57
+ :param species:
58
+ Mapping from internal species ids to Species records.
59
+ :type species: Dict[str, Species]
60
+
61
+ :param reactions:
62
+ Mapping from internal reaction ids to Reaction records.
63
+ :type reactions: Dict[str, Reaction]
64
+
65
+ :param rules:
66
+ Mapping from internal rule ids to Rule records.
67
+ :type rules: Dict[str, Rule]
68
+
69
+ :param graph_attrs:
70
+ Original graph-level attributes from the input digraph.
71
+ :type graph_attrs: Dict[str, Any]
72
+
73
+ :param metadata:
74
+ Additional canonical metadata for the SynCRN object.
75
+ :type metadata: Dict[str, Any]
76
+
77
+ .. rubric:: Example
78
+
79
+ .. code-block:: python
80
+
81
+ syn = SynCRN.from_reaction_strings(["A>>B", "B>>C"])
82
+ print(syn.n_species)
83
+ print(syn.to_equations())
84
+ """
85
+
86
+ species: Dict[str, Species] = field(default_factory=dict)
87
+ reactions: Dict[str, Reaction] = field(default_factory=dict)
88
+ rules: Dict[str, Rule] = field(default_factory=dict)
89
+ graph_attrs: Dict[str, Any] = field(default_factory=dict)
90
+ metadata: Dict[str, Any] = field(default_factory=dict)
91
+
92
+ @classmethod
93
+ def from_digraph(
94
+ cls,
95
+ crn: nx.DiGraph,
96
+ *,
97
+ species_kind: str = "species",
98
+ reaction_kinds: Tuple[str, ...] = ("reaction", "rule"),
99
+ species_prefix: str = "s_",
100
+ reaction_prefix: str = "r_",
101
+ rule_prefix: str = "rule_",
102
+ strict: bool = True,
103
+ id_style: str = "prefixed",
104
+ ) -> "SynCRN":
105
+ """Build a canonical SynCRN object from a species--reaction bipartite digraph.
106
+
107
+ The current SynKit-CRN graph frequently stores concrete reaction-instance
108
+ nodes with ``kind="rule"``. This constructor preserves that original kind
109
+ in ``Reaction.source_kind`` while also converting such nodes into concrete
110
+ reaction instances for downstream computation.
111
+
112
+ Internal ids follow the same policy as
113
+ :meth:`from_reaction_strings`: ``id_style="prefixed"`` mints ``s_1`` /
114
+ ``r_1`` / ``rule_1``, while ``id_style="numeric"`` mints ``1..n`` for
115
+ species and ``n+1..n+m`` for reactions.
116
+
117
+ :param crn:
118
+ Directed bipartite graph with species and reaction-like nodes.
119
+ :type crn: nx.DiGraph
120
+
121
+ :param species_kind:
122
+ Node-kind value used for species nodes.
123
+ :type species_kind: str
124
+
125
+ :param reaction_kinds:
126
+ Node-kind values used for reaction-instance nodes.
127
+ :type reaction_kinds: Tuple[str, ...]
128
+
129
+ :param species_prefix:
130
+ Prefix for generated internal species ids.
131
+ :type species_prefix: str
132
+
133
+ :param reaction_prefix:
134
+ Prefix for generated internal reaction ids.
135
+ :type reaction_prefix: str
136
+
137
+ :param rule_prefix:
138
+ Prefix for generated internal rule ids.
139
+ :type rule_prefix: str
140
+
141
+ :param strict:
142
+ Whether malformed graph structure should raise an error.
143
+ :type strict: bool
144
+
145
+ :param id_style:
146
+ Internal-id policy, either ``"prefixed"`` (default) or ``"numeric"``.
147
+ :type id_style: str
148
+
149
+ :return:
150
+ Canonical SynCRN object.
151
+ :rtype: SynCRN
152
+
153
+ :raises TypeError:
154
+ If ``crn`` is not an ``nx.DiGraph``.
155
+
156
+ :raises ValueError:
157
+ If ``id_style`` is not a recognised policy.
158
+
159
+ .. rubric:: Example
160
+
161
+ .. code-block:: python
162
+
163
+ syn = SynCRN.from_digraph(crn)
164
+ print(syn.to_equations())
165
+ """
166
+ if not isinstance(crn, nx.DiGraph):
167
+ raise TypeError(f"crn must be nx.DiGraph, got {type(crn).__name__}")
168
+
169
+ species_nodes, reaction_nodes = _collect_bipartite_nodes(
170
+ crn,
171
+ species_kind=species_kind,
172
+ reaction_kinds=reaction_kinds,
173
+ )
174
+ _validate_bipartite_node_sets(
175
+ species_nodes=species_nodes,
176
+ reaction_nodes=reaction_nodes,
177
+ strict=strict,
178
+ )
179
+
180
+ species_node_to_id, reaction_node_to_id = _make_internal_id_maps(
181
+ species_nodes=species_nodes,
182
+ reaction_nodes=reaction_nodes,
183
+ species_prefix=species_prefix,
184
+ reaction_prefix=reaction_prefix,
185
+ id_style=id_style,
186
+ )
187
+
188
+ species = _build_species_table_from_graph(
189
+ crn,
190
+ species_nodes=species_nodes,
191
+ species_node_to_id=species_node_to_id,
192
+ )
193
+
194
+ rules: Dict[str, Rule] = {}
195
+ rule_key_to_id: Dict[Tuple[Optional[int], Optional[str]], str] = {}
196
+ reactions: Dict[str, Reaction] = {}
197
+
198
+ for rnode in reaction_nodes:
199
+ rid = reaction_node_to_id[rnode]
200
+ reactions[rid] = _build_reaction_from_graph_node(
201
+ crn,
202
+ rnode=rnode,
203
+ rid=rid,
204
+ species_node_to_id=species_node_to_id,
205
+ rules=rules,
206
+ rule_key_to_id=rule_key_to_id,
207
+ rule_prefix=rule_prefix,
208
+ strict=strict,
209
+ )
210
+
211
+ return cls(
212
+ species=species,
213
+ reactions=reactions,
214
+ rules=rules,
215
+ graph_attrs=dict(crn.graph),
216
+ metadata={
217
+ "source_graph_type": type(crn).__name__,
218
+ "id_style": id_style,
219
+ },
220
+ )
221
+
222
+ @property
223
+ def species_ids(self) -> List[str]:
224
+ """Return the internal species order.
225
+
226
+ :return:
227
+ Ordered list of species ids.
228
+ :rtype: List[str]
229
+
230
+ .. rubric:: Example
231
+
232
+ .. code-block:: python
233
+
234
+ print(syn.species_ids)
235
+ """
236
+ return list(self.species.keys())
237
+
238
+ @property
239
+ def reaction_ids(self) -> List[str]:
240
+ """Return the internal reaction order.
241
+
242
+ :return:
243
+ Ordered list of reaction ids.
244
+ :rtype: List[str]
245
+
246
+ .. rubric:: Example
247
+
248
+ .. code-block:: python
249
+
250
+ print(syn.reaction_ids)
251
+ """
252
+ return list(self.reactions.keys())
253
+
254
+ @property
255
+ def rule_ids(self) -> List[str]:
256
+ """Return the internal rule order.
257
+
258
+ :return:
259
+ Ordered list of rule ids.
260
+ :rtype: List[str]
261
+
262
+ .. rubric:: Example
263
+
264
+ .. code-block:: python
265
+
266
+ print(syn.rule_ids)
267
+ """
268
+ return list(self.rules.keys())
269
+
270
+ @property
271
+ def n_species(self) -> int:
272
+ """Return the number of species.
273
+
274
+ :return:
275
+ Number of species.
276
+ :rtype: int
277
+ """
278
+ return len(self.species)
279
+
280
+ @property
281
+ def n_reactions(self) -> int:
282
+ """Return the number of reactions.
283
+
284
+ :return:
285
+ Number of reactions.
286
+ :rtype: int
287
+ """
288
+ return len(self.reactions)
289
+
290
+ @property
291
+ def n_rules(self) -> int:
292
+ """Return the number of unique abstract rules.
293
+
294
+ :return:
295
+ Number of rules.
296
+ :rtype: int
297
+ """
298
+ return len(self.rules)
299
+
300
+ def __repr__(self) -> str:
301
+ """Return a compact developer-facing representation.
302
+
303
+ :return:
304
+ Summary representation string.
305
+ :rtype: str
306
+ """
307
+ return (
308
+ f"SynCRN(n_species={self.n_species}, "
309
+ f"n_reactions={self.n_reactions}, n_rules={self.n_rules})"
310
+ )
311
+
312
+ def __str__(self) -> str:
313
+ """Return a human-readable text summary.
314
+
315
+ :return:
316
+ Multiline description string.
317
+ :rtype: str
318
+ """
319
+ return self.describe(include_species=True, species="label")
320
+
321
+ def _species_token(self, species_id: str, mode: str = "label") -> str:
322
+ """Resolve how a species should be displayed.
323
+
324
+ Supported modes are ``"id"``, ``"label"``, ``"smiles"``, and ``"source"``.
325
+
326
+ :param species_id:
327
+ Internal species id.
328
+ :type species_id: str
329
+
330
+ :param mode:
331
+ Species display mode.
332
+ :type mode: str
333
+
334
+ :return:
335
+ Display token for the species.
336
+ :rtype: str
337
+
338
+ :raises ValueError:
339
+ If the display mode is unsupported.
340
+
341
+ .. rubric:: Example
342
+
343
+ .. code-block:: python
344
+
345
+ syn._species_token("1", mode="label")
346
+ """
347
+ sp = self.species[species_id]
348
+ if mode == "id":
349
+ return sp.id
350
+ if mode == "label":
351
+ return sp.label
352
+ if mode == "smiles":
353
+ return sp.smiles or sp.label
354
+ if mode == "source":
355
+ return str(sp.source_node_id)
356
+ raise ValueError("species mode must be one of: id, label, smiles, source")
357
+
358
+ def format_reaction(
359
+ self,
360
+ reaction_id: str,
361
+ *,
362
+ species: str = "label",
363
+ include_id: bool = True,
364
+ include_rule: bool = False,
365
+ include_step: bool = False,
366
+ arrow: str = ">>",
367
+ ) -> str:
368
+ """Format one reaction as text.
369
+
370
+ :param reaction_id:
371
+ Internal reaction id.
372
+ :type reaction_id: str
373
+
374
+ :param species:
375
+ Species display mode.
376
+ :type species: str
377
+
378
+ :param include_id:
379
+ Whether to include the internal reaction id.
380
+ :type include_id: bool
381
+
382
+ :param include_rule:
383
+ Whether to include rule provenance.
384
+ :type include_rule: bool
385
+
386
+ :param include_step:
387
+ Whether to include step provenance.
388
+ :type include_step: bool
389
+
390
+ :param arrow:
391
+ Arrow string between lhs and rhs.
392
+ :type arrow: str
393
+
394
+ :return:
395
+ Human-readable reaction string.
396
+ :rtype: str
397
+
398
+ .. rubric:: Example
399
+
400
+ .. code-block:: python
401
+
402
+ syn.format_reaction("r_1", species="label", include_rule=True)
403
+ """
404
+ rxn = self.reactions[reaction_id]
405
+ return rxn.format(
406
+ lambda sid: self._species_token(sid, species),
407
+ include_id=include_id,
408
+ include_rule=include_rule,
409
+ include_step=include_step,
410
+ arrow=arrow,
411
+ )
412
+
413
+ def to_equations(
414
+ self,
415
+ *,
416
+ species: str = "label",
417
+ include_id: bool = True,
418
+ include_rule: bool = False,
419
+ include_step: bool = False,
420
+ arrow: str = ">>",
421
+ ) -> List[str]:
422
+ """Return the network as a list of formatted reaction equations.
423
+
424
+ :param species:
425
+ Species display mode.
426
+ :type species: str
427
+
428
+ :param include_id:
429
+ Whether to include internal reaction ids.
430
+ :type include_id: bool
431
+
432
+ :param include_rule:
433
+ Whether to include rule provenance.
434
+ :type include_rule: bool
435
+
436
+ :param include_step:
437
+ Whether to include step provenance.
438
+ :type include_step: bool
439
+
440
+ :param arrow:
441
+ Arrow string between lhs and rhs.
442
+ :type arrow: str
443
+
444
+ :return:
445
+ List of formatted reaction equations.
446
+ :rtype: List[str]
447
+
448
+ .. rubric:: Example
449
+
450
+ .. code-block:: python
451
+
452
+ eqs = syn.to_equations(species="smiles", include_rule=True)
453
+ print("\\n".join(eqs))
454
+ """
455
+ return [
456
+ self.format_reaction(
457
+ rid,
458
+ species=species,
459
+ include_id=include_id,
460
+ include_rule=include_rule,
461
+ include_step=include_step,
462
+ arrow=arrow,
463
+ )
464
+ for rid in self.reaction_ids
465
+ ]
466
+
467
+ def describe(
468
+ self,
469
+ *,
470
+ include_species: bool = False,
471
+ species: str = "label",
472
+ ) -> str:
473
+ """Return a human-readable multiline description of the network.
474
+
475
+ :param include_species:
476
+ Whether to append a final line listing species names.
477
+ :type include_species: bool
478
+
479
+ :param species:
480
+ Species display mode used in the text summary.
481
+ :type species: str
482
+
483
+ :return:
484
+ Multiline text description.
485
+ :rtype: str
486
+
487
+ .. rubric:: Example
488
+
489
+ .. code-block:: python
490
+
491
+ print(syn.describe(include_species=True, species="label"))
492
+ """
493
+ lines = [f"SynCRN: {self.n_species} species, {self.n_reactions} reactions"]
494
+
495
+ for rid in self.reaction_ids:
496
+ rxn = self.reactions[rid]
497
+ lhs = rxn.format_side(
498
+ rxn.lhs, lambda sid: self._species_token(sid, species)
499
+ )
500
+ rhs = rxn.format_side(
501
+ rxn.rhs, lambda sid: self._species_token(sid, species)
502
+ )
503
+
504
+ line = f" {rxn.id}: {lhs} >> {rhs}"
505
+ if rxn.rule_index is not None:
506
+ line += f" rule {rxn.rule_index}"
507
+
508
+ lines.append(line)
509
+
510
+ if include_species:
511
+ names = [self._species_token(sid, species) for sid in self.species_ids]
512
+ lines.append("Species: " + ", ".join(names))
513
+
514
+ return "\n".join(lines)
515
+
516
+ def to_dict(self) -> Dict[str, Any]:
517
+ """Return a nested JSON-like dictionary representation.
518
+
519
+ :return:
520
+ Full SynCRN object as a dictionary.
521
+ :rtype: Dict[str, Any]
522
+
523
+ .. rubric:: Example
524
+
525
+ .. code-block:: python
526
+
527
+ data = syn.to_dict()
528
+ print(data["species"].keys())
529
+ """
530
+ return {
531
+ "graph_attrs": dict(self.graph_attrs),
532
+ "metadata": dict(self.metadata),
533
+ "species": {sid: sp.to_dict() for sid, sp in self.species.items()},
534
+ "rules": {rid: rule.to_dict() for rid, rule in self.rules.items()},
535
+ "reactions": {rid: rxn.to_dict() for rid, rxn in self.reactions.items()},
536
+ }
537
+
538
+ def to_stoichiometric_matrices(
539
+ self,
540
+ *,
541
+ sparse: bool = False,
542
+ dtype: Any = None,
543
+ ) -> Dict[str, Any]:
544
+ """Construct stoichiometric matrices in canonical species and reaction order.
545
+
546
+ The returned dictionary contains:
547
+
548
+ - ``species_order``
549
+ - ``reaction_order``
550
+ - ``S_minus``: reactant-incidence matrix
551
+ - ``S_plus``: product-incidence matrix
552
+ - ``S``: net stoichiometric matrix
553
+
554
+ Matrices are ``numpy.ndarray`` of shape ``(n_species, n_reactions)``, so
555
+ they can be handed straight to :mod:`numpy.linalg` and to
556
+ :mod:`synkit.CRN.Props`. Reaction networks are sparse — a species takes
557
+ part in a handful of reactions regardless of network size — so pass
558
+ ``sparse=True`` for large networks to get ``scipy.sparse.csr_array``
559
+ matrices built from the nonzeros only, without ever allocating
560
+ ``n_species x n_reactions`` cells.
561
+
562
+ :param sparse:
563
+ Whether to return ``scipy.sparse.csr_array`` instead of dense
564
+ ``numpy.ndarray`` matrices.
565
+ :type sparse: bool
566
+
567
+ :param dtype:
568
+ Element dtype. Defaults to ``numpy.int64`` when every coefficient is
569
+ integral and ``numpy.float64`` otherwise.
570
+ :type dtype: Any
571
+
572
+ :return:
573
+ Stoichiometric matrix view of the network.
574
+ :rtype: Dict[str, Any]
575
+
576
+ .. rubric:: Example
577
+
578
+ .. code-block:: python
579
+
580
+ mats = syn.to_stoichiometric_matrices()
581
+ print(mats["species_order"])
582
+ print(mats["reaction_order"])
583
+ print(mats["S"])
584
+
585
+ big = syn.to_stoichiometric_matrices(sparse=True)
586
+ print(big["S"].nnz)
587
+ """
588
+ species_order = self.species_ids
589
+ reaction_order = self.reaction_ids
590
+
591
+ n = len(species_order)
592
+ m = len(reaction_order)
593
+
594
+ sidx = {sid: i for i, sid in enumerate(species_order)}
595
+ ridx = {rid: j for j, rid in enumerate(reaction_order)}
596
+
597
+ minus_entries: List[Tuple[int, int, Any]] = []
598
+ plus_entries: List[Tuple[int, int, Any]] = []
599
+
600
+ for rid, rxn in self.reactions.items():
601
+ j = ridx[rid]
602
+ for sid, coeff in rxn.lhs.items():
603
+ minus_entries.append((sidx[sid], j, coeff))
604
+ for sid, coeff in rxn.rhs.items():
605
+ plus_entries.append((sidx[sid], j, coeff))
606
+
607
+ if dtype is None:
608
+ values = [v for _, _, v in minus_entries] + [v for _, _, v in plus_entries]
609
+ integral = all(
610
+ isinstance(v, (int, np.integer)) or float(v).is_integer()
611
+ for v in values
612
+ )
613
+ dtype = np.int64 if integral else np.float64
614
+
615
+ if sparse:
616
+ s_minus = _sparse_from_entries(minus_entries, shape=(n, m), dtype=dtype)
617
+ s_plus = _sparse_from_entries(plus_entries, shape=(n, m), dtype=dtype)
618
+ else:
619
+ s_minus = _dense_from_entries(minus_entries, shape=(n, m), dtype=dtype)
620
+ s_plus = _dense_from_entries(plus_entries, shape=(n, m), dtype=dtype)
621
+
622
+ return {
623
+ "species_order": species_order,
624
+ "reaction_order": reaction_order,
625
+ "S_minus": s_minus,
626
+ "S_plus": s_plus,
627
+ "S": s_plus - s_minus,
628
+ }
629
+
630
+ def to_petrinet(self) -> Dict[str, Any]:
631
+ """Return a Petri-net style pre/post incidence view.
632
+
633
+ The returned dictionary contains:
634
+
635
+ - ``places``: species ids
636
+ - ``transitions``: reaction ids
637
+ - ``pre``: input incidence map
638
+ - ``post``: output incidence map
639
+
640
+ :return:
641
+ Petri-net incidence representation.
642
+ :rtype: Dict[str, Any]
643
+
644
+ .. rubric:: Example
645
+
646
+ .. code-block:: python
647
+
648
+ pn = syn.to_petrinet()
649
+ print(pn["pre"])
650
+ print(pn["post"])
651
+ """
652
+ pre: Dict[str, Dict[str, int]] = {sid: {} for sid in self.species_ids}
653
+ post: Dict[str, Dict[str, int]] = {sid: {} for sid in self.species_ids}
654
+
655
+ for rid, rxn in self.reactions.items():
656
+ for sid, coeff in rxn.lhs.items():
657
+ pre[sid][rid] = coeff
658
+ for sid, coeff in rxn.rhs.items():
659
+ post[sid][rid] = coeff
660
+
661
+ return {
662
+ "places": list(self.species_ids),
663
+ "transitions": list(self.reaction_ids),
664
+ "pre": pre,
665
+ "post": post,
666
+ }
667
+
668
+ def to_digraph(
669
+ self,
670
+ *,
671
+ node_ids: str = "source",
672
+ reaction_kind: Optional[str] = None,
673
+ include_internal_ids: bool = True,
674
+ ) -> nx.DiGraph:
675
+ """Reconstruct a species--reaction bipartite digraph.
676
+
677
+ By default, this method preserves original node ids and original
678
+ reaction-node kinds. This means that input reaction nodes with
679
+ ``kind="rule"`` will still appear as ``kind="rule"`` after round-trip.
680
+
681
+ :param node_ids:
682
+ ``"source"`` preserves original node ids, while ``"internal"``
683
+ uses canonical ids such as ``s_1`` and ``r_1``.
684
+ :type node_ids: str
685
+
686
+ :param reaction_kind:
687
+ Optional override for reconstructed reaction-node kind.
688
+ :type reaction_kind: Optional[str]
689
+
690
+ :param include_internal_ids:
691
+ Whether to attach ``syncrn_id`` and ``source_node_id`` as node attributes.
692
+ :type include_internal_ids: bool
693
+
694
+ :return:
695
+ Reconstructed bipartite digraph.
696
+ :rtype: nx.DiGraph
697
+
698
+ :raises ValueError:
699
+ If ``node_ids`` is not ``"source"`` or ``"internal"``.
700
+
701
+ .. rubric:: Example
702
+
703
+ .. code-block:: python
704
+
705
+ g2 = syn.to_digraph()
706
+ g3 = syn.to_digraph(node_ids="internal", reaction_kind="reaction")
707
+ """
708
+ if node_ids not in {"source", "internal"}:
709
+ raise ValueError("node_ids must be 'source' or 'internal'")
710
+
711
+ g = nx.DiGraph()
712
+ g.graph.update(self.graph_attrs)
713
+ g.graph.update(self.metadata)
714
+
715
+ species_node_map: Dict[str, Hashable] = {}
716
+ reaction_node_map: Dict[str, Hashable] = {}
717
+
718
+ for sid, sp in self.species.items():
719
+ nid = _resolve_species_node_id(sp, node_ids=node_ids)
720
+ g.add_node(
721
+ nid,
722
+ **_species_node_attrs(sp, include_internal_ids=include_internal_ids),
723
+ )
724
+ species_node_map[sid] = nid
725
+
726
+ for rid, rxn in self.reactions.items():
727
+ nid = _resolve_reaction_node_id(rxn, node_ids=node_ids)
728
+ g.add_node(
729
+ nid,
730
+ **_reaction_node_attrs(
731
+ rxn,
732
+ reaction_kind=reaction_kind,
733
+ include_internal_ids=include_internal_ids,
734
+ ),
735
+ )
736
+ reaction_node_map[rid] = nid
737
+
738
+ for rid, rxn in self.reactions.items():
739
+ _add_reaction_edges_to_graph(
740
+ g,
741
+ rxn=rxn,
742
+ reaction_node=reaction_node_map[rid],
743
+ species_node_map=species_node_map,
744
+ )
745
+
746
+ return g
747
+
748
+ @classmethod
749
+ def from_reaction_strings(
750
+ cls,
751
+ rxns: List[str],
752
+ rules: Optional[List[Optional[str]]] = None,
753
+ *,
754
+ parser: Optional[Callable[[str], Any]] = None,
755
+ strict: bool = True,
756
+ id_style: str = "prefixed",
757
+ species_prefix: str = "s_",
758
+ reaction_prefix: str = "r_",
759
+ rule_prefix: str = "rule_",
760
+ ) -> "SynCRN":
761
+ """Build a SynCRN object directly from reaction strings.
762
+
763
+ Reactions and rules are interpreted pairwise, so ``rxns[i]`` corresponds
764
+ to ``rules[i]``.
765
+
766
+ ID policy
767
+ ---------
768
+ By default this constructor mints the same prefixed ids as
769
+ :meth:`from_digraph`, so code written against one constructor works
770
+ against the other:
771
+
772
+ - Species ids: ``"s_1"``, ``"s_2"``, ...
773
+ - Reaction ids: ``"r_1"``, ``"r_2"``, ...
774
+ - Rule ids: ``"rule_1"``, ``"rule_2"``, ...
775
+
776
+ Passing ``id_style="numeric"`` restores the legacy single-namespace
777
+ scheme, where species are ``"1".."n"`` and reaction ids continue at
778
+ ``"n+1"``. That scheme is ambiguous — a species label can coincide with
779
+ a reaction id — and is kept only for backwards compatibility.
780
+
781
+ Species are indexed by first appearance in the input reactions.
782
+
783
+ :param rxns:
784
+ List of reaction strings such as ``"2A>>B+3C"``.
785
+ :type rxns: List[str]
786
+
787
+ :param rules:
788
+ Optional list of rule strings pairwise aligned with ``rxns``.
789
+ :type rules: Optional[List[Optional[str]]]
790
+
791
+ :param parser:
792
+ Optional side parser. It should accept one side string such as
793
+ ``"2A+B"`` and return either a mapping, an object with
794
+ ``to_dict()``, or an object with ``items()``.
795
+ :type parser: Optional[Callable[[str], Any]]
796
+
797
+ :param strict:
798
+ Whether malformed reaction strings or empty sides should raise an error.
799
+ :type strict: bool
800
+
801
+ :param id_style:
802
+ Internal-id policy, either ``"prefixed"`` (default) or ``"numeric"``.
803
+ :type id_style: str
804
+
805
+ :param species_prefix:
806
+ Prefix for generated species ids under ``id_style="prefixed"``.
807
+ :type species_prefix: str
808
+
809
+ :param reaction_prefix:
810
+ Prefix for generated reaction ids under ``id_style="prefixed"``.
811
+ :type reaction_prefix: str
812
+
813
+ :param rule_prefix:
814
+ Prefix for generated rule ids under ``id_style="prefixed"``.
815
+ :type rule_prefix: str
816
+
817
+ :return:
818
+ Canonical SynCRN object.
819
+ :rtype: SynCRN
820
+
821
+ :raises TypeError:
822
+ If ``rxns`` or ``rules`` have invalid types.
823
+
824
+ :raises ValueError:
825
+ If reaction strings are malformed, rules are not pairwise aligned,
826
+ or ``id_style`` is unknown.
827
+
828
+ .. rubric:: Example
829
+
830
+ .. code-block:: python
831
+
832
+ syn = SynCRN.from_reaction_strings(["2A>>B+3C"])
833
+ print(syn.species_ids) # ['s_1', 's_2', 's_3']
834
+ print(syn.reaction_ids) # ['r_1']
835
+
836
+ legacy = SynCRN.from_reaction_strings(["2A>>B+3C"], id_style="numeric")
837
+ print(legacy.species_ids) # ['1', '2', '3']
838
+ """
839
+ if id_style not in ID_STYLES:
840
+ raise ValueError(
841
+ f"id_style must be one of {sorted(ID_STYLES)}, got {id_style!r}"
842
+ )
843
+ if not isinstance(rxns, (list, tuple)) or not all(
844
+ isinstance(x, str) for x in rxns
845
+ ):
846
+ raise TypeError("rxns must be a list or tuple of reaction strings")
847
+
848
+ if len(rxns) == 0:
849
+ return cls(
850
+ species={},
851
+ reactions={},
852
+ rules={},
853
+ graph_attrs={},
854
+ metadata={
855
+ "source": "reaction_strings",
856
+ "n_input_reactions": 0,
857
+ "has_pairwise_rules": False,
858
+ "id_style": id_style,
859
+ },
860
+ )
861
+
862
+ has_rules = rules is not None and len(rules) > 0
863
+ if has_rules:
864
+ if not isinstance(rules, (list, tuple)):
865
+ raise TypeError("rules must be a list or tuple when provided")
866
+ if len(rules) != len(rxns):
867
+ raise ValueError(
868
+ f"rules must be pairwise with rxns: got {len(rxns)} reactions and "
869
+ f"{len(rules)} rules"
870
+ )
871
+ if not all(r is None or isinstance(r, str) for r in rules):
872
+ raise TypeError("rules entries must be strings or None")
873
+
874
+ parse_side = parser if parser is not None else _default_parse_side_text
875
+
876
+ parsed_rxns = [
877
+ _parse_reaction_string_entry(
878
+ rxn_text=rxn_text,
879
+ rxn_index=i,
880
+ rule_repr=rules[i] if has_rules else None,
881
+ has_rules=has_rules,
882
+ parse_side=parse_side,
883
+ strict=strict,
884
+ )
885
+ for i, rxn_text in enumerate(rxns)
886
+ ]
887
+
888
+ species_order = _species_order_from_parsed_reactions(parsed_rxns)
889
+ minted = _mint_reaction_string_ids(
890
+ n_species=len(species_order),
891
+ n_reactions=len(parsed_rxns),
892
+ id_style=id_style,
893
+ species_prefix=species_prefix,
894
+ reaction_prefix=reaction_prefix,
895
+ rule_prefix=rule_prefix,
896
+ )
897
+ label_to_sid = dict(zip(species_order, minted["species_ids"]))
898
+
899
+ species = _build_species_table_from_labels(
900
+ species_order,
901
+ species_ids=minted["species_ids"],
902
+ species_source_ids=minted["species_source_ids"],
903
+ )
904
+ rules_table = _build_rules_table_from_reaction_strings(
905
+ parsed_rxns,
906
+ rule_ids=minted["rule_ids"],
907
+ has_rules=has_rules,
908
+ )
909
+ reactions = _build_reactions_from_parsed_strings(
910
+ parsed_rxns,
911
+ label_to_sid=label_to_sid,
912
+ reaction_ids=minted["reaction_ids"],
913
+ reaction_source_ids=minted["reaction_source_ids"],
914
+ rule_ids=minted["rule_ids"],
915
+ has_rules=has_rules,
916
+ )
917
+
918
+ return cls(
919
+ species=species,
920
+ reactions=reactions,
921
+ rules=rules_table,
922
+ graph_attrs={},
923
+ metadata={
924
+ "source": "reaction_strings",
925
+ "n_input_reactions": len(rxns),
926
+ "has_pairwise_rules": has_rules,
927
+ "id_style": id_style,
928
+ },
929
+ )