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,577 @@
1
+ """Finite occurrence processes with explicit material flow and concurrency."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from enum import Enum
7
+ from typing import Any, Hashable, Iterable, Mapping
8
+
9
+ import networkx as nx
10
+
11
+ from synkit.Graph.Morphism import LLGError, LLGMorphism, LewisLabelledGraph
12
+ from synkit.Rule.Apply import RuleSpan
13
+ from synkit.Rule.Compose import (
14
+ CommutationCertificate,
15
+ LawIssue,
16
+ rule_spans_isomorphic,
17
+ )
18
+
19
+
20
+ class ProcessIssueCode(str, Enum):
21
+ """Stable failures for occurrence values, flows, orders, and choices."""
22
+
23
+ DUPLICATE_ID = "PROCESS_DUPLICATE_ID"
24
+ UNKNOWN_MATERIAL = "PROCESS_UNKNOWN_MATERIAL"
25
+ UNKNOWN_EVENT = "PROCESS_UNKNOWN_EVENT"
26
+ ENDPOINT_PARTITION = "PROCESS_ENDPOINT_PARTITION"
27
+ BINDING_NOT_ISOMORPHIC = "PROCESS_BINDING_NOT_ISOMORPHIC"
28
+ MATERIAL_REUSED = "PROCESS_MATERIAL_REUSED"
29
+ CAUSAL_CYCLE = "PROCESS_CAUSAL_CYCLE"
30
+ MISSING_INDEPENDENCE = "PROCESS_MISSING_INDEPENDENCE"
31
+ INVALID_INDEPENDENCE = "PROCESS_INVALID_INDEPENDENCE"
32
+ EXTRA_INDEPENDENCE = "PROCESS_EXTRA_INDEPENDENCE"
33
+ INVALID_EXTENSION = "PROCESS_INVALID_LINEAR_EXTENSION"
34
+ EXTENSION_LIMIT = "PROCESS_LINEAR_EXTENSION_LIMIT"
35
+ SERIES_PARALLEL_LIMIT = "PROCESS_SERIES_PARALLEL_LIMIT"
36
+ INVALID_ALTERNATIVE = "PROCESS_INVALID_ALTERNATIVE"
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class ProcessIssue:
41
+ code: ProcessIssueCode
42
+ message: str
43
+ context: Mapping[str, Any] = field(default_factory=dict)
44
+
45
+ def to_dict(self) -> dict[str, Any]:
46
+ return {
47
+ "code": self.code.value,
48
+ "message": self.message,
49
+ "context": dict(self.context),
50
+ }
51
+
52
+
53
+ class ProcessError(ValueError):
54
+ """Raised when occurrence-process data do not prove their semantics."""
55
+
56
+ def __init__(self, *issues: ProcessIssue):
57
+ self.issues = tuple(issues)
58
+ super().__init__("; ".join(issue.message for issue in issues))
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class MaterialOccurrence:
63
+ """One material copy; isomorphic values do not identify copy IDs."""
64
+
65
+ occurrence_id: str
66
+ value: LewisLabelledGraph
67
+
68
+ def __post_init__(self) -> None:
69
+ if not self.occurrence_id:
70
+ raise ValueError("A material occurrence requires a non-empty ID.")
71
+
72
+
73
+ @dataclass(frozen=True)
74
+ class MaterialBinding:
75
+ """An endpoint component isomorphism to one material occurrence."""
76
+
77
+ material_id: str
78
+ endpoint_nodes: frozenset[Hashable]
79
+ mapping: tuple[tuple[Hashable, Hashable], ...]
80
+
81
+ def __post_init__(self) -> None:
82
+ object.__setattr__(self, "endpoint_nodes", frozenset(self.endpoint_nodes))
83
+ pairs = (
84
+ self.mapping.items() if isinstance(self.mapping, Mapping) else self.mapping
85
+ )
86
+ object.__setattr__(self, "mapping", tuple(sorted(pairs, key=repr)))
87
+
88
+
89
+ @dataclass(frozen=True)
90
+ class RuleOccurrence:
91
+ """One event identity, independently of equality of its rule value."""
92
+
93
+ occurrence_id: str
94
+ rule: RuleSpan
95
+ inputs: tuple[MaterialBinding, ...] = ()
96
+ outputs: tuple[MaterialBinding, ...] = ()
97
+
98
+ def __post_init__(self) -> None:
99
+ if not self.occurrence_id:
100
+ raise ValueError("A rule occurrence requires a non-empty ID.")
101
+ object.__setattr__(self, "inputs", tuple(self.inputs))
102
+ object.__setattr__(self, "outputs", tuple(self.outputs))
103
+
104
+
105
+ @dataclass(frozen=True)
106
+ class IndependenceWitness:
107
+ """One incomparable event pair and its executable commutation proof."""
108
+
109
+ first_event: str
110
+ second_event: str
111
+ certificate: CommutationCertificate
112
+
113
+ @property
114
+ def key(self) -> frozenset[str]:
115
+ return frozenset((self.first_event, self.second_event))
116
+
117
+
118
+ @dataclass(frozen=True)
119
+ class LinearExtensionEquivalence:
120
+ """A path of adjacent certified-independent swaps between extensions."""
121
+
122
+ source: tuple[str, ...]
123
+ target: tuple[str, ...]
124
+ swaps: tuple[tuple[str, str], ...]
125
+
126
+ def replay(self, process: "OccurrenceProcess") -> bool:
127
+ if not process.is_linear_extension(
128
+ self.source
129
+ ) or not process.is_linear_extension(self.target):
130
+ return False
131
+ current = list(self.source)
132
+ witnesses = process.independence_by_pair
133
+ for left, right in self.swaps:
134
+ try:
135
+ index = next(
136
+ position
137
+ for position in range(len(current) - 1)
138
+ if current[position : position + 2] == [left, right]
139
+ )
140
+ except StopIteration:
141
+ return False
142
+ witness = witnesses.get(frozenset((left, right)))
143
+ if witness is None or not witness.certificate.replay():
144
+ return False
145
+ current[index], current[index + 1] = right, left
146
+ return tuple(current) == self.target
147
+
148
+
149
+ def _component_graph(
150
+ endpoint: LewisLabelledGraph, nodes: frozenset[Hashable]
151
+ ) -> LewisLabelledGraph:
152
+ graph = endpoint.to_networkx().subgraph(nodes).copy()
153
+ return LewisLabelledGraph.from_networkx(
154
+ graph, endpoint.schema, name=(endpoint.name, "component")
155
+ )
156
+
157
+
158
+ def _components(endpoint: LewisLabelledGraph) -> frozenset[frozenset[Hashable]]:
159
+ return frozenset(
160
+ frozenset(component)
161
+ for component in nx.connected_components(endpoint.to_networkx())
162
+ )
163
+
164
+
165
+ def _witness_rule_matches(declared: RuleSpan, witnessed: RuleSpan) -> bool:
166
+ """Require the supplied execution contract as well as the carrier span.
167
+
168
+ Arbitrary guard equivalence or guard transport is not inferred from span
169
+ isomorphism. A guarded event must use the same condition object that the
170
+ finite commutation witness executed.
171
+ """
172
+ return (
173
+ declared.condition is witnessed.condition
174
+ and declared.boundary == witnessed.boundary
175
+ and declared.environment == witnessed.environment
176
+ and rule_spans_isomorphic(declared, witnessed)
177
+ )
178
+
179
+
180
+ @dataclass(frozen=True)
181
+ class OccurrenceProcess:
182
+ """A finite causal configuration with explicit material-copy flow."""
183
+
184
+ process_id: str
185
+ materials: tuple[MaterialOccurrence, ...]
186
+ events: tuple[RuleOccurrence, ...]
187
+ independence: tuple[IndependenceWitness, ...] = ()
188
+
189
+ def __post_init__(self) -> None:
190
+ object.__setattr__(self, "materials", tuple(self.materials))
191
+ object.__setattr__(self, "events", tuple(self.events))
192
+ object.__setattr__(self, "independence", tuple(self.independence))
193
+ issues = self._validation_issues()
194
+ if issues:
195
+ raise ProcessError(*issues)
196
+
197
+ @property
198
+ def material_by_id(self) -> dict[str, MaterialOccurrence]:
199
+ return {item.occurrence_id: item for item in self.materials}
200
+
201
+ @property
202
+ def event_by_id(self) -> dict[str, RuleOccurrence]:
203
+ return {item.occurrence_id: item for item in self.events}
204
+
205
+ @property
206
+ def independence_by_pair(self) -> dict[frozenset[str], IndependenceWitness]:
207
+ return {item.key: item for item in self.independence}
208
+
209
+ def _binding_issues(
210
+ self,
211
+ event: RuleOccurrence,
212
+ bindings: tuple[MaterialBinding, ...],
213
+ endpoint: LewisLabelledGraph,
214
+ side: str,
215
+ ) -> list[ProcessIssue]:
216
+ issues: list[ProcessIssue] = []
217
+ materials = self.material_by_id
218
+ expected = _components(endpoint)
219
+ observed = frozenset(binding.endpoint_nodes for binding in bindings)
220
+ if observed != expected or len(observed) != len(bindings):
221
+ issues.append(
222
+ ProcessIssue(
223
+ ProcessIssueCode.ENDPOINT_PARTITION,
224
+ "Bindings must partition an endpoint into connected components.",
225
+ {"event": event.occurrence_id, "side": side},
226
+ )
227
+ )
228
+ material_ids = tuple(binding.material_id for binding in bindings)
229
+ if len(set(material_ids)) != len(material_ids):
230
+ issues.append(
231
+ ProcessIssue(
232
+ ProcessIssueCode.MATERIAL_REUSED,
233
+ "One material copy cannot fill two endpoint components.",
234
+ {"event": event.occurrence_id, "side": side},
235
+ )
236
+ )
237
+ for binding in bindings:
238
+ material = materials.get(binding.material_id)
239
+ if material is None:
240
+ issues.append(
241
+ ProcessIssue(
242
+ ProcessIssueCode.UNKNOWN_MATERIAL,
243
+ "A binding names an unknown material occurrence.",
244
+ {"event": event.occurrence_id, "material": binding.material_id},
245
+ )
246
+ )
247
+ continue
248
+ if binding.endpoint_nodes not in expected:
249
+ continue
250
+ component = _component_graph(endpoint, binding.endpoint_nodes)
251
+ try:
252
+ morphism = LLGMorphism(component, material.value, binding.mapping)
253
+ except LLGError as error:
254
+ issues.append(
255
+ ProcessIssue(
256
+ ProcessIssueCode.BINDING_NOT_ISOMORPHIC,
257
+ "A material binding is not a strict component isomorphism.",
258
+ {
259
+ "event": event.occurrence_id,
260
+ "material": binding.material_id,
261
+ "issues": tuple(issue.to_dict() for issue in error.issues),
262
+ },
263
+ )
264
+ )
265
+ else:
266
+ if not morphism.is_isomorphism:
267
+ issues.append(
268
+ ProcessIssue(
269
+ ProcessIssueCode.BINDING_NOT_ISOMORPHIC,
270
+ "A binding must cover the whole material occurrence.",
271
+ {
272
+ "event": event.occurrence_id,
273
+ "material": binding.material_id,
274
+ },
275
+ )
276
+ )
277
+ return issues
278
+
279
+ def _flow_graph(self) -> tuple[nx.DiGraph, list[ProcessIssue]]:
280
+ graph = nx.DiGraph()
281
+ graph.add_nodes_from(event.occurrence_id for event in self.events)
282
+ producers: dict[str, str] = {}
283
+ consumers: dict[str, str] = {}
284
+ issues: list[ProcessIssue] = []
285
+ for event in self.events:
286
+ for binding in event.outputs:
287
+ if binding.material_id in producers:
288
+ issues.append(
289
+ ProcessIssue(
290
+ ProcessIssueCode.MATERIAL_REUSED,
291
+ "A material occurrence has more than one producer.",
292
+ {"material": binding.material_id},
293
+ )
294
+ )
295
+ producers[binding.material_id] = event.occurrence_id
296
+ for binding in event.inputs:
297
+ if binding.material_id in consumers:
298
+ issues.append(
299
+ ProcessIssue(
300
+ ProcessIssueCode.MATERIAL_REUSED,
301
+ "A material occurrence has more than one consumer.",
302
+ {"material": binding.material_id},
303
+ )
304
+ )
305
+ consumers[binding.material_id] = event.occurrence_id
306
+ for material in set(producers) & set(consumers):
307
+ graph.add_edge(producers[material], consumers[material], material=material)
308
+ if not nx.is_directed_acyclic_graph(graph):
309
+ issues.append(
310
+ ProcessIssue(
311
+ ProcessIssueCode.CAUSAL_CYCLE,
312
+ "Occurrence causality must be a finite DAG; unfold repeated cycles.",
313
+ )
314
+ )
315
+ return graph, issues
316
+
317
+ def _independence_issues(self, graph: nx.DiGraph) -> list[ProcessIssue]:
318
+ if not nx.is_directed_acyclic_graph(graph):
319
+ return []
320
+ closure = nx.transitive_closure_dag(graph)
321
+ event_ids = set(graph)
322
+ incomparable = {
323
+ frozenset((left, right))
324
+ for left in event_ids
325
+ for right in event_ids
326
+ if repr(left) < repr(right)
327
+ and not closure.has_edge(left, right)
328
+ and not closure.has_edge(right, left)
329
+ }
330
+ witnesses = self.independence_by_pair
331
+ issues: list[ProcessIssue] = []
332
+ missing = incomparable - set(witnesses)
333
+ extra = set(witnesses) - incomparable
334
+ if missing:
335
+ issues.append(
336
+ ProcessIssue(
337
+ ProcessIssueCode.MISSING_INDEPENDENCE,
338
+ "Every incomparable event pair needs a commutation certificate.",
339
+ {"pairs": tuple(sorted(map(repr, missing)))},
340
+ )
341
+ )
342
+ if extra:
343
+ issues.append(
344
+ ProcessIssue(
345
+ ProcessIssueCode.EXTRA_INDEPENDENCE,
346
+ "A causal pair cannot also be declared independent.",
347
+ {"pairs": tuple(sorted(map(repr, extra)))},
348
+ )
349
+ )
350
+ events = self.event_by_id
351
+ for pair, witness in witnesses.items():
352
+ if len(pair) != 2 or not pair <= event_ids:
353
+ issues.append(
354
+ ProcessIssue(
355
+ ProcessIssueCode.UNKNOWN_EVENT,
356
+ "An independence witness names unknown or identical events.",
357
+ {"pair": repr(pair)},
358
+ )
359
+ )
360
+ continue
361
+ first = events[witness.first_event]
362
+ second = events[witness.second_event]
363
+ certificate = witness.certificate
364
+ first_rule = certificate.first_then_second[0].certificate.rule
365
+ second_rule = certificate.second_then_first[0].certificate.rule
366
+ if (
367
+ not certificate.replay()
368
+ or not _witness_rule_matches(first.rule, first_rule)
369
+ or not _witness_rule_matches(second.rule, second_rule)
370
+ ):
371
+ issues.append(
372
+ ProcessIssue(
373
+ ProcessIssueCode.INVALID_INDEPENDENCE,
374
+ "The independence certificate does not prove its event pair.",
375
+ {"pair": tuple(sorted(pair))},
376
+ )
377
+ )
378
+ return issues
379
+
380
+ def _validation_issues(self) -> list[ProcessIssue]:
381
+ issues: list[ProcessIssue] = []
382
+ material_ids = [item.occurrence_id for item in self.materials]
383
+ event_ids = [item.occurrence_id for item in self.events]
384
+ if len(set(material_ids)) != len(material_ids) or len(set(event_ids)) != len(
385
+ event_ids
386
+ ):
387
+ issues.append(
388
+ ProcessIssue(
389
+ ProcessIssueCode.DUPLICATE_ID,
390
+ "Material and event IDs must be unique within their namespaces.",
391
+ )
392
+ )
393
+ return issues
394
+ for event in self.events:
395
+ issues.extend(
396
+ self._binding_issues(event, event.inputs, event.rule.left, "input")
397
+ )
398
+ issues.extend(
399
+ self._binding_issues(event, event.outputs, event.rule.right, "output")
400
+ )
401
+ graph, flow_issues = self._flow_graph()
402
+ issues.extend(flow_issues)
403
+ issues.extend(self._independence_issues(graph))
404
+ return issues
405
+
406
+ @property
407
+ def causal_graph(self) -> nx.DiGraph:
408
+ graph, issues = self._flow_graph()
409
+ if issues:
410
+ raise ProcessError(*issues)
411
+ return graph
412
+
413
+ @property
414
+ def causal_pairs(self) -> tuple[tuple[str, str], ...]:
415
+ closure = nx.transitive_closure_dag(self.causal_graph)
416
+ return tuple(sorted(closure.edges, key=repr))
417
+
418
+ @property
419
+ def cover_pairs(self) -> tuple[tuple[str, str], ...]:
420
+ reduction = nx.transitive_reduction(self.causal_graph)
421
+ return tuple(sorted(reduction.edges, key=repr))
422
+
423
+ @property
424
+ def incomparable_pairs(self) -> tuple[frozenset[str], ...]:
425
+ causal = set(self.causal_pairs)
426
+ result = []
427
+ event_ids = sorted(self.event_by_id, key=repr)
428
+ for index, left in enumerate(event_ids):
429
+ for right in event_ids[index + 1 :]:
430
+ if (left, right) not in causal and (right, left) not in causal:
431
+ result.append(frozenset((left, right)))
432
+ return tuple(result)
433
+
434
+ def linear_extensions(
435
+ self, *, max_extensions: int = 10_000
436
+ ) -> tuple[tuple[str, ...], ...]:
437
+ """Enumerate all extensions or raise instead of returning a prefix."""
438
+ if max_extensions <= 0:
439
+ raise ValueError("max_extensions must be positive.")
440
+ result = []
441
+ for extension in nx.all_topological_sorts(self.causal_graph):
442
+ if len(result) >= max_extensions:
443
+ raise ProcessError(
444
+ ProcessIssue(
445
+ ProcessIssueCode.EXTENSION_LIMIT,
446
+ "Linear-extension enumeration exceeded its explicit bound.",
447
+ {"limit": max_extensions},
448
+ )
449
+ )
450
+ result.append(tuple(extension))
451
+ return tuple(sorted(result, key=repr))
452
+
453
+ def is_linear_extension(self, extension: Iterable[str]) -> bool:
454
+ order = tuple(extension)
455
+ if set(order) != set(self.event_by_id) or len(order) != len(self.events):
456
+ return False
457
+ position = {event: index for index, event in enumerate(order)}
458
+ return all(
459
+ position[left] < position[right] for left, right in self.causal_pairs
460
+ )
461
+
462
+ def extension_equivalence(
463
+ self, source: Iterable[str], target: Iterable[str]
464
+ ) -> LinearExtensionEquivalence:
465
+ """Construct adjacent-independent swaps between two valid extensions."""
466
+ source_order = tuple(source)
467
+ target_order = tuple(target)
468
+ if not self.is_linear_extension(source_order) or not self.is_linear_extension(
469
+ target_order
470
+ ):
471
+ raise ProcessError(
472
+ ProcessIssue(
473
+ ProcessIssueCode.INVALID_EXTENSION,
474
+ "Both orders must be linear extensions of this process.",
475
+ )
476
+ )
477
+ current = list(source_order)
478
+ swaps = []
479
+ for target_index, desired in enumerate(target_order):
480
+ position = current.index(desired)
481
+ while position > target_index:
482
+ left, right = current[position - 1], current[position]
483
+ if frozenset((left, right)) not in self.independence_by_pair:
484
+ raise ProcessError(
485
+ ProcessIssue(
486
+ ProcessIssueCode.INVALID_EXTENSION,
487
+ "A required adjacent swap lacks independence evidence.",
488
+ {"pair": (left, right)},
489
+ )
490
+ )
491
+ current[position - 1], current[position] = right, left
492
+ swaps.append((left, right))
493
+ position -= 1
494
+ witness = LinearExtensionEquivalence(source_order, target_order, tuple(swaps))
495
+ if not witness.replay(self):
496
+ raise ProcessError(
497
+ ProcessIssue(
498
+ ProcessIssueCode.INVALID_EXTENSION,
499
+ "The adjacent-swap equivalence does not replay.",
500
+ )
501
+ )
502
+ return witness
503
+
504
+
505
+ @dataclass(frozen=True)
506
+ class ProcessAlternative:
507
+ alternative_id: str
508
+ process: OccurrenceProcess
509
+
510
+
511
+ @dataclass(frozen=True)
512
+ class ChoiceWitness:
513
+ """Why two processes remain alternatives rather than one quotient class."""
514
+
515
+ first_alternative: str
516
+ second_alternative: str
517
+ kind: str
518
+ reason: str
519
+ conflicts: tuple[LawIssue, ...] = ()
520
+
521
+ def __post_init__(self) -> None:
522
+ if self.kind not in {"conflict", "material_assignment"}:
523
+ raise ValueError("Choice kind must be conflict or material_assignment.")
524
+ if self.kind == "conflict" and not self.conflicts:
525
+ raise ValueError("A conflict choice requires failed law premises.")
526
+
527
+
528
+ @dataclass(frozen=True)
529
+ class OccurrenceProcessFamily:
530
+ """A retained family of conflicting or materially ambiguous processes."""
531
+
532
+ alternatives: tuple[ProcessAlternative, ...]
533
+ choices: tuple[ChoiceWitness, ...]
534
+
535
+ def __post_init__(self) -> None:
536
+ alternatives = tuple(self.alternatives)
537
+ choices = tuple(self.choices)
538
+ object.__setattr__(self, "alternatives", alternatives)
539
+ object.__setattr__(self, "choices", choices)
540
+ identifiers = tuple(item.alternative_id for item in alternatives)
541
+ issues = []
542
+ if len(set(identifiers)) != len(identifiers):
543
+ issues.append(
544
+ ProcessIssue(
545
+ ProcessIssueCode.DUPLICATE_ID,
546
+ "Process alternative IDs must be unique.",
547
+ )
548
+ )
549
+ known = set(identifiers)
550
+ for choice in choices:
551
+ pair = {choice.first_alternative, choice.second_alternative}
552
+ if len(pair) != 2 or not pair <= known:
553
+ issues.append(
554
+ ProcessIssue(
555
+ ProcessIssueCode.INVALID_ALTERNATIVE,
556
+ "A choice must connect two known distinct alternatives.",
557
+ {"pair": tuple(sorted(pair))},
558
+ )
559
+ )
560
+ if issues:
561
+ raise ProcessError(*issues)
562
+
563
+
564
+ __all__ = [
565
+ "ChoiceWitness",
566
+ "IndependenceWitness",
567
+ "LinearExtensionEquivalence",
568
+ "MaterialBinding",
569
+ "MaterialOccurrence",
570
+ "OccurrenceProcess",
571
+ "OccurrenceProcessFamily",
572
+ "ProcessAlternative",
573
+ "ProcessError",
574
+ "ProcessIssue",
575
+ "ProcessIssueCode",
576
+ "RuleOccurrence",
577
+ ]
@@ -0,0 +1,66 @@
1
+ """Structural coexistence queries on explicitly bound occurrence histories."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Iterable
6
+
7
+ from .history import (
8
+ ConfigurationCut,
9
+ HistoryError,
10
+ HistoryIssue,
11
+ HistoryIssueCode,
12
+ OccurrenceMTG,
13
+ )
14
+
15
+
16
+ def cooccurrence_configuration(
17
+ history: OccurrenceMTG, material_ids: Iterable[str]
18
+ ) -> ConfigurationCut | None:
19
+ """Find the least structural ideal containing every requested material copy.
20
+
21
+ The producer downset is necessary for presence. If it contains a requested
22
+ copy's consumer, coexistence is impossible; otherwise that downset is the
23
+ least witnessing ideal. No schedules or ideals are enumerated. This tests
24
+ structural presence, not arbitrary guards, rates or physical feasibility.
25
+
26
+ :param history: Retained occurrence history, revalidated before querying.
27
+ :param material_ids: Known material occurrence IDs; duplicates are ignored.
28
+ :return: Least witnessing cut, or ``None`` if no structural cut exists.
29
+ :raises HistoryError: If the retained history or a material ID is invalid.
30
+ """
31
+ replay = history.replay()
32
+ if not replay.valid:
33
+ raise HistoryError(*replay.issues)
34
+ requested = frozenset(material_ids)
35
+ unknown = requested - set(history.material_by_id)
36
+ if unknown:
37
+ raise HistoryError(
38
+ HistoryIssue(
39
+ HistoryIssueCode.FLOW_LOOKUP,
40
+ "A coexistence query must name known material occurrence IDs.",
41
+ {"unknown": tuple(sorted(unknown, key=repr))},
42
+ )
43
+ )
44
+ producers = {
45
+ binding.material_id: event.occurrence_id
46
+ for event in history.events
47
+ for binding in event.outputs
48
+ }
49
+ consumers = {
50
+ binding.material_id: event.occurrence_id
51
+ for event in history.events
52
+ for binding in event.inputs
53
+ }
54
+ predecessors: dict[str, set[str]] = {}
55
+ for first, second in history.causal_pairs:
56
+ predecessors.setdefault(second, set()).add(first)
57
+ pending = [producers[copy] for copy in requested if copy in producers]
58
+ completed: set[str] = set()
59
+ while pending:
60
+ event = pending.pop()
61
+ if event not in completed:
62
+ completed.add(event)
63
+ pending.extend(predecessors.get(event, ()))
64
+ if any(consumers.get(copy) in completed for copy in requested):
65
+ return None
66
+ return history.configuration_cut(completed)