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,696 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import defaultdict
4
+
5
+ import numpy as np
6
+ from scipy.optimize import linear_sum_assignment
7
+
8
+ from ...graph.labeled_graph import LabeledGraph
9
+ from ...graph.synkit_adapter import canonical_graph_hash, stable_int_token
10
+
11
+
12
+ class GraphMatcher:
13
+ """WL-partitioned sequential linear-assignment (SLAP) matcher.
14
+
15
+ Uses graph-label refinement and successive LAP decisions to construct
16
+ heuristic mappings. This is the default matching engine of ``AAMapper``;
17
+ it does not itself solve a reduced uncertainty kernel or run PABS.
18
+ """
19
+
20
+ _INF_INT = 100000
21
+ _AUT_DEDUP_MAX_NODES = 20
22
+ _AUT_DEDUP_MAX_AUTOMORPHISMS = 256
23
+
24
+ def __init__(
25
+ self,
26
+ binary=False,
27
+ max_lap_fingerprints=10000,
28
+ cache_label_blocks=False,
29
+ deterministic_labels=False,
30
+ ):
31
+ self.binary = binary
32
+ self.max_lap_fingerprints = max_lap_fingerprints
33
+ self.cache_label_blocks = cache_label_blocks
34
+ self.deterministic_labels = deterministic_labels
35
+ self._stack = []
36
+ self._visited = set()
37
+ self._label_block_cache = {}
38
+ self.results = []
39
+ self.minval = self._INF_INT
40
+ self._keep_lap_sols = True
41
+
42
+ def reset(self):
43
+ """Clear state."""
44
+ self._stack.clear()
45
+ self._visited.clear()
46
+ self._label_block_cache.clear()
47
+ self.results.clear()
48
+ self.minval = self._INF_INT
49
+
50
+ def get_maps(self, lgp, break_sym_targets=None, interactive=False, base=None):
51
+ """Find the best scored mapping states reached by WL/SLAP matching."""
52
+ self.reset()
53
+ interactive = bool(interactive and break_sym_targets is not None)
54
+ self._keep_lap_sols = break_sym_targets is not None
55
+
56
+ seed = [lgp[0].copy(), lgp[1].copy()]
57
+ pending = [[lgp[0].copy(), lgp[1].copy()]]
58
+ completed = []
59
+
60
+ if self.binary:
61
+ for pair in pending:
62
+ for graph in pair:
63
+ graph.binarize_graph()
64
+
65
+ chosen = [[], []]
66
+ if interactive:
67
+ base = self._read_index_base(base)
68
+
69
+ while pending:
70
+ self._stack.extend(pending)
71
+ self.results.clear()
72
+ self.minval = self._INF_INT
73
+ self._drain_stack()
74
+ self._keep_minimum_results()
75
+
76
+ if break_sym_targets is None:
77
+ break
78
+ if interactive:
79
+ pending, chosen = self._interactive_symmetry_split(
80
+ seed,
81
+ base,
82
+ chosen,
83
+ break_sym_targets,
84
+ )
85
+ self._visited.clear()
86
+ else:
87
+ pending, completed = self._symmetry_split(completed, break_sym_targets)
88
+
89
+ if break_sym_targets is not None and not interactive:
90
+ self.results = completed
91
+
92
+ self._keep_minimum_results()
93
+ if break_sym_targets is not None:
94
+ self._drop_isomorphic_symmetry_choices(seed, break_sym_targets)
95
+
96
+ for result in self.results:
97
+ _compress_pair_labels(result["lgp"])
98
+ if interactive:
99
+ result["base"] = base
100
+ result["choices"] = ";".join(
101
+ f"{i + base}>>{j + base}" for i, j in zip(chosen[0], chosen[1])
102
+ )
103
+
104
+ def _read_index_base(self, base):
105
+ while base not in [0, 1]:
106
+ try:
107
+ base = int(input("select 0 or 1-based indexing [0/1]:"))
108
+ except ValueError:
109
+ pass
110
+ return base
111
+
112
+ def _drain_stack(self):
113
+ while self._stack:
114
+ self._solve_partition_path(self._stack.pop())
115
+
116
+ def _keep_minimum_results(self):
117
+ if not self.results:
118
+ return
119
+ best = min(result["val"] for result in self.results)
120
+ self.results = [result for result in self.results if result["val"] == best]
121
+
122
+ def _drop_isomorphic_symmetry_choices(self, initial_pair, break_sym_targets):
123
+ if len(self.results) < 2:
124
+ return
125
+
126
+ if self._dedup_with_automorphism_keys(initial_pair, break_sym_targets):
127
+ return
128
+
129
+ merged_initial = _merge_pair(initial_pair)
130
+ kept = []
131
+ seen = set()
132
+ for result in self.results:
133
+ merged = merged_initial.copy()
134
+ shift = len(result["lgp"][0].labels)
135
+ for react_idx in break_sym_targets:
136
+ label = result["lgp"][0].labels[react_idx]
137
+ product_idx = result["lgp"][1].label2idxs[label][0]
138
+ merged.graph[react_idx][product_idx + shift] = 1
139
+ merged.graph[product_idx + shift][react_idx] = 1
140
+ key = canonical_graph_hash(merged, binary=True)
141
+ if key in seen:
142
+ continue
143
+ seen.add(key)
144
+ kept.append(result)
145
+ self.results = kept
146
+
147
+ def _dedup_with_automorphism_keys(self, initial_pair, break_sym_targets):
148
+ try:
149
+ if len(initial_pair[0].labels) > self._AUT_DEDUP_MAX_NODES:
150
+ return False
151
+
152
+ from ..exact.kernel_solvers.enumerate import (
153
+ _automorphism_permutations,
154
+ canonical_partial_mapping_key,
155
+ )
156
+
157
+ for graph in initial_pair:
158
+ perms = _automorphism_permutations(
159
+ graph,
160
+ self.binary,
161
+ self._AUT_DEDUP_MAX_AUTOMORPHISMS + 1,
162
+ )
163
+ if len(perms) > self._AUT_DEDUP_MAX_AUTOMORPHISMS:
164
+ return False
165
+
166
+ seen = set()
167
+ unique = []
168
+ for result in self.results:
169
+ pairs = []
170
+ for react_idx in break_sym_targets:
171
+ label = result["lgp"][0].labels[react_idx]
172
+ product_idxs = result["lgp"][1].label2idxs.get(label, [])
173
+ if product_idxs:
174
+ pairs.append((react_idx, product_idxs[0]))
175
+ key = canonical_partial_mapping_key(initial_pair, pairs, self.binary)
176
+ if key in seen:
177
+ continue
178
+ seen.add(key)
179
+ unique.append(result)
180
+ self.results = unique
181
+ return True
182
+ except Exception:
183
+ return False
184
+
185
+ def _symmetry_split(self, completed, targets):
186
+ next_pairs = []
187
+ while self.results:
188
+ result = self.results.pop()
189
+ pair = result["lgp"]
190
+ label = self._next_symmetry_label(pair, targets)
191
+ if label is None:
192
+ completed.append(result)
193
+ continue
194
+
195
+ new_label = _fresh_label(pair)
196
+ sols, class_pair = result["lap_sols"][label]
197
+ for sol in sols:
198
+ if len(set(sol["groups_pair"][0])) > 1:
199
+ continue
200
+
201
+ react_class = list(class_pair[0].values())[0]
202
+ react_idx = min(react_class["idxs"])
203
+ product_group = (
204
+ 0
205
+ if sol["fingerprint"] is None
206
+ else int(np.where(sol["fingerprint"][0] > 0)[0][0])
207
+ )
208
+ product_class = list(class_pair[1].values())[product_group]
209
+
210
+ wl_labels = _initial_wl_labels(pair[1])
211
+ seen_wl = set()
212
+ for product_idx in product_class["idxs"]:
213
+ wl_label = wl_labels[product_idx]
214
+ if wl_label in seen_wl:
215
+ continue
216
+ seen_wl.add(wl_label)
217
+ branched = [pair[0].copy(), pair[1].copy()]
218
+ _assign_singleton_label(branched[0], label, react_idx, new_label)
219
+ _assign_singleton_label(branched[1], label, product_idx, new_label)
220
+
221
+ key = _label_state_key(branched)
222
+ if key in self._visited:
223
+ continue
224
+ self._clear_cached_labels(branched, [label, new_label])
225
+ self._visited.add(key)
226
+ next_pairs.append(branched)
227
+ return next_pairs, completed
228
+
229
+ def _next_symmetry_label(self, pair, targets):
230
+ for label in reversed(self._labels_by_size(pair)):
231
+ idxs = pair[0].label2idxs[label]
232
+ if len(idxs) > 1 and idxs[0] in targets:
233
+ return label
234
+ return None
235
+
236
+ def _interactive_symmetry_split(self, initial_pair, base, chosen, targets):
237
+ choices = self._collect_interactive_choices(targets)
238
+ selected = None
239
+ min_count = self._INF_INT
240
+ for react_idx in targets:
241
+ candidates = choices[react_idx]
242
+ if 1 < len(candidates) < min_count:
243
+ selected = (react_idx, candidates)
244
+ min_count = len(candidates)
245
+
246
+ if selected is None:
247
+ return [], chosen
248
+
249
+ react_idx, candidates = selected
250
+ options = ", ".join(str(i + base) for i in sorted(candidates))
251
+ while True:
252
+ try:
253
+ product_idx = (
254
+ int(input(f"{react_idx + base} >> ? (probably in {options}):"))
255
+ - base
256
+ )
257
+ break
258
+ except ValueError:
259
+ pass
260
+
261
+ chosen[0].append(react_idx)
262
+ chosen[1].append(product_idx)
263
+ constrained = [initial_pair[0].copy(), initial_pair[1].copy()]
264
+ next_label = 1000
265
+ for r_idx, p_idx in zip(chosen[0], chosen[1]):
266
+ while next_label in constrained[0].label2idxs:
267
+ next_label += 1
268
+ constrained[0].labels[r_idx] = next_label
269
+ constrained[1].labels[p_idx] = next_label
270
+ next_label += 1
271
+ constrained[0].build_label2idxs()
272
+ constrained[1].build_label2idxs()
273
+ return [constrained], chosen
274
+
275
+ def _collect_interactive_choices(self, targets):
276
+ choices = defaultdict(set)
277
+ for result in self.results:
278
+ pair = result["lgp"]
279
+ for label, react_idxs in pair[0].label2idxs.items():
280
+ if react_idxs[0] not in targets:
281
+ continue
282
+ sols, class_pair = result["lap_sols"][label]
283
+ left_classes = list(class_pair[0].values())
284
+ right_classes = list(class_pair[1].values())
285
+ n_left = len(left_classes)
286
+ n_right = len(right_classes)
287
+ if n_left == 1 or n_right == 1:
288
+ fingerprint = np.ones((n_left, n_right), dtype=bool)
289
+ else:
290
+ fingerprint = np.zeros((n_left, n_right), dtype=bool)
291
+ for sol in sols:
292
+ if len(set(sol["groups_pair"][0])) == 1:
293
+ fingerprint |= sol["fingerprint"] > 0
294
+ for i, left in enumerate(left_classes):
295
+ for j, right in enumerate(right_classes):
296
+ if not fingerprint[i, j]:
297
+ continue
298
+ for react_idx in left["idxs"]:
299
+ choices[react_idx].update(right["idxs"])
300
+ return choices
301
+
302
+ def _solve_partition_path(self, pair, avoid_multi_sols=None):
303
+ avoid_multi_sols = [1] if avoid_multi_sols is None else avoid_multi_sols
304
+ total = 0
305
+ lap_sols = {}
306
+
307
+ for label in self._labels_by_size(pair):
308
+ cached = label in pair[0]._irred_labels
309
+ if cached:
310
+ sols, class_pair = pair[0]._irred_labels[label]
311
+ else:
312
+ sols, class_pair = self._solve_label_block(pair, label)
313
+
314
+ total += sols[0]["val"]
315
+ if total > self.minval:
316
+ return
317
+
318
+ irreducible = cached or self._should_stop_on_multiple(
319
+ pair, label, sols, avoid_multi_sols
320
+ )
321
+ if not irreducible:
322
+ for sol in sols:
323
+ if not sol["proper"]:
324
+ continue
325
+ if len(set(sol["groups_pair"][0])) == 1:
326
+ irreducible = True
327
+ break
328
+ branched = self._refine_by_solution(pair, label, sol, class_pair)
329
+ key = _label_state_key(branched)
330
+ if key not in self._visited:
331
+ self._visited.add(key)
332
+ self._stack.append(branched)
333
+
334
+ if self._keep_lap_sols:
335
+ lap_sols[label] = (sols, _classes_as_legacy_pair(class_pair))
336
+ if irreducible:
337
+ pair[0]._irred_labels.setdefault(label, (sols, class_pair))
338
+ continue
339
+ return
340
+
341
+ self.results.append({"lgp": pair, "val": total, "lap_sols": lap_sols})
342
+ self.minval = min(self.minval, total)
343
+
344
+ def _should_stop_on_multiple(self, pair, label, sols, avoid_multi_sols):
345
+ initial_label = pair[0]._ini_labels[pair[0].label2idxs[label][0]]
346
+ if initial_label not in avoid_multi_sols:
347
+ return False
348
+ return sum(1 for sol in sols if sol["proper"]) > 1
349
+
350
+ def _labels_by_size(self, pair):
351
+ return sorted(
352
+ pair[0].label2idxs, key=lambda label: -len(pair[0].label2idxs[label])
353
+ )
354
+
355
+ def _solve_label_block(self, pair, label):
356
+ key = self._label_block_key(pair, label)
357
+ if key is not None and key in self._label_block_cache:
358
+ cost, masks, n_classes, class_pair = self._label_block_cache[key]
359
+ return self._enumerate_lap_fingerprints(cost, masks, n_classes), class_pair
360
+
361
+ cost, masks, n_classes, class_pair = self._cost_matrix(pair, label)
362
+ if key is not None:
363
+ self._label_block_cache[key] = (cost, masks, n_classes, class_pair)
364
+ return self._enumerate_lap_fingerprints(cost, masks, n_classes), class_pair
365
+
366
+ def _label_block_key(self, pair, label):
367
+ if not self.cache_label_blocks:
368
+ return None
369
+ return (label, tuple(pair[0].labels), tuple(pair[1].labels))
370
+
371
+ def _cost_matrix(self, pair, label):
372
+ blocks = [self._partition_block(graph, label) for graph in pair]
373
+ left_classes, left_offsets = blocks[0]
374
+ right_classes, right_offsets = blocks[1]
375
+ cost = np.empty((left_offsets[-1], right_offsets[-1]), dtype=int)
376
+ for left_pos, left in enumerate(left_classes):
377
+ a0, a1 = left_offsets[left_pos], left_offsets[left_pos + 1]
378
+ left_nbrs = left[2]
379
+ for right_pos, right in enumerate(right_classes):
380
+ b0, b1 = right_offsets[right_pos], right_offsets[right_pos + 1]
381
+ cost[a0:a1, b0:b1] = self._neighborhood_cost(left_nbrs, right[2])
382
+
383
+ n_classes = [len(left_classes), len(right_classes)]
384
+ masks = (
385
+ [
386
+ _block_mask(n_classes[0], left_offsets),
387
+ _block_mask(n_classes[1], right_offsets),
388
+ ]
389
+ if n_classes[0] > 1 and n_classes[1] > 1
390
+ else [None, None]
391
+ )
392
+ return cost, masks, n_classes, [left_classes, right_classes]
393
+
394
+ def _partition_block(self, graph, label):
395
+ classes = self._next_classes(graph, label)
396
+ offsets = [0]
397
+ for _, idxs, _ in classes:
398
+ offsets.append(offsets[-1] + len(idxs))
399
+
400
+ return classes, offsets
401
+
402
+ def _next_classes(self, graph, label):
403
+ buckets = {}
404
+ labels = graph.labels
405
+ adjacency = graph.graph
406
+ for idx in graph.label2idxs[label]:
407
+ if self.binary:
408
+ nbrs = {}
409
+ for nbr in adjacency.get(idx, {}):
410
+ nbr_label = labels[nbr]
411
+ nbrs[nbr_label] = nbrs.get(nbr_label, 0) + 1
412
+ if self.deterministic_labels:
413
+ signature = (label, tuple(sorted(nbrs.items())))
414
+ else:
415
+ signature = (label, frozenset(nbrs.items()))
416
+ else:
417
+ nbrs = defaultdict(list)
418
+ for nbr, weight in adjacency.get(idx, {}).items():
419
+ nbrs[labels[nbr]].append(weight)
420
+ nbrs = {
421
+ nbr_label: sorted(weights, reverse=True)
422
+ for nbr_label, weights in nbrs.items()
423
+ }
424
+ signature = (
425
+ label,
426
+ tuple(
427
+ (nbr_label, tuple(weights))
428
+ for nbr_label, weights in sorted(nbrs.items())
429
+ ),
430
+ )
431
+ color = self._label_token(signature)
432
+ if color not in buckets:
433
+ buckets[color] = [color, [], nbrs]
434
+ buckets[color][1].append(idx)
435
+ return list(buckets.values())
436
+
437
+ def _label_token(self, signature):
438
+ if self.deterministic_labels:
439
+ return stable_int_token(signature)
440
+ return hash(signature) & 0x7FFFFFFFFFFFFFFF
441
+
442
+ def _neighborhood_cost(self, left, right):
443
+ if self.binary:
444
+ cost = 0
445
+ for label, count in left.items():
446
+ other = right.get(label)
447
+ cost += count if other is None else abs(count - other)
448
+ for label, count in right.items():
449
+ if label not in left:
450
+ cost += count
451
+ return cost
452
+
453
+ cost = 0
454
+ for label, weights in left.items():
455
+ cost += self._weight_multiset_cost(weights, right.get(label, []))
456
+ for label, weights in right.items():
457
+ if label not in left:
458
+ cost += sum(weights)
459
+ return cost
460
+
461
+ def _weight_multiset_cost(self, left, right):
462
+ if self.binary:
463
+ return abs(left - right)
464
+
465
+ count = min(len(left), len(right))
466
+ paired = sum(abs(left[i] - right[i]) for i in range(count))
467
+ return paired + sum(left[count:]) + sum(right[count:])
468
+
469
+ def _enumerate_lap_fingerprints(self, cost, masks, n_classes):
470
+ if n_classes[0] == 1 or n_classes[1] == 1:
471
+ row, col = linear_sum_assignment(cost)
472
+ sols = [
473
+ {
474
+ "row": row,
475
+ "col": col,
476
+ "val": np.sum(cost[row, col]),
477
+ "fingerprint": None,
478
+ "groups_pair": [[0] * n_classes[0], [0] * n_classes[1]],
479
+ }
480
+ ]
481
+ else:
482
+ sols = self._enumerate_structural_fingerprints(cost, masks, n_classes)
483
+
484
+ self._mark_proper_solutions(sols)
485
+ return sols
486
+
487
+ def _enumerate_structural_fingerprints(self, cost, masks, n_classes):
488
+ solutions = []
489
+ best = None
490
+ perturbed = 100 * cost
491
+ for iteration in range(self.max_lap_fingerprints):
492
+ row, col = linear_sum_assignment(perturbed)
493
+ value = np.sum(cost[row, col])
494
+ if iteration == 0:
495
+ best = value
496
+ if value != best:
497
+ break
498
+
499
+ fingerprint = masks[0][:, row] @ masks[1][:, col].T
500
+ if any(np.all(fingerprint == sol["fingerprint"]) for sol in solutions):
501
+ break
502
+
503
+ solutions.append(
504
+ {
505
+ "row": row,
506
+ "col": col,
507
+ "val": value,
508
+ "fingerprint": fingerprint,
509
+ "groups_pair": _fingerprint_components(fingerprint, n_classes),
510
+ }
511
+ )
512
+ perturbed += ((masks[0].T @ fingerprint @ masks[1]) > 0).astype(int)
513
+ return solutions
514
+
515
+ def _mark_proper_solutions(self, sols):
516
+ if not sols:
517
+ return
518
+ group_rows = [sol["groups_pair"][0] + sol["groups_pair"][1] for sol in sols]
519
+ width = len(group_rows[0])
520
+ for sol in sols:
521
+ sol["proper"] = True
522
+
523
+ for i, row_i in enumerate(group_rows):
524
+ for j, row_j in enumerate(group_rows):
525
+ if i == j or not sols[j]["proper"]:
526
+ continue
527
+ image = defaultdict(set)
528
+ for idx in range(width):
529
+ image[row_i[idx]].add(row_j[idx])
530
+ if all(len(values) == 1 for values in image.values()):
531
+ sols[i]["proper"] = False
532
+ break
533
+
534
+ def _refine_by_solution(self, pair, label, sol, class_pair):
535
+ refined = [pair[0].copy(), pair[1].copy()]
536
+
537
+ group_to_label = {}
538
+ for group, (next_label, _, _) in zip(sol["groups_pair"][1], class_pair[1]):
539
+ group_to_label[group] = next_label
540
+
541
+ fresh = _fresh_label(refined)
542
+ for group in sol["groups_pair"][0]:
543
+ if group not in group_to_label:
544
+ group_to_label[group] = fresh
545
+ fresh += 1
546
+
547
+ for side in range(2):
548
+ merged = defaultdict(list)
549
+ for group, (next_label, idxs, _) in zip(
550
+ sol["groups_pair"][side], class_pair[side]
551
+ ):
552
+ merged[group_to_label[group]].extend(idxs)
553
+
554
+ del refined[side].label2idxs[label]
555
+ for next_label, idxs in merged.items():
556
+ refined[side].label2idxs[next_label] = sorted(idxs)
557
+ for idx in idxs:
558
+ refined[side].labels[idx] = next_label
559
+
560
+ self._clear_cached_labels(refined, group_to_label.values())
561
+ return refined
562
+
563
+ def _clear_cached_labels(self, pair, parent_labels):
564
+ neighboring_labels = defaultdict(set)
565
+ for parent in parent_labels:
566
+ for graph in pair:
567
+ for idx in graph.label2idxs[parent]:
568
+ neighboring_labels[parent].update(
569
+ graph.labels[nbr] for nbr in graph.graph[idx]
570
+ )
571
+
572
+ dirty = set(parent_labels)
573
+ for left_label, left_neighbors in neighboring_labels.items():
574
+ for right_label, right_neighbors in neighboring_labels.items():
575
+ if left_label > right_label:
576
+ dirty.update(left_neighbors & right_neighbors)
577
+
578
+ for label in dirty:
579
+ pair[0]._irred_labels.pop(label, None)
580
+
581
+
582
+ def _classes_as_legacy_pair(class_pair):
583
+ return [_classes_as_legacy_dict(classes) for classes in class_pair]
584
+
585
+
586
+ def _block_mask(n_classes, offsets):
587
+ mask = np.zeros((n_classes, offsets[-1]), dtype=int)
588
+ for row in range(n_classes):
589
+ mask[row, offsets[row] : offsets[row + 1]] = 1
590
+ return mask
591
+
592
+
593
+ def _classes_as_legacy_dict(classes):
594
+ return {
595
+ color: {
596
+ "idxs": list(idxs),
597
+ "nbrs": {
598
+ label: ([1] * weights if isinstance(weights, int) else list(weights))
599
+ for label, weights in nbrs.items()
600
+ },
601
+ }
602
+ for color, idxs, nbrs in classes
603
+ }
604
+
605
+
606
+ def _fingerprint_components(fingerprint, n_classes):
607
+ n_left, n_right = n_classes
608
+ groups_left = [-1] * n_left
609
+ groups_right = [-1] * n_right
610
+ group_id = 0
611
+
612
+ for start in range(n_left):
613
+ if groups_left[start] != -1:
614
+ continue
615
+ frontier_left = {start}
616
+ seen_left = set()
617
+ seen_right = set()
618
+ while frontier_left:
619
+ fresh_left = frontier_left - seen_left
620
+ if not fresh_left:
621
+ break
622
+ frontier_right = set()
623
+ for left in fresh_left:
624
+ groups_left[left] = group_id
625
+ frontier_right.update(np.where(fingerprint[left] > 0)[0])
626
+ seen_left.update(fresh_left)
627
+
628
+ fresh_right = frontier_right - seen_right
629
+ if not fresh_right:
630
+ break
631
+ frontier_left = set()
632
+ for right in fresh_right:
633
+ groups_right[right] = group_id
634
+ frontier_left.update(np.where(fingerprint.T[right] > 0)[0])
635
+ seen_right.update(fresh_right)
636
+ group_id += 1
637
+
638
+ for right in range(n_right):
639
+ if groups_right[right] == -1:
640
+ groups_right[right] = group_id
641
+ group_id += 1
642
+
643
+ return [groups_left, groups_right]
644
+
645
+
646
+ def _fresh_label(pair):
647
+ labels = set(pair[0].label2idxs) | set(pair[1].label2idxs)
648
+ return max(labels, default=999) + 1
649
+
650
+
651
+ def _initial_wl_labels(graph):
652
+ if graph._ini_wl_labels is None:
653
+ graph._ini_wl_labels = graph.get_WL_labels()
654
+ return graph._ini_wl_labels
655
+
656
+
657
+ def _assign_singleton_label(graph, old_label, idx, new_label):
658
+ graph.labels[idx] = new_label
659
+ graph.label2idxs[old_label].remove(idx)
660
+ graph.label2idxs[new_label].append(idx)
661
+
662
+
663
+ def _merge_pair(pair):
664
+ shift = len(pair[0].labels)
665
+ left = {idx: dict(nbrs) for idx, nbrs in pair[0].graph.items()}
666
+ right = {
667
+ idx + shift: {nbr + shift: weight for nbr, weight in nbrs.items()}
668
+ for idx, nbrs in pair[1].graph.items()
669
+ }
670
+ return LabeledGraph({**left, **right}, pair[0].labels + pair[1].labels)
671
+
672
+
673
+ def _compress_pair_labels(pair):
674
+ old_to_new = {}
675
+ next_label = 1
676
+ for label in pair[0].labels:
677
+ if label not in old_to_new:
678
+ old_to_new[label] = next_label
679
+ next_label += 1
680
+
681
+ for graph in pair:
682
+ graph.labels = [old_to_new[label] for label in graph.labels]
683
+ graph.build_label2idxs()
684
+
685
+
686
+ def _label_state_key(pair):
687
+ old_to_new = {}
688
+ next_label = 1
689
+ key = []
690
+ for graph in pair:
691
+ for label in graph.labels:
692
+ if label not in old_to_new:
693
+ old_to_new[label] = next_label
694
+ next_label += 1
695
+ key.append(old_to_new[label])
696
+ return tuple(key)
@@ -0,0 +1 @@
1
+ """Mapping validation, identifiability, and annotation evaluation."""