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,1059 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import Counter, defaultdict
4
+ from dataclasses import dataclass
5
+ from time import perf_counter
6
+ from typing import (
7
+ Any,
8
+ Callable,
9
+ Dict,
10
+ Hashable,
11
+ Iterable,
12
+ List,
13
+ Mapping,
14
+ Optional,
15
+ Sequence,
16
+ Set,
17
+ Tuple,
18
+ )
19
+ import hashlib
20
+
21
+ import networkx as nx
22
+
23
+ from ..kinds import REACTION_KINDS
24
+
25
+ __all__ = [
26
+ "SymmetryConfig",
27
+ "WLResult",
28
+ "AutomorphismResult",
29
+ "IsomorphismResult",
30
+ "CanonicalResult",
31
+ "freeze",
32
+ "hash_text",
33
+ "prepare_graph",
34
+ "node_token",
35
+ "edge_token",
36
+ "node_matcher",
37
+ "edge_matcher",
38
+ "build_fast_signature",
39
+ "invert_mapping",
40
+ "orbits_from_mappings",
41
+ "should_stop",
42
+ "graph_key_from_order",
43
+ "approx_automorphism_count_from_cells",
44
+ ]
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class SymmetryConfig:
49
+ """Configuration controlling semantic versus topological symmetry matching.
50
+
51
+ The configuration determines which node and edge attributes participate in
52
+ token construction, canonicalization, isomorphism tests, and automorphism
53
+ analysis.
54
+
55
+ :param kind_attr_key:
56
+ Node attribute name used to identify node kind.
57
+ :type kind_attr_key: str
58
+
59
+ :param species_kind_value:
60
+ Attribute value indicating a species node.
61
+ :type species_kind_value: str
62
+
63
+ :param rule_kind_value:
64
+ Attribute value indicating a rule/reaction node.
65
+ :type rule_kind_value: str
66
+
67
+ :param require_kind:
68
+ Whether node kind must always be included in node tokens.
69
+ :type require_kind: bool
70
+
71
+ :param species_identity_keys:
72
+ Preferred attribute keys used to identify species nodes.
73
+ :type species_identity_keys: Tuple[str, ...]
74
+
75
+ :param rule_identity_keys:
76
+ Preferred attribute keys used to identify rule nodes.
77
+ :type rule_identity_keys: Tuple[str, ...]
78
+
79
+ :param species_extra_attr_keys:
80
+ Additional species-node attributes to include in node tokens.
81
+ :type species_extra_attr_keys: Tuple[str, ...]
82
+
83
+ :param rule_extra_attr_keys:
84
+ Additional rule-node attributes to include in node tokens.
85
+ :type rule_extra_attr_keys: Tuple[str, ...]
86
+
87
+ :param other_node_attr_keys:
88
+ Attributes to include for nodes that are neither species nor rule nodes.
89
+ :type other_node_attr_keys: Tuple[str, ...]
90
+
91
+ :param generic_node_attr_keys:
92
+ Generic node attributes appended for all node kinds.
93
+ :type generic_node_attr_keys: Tuple[str, ...]
94
+
95
+ :param ignored_node_attr_keys:
96
+ Node attributes ignored during token construction.
97
+ :type ignored_node_attr_keys: Tuple[str, ...]
98
+
99
+ :param edge_attr_keys:
100
+ Edge attributes included in edge tokens.
101
+ :type edge_attr_keys: Tuple[str, ...]
102
+
103
+ :param ignored_edge_attr_keys:
104
+ Edge attributes ignored during edge token construction.
105
+ :type ignored_edge_attr_keys: Tuple[str, ...]
106
+
107
+ :param use_edge_direction:
108
+ Whether edge direction is respected in symmetry computations.
109
+ :type use_edge_direction: bool
110
+
111
+ :param include_missing_keys:
112
+ Whether missing identity keys should be represented explicitly as
113
+ ``None`` in node tokens.
114
+ :type include_missing_keys: bool
115
+
116
+ .. rubric:: Examples
117
+
118
+ .. code-block:: python
119
+
120
+ from synkit.CRN.Sym._common import SymmetryConfig
121
+
122
+ semantic_cfg = SymmetryConfig.semantic()
123
+ topo_cfg = SymmetryConfig.topological()
124
+ """
125
+
126
+ kind_attr_key: str = "kind"
127
+ species_kind_value: str = "species"
128
+ rule_kind_value: str = "rule"
129
+ require_kind: bool = True
130
+
131
+ species_identity_keys: Tuple[str, ...] = ("smiles", "label")
132
+ rule_identity_keys: Tuple[str, ...] = ("rule_repr", "rxn_repr", "label")
133
+
134
+ species_extra_attr_keys: Tuple[str, ...] = ()
135
+ rule_extra_attr_keys: Tuple[str, ...] = ()
136
+ other_node_attr_keys: Tuple[str, ...] = ()
137
+ generic_node_attr_keys: Tuple[str, ...] = ()
138
+
139
+ ignored_node_attr_keys: Tuple[str, ...] = (
140
+ "syncrn_id",
141
+ "source_node_id",
142
+ "app_index",
143
+ "rule_index",
144
+ "step",
145
+ "rxn_id",
146
+ )
147
+
148
+ edge_attr_keys: Tuple[str, ...] = ("role", "stoich")
149
+ ignored_edge_attr_keys: Tuple[str, ...] = ("rxn_id",)
150
+ use_edge_direction: bool = True
151
+ include_missing_keys: bool = False
152
+
153
+ @classmethod
154
+ def semantic(cls) -> "SymmetryConfig":
155
+ """Build a semantic symmetry configuration.
156
+
157
+ :return:
158
+ Configuration that preserves node identities and stoichiometric edge
159
+ semantics.
160
+ :rtype: SymmetryConfig
161
+ """
162
+ return cls(
163
+ species_identity_keys=("smiles", "label"),
164
+ rule_identity_keys=("rule_repr", "rxn_repr", "label"),
165
+ edge_attr_keys=("role", "stoich"),
166
+ require_kind=True,
167
+ use_edge_direction=True,
168
+ )
169
+
170
+ @classmethod
171
+ def topological(cls) -> "SymmetryConfig":
172
+ """Build a topology-focused symmetry configuration.
173
+
174
+ This mode ignores species and rule identities while still respecting
175
+ node kinds and selected edge annotations.
176
+
177
+ :return:
178
+ Configuration emphasizing graph topology over node identity.
179
+ :rtype: SymmetryConfig
180
+ """
181
+ return cls(
182
+ species_identity_keys=(),
183
+ rule_identity_keys=(),
184
+ species_extra_attr_keys=(),
185
+ rule_extra_attr_keys=(),
186
+ other_node_attr_keys=(),
187
+ generic_node_attr_keys=(),
188
+ edge_attr_keys=("role", "stoich"),
189
+ require_kind=True,
190
+ use_edge_direction=True,
191
+ )
192
+
193
+
194
+ @dataclass(frozen=True)
195
+ class WLResult:
196
+ """
197
+ Result of Weisfeiler-Lehman refinement.
198
+
199
+ :param colors:
200
+ Final color assigned to each node.
201
+ :type colors: Dict[Any, str]
202
+
203
+ :param orbits:
204
+ Approximate orbit partition induced by final color classes.
205
+ :type orbits: List[Set[Any]]
206
+
207
+ :param color_hist:
208
+ Histogram of final colors.
209
+ :type color_hist: Dict[str, int]
210
+
211
+ :param iters_run:
212
+ Number of WL iterations actually executed.
213
+ :type iters_run: int
214
+
215
+ :param stabilized:
216
+ Whether refinement stabilized before the iteration limit.
217
+ :type stabilized: bool
218
+
219
+ :param approx_automorphism_count:
220
+ Approximate automorphism count inferred from unresolved cells, if
221
+ available.
222
+ :type approx_automorphism_count: Optional[int]
223
+
224
+ :param canonical_order:
225
+ Canonical node ordering induced by WL colors and tie-breaking rules.
226
+ :type canonical_order: List[Any]
227
+ """
228
+
229
+ colors: Dict[Any, str]
230
+ orbits: List[Set[Any]]
231
+ color_hist: Dict[str, int]
232
+ iters_run: int
233
+ stabilized: bool
234
+ approx_automorphism_count: Optional[int]
235
+ canonical_order: List[Any]
236
+
237
+
238
+ @dataclass(frozen=True)
239
+ class AutomorphismResult:
240
+ """
241
+ Result of an automorphism computation.
242
+
243
+ :param graph_type:
244
+ Graph representation type used in the computation.
245
+ :type graph_type: str
246
+
247
+ :param automorphism_count:
248
+ Number of discovered automorphisms.
249
+ :type automorphism_count: int
250
+
251
+ :param sample_mappings:
252
+ Sample node mappings realizing automorphisms.
253
+ :type sample_mappings: List[Dict[Any, Any]]
254
+
255
+ :param orbits:
256
+ Orbit partition induced by the sampled mappings.
257
+ :type orbits: List[Set[Any]]
258
+
259
+ :param elapsed_seconds:
260
+ Runtime in seconds.
261
+ :type elapsed_seconds: float
262
+
263
+ :param stopped_early:
264
+ Whether the computation stopped early due to timeout or count limit.
265
+ :type stopped_early: bool
266
+ """
267
+
268
+ graph_type: str
269
+ automorphism_count: int
270
+ sample_mappings: List[Dict[Any, Any]]
271
+ orbits: List[Set[Any]]
272
+ elapsed_seconds: float
273
+ stopped_early: bool
274
+
275
+
276
+ @dataclass(frozen=True)
277
+ class IsomorphismResult:
278
+ """
279
+ Result of an isomorphism test.
280
+
281
+ :param isomorphic:
282
+ Whether the compared graphs are isomorphic.
283
+ :type isomorphic: bool
284
+
285
+ :param mapping:
286
+ Witness mapping from the first graph to the second graph, if found.
287
+ :type mapping: Optional[Dict[Any, Any]]
288
+
289
+ :param elapsed_seconds:
290
+ Runtime in seconds.
291
+ :type elapsed_seconds: float
292
+
293
+ :param rejected_by_invariants:
294
+ Whether the comparison was rejected using fast invariants before an exact
295
+ search.
296
+ :type rejected_by_invariants: bool
297
+
298
+ :param mode:
299
+ Matching mode identifier, for example semantic or topological.
300
+ :type mode: str
301
+ """
302
+
303
+ isomorphic: bool
304
+ mapping: Optional[Dict[Any, Any]]
305
+ elapsed_seconds: float
306
+ rejected_by_invariants: bool
307
+ mode: str
308
+
309
+
310
+ @dataclass(frozen=True)
311
+ class CanonicalResult:
312
+ """
313
+ Result of graph canonicalization.
314
+
315
+ :param canonical_order:
316
+ Canonical node ordering.
317
+ :type canonical_order: List[Any]
318
+
319
+ :param canonical_key:
320
+ Canonical key derived from the canonical order.
321
+ :type canonical_key: Tuple[Any, ...]
322
+
323
+ :param exact:
324
+ Whether the result is exact rather than approximate.
325
+ :type exact: bool
326
+
327
+ :param elapsed_seconds:
328
+ Runtime in seconds.
329
+ :type elapsed_seconds: float
330
+ """
331
+
332
+ canonical_order: List[Any]
333
+ canonical_key: Tuple[Any, ...]
334
+ exact: bool
335
+ elapsed_seconds: float
336
+
337
+
338
+ def freeze(x: Any) -> Hashable:
339
+ """Convert common container types into a stable hashable representation.
340
+
341
+ :param x:
342
+ Object to freeze.
343
+ :type x: Any
344
+
345
+ :return:
346
+ Hashable recursively frozen representation.
347
+ :rtype: Hashable
348
+
349
+ .. rubric:: Examples
350
+
351
+ .. code-block:: python
352
+
353
+ freeze({"b": [2, 1], "a": {"x", "y"}})
354
+ """
355
+ if isinstance(x, dict):
356
+ return tuple(
357
+ (k, freeze(v)) for k, v in sorted(x.items(), key=lambda kv: str(kv[0]))
358
+ )
359
+ if isinstance(x, (list, tuple)):
360
+ return tuple(freeze(v) for v in x)
361
+ if isinstance(x, set):
362
+ return tuple(sorted((freeze(v) for v in x), key=str))
363
+ return x
364
+
365
+
366
+ def hash_text(text: str, *, digest_size: int = 16) -> str:
367
+ """Hash text with BLAKE2b.
368
+
369
+ :param text:
370
+ Input text.
371
+ :type text: str
372
+
373
+ :param digest_size:
374
+ Output digest size in bytes.
375
+ :type digest_size: int
376
+
377
+ :return:
378
+ Hex digest string.
379
+ :rtype: str
380
+ """
381
+ h = hashlib.blake2b(digest_size=digest_size)
382
+ h.update(text.encode("utf-8", errors="replace"))
383
+ return h.hexdigest()
384
+
385
+
386
+ def should_stop(
387
+ start: float,
388
+ timeout_sec: Optional[float],
389
+ *,
390
+ count: Optional[int] = None,
391
+ max_count: Optional[int] = None,
392
+ ) -> bool:
393
+ """Decide whether a search should stop.
394
+
395
+ :param start:
396
+ Start time from :func:`perf_counter`.
397
+ :type start: float
398
+
399
+ :param timeout_sec:
400
+ Optional timeout in seconds.
401
+ :type timeout_sec: Optional[float]
402
+
403
+ :param count:
404
+ Current number of discovered solutions.
405
+ :type count: Optional[int]
406
+
407
+ :param max_count:
408
+ Optional maximum number of solutions to collect.
409
+ :type max_count: Optional[int]
410
+
411
+ :return:
412
+ ``True`` if the search should stop.
413
+ :rtype: bool
414
+ """
415
+ if timeout_sec is not None and perf_counter() - start > timeout_sec:
416
+ return True
417
+ if max_count is not None and count is not None and count >= max_count:
418
+ return True
419
+ return False
420
+
421
+
422
+ def _factorial_capped(n: int, cap: int) -> int:
423
+ """Compute ``n!`` with an upper cap.
424
+
425
+ :param n:
426
+ Nonnegative integer.
427
+ :type n: int
428
+
429
+ :param cap:
430
+ Upper cap for early stopping.
431
+ :type cap: int
432
+
433
+ :return:
434
+ ``min(n!, cap)``.
435
+ :rtype: int
436
+ """
437
+ out = 1
438
+ for i in range(2, n + 1):
439
+ out *= i
440
+ if out >= cap:
441
+ return cap
442
+ return out
443
+
444
+
445
+ def _product_factorials_capped(values: Iterable[int], cap: int) -> int:
446
+ """Compute a capped product of factorials.
447
+
448
+ :param values:
449
+ Iterable of cell sizes.
450
+ :type values: Iterable[int]
451
+
452
+ :param cap:
453
+ Upper cap for early stopping.
454
+ :type cap: int
455
+
456
+ :return:
457
+ Product of factorials, capped at ``cap``.
458
+ :rtype: int
459
+ """
460
+ out = 1
461
+ for v in values:
462
+ out *= _factorial_capped(v, cap)
463
+ if out >= cap:
464
+ return cap
465
+ return out
466
+
467
+
468
+ def approx_automorphism_count_from_cells(
469
+ cells: Iterable[Sequence[Any]], *, cap: int = 10**18
470
+ ) -> int:
471
+ """Approximate automorphism count from unresolved cell sizes.
472
+
473
+ This estimate assumes each cell can be permuted independently.
474
+
475
+ :param cells:
476
+ Iterable of partition cells.
477
+ :type cells: Iterable[Sequence[Any]]
478
+
479
+ :param cap:
480
+ Upper cap on the returned count.
481
+ :type cap: int
482
+
483
+ :return:
484
+ Approximate capped automorphism count.
485
+ :rtype: int
486
+ """
487
+ return _product_factorials_capped((len(c) for c in cells), cap)
488
+
489
+
490
+ def _copy_as_digraph(G: nx.Graph) -> nx.DiGraph:
491
+ """Copy a NetworkX graph into a simple directed graph.
492
+
493
+ Undirected edges are duplicated in both directions. Multi-edges are merged
494
+ into a simple :class:`nx.DiGraph`.
495
+
496
+ :param G:
497
+ Input graph.
498
+ :type G: nx.Graph
499
+
500
+ :return:
501
+ Directed graph copy.
502
+ :rtype: nx.DiGraph
503
+ """
504
+ if isinstance(G, nx.DiGraph):
505
+ return G.copy()
506
+ if isinstance(G, nx.MultiDiGraph):
507
+ H = nx.DiGraph()
508
+ H.add_nodes_from((n, dict(d)) for n, d in G.nodes(data=True))
509
+ for u, v, _, d in G.edges(keys=True, data=True):
510
+ if H.has_edge(u, v):
511
+ H[u][v].update(d)
512
+ else:
513
+ H.add_edge(u, v, **dict(d))
514
+ return H
515
+ if isinstance(G, nx.MultiGraph):
516
+ H = nx.DiGraph()
517
+ H.add_nodes_from((n, dict(d)) for n, d in G.nodes(data=True))
518
+ for u, v, _, d in G.edges(keys=True, data=True):
519
+ if not H.has_edge(u, v):
520
+ H.add_edge(u, v, **dict(d))
521
+ if not H.has_edge(v, u):
522
+ H.add_edge(v, u, **dict(d))
523
+ return H
524
+ H = nx.DiGraph()
525
+ H.add_nodes_from((n, dict(d)) for n, d in G.nodes(data=True))
526
+ for u, v, d in G.edges(data=True):
527
+ H.add_edge(u, v, **dict(d))
528
+ if not G.is_directed() and not H.has_edge(v, u):
529
+ H.add_edge(v, u, **dict(d))
530
+ return H
531
+
532
+
533
+ def prepare_graph(
534
+ source: Any,
535
+ *,
536
+ include_rule: bool = True,
537
+ include_stoich: bool = True,
538
+ ) -> Tuple[nx.DiGraph, str]:
539
+ """Convert a supported source object into a directed graph.
540
+
541
+ Supported inputs are:
542
+ - a NetworkX graph
543
+ - an object exposing ``to_digraph()``
544
+
545
+ :param source:
546
+ Input graph-like object.
547
+ :type source: Any
548
+
549
+ :param include_rule:
550
+ Whether the resulting graph should be treated as bipartite
551
+ species-rule graph rather than a species-only graph.
552
+ :type include_rule: bool
553
+
554
+ :param include_stoich:
555
+ Reserved compatibility flag.
556
+ :type include_stoich: bool
557
+
558
+ :return:
559
+ Pair ``(graph, graph_type)``.
560
+ :rtype: Tuple[nx.DiGraph, str]
561
+
562
+ :raises TypeError:
563
+ If the input is unsupported.
564
+ """
565
+ _ = include_stoich
566
+
567
+ if isinstance(source, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)):
568
+ return _copy_as_digraph(source), "bipartite" if include_rule else "species"
569
+ if hasattr(source, "to_digraph") and callable(source.to_digraph):
570
+ return _copy_as_digraph(source.to_digraph()), (
571
+ "bipartite" if include_rule else "species"
572
+ )
573
+ raise TypeError("Expected a NetworkX graph or an object with to_digraph().")
574
+
575
+
576
+ def _first_present(attrs: Mapping[str, Any], keys: Sequence[str]) -> Any:
577
+ """Return the first non-``None`` attribute value among preferred keys.
578
+
579
+ :param attrs:
580
+ Attribute mapping.
581
+ :type attrs: Mapping[str, Any]
582
+
583
+ :param keys:
584
+ Candidate attribute keys in priority order.
585
+ :type keys: Sequence[str]
586
+
587
+ :return:
588
+ First present non-``None`` value, or ``None``.
589
+ :rtype: Any
590
+ """
591
+ for k in keys:
592
+ v = attrs.get(k, None)
593
+ if v is not None:
594
+ return v
595
+ return None
596
+
597
+
598
+ def node_kind(attrs: Mapping[str, Any], config: SymmetryConfig) -> Any:
599
+ """Extract node kind from an attribute mapping.
600
+
601
+ :param attrs:
602
+ Node attribute mapping.
603
+ :type attrs: Mapping[str, Any]
604
+
605
+ :param config:
606
+ Symmetry configuration.
607
+ :type config: SymmetryConfig
608
+
609
+ :return:
610
+ Node kind value or ``None``.
611
+ :rtype: Any
612
+
613
+ .. note::
614
+ The legacy reaction spelling ``"rule"`` is normalized to
615
+ ``config.rule_kind_value`` so that two otherwise identical networks do
616
+ not canonicalize differently merely because one spells reaction nodes
617
+ ``kind="reaction"`` and the other ``kind="rule"``. See
618
+ :mod:`synkit.CRN.kinds`.
619
+ """
620
+ kind = attrs.get(config.kind_attr_key, None)
621
+ if isinstance(kind, str) and kind.strip().lower() in REACTION_KINDS:
622
+ return config.rule_kind_value
623
+ return kind
624
+
625
+
626
+ def _append_identity_token(
627
+ out: List[Any],
628
+ attrs: Mapping[str, Any],
629
+ identity_keys: Sequence[str],
630
+ config: SymmetryConfig,
631
+ ) -> None:
632
+ """Append an identity token to a node token list when available.
633
+
634
+ :param out:
635
+ Output token list to mutate.
636
+ :type out: List[Any]
637
+
638
+ :param attrs:
639
+ Node attributes.
640
+ :type attrs: Mapping[str, Any]
641
+
642
+ :param identity_keys:
643
+ Preferred identity keys.
644
+ :type identity_keys: Sequence[str]
645
+
646
+ :param config:
647
+ Symmetry configuration.
648
+ :type config: SymmetryConfig
649
+
650
+ :return:
651
+ ``None``.
652
+ :rtype: None
653
+ """
654
+ ident = _first_present(attrs, identity_keys)
655
+ if ident is not None:
656
+ out.append(("identity", freeze(ident)))
657
+ elif config.include_missing_keys and identity_keys:
658
+ out.append(("identity", None))
659
+
660
+
661
+ def _append_selected_attrs(
662
+ out: List[Any],
663
+ attrs: Mapping[str, Any],
664
+ keys: Sequence[str],
665
+ ignored_keys: Sequence[str],
666
+ ) -> None:
667
+ """Append selected attributes to a token list.
668
+
669
+ :param out:
670
+ Output token list to mutate.
671
+ :type out: List[Any]
672
+
673
+ :param attrs:
674
+ Attribute mapping.
675
+ :type attrs: Mapping[str, Any]
676
+
677
+ :param keys:
678
+ Attribute keys to include.
679
+ :type keys: Sequence[str]
680
+
681
+ :param ignored_keys:
682
+ Attribute keys to skip.
683
+ :type ignored_keys: Sequence[str]
684
+
685
+ :return:
686
+ ``None``.
687
+ :rtype: None
688
+ """
689
+ for k in keys:
690
+ if k not in ignored_keys:
691
+ out.append((k, freeze(attrs.get(k, None))))
692
+
693
+
694
+ def _species_node_token(
695
+ attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
696
+ ) -> None:
697
+ """Append species-specific token components.
698
+
699
+ :param attrs:
700
+ Node attributes.
701
+ :type attrs: Mapping[str, Any]
702
+
703
+ :param config:
704
+ Symmetry configuration.
705
+ :type config: SymmetryConfig
706
+
707
+ :param out:
708
+ Output token list to mutate.
709
+ :type out: List[Any]
710
+
711
+ :return:
712
+ ``None``.
713
+ :rtype: None
714
+ """
715
+ _append_identity_token(out, attrs, config.species_identity_keys, config)
716
+ _append_selected_attrs(
717
+ out,
718
+ attrs,
719
+ config.species_extra_attr_keys,
720
+ config.ignored_node_attr_keys,
721
+ )
722
+
723
+
724
+ def _rule_node_token(
725
+ attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
726
+ ) -> None:
727
+ """Append rule-specific token components.
728
+
729
+ :param attrs:
730
+ Node attributes.
731
+ :type attrs: Mapping[str, Any]
732
+
733
+ :param config:
734
+ Symmetry configuration.
735
+ :type config: SymmetryConfig
736
+
737
+ :param out:
738
+ Output token list to mutate.
739
+ :type out: List[Any]
740
+
741
+ :return:
742
+ ``None``.
743
+ :rtype: None
744
+ """
745
+ _append_identity_token(out, attrs, config.rule_identity_keys, config)
746
+ _append_selected_attrs(
747
+ out,
748
+ attrs,
749
+ config.rule_extra_attr_keys,
750
+ config.ignored_node_attr_keys,
751
+ )
752
+
753
+
754
+ def _other_node_token(
755
+ attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
756
+ ) -> None:
757
+ """Append generic token components for non-species, non-rule nodes.
758
+
759
+ :param attrs:
760
+ Node attributes.
761
+ :type attrs: Mapping[str, Any]
762
+
763
+ :param config:
764
+ Symmetry configuration.
765
+ :type config: SymmetryConfig
766
+
767
+ :param out:
768
+ Output token list to mutate.
769
+ :type out: List[Any]
770
+
771
+ :return:
772
+ ``None``.
773
+ :rtype: None
774
+ """
775
+ _append_selected_attrs(
776
+ out,
777
+ attrs,
778
+ config.other_node_attr_keys,
779
+ config.ignored_node_attr_keys,
780
+ )
781
+
782
+
783
+ def _append_generic_node_attrs(
784
+ attrs: Mapping[str, Any], config: SymmetryConfig, out: List[Any]
785
+ ) -> None:
786
+ """Append generic node attributes shared across all node kinds.
787
+
788
+ :param attrs:
789
+ Node attributes.
790
+ :type attrs: Mapping[str, Any]
791
+
792
+ :param config:
793
+ Symmetry configuration.
794
+ :type config: SymmetryConfig
795
+
796
+ :param out:
797
+ Output token list to mutate.
798
+ :type out: List[Any]
799
+
800
+ :return:
801
+ ``None``.
802
+ :rtype: None
803
+ """
804
+ for k in config.generic_node_attr_keys:
805
+ if k == config.kind_attr_key or k in config.ignored_node_attr_keys:
806
+ continue
807
+ out.append((k, freeze(attrs.get(k, None))))
808
+
809
+
810
+ def node_token(attrs: Mapping[str, Any], config: SymmetryConfig) -> Tuple[Any, ...]:
811
+ """Build a canonical token for a node.
812
+
813
+ The token depends on node kind, configured identity keys, optional extra
814
+ attributes, and generic attributes shared by all node kinds.
815
+
816
+ :param attrs:
817
+ Node attribute mapping.
818
+ :type attrs: Mapping[str, Any]
819
+
820
+ :param config:
821
+ Symmetry configuration controlling token content.
822
+ :type config: SymmetryConfig
823
+
824
+ :return:
825
+ Immutable node token.
826
+ :rtype: Tuple[Any, ...]
827
+
828
+ .. rubric:: Examples
829
+
830
+ .. code-block:: python
831
+
832
+ cfg = SymmetryConfig.semantic()
833
+ tok = node_token({"kind": "species", "smiles": "CCO"}, cfg)
834
+ print(tok)
835
+ """
836
+ kind = node_kind(attrs, config)
837
+ out: List[Any] = []
838
+
839
+ if config.require_kind:
840
+ out.append((config.kind_attr_key, freeze(kind)))
841
+
842
+ if kind == config.species_kind_value:
843
+ _species_node_token(attrs, config, out)
844
+ elif kind == config.rule_kind_value:
845
+ _rule_node_token(attrs, config, out)
846
+ else:
847
+ _other_node_token(attrs, config, out)
848
+
849
+ _append_generic_node_attrs(attrs, config, out)
850
+ return tuple(out)
851
+
852
+
853
+ def edge_token(attrs: Mapping[str, Any], config: SymmetryConfig) -> Tuple[Any, ...]:
854
+ """Build a canonical token for an edge.
855
+
856
+ :param attrs:
857
+ Edge attribute mapping.
858
+ :type attrs: Mapping[str, Any]
859
+
860
+ :param config:
861
+ Symmetry configuration.
862
+ :type config: SymmetryConfig
863
+
864
+ :return:
865
+ Immutable edge token.
866
+ :rtype: Tuple[Any, ...]
867
+
868
+ .. rubric:: Examples
869
+
870
+ .. code-block:: python
871
+
872
+ cfg = SymmetryConfig.semantic()
873
+ tok = edge_token({"role": "reactant", "stoich": 2}, cfg)
874
+ """
875
+ out: List[Any] = []
876
+ for k in config.edge_attr_keys:
877
+ if k in config.ignored_edge_attr_keys:
878
+ continue
879
+ out.append((k, freeze(attrs.get(k, None))))
880
+ return tuple(out)
881
+
882
+
883
+ def node_matcher(
884
+ config: SymmetryConfig,
885
+ ) -> Callable[[Dict[str, Any], Dict[str, Any]], bool]:
886
+ """Build a node matcher function based on :func:`node_token`.
887
+
888
+ :param config:
889
+ Symmetry configuration.
890
+ :type config: SymmetryConfig
891
+
892
+ :return:
893
+ Predicate comparing two node attribute dictionaries.
894
+ :rtype: Callable[[Dict[str, Any], Dict[str, Any]], bool]
895
+ """
896
+
897
+ def match(a1: Dict[str, Any], a2: Dict[str, Any]) -> bool:
898
+ return node_token(a1, config) == node_token(a2, config)
899
+
900
+ return match
901
+
902
+
903
+ def edge_matcher(
904
+ config: SymmetryConfig,
905
+ ) -> Callable[[Dict[str, Any], Dict[str, Any]], bool]:
906
+ """Build an edge matcher function based on :func:`edge_token`.
907
+
908
+ :param config:
909
+ Symmetry configuration.
910
+ :type config: SymmetryConfig
911
+
912
+ :return:
913
+ Predicate comparing two edge attribute dictionaries.
914
+ :rtype: Callable[[Dict[str, Any], Dict[str, Any]], bool]
915
+ """
916
+
917
+ def match(a1: Dict[str, Any], a2: Dict[str, Any]) -> bool:
918
+ return edge_token(a1, config) == edge_token(a2, config)
919
+
920
+ return match
921
+
922
+
923
+ def build_fast_signature(
924
+ G: nx.DiGraph,
925
+ graph_type: str,
926
+ config: SymmetryConfig,
927
+ *,
928
+ wl_color_hist: Optional[Mapping[str, int]] = None,
929
+ ) -> Tuple[Any, ...]:
930
+ """Build a fast graph signature for cheap rejection tests.
931
+
932
+ The signature includes graph size, node-token histogram, edge-token
933
+ histogram, in/out-degree histogram, and optional WL color histogram.
934
+
935
+ :param G:
936
+ Directed graph.
937
+ :type G: nx.DiGraph
938
+
939
+ :param graph_type:
940
+ Graph type label.
941
+ :type graph_type: str
942
+
943
+ :param config:
944
+ Symmetry configuration.
945
+ :type config: SymmetryConfig
946
+
947
+ :param wl_color_hist:
948
+ Optional WL color histogram.
949
+ :type wl_color_hist: Optional[Mapping[str, int]]
950
+
951
+ :return:
952
+ Fast signature tuple.
953
+ :rtype: Tuple[Any, ...]
954
+ """
955
+ node_hist = Counter(node_token(G.nodes[v], config) for v in G.nodes())
956
+ edge_hist = Counter(edge_token(attrs, config) for _, _, attrs in G.edges(data=True))
957
+ deg_hist = Counter((G.in_degree(v), G.out_degree(v)) for v in G.nodes())
958
+ return (
959
+ graph_type,
960
+ G.number_of_nodes(),
961
+ G.number_of_edges(),
962
+ tuple(sorted(node_hist.items(), key=str)),
963
+ tuple(sorted(edge_hist.items(), key=str)),
964
+ tuple(sorted(deg_hist.items(), key=str)),
965
+ tuple(sorted((wl_color_hist or {}).items(), key=str)),
966
+ )
967
+
968
+
969
+ def graph_key_from_order(
970
+ G: nx.DiGraph, order: Sequence[Any], config: SymmetryConfig
971
+ ) -> Tuple[Any, ...]:
972
+ """Build a canonical graph key from a node order.
973
+
974
+ :param G:
975
+ Directed graph.
976
+ :type G: nx.DiGraph
977
+
978
+ :param order:
979
+ Node order.
980
+ :type order: Sequence[Any]
981
+
982
+ :param config:
983
+ Symmetry configuration.
984
+ :type config: SymmetryConfig
985
+
986
+ :return:
987
+ Canonical graph key.
988
+ :rtype: Tuple[Any, ...]
989
+ """
990
+ pos = {v: i for i, v in enumerate(order)}
991
+ node_part = tuple((i, node_token(G.nodes[v], config)) for i, v in enumerate(order))
992
+ edge_rows: List[Tuple[int, int, Tuple[Any, ...]]] = []
993
+ for u, v, attrs in G.edges(data=True):
994
+ edge_rows.append((pos[u], pos[v], edge_token(attrs, config)))
995
+ edge_rows.sort(key=lambda x: (x[0], x[1], str(x[2])))
996
+ return (node_part, tuple(edge_rows))
997
+
998
+
999
+ def invert_mapping(mapping: Dict[Any, Any]) -> Dict[Any, Any]:
1000
+ """Invert a one-to-one mapping.
1001
+
1002
+ :param mapping:
1003
+ Mapping to invert.
1004
+ :type mapping: Dict[Any, Any]
1005
+
1006
+ :return:
1007
+ Inverted mapping.
1008
+ :rtype: Dict[Any, Any]
1009
+ """
1010
+ return {v: k for k, v in mapping.items()}
1011
+
1012
+
1013
+ def orbits_from_mappings(
1014
+ nodes: Sequence[Any], mappings: Iterable[Dict[Any, Any]]
1015
+ ) -> List[Set[Any]]:
1016
+ """Build orbit classes from a collection of automorphism mappings.
1017
+
1018
+ :param nodes:
1019
+ Nodes whose orbit partition should be computed.
1020
+ :type nodes: Sequence[Any]
1021
+
1022
+ :param mappings:
1023
+ Automorphism mappings.
1024
+ :type mappings: Iterable[Dict[Any, Any]]
1025
+
1026
+ :return:
1027
+ List of orbit sets.
1028
+ :rtype: List[Set[Any]]
1029
+
1030
+ .. rubric:: Examples
1031
+
1032
+ .. code-block:: python
1033
+
1034
+ nodes = ["A", "B", "C"]
1035
+ maps = [{"A": "B", "B": "A", "C": "C"}]
1036
+ print(orbits_from_mappings(nodes, maps))
1037
+ """
1038
+ parent: Dict[Any, Any] = {n: n for n in nodes}
1039
+
1040
+ def find(x: Any) -> Any:
1041
+ while parent[x] != x:
1042
+ parent[x] = parent[parent[x]]
1043
+ x = parent[x]
1044
+ return x
1045
+
1046
+ def union(a: Any, b: Any) -> None:
1047
+ ra, rb = find(a), find(b)
1048
+ if ra != rb:
1049
+ parent[rb] = ra
1050
+
1051
+ for m in mappings:
1052
+ for a, b in m.items():
1053
+ if a in parent and b in parent:
1054
+ union(a, b)
1055
+
1056
+ buckets: Dict[Any, Set[Any]] = defaultdict(set)
1057
+ for n in nodes:
1058
+ buckets[find(n)].add(n)
1059
+ return list(buckets.values())