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,711 @@
1
+ """Native terminal-H reduction, exact all-shell transports and full native fallback."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ctypes as ct
6
+ import math
7
+ import time
8
+ from collections import Counter
9
+ from dataclasses import dataclass, replace
10
+ from functools import lru_cache
11
+ from numbers import Integral, Real
12
+ from pathlib import Path
13
+
14
+ import numpy as np
15
+
16
+ from ..exact.distance_search.records import (
17
+ DistanceEnumerationResult,
18
+ ExactEnumerationLimitError,
19
+ normalize_distance_target,
20
+ )
21
+ from ..exact.hydrogen import HydrogenTransferPlan
22
+ from ..slap.lap import _adjacency_and_elements
23
+ from .classification import (
24
+ _CALLBACK as CLASS_CALLBACK,
25
+ )
26
+ from .classification import (
27
+ _Problem as ClassProblem,
28
+ )
29
+ from .classification import (
30
+ _records_callback,
31
+ )
32
+ from .classification import (
33
+ _Statistics as ClassStatistics,
34
+ )
35
+ from .runtime import (
36
+ BACKENDS,
37
+ CALLBACK,
38
+ INT,
39
+ LONG,
40
+ REASONS,
41
+ Problem,
42
+ Request,
43
+ Statistics,
44
+ load_core,
45
+ )
46
+
47
+
48
+ class Attributes(ct.Structure):
49
+ _fields_ = [
50
+ (name + "_" + side, INT)
51
+ for name in (
52
+ "atomic_numbers",
53
+ "isotopes",
54
+ "charges",
55
+ "radicals",
56
+ "dummies",
57
+ "stereo",
58
+ )
59
+ for side in ("r", "p")
60
+ ]
61
+
62
+
63
+ class HydrogenStatistics(ct.Structure):
64
+ _fields_ = [(name, ct.c_int32) for name in ("supported", "eligibility_reason", "transport_complete", "reason")]
65
+ _fields_ += [
66
+ (name, ct.c_int64)
67
+ for name in (
68
+ "full_nodes",
69
+ "quotient_nodes",
70
+ "heavy_candidates",
71
+ "transport_plans",
72
+ "transport_nodes",
73
+ )
74
+ ]
75
+ _fields_ += [(name, ct.c_double) for name in ("reduction_seconds", "heavy_seconds", "transport_seconds")]
76
+
77
+
78
+ class TransportStatistics(ct.Structure):
79
+ _fields_ = [(name, ct.c_int64) for name in ("minimum2", "plans", "nodes")]
80
+ _fields_ += [
81
+ ("complete", ct.c_int32),
82
+ ("reason", ct.c_int32),
83
+ ("elapsed_seconds", ct.c_double),
84
+ ]
85
+
86
+
87
+ FLOW_CALLBACK = ct.CFUNCTYPE(ct.c_int, INT, ct.c_int64, ct.c_char_p, ct.c_void_p)
88
+ ELIGIBILITY = {
89
+ 0: None,
90
+ 1: "no_hydrogen_reduction",
91
+ 2: "isotopic_hydrogen",
92
+ 3: "annotated_stereochemistry",
93
+ 4: "bonded_hydrogen_placeholder",
94
+ 5: "nonneutral_or_radical_hydrogen",
95
+ 6: "nonterminal_hydrogen",
96
+ 7: "nonordinary_hydrogen_bond",
97
+ 8: "hydrogen_class_attributes",
98
+ 9: "quotient_backend_unsupported",
99
+ }
100
+
101
+
102
+ @lru_cache(maxsize=8)
103
+ def _load(path):
104
+ library = load_core(str(Path(path).resolve()))
105
+ if library.synkit_mapper_hydrogen_abi() != 1:
106
+ raise RuntimeError("Unsupported native hydrogen ABI")
107
+ library.synkit_mapper_hydrogen_error.restype = ct.c_char_p
108
+ library.synkit_mapper_hydrogen_transport.argtypes = [
109
+ ct.c_int,
110
+ INT,
111
+ INT,
112
+ ct.c_int,
113
+ ct.c_int,
114
+ ct.c_int64,
115
+ ct.c_double,
116
+ ct.c_int64,
117
+ ct.c_int64,
118
+ ct.c_void_p,
119
+ FLOW_CALLBACK,
120
+ ct.c_void_p,
121
+ ct.POINTER(TransportStatistics),
122
+ ]
123
+ library.synkit_mapper_hydrogen_transport.restype = ct.c_int
124
+ library.synkit_mapper_hydrogen_solve.argtypes = [
125
+ ct.POINTER(Problem),
126
+ ct.POINTER(Attributes),
127
+ ct.POINTER(Request),
128
+ ct.c_int64,
129
+ CALLBACK,
130
+ ct.c_void_p,
131
+ ct.POINTER(Statistics),
132
+ ct.POINTER(HydrogenStatistics),
133
+ ]
134
+ library.synkit_mapper_hydrogen_solve.restype = ct.c_int
135
+ library.synkit_mapper_hydrogen_solve_classified.argtypes = [
136
+ ct.POINTER(Problem),
137
+ ct.POINTER(Attributes),
138
+ ct.POINTER(Request),
139
+ ct.POINTER(ClassProblem),
140
+ ct.c_int64,
141
+ ct.c_int64,
142
+ CLASS_CALLBACK,
143
+ ct.c_void_p,
144
+ ct.POINTER(Statistics),
145
+ ct.POINTER(HydrogenStatistics),
146
+ ct.POINTER(ClassStatistics),
147
+ ]
148
+ library.synkit_mapper_hydrogen_solve_classified.restype = ct.c_int
149
+ if hasattr(library, "synkit_mapper_hydrogen_solve_classified_updates"):
150
+ function = library.synkit_mapper_hydrogen_solve_classified_updates
151
+ function.argtypes = library.synkit_mapper_hydrogen_solve_classified.argtypes
152
+ function.restype = ct.c_int
153
+ return library
154
+
155
+
156
+ def _cap(value, name):
157
+ if value is None:
158
+ return -1
159
+ if isinstance(value, bool) or not isinstance(value, Integral) or not 0 < value < 2**63:
160
+ raise ValueError(f"{name} must be positive int64 or None")
161
+ return int(value)
162
+
163
+
164
+ def _decimal_integer(payload):
165
+ # Convert exact native counts without changing Python's process-wide
166
+ # decimal-string security limit. Each parsed chunk is at most nine digits.
167
+ value = 0
168
+ for start in range(0, len(payload), 9):
169
+ chunk = payload[start : start + 9]
170
+ value = value * 10 ** len(chunk) + int(chunk)
171
+ return value
172
+
173
+
174
+ @dataclass(frozen=True)
175
+ class NativeHydrogenEnumerationResult:
176
+ plans: tuple[HydrogenTransferPlan, ...]
177
+ minimum_distance: int
178
+ labeled_mapping_count: int
179
+ complete: bool
180
+ observed_plan_count: int
181
+ truncation_reason: str | None
182
+ backend_statistics: dict
183
+
184
+
185
+ def enumerate_hydrogen_transfers(
186
+ rows,
187
+ columns,
188
+ *,
189
+ library_path,
190
+ reservoir=None,
191
+ CD="minimal",
192
+ CD_mode="exact",
193
+ max_plans=None,
194
+ max_nodes=None,
195
+ collect_plans=True,
196
+ time_limit_seconds=None,
197
+ cancel_token=None,
198
+ plan_callback=None,
199
+ ):
200
+ """Enumerate every parent-flow orbit in a minimum, exact or at-most shell.
201
+
202
+ For a real-parent transfer the cost is two bond edits; entering/leaving
203
+ the isolated dummy reservoir costs one. Multiplicities use exact native
204
+ integer arithmetic and are converted from decimal strings here.
205
+ """
206
+ started = time.perf_counter()
207
+ rows, columns = tuple(rows), tuple(columns)
208
+ if (
209
+ len(rows) != len(columns)
210
+ or not rows
211
+ or any(isinstance(v, bool) or not isinstance(v, Integral) or not 0 <= v < 2**31 for v in rows + columns)
212
+ ):
213
+ raise ValueError("H counts must be equally sized nonnegative integer arrays")
214
+ if reservoir is not None and (
215
+ isinstance(reservoir, bool) or not isinstance(reservoir, Integral) or not 0 <= reservoir < len(rows)
216
+ ):
217
+ raise ValueError("reservoir must be a valid parent index or None")
218
+ target = normalize_distance_target(CD)
219
+ if CD_mode not in {"exact", "at_most"} or (target == "minimal" and CD_mode != "exact"):
220
+ raise ValueError("at_most requires numeric CD")
221
+ if not isinstance(collect_plans, bool):
222
+ raise TypeError("collect_plans must be boolean")
223
+ if time_limit_seconds is not None and (
224
+ isinstance(time_limit_seconds, bool)
225
+ or not isinstance(time_limit_seconds, Real)
226
+ or not math.isfinite(time_limit_seconds)
227
+ or time_limit_seconds < 0
228
+ ):
229
+ raise ValueError("time_limit_seconds must be finite and nonnegative")
230
+ query = 0 if target == "minimal" else 2 if CD_mode == "at_most" else 1
231
+ # Every H transport has integral CD. An odd target2 denotes an empty shell.
232
+ target2 = (
233
+ 0
234
+ if target == "minimal"
235
+ else (math.floor(target * 2) if query == 2 else int(target * 2) if target.is_integer() else 1)
236
+ )
237
+ r, p = (np.ascontiguousarray(v, dtype=np.int32) for v in (rows, columns))
238
+ plans, errors = [], []
239
+ observed_count = 0
240
+ labeled_total = 0
241
+
242
+ @FLOW_CALLBACK
243
+ def receive(cells, cost2, multiplicity, _):
244
+ nonlocal observed_count, labeled_total
245
+ try:
246
+ size = len(rows)
247
+ plan = HydrogenTransferPlan(
248
+ tuple(
249
+ (i, j, cells[i * size + j])
250
+ for i in range(size)
251
+ for j in range(size)
252
+ if i != j and cells[i * size + j]
253
+ ),
254
+ tuple(cells[i * size + i] for i in range(size)),
255
+ cost2 // 2,
256
+ _decimal_integer(multiplicity),
257
+ )
258
+ observed_count += 1
259
+ labeled_total += plan.labeled_mapping_count
260
+ if collect_plans:
261
+ plans.append(plan)
262
+ return int(bool(plan_callback(plan))) if plan_callback is not None else 0
263
+ except BaseException as error: # noqa: BLE001 - C callback boundary.
264
+ errors.append(error)
265
+ return 1
266
+
267
+ library = _load(str(Path(library_path).resolve()))
268
+ statistics = TransportStatistics()
269
+ status = library.synkit_mapper_hydrogen_transport(
270
+ len(rows),
271
+ r.ctypes.data_as(INT),
272
+ p.ctypes.data_as(INT),
273
+ -1 if reservoir is None else reservoir,
274
+ query,
275
+ target2,
276
+ (-1 if time_limit_seconds is None else max(0, time_limit_seconds - (time.perf_counter() - started))),
277
+ _cap(max_plans, "max_plans"),
278
+ _cap(max_nodes, "max_nodes"),
279
+ None if cancel_token is None else cancel_token.pointer,
280
+ receive,
281
+ None,
282
+ ct.byref(statistics),
283
+ )
284
+ if errors:
285
+ raise errors[0]
286
+ if status:
287
+ raise RuntimeError(library.synkit_mapper_hydrogen_error().decode())
288
+ return NativeHydrogenEnumerationResult(
289
+ tuple(plans),
290
+ statistics.minimum2 // 2,
291
+ labeled_total,
292
+ bool(statistics.complete),
293
+ observed_count,
294
+ "plan_limit" if statistics.reason == 3 else REASONS.get(statistics.reason),
295
+ {name: getattr(statistics, name) for name, _ in TransportStatistics._fields_},
296
+ )
297
+
298
+
299
+ # Reduction, lifting and fallback preserve one deadline and enumeration contract.
300
+ def enumerate_hydrogen_mappings( # noqa: C901
301
+ lgp,
302
+ *,
303
+ library_path,
304
+ backend="pabs",
305
+ CD="minimal",
306
+ CD_mode="exact",
307
+ binary=False,
308
+ symmetry=True,
309
+ node_properties=(),
310
+ time_limit_seconds=None,
311
+ max_states=None,
312
+ max_mappings=None,
313
+ max_classes=None,
314
+ max_frontier=None,
315
+ memory_limit_bytes=None,
316
+ collect_mappings=True,
317
+ mapping_callback=None,
318
+ compute_minimum_cost=None,
319
+ max_bijections=None,
320
+ tolerance=1e-9,
321
+ proof_only=False,
322
+ cancel_token=None,
323
+ max_transport_plans=None,
324
+ max_canonical_nodes=None,
325
+ fallback=True,
326
+ _native_classifier=None,
327
+ _native_class_registry=None,
328
+ _stereo_annotated=False,
329
+ ):
330
+ """Search explicit-H classes natively using a checked terminal-H quotient.
331
+
332
+ Numeric queries enumerate all bounded transports after a heavy minimum-H
333
+ lower-envelope search. Unsupported chemistry explicitly invokes the full
334
+ requested native backend on the SAME remaining wall budget, and records
335
+ why reduction was declined. ``fallback=False`` returns unsupported instead.
336
+ """
337
+ from .core import _ids, enumerate_core_mappings
338
+
339
+ started = time.perf_counter()
340
+ target = normalize_distance_target(CD)
341
+ if backend not in BACKENDS or backend == "slap":
342
+ raise ValueError("Hydrogen quotient requires an exact native heavy backend")
343
+ if CD_mode not in {"exact", "at_most"} or (CD_mode == "at_most" and target == "minimal"):
344
+ raise ValueError("at_most requires numeric CD")
345
+ if proof_only and target != "minimal":
346
+ raise ValueError("proof_only requires minimum CD")
347
+ if compute_minimum_cost and target != "minimal":
348
+ raise ValueError("Numeric H queries do not run a separate minimum proof")
349
+ if mapping_callback is not None and not callable(mapping_callback):
350
+ raise TypeError("mapping_callback must be callable")
351
+ for name, value in (
352
+ ("binary", binary),
353
+ ("symmetry", symmetry),
354
+ ("collect_mappings", collect_mappings),
355
+ ("fallback", fallback),
356
+ ("proof_only", proof_only),
357
+ ):
358
+ if not isinstance(value, bool):
359
+ raise TypeError(f"{name} must be boolean")
360
+ if time_limit_seconds is not None and (
361
+ isinstance(time_limit_seconds, bool)
362
+ or not isinstance(time_limit_seconds, Real)
363
+ or not math.isfinite(time_limit_seconds)
364
+ or time_limit_seconds < 0
365
+ ):
366
+ raise ValueError("time_limit_seconds must be finite and nonnegative")
367
+ if (
368
+ not isinstance(tolerance, Real)
369
+ or isinstance(tolerance, bool)
370
+ or not math.isfinite(tolerance)
371
+ or not 0 <= tolerance < 0.25
372
+ ):
373
+ raise ValueError("tolerance must be finite and in [0,0.25)")
374
+ if len(lgp) != 2:
375
+ raise ValueError("Expected two endpoint graphs")
376
+ matrices, elements = zip(*(_adjacency_and_elements(graph, binary) for graph in lgp))
377
+ n = len(elements[0])
378
+ if (
379
+ not 1 <= n <= 1024
380
+ or any(matrix.shape != (n, n) for matrix in matrices)
381
+ or Counter(elements[0]) != Counter(elements[1])
382
+ ):
383
+ raise ValueError("Hydrogen endpoints require equal compatible inventories")
384
+ keepalive = []
385
+ for matrix in matrices:
386
+ if (
387
+ not np.isfinite(matrix).all()
388
+ or np.any(matrix < 0)
389
+ or np.any(matrix > 500000)
390
+ or np.any(np.diag(matrix))
391
+ or not np.array_equal(matrix, matrix.T)
392
+ or not np.allclose(matrix * 2, np.rint(matrix * 2), atol=tolerance, rtol=0)
393
+ ):
394
+ raise ValueError("Native H requires nonnegative half-integer symmetric loop-free weights")
395
+ keepalive.append(np.ascontiguousarray(np.rint(matrix * 2), dtype=np.int64))
396
+ registry = {}
397
+ types = [_ids(side, registry) for side in elements]
398
+ registry = {}
399
+ nodes = []
400
+ for graph, side in zip(lgp, elements):
401
+ if set(node_properties) - graph.props.keys():
402
+ raise ValueError("Missing symmetry node properties")
403
+ nodes.append(
404
+ _ids(
405
+ [(v, *(graph.props[name][i] for name in node_properties)) for i, v in enumerate(side)],
406
+ registry,
407
+ )
408
+ )
409
+ edges = [
410
+ np.ascontiguousarray(graph.props.get("_native_edge_colors", np.zeros((n, n))), dtype=np.int32) for graph in lgp
411
+ ]
412
+ if any(edge.shape != (n, n) for edge in edges):
413
+ raise ValueError("Invalid native edge colors")
414
+ keepalive.extend(types + nodes + edges)
415
+ problem = Problem(
416
+ 1,
417
+ n,
418
+ *(a.ctypes.data_as(LONG) for a in keepalive[:2]),
419
+ *(a.ctypes.data_as(INT) for a in types + nodes + edges),
420
+ )
421
+ metadata = []
422
+ for name in (
423
+ "atomic_numbers",
424
+ "isotopes",
425
+ "charges",
426
+ "radicals",
427
+ "dummies",
428
+ "stereo",
429
+ ):
430
+ for graph, labels in zip(lgp, elements):
431
+ if name in {"atomic_numbers", "isotopes"}:
432
+ index = 0 if name == "atomic_numbers" else 1
433
+ values = [
434
+ (v[index] if isinstance(v, tuple) and len(v) == 2 else v if index == 0 else 0) for v in labels
435
+ ]
436
+ elif name == "stereo":
437
+ values = [
438
+ bool(a) or bool(b)
439
+ for a, b in zip(
440
+ graph.props.get("stereo", [0] * n),
441
+ graph.props.get("_stereo_anchor", [0] * n),
442
+ )
443
+ ]
444
+ if _stereo_annotated:
445
+ values[0] = True
446
+ else:
447
+ values = graph.props.get(
448
+ {"charges": "charge", "radicals": "radical", "dummies": "dummy"}[name],
449
+ [0] * n,
450
+ )
451
+ metadata.append(np.ascontiguousarray(values, dtype=np.int32))
452
+ attrs = Attributes(*(v.ctypes.data_as(INT) for v in metadata))
453
+ maximum = float((matrices[0].sum() + matrices[1].sum()) / 2)
454
+ query = 3 if proof_only else 0 if target == "minimal" else 2 if CD_mode == "at_most" else 1
455
+ impossible = False
456
+ if target == "minimal":
457
+ target2 = 0
458
+ elif query == 2:
459
+ target2 = math.floor((min(target, maximum) + tolerance) * 2)
460
+ else:
461
+ target2 = round(target * 2)
462
+ impossible = target > maximum + tolerance or abs(target - target2 / 2) > tolerance
463
+ request = Request()
464
+ (
465
+ request.abi_version,
466
+ request.backend,
467
+ request.query,
468
+ request.workers,
469
+ request.symmetry,
470
+ ) = (1, BACKENDS[backend], query, 1, int(symmetry))
471
+ request.target2 = target2
472
+ request.seconds = -1 if time_limit_seconds is None else max(0, time_limit_seconds - (time.perf_counter() - started))
473
+ for name, value in (
474
+ ("max_states", max_states),
475
+ ("max_candidates", max_mappings),
476
+ ("max_classes", max_classes),
477
+ ("max_frontier", max_frontier),
478
+ ("memory_limit_bytes", memory_limit_bytes),
479
+ ):
480
+ setattr(request, name, _cap(value, name))
481
+ request.cancel_token = None if cancel_token is None else cancel_token.pointer
482
+ total = math.prod(math.factorial(count) for count in Counter(elements[0]).values())
483
+ if max_bijections is not None and total > _cap(max_bijections, "max_bijections"):
484
+ raise ExactEnumerationLimitError("Full explicit-H bijections exceed max_bijections")
485
+ library = _load(str(Path(library_path).resolve()))
486
+ stats, hstats, cstats = Statistics(), HydrogenStatistics(), ClassStatistics()
487
+ mappings, distances, errors = [], [], []
488
+
489
+ def receive_map(mapping, cost):
490
+ if collect_mappings:
491
+ mappings.append(list(mapping))
492
+ distances.append(cost)
493
+ if mapping_callback is not None:
494
+ return mapping_callback(list(mapping), cost)
495
+ return False
496
+
497
+ @CALLBACK
498
+ def receive(pointer, cost2, _):
499
+ try:
500
+ return int(bool(receive_map(pointer[:n], cost2 / 2)))
501
+ except BaseException as error: # noqa: BLE001 - C callback boundary.
502
+ errors.append(error)
503
+ return 1
504
+
505
+ def receive_class(record):
506
+ _native_class_registry[record.representative] = record
507
+ return receive_map(record.representative, record.cost2 / 2)
508
+
509
+ request.seconds = -1 if time_limit_seconds is None else max(0, time_limit_seconds - (time.perf_counter() - started))
510
+
511
+ if impossible:
512
+ stats.search_complete = stats.classification_complete = stats.hydrogen_complete = 1
513
+ stats.minimum2 = -1
514
+ elif _native_classifier is not None and not proof_only:
515
+ if _native_class_registry is None:
516
+ raise ValueError("Native class registry required")
517
+ request.classify = 1
518
+ updates = hasattr(library, "synkit_mapper_hydrogen_solve_classified_updates")
519
+ delivered_classes = []
520
+ class_callback = _records_callback(n, delivered_classes, errors, receive_class, upserts=updates)
521
+ function = (
522
+ library.synkit_mapper_hydrogen_solve_classified_updates
523
+ if updates
524
+ else library.synkit_mapper_hydrogen_solve_classified
525
+ )
526
+ status = function(
527
+ ct.byref(problem),
528
+ ct.byref(attrs),
529
+ ct.byref(request),
530
+ ct.byref(_native_classifier.problem),
531
+ _cap(max_transport_plans, "max_transport_plans"),
532
+ _cap(max_canonical_nodes, "max_canonical_nodes"),
533
+ class_callback,
534
+ None,
535
+ ct.byref(stats),
536
+ ct.byref(hstats),
537
+ ct.byref(cstats),
538
+ )
539
+ if errors:
540
+ raise errors[0]
541
+ if status:
542
+ raise RuntimeError(library.synkit_mapper_hydrogen_error().decode())
543
+ else:
544
+ status = library.synkit_mapper_hydrogen_solve(
545
+ ct.byref(problem),
546
+ ct.byref(attrs),
547
+ ct.byref(request),
548
+ _cap(max_transport_plans, "max_transport_plans"),
549
+ receive,
550
+ None,
551
+ ct.byref(stats),
552
+ ct.byref(hstats),
553
+ )
554
+ if errors:
555
+ raise errors[0]
556
+ if status:
557
+ raise RuntimeError(library.synkit_mapper_hydrogen_error().decode())
558
+ if stats.status == 2 and hstats.eligibility_reason and fallback:
559
+ result = enumerate_core_mappings(
560
+ lgp,
561
+ library_path=library_path,
562
+ backend=backend,
563
+ CD=CD,
564
+ CD_mode=CD_mode,
565
+ binary=binary,
566
+ symmetry=symmetry and hstats.eligibility_reason != 8,
567
+ node_properties=node_properties,
568
+ time_limit_seconds=(
569
+ None if time_limit_seconds is None else max(0, time_limit_seconds - (time.perf_counter() - started))
570
+ ),
571
+ max_states=max_states,
572
+ max_mappings=max_mappings,
573
+ max_classes=max_classes,
574
+ max_frontier=max_frontier,
575
+ memory_limit_bytes=memory_limit_bytes,
576
+ collect_mappings=collect_mappings,
577
+ mapping_callback=mapping_callback,
578
+ compute_minimum_cost=compute_minimum_cost,
579
+ max_bijections=max_bijections,
580
+ tolerance=tolerance,
581
+ proof_only=proof_only,
582
+ cancel_token=cancel_token,
583
+ _native_classifier=_native_classifier,
584
+ _native_class_registry=_native_class_registry,
585
+ )
586
+ details = dict(result.backend_statistics or {})
587
+ details.update(
588
+ hydrogen_quotient=False,
589
+ hydrogen_quotient_fallback=ELIGIBILITY[hstats.eligibility_reason],
590
+ hydrogen_reduction_seconds=hstats.reduction_seconds,
591
+ hydrogen_fallback_symmetry_disabled=hstats.eligibility_reason == 8,
592
+ )
593
+ return replace(
594
+ result,
595
+ backend_statistics=details,
596
+ elapsed_seconds=time.perf_counter() - started,
597
+ )
598
+ details = {name: getattr(stats, name) for name, _ in Statistics._fields_}
599
+ details.update(
600
+ hydrogen_quotient=bool(hstats.supported),
601
+ hydrogen_quotient_fallback=ELIGIBILITY[hstats.eligibility_reason],
602
+ hydrogen_statistics={name: getattr(hstats, name) for name, _ in HydrogenStatistics._fields_},
603
+ hydrogen_complete=bool(stats.hydrogen_complete),
604
+ core_abi=1,
605
+ backend=backend,
606
+ solver_version=(
607
+ library.synkit_mapper_backend_version(BACKENDS[backend]).decode()
608
+ if hasattr(library, "synkit_mapper_backend_version")
609
+ else "unknown"
610
+ ),
611
+ library_path=str(Path(library_path).resolve()),
612
+ threads=1,
613
+ CD_mode=CD_mode,
614
+ proof_only=proof_only,
615
+ requested_symmetry=symmetry,
616
+ cost_scale=2,
617
+ enumeration_search_seconds=max(0, stats.enumeration_seconds - stats.callback_seconds),
618
+ mapping_count_scope="lifted_hydrogen_transport_orbits",
619
+ classification_reason={
620
+ 0: None,
621
+ 1: "time_limit",
622
+ 2: "canonical_node_limit",
623
+ 3: "class_limit",
624
+ 4: "callback_stop",
625
+ 5: "cancelled",
626
+ }[cstats.reason],
627
+ classification_delivery=(
628
+ "incremental_discovery"
629
+ if _native_classifier is not None and hasattr(library, "synkit_mapper_hydrogen_solve_classified_updates")
630
+ else "legacy_batch" if _native_classifier is not None else None
631
+ ),
632
+ delivered_class_count=(
633
+ len(delivered_classes) if _native_classifier is not None and not impossible and not proof_only else None
634
+ ),
635
+ )
636
+ if backend == "scip":
637
+ details.update(
638
+ numerical_qualification=(
639
+ "solver OPTIMAL under floating feasibility tolerances; " "no checked formal certificate"
640
+ ),
641
+ relative_gap=0,
642
+ absolute_gap=0,
643
+ feasibility_tolerance=1e-9,
644
+ )
645
+ complete = bool(stats.search_complete) and not proof_only
646
+ reason = "hydrogen_plan_limit" if hstats.reason == 11 else REASONS.get(stats.reason)
647
+ return DistanceEnumerationResult(
648
+ target=target,
649
+ cost=next(iter(set(distances))) if len(set(distances)) == 1 else None,
650
+ minimum_cost=stats.minimum2 / 2 if stats.minimum_proven else None,
651
+ mappings=mappings,
652
+ distances=distances,
653
+ total_bijections=total,
654
+ maximum_cost_upper_bound=maximum,
655
+ visited_leaves=stats.leaves,
656
+ pruned_branches=0,
657
+ visited_nodes=stats.nodes,
658
+ elapsed_seconds=time.perf_counter() - started,
659
+ status=(
660
+ ("complete" if stats.candidate_count else "no_solutions")
661
+ if complete
662
+ else "unsupported" if stats.status == 2 else "timeout"
663
+ ),
664
+ complete=complete,
665
+ truncation_reason=reason or ("proof_only" if proof_only else None),
666
+ selected_mapping_count=stats.candidate_count,
667
+ selected_labeled_mapping_count=None,
668
+ scope="full_weighted_distance_via_terminal_hydrogen_quotient",
669
+ symmetry_group_order=None,
670
+ symmetry_search_complete=False,
671
+ symmetry_quotient_complete=False,
672
+ backend=f"hydrogen_{backend}_cpp",
673
+ backend_statistics=details,
674
+ )
675
+
676
+
677
+ def map_reaction_hydrogen(
678
+ reaction,
679
+ *,
680
+ library_path,
681
+ backend="pabs",
682
+ max_transport_plans=None,
683
+ max_canonical_nodes=None,
684
+ native_classification=True,
685
+ **options,
686
+ ):
687
+ """Map all explicit-H classes with native reduction, flows and classification."""
688
+ from ...reaction_search import _map_reaction_with_search
689
+
690
+ def search(graphs, **kwargs):
691
+ kwargs.pop("backend")
692
+ return enumerate_hydrogen_mappings(
693
+ graphs,
694
+ library_path=kwargs.pop("library_path"),
695
+ backend=backend,
696
+ max_transport_plans=max_transport_plans,
697
+ max_canonical_nodes=max_canonical_nodes,
698
+ _stereo_annotated=isinstance(reaction, str) and ("@" in reaction or "|" in reaction),
699
+ **kwargs,
700
+ )
701
+
702
+ options.setdefault("objective", "lewis")
703
+ options.setdefault("hydrogen", "explicit")
704
+ return _map_reaction_with_search(
705
+ reaction,
706
+ search_function=search,
707
+ backend="cpp",
708
+ library_path=library_path,
709
+ native_classification_library=library_path if native_classification else None,
710
+ **options,
711
+ )