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,679 @@
1
+ """Python exact graph-assignment search with propagation and assignment bounds.
2
+
3
+ This is the Python implementation selected by the PABS (Propagation and
4
+ Assignment-Bounded Search) interface. It maintains reversible atom-assignment
5
+ domains, enforces one-to-one matching, and prunes using assignment lower bounds.
6
+ It searches the full compatible assignment space under any explicitly supplied
7
+ fixed assignments; it does not extract a SLAP uncertainty kernel. Inputs outside
8
+ its arithmetic domain use ``enumerate_distance_mappings`` with a recorded
9
+ fallback. Both routes preserve the requested chemical-distance objective.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import math
15
+ import time
16
+ from collections import Counter
17
+ from dataclasses import dataclass, replace
18
+ from numbers import Integral, Real
19
+
20
+ import numpy as np
21
+
22
+ from ....graph.automorphism import bounded_automorphism_permutations
23
+ from ...slap.lap import _adjacency_and_elements
24
+ from ..distance_search.search import enumerate_distance_mappings
25
+ from ..distance_search.records import (
26
+ DistanceEnumerationResult,
27
+ ExactEnumerationLimitError,
28
+ normalize_distance_target,
29
+ )
30
+ from .propagation_search import PropagatedSearch
31
+ from .propagation_symmetry import bounded_generated_subgroup
32
+ from .propagation_limits import PropagationDeadline, check_deadline
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class PropagationConfig:
37
+ """Controls for Python exact propagation, assignment bounds, and residual spectra."""
38
+
39
+ domain_propagation: bool = True
40
+ adaptive_bounds: bool = True
41
+ incremental_assignments: bool = True
42
+ cache_propagation: bool = True
43
+ block_assignments: bool = True
44
+ typed_mass_bounds: bool = True
45
+ typed_transport_bounds: bool = True
46
+ batch_forced_assignments: bool = True
47
+ cost_lattice_pruning: bool = True
48
+ # These incumbent/star experiments are optional: the paired 100-case
49
+ # histogram+batch run showed better end-to-end performance without them.
50
+ seed_local_search: bool = False
51
+ seed_improvement_seconds: float = 0.05
52
+ star_assignment_bounds: bool = False
53
+ star_bound_residual_limit: int = 16
54
+ star_bound_max_anchors: int = 32
55
+ star_bound_seconds: float = 0.25
56
+ pairwise_edge_bounds: bool = False
57
+ pairwise_bound_residual_limit: int = 12
58
+ pairwise_bound_max_anchors: int = 16
59
+ pairwise_bound_seconds: float = 0.1
60
+ column_cost_bounds: bool = True
61
+ cycle_cost_bounds: bool = True
62
+ # Experimental SR-GED style exact residual bound. It is opt-in until the
63
+ # cohort benchmark demonstrates an end-to-end win.
64
+ separator_bounds: bool = False
65
+ separator_spectrum: bool = False
66
+ separator_residual_limit: int = 10
67
+ separator_max_size: int = 2
68
+ separator_max_states: int = 20_000
69
+ separator_spectrum_max_calls: int = 16
70
+ # Factor-wise bitset support is a cheaper but weaker residual CD test.
71
+ factor_spectrum_bounds: bool = False
72
+ factor_spectrum_residual_limit: int = 10
73
+ factor_spectrum_max_range: int = 4096
74
+ factor_spectrum_max_calls: int = 16
75
+ # Exact fixed-order suffix-state merging for small selected-CD residuals.
76
+ suffix_spectrum: bool = False
77
+ suffix_spectrum_orbit_pruning: bool = False
78
+ suffix_spectrum_representation: str = "cost_table"
79
+ suffix_spectrum_max_reward: int = 4096
80
+ suffix_spectrum_residual_limit: int = 10
81
+ suffix_spectrum_max_states: int = 20_000
82
+ suffix_spectrum_max_cost: int = 4096
83
+ suffix_spectrum_max_calls: int = 2
84
+ suffix_spectrum_max_seconds_per_call: float = 0.002
85
+ branch_order: str = "default"
86
+ small_residual: int = 48
87
+ bound_interval: int = 4
88
+ bound_slack: int = 16
89
+
90
+ def __post_init__(self):
91
+ for name in (
92
+ "domain_propagation",
93
+ "adaptive_bounds",
94
+ "incremental_assignments",
95
+ "cache_propagation",
96
+ "block_assignments",
97
+ "typed_mass_bounds",
98
+ "typed_transport_bounds",
99
+ "batch_forced_assignments",
100
+ "cost_lattice_pruning",
101
+ "seed_local_search",
102
+ "star_assignment_bounds",
103
+ "column_cost_bounds",
104
+ "cycle_cost_bounds",
105
+ "separator_bounds",
106
+ "separator_spectrum",
107
+ "factor_spectrum_bounds",
108
+ "suffix_spectrum",
109
+ "suffix_spectrum_orbit_pruning",
110
+ ):
111
+ if not isinstance(getattr(self, name), bool):
112
+ raise TypeError(f"{name} must be Boolean")
113
+ for name in (
114
+ "small_residual",
115
+ "bound_interval",
116
+ "bound_slack",
117
+ "star_bound_residual_limit",
118
+ "star_bound_max_anchors",
119
+ "pairwise_bound_residual_limit",
120
+ "pairwise_bound_max_anchors",
121
+ "separator_residual_limit",
122
+ "separator_max_size",
123
+ "separator_max_states",
124
+ "separator_spectrum_max_calls",
125
+ "factor_spectrum_residual_limit",
126
+ "factor_spectrum_max_range",
127
+ "factor_spectrum_max_calls",
128
+ "suffix_spectrum_residual_limit",
129
+ "suffix_spectrum_max_states",
130
+ "suffix_spectrum_max_cost",
131
+ "suffix_spectrum_max_reward",
132
+ "suffix_spectrum_max_calls",
133
+ ):
134
+ value = getattr(self, name)
135
+ if isinstance(value, bool) or not isinstance(value, Integral) or value < 1:
136
+ raise ValueError(f"{name} must be a positive integer")
137
+ if not isinstance(
138
+ self.suffix_spectrum_representation, str
139
+ ) or self.suffix_spectrum_representation not in {
140
+ "cost_table",
141
+ "reward_frontier",
142
+ }:
143
+ raise ValueError(
144
+ "suffix_spectrum_representation must be cost_table or reward_frontier"
145
+ )
146
+ suffix_seconds = self.suffix_spectrum_max_seconds_per_call
147
+ if (
148
+ isinstance(suffix_seconds, bool)
149
+ or not isinstance(suffix_seconds, Real)
150
+ or not math.isfinite(suffix_seconds)
151
+ or suffix_seconds <= 0
152
+ or suffix_seconds > 0.1
153
+ ):
154
+ raise ValueError(
155
+ "suffix_spectrum_max_seconds_per_call must be finite and in (0, 0.1]"
156
+ )
157
+ star_seconds = self.star_bound_seconds
158
+ if (
159
+ isinstance(star_seconds, bool)
160
+ or not isinstance(star_seconds, Real)
161
+ or not math.isfinite(star_seconds)
162
+ or star_seconds < 0
163
+ or star_seconds > 0.5
164
+ ):
165
+ raise ValueError("star_bound_seconds must be finite and in [0, 0.5]")
166
+ seconds = self.seed_improvement_seconds
167
+ if (
168
+ isinstance(seconds, bool)
169
+ or not isinstance(seconds, Real)
170
+ or not math.isfinite(seconds)
171
+ or seconds < 0
172
+ or seconds > 0.1
173
+ ):
174
+ raise ValueError("seed_improvement_seconds must be finite and in [0, 0.1]")
175
+ if not isinstance(self.branch_order, str) or self.branch_order not in {
176
+ "default",
177
+ "pagerank",
178
+ "impact",
179
+ "contention",
180
+ }:
181
+ raise ValueError(
182
+ "branch_order must be 'default', 'pagerank', 'impact', or 'contention'"
183
+ )
184
+ pairwise_seconds = self.pairwise_bound_seconds
185
+ if (
186
+ isinstance(pairwise_seconds, bool)
187
+ or not isinstance(pairwise_seconds, Real)
188
+ or not math.isfinite(pairwise_seconds)
189
+ or pairwise_seconds < 0
190
+ or pairwise_seconds > 0.5
191
+ ):
192
+ raise ValueError("pairwise_bound_seconds must be finite and in [0, 0.5]")
193
+
194
+
195
+ def _validate_options(tolerance, seconds, caps, flags, callback, config):
196
+ if (
197
+ isinstance(tolerance, bool)
198
+ or not isinstance(tolerance, Real)
199
+ or not math.isfinite(tolerance)
200
+ or tolerance < 0
201
+ ):
202
+ raise ValueError("tolerance must be finite and nonnegative")
203
+ if seconds is not None and (
204
+ isinstance(seconds, bool)
205
+ or not isinstance(seconds, Real)
206
+ or not math.isfinite(seconds)
207
+ or seconds < 0
208
+ ):
209
+ raise ValueError("time_limit_seconds must be finite and nonnegative")
210
+ for name, value in caps.items():
211
+ if value is not None and (
212
+ isinstance(value, bool) or not isinstance(value, Integral) or value < 1
213
+ ):
214
+ raise ValueError(f"{name} must be a positive integer or None")
215
+ for name, value in flags.items():
216
+ if not isinstance(value, bool):
217
+ raise TypeError(f"{name} must be Boolean")
218
+ if callback is not None and not callable(callback):
219
+ raise TypeError("mapping_callback must be callable or None")
220
+ if not isinstance(config, PropagationConfig):
221
+ raise TypeError("config must be a PropagationConfig")
222
+
223
+
224
+ def _fixed_and_seed(labels, product_labels, fixed_mapping, initial_mapping):
225
+ n = len(labels)
226
+ if fixed_mapping is not None and not hasattr(fixed_mapping, "items"):
227
+ raise TypeError("fixed_mapping must be a mapping or None")
228
+ fixed = dict(fixed_mapping or {})
229
+ if any(
230
+ isinstance(x, bool) or not isinstance(x, Integral)
231
+ for pair in fixed.items()
232
+ for x in pair
233
+ ):
234
+ raise TypeError("fixed_mapping indices must be integers")
235
+ if any(not 0 <= i < n or not 0 <= j < n for i, j in fixed.items()):
236
+ raise ValueError("fixed_mapping index out of range")
237
+ if len(set(fixed.values())) != len(fixed) or any(
238
+ labels[i] != product_labels[j] for i, j in fixed.items()
239
+ ):
240
+ raise ValueError("fixed_mapping must be injective and atom-compatible")
241
+ if initial_mapping is None:
242
+ seed = [-1] * n
243
+ available = set(range(n)) - set(fixed.values())
244
+ for i, j in fixed.items():
245
+ seed[i] = int(j)
246
+ for i in range(n):
247
+ if i in fixed:
248
+ continue
249
+ j = next(j for j in sorted(available) if labels[i] == product_labels[j])
250
+ seed[i] = j
251
+ available.remove(j)
252
+ else:
253
+ seed = list(initial_mapping)
254
+ if any(isinstance(x, bool) or not isinstance(x, Integral) for x in seed):
255
+ raise TypeError("initial_mapping indices must be integers")
256
+ if sorted(seed) != list(range(n)) or any(
257
+ labels[i] != product_labels[j] for i, j in enumerate(seed)
258
+ ):
259
+ raise ValueError("initial_mapping must be an atom-compatible permutation")
260
+ if any(seed[i] != j for i, j in fixed.items()):
261
+ raise ValueError("initial_mapping must agree with fixed_mapping")
262
+ return fixed, list(map(int, seed))
263
+
264
+
265
+ def _eligible(a, b, certify):
266
+ if certify:
267
+ return "legacy_prefix_certificate_requested"
268
+ if len(a) > 256:
269
+ return "order_above_256"
270
+ if not np.isfinite(a).all() or not np.isfinite(b).all():
271
+ return "nonfinite_weights"
272
+ if (
273
+ not np.array_equal(a, a.T)
274
+ or not np.array_equal(b, b.T)
275
+ or np.any(np.diag(a))
276
+ or np.any(np.diag(b))
277
+ ):
278
+ return "directed_or_looped_graph"
279
+ if (
280
+ not np.equal(a * 2, np.rint(a * 2)).all()
281
+ or not np.equal(b * 2, np.rint(b * 2)).all()
282
+ ):
283
+ return "weights_outside_half_integer_lattice"
284
+ if 2 * (np.abs(a).sum() + np.abs(b).sum()) > 2**30:
285
+ return "weights_above_exact_integer_range"
286
+ return None
287
+
288
+
289
+ def _result(
290
+ target,
291
+ total,
292
+ maximum,
293
+ started,
294
+ search,
295
+ mappings,
296
+ distances,
297
+ minimum,
298
+ complete,
299
+ group,
300
+ scope,
301
+ ):
302
+ count = search.selected
303
+ cost = minimum if target == "minimal" else float(target) if count else None
304
+ return DistanceEnumerationResult(
305
+ target=target,
306
+ cost=cost,
307
+ minimum_cost=minimum,
308
+ mappings=mappings,
309
+ distances=distances,
310
+ total_bijections=total,
311
+ maximum_cost_upper_bound=maximum,
312
+ visited_leaves=search.statistics["visited_leaves"],
313
+ pruned_branches=sum(
314
+ search.statistics[k]
315
+ for k in ("lower_bound_pruned", "upper_bound_pruned", "hall_pruned")
316
+ ),
317
+ elapsed_seconds=time.perf_counter() - started,
318
+ status="timeout" if not complete else "complete" if count else "no_solutions",
319
+ complete=complete,
320
+ truncation_reason=search.stop_reason,
321
+ scope=scope,
322
+ visited_nodes=search.statistics["visited_nodes"],
323
+ symmetry_pruned_branches=search.statistics["symmetry_pruned"],
324
+ lower_bound_pruned_branches=search.statistics["lower_bound_pruned"],
325
+ upper_bound_pruned_branches=search.statistics["upper_bound_pruned"],
326
+ selected_mapping_count=count,
327
+ symmetry_group_order=len(group),
328
+ symmetry_automorphism_count=len(group),
329
+ selected_labeled_mapping_count=(
330
+ count
331
+ if search.expand
332
+ else None if search.reactant_symmetry else count * len(group)
333
+ ),
334
+ backend="synister_cp",
335
+ backend_statistics={"search": dict(search.statistics)},
336
+ )
337
+
338
+
339
+ def _initialize_search(
340
+ a,
341
+ b,
342
+ labels,
343
+ product_labels,
344
+ lgp,
345
+ seed,
346
+ fixed,
347
+ config,
348
+ deadline,
349
+ *,
350
+ binary,
351
+ symmetry_pruning,
352
+ reactant_symmetry_pruning,
353
+ expand_symmetry,
354
+ symmetry_node_properties,
355
+ max_symmetry_automorphisms,
356
+ symmetry_timeout_seconds,
357
+ symmetry_max_search_nodes,
358
+ ):
359
+ """Budget symmetry discovery, subgroup closure and certified root setup."""
360
+ check_deadline(deadline)
361
+ group = (tuple(range(len(a))),)
362
+ reactant_group = (tuple(range(len(a))),)
363
+ discovered = True
364
+ if symmetry_pruning:
365
+ remaining = None if deadline is None else max(0, deadline - time.perf_counter())
366
+ seconds = (
367
+ math.inf if symmetry_timeout_seconds is None else symmetry_timeout_seconds
368
+ )
369
+ if remaining is not None:
370
+ seconds = min(seconds, remaining)
371
+ symmetry_deadline = deadline
372
+ automorphism_seconds = seconds
373
+ if math.isfinite(seconds):
374
+ local_deadline = time.perf_counter() + seconds
375
+ if symmetry_deadline is None or local_deadline < symmetry_deadline:
376
+ symmetry_deadline = local_deadline
377
+ automorphism_seconds = (
378
+ 0.4 * seconds if reactant_symmetry_pruning else 0.8 * seconds
379
+ )
380
+ permutations, discovered = bounded_automorphism_permutations(
381
+ lgp[1],
382
+ binary,
383
+ limit=max_symmetry_automorphisms,
384
+ timeout_seconds=automorphism_seconds,
385
+ max_search_nodes=symmetry_max_search_nodes,
386
+ node_properties=symmetry_node_properties,
387
+ )
388
+ group = bounded_generated_subgroup(
389
+ permutations,
390
+ max_order=max_symmetry_automorphisms,
391
+ deadline=symmetry_deadline,
392
+ )
393
+ group = tuple(g for g in group if all(g[j] == j for j in fixed.values()))
394
+ if reactant_symmetry_pruning:
395
+ if not symmetry_pruning:
396
+ raise ValueError("reactant symmetry pruning requires symmetry_pruning=True")
397
+ remaining = None if deadline is None else max(0, deadline - time.perf_counter())
398
+ seconds = (
399
+ math.inf if symmetry_timeout_seconds is None else symmetry_timeout_seconds
400
+ )
401
+ if remaining is not None:
402
+ seconds = min(seconds, remaining)
403
+ automorphism_seconds = seconds
404
+ if math.isfinite(seconds):
405
+ automorphism_seconds = 0.4 * seconds
406
+ permutations, source_discovered = bounded_automorphism_permutations(
407
+ lgp[0],
408
+ binary,
409
+ limit=max_symmetry_automorphisms,
410
+ timeout_seconds=automorphism_seconds,
411
+ max_search_nodes=symmetry_max_search_nodes,
412
+ node_properties=symmetry_node_properties,
413
+ )
414
+ reactant_group = bounded_generated_subgroup(
415
+ permutations,
416
+ max_order=max_symmetry_automorphisms,
417
+ deadline=symmetry_deadline if symmetry_pruning else deadline,
418
+ )
419
+ reactant_group = tuple(
420
+ g for g in reactant_group if all(g[i] == i for i in fixed)
421
+ )
422
+ discovered = discovered and source_discovered
423
+ check_deadline(deadline)
424
+ search = PropagatedSearch(
425
+ np.rint(a * 4).astype(np.int64),
426
+ np.rint(b * 4).astype(np.int64),
427
+ labels,
428
+ product_labels,
429
+ seed,
430
+ fixed,
431
+ group,
432
+ reactant_group,
433
+ expand_symmetry,
434
+ config,
435
+ deadline,
436
+ )
437
+ check_deadline(deadline)
438
+ return search, group, reactant_group, discovered
439
+
440
+
441
+ def _setup_timeout(target, total, maximum, started, scope):
442
+ """Report an interrupted preprocessing phase without claiming infeasibility."""
443
+ return DistanceEnumerationResult(
444
+ target=target,
445
+ cost=None,
446
+ minimum_cost=None,
447
+ mappings=[],
448
+ distances=[],
449
+ total_bijections=total,
450
+ maximum_cost_upper_bound=maximum,
451
+ visited_leaves=0,
452
+ pruned_branches=0,
453
+ elapsed_seconds=time.perf_counter() - started,
454
+ status="timeout",
455
+ complete=False,
456
+ truncation_reason="time_limit",
457
+ scope=scope,
458
+ backend="synister_cp",
459
+ symmetry_search_complete=False,
460
+ backend_statistics={
461
+ "search": {"interrupted_phase": "preprocessing", "visited_nodes": 0}
462
+ },
463
+ )
464
+
465
+
466
+ def enumerate_synister_cp_mappings( # noqa: C901
467
+ lgp,
468
+ *,
469
+ CD="minimal",
470
+ binary=True,
471
+ initial_mapping=None,
472
+ fixed_mapping=None,
473
+ max_bijections=1_000_000,
474
+ max_mappings=None,
475
+ tolerance=1e-9,
476
+ time_limit_seconds=None,
477
+ symmetry_pruning=False,
478
+ reactant_symmetry_pruning=False,
479
+ expand_symmetry=False,
480
+ symmetry_node_properties=(),
481
+ max_symmetry_automorphisms=256,
482
+ symmetry_timeout_seconds=0.25,
483
+ symmetry_max_search_nodes=10_000,
484
+ collect_mappings=True,
485
+ mapping_callback=None,
486
+ compute_minimum_cost=True,
487
+ certify=False,
488
+ config=None,
489
+ ):
490
+ """Enumerate exact weighted shells using Python graph-assignment propagation.
491
+
492
+ This is the Python implementation behind ``enumerate_pabs_mappings``.
493
+ Propagation maintains compatible one-to-one assignments; LAP lower bounds
494
+ prune partial mappings that cannot reach the requested cost. A supplied
495
+ ``initial_mapping`` is a feasible seed, while ``fixed_mapping`` restricts
496
+ the search. No SLAP uncertainty kernel is extracted.
497
+
498
+ The new path supports loop-free undirected half-integer matrices of order
499
+ at most 256 in a bounded integer range. Generic graphs and legacy tree-cover
500
+ certificates explicitly fall back to the unchanged legacy solver. Verified
501
+ cyclic product subgroups provide unique expansion, including fixed subspaces.
502
+
503
+ :param lgp: Reactant/product labeled graphs.
504
+ :param CD: ``'minimal'`` or a nonnegative selected distance.
505
+ :param config: Optional propagation and adaptive-bound controls.
506
+ :return: Standard distance-enumeration result with explicit backend metadata.
507
+ """
508
+ started = time.perf_counter()
509
+ target = normalize_distance_target(CD)
510
+ config = PropagationConfig() if config is None else config
511
+ flags = dict(
512
+ binary=binary,
513
+ symmetry_pruning=symmetry_pruning,
514
+ expand_symmetry=expand_symmetry,
515
+ collect_mappings=collect_mappings,
516
+ compute_minimum_cost=compute_minimum_cost,
517
+ certify=certify,
518
+ )
519
+ if not isinstance(reactant_symmetry_pruning, bool):
520
+ raise TypeError("reactant_symmetry_pruning must be Boolean")
521
+ if reactant_symmetry_pruning and not symmetry_pruning:
522
+ raise ValueError("reactant_symmetry_pruning requires symmetry_pruning=True")
523
+ caps = dict(
524
+ max_bijections=max_bijections,
525
+ max_mappings=max_mappings,
526
+ max_symmetry_automorphisms=max_symmetry_automorphisms,
527
+ symmetry_max_search_nodes=symmetry_max_search_nodes,
528
+ )
529
+ _validate_options(
530
+ tolerance, time_limit_seconds, caps, flags, mapping_callback, config
531
+ )
532
+ if max_symmetry_automorphisms is None or symmetry_max_search_nodes is None:
533
+ raise ValueError("Symmetry discovery caps must be positive integers")
534
+ _validate_options(tolerance, symmetry_timeout_seconds, {}, {}, None, config)
535
+ if expand_symmetry and not symmetry_pruning:
536
+ raise ValueError("expand_symmetry requires symmetry_pruning")
537
+ if isinstance(symmetry_node_properties, str):
538
+ raise TypeError("symmetry_node_properties must be a sequence")
539
+ a, labels = _adjacency_and_elements(lgp[0], binary)
540
+ b, product_labels = _adjacency_and_elements(lgp[1], binary)
541
+ if a.shape != b.shape or Counter(labels) != Counter(product_labels):
542
+ raise ValueError("Endpoints must have equal atom-type inventories")
543
+ fixed, seed = _fixed_and_seed(
544
+ labels, product_labels, fixed_mapping, initial_mapping
545
+ )
546
+ reason = _eligible(a, b, certify)
547
+ if reason is not None:
548
+ result = enumerate_distance_mappings(
549
+ lgp,
550
+ CD=CD,
551
+ initial_mapping=initial_mapping,
552
+ fixed_mapping=fixed_mapping,
553
+ tolerance=tolerance,
554
+ time_limit_seconds=time_limit_seconds,
555
+ mapping_callback=mapping_callback,
556
+ symmetry_node_properties=symmetry_node_properties,
557
+ symmetry_timeout_seconds=symmetry_timeout_seconds,
558
+ **flags,
559
+ **caps,
560
+ )
561
+ statistics = dict(result.backend_statistics or {})
562
+ statistics["synister_cp_fallback"] = reason
563
+ return replace(
564
+ result, backend="synister_cp_legacy_fallback", backend_statistics=statistics
565
+ )
566
+ counts = Counter(labels[i] for i in range(len(labels)) if i not in fixed)
567
+ total = math.prod(math.factorial(count) for count in counts.values())
568
+ if max_bijections is not None and total > max_bijections:
569
+ raise ExactEnumerationLimitError(
570
+ f"{total:,} atom-compatible bijections exceed max_bijections={max_bijections:,}"
571
+ )
572
+ maximum = 0.5 * float(np.abs(a).sum() + np.abs(b).sum())
573
+ deadline = None if time_limit_seconds is None else started + time_limit_seconds
574
+ scope = (
575
+ "fixed_assignment_subspace"
576
+ if fixed
577
+ else "complete_atom_compatible_assignment_space"
578
+ )
579
+ if symmetry_pruning and not expand_symmetry:
580
+ scope = (
581
+ "verified_reactant_product_orbit_representatives"
582
+ if reactant_symmetry_pruning
583
+ else "verified_cyclic_product_orbit_representatives"
584
+ ) + ("_within_fixed_subspace" if fixed else "")
585
+ try:
586
+ search, group, reactant_group, discovered = _initialize_search(
587
+ a,
588
+ b,
589
+ labels,
590
+ product_labels,
591
+ lgp,
592
+ seed,
593
+ fixed,
594
+ config,
595
+ deadline,
596
+ binary=binary,
597
+ symmetry_pruning=symmetry_pruning,
598
+ reactant_symmetry_pruning=reactant_symmetry_pruning,
599
+ expand_symmetry=expand_symmetry,
600
+ symmetry_node_properties=symmetry_node_properties,
601
+ max_symmetry_automorphisms=max_symmetry_automorphisms,
602
+ symmetry_timeout_seconds=symmetry_timeout_seconds,
603
+ symmetry_max_search_nodes=symmetry_max_search_nodes,
604
+ )
605
+ except PropagationDeadline:
606
+ return _setup_timeout(target, total, maximum, started, scope)
607
+ search.statistics["preprocessing_seconds"] = time.perf_counter() - started
608
+ search.selected, search.stop_reason = 0, None
609
+ minimum, complete = None, True
610
+ if target == "minimal" or compute_minimum_cost:
611
+ before = time.perf_counter()
612
+ complete = search.prove()
613
+ search.statistics["minimum_proof_seconds"] = time.perf_counter() - before
614
+ search.statistics["minimum_proof_nodes"] = search.statistics["visited_nodes"]
615
+ search.statistics["minimum_proof_status"] = (
616
+ "proved" if complete else "time_limit"
617
+ )
618
+ if complete:
619
+ minimum = search.incumbent / 4
620
+ else:
621
+ search.statistics["minimum_proof_seconds"] = None
622
+ search.statistics["minimum_proof_nodes"] = None
623
+ search.statistics["minimum_proof_status"] = "not_requested"
624
+ search.statistics["incumbent_quarters"] = int(search.incumbent)
625
+ search.statistics["root_lower_bound_quarters"] = int(search.root_lower)
626
+ search.statistics["root_gap_quarters"] = int(search.incumbent - search.root_lower)
627
+ mappings, distances = [], []
628
+
629
+ def emit(mapping, cost):
630
+ if collect_mappings:
631
+ mappings.append(mapping)
632
+ distances.append(cost)
633
+ if mapping_callback is not None:
634
+ mapping_callback(mapping, cost)
635
+
636
+ enumeration_seconds = 0.0
637
+ enumeration_nodes = 0
638
+ if target != "minimal" or complete:
639
+ selected = minimum if target == "minimal" else target
640
+ lower, upper = math.ceil((selected - tolerance) * 4), math.floor(
641
+ (selected + tolerance) * 4
642
+ )
643
+ before_nodes = search.statistics["visited_nodes"]
644
+ enumeration_started = time.perf_counter()
645
+ complete = search.enumerate(lower, upper, emit, max_mappings)
646
+ enumeration_seconds = time.perf_counter() - enumeration_started
647
+ enumeration_nodes = search.statistics["visited_nodes"] - before_nodes
648
+ search.statistics["enumeration_seconds"] = enumeration_seconds
649
+ search.statistics["enumeration_nodes"] = enumeration_nodes
650
+ search.statistics["enumeration_status"] = (
651
+ "complete"
652
+ if enumeration_seconds and complete
653
+ else "incomplete" if enumeration_seconds else "not_started"
654
+ )
655
+ scope = (
656
+ "fixed_assignment_subspace"
657
+ if fixed
658
+ else "complete_atom_compatible_assignment_space"
659
+ )
660
+ if symmetry_pruning and not expand_symmetry:
661
+ scope = (
662
+ "verified_reactant_product_orbit_representatives"
663
+ if reactant_symmetry_pruning
664
+ else "verified_cyclic_product_orbit_representatives"
665
+ ) + ("_within_fixed_subspace" if fixed else "")
666
+ result = _result(
667
+ target,
668
+ total,
669
+ maximum,
670
+ started,
671
+ search,
672
+ mappings,
673
+ distances,
674
+ minimum,
675
+ complete,
676
+ group,
677
+ scope,
678
+ )
679
+ return replace(result, symmetry_search_complete=discovered)