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,685 @@
1
+ """Select mapping methods through one interface and report their search scope."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from collections import Counter
7
+ from dataclasses import dataclass
8
+ from numbers import Integral
9
+
10
+ import numpy as np
11
+
12
+ from .solvers.exact.distance_search.exhaustive import ExactMapper
13
+ from .solvers.exact.distance_search.records import normalize_distance_target
14
+ from .solvers.exact.distance_search.search import enumerate_distance_mappings
15
+ from .solvers.exact.hybrid import enumerate_hybrid_distance_mappings
16
+ from .solvers.exact.kernel_solvers.enumerate import enumerate_kernel_optima
17
+ from .solvers.exact.kernel_solvers.kernel import apply_kernel_solution, extract_kernel
18
+ from .solvers.exact.kernel_solvers.milp import solve_kernel_milp
19
+ from .solvers.exact.search import enumerate_pabs_mappings
20
+ from .graph.labeled_graph import LabeledGraph
21
+ from .reaction_search import ReactionMappingResult, _map_reaction_with_search
22
+ from .solvers.slap.lap import _adjacency_and_elements, chemical_distance, recover_mapping
23
+ from .solvers.slap.sequential import GraphMatcher
24
+
25
+ _SCOPES = {
26
+ "slap": "heuristic",
27
+ "kernel": "reduced_kernel",
28
+ "milp": "reduced_kernel",
29
+ "distance": "full_space",
30
+ "propagate": "full_space",
31
+ "native": "full_space",
32
+ "edits": "full_space",
33
+ "exhaustive": "full_space",
34
+ }
35
+ _ENUMERATORS = {"distance", "propagate", "native", "edits"}
36
+ _REACTION_OPTIONS = {
37
+ "hydrogen",
38
+ "objective",
39
+ "balance",
40
+ "max_hydrogen_plans",
41
+ "classification_max_search_nodes",
42
+ }
43
+ _DEFAULTS = {
44
+ "backend": None,
45
+ "CD": "minimal",
46
+ "CD_mode": "exact",
47
+ "binary": False,
48
+ "hydrogen": None,
49
+ "objective": "lewis",
50
+ "balance": "strict",
51
+ "max_hydrogen_plans": 10_000,
52
+ "classification_max_search_nodes": 100_000,
53
+ "library_path": None,
54
+ "search_config": None,
55
+ "max_edit_support_pairs": 50_000,
56
+ "max_lap_fingerprints": 10_000,
57
+ "solver": "pabs",
58
+ "allowed_domains": None,
59
+ "hydrogen_quotient": False,
60
+ }
61
+ _OPTION_NAMES = _DEFAULTS.keys() | {
62
+ "time_limit_seconds",
63
+ "max_mappings",
64
+ "max_bijections",
65
+ }
66
+
67
+
68
+ @dataclass(frozen=True)
69
+ class MappingResult:
70
+ """Common result for graph pairs and reaction SMILES.
71
+
72
+ ``mappings[i][j]`` is the product index assigned to reactant atom ``j``.
73
+ SMILES results retain symmetry class representatives. ``minimum_cost`` and
74
+ ``optimality_proven`` apply within ``search_scope`` and the chosen objective.
75
+ ``complete`` means enumeration and any chemical classification completed in
76
+ that scope. It is always false for heuristic or single-optimum methods.
77
+ A proven minimum can coexist with incomplete enumeration.
78
+
79
+ For compressed hydrogen, the minimum is for heavy bonds; returned distances
80
+ include conditional hydrogen lifts. See ``reaction_result`` for that scope.
81
+ ``search_result`` preserves the underlying solver's records and statistics.
82
+ """
83
+
84
+ method: str
85
+ search_scope: str
86
+ mappings: tuple[tuple[int, ...], ...]
87
+ distances: tuple[float, ...]
88
+ minimum_cost: float | None
89
+ optimality_proven: bool
90
+ complete: bool
91
+ incomplete_reason: str | None
92
+ elapsed_seconds: float
93
+ search_result: object
94
+ reaction_result: ReactionMappingResult | None = None
95
+
96
+ @property
97
+ def mapped_reactions(self) -> tuple[str, ...]:
98
+ """Mapped reaction SMILES, or an empty tuple for graph input."""
99
+ return (
100
+ ()
101
+ if self.reaction_result is None
102
+ else self.reaction_result.mapped_reactions
103
+ )
104
+
105
+ def as_dict(self) -> dict[str, object]:
106
+ """Export common metadata and chemistry records without solver internals."""
107
+ return {
108
+ "method": self.method,
109
+ "search_scope": self.search_scope,
110
+ "mappings": [list(mapping) for mapping in self.mappings],
111
+ "distances": list(self.distances),
112
+ "minimum_cost": self.minimum_cost,
113
+ "optimality_proven": self.optimality_proven,
114
+ "complete": self.complete,
115
+ "incomplete_reason": self.incomplete_reason,
116
+ "elapsed_seconds": self.elapsed_seconds,
117
+ "reaction_result": (
118
+ None if self.reaction_result is None else self.reaction_result.as_dict()
119
+ ),
120
+ }
121
+
122
+
123
+ @dataclass
124
+ class _SearchOutcome:
125
+ """Search results consumed by the reaction-SMILES frontend."""
126
+
127
+ mappings: list[list[int]]
128
+ distances: list[float]
129
+ minimum_cost: float | None
130
+ optimality_proven: bool
131
+ complete: bool
132
+ truncation_reason: str | None
133
+ backend: str
134
+ selected_mapping_count: int
135
+ raw_result: object
136
+
137
+ @property
138
+ def backend_statistics(self):
139
+ return getattr(self.raw_result, "backend_statistics", None)
140
+
141
+ @property
142
+ def selected_labeled_mapping_count(self):
143
+ return getattr(
144
+ self.raw_result,
145
+ "selected_labeled_mapping_count",
146
+ self.selected_mapping_count,
147
+ )
148
+
149
+
150
+ def _prepare_graphs(data):
151
+ if (
152
+ not isinstance(data, (list, tuple))
153
+ or len(data) != 2
154
+ or not all(isinstance(graph, LabeledGraph) for graph in data)
155
+ ):
156
+ raise TypeError(
157
+ "data must be reaction SMILES or a pair of LabeledGraph objects"
158
+ )
159
+ graphs = []
160
+ inventories = []
161
+ label_codes = {}
162
+ for source in data:
163
+ if any(source.graph[i].get(i, 0) != 0 for i in range(len(source.labels))):
164
+ raise ValueError("Mapper requires loop-free graphs")
165
+ adjacency, elements = _adjacency_and_elements(source, False)
166
+ if len(elements) != len(source.labels):
167
+ raise ValueError("Atom labels must have one entry per node")
168
+ if not np.isfinite(adjacency).all() or (adjacency < 0).any():
169
+ raise ValueError("Bond weights must be finite and non-negative")
170
+ if not np.array_equal(adjacency, adjacency.T):
171
+ raise ValueError("Mapper requires symmetric undirected graphs")
172
+ graph = LabeledGraph(
173
+ {
174
+ i: {int(j): float(row[j]) for j in np.flatnonzero(row)}
175
+ for i, row in enumerate(adjacency)
176
+ },
177
+ [label_codes.setdefault(element, len(label_codes)) for element in elements],
178
+ )
179
+ graph.props = dict(source.props)
180
+ graph.props["atomic numbers"] = elements
181
+ graphs.append(graph)
182
+ inventories.append(Counter(elements))
183
+ if inventories[0] != inventories[1]:
184
+ raise ValueError("Endpoints must have equal atom-type inventories")
185
+ return graphs
186
+
187
+
188
+ # Keep the independent method/option compatibility checks together.
189
+ def _check_method_options(method, config, requested): # noqa: C901
190
+ """Reject queries and limits unsupported by the selected method."""
191
+ backend = config["backend"]
192
+ if {"solver", "allowed_domains", "hydrogen_quotient"} & requested.keys():
193
+ if method != "native":
194
+ raise ValueError(
195
+ "solver, allowed_domains and hydrogen_quotient require method='native'"
196
+ )
197
+ from .solvers.native.runtime import BACKENDS
198
+
199
+ if config["solver"] not in BACKENDS:
200
+ raise ValueError(f"Unknown native solver: {config['solver']}")
201
+ if config["allowed_domains"] is not None and config["solver"] not in {
202
+ "kbb",
203
+ "kmilp",
204
+ }:
205
+ raise ValueError("allowed_domains requires solver='kbb' or 'kmilp'")
206
+ if not isinstance(config["hydrogen_quotient"], bool):
207
+ raise TypeError("hydrogen_quotient must be boolean")
208
+ if config["hydrogen_quotient"] and config["solver"] in {
209
+ "kbb",
210
+ "kmilp",
211
+ "slap",
212
+ "edit",
213
+ }:
214
+ raise ValueError(
215
+ "hydrogen_quotient requires a full-space weighted exact solver"
216
+ )
217
+ if backend not in {"python", "cpp"}:
218
+ raise ValueError(
219
+ "backend must be 'python', 'cpp', or None for automatic selection"
220
+ )
221
+ native_slap = method == "slap" and backend == "cpp"
222
+ if method == "native" and backend != "cpp":
223
+ raise ValueError("method='native' requires backend='cpp'")
224
+ if backend == "cpp" and method not in {"native", "slap"}:
225
+ raise ValueError(
226
+ "backend='cpp' currently supports method='slap' or method='native'"
227
+ )
228
+ if config["CD_mode"] not in {"exact", "at_most"}:
229
+ raise ValueError("CD_mode must be 'exact' or 'at_most'")
230
+ if config["CD_mode"] == "at_most" and (
231
+ method != "native" or config["CD"] == "minimal"
232
+ ):
233
+ raise ValueError("CD_mode='at_most' requires method='native' and numeric CD")
234
+ if method not in _ENUMERATORS:
235
+ if config["CD"] != "minimal":
236
+ raise ValueError(f"method='{method}' only supports CD='minimal'")
237
+ for name in ("time_limit_seconds", "max_mappings", "max_bijections"):
238
+ if native_slap and name != "max_bijections":
239
+ continue
240
+ if config[name] is not None:
241
+ raise ValueError(f"method='{method}' does not support {name}")
242
+ if method != "native" and not native_slap and config["library_path"] is not None:
243
+ raise ValueError(
244
+ "library_path requires method='native' or method='slap', backend='cpp'"
245
+ )
246
+ if (method == "native" or native_slap) and config["library_path"] is None:
247
+ from .solvers.native.runtime import resolve_library_path
248
+
249
+ config["library_path"] = resolve_library_path()
250
+ if "max_lap_fingerprints" in requested and method != "slap":
251
+ raise ValueError("max_lap_fingerprints requires method='slap'")
252
+ if method == "slap" and (
253
+ isinstance(config["max_lap_fingerprints"], bool)
254
+ or not isinstance(config["max_lap_fingerprints"], Integral)
255
+ or not 1 <= config["max_lap_fingerprints"] < 2**31
256
+ ):
257
+ raise ValueError("max_lap_fingerprints must be a positive C ABI integer")
258
+ if method != "propagate" and config["search_config"] is not None:
259
+ raise ValueError("search_config requires method='propagate'")
260
+ if method != "edits" and "max_edit_support_pairs" in requested:
261
+ raise ValueError("max_edit_support_pairs requires method='edits'")
262
+ if method == "edits":
263
+ if config["CD"] == "minimal" or not config["binary"]:
264
+ raise ValueError("method='edits' requires numeric CD and binary=True")
265
+ if config["max_bijections"] is not None:
266
+ raise ValueError("method='edits' does not support max_bijections")
267
+
268
+
269
+ class Mapper:
270
+ """Select a solver by a concise name and map SMILES or labeled graph pairs.
271
+
272
+ ``Mapper(method='propagate')`` defaults to full-space exact Python search.
273
+ :meth:`methods` lists the eight accepted names. ``slap`` is heuristic;
274
+ ``kernel`` enumerates optima in a SLAP-derived restricted problem; ``milp``
275
+ solves that problem for one optimum using optional PuLP/CBC. ``distance``
276
+ and ``propagate`` enumerate full-space minimum or numeric CD shells.
277
+ ``native`` loads the bundled wheel library or an explicit ``library_path``.
278
+ ``edits`` requires
279
+ numeric ``CD`` and ``binary=True``. ``exhaustive`` returns one global optimum
280
+ and is intended for small graphs.
281
+
282
+ Constructor options become defaults; options passed to :meth:`map` override
283
+ them for that call. Reaction input defaults to Lewis CD with explicit H;
284
+ ``objective='bond'`` selects the previous heavy-atom bond-only default.
285
+ Graph input uses its supplied weighted bonds and must be symmetric and
286
+ loop-free, with equal atom-type inventories. Inputs are copied before matching.
287
+
288
+ Enumeration methods default to a 10-second shared deadline and 100,000
289
+ mappings. Assignment enumerators cap graph input at 1,000,000 bijections;
290
+ reaction input has no default factorial admission cap. An explicit
291
+ ``max_bijections`` limits either input. SLAP, kernel, MILP and exhaustive
292
+ methods have no deadline or
293
+ output-cap support; supplying these limits raises an error. Existing solver
294
+ APIs remain available for their specialized options.
295
+ """
296
+
297
+ def __init__(self, method="propagate", **options):
298
+ if not isinstance(method, str) or method not in _SCOPES:
299
+ raise ValueError(
300
+ f"Unknown mapping method {method!r}; choose from {', '.join(_SCOPES)}"
301
+ )
302
+ self._check_option_names(options)
303
+ self.method = method
304
+ self._options = dict(options)
305
+
306
+ @staticmethod
307
+ def methods() -> tuple[str, ...]:
308
+ """Return the supported mapping method names."""
309
+ return tuple(_SCOPES)
310
+
311
+ @staticmethod
312
+ def native_methods(*, available_only=False, library_path=None) -> tuple[str, ...]:
313
+ """Native solver names for ``Mapper('native', solver=..., ...)``.
314
+
315
+ Availability depends on the explicitly supplied library's capabilities;
316
+ optional SDKs and restricted-domain methods retain their own scope.
317
+ ``available_only=True`` inspects the explicit or bundled library.
318
+ """
319
+ from .solvers.native.runtime import BACKENDS, available_native_methods
320
+
321
+ if available_only:
322
+ return available_native_methods(library_path)
323
+ return tuple(BACKENDS)
324
+
325
+ @staticmethod
326
+ def _check_option_names(options):
327
+ unknown = options.keys() - _OPTION_NAMES
328
+ if unknown:
329
+ raise TypeError(f"Unknown Mapper option(s): {', '.join(sorted(unknown))}")
330
+
331
+ def map(self, data, **options) -> MappingResult:
332
+ """Map reaction SMILES or ``(reactant_graph, product_graph)``.
333
+
334
+ Common options are ``CD='minimal'`` and ``binary=False``. Enumeration
335
+ methods accept ``time_limit_seconds`` and ``max_mappings``. Assignment
336
+ enumerators accept ``max_bijections``. Reaction inputs also accept
337
+ ``hydrogen``, ``objective``, ``balance``, ``max_hydrogen_plans``, and
338
+ ``classification_max_search_nodes``, as documented by ``map_reaction``.
339
+ ``CD_mode='at_most'`` selects distance <= numeric CD for native search;
340
+ the default ``'exact'`` selects exactly CD. With CD omitted, exact
341
+ enumerators return every minimum-distance class for reaction input.
342
+ ``search_config`` is propagation-only; ``library_path`` is native-only;
343
+ ``max_edit_support_pairs`` limits the edit-support method.
344
+ ``backend='cpp'`` also selects opt-in deterministic weighted SLAP with
345
+ a library built by ``_native.build.build_mapper_native``. Its search
346
+ remains heuristic; exact chemistry/classification is shared.
347
+ """
348
+ self._check_option_names(options)
349
+ requested = self._options | options
350
+ method = self.method
351
+ if method not in _SCOPES:
352
+ raise ValueError(f"Unknown mapping method {method!r}")
353
+ enumerating = method in _ENUMERATORS
354
+ backend = requested.get("backend")
355
+ if backend is None:
356
+ backend = "cpp" if method == "native" else "python"
357
+ native_slap = method == "slap" and backend == "cpp"
358
+ reaction_input = isinstance(data, str)
359
+ config = (
360
+ _DEFAULTS
361
+ | {
362
+ "time_limit_seconds": 10.0 if enumerating or native_slap else None,
363
+ "max_mappings": 100_000 if enumerating or native_slap else None,
364
+ "max_bijections": (
365
+ 1_000_000
366
+ if not reaction_input
367
+ and method in {"distance", "propagate", "native"}
368
+ else None
369
+ ),
370
+ }
371
+ | requested
372
+ | {"backend": backend}
373
+ )
374
+ config["CD"] = normalize_distance_target(config["CD"])
375
+ if not isinstance(config["binary"], bool):
376
+ raise TypeError("binary must be boolean")
377
+ _check_method_options(method, config, requested)
378
+ if not reaction_input and requested.keys() & _REACTION_OPTIONS:
379
+ raise ValueError(
380
+ "Hydrogen, objective, balance and classification options require reaction SMILES"
381
+ )
382
+ started = time.perf_counter()
383
+ shared_core = False
384
+ if method == "native":
385
+ from .solvers.native.runtime import has_core
386
+
387
+ shared_core = has_core(str(config["library_path"]))
388
+ if not shared_core and config["solver"] != "pabs":
389
+ raise ValueError("solver requires a library from build_mapper_native")
390
+ native_classes = shared_core
391
+ scope = (
392
+ "reduced_kernel"
393
+ if method == "native" and config["solver"] in {"kbb", "kmilp"}
394
+ else "heuristic"
395
+ if method == "native" and config["solver"] == "slap"
396
+ else _SCOPES[method]
397
+ )
398
+ if reaction_input:
399
+
400
+ def search(graphs, **query):
401
+ return self._solve(
402
+ graphs,
403
+ config | {"time_limit_seconds": query["time_limit_seconds"]},
404
+ mapping_callback=query["mapping_callback"],
405
+ collect_mappings=False,
406
+ native_symmetry=query.get("symmetry", False),
407
+ node_properties=query.get("node_properties", ()),
408
+ native_classifier=query.get("_native_classifier"),
409
+ native_class_registry=query.get("_native_class_registry"),
410
+ )
411
+
412
+ reaction = _map_reaction_with_search(
413
+ data,
414
+ search_function=search,
415
+ backend=backend,
416
+ native_classification_library=config["library_path"]
417
+ if native_classes
418
+ else None,
419
+ **{name: config[name] for name in _REACTION_OPTIONS},
420
+ **{
421
+ name: config[name]
422
+ for name in (
423
+ "CD",
424
+ "CD_mode",
425
+ "binary",
426
+ "time_limit_seconds",
427
+ "max_mappings",
428
+ "max_bijections",
429
+ "search_config",
430
+ "library_path",
431
+ )
432
+ },
433
+ )
434
+ outcome = reaction.search_result
435
+ return MappingResult(
436
+ method,
437
+ scope,
438
+ tuple(item.mapping for item in reaction.reactions),
439
+ tuple(item.distance for item in reaction.reactions),
440
+ outcome.minimum_cost,
441
+ outcome.optimality_proven,
442
+ reaction.complete,
443
+ reaction.incomplete_reason,
444
+ time.perf_counter() - started,
445
+ outcome.raw_result,
446
+ reaction,
447
+ )
448
+ outcome = self._solve(data, config)
449
+ return MappingResult(
450
+ method,
451
+ scope,
452
+ tuple(tuple(mapping) for mapping in outcome.mappings),
453
+ tuple(outcome.distances),
454
+ outcome.minimum_cost,
455
+ outcome.optimality_proven,
456
+ outcome.complete,
457
+ outcome.truncation_reason,
458
+ time.perf_counter() - started,
459
+ outcome.raw_result,
460
+ )
461
+
462
+ def _enumerate_graphs(
463
+ self,
464
+ graphs,
465
+ options,
466
+ mapping_callback,
467
+ collect_mappings,
468
+ native_symmetry=False,
469
+ node_properties=(),
470
+ native_classifier=None,
471
+ native_class_registry=None,
472
+ ):
473
+ """Adapt distance-shell enumerators to the common result fields."""
474
+ method = self.method
475
+ query = {
476
+ name: options[name]
477
+ for name in (
478
+ "CD",
479
+ "binary",
480
+ "time_limit_seconds",
481
+ "max_mappings",
482
+ "max_bijections",
483
+ )
484
+ }
485
+ query.update(
486
+ collect_mappings=collect_mappings,
487
+ mapping_callback=mapping_callback,
488
+ compute_minimum_cost=(options["CD"] == "minimal"),
489
+ )
490
+ if method == "distance":
491
+ result = enumerate_distance_mappings(graphs, **query)
492
+ elif method == "edits":
493
+ result = enumerate_hybrid_distance_mappings(
494
+ graphs,
495
+ backend="edit_support",
496
+ max_edit_support_pairs=options["max_edit_support_pairs"],
497
+ **query,
498
+ )
499
+ else:
500
+ if options["search_config"] is not None:
501
+ query["config"] = options["search_config"]
502
+ if method == "native":
503
+ query.update(
504
+ CD_mode=options["CD_mode"],
505
+ symmetry=native_symmetry and options["solver"] in {"pabs", "cpabs"},
506
+ node_properties=node_properties,
507
+ )
508
+ from .solvers.native.runtime import has_core
509
+
510
+ if has_core(str(options["library_path"])):
511
+ from .solvers.native.core import enumerate_core_mappings
512
+
513
+ enumerate_native = enumerate_core_mappings
514
+ additional = {"allowed_domains": options["allowed_domains"]}
515
+ if options["hydrogen_quotient"]:
516
+ from .solvers.native.hydrogen import enumerate_hydrogen_mappings
517
+
518
+ enumerate_native = enumerate_hydrogen_mappings
519
+ additional = {
520
+ "max_transport_plans": options["max_hydrogen_plans"],
521
+ "max_canonical_nodes": options[
522
+ "classification_max_search_nodes"
523
+ ],
524
+ }
525
+
526
+ result = enumerate_native(
527
+ graphs,
528
+ backend=options["solver"],
529
+ library_path=options["library_path"],
530
+ **additional,
531
+ _native_classifier=native_classifier,
532
+ _native_class_registry=native_class_registry,
533
+ **query,
534
+ )
535
+ return _SearchOutcome(
536
+ result.mappings,
537
+ result.distances,
538
+ result.minimum_cost,
539
+ result.minimum_cost is not None,
540
+ result.complete,
541
+ result.truncation_reason,
542
+ result.backend,
543
+ result.selected_mapping_count,
544
+ result,
545
+ )
546
+ result = enumerate_pabs_mappings(
547
+ graphs,
548
+ backend="cpp" if method == "native" else "python",
549
+ library_path=options["library_path"],
550
+ **query,
551
+ )
552
+ return _SearchOutcome(
553
+ result.mappings,
554
+ result.distances,
555
+ result.minimum_cost,
556
+ result.minimum_cost is not None,
557
+ result.complete,
558
+ result.truncation_reason,
559
+ result.backend,
560
+ result.selected_mapping_count,
561
+ result,
562
+ )
563
+
564
+ def _solve(
565
+ self,
566
+ data,
567
+ options,
568
+ *,
569
+ mapping_callback=None,
570
+ collect_mappings=True,
571
+ native_symmetry=False,
572
+ node_properties=(),
573
+ native_classifier=None,
574
+ native_class_registry=None,
575
+ ):
576
+ graphs = _prepare_graphs(data)
577
+ method, binary = self.method, options["binary"]
578
+ if method in _ENUMERATORS:
579
+ return self._enumerate_graphs(
580
+ graphs,
581
+ options,
582
+ mapping_callback,
583
+ collect_mappings,
584
+ native_symmetry,
585
+ node_properties,
586
+ native_classifier,
587
+ native_class_registry,
588
+ )
589
+
590
+ if method == "slap" and options["backend"] == "cpp":
591
+ from .solvers.native.slap import solve_slap
592
+
593
+ raw = solve_slap(
594
+ graphs,
595
+ library_path=options["library_path"],
596
+ binary=binary,
597
+ max_lap_fingerprints=options["max_lap_fingerprints"],
598
+ time_limit_seconds=options["time_limit_seconds"],
599
+ max_mappings=options["max_mappings"],
600
+ collect_mappings=collect_mappings,
601
+ mapping_callback=mapping_callback,
602
+ )
603
+ return _SearchOutcome(
604
+ raw.mappings,
605
+ raw.distances,
606
+ None,
607
+ False,
608
+ False,
609
+ raw.truncation_reason or "heuristic",
610
+ raw.backend,
611
+ raw.selected_mapping_count,
612
+ raw,
613
+ )
614
+
615
+ if method == "exhaustive":
616
+ if binary:
617
+ for graph in graphs:
618
+ graph.binarize_graph()
619
+ raw = ExactMapper().solve(*graphs)
620
+ mappings, proven, complete, reason = (
621
+ [raw.mapping],
622
+ True,
623
+ False,
624
+ "optimum_only",
625
+ )
626
+ else:
627
+ matcher = GraphMatcher(
628
+ binary=binary,
629
+ max_lap_fingerprints=options["max_lap_fingerprints"],
630
+ )
631
+ matcher.get_maps(
632
+ graphs, break_sym_targets=list(range(len(graphs[0].labels)))
633
+ )
634
+ if not matcher.results:
635
+ raise RuntimeError("SLAP did not produce a mapping")
636
+ if method == "slap":
637
+ raw = matcher
638
+ mappings = [recover_mapping(item["lgp"]) for item in matcher.results]
639
+ proven, complete, reason = False, False, "heuristic"
640
+ else:
641
+ kernel = extract_kernel(matcher.results, graphs, binary=binary)
642
+ if method == "kernel":
643
+ raw = enumerate_kernel_optima(kernel, unique=False)
644
+ mappings = raw.mappings
645
+ proven = bool(raw.proven_optimal and mappings)
646
+ complete = bool(raw.enumeration_complete and proven)
647
+ reason = None if complete else "kernel_enumeration_incomplete"
648
+ else:
649
+ raw = solve_kernel_milp(kernel)
650
+ mappings = [
651
+ apply_kernel_solution(kernel, sub) for sub in raw.sub_mappings
652
+ ]
653
+ proven = bool(raw.proven_optimal and mappings)
654
+ complete, reason = (
655
+ False,
656
+ ("optimum_only" if proven else "optimum_unproven"),
657
+ )
658
+ distances = []
659
+ for mapping in mappings:
660
+ if sorted(mapping) != list(range(len(graphs[0].labels))) or any(
661
+ graphs[0].labels[i] != graphs[1].labels[j]
662
+ for i, j in enumerate(mapping)
663
+ ):
664
+ raise RuntimeError(
665
+ f"method='{method}' returned an invalid atom bijection"
666
+ )
667
+ cost = chemical_distance(graphs, mapping, binary)
668
+ distances.append(cost)
669
+ if mapping_callback is not None:
670
+ mapping_callback(mapping, cost)
671
+ minimum = min(distances) if proven and distances else None
672
+ return _SearchOutcome(
673
+ mappings if collect_mappings else [],
674
+ distances if collect_mappings else [],
675
+ minimum,
676
+ proven,
677
+ complete,
678
+ reason,
679
+ method,
680
+ len(mappings),
681
+ raw,
682
+ )
683
+
684
+
685
+ __all__ = ["Mapper", "MappingResult"]