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,66 @@
1
+ """Budgeted verified cyclic-subgroup selection for exact propagation search."""
2
+
3
+ from .propagation_limits import PropagationDeadline, check_deadline
4
+
5
+
6
+ def bounded_cyclic_subgroup(permutations, *, max_order, deadline):
7
+ """Retain a fully closed subgroup; interruption never returns partial powers."""
8
+ if not permutations:
9
+ return ()
10
+ identity = tuple(range(len(permutations[0])))
11
+ best = (identity,)
12
+ for generator in permutations:
13
+ check_deadline(deadline)
14
+ powers, seen, current = [identity], {identity}, identity
15
+ while len(powers) <= max_order:
16
+ check_deadline(deadline)
17
+ current = tuple(generator[image] for image in current)
18
+ if current == identity:
19
+ if len(powers) > len(best):
20
+ best = tuple(powers)
21
+ break
22
+ if current in seen:
23
+ break
24
+ seen.add(current)
25
+ powers.append(current)
26
+ return best
27
+
28
+
29
+ def bounded_generated_subgroup(permutations, *, max_order, deadline):
30
+ """Close verified generators, retaining only fully completed subgroups."""
31
+ if not permutations:
32
+ return ()
33
+ identity = tuple(range(len(permutations[0])))
34
+ subgroup = {identity}
35
+ try:
36
+ for generator in permutations:
37
+ check_deadline(deadline)
38
+ if generator in subgroup:
39
+ continue
40
+ inverse = [0] * len(generator)
41
+ for index, image in enumerate(generator):
42
+ inverse[image] = index
43
+ inverse = tuple(inverse)
44
+ closure = set(subgroup)
45
+ pending = list(subgroup)
46
+ exceeded = False
47
+ factors = (*subgroup, generator, inverse)
48
+ while pending and not exceeded:
49
+ check_deadline(deadline)
50
+ current = pending.pop()
51
+ for factor in factors:
52
+ product = tuple(
53
+ current[factor[index]] for index in range(len(identity))
54
+ )
55
+ if product in closure:
56
+ continue
57
+ if len(closure) >= max_order:
58
+ exceeded = True
59
+ break
60
+ closure.add(product)
61
+ pending.append(product)
62
+ if not exceeded:
63
+ subgroup = closure
64
+ except PropagationDeadline:
65
+ pass
66
+ return tuple(sorted(subgroup))
@@ -0,0 +1,136 @@
1
+ """Reversible typed bond masses for cheap exact graph-search bounds."""
2
+
3
+ import numpy as np
4
+
5
+
6
+ class ResidualTypedBonds:
7
+ """Track signed sums by unordered atom-type pairs and absolute edge mass."""
8
+
9
+ def __init__(self, a, b, labels, product_labels, rows, columns):
10
+ types = {
11
+ label: i
12
+ for i, label in enumerate(dict.fromkeys([*labels, *product_labels]))
13
+ }
14
+ size = len(types)
15
+ self.a, self.b = a, b
16
+
17
+ def buckets(values):
18
+ colors = np.asarray([types[label] for label in values], dtype=int)
19
+ return np.minimum(colors[:, None], colors) * size + np.maximum(
20
+ colors[:, None], colors
21
+ )
22
+
23
+ self.reactant_buckets, self.product_buckets = buckets(labels), buckets(
24
+ product_labels
25
+ )
26
+ # Small exact bond alphabets are common in this objective. Keep a
27
+ # reversible histogram only when its dense representation is bounded;
28
+ # uncommon large alphabets retain the existing linear bound.
29
+ weights = np.concatenate((a[np.triu_indices(len(a), 1)],
30
+ b[np.triu_indices(len(b), 1)]))
31
+ self.levels = np.unique(weights)
32
+ if not len(self.levels):
33
+ self.levels = np.asarray([0], dtype=np.int64)
34
+ self.histogram_enabled = len(self.levels) <= 16
35
+ self.level_index = (
36
+ {int(value): index for index, value in enumerate(self.levels)}
37
+ if self.histogram_enabled
38
+ else None
39
+ )
40
+ self.reactant_histogram = None
41
+ self.product_histogram = None
42
+ if self.histogram_enabled:
43
+ shape = (size * size, len(self.levels))
44
+ self.reactant_histogram = np.zeros(shape, dtype=np.int64)
45
+ self.product_histogram = np.zeros(shape, dtype=np.int64)
46
+ self._initialize_histogram(
47
+ a, self.reactant_buckets, rows, self.reactant_histogram, self.levels
48
+ )
49
+ self._initialize_histogram(
50
+ b, self.product_buckets, columns, self.product_histogram, self.levels
51
+ )
52
+ self.reactant = np.zeros(size * size, dtype=np.int64)
53
+ self.product = np.zeros(size * size, dtype=np.int64)
54
+ self.reactant_absolute = self._initialize(
55
+ a, self.reactant_buckets, rows, self.reactant
56
+ )
57
+ self.product_absolute = self._initialize(
58
+ b, self.product_buckets, columns, self.product
59
+ )
60
+
61
+ @staticmethod
62
+ def _initialize(matrix, buckets, positions, mass):
63
+ positions = np.asarray(positions, dtype=int)
64
+ i, j = np.triu_indices(len(positions), 1)
65
+ left, right = positions[i], positions[j]
66
+ weights = matrix[left, right]
67
+ np.add.at(mass, buckets[left, right], weights)
68
+ return int(np.abs(weights).sum())
69
+
70
+ def lower_bound(self):
71
+ """Sum reverse-triangle bounds over disjoint, preserved type classes."""
72
+ return int(np.abs(self.reactant - self.product).sum())
73
+
74
+ @staticmethod
75
+ def _initialize_histogram(matrix, buckets, positions, histogram, levels):
76
+ positions = np.asarray(positions, dtype=int)
77
+ i, j = np.triu_indices(len(positions), 1)
78
+ left, right = positions[i], positions[j]
79
+ weight_index = np.searchsorted(levels, matrix[left, right])
80
+ np.add.at(histogram, (buckets[left, right], weight_index), 1)
81
+
82
+ def transport_lower_bound(self):
83
+ """Return the typed residual pair-transport bound, if cheaply indexed.
84
+
85
+ Within each unordered atom-type pair, a compatible vertex bijection
86
+ induces a bijection on unordered residual pairs. Matching those pair
87
+ weights without the vertex-consistency constraints gives a relaxation.
88
+ For one-dimensional absolute cost, sorted matching equals the integral
89
+ of the absolute difference between cumulative histograms.
90
+ """
91
+ if not self.histogram_enabled:
92
+ return None
93
+ cumulative = np.cumsum(
94
+ self.reactant_histogram - self.product_histogram, axis=1
95
+ )
96
+ gaps = np.diff(self.levels)
97
+ # A valid completion has equal pair counts in every compatible type
98
+ # bucket, so the final cumulative count must be zero.
99
+ if np.any(cumulative[:, -1]):
100
+ return None
101
+ return int((np.abs(cumulative[:, :-1]) @ gaps).sum())
102
+
103
+ def upper_bound(self):
104
+ """Bound residual disagreement by the sum of absolute bond masses."""
105
+ return self.reactant_absolute + self.product_absolute
106
+
107
+ def remove(self, row, image, rows, columns):
108
+ """Remove incident residual edges and return an exact restoration token."""
109
+ rows, columns = np.asarray(rows, dtype=int), np.asarray(columns, dtype=int)
110
+ rb, pb = self.reactant_buckets[row, rows], self.product_buckets[image, columns]
111
+ rw, pw = self.a[row, rows], self.b[image, columns]
112
+ ra, pa = int(np.abs(rw).sum()), int(np.abs(pw).sum())
113
+ np.add.at(self.reactant, rb, -rw)
114
+ np.add.at(self.product, pb, -pw)
115
+ histogram_token = None
116
+ if self.histogram_enabled:
117
+ ri = np.fromiter((self.level_index[int(x)] for x in rw), dtype=int)
118
+ pi = np.fromiter((self.level_index[int(x)] for x in pw), dtype=int)
119
+ np.add.at(self.reactant_histogram, (rb, ri), -1)
120
+ np.add.at(self.product_histogram, (pb, pi), -1)
121
+ histogram_token = rb, ri, pb, pi
122
+ self.reactant_absolute -= ra
123
+ self.product_absolute -= pa
124
+ return rb, rw, pb, pw, ra, pa, histogram_token
125
+
126
+ def restore(self, token):
127
+ """Restore the signed typed sums and absolute masses after a child."""
128
+ rb, rw, pb, pw, ra, pa, histogram_token = token
129
+ np.add.at(self.reactant, rb, rw)
130
+ np.add.at(self.product, pb, pw)
131
+ if histogram_token is not None:
132
+ rb, ri, pb, pi = histogram_token
133
+ np.add.at(self.reactant_histogram, (rb, ri), 1)
134
+ np.add.at(self.product_histogram, (pb, pi), 1)
135
+ self.reactant_absolute += ra
136
+ self.product_absolute += pa
@@ -0,0 +1,201 @@
1
+ """Exact residual spectra using sparse source bonds and a frontier state.
2
+
3
+ For signed weights, ``|a-b| = |a| + |b| - w(a,b)``, where ``w`` is
4
+ twice the smaller magnitude when the signs agree, and zero otherwise.
5
+ Product bond mass is constant under a bijection. Consequently the only
6
+ prefix images needed for future pair rewards are those on the source-bond
7
+ frontier. The used-image subset still retains every assigned image.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import time
13
+ from numbers import Integral
14
+
15
+ import numpy as np
16
+
17
+ from .suffix_spectrum import SuffixCostSpectrum, _StateLimit
18
+
19
+
20
+ class RewardFrontierSpectrum(SuffixCostSpectrum):
21
+ """Bounded exact CD supports computed from sparse preserved-bond rewards.
22
+
23
+ Domains and unary costs are static within this prepared instance. Source
24
+ rows retain their supplied order. Canonicalization remains in reconstruction
25
+ so the memoized support does not depend on a prefix-specific subgroup.
26
+ An excessive reward envelope or interrupted preparation is a fallback,
27
+ never an infeasibility certificate.
28
+
29
+ At each assignment the increment is unary cost plus source bond mass to
30
+ earlier rows plus product bond mass to used images, minus the conserved
31
+ bond reward. This equals the literal sum of nonnegative pair disagreements,
32
+ so distance-cap pruning remains valid without retaining a future-cost table.
33
+ """
34
+
35
+ def __init__(self, *args, max_reward=4096, **kwargs):
36
+ super().__init__(*args, **kwargs)
37
+ if (
38
+ isinstance(max_reward, bool)
39
+ or not isinstance(max_reward, Integral)
40
+ or max_reward < 1
41
+ ):
42
+ raise ValueError("max_reward must be a positive integer")
43
+ if len(set(self.rows)) != self.n or len(set(self.columns)) != self.n:
44
+ raise ValueError("residual rows and columns must be distinct")
45
+ a = self.a[np.ix_(self.rows, self.rows)]
46
+ b = self.b[np.ix_(self.columns, self.columns)]
47
+ if (
48
+ not np.array_equal(a, a.T)
49
+ or not np.array_equal(b, b.T)
50
+ or np.any(np.diag(a))
51
+ or np.any(np.diag(b))
52
+ ):
53
+ raise ValueError("reward spectra require symmetric zero-diagonal matrices")
54
+ # Python integers avoid overflowing sums and signed magnitudes.
55
+ self.source = tuple(tuple(int(x) for x in row) for row in a)
56
+ self.product = tuple(tuple(int(x) for x in row) for row in b)
57
+ self.domain_masks = tuple(
58
+ sum(1 << col for col in range(self.n) if self.allowed[row, col])
59
+ for row in range(self.n)
60
+ )
61
+ shifts = tuple(
62
+ max(
63
+ (
64
+ int(self.unary[row, col])
65
+ for col in range(self.n)
66
+ if self.allowed[row, col]
67
+ ),
68
+ default=0,
69
+ )
70
+ for row in range(self.n)
71
+ )
72
+ self.unary_costs = tuple(
73
+ tuple(int(value) for value in row) for row in self.unary
74
+ )
75
+ self.prefix_mass = tuple(
76
+ sum(abs(self.source[row][depth]) for row in range(depth))
77
+ for depth in range(self.n)
78
+ )
79
+ self.product_mass_edges = tuple(
80
+ tuple(
81
+ (1 << image, abs(weight)) for image, weight in enumerate(row) if weight
82
+ )
83
+ for row in self.product
84
+ )
85
+ self._product_mass_cache = {}
86
+ self.constant = sum(shifts) + sum(
87
+ abs(self.source[i][k]) + abs(self.product[i][k])
88
+ for i in range(self.n)
89
+ for k in range(i + 1, self.n)
90
+ )
91
+ self.max_reward = int(max_reward)
92
+ self.frontiers = tuple(
93
+ tuple(
94
+ row
95
+ for row in range(depth)
96
+ if any(self.source[row][next_row] for next_row in range(depth, self.n))
97
+ )
98
+ for depth in range(self.n + 1)
99
+ )
100
+ self.max_frontier = max(map(len, self.frontiers), default=0)
101
+ self._advance = tuple(
102
+ tuple(
103
+ -1 if row == depth else self.frontiers[depth].index(row)
104
+ for row in self.frontiers[depth + 1]
105
+ )
106
+ for depth in range(self.n)
107
+ )
108
+ self._incident = tuple(
109
+ tuple(
110
+ (position, self.source[row][depth])
111
+ for position, row in enumerate(self.frontiers[depth])
112
+ if self.source[row][depth]
113
+ )
114
+ for depth in range(self.n)
115
+ )
116
+
117
+ @staticmethod
118
+ def _reward(source, product):
119
+ if (source > 0 and product > 0) or (source < 0 and product < 0):
120
+ return 2 * min(abs(source), abs(product))
121
+ return 0
122
+
123
+ def _used_mass(self, used, col):
124
+ if not self.product_mass_edges[col]:
125
+ return 0
126
+ key = used, col
127
+ value = self._product_mass_cache.get(key)
128
+ if value is None:
129
+ value = sum(
130
+ weight for bit, weight in self.product_mass_edges[col] if used & bit
131
+ )
132
+ self._product_mass_cache[key] = value
133
+ return value
134
+
135
+ def _frontier_state(self, depth, used, frontier):
136
+ self._check()
137
+ key = depth, used, frontier
138
+ cached = self.memo.get(key)
139
+ if cached is not None:
140
+ return key, cached[0]
141
+ self._tick()
142
+ if depth == self.n:
143
+ self.memo[key] = (1, ())
144
+ return key, 1
145
+ support, branches = 0, []
146
+ candidates = self.domain_masks[depth] & ~used
147
+ while candidates:
148
+ self._check()
149
+ bit = candidates & -candidates
150
+ col = bit.bit_length() - 1
151
+ candidates ^= bit
152
+ increment = (
153
+ self.unary_costs[depth][col]
154
+ + self.prefix_mass[depth]
155
+ + self._used_mass(used, col)
156
+ )
157
+ for position, weight in self._incident[depth]:
158
+ increment -= self._reward(weight, self.product[frontier[position]][col])
159
+ if increment < 0:
160
+ raise ArithmeticError(
161
+ "Sparse reward increment disagrees with a nonnegative CD"
162
+ )
163
+ if increment > self.max_cost:
164
+ continue
165
+ child_frontier = tuple(
166
+ col if position == -1 else frontier[position]
167
+ for position in self._advance[depth]
168
+ )
169
+ child_key, child_support = self._frontier_state(
170
+ depth + 1, used | bit, child_frontier
171
+ )
172
+ if child_support:
173
+ support |= child_support << increment
174
+ branches.append((col, increment, child_key))
175
+ self.transitions += 1
176
+ support &= (1 << (self.max_cost + 1)) - 1
177
+ self.memo[key] = support, tuple(branches)
178
+ return key, support
179
+
180
+ def prepare(self):
181
+ """Prepare exact supports in the admitted envelope, or request fallback."""
182
+ if self._prepared:
183
+ return not self._prepare_failed
184
+ self._prepared = True
185
+ self._prepare_started = time.perf_counter()
186
+ self.support = 0
187
+ if self.constant > self.max_reward:
188
+ self._prepare_failed = True
189
+ return False
190
+ try:
191
+ self.root_key, self.support = self._frontier_state(0, 0, ())
192
+ return True
193
+ except _StateLimit:
194
+ self._prepare_failed = True
195
+ self.memo.clear()
196
+ self._product_mass_cache.clear()
197
+ self.support = 0
198
+ return False
199
+
200
+
201
+ __all__ = ["RewardFrontierSpectrum"]
@@ -0,0 +1,258 @@
1
+ """Bounded exact separator lower bound for residual weighted assignments.
2
+
3
+ This is an optimization oracle, not a shell enumerator. It returns the exact
4
+ minimum residual cost when it finishes within its state budget; callers may
5
+ only use that value as an admissible lower bound. If the budget is exceeded it
6
+ returns ``None`` and the ordinary PABS bounds remain in force.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import itertools
12
+ import math
13
+
14
+ import numpy as np
15
+
16
+ from .propagation_limits import check_deadline
17
+
18
+
19
+ class _StateLimit(Exception):
20
+ pass
21
+
22
+
23
+ def _components(rows, adjacency, removed):
24
+ remaining = set(rows) - set(removed)
25
+ result = []
26
+ while remaining:
27
+ root = min(remaining)
28
+ component = {root}
29
+ stack = [root]
30
+ remaining.remove(root)
31
+ while stack:
32
+ node = stack.pop()
33
+ neighbors = [other for other in remaining if adjacency[node, other]]
34
+ for other in neighbors:
35
+ remaining.remove(other)
36
+ component.add(other)
37
+ stack.append(other)
38
+ result.append(tuple(sorted(component)))
39
+ return result
40
+
41
+
42
+ def _balanced_component_split(components, n, alpha):
43
+ """Group disconnected components into two balanced nonempty sides."""
44
+ if len(components) < 2:
45
+ return None
46
+ limit = math.floor(alpha * n)
47
+ best = None
48
+ # Components are disjoint, so a subset determines one side of the cut.
49
+ for mask in range(1, (1 << len(components)) - 1):
50
+ if not (mask & 1):
51
+ continue # remove the symmetric duplicate
52
+ left = tuple(
53
+ sorted(i for k, c in enumerate(components) if mask >> k & 1 for i in c)
54
+ )
55
+ right = tuple(
56
+ sorted(
57
+ i for k, c in enumerate(components) if not (mask >> k & 1) for i in c
58
+ )
59
+ )
60
+ if not left or not right or max(len(left), len(right)) > limit:
61
+ continue
62
+ candidate = (max(len(left), len(right)), left, right)
63
+ if best is None or candidate < best:
64
+ best = candidate
65
+ return None if best is None else (best[1], best[2])
66
+
67
+
68
+ def _find_separator(rows, adjacency, max_separator_size, alpha):
69
+ n = len(rows)
70
+ for size in range(1, min(max_separator_size, n - 2) + 1):
71
+ for separator in itertools.combinations(rows, size):
72
+ components = _components(rows, adjacency, separator)
73
+ split = _balanced_component_split(components, n, alpha)
74
+ if split is not None:
75
+ return tuple(separator), split
76
+ return None
77
+
78
+
79
+ def minimum_separator_residual_cost( # noqa: C901
80
+ a,
81
+ b,
82
+ rows,
83
+ columns,
84
+ unary,
85
+ allowed,
86
+ *,
87
+ deadline=None,
88
+ max_separator_size=2,
89
+ balance_alpha=2 / 3,
90
+ max_states=20_000,
91
+ base_case=4,
92
+ ):
93
+ """Return an exact minimum residual QAP cost in integer quarter units.
94
+
95
+ ``unary[x, y]`` contains all costs between residual assignment ``x -> y``
96
+ and already-fixed rows. Pair costs use the mapper's exact absolute edge
97
+ disagreement ``abs(a[i,k] - b[j,l])``; one undirected pair is counted once.
98
+ Element compatibility and propagated domains are represented by ``allowed``.
99
+
100
+ A separator's assigned image is absorbed into child unary costs. Once the
101
+ source sides are disconnected, their cross cost is independent of the
102
+ particular child maps and is the product cut insertion cost. This is the
103
+ SR-GED decomposition specialized to the mapper's fixed-cardinality,
104
+ weighted assignment objective.
105
+ """
106
+ rows, columns = tuple(rows), tuple(columns)
107
+ unary = np.asarray(unary, dtype=np.int64)
108
+ allowed = np.asarray(allowed, dtype=bool)
109
+ if (
110
+ len(rows) != len(columns)
111
+ or unary.shape != allowed.shape
112
+ or unary.shape != (len(rows), len(columns))
113
+ ):
114
+ raise ValueError("residual arrays must be square and match row/column sets")
115
+ if not rows:
116
+ return 0
117
+ if len(rows) > 12:
118
+ return None
119
+ adjacency = np.asarray(a != 0, dtype=bool)
120
+ states = 0
121
+
122
+ def tick():
123
+ nonlocal states
124
+ check_deadline(deadline)
125
+ states += 1
126
+ if states > max_states:
127
+ raise _StateLimit
128
+
129
+ def pair_cost(i, k, j, image):
130
+ return abs(int(a[i, k]) - int(b[j, image]))
131
+
132
+ def brute(rset, cset, costs, domains):
133
+ best = None
134
+ for permutation in itertools.permutations(range(len(cset))):
135
+ tick()
136
+ if any(not domains[x, permutation[x]] for x in range(len(rset))):
137
+ continue
138
+ value = sum(int(costs[x, permutation[x]]) for x in range(len(rset)))
139
+ for x in range(len(rset)):
140
+ for y in range(x + 1, len(rset)):
141
+ value += pair_cost(
142
+ rset[x], rset[y], cset[permutation[x]], cset[permutation[y]]
143
+ )
144
+ best = value if best is None else min(best, value)
145
+ return best
146
+
147
+ def solve(rset, cset, costs, domains):
148
+ tick()
149
+ n = len(rset)
150
+ if n <= base_case:
151
+ return brute(rset, cset, costs, domains)
152
+ found = _find_separator(rset, adjacency, max_separator_size, balance_alpha)
153
+ if found is None:
154
+ return brute(rset, cset, costs, domains)
155
+ separator, (left_rows, right_rows) = found
156
+ separator_positions = [rset.index(i) for i in separator]
157
+ best = None
158
+ for chosen in itertools.permutations(cset, len(separator)):
159
+ tick()
160
+ if any(
161
+ not domains[pos, cset.index(j)]
162
+ for pos, j in zip(separator_positions, chosen)
163
+ ):
164
+ continue
165
+ chosen_map = dict(zip(separator, chosen))
166
+ separator_cost = sum(
167
+ int(costs[pos, cset.index(chosen_map[row])])
168
+ for pos, row in zip(separator_positions, separator)
169
+ )
170
+ for p, row in enumerate(separator):
171
+ for q in range(p + 1, len(separator)):
172
+ other = separator[q]
173
+ separator_cost += pair_cost(
174
+ row, other, chosen_map[row], chosen_map[other]
175
+ )
176
+ free_columns = tuple(j for j in cset if j not in set(chosen))
177
+ left_size = len(left_rows)
178
+ if left_size > len(free_columns):
179
+ continue
180
+ left_positions = [rset.index(i) for i in left_rows]
181
+ right_positions = [rset.index(i) for i in right_rows]
182
+ for left_columns in itertools.combinations(free_columns, left_size):
183
+ tick()
184
+ right_columns = tuple(
185
+ j for j in free_columns if j not in set(left_columns)
186
+ )
187
+ if len(right_columns) != len(right_rows):
188
+ continue
189
+ left_costs = (
190
+ np.asarray(
191
+ [
192
+ [costs[x, cset.index(j)] for j in left_columns]
193
+ for x in left_positions
194
+ ],
195
+ dtype=np.int64,
196
+ )
197
+ .reshape(len(left_rows), len(left_columns))
198
+ .copy()
199
+ )
200
+ right_costs = (
201
+ np.asarray(
202
+ [
203
+ [costs[x, cset.index(j)] for j in right_columns]
204
+ for x in right_positions
205
+ ],
206
+ dtype=np.int64,
207
+ )
208
+ .reshape(len(right_rows), len(right_columns))
209
+ .copy()
210
+ )
211
+ left_allowed = np.asarray(
212
+ [
213
+ [domains[x, cset.index(j)] for j in left_columns]
214
+ for x in left_positions
215
+ ],
216
+ dtype=bool,
217
+ ).reshape(len(left_rows), len(left_columns))
218
+ right_allowed = np.asarray(
219
+ [
220
+ [domains[x, cset.index(j)] for j in right_columns]
221
+ for x in right_positions
222
+ ],
223
+ dtype=bool,
224
+ ).reshape(len(right_rows), len(right_columns))
225
+ for x, row in enumerate(left_rows):
226
+ for y, image in enumerate(left_columns):
227
+ left_costs[x, y] += sum(
228
+ pair_cost(row, sep_row, image, chosen_map[sep_row])
229
+ for sep_row in separator
230
+ )
231
+ for x, row in enumerate(right_rows):
232
+ for y, image in enumerate(right_columns):
233
+ right_costs[x, y] += sum(
234
+ pair_cost(row, sep_row, image, chosen_map[sep_row])
235
+ for sep_row in separator
236
+ )
237
+ left_value = solve(left_rows, left_columns, left_costs, left_allowed)
238
+ if left_value is None:
239
+ continue
240
+ right_value = solve(
241
+ right_rows, right_columns, right_costs, right_allowed
242
+ )
243
+ if right_value is None:
244
+ continue
245
+ cut = sum(
246
+ abs(int(b[j, k])) for j in left_columns for k in right_columns
247
+ )
248
+ total = separator_cost + left_value + right_value + cut
249
+ best = total if best is None else min(best, total)
250
+ return best
251
+
252
+ try:
253
+ return solve(rows, columns, unary, allowed)
254
+ except _StateLimit:
255
+ return None
256
+
257
+
258
+ __all__ = ["minimum_separator_residual_cost"]