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,375 @@
1
+ """Reference-free exact enumeration from binary bond-edit supports.
2
+
3
+ For the undirected binary bond-presence distance, every mapping at chemical distance
4
+ ``C`` has a unique set of reactant-only edges and product-only edges. Removing
5
+ those edges leaves two colour-preserving isomorphic spanning graphs. This
6
+ module enumerates those edit supports before atom bijections and independently
7
+ rescales every completed isomorphism against the original endpoints.
8
+
9
+ The backend is intended for small-CD shells. Explicit support, mapping, and
10
+ wall-time limits return an incomplete result and never a false completeness
11
+ claim.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import itertools
17
+ import math
18
+ import time
19
+ from collections import Counter, defaultdict
20
+ from dataclasses import dataclass
21
+ from numbers import Integral, Real
22
+
23
+ import networkx as nx
24
+ import numpy as np
25
+
26
+ from ..slap.lap import _adjacency_and_elements
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class BinaryEditBudget:
31
+ """Exact broken/formed-edge budget implied by one binary CD shell."""
32
+
33
+ target: float | int
34
+ reactant_edge_count: int
35
+ product_edge_count: int
36
+ broken_edge_count: int | None
37
+ formed_edge_count: int | None
38
+ support_pair_count: int
39
+ feasible: bool
40
+ rejection_reason: str | None = None
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class BinaryEditSupportResult:
45
+ """Complete labeled mappings from edit-support-first enumeration."""
46
+
47
+ target: float | int
48
+ mappings: list[list[int]]
49
+ complete: bool
50
+ truncation_reason: str | None
51
+ budget: BinaryEditBudget
52
+ support_pairs_visited: int
53
+ invariant_compatible_pairs: int
54
+ isomorphisms_visited: int
55
+ selected_mapping_count: int
56
+ elapsed_seconds: float
57
+ scope: str = "complete_binary_atom_compatible_cd_shell"
58
+
59
+
60
+ def _validate_limit(value, name):
61
+ if value is not None and (
62
+ isinstance(value, bool) or not isinstance(value, Integral) or value < 1
63
+ ):
64
+ raise ValueError(f"{name} must be a positive integer or None")
65
+
66
+
67
+ def _binary_undirected_endpoints(lgp):
68
+ reactant, reactant_elements = _adjacency_and_elements(lgp[0], True)
69
+ product, product_elements = _adjacency_and_elements(lgp[1], True)
70
+ if reactant.shape != product.shape:
71
+ raise ValueError("reactant and product must have the same number of atoms")
72
+ if not np.array_equal(reactant, reactant.T) or not np.array_equal(
73
+ product, product.T
74
+ ):
75
+ raise ValueError("edit-support enumeration requires undirected graphs")
76
+ if np.any(np.diag(reactant)) or np.any(np.diag(product)):
77
+ raise ValueError("edit-support enumeration requires loop-free graphs")
78
+ if Counter(reactant_elements) != Counter(product_elements):
79
+ raise ValueError("reactant and product atom-type multisets differ")
80
+ return reactant, product, reactant_elements, product_elements
81
+
82
+
83
+ def _edge_tuple(adjacency):
84
+ return tuple(
85
+ (left, right)
86
+ for left in range(adjacency.shape[0])
87
+ for right in range(left + 1, adjacency.shape[0])
88
+ if adjacency[left, right] != 0
89
+ )
90
+
91
+
92
+ def binary_edit_budget(lgp, CD, *, tolerance: float = 1e-9) -> BinaryEditBudget:
93
+ """Return the exact binary broken/formed-edge budget for ``CD``.
94
+
95
+ An infeasible numeric lattice value returns ``feasible=False`` rather than
96
+ raising. Structural input violations still raise because this backend is
97
+ defined only for balanced, undirected, loop-free endpoints after bond
98
+ orders are reduced to bond presence.
99
+ """
100
+ if isinstance(CD, bool) or not isinstance(CD, Real):
101
+ raise TypeError("CD must be a non-negative number")
102
+ target_value = float(CD)
103
+ if not math.isfinite(target_value) or target_value < 0:
104
+ raise ValueError("CD must be finite and non-negative")
105
+ if tolerance < 0 or not math.isfinite(tolerance):
106
+ raise ValueError("tolerance must be finite and non-negative")
107
+
108
+ reactant, product, _, _ = _binary_undirected_endpoints(lgp)
109
+ reactant_edges = len(_edge_tuple(reactant))
110
+ product_edges = len(_edge_tuple(product))
111
+ rounded = round(target_value)
112
+ if not math.isclose(target_value, rounded, abs_tol=tolerance, rel_tol=0.0):
113
+ return BinaryEditBudget(
114
+ target=target_value,
115
+ reactant_edge_count=reactant_edges,
116
+ product_edge_count=product_edges,
117
+ broken_edge_count=None,
118
+ formed_edge_count=None,
119
+ support_pair_count=0,
120
+ feasible=False,
121
+ rejection_reason="binary chemical distance must be integral",
122
+ )
123
+
124
+ target = int(rounded)
125
+ broken_numerator = target + reactant_edges - product_edges
126
+ formed_numerator = target - reactant_edges + product_edges
127
+ if broken_numerator % 2 or formed_numerator % 2:
128
+ return BinaryEditBudget(
129
+ target=target,
130
+ reactant_edge_count=reactant_edges,
131
+ product_edge_count=product_edges,
132
+ broken_edge_count=None,
133
+ formed_edge_count=None,
134
+ support_pair_count=0,
135
+ feasible=False,
136
+ rejection_reason="target violates the binary bond-count parity lattice",
137
+ )
138
+ broken = broken_numerator // 2
139
+ formed = formed_numerator // 2
140
+ if broken < 0 or formed < 0 or broken > reactant_edges or formed > product_edges:
141
+ return BinaryEditBudget(
142
+ target=target,
143
+ reactant_edge_count=reactant_edges,
144
+ product_edge_count=product_edges,
145
+ broken_edge_count=broken,
146
+ formed_edge_count=formed,
147
+ support_pair_count=0,
148
+ feasible=False,
149
+ rejection_reason="target implies an impossible broken/formed-edge budget",
150
+ )
151
+ support_pairs = math.comb(reactant_edges, broken) * math.comb(product_edges, formed)
152
+ return BinaryEditBudget(
153
+ target=target,
154
+ reactant_edge_count=reactant_edges,
155
+ product_edge_count=product_edges,
156
+ broken_edge_count=broken,
157
+ formed_edge_count=formed,
158
+ support_pair_count=support_pairs,
159
+ feasible=True,
160
+ )
161
+
162
+
163
+ def _typed_label(value):
164
+ return f"{type(value).__module__}.{type(value).__qualname__}:{value!r}"
165
+
166
+
167
+ def _endpoint_graph(adjacency, elements):
168
+ graph = nx.Graph()
169
+ graph.add_nodes_from(
170
+ (atom, {"element": _typed_label(element)})
171
+ for atom, element in enumerate(elements)
172
+ )
173
+ graph.add_edges_from(_edge_tuple(adjacency))
174
+ return graph
175
+
176
+
177
+ def _residual_graph(endpoint, deleted_edges):
178
+ residual = endpoint.copy()
179
+ residual.remove_edges_from(deleted_edges)
180
+ return residual
181
+
182
+
183
+ def _residual_invariant(graph):
184
+ """Return an isomorphism-invariant, collision-tolerant bucket key."""
185
+ components = []
186
+ for nodes in nx.connected_components(graph):
187
+ subgraph = graph.subgraph(nodes)
188
+ labels = Counter(subgraph.nodes[node]["element"] for node in nodes)
189
+ degree_labels = Counter(
190
+ (subgraph.nodes[node]["element"], subgraph.degree[node]) for node in nodes
191
+ )
192
+ components.append(
193
+ (
194
+ len(nodes),
195
+ subgraph.number_of_edges(),
196
+ tuple(sorted(labels.items())),
197
+ tuple(sorted(degree_labels.items())),
198
+ )
199
+ )
200
+ return tuple(sorted(components))
201
+
202
+
203
+ def _mapping_cost(reactant, product, mapping):
204
+ images = np.asarray(mapping, dtype=int)
205
+ mapped_product = product[images[:, None], images[None, :]]
206
+ return 0.5 * float(np.abs(reactant - mapped_product).sum())
207
+
208
+
209
+ def enumerate_binary_edit_support_mappings( # noqa: C901
210
+ lgp,
211
+ CD,
212
+ *,
213
+ tolerance: float = 1e-9,
214
+ max_support_pairs: int | None = 100_000,
215
+ max_mappings: int | None = 100_000,
216
+ time_limit_seconds: float | None = None,
217
+ collect_mappings: bool = True,
218
+ mapping_callback=None,
219
+ ) -> BinaryEditSupportResult:
220
+ """Enumerate the complete global binary shell at a supplied numeric CD.
221
+
222
+ The default support and mapping caps protect interactive use. A cap never
223
+ changes the mathematical shell: reaching one returns ``complete=False``.
224
+ For exact counting with bounded output memory, use
225
+ ``collect_mappings=False, max_mappings=None`` and optionally a callback.
226
+ The wall-time check is cooperative at support and yielded-isomorphism
227
+ boundaries; use an external process limit when a hard deadline is needed.
228
+ """
229
+ started = time.perf_counter()
230
+ _validate_limit(max_support_pairs, "max_support_pairs")
231
+ _validate_limit(max_mappings, "max_mappings")
232
+ if time_limit_seconds is not None and (
233
+ isinstance(time_limit_seconds, bool)
234
+ or not isinstance(time_limit_seconds, Real)
235
+ or not math.isfinite(float(time_limit_seconds))
236
+ or time_limit_seconds < 0
237
+ ):
238
+ raise ValueError("time_limit_seconds must be finite and non-negative")
239
+ if not isinstance(collect_mappings, bool):
240
+ raise TypeError("collect_mappings must be boolean")
241
+ if mapping_callback is not None and not callable(mapping_callback):
242
+ raise TypeError("mapping_callback must be callable or None")
243
+
244
+ reactant, product, reactant_elements, product_elements = (
245
+ _binary_undirected_endpoints(lgp)
246
+ )
247
+ budget = binary_edit_budget(lgp, CD, tolerance=tolerance)
248
+ if not budget.feasible:
249
+ return BinaryEditSupportResult(
250
+ target=budget.target,
251
+ mappings=[],
252
+ complete=True,
253
+ truncation_reason=None,
254
+ budget=budget,
255
+ support_pairs_visited=0,
256
+ invariant_compatible_pairs=0,
257
+ isomorphisms_visited=0,
258
+ selected_mapping_count=0,
259
+ elapsed_seconds=time.perf_counter() - started,
260
+ )
261
+ if max_support_pairs is not None and budget.support_pair_count > max_support_pairs:
262
+ return BinaryEditSupportResult(
263
+ target=budget.target,
264
+ mappings=[],
265
+ complete=False,
266
+ truncation_reason="support_pair_limit",
267
+ budget=budget,
268
+ support_pairs_visited=0,
269
+ invariant_compatible_pairs=0,
270
+ isomorphisms_visited=0,
271
+ selected_mapping_count=0,
272
+ elapsed_seconds=time.perf_counter() - started,
273
+ )
274
+
275
+ deadline = (
276
+ None if time_limit_seconds is None else started + float(time_limit_seconds)
277
+ )
278
+ reactant_edges = _edge_tuple(reactant)
279
+ product_edges = _edge_tuple(product)
280
+ reactant_graph = _endpoint_graph(reactant, reactant_elements)
281
+ product_graph = _endpoint_graph(product, product_elements)
282
+ product_variants = defaultdict(list)
283
+ for deleted in itertools.combinations(product_edges, budget.formed_edge_count):
284
+ if deadline is not None and time.perf_counter() >= deadline:
285
+ return BinaryEditSupportResult(
286
+ target=budget.target,
287
+ mappings=[],
288
+ complete=False,
289
+ truncation_reason="time_limit",
290
+ budget=budget,
291
+ support_pairs_visited=0,
292
+ invariant_compatible_pairs=0,
293
+ isomorphisms_visited=0,
294
+ selected_mapping_count=0,
295
+ elapsed_seconds=time.perf_counter() - started,
296
+ )
297
+ residual = _residual_graph(product_graph, deleted)
298
+ product_variants[_residual_invariant(residual)].append(deleted)
299
+
300
+ node_match = nx.algorithms.isomorphism.categorical_node_match("element", None)
301
+ mappings = []
302
+ support_pairs_visited = 0
303
+ compatible_pairs = 0
304
+ isomorphisms_visited = 0
305
+ selected_mapping_count = 0
306
+ truncation_reason = None
307
+
308
+ for reactant_deleted in itertools.combinations(
309
+ reactant_edges, budget.broken_edge_count
310
+ ):
311
+ if deadline is not None and time.perf_counter() >= deadline:
312
+ truncation_reason = "time_limit"
313
+ break
314
+ reactant_residual = _residual_graph(reactant_graph, reactant_deleted)
315
+ key = _residual_invariant(reactant_residual)
316
+ candidates = product_variants.get(key, ())
317
+ support_pairs_visited += math.comb(len(product_edges), budget.formed_edge_count)
318
+ for product_deleted in candidates:
319
+ if deadline is not None and time.perf_counter() >= deadline:
320
+ truncation_reason = "time_limit"
321
+ break
322
+ compatible_pairs += 1
323
+ product_residual = _residual_graph(product_graph, product_deleted)
324
+ matcher = nx.algorithms.isomorphism.GraphMatcher(
325
+ reactant_residual,
326
+ product_residual,
327
+ node_match=node_match,
328
+ )
329
+ for isomorphism in matcher.isomorphisms_iter():
330
+ if deadline is not None and time.perf_counter() >= deadline:
331
+ truncation_reason = "time_limit"
332
+ break
333
+ isomorphisms_visited += 1
334
+ mapping = [int(isomorphism[atom]) for atom in range(reactant.shape[0])]
335
+ cost = _mapping_cost(reactant, product, mapping)
336
+ if not math.isclose(
337
+ cost,
338
+ budget.target,
339
+ abs_tol=tolerance,
340
+ rel_tol=0.0,
341
+ ):
342
+ continue
343
+ selected_mapping_count += 1
344
+ if collect_mappings:
345
+ mappings.append(mapping)
346
+ if mapping_callback is not None:
347
+ mapping_callback(mapping, cost)
348
+ if max_mappings is not None and selected_mapping_count >= max_mappings:
349
+ truncation_reason = "mapping_limit"
350
+ break
351
+ if truncation_reason is not None:
352
+ break
353
+ if truncation_reason is not None:
354
+ break
355
+
356
+ return BinaryEditSupportResult(
357
+ target=budget.target,
358
+ mappings=mappings,
359
+ complete=truncation_reason is None,
360
+ truncation_reason=truncation_reason,
361
+ budget=budget,
362
+ support_pairs_visited=support_pairs_visited,
363
+ invariant_compatible_pairs=compatible_pairs,
364
+ isomorphisms_visited=isomorphisms_visited,
365
+ selected_mapping_count=selected_mapping_count,
366
+ elapsed_seconds=time.perf_counter() - started,
367
+ )
368
+
369
+
370
+ __all__ = [
371
+ "BinaryEditBudget",
372
+ "BinaryEditSupportResult",
373
+ "binary_edit_budget",
374
+ "enumerate_binary_edit_support_mappings",
375
+ ]
@@ -0,0 +1,224 @@
1
+ """Choose an exact solver for a distance shell and record the selection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from collections import Counter
7
+ from dataclasses import asdict, dataclass
8
+ from numbers import Integral
9
+ from typing import Literal
10
+
11
+ import numpy as np
12
+
13
+ from ..slap.lap import _adjacency_and_elements
14
+ from .distance_search.search import enumerate_distance_mappings, normalize_distance_target
15
+ from .distance_search.records import DistanceEnumerationResult
16
+ from .edit_support import (
17
+ BinaryEditBudget,
18
+ binary_edit_budget,
19
+ enumerate_binary_edit_support_mappings,
20
+ )
21
+
22
+ HybridBackend = Literal["auto", "assignment", "edit_support"]
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class HybridBackendDecision:
27
+ """Machine-readable explanation of one exact backend choice."""
28
+
29
+ requested_backend: HybridBackend
30
+ selected_backend: str
31
+ selection_reason: str
32
+ edit_support_pair_count: int | None
33
+ edit_support_pair_limit: int | None
34
+ output_scope: str
35
+
36
+ def as_dict(self) -> dict[str, object]:
37
+ return asdict(self)
38
+
39
+
40
+ def _total_bijections(lgp, binary):
41
+ reactant, reactant_elements = _adjacency_and_elements(lgp[0], binary)
42
+ product, product_elements = _adjacency_and_elements(lgp[1], binary)
43
+ if reactant.shape != product.shape:
44
+ raise ValueError("reactant and product must have the same number of atoms")
45
+ reactant_counts = Counter(reactant_elements)
46
+ if reactant_counts != Counter(product_elements):
47
+ raise ValueError("reactant and product atom-type multisets differ")
48
+ total = math.prod(math.factorial(count) for count in reactant_counts.values())
49
+ maximum = 0.5 * float(np.abs(reactant).sum() + np.abs(product).sum())
50
+ return total, maximum
51
+
52
+
53
+ def _edit_ineligibility(target, binary, options):
54
+ if target == "minimal":
55
+ return "edit-support backend requires a numeric shell"
56
+ if not binary:
57
+ return "edit-support backend requires binary bond presence"
58
+ if options.get("certify", False):
59
+ return "edit-support backend does not emit prefix certificates"
60
+ if options.get("symmetry_pruning", False):
61
+ return "edit-support backend currently returns labeled mappings only"
62
+ if options.get("expand_symmetry", False):
63
+ return "edit-support backend does not use symmetry expansion"
64
+ if options.get("fixed_mapping"):
65
+ return "edit-support backend does not support fixed assignments"
66
+ if options.get("compute_minimum_cost", False):
67
+ return "edit-support backend does not run the optimization pass"
68
+ return None
69
+
70
+
71
+ def enumerate_hybrid_distance_mappings(
72
+ lgp,
73
+ *,
74
+ CD="minimal",
75
+ binary=True,
76
+ backend: HybridBackend = "auto",
77
+ max_edit_support_pairs: int | None = 50_000,
78
+ max_bijections: int | None = 1_000_000,
79
+ tolerance: float = 1e-9,
80
+ time_limit_seconds: float | None = None,
81
+ max_mappings: int | None = None,
82
+ collect_mappings: bool = True,
83
+ mapping_callback=None,
84
+ compute_minimum_cost: bool = False,
85
+ **assignment_options,
86
+ ) -> DistanceEnumerationResult:
87
+ """Select an exact backend while preserving the requested output scope.
88
+
89
+ The edit-support route is selected only for a numeric, binary, labeled
90
+ shell without fixed assignments or certificate requests. Otherwise the
91
+ complete assignment branch-and-bound implementation is used. Every result
92
+ records the backend and selection evidence; automatic selection never
93
+ substitutes a quotient shell for a labeled shell.
94
+ """
95
+ if backend not in {"auto", "assignment", "edit_support"}:
96
+ raise ValueError("backend must be 'auto', 'assignment', or 'edit_support'")
97
+ if max_edit_support_pairs is not None and (
98
+ isinstance(max_edit_support_pairs, bool)
99
+ or not isinstance(max_edit_support_pairs, Integral)
100
+ or max_edit_support_pairs < 1
101
+ ):
102
+ raise ValueError("max_edit_support_pairs must be positive or None")
103
+
104
+ target = normalize_distance_target(CD)
105
+ options = dict(assignment_options)
106
+ options["compute_minimum_cost"] = compute_minimum_cost
107
+ reason = _edit_ineligibility(target, binary, options)
108
+ budget: BinaryEditBudget | None = None
109
+ if reason is None:
110
+ try:
111
+ budget = binary_edit_budget(lgp, target, tolerance=tolerance)
112
+ except ValueError as error:
113
+ reason = f"edit-support structural precondition failed: {error}"
114
+ if (
115
+ reason is None
116
+ and budget is not None
117
+ and max_edit_support_pairs is not None
118
+ and budget.support_pair_count > max_edit_support_pairs
119
+ ):
120
+ reason = (
121
+ f"estimated support pairs {budget.support_pair_count} exceed "
122
+ f"selector limit {max_edit_support_pairs}"
123
+ )
124
+
125
+ select_edit = backend == "edit_support" or (backend == "auto" and reason is None)
126
+ if backend == "edit_support" and reason is not None:
127
+ raise ValueError(reason)
128
+ if backend == "assignment":
129
+ reason = "assignment backend explicitly requested"
130
+ select_edit = False
131
+
132
+ if not select_edit:
133
+ decision = HybridBackendDecision(
134
+ requested_backend=backend,
135
+ selected_backend="assignment_branch_and_bound",
136
+ selection_reason=reason or "assignment backend selected",
137
+ edit_support_pair_count=(
138
+ None if budget is None else budget.support_pair_count
139
+ ),
140
+ edit_support_pair_limit=max_edit_support_pairs,
141
+ output_scope="complete_atom_compatible_assignment_space",
142
+ )
143
+ result = enumerate_distance_mappings(
144
+ lgp,
145
+ CD=target,
146
+ binary=binary,
147
+ max_bijections=max_bijections,
148
+ tolerance=tolerance,
149
+ time_limit_seconds=time_limit_seconds,
150
+ max_mappings=max_mappings,
151
+ collect_mappings=collect_mappings,
152
+ mapping_callback=mapping_callback,
153
+ compute_minimum_cost=compute_minimum_cost,
154
+ **assignment_options,
155
+ )
156
+ result.backend = decision.selected_backend
157
+ result.backend_statistics = {
158
+ **(result.backend_statistics or {}),
159
+ "selector": decision.as_dict(),
160
+ }
161
+ return result
162
+
163
+ decision = HybridBackendDecision(
164
+ requested_backend=backend,
165
+ selected_backend="binary_edit_support",
166
+ selection_reason=(
167
+ "numeric binary support estimate is within the selector limit"
168
+ ),
169
+ edit_support_pair_count=budget.support_pair_count,
170
+ edit_support_pair_limit=max_edit_support_pairs,
171
+ output_scope="complete_binary_atom_compatible_cd_shell",
172
+ )
173
+ edit_result = enumerate_binary_edit_support_mappings(
174
+ lgp,
175
+ target,
176
+ tolerance=tolerance,
177
+ max_support_pairs=max_edit_support_pairs,
178
+ max_mappings=max_mappings,
179
+ time_limit_seconds=time_limit_seconds,
180
+ collect_mappings=collect_mappings,
181
+ mapping_callback=mapping_callback,
182
+ )
183
+ total_bijections, maximum_cost = _total_bijections(lgp, True)
184
+ selected_cost = float(target) if edit_result.selected_mapping_count else None
185
+ status = (
186
+ "timeout"
187
+ if not edit_result.complete
188
+ else ("complete" if edit_result.selected_mapping_count else "no_solutions")
189
+ )
190
+ return DistanceEnumerationResult(
191
+ target=target,
192
+ cost=selected_cost,
193
+ minimum_cost=None,
194
+ mappings=edit_result.mappings,
195
+ distances=[float(target)] * len(edit_result.mappings),
196
+ total_bijections=total_bijections,
197
+ maximum_cost_upper_bound=maximum_cost,
198
+ visited_leaves=edit_result.isomorphisms_visited,
199
+ pruned_branches=(
200
+ edit_result.support_pairs_visited - edit_result.invariant_compatible_pairs
201
+ ),
202
+ elapsed_seconds=edit_result.elapsed_seconds,
203
+ status=status,
204
+ complete=edit_result.complete,
205
+ truncation_reason=edit_result.truncation_reason,
206
+ scope=edit_result.scope,
207
+ visited_nodes=edit_result.support_pairs_visited,
208
+ selected_mapping_count=edit_result.selected_mapping_count,
209
+ selected_labeled_mapping_count=edit_result.selected_mapping_count,
210
+ backend=decision.selected_backend,
211
+ backend_statistics={
212
+ "selector": decision.as_dict(),
213
+ "support_pairs_visited": edit_result.support_pairs_visited,
214
+ "invariant_compatible_pairs": (edit_result.invariant_compatible_pairs),
215
+ "isomorphisms_visited": edit_result.isomorphisms_visited,
216
+ },
217
+ )
218
+
219
+
220
+ __all__ = [
221
+ "HybridBackend",
222
+ "HybridBackendDecision",
223
+ "enumerate_hybrid_distance_mappings",
224
+ ]