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,708 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import defaultdict
4
+ from math import cos, pi, sin
5
+ from typing import Callable, Dict, Hashable, Iterable, List, Mapping, Tuple
6
+
7
+ import networkx as nx
8
+
9
+ from ..kinds import is_reaction_node
10
+
11
+ Pos = Dict[Hashable, Tuple[float, float]]
12
+ LayoutFunc = Callable[..., Pos]
13
+
14
+
15
+ def _sort_nodes(graph: nx.DiGraph, nodes: Iterable[Hashable]) -> List[Hashable]:
16
+ """Return nodes in a deterministic visualization order.
17
+
18
+ Nodes are sorted by selected node attributes and finally by node identifier.
19
+
20
+ :param graph: Directed graph containing node metadata.
21
+ :type graph: nx.DiGraph
22
+ :param nodes: Nodes to sort.
23
+ :type nodes: Iterable[Hashable]
24
+ :return: Sorted node identifiers.
25
+ :rtype: List[Hashable]
26
+ """
27
+ return sorted(
28
+ nodes,
29
+ key=lambda n: (
30
+ graph.nodes[n].get("kind", ""),
31
+ graph.nodes[n].get("step", 10**9),
32
+ graph.nodes[n].get("rule_index", 10**9),
33
+ graph.nodes[n].get("app_index", 10**9),
34
+ str(graph.nodes[n].get("label", "")),
35
+ str(n),
36
+ ),
37
+ )
38
+
39
+
40
+ def _stack_vertical(
41
+ graph: nx.DiGraph,
42
+ nodes: Iterable[Hashable],
43
+ *,
44
+ x: float,
45
+ node_spacing: float,
46
+ ) -> Pos:
47
+ """Place nodes in a vertical stack centered around ``y = 0``.
48
+
49
+ :param graph: Directed graph containing node metadata.
50
+ :type graph: nx.DiGraph
51
+ :param nodes: Nodes to place.
52
+ :type nodes: Iterable[Hashable]
53
+ :param x: Shared x-coordinate for the stack.
54
+ :type x: float
55
+ :param node_spacing: Distance between adjacent nodes along the y-axis.
56
+ :type node_spacing: float
57
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
58
+ :rtype: Pos
59
+ """
60
+ ordered = _sort_nodes(graph, nodes)
61
+ n = len(ordered)
62
+ if n == 0:
63
+ return {}
64
+
65
+ center = (n - 1) / 2.0
66
+ pos: Pos = {}
67
+ for i, node in enumerate(ordered):
68
+ y = (center - i) * node_spacing
69
+ pos[node] = (x, y)
70
+ return pos
71
+
72
+
73
+ def _stack_horizontal(
74
+ graph: nx.DiGraph,
75
+ nodes: Iterable[Hashable],
76
+ *,
77
+ y: float,
78
+ node_spacing: float,
79
+ ) -> Pos:
80
+ """Place nodes in a horizontal stack centered around ``x = 0``.
81
+
82
+ :param graph: Directed graph containing node metadata.
83
+ :type graph: nx.DiGraph
84
+ :param nodes: Nodes to place.
85
+ :type nodes: Iterable[Hashable]
86
+ :param y: Shared y-coordinate for the stack.
87
+ :type y: float
88
+ :param node_spacing: Distance between adjacent nodes along the x-axis.
89
+ :type node_spacing: float
90
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
91
+ :rtype: Pos
92
+ """
93
+ ordered = _sort_nodes(graph, nodes)
94
+ n = len(ordered)
95
+ if n == 0:
96
+ return {}
97
+
98
+ center = (n - 1) / 2.0
99
+ pos: Pos = {}
100
+ for i, node in enumerate(ordered):
101
+ x = (i - center) * node_spacing
102
+ pos[node] = (x, y)
103
+ return pos
104
+
105
+
106
+ def _rule_x_from_step(graph: nx.DiGraph, node: Hashable) -> float:
107
+ """Compute the logical x-layer of a rule node from its step annotation.
108
+
109
+ Rule layers are placed at odd indices: ``1, 3, 5, ...``.
110
+
111
+ :param graph: Directed graph containing node metadata.
112
+ :type graph: nx.DiGraph
113
+ :param node: Rule node identifier.
114
+ :type node: Hashable
115
+ :return: Logical x-layer for the rule node.
116
+ :rtype: float
117
+ """
118
+ step = graph.nodes[node].get("step")
119
+ return float(2 * int(step) - 1) if step is not None else 1.0
120
+
121
+
122
+ def _species_x_from_incidence(graph: nx.DiGraph, node: Hashable) -> float:
123
+ """Infer the logical x-layer of a species node from adjacent rule nodes.
124
+
125
+ The heuristic is:
126
+ - if the species is a reactant of one or more rules, place it immediately
127
+ before the earliest such rule
128
+ - otherwise, if it is a product of one or more rules, place it immediately
129
+ after the earliest such rule
130
+ - otherwise place it at layer ``0``
131
+
132
+ :param graph: Directed graph containing node and edge metadata.
133
+ :type graph: nx.DiGraph
134
+ :param node: Species node identifier.
135
+ :type node: Hashable
136
+ :return: Logical x-layer for the species node.
137
+ :rtype: float
138
+ """
139
+ reactant_steps: List[int] = []
140
+ product_steps: List[int] = []
141
+
142
+ for _, v, d in graph.out_edges(node, data=True):
143
+ if d.get("role") == "reactant" and is_reaction_node(graph.nodes[v]):
144
+ step = graph.nodes[v].get("step", d.get("step"))
145
+ if step is not None:
146
+ reactant_steps.append(int(step))
147
+
148
+ for u, _, d in graph.in_edges(node, data=True):
149
+ if d.get("role") == "product" and is_reaction_node(graph.nodes[u]):
150
+ step = graph.nodes[u].get("step", d.get("step"))
151
+ if step is not None:
152
+ product_steps.append(int(step))
153
+
154
+ if reactant_steps:
155
+ return float(2 * (min(reactant_steps) - 1))
156
+ if product_steps:
157
+ return float(2 * min(product_steps))
158
+ return 0.0
159
+
160
+
161
+ def _logical_layers(
162
+ graph: nx.DiGraph,
163
+ *,
164
+ species_nodes: List[Hashable],
165
+ rule_nodes: List[Hashable],
166
+ ) -> Dict[int, List[Hashable]]:
167
+ """Build integer logical layers for species and rule nodes.
168
+
169
+ :param graph: Directed graph containing node and edge metadata.
170
+ :type graph: nx.DiGraph
171
+ :param species_nodes: List of species node identifiers.
172
+ :type species_nodes: List[Hashable]
173
+ :param rule_nodes: List of rule node identifiers.
174
+ :type rule_nodes: List[Hashable]
175
+ :return: Mapping from integer layer index to nodes in that layer.
176
+ :rtype: Dict[int, List[Hashable]]
177
+ """
178
+ layers: Dict[int, List[Hashable]] = defaultdict(list)
179
+
180
+ for node in species_nodes:
181
+ layers[int(_species_x_from_incidence(graph, node))].append(node)
182
+
183
+ for node in rule_nodes:
184
+ layers[int(_rule_x_from_step(graph, node))].append(node)
185
+
186
+ return dict(layers)
187
+
188
+
189
+ def step_layout(
190
+ graph: nx.DiGraph,
191
+ *,
192
+ species_nodes: List[Hashable],
193
+ rule_nodes: List[Hashable],
194
+ node_spacing: float = 1.4,
195
+ layer_spacing: float = 2.5,
196
+ **_: object,
197
+ ) -> Pos:
198
+ """Compute a layered step-wise layout.
199
+
200
+ Species and rule nodes are assigned to alternating logical x-layers inferred
201
+ from rule steps and incidence relationships.
202
+
203
+ :param graph: Directed graph containing species and rule nodes.
204
+ :type graph: nx.DiGraph
205
+ :param species_nodes: List of species node identifiers.
206
+ :type species_nodes: List[Hashable]
207
+ :param rule_nodes: List of rule node identifiers.
208
+ :type rule_nodes: List[Hashable]
209
+ :param node_spacing: Vertical spacing between nodes in the same layer.
210
+ :type node_spacing: float
211
+ :param layer_spacing: Horizontal spacing between adjacent layers.
212
+ :type layer_spacing: float
213
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
214
+ :rtype: Pos
215
+
216
+ .. code-block:: python
217
+
218
+ pos = step_layout(
219
+ graph,
220
+ species_nodes=species_nodes,
221
+ rule_nodes=rule_nodes,
222
+ node_spacing=1.6,
223
+ layer_spacing=3.0,
224
+ )
225
+ """
226
+ groups: Dict[float, List[Hashable]] = defaultdict(list)
227
+
228
+ for node in species_nodes:
229
+ groups[_species_x_from_incidence(graph, node)].append(node)
230
+
231
+ for node in rule_nodes:
232
+ groups[_rule_x_from_step(graph, node)].append(node)
233
+
234
+ pos: Pos = {}
235
+ for raw_x in sorted(groups):
236
+ pos.update(
237
+ _stack_vertical(
238
+ graph,
239
+ groups[raw_x],
240
+ x=raw_x * layer_spacing,
241
+ node_spacing=node_spacing,
242
+ )
243
+ )
244
+ return pos
245
+
246
+
247
+ def bipartite_layout(
248
+ graph: nx.DiGraph,
249
+ *,
250
+ species_nodes: List[Hashable],
251
+ rule_nodes: List[Hashable],
252
+ node_spacing: float = 1.4,
253
+ layer_spacing: float = 3.0,
254
+ orientation: str = "vertical",
255
+ **_: object,
256
+ ) -> Pos:
257
+ """Compute a two-layer species-rule layout.
258
+
259
+ :param graph: Directed graph containing species and rule nodes.
260
+ :type graph: nx.DiGraph
261
+ :param species_nodes: List of species node identifiers.
262
+ :type species_nodes: List[Hashable]
263
+ :param rule_nodes: List of rule node identifiers.
264
+ :type rule_nodes: List[Hashable]
265
+ :param node_spacing: Spacing between adjacent nodes within a layer.
266
+ :type node_spacing: float
267
+ :param layer_spacing: Distance between the species and rule layers.
268
+ :type layer_spacing: float
269
+ :param orientation: Layout orientation. Must be ``"vertical"`` or
270
+ ``"horizontal"``.
271
+ :type orientation: str
272
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
273
+ :rtype: Pos
274
+ :raises ValueError: If ``orientation`` is not supported.
275
+ """
276
+ pos: Pos = {}
277
+
278
+ if orientation == "vertical":
279
+ pos.update(
280
+ _stack_vertical(graph, species_nodes, x=0.0, node_spacing=node_spacing)
281
+ )
282
+ pos.update(
283
+ _stack_vertical(
284
+ graph,
285
+ rule_nodes,
286
+ x=layer_spacing,
287
+ node_spacing=node_spacing,
288
+ )
289
+ )
290
+ return pos
291
+
292
+ if orientation == "horizontal":
293
+ pos.update(
294
+ _stack_horizontal(graph, species_nodes, y=0.0, node_spacing=node_spacing)
295
+ )
296
+ pos.update(
297
+ _stack_horizontal(
298
+ graph,
299
+ rule_nodes,
300
+ y=-layer_spacing,
301
+ node_spacing=node_spacing,
302
+ )
303
+ )
304
+ return pos
305
+
306
+ raise ValueError("orientation must be 'vertical' or 'horizontal'")
307
+
308
+
309
+ def multipartite_step_layout(
310
+ graph: nx.DiGraph,
311
+ *,
312
+ species_nodes: List[Hashable],
313
+ rule_nodes: List[Hashable],
314
+ **_: object,
315
+ ) -> Pos:
316
+ """Compute a multipartite layout using inferred step layers.
317
+
318
+ This layout is useful for larger step-annotated graphs where a simple custom
319
+ step layout becomes crowded.
320
+
321
+ :param graph: Directed graph containing species and rule nodes.
322
+ :type graph: nx.DiGraph
323
+ :param species_nodes: List of species node identifiers.
324
+ :type species_nodes: List[Hashable]
325
+ :param rule_nodes: List of rule node identifiers.
326
+ :type rule_nodes: List[Hashable]
327
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
328
+ :rtype: Pos
329
+ """
330
+ G = graph.copy()
331
+ layers = _logical_layers(
332
+ graph,
333
+ species_nodes=species_nodes,
334
+ rule_nodes=rule_nodes,
335
+ )
336
+
337
+ node_to_layer = {node: layer for layer, nodes in layers.items() for node in nodes}
338
+
339
+ for node in G.nodes:
340
+ G.nodes[node]["subset"] = node_to_layer.get(node, 0)
341
+
342
+ return nx.multipartite_layout(G, subset_key="subset", align="vertical")
343
+
344
+
345
+ def radial_step_layout(
346
+ graph: nx.DiGraph,
347
+ *,
348
+ species_nodes: List[Hashable],
349
+ rule_nodes: List[Hashable],
350
+ radius_step: float = 1.8,
351
+ **_: object,
352
+ ) -> Pos:
353
+ """Compute a radial step layout using concentric circles.
354
+
355
+ Logical layers are mapped to increasing radii.
356
+
357
+ :param graph: Directed graph containing species and rule nodes.
358
+ :type graph: nx.DiGraph
359
+ :param species_nodes: List of species node identifiers.
360
+ :type species_nodes: List[Hashable]
361
+ :param rule_nodes: List of rule node identifiers.
362
+ :type rule_nodes: List[Hashable]
363
+ :param radius_step: Radial increment between adjacent logical layers.
364
+ :type radius_step: float
365
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
366
+ :rtype: Pos
367
+ """
368
+ layers = _logical_layers(
369
+ graph,
370
+ species_nodes=species_nodes,
371
+ rule_nodes=rule_nodes,
372
+ )
373
+
374
+ pos: Pos = {}
375
+ for layer in sorted(layers):
376
+ nodes = _sort_nodes(graph, layers[layer])
377
+ n = len(nodes)
378
+ radius = max(1.0, layer * radius_step)
379
+
380
+ for i, node in enumerate(nodes):
381
+ theta = (2.0 * pi * i) / max(n, 1)
382
+ pos[node] = (radius * cos(theta), radius * sin(theta))
383
+
384
+ return pos
385
+
386
+
387
+ def circular_bipartite_layout(
388
+ graph: nx.DiGraph,
389
+ *,
390
+ species_nodes: List[Hashable],
391
+ rule_nodes: List[Hashable],
392
+ species_radius: float = 2.5,
393
+ rule_radius: float = 4.0,
394
+ **_: object,
395
+ ) -> Pos:
396
+ """Compute a circular bipartite layout using two concentric circles.
397
+
398
+ Species and rule nodes are placed on separate rings.
399
+
400
+ :param graph: Directed graph containing species and rule nodes.
401
+ :type graph: nx.DiGraph
402
+ :param species_nodes: List of species node identifiers.
403
+ :type species_nodes: List[Hashable]
404
+ :param rule_nodes: List of rule node identifiers.
405
+ :type rule_nodes: List[Hashable]
406
+ :param species_radius: Radius of the species ring.
407
+ :type species_radius: float
408
+ :param rule_radius: Radius of the rule ring.
409
+ :type rule_radius: float
410
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
411
+ :rtype: Pos
412
+ """
413
+ pos: Pos = {}
414
+ s_nodes = _sort_nodes(graph, species_nodes)
415
+ r_nodes = _sort_nodes(graph, rule_nodes)
416
+
417
+ for i, node in enumerate(s_nodes):
418
+ theta = (2.0 * pi * i) / max(len(s_nodes), 1)
419
+ pos[node] = (species_radius * cos(theta), species_radius * sin(theta))
420
+
421
+ for i, node in enumerate(r_nodes):
422
+ theta = (2.0 * pi * i) / max(len(r_nodes), 1)
423
+ pos[node] = (rule_radius * cos(theta), rule_radius * sin(theta))
424
+
425
+ return pos
426
+
427
+
428
+ def degree_shell_layout(
429
+ graph: nx.DiGraph,
430
+ *,
431
+ species_nodes: List[Hashable],
432
+ rule_nodes: List[Hashable],
433
+ **_: object,
434
+ ) -> Pos:
435
+ """Compute a degree-based shell layout.
436
+
437
+ Nodes with the highest degree are placed in the inner shell, followed by
438
+ medium-degree and lower-degree shells.
439
+
440
+ :param graph: Directed graph containing species and rule nodes.
441
+ :type graph: nx.DiGraph
442
+ :param species_nodes: List of species node identifiers.
443
+ :type species_nodes: List[Hashable]
444
+ :param rule_nodes: List of rule node identifiers.
445
+ :type rule_nodes: List[Hashable]
446
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
447
+ :rtype: Pos
448
+ """
449
+ by_degree = sorted(graph.degree, key=lambda x: (-x[1], str(x[0])))
450
+ n_total = len(by_degree)
451
+
452
+ q1 = max(1, n_total // 4)
453
+ q2 = max(2, n_total // 2)
454
+
455
+ high = [node for node, _ in by_degree[:q1]]
456
+ mid = [node for node, _ in by_degree[q1:q2]]
457
+ rest = [node for node, _ in by_degree[q2:]]
458
+
459
+ shells = [shell for shell in (high, mid, rest) if shell]
460
+ return nx.shell_layout(graph, nlist=shells)
461
+
462
+
463
+ def choose_auto_layout(
464
+ graph: nx.DiGraph,
465
+ *,
466
+ species_nodes: List[Hashable],
467
+ rule_nodes: List[Hashable],
468
+ ) -> str:
469
+ """Choose a layout heuristically.
470
+
471
+ Preference is given to step-aware layouts when rule step annotations are
472
+ available. Larger or denser graphs are routed to layouts that typically
473
+ reduce clutter.
474
+
475
+ :param graph: Directed graph containing species and rule nodes.
476
+ :type graph: nx.DiGraph
477
+ :param species_nodes: List of species node identifiers.
478
+ :type species_nodes: List[Hashable]
479
+ :param rule_nodes: List of rule node identifiers.
480
+ :type rule_nodes: List[Hashable]
481
+ :return: Selected layout name.
482
+ :rtype: str
483
+ """
484
+ n_nodes = graph.number_of_nodes()
485
+ n_edges = graph.number_of_edges()
486
+ density = nx.density(graph) if n_nodes > 1 else 0.0
487
+ has_step = any(graph.nodes[node].get("step") is not None for node in rule_nodes)
488
+
489
+ if has_step and (n_nodes >= 35 or n_edges >= 55 or density >= 0.045):
490
+ return "multipartite_step"
491
+ if has_step:
492
+ return "step"
493
+ if n_nodes >= 40 or density >= 0.06:
494
+ return "kamada_kawai"
495
+ return "spring"
496
+
497
+
498
+ def _spring_layout(
499
+ graph: nx.DiGraph,
500
+ *,
501
+ seed: int,
502
+ **_: object,
503
+ ) -> Pos:
504
+ """Compute a spring layout with a slightly enlarged optimal distance.
505
+
506
+ :param graph: Directed graph to layout.
507
+ :type graph: nx.DiGraph
508
+ :param seed: Random seed for the spring layout.
509
+ :type seed: int
510
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
511
+ :rtype: Pos
512
+ """
513
+ n_nodes = max(graph.number_of_nodes(), 1)
514
+ k = 1.8 / (n_nodes**0.5)
515
+ return nx.spring_layout(graph, seed=seed, k=k, iterations=200)
516
+
517
+
518
+ def _kamada_kawai_layout(
519
+ graph: nx.DiGraph,
520
+ **_: object,
521
+ ) -> Pos:
522
+ """Compute a Kamada-Kawai layout.
523
+
524
+ :param graph: Directed graph to layout.
525
+ :type graph: nx.DiGraph
526
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
527
+ :rtype: Pos
528
+ """
529
+ return nx.kamada_kawai_layout(graph)
530
+
531
+
532
+ def _spectral_layout(
533
+ graph: nx.DiGraph,
534
+ **_: object,
535
+ ) -> Pos:
536
+ """Compute a spectral layout.
537
+
538
+ :param graph: Directed graph to layout.
539
+ :type graph: nx.DiGraph
540
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
541
+ :rtype: Pos
542
+ """
543
+ return nx.spectral_layout(graph)
544
+
545
+
546
+ def _shell_layout(
547
+ graph: nx.DiGraph,
548
+ *,
549
+ species_nodes: List[Hashable],
550
+ rule_nodes: List[Hashable],
551
+ **_: object,
552
+ ) -> Pos:
553
+ """Compute a shell layout with species and rule nodes grouped into shells.
554
+
555
+ :param graph: Directed graph to layout.
556
+ :type graph: nx.DiGraph
557
+ :param species_nodes: List of species node identifiers.
558
+ :type species_nodes: List[Hashable]
559
+ :param rule_nodes: List of rule node identifiers.
560
+ :type rule_nodes: List[Hashable]
561
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
562
+ :rtype: Pos
563
+ """
564
+ shells: List[List[Hashable]] = []
565
+ if species_nodes:
566
+ shells.append(species_nodes)
567
+ if rule_nodes:
568
+ shells.append(rule_nodes)
569
+ return nx.shell_layout(graph, nlist=shells) if shells else nx.shell_layout(graph)
570
+
571
+
572
+ def _spiral_layout(
573
+ graph: nx.DiGraph,
574
+ **_: object,
575
+ ) -> Pos:
576
+ """Compute a spiral layout.
577
+
578
+ :param graph: Directed graph to layout.
579
+ :type graph: nx.DiGraph
580
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
581
+ :rtype: Pos
582
+ """
583
+ return nx.spiral_layout(graph)
584
+
585
+
586
+ def _random_layout(
587
+ graph: nx.DiGraph,
588
+ *,
589
+ seed: int,
590
+ **_: object,
591
+ ) -> Pos:
592
+ """Compute a random layout.
593
+
594
+ :param graph: Directed graph to layout.
595
+ :type graph: nx.DiGraph
596
+ :param seed: Random seed for layout generation.
597
+ :type seed: int
598
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
599
+ :rtype: Pos
600
+ """
601
+ return nx.random_layout(graph, seed=seed)
602
+
603
+
604
+ _LAYOUT_REGISTRY: Mapping[str, LayoutFunc] = {
605
+ "step": step_layout,
606
+ "bipartite": bipartite_layout,
607
+ "multipartite_step": multipartite_step_layout,
608
+ "radial_step": radial_step_layout,
609
+ "circular_bipartite": circular_bipartite_layout,
610
+ "degree_shell": degree_shell_layout,
611
+ "spring": _spring_layout,
612
+ "kamada_kawai": _kamada_kawai_layout,
613
+ "spectral": _spectral_layout,
614
+ "shell": _shell_layout,
615
+ "spiral": _spiral_layout,
616
+ "random": _random_layout,
617
+ }
618
+
619
+
620
+ def available_layouts() -> List[str]:
621
+ """Return the list of supported layout names.
622
+
623
+ :return: Supported layout names, including ``"auto"``.
624
+ :rtype: List[str]
625
+
626
+ .. code-block:: python
627
+
628
+ names = available_layouts()
629
+ print(names)
630
+ """
631
+ return ["auto", *_LAYOUT_REGISTRY.keys()]
632
+
633
+
634
+ def compute_layout(
635
+ graph: nx.DiGraph,
636
+ *,
637
+ species_nodes: List[Hashable],
638
+ rule_nodes: List[Hashable],
639
+ layout: str = "step",
640
+ node_spacing: float = 1.4,
641
+ layer_spacing: float = 2.5,
642
+ seed: int = 0,
643
+ orientation: str = "vertical",
644
+ ) -> Pos:
645
+ """Compute node positions for CRN visualization.
646
+
647
+ :param graph: Directed graph containing species and rule nodes.
648
+ :type graph: nx.DiGraph
649
+ :param species_nodes: List of species node identifiers.
650
+ :type species_nodes: List[Hashable]
651
+ :param rule_nodes: List of rule node identifiers.
652
+ :type rule_nodes: List[Hashable]
653
+ :param layout: Layout name. Supported values are ``"auto"``, ``"step"``,
654
+ ``"bipartite"``, ``"multipartite_step"``, ``"radial_step"``,
655
+ ``"circular_bipartite"``, ``"degree_shell"``, ``"spring"``,
656
+ ``"kamada_kawai"``, ``"spectral"``, ``"shell"``, ``"spiral"``,
657
+ and ``"random"``.
658
+ :type layout: str
659
+ :param node_spacing: Spacing between nodes within a layer for layouts that
660
+ support it.
661
+ :type node_spacing: float
662
+ :param layer_spacing: Spacing between layers for layouts that support it.
663
+ :type layer_spacing: float
664
+ :param seed: Random seed for stochastic layouts.
665
+ :type seed: int
666
+ :param orientation: Orientation for the bipartite layout. Must be
667
+ ``"vertical"`` or ``"horizontal"``.
668
+ :type orientation: str
669
+ :return: Mapping from node identifier to ``(x, y)`` coordinates.
670
+ :rtype: Pos
671
+ :raises ValueError: If the requested layout name is not supported.
672
+
673
+ .. code-block:: python
674
+
675
+ pos = compute_layout(
676
+ graph,
677
+ species_nodes=species_nodes,
678
+ rule_nodes=rule_nodes,
679
+ layout="auto",
680
+ )
681
+ """
682
+ resolved_layout = (
683
+ choose_auto_layout(
684
+ graph,
685
+ species_nodes=species_nodes,
686
+ rule_nodes=rule_nodes,
687
+ )
688
+ if layout == "auto"
689
+ else layout
690
+ )
691
+
692
+ try:
693
+ layout_func = _LAYOUT_REGISTRY[resolved_layout]
694
+ except KeyError as exc:
695
+ supported = ", ".join(repr(name) for name in available_layouts())
696
+ raise ValueError(
697
+ f"Unsupported layout {layout!r}. Use one of: {supported}."
698
+ ) from exc
699
+
700
+ return layout_func(
701
+ graph,
702
+ species_nodes=species_nodes,
703
+ rule_nodes=rule_nodes,
704
+ node_spacing=node_spacing,
705
+ layer_spacing=layer_spacing,
706
+ seed=seed,
707
+ orientation=orientation,
708
+ )