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,239 @@
1
+ import hashlib
2
+ import networkx as nx
3
+
4
+
5
+ class GraphSignature:
6
+ """Provides methods to generate canonical signatures for graph edges (with
7
+ flexible 'order' and 'state' attributes, and node degrees/neighbor
8
+ information), various spectral invariants, adjacency matrix, and complete
9
+ graphs.
10
+
11
+ Aims for high uniqueness without relying solely on isomorphism
12
+ checks.
13
+ """
14
+
15
+ def __init__(self, graph: nx.Graph):
16
+ """Initializes the GraphSignature class with a specified graph.
17
+
18
+ :param graph: A NetworkX graph instance.
19
+ :type graph: nx.Graph
20
+ """
21
+ self.graph = graph
22
+ self._validate_graph()
23
+
24
+ def _validate_graph(self):
25
+ """Validates that all nodes have the required attributes ('element' and
26
+ 'charge'), and all edges have the required 'order' attribute as int,
27
+ float, or tuple of two floats, and optionally the 'state' attribute.
28
+
29
+ :raises ValueError: If required node or edge attributes are missing or
30
+ have invalid types.
31
+ """
32
+ for node, data in self.graph.nodes(data=True):
33
+ if "element" not in data:
34
+ raise ValueError(f"Node {node} is missing the 'element' attribute.")
35
+ if "charge" not in data:
36
+ raise ValueError(f"Node {node} is missing the 'charge' attribute.")
37
+
38
+ for u, v, data in self.graph.edges(data=True):
39
+ if "order" not in data:
40
+ raise ValueError(f"Edge ({u}, {v}) is missing the 'order' attribute.")
41
+ order = data["order"]
42
+ if isinstance(order, tuple):
43
+ if len(order) != 2 or not all(
44
+ isinstance(o, (int, float)) for o in order
45
+ ):
46
+ raise ValueError(
47
+ f"Edge ({u}, {v}) has an invalid 'order'. It must be a tuple of two ints/floats."
48
+ )
49
+ elif not isinstance(order, (int, float)):
50
+ raise ValueError(
51
+ f"Edge ({u}, {v}) has an invalid 'order'. It must be an int, float, or a tuple of two ints/floats."
52
+ )
53
+
54
+ # Optional: Validate 'state' attribute if present
55
+ state = data.get("state", "steady") # Default to 'steady' if missing
56
+ if state not in {"break", "form", "steady"}:
57
+ raise ValueError(
58
+ f"Edge ({u}, {v}) has an invalid 'state'. It must be 'break', 'form', or 'steady'."
59
+ )
60
+
61
+ def create_edge_signature(
62
+ self, include_neighbors: bool = False, max_hop: int = 2
63
+ ) -> str:
64
+ """Generates a canonical edge signature by formatting each edge with
65
+ sorted node elements (including charge), node degrees, bond order, bond
66
+ state, and optionally including neighbor information and topological
67
+ context.
68
+
69
+ :param include_neighbors: Whether to include neighbors' details in the edge signature.
70
+ :type include_neighbors: bool
71
+ :param max_hop: Maximum number of hops to include for neighbor-level structural information.
72
+ :type max_hop: int
73
+
74
+ :return: A concatenated and sorted string of edge representations.
75
+ :rtype: str
76
+ """
77
+ edge_signature_parts = []
78
+
79
+ for u, v, data in self.graph.edges(data=True):
80
+ # Retrieve bond order (default to (1.0, 1.0) if missing)
81
+ order = data.get("order", (1.0, 1.0))
82
+
83
+ # Format order as a tuple (default or actual value)
84
+ if isinstance(order, tuple):
85
+ order_str = f"{{{order[0]:.1f},{order[1]:.1f}}}"
86
+ else:
87
+ order_str = f"{float(order):.1f}"
88
+
89
+ # Get node elements and charges for both nodes
90
+ node1_element = self.graph.nodes[u].get(
91
+ "element", "X"
92
+ ) # Default to 'X' if missing
93
+ node1_charge = self.graph.nodes[u].get(
94
+ "charge", 0
95
+ ) # Default to 0 if missing
96
+ node2_element = self.graph.nodes[v].get("element", "X")
97
+ node2_charge = self.graph.nodes[v].get("charge", 0)
98
+
99
+ # Construct node representation with element and charge
100
+ node1 = f"{node1_element}{node1_charge}"
101
+ node2 = f"{node2_element}{node2_charge}"
102
+
103
+ # Optionally include neighbors in the signature
104
+ if include_neighbors:
105
+ neighbors_u = sorted(
106
+ [
107
+ f"{self.graph.nodes[neighbor].get('element', 'X')}{self.graph.nodes[neighbor].get('charge', 0)}"
108
+ + f"d{self.graph.degree(neighbor)}"
109
+ for neighbor in self.graph.neighbors(u)
110
+ ]
111
+ )
112
+ neighbors_v = sorted(
113
+ [
114
+ f"{self.graph.nodes[neighbor].get('element', 'X')}{self.graph.nodes[neighbor].get('charge', 0)}"
115
+ + f"d{self.graph.degree(neighbor)}"
116
+ for neighbor in self.graph.neighbors(v)
117
+ ]
118
+ )
119
+
120
+ # Represent neighbors within square brackets
121
+ node1_neighbors = "".join(neighbors_u)
122
+ node2_neighbors = "".join(neighbors_v)
123
+ node1 = f"{node1}[{node1_neighbors}]"
124
+ node2 = f"{node2}[{node2_neighbors}]"
125
+
126
+ # Include k-hop neighborhood information
127
+ if max_hop > 1:
128
+ node1_neighbors_khop = self._get_khop_neighbors(u, max_hop)
129
+ node2_neighbors_khop = self._get_khop_neighbors(v, max_hop)
130
+ node1 += f"[{node1_neighbors_khop}]"
131
+ node2 += f"[{node2_neighbors_khop}]"
132
+
133
+ # Sort nodes to ensure consistency in edge signature (avoid direction dependency)
134
+ node1, node2 = sorted([node1, node2])
135
+
136
+ # Format the edge signature and append it
137
+ edge_part = f"{node1}{order_str}{node2}"
138
+ edge_signature_parts.append(edge_part)
139
+
140
+ # Sort all edge signatures to ensure consistency in the final representation
141
+ return "/".join(sorted(edge_signature_parts))
142
+
143
+ def _get_khop_neighbors(self, node, max_hop):
144
+ """Retrieves the k-hop neighborhood information for a given node.
145
+
146
+ :param node: The node for which to get neighborhood information.
147
+ :type node: int
148
+ :param max_hop: Maximum number of hops for neighborhood exploration.
149
+ :type max_hop: int
150
+
151
+ :return: A concatenated string representing the k-hop neighborhood information.
152
+ :rtype: str
153
+ """
154
+ k_hop_neighbors = []
155
+ current_hop_neighbors = [node]
156
+ for _ in range(max_hop):
157
+ next_hop_neighbors = []
158
+ for n in current_hop_neighbors:
159
+ next_hop_neighbors.extend(list(self.graph.neighbors(n)))
160
+ # Filter out already seen nodes to avoid loops
161
+ next_hop_neighbors = set(next_hop_neighbors) - set(k_hop_neighbors)
162
+ k_hop_neighbors.extend(next_hop_neighbors)
163
+ current_hop_neighbors = next_hop_neighbors
164
+
165
+ # Return sorted k-hop neighborhood info
166
+ return "".join(
167
+ sorted(
168
+ [
169
+ f"{self.graph.nodes[neighbor].get('element', 'X')}{self.graph.nodes[neighbor].get('charge', 0)}"
170
+ for neighbor in k_hop_neighbors
171
+ ]
172
+ )
173
+ )
174
+
175
+ def create_wl_hash(self, iterations: int = 3) -> str:
176
+ """Generates a Weisfeiler-Lehman (WL) hash for the graph to capture its
177
+ structural features.
178
+
179
+ :param iterations: Number of WL iterations to perform.
180
+ :type iterations: int
181
+
182
+ :return: A hexadecimal hash representing the WL feature.
183
+ :rtype: str
184
+ """
185
+ # Initialize labels with both 'element' and 'charge'
186
+ labels = {
187
+ node: f"{data['element']}{data.get('charge', 0)}"
188
+ for node, data in self.graph.nodes(data=True)
189
+ }
190
+ for _ in range(iterations):
191
+ new_labels = {}
192
+ for node in self.graph.nodes():
193
+ # Gather sorted labels of neighbors
194
+ neighbor_labels = sorted(
195
+ labels[neighbor] for neighbor in self.graph.neighbors(node)
196
+ )
197
+ # Concatenate current label with neighbor labels
198
+ concatenated = labels[node] + "".join(neighbor_labels)
199
+ # Hash the concatenated string to obtain a new label
200
+ new_label = hashlib.sha256(concatenated.encode()).hexdigest()
201
+ new_labels[node] = new_label
202
+ labels = new_labels
203
+ # Aggregate all node labels into a sorted string and hash it
204
+ sorted_labels = sorted(labels.values())
205
+ aggregated = "".join(sorted_labels)
206
+ graph_hash = hashlib.sha256(aggregated.encode()).hexdigest()
207
+ return graph_hash
208
+
209
+ def create_graph_signature(
210
+ self,
211
+ include_wl_hash: bool = True,
212
+ include_neighbors: bool = True,
213
+ max_hop: int = 1,
214
+ ) -> str:
215
+ """Combines edge, various spectral invariants, and WL hash into a
216
+ single comprehensive graph signature.
217
+
218
+ :param include_wl_hash: Whether to include the Weisfeiler-Lehman hash.
219
+ :type include_wl_hash: bool
220
+ :param include_neighbors: Whether to include neighbor information in edge signatures.
221
+ :type include_neighbors: bool
222
+ :param max_hop: Maximum neighborhood depth used by edge signatures.
223
+ :type max_hop: int
224
+
225
+ :return: A concatenated string representing the complete graph signature.
226
+ :rtype: str
227
+ """
228
+ signatures = []
229
+
230
+ if include_wl_hash:
231
+ wl_signature = self.create_wl_hash()
232
+ signatures.append(f"{wl_signature}")
233
+
234
+ edge_signature = self.create_edge_signature(
235
+ include_neighbors=include_neighbors, max_hop=max_hop
236
+ )
237
+ signatures.append(f"{edge_signature}")
238
+
239
+ return "|".join(signatures)
@@ -0,0 +1,133 @@
1
+ import networkx as nx
2
+ import hashlib
3
+ from typing import Optional, Any
4
+
5
+
6
+ class HashFPs:
7
+ def __init__(
8
+ self, graph: nx.Graph, numBits: int = 256, hash_alg: str = "sha256"
9
+ ) -> None:
10
+ """Initialize the HashFPs class with a graph and configuration
11
+ settings.
12
+
13
+ :param graph: The graph to be fingerprinted.
14
+ :type graph: nx.Graph
15
+ :param numBits: Number of bits in the output binary hash. Default is 256 bits.
16
+ :type numBits: int
17
+ :param hash_alg: The hash algorithm to use, such as 'sha256' or 'sha512'.
18
+ :type hash_alg: str
19
+
20
+ :raises ValueError: If `numBits` is non-positive or if `hash_alg` is not supported by hashlib.
21
+ """
22
+ self.graph = graph
23
+ self.numBits = numBits
24
+ self.hash_alg = hash_alg
25
+ self.validate_parameters()
26
+
27
+ def validate_parameters(self) -> None:
28
+ """Validate the initial parameters for errors."""
29
+ if self.numBits <= 0:
30
+ raise ValueError("Number of bits must be positive")
31
+ if not hasattr(hashlib, self.hash_alg):
32
+ raise ValueError(f"Unsupported hash algorithm: {self.hash_alg}")
33
+
34
+ def hash_fps(
35
+ self,
36
+ start_node: Optional[int] = None,
37
+ end_node: Optional[int] = None,
38
+ max_path_length: Optional[int] = None,
39
+ ) -> str:
40
+ """Generate a binary hash fingerprint of the graph based on its paths
41
+ and cycles.
42
+
43
+ :param start_node: The starting node index for path detection.
44
+ :type start_node: Optional[int]
45
+ :param end_node: The ending node index for path detection.
46
+ :type end_node: Optional[int]
47
+ :param max_path_length: The maximum length for paths to be considered.
48
+ :type max_path_length: Optional[int]
49
+
50
+ :return: A binary string representing the truncated hash of the graph's structural features.
51
+ :rtype: str
52
+ """
53
+ hash_object = self.initialize_hash()
54
+ features = self.extract_features(start_node, end_node, max_path_length)
55
+ full_hash_binary = self.finalize_hash(hash_object, features)
56
+ return full_hash_binary
57
+
58
+ def initialize_hash(self) -> Any:
59
+ """Initialize and return the hash object based on the specified
60
+ algorithm."""
61
+ return getattr(hashlib, self.hash_alg)()
62
+
63
+ def extract_features(
64
+ self,
65
+ start_node: Optional[int],
66
+ end_node: Optional[int],
67
+ max_path_length: Optional[int],
68
+ ) -> str:
69
+ """Extract features from the graph based on paths and cycles.
70
+
71
+ :param start_node: The starting node for path detection.
72
+ :type start_node: Optional[int]
73
+ :param end_node: The ending node for path detection.
74
+ :type end_node: Optional[int]
75
+ :param max_path_length: Cutoff for path length during detection.
76
+ :type max_path_length: Optional[int]
77
+
78
+ :return: A string of concatenated feature values.
79
+ :rtype: str
80
+ """
81
+ cycles = list(nx.simple_cycles(self.graph))
82
+ paths = []
83
+ if start_node is not None and end_node is not None:
84
+ paths = list(
85
+ nx.all_simple_paths(
86
+ self.graph,
87
+ source=start_node,
88
+ target=end_node,
89
+ cutoff=max_path_length,
90
+ )
91
+ )
92
+ features = [len(c) for c in cycles] + [len(p) for p in paths]
93
+ return "".join(map(str, features))
94
+
95
+ def finalize_hash(self, hash_object: Any, features: str) -> str:
96
+ """Finalize the hash using the features extracted and return the hash
97
+ as a binary string.
98
+
99
+ :param hash_object: The hash object.
100
+ :type hash_object: Any
101
+ :param features: Concatenated string of graph features.
102
+ :type features: str
103
+
104
+ :return: The final binary string of the hash, truncated or extended to `numBits`.
105
+ :rtype: str
106
+ """
107
+ hash_object.update(features.encode())
108
+ full_hash_binary = bin(int(hash_object.hexdigest(), 16))[2:]
109
+ if len(full_hash_binary) < self.numBits:
110
+ full_hash_binary += self.iterative_deepening(
111
+ hash_object, self.numBits - len(full_hash_binary)
112
+ )
113
+ return full_hash_binary[: self.numBits]
114
+
115
+ def iterative_deepening(self, hash_object: Any, remaining_bits: int) -> str:
116
+ """Extend hash length using iterative hashing until the desired bit
117
+ length is achieved.
118
+
119
+ :param hash_object: The hash object for iterative deepening.
120
+ :type hash_object: hashlib._Hash
121
+ :param remaining_bits: Number of bits needed to reach `numBits`.
122
+ :type remaining_bits: int
123
+
124
+ :return: Additional binary data to achieve the desired hash length.
125
+ :rtype: str
126
+ """
127
+ additional_data = ""
128
+ while (
129
+ len(additional_data) * 4 < remaining_bits
130
+ ): # Each hex digit represents 4 bits
131
+ hash_object.update(additional_data.encode())
132
+ additional_data += hash_object.hexdigest()
133
+ return bin(int(additional_data, 16))[2:][:remaining_bits]
@@ -0,0 +1,88 @@
1
+ import networkx as nx
2
+ import hashlib
3
+ from typing import Any
4
+
5
+
6
+ class MorganFPs:
7
+ def __init__(
8
+ self,
9
+ graph: nx.Graph,
10
+ radius: int = 3,
11
+ nBits: int = 1024,
12
+ hash_alg: str = "sha256",
13
+ ):
14
+ """Initialize the MorganFPs class to generate fingerprints based on the
15
+ Morgan algorithm, approximating Extended Connectivity Fingerprints
16
+ (ECFPs).
17
+
18
+ :param graph: The graph to analyze.
19
+ :type graph: nx.Graph
20
+ :param radius: The radius to consider for node neighborhood analysis.
21
+ :type radius: int
22
+ :param nBits: Total number of bits in the final fingerprint output.
23
+ :type nBits: int
24
+ :param hash_alg: Hash algorithm to use for generating hashes of node
25
+ neighborhoods.
26
+ :type hash_alg: str
27
+ """
28
+ self.graph = graph
29
+ self.radius = radius
30
+ self.nBits = nBits
31
+ self.hash_alg = hash_alg
32
+ self.hash_function = getattr(hashlib, self.hash_alg)
33
+
34
+ def generate_fingerprint(self) -> str:
35
+ """Generate a binary string fingerprint of the graph based on the local
36
+ environments of nodes. Ensures the output is exactly `nBits` in length
37
+ using iterative deepening if necessary.
38
+
39
+ :return: A binary string of length `nBits` representing the fingerprint of the graph.
40
+ :rtype: str
41
+ """
42
+ fingerprint = ""
43
+ for node in self.graph.nodes():
44
+ neighborhood = nx.single_source_shortest_path_length(
45
+ self.graph, node, cutoff=self.radius
46
+ )
47
+ neighborhood_str = "-".join(
48
+ [
49
+ f"{nbr}-{dist}"
50
+ for nbr, dist in sorted(neighborhood.items())
51
+ if nbr != node
52
+ ]
53
+ )
54
+ hash_obj = self.hash_function(neighborhood_str.encode())
55
+ node_hash = bin(int(hash_obj.hexdigest(), 16))[2:].zfill(
56
+ hash_obj.digest_size * 8
57
+ )
58
+ if len(fingerprint) + len(node_hash) > self.nBits:
59
+ needed_bits = self.nBits - len(fingerprint)
60
+ node_hash = node_hash[:needed_bits]
61
+ fingerprint += node_hash
62
+ if len(fingerprint) == self.nBits:
63
+ return fingerprint
64
+
65
+ if len(fingerprint) < self.nBits:
66
+ fingerprint += self.iterative_deepening(
67
+ hash_obj, self.nBits - len(fingerprint)
68
+ )
69
+ return fingerprint
70
+
71
+ def iterative_deepening(self, hash_object: Any, remaining_bits: int) -> str:
72
+ """Extend the hash length using iterative hashing until the desired bit
73
+ length is achieved.
74
+
75
+ :param hash_object: The hash object used for iterative deepening.
76
+ :type hash_object: hashlib._Hash
77
+ :param remaining_bits: Number of bits needed to complete the fingerprint to
78
+ `nBits`.
79
+ :type remaining_bits: int
80
+
81
+ :return: Additional binary data to achieve the desired hash length.
82
+ :rtype: str
83
+ """
84
+ additional_data = ""
85
+ while len(additional_data) * 4 < remaining_bits:
86
+ hash_object.update(additional_data.encode())
87
+ additional_data += hash_object.hexdigest()
88
+ return bin(int(additional_data, 16))[2:][:remaining_bits]
@@ -0,0 +1,81 @@
1
+ import networkx as nx
2
+ import hashlib
3
+ from typing import Any
4
+
5
+
6
+ class PathFPs:
7
+ def __init__(
8
+ self,
9
+ graph: nx.Graph,
10
+ max_length: int = 10,
11
+ nBits: int = 1024,
12
+ hash_alg: str = "sha256",
13
+ ) -> None:
14
+ """Initialize the PathFPs class to create a binary fingerprint based on
15
+ paths in a graph.
16
+
17
+ :param graph: Graph on which to perform analysis.
18
+ :type graph: nx.Graph
19
+ :param max_length: Limit on path lengths considered in the fingerprint.
20
+ :type max_length: int
21
+ :param nBits: Size of the binary fingerprint in bits.
22
+ :type nBits: int
23
+ :param hash_alg: Cryptographic hash function used for path hashing.
24
+ :type hash_alg: str
25
+ """
26
+ self.graph = graph
27
+ self.max_length = max_length
28
+ self.nBits = nBits
29
+ self.hash_alg = hash_alg
30
+ self.hash_function = getattr(hashlib, self.hash_alg)
31
+
32
+ def generate_fingerprint(self) -> str:
33
+ """Generate a binary string fingerprint of the graph by hashing paths
34
+ up to a certain length and combining them.
35
+
36
+ :return: A binary string of length `nBits` that represents the fingerprint of the graph.
37
+ :rtype: str
38
+ """
39
+ fingerprint = ""
40
+ for node in self.graph.nodes():
41
+ for target in self.graph.nodes():
42
+ if node != target:
43
+ for path in nx.all_simple_paths(
44
+ self.graph, source=node, target=target, cutoff=self.max_length
45
+ ):
46
+ path_str = "-".join(map(str, path))
47
+ hash_obj = self.hash_function(path_str.encode())
48
+ path_hash = bin(int(hash_obj.hexdigest(), 16))[2:].zfill(
49
+ hash_obj.digest_size * 8
50
+ )
51
+ if len(fingerprint) + len(path_hash) > self.nBits:
52
+ needed_bits = self.nBits - len(fingerprint)
53
+ path_hash = path_hash[:needed_bits]
54
+ fingerprint += path_hash
55
+ if len(fingerprint) == self.nBits:
56
+ return fingerprint
57
+
58
+ if len(fingerprint) < self.nBits:
59
+ fingerprint += self.iterative_deepening(
60
+ hash_obj, self.nBits - len(fingerprint)
61
+ )
62
+ return fingerprint
63
+
64
+ def iterative_deepening(self, hash_object: Any, remaining_bits: int) -> str:
65
+ """Extend the hash length using iterative hashing until the desired bit
66
+ length is achieved.
67
+
68
+ :param hash_object: The hash object used for iterative deepening.
69
+ :type hash_object: hashlib._Hash
70
+ :param remaining_bits: Number of bits needed to complete the fingerprint
71
+ to `nBits`.
72
+ :type remaining_bits: int
73
+
74
+ :return: Additional binary data to achieve the desired hash length.
75
+ :rtype: str
76
+ """
77
+ additional_data = ""
78
+ while len(additional_data) * 4 < remaining_bits:
79
+ hash_object.update(additional_data.encode())
80
+ additional_data += hash_object.hexdigest()
81
+ return bin(int(additional_data, 16))[2:][:remaining_bits]
@@ -0,0 +1,134 @@
1
+ import copy
2
+ import networkx as nx
3
+ from typing import List, Dict, Union, Tuple
4
+
5
+
6
+ class WLHash:
7
+ """A class that implements the Weisfeiler-Lehman graph hashing algorithm,
8
+ supporting multiple node/edge attributes for hashing.
9
+
10
+ :ivar node: A single attribute name or a list of attribute names for nodes used in hashing.
11
+ :ivar edge: A single attribute name or a list of attribute names for edges used in hashing.
12
+ :ivar iterations: Number of iterations for the Weisfeiler-Lehman algorithm.
13
+ :ivar digest_size: Length of the hash to be generated.
14
+ """
15
+
16
+ def __init__(
17
+ self,
18
+ node: Union[str, List[str]] = ["element", "charge"],
19
+ edge: Union[str, List[str]] = "order",
20
+ iterations: int = 5,
21
+ digest_size: int = 16,
22
+ ):
23
+ """Initializes the WLHash class with configuration for hashing.
24
+
25
+ :param node: A node attribute name or list of node attribute names.
26
+ :param edge: An edge attribute name or list of edge attribute names.
27
+ :param iterations: The number of WL iterations (default 5).
28
+ :param digest_size: The length of the generated hash (default 16).
29
+ """
30
+ self.node = node
31
+ self.edge = edge
32
+ self.iterations = iterations
33
+ self.digest_size = digest_size
34
+
35
+ def _prepare_graph(
36
+ self, graph: nx.Graph
37
+ ) -> Tuple[nx.Graph, Union[str, None], Union[str, None]]:
38
+ """Prepare a deep copy of the graph with combined/missing node and edge
39
+ attributes.
40
+
41
+ Returns (H, node_attr_name, edge_attr_name).
42
+ """
43
+ # Deep-copy to avoid mutating original graph
44
+ H = copy.deepcopy(graph)
45
+
46
+ # --- NODE ATTRIBUTE HANDLING ---
47
+ if isinstance(self.node, (list, tuple)) and len(self.node) > 1:
48
+ combined_node_attr = "_wl_hash_node_attr"
49
+ for n, data in H.nodes(data=True):
50
+ # Combine each attribute's string value (default empty)
51
+ vals = [str(data.get(attr, "")) for attr in self.node]
52
+ data[combined_node_attr] = "|".join(vals)
53
+ node_attr_name = combined_node_attr
54
+ else:
55
+ node_attr_name = (
56
+ self.node
57
+ if isinstance(self.node, str)
58
+ else (self.node[0] if self.node else None)
59
+ )
60
+ # Ensure missing attributes default to empty string
61
+ if node_attr_name:
62
+ for _, data in H.nodes(data=True):
63
+ data.setdefault(node_attr_name, "")
64
+
65
+ # --- EDGE ATTRIBUTE HANDLING ---
66
+ if isinstance(self.edge, (list, tuple)) and len(self.edge) > 1:
67
+ combined_edge_attr = "_wl_hash_edge_attr"
68
+ for u, v, data in H.edges(data=True):
69
+ vals = [str(data.get(attr, "")) for attr in self.edge]
70
+ data[combined_edge_attr] = "|".join(vals)
71
+ edge_attr_name = combined_edge_attr
72
+ else:
73
+ edge_attr_name = (
74
+ self.edge
75
+ if isinstance(self.edge, str)
76
+ else (self.edge[0] if self.edge else None)
77
+ )
78
+ if edge_attr_name:
79
+ for _, _, data in H.edges(data=True):
80
+ data.setdefault(edge_attr_name, "")
81
+
82
+ return H, node_attr_name, edge_attr_name
83
+
84
+ def weisfeiler_lehman_graph_hash(self, graph: nx.Graph) -> str:
85
+ """Computes the WL graph hash for the entire graph."""
86
+ G, node_attr, edge_attr = self._prepare_graph(graph)
87
+ return nx.weisfeiler_lehman_graph_hash(
88
+ G,
89
+ node_attr=node_attr,
90
+ edge_attr=edge_attr,
91
+ iterations=self.iterations,
92
+ digest_size=self.digest_size,
93
+ )
94
+
95
+ def weisfeiler_lehman_subgraph_hashes(
96
+ self, graph: nx.Graph
97
+ ) -> Dict[Union[int, str], List[str]]:
98
+ """Computes the WL subgraph hashes for each node in the graph."""
99
+ G, node_attr, edge_attr = self._prepare_graph(graph)
100
+ return nx.weisfeiler_lehman_subgraph_hashes(
101
+ G,
102
+ node_attr=node_attr,
103
+ edge_attr=edge_attr,
104
+ iterations=self.iterations,
105
+ digest_size=self.digest_size,
106
+ )
107
+
108
+ def process_data(
109
+ self,
110
+ data: List[Dict[str, Union[str, nx.Graph]]],
111
+ graph_key: str = "ITS",
112
+ subgraph: bool = False,
113
+ ) -> List[Dict[str, Union[str, None]]]:
114
+ """Applies WL hashing (or subgraph hashing) to a list of data entries.
115
+
116
+ Each entry must contain a graph under 'graph_key'.
117
+ """
118
+ for entry in data:
119
+ if graph_key in entry and isinstance(entry[graph_key], nx.Graph):
120
+ graph = entry[graph_key]
121
+ try:
122
+ if subgraph:
123
+ entry["WL"] = self.weisfeiler_lehman_subgraph_hashes(graph)
124
+ else:
125
+ entry["WL"] = self.weisfeiler_lehman_graph_hash(graph)
126
+ except Exception as e:
127
+ print(f"Error processing graph {entry.get('name', 'Unnamed')}: {e}")
128
+ entry["WL"] = None
129
+ else:
130
+ print(
131
+ f"Missing or invalid '{graph_key}' for graph in data: {entry.get('name', 'Unnamed')}"
132
+ )
133
+ entry["WL"] = None
134
+ return data