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,30 @@
1
+ """Result container and graph-label reconstruction for exact enumeration."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Dict, List
5
+
6
+
7
+ @dataclass
8
+ class EnumerationResult:
9
+ """Symmetry-distinct exact optima for a kernel."""
10
+
11
+ cost: float
12
+ mappings: List[List[int]]
13
+ sub_mappings: List[Dict[int, int]]
14
+ results: List[dict]
15
+ proven_optimal: bool = True
16
+ enumeration_complete: bool = True
17
+
18
+
19
+ def mapping_to_lgp(lgp, mapping):
20
+ """Return a resolved graph pair whose labels encode ``mapping``."""
21
+ out = [lgp[0].copy(), lgp[1].copy()]
22
+ labels0 = list(range(1, len(mapping) + 1))
23
+ labels1 = [0] * len(mapping)
24
+ for i, p in enumerate(mapping):
25
+ labels1[p] = i + 1
26
+ out[0].labels = labels0
27
+ out[1].labels = labels1
28
+ out[0].build_label2idxs()
29
+ out[1].build_label2idxs()
30
+ return out
@@ -0,0 +1,256 @@
1
+ """
2
+ Reduced uncertainty subproblems derived from supplied SLAP mapping results.
3
+
4
+ The supplied heuristic mappings determine which atom assignments agree and
5
+ which vary. ``extract_kernel`` holds agreeing assignments fixed and builds
6
+ candidate domains for the remaining atoms, with fallback label-class domains
7
+ and closure of small same-element uncertain blocks where applicable. Agreement
8
+ among supplied SLAP results is not a proof that an assignment is forced across
9
+ all global optima.
10
+
11
+ ``Kernel`` is this reduced problem, not a mapping algorithm. Branch-and-bound
12
+ or optional MILP solves its remaining assignments exactly while respecting the
13
+ fixed assignments and candidate domains. ``apply_kernel_solution`` combines
14
+ the subsolution with the fixed part. An exact kernel solution is optimal within
15
+ the supplied restrictions; a full-space global optimum requires a full-space
16
+ search or an independent global proof. WL colour agreement alone is not used
17
+ as a certainty test because a reaction can change an atom's environment.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ from dataclasses import dataclass
23
+ from typing import Dict, List, Optional, Set
24
+
25
+ from ...slap.lap import recover_mapping
26
+
27
+ _MAX_CLOSED_ELEMENT_BLOCK = 4
28
+
29
+
30
+ @dataclass
31
+ class Kernel:
32
+ """Reduced atom-assignment problem with fixed assignments and candidate domains.
33
+
34
+ :ivar r_idx, p_idx: Original reactant/product atom indices that remain uncertain.
35
+ :vartype r_idx, p_idx: list[int]
36
+ :ivar r_colors, p_colors: Atom type (atomic number) of each kernel atom; reactant atom
37
+ ``r_idx[a]`` may map to product atom ``p_idx[b]`` only if
38
+ ``r_colors[a] == p_colors[b]``.
39
+ :vartype r_colors, p_colors: list[int]
40
+ :ivar fixed_mapping: Assignments held fixed: original reactant index -> product index.
41
+ :vartype fixed_mapping: dict[int, int]
42
+ :ivar candidate_images: Allowed product atom indices for each uncertain atom in ``r_idx``.
43
+ Extraction derives these from supplied SLAP results, with fallback
44
+ label-class domains and closure of small uncertain element blocks.
45
+ The exact kernel solver stays inside the supplied candidate sets.
46
+ :vartype candidate_images: list[list[int]]
47
+ :ivar lgp: The original reactant/product graph pair (for adjacency look-ups).
48
+ :vartype lgp: list
49
+ :ivar binary: Whether bond orders are binarised.
50
+ :vartype binary: bool
51
+ """
52
+
53
+ r_idx: List[int]
54
+ p_idx: List[int]
55
+ r_colors: List[int]
56
+ p_colors: List[int]
57
+ fixed_mapping: Dict[int, int]
58
+ lgp: list
59
+ binary: bool = False
60
+ candidate_images: Optional[List[List[int]]] = None
61
+
62
+ @property
63
+ def size(self) -> int:
64
+ """Number of uncertain reactant atoms (== uncertain product atoms)."""
65
+ return len(self.r_idx)
66
+
67
+ @property
68
+ def is_trivial(self) -> bool:
69
+ """True when the supplied kernel has no uncertain atoms to optimize."""
70
+ return self.size == 0
71
+
72
+
73
+ def _atomic_numbers(lg):
74
+ nums = lg.props.get("atomic numbers")
75
+ if nums is None:
76
+ nums = list(lg.labels)
77
+ return list(nums)
78
+
79
+
80
+ def extract_kernel(results, lgp, binary=False):
81
+ """Extract a restricted uncertainty problem from supplied SLAP results.
82
+
83
+ Agreement among the supplied heuristic mappings defines fixed assignments;
84
+ variation defines uncertain domains. These restrictions are local to the
85
+ supplied results and do not establish certainty over every global optimum.
86
+
87
+ :param results: Best observed results from :meth:`GraphMatcher.get_maps`; each must carry a
88
+ fully resolved ``"lgp"`` graph pair.
89
+ :type results: list[dict]
90
+ :param lgp: The original reactant/product graph pair (equal atom counts).
91
+ :type lgp: list[LabeledGraph]
92
+ :param binary: Whether bond orders are binarised (recorded on the kernel).
93
+ :type binary: bool, optional
94
+
95
+ :rtype: Kernel
96
+ """
97
+ n = len(lgp[0].labels)
98
+ mappings = [recover_mapping(r["lgp"]) for r in results]
99
+ if not mappings:
100
+ mappings = [list(range(n))]
101
+
102
+ images = [set(m[i] for m in mappings) for i in range(n)]
103
+ elements_r = _atomic_numbers(lgp[0])
104
+ elements_p = _atomic_numbers(lgp[1])
105
+ fixed_mapping, r_idx, p_idx, candidate_images = _compact_kernel_parts(images)
106
+
107
+ if not _kernel_parts_are_valid(fixed_mapping, r_idx, p_idx, candidate_images):
108
+ images = _label_class_candidate_images(results, lgp, elements_r, elements_p)
109
+ fixed_mapping, r_idx, p_idx, candidate_images = _propagated_kernel_parts(images)
110
+
111
+ candidate_images = _close_small_element_blocks(
112
+ r_idx,
113
+ p_idx,
114
+ candidate_images,
115
+ elements_r,
116
+ elements_p,
117
+ )
118
+
119
+ return Kernel(
120
+ r_idx=r_idx,
121
+ p_idx=p_idx,
122
+ r_colors=[elements_r[i] for i in r_idx],
123
+ p_colors=[elements_p[p] for p in p_idx],
124
+ fixed_mapping=fixed_mapping,
125
+ lgp=lgp,
126
+ binary=binary,
127
+ candidate_images=candidate_images,
128
+ )
129
+
130
+
131
+ def _compact_kernel_parts(images):
132
+ fixed_mapping = {}
133
+ r_idx = []
134
+ for i, candidates in enumerate(images):
135
+ if len(candidates) == 1:
136
+ fixed_mapping[i] = next(iter(candidates))
137
+ else:
138
+ r_idx.append(i)
139
+
140
+ p_set = set()
141
+ for i in r_idx:
142
+ p_set.update(images[i])
143
+ p_idx = sorted(p_set)
144
+ candidate_images = [sorted(images[i]) for i in r_idx]
145
+ return fixed_mapping, r_idx, p_idx, candidate_images
146
+
147
+
148
+ def _kernel_parts_are_valid(fixed_mapping, r_idx, p_idx, candidate_images):
149
+ if len(p_idx) != len(r_idx):
150
+ return False
151
+ p_set = set(p_idx)
152
+ return all(
153
+ candidates and set(candidates) <= p_set for candidates in candidate_images
154
+ )
155
+
156
+
157
+ def _label_class_candidate_images(results, lgp, elements_r, elements_p):
158
+ n = len(lgp[0].labels)
159
+ images: List[Set[int]] = [set() for _ in range(n)]
160
+ for result in results:
161
+ left, right = result["lgp"]
162
+ fallback = recover_mapping(result["lgp"])
163
+ for i, label in enumerate(left.labels):
164
+ for p in right.label2idxs.get(label, []):
165
+ if 0 <= p < n and elements_r[i] == elements_p[p]:
166
+ images[i].add(p)
167
+ p = fallback[i] if i < len(fallback) else -1
168
+ if 0 <= p < n and elements_r[i] == elements_p[p]:
169
+ images[i].add(p)
170
+ if not results:
171
+ images = [{i} for i in range(n)]
172
+ return images
173
+
174
+
175
+ def _propagated_kernel_parts(images):
176
+ images = [set(candidates) for candidates in images]
177
+ fixed_mapping = {}
178
+ unresolved = set(range(len(images)))
179
+ used_products = set()
180
+ changed = True
181
+ while changed:
182
+ changed = False
183
+ for i in list(unresolved):
184
+ images[i].difference_update(used_products)
185
+ if len(images[i]) != 1:
186
+ continue
187
+ p = next(iter(images[i]))
188
+ fixed_mapping[i] = p
189
+ used_products.add(p)
190
+ unresolved.remove(i)
191
+ for j in unresolved:
192
+ images[j].discard(p)
193
+ changed = True
194
+ break
195
+
196
+ r_idx = sorted(unresolved)
197
+ p_set = set()
198
+ for i in r_idx:
199
+ p_set.update(images[i])
200
+ p_idx = sorted(p_set)
201
+ candidate_images = [sorted(images[i]) for i in r_idx]
202
+ return fixed_mapping, r_idx, p_idx, candidate_images
203
+
204
+
205
+ def _close_small_element_blocks(r_idx, p_idx, candidate_images, elements_r, elements_p):
206
+ """Let exact mode fully permute small same-element uncertain blocks."""
207
+ if not candidate_images:
208
+ return candidate_images
209
+
210
+ out = [sorted(set(candidates)) for candidates in candidate_images]
211
+ positions_by_element = {}
212
+ products_by_element = {}
213
+ for a, i in enumerate(r_idx):
214
+ positions_by_element.setdefault(elements_r[i], []).append(a)
215
+ for p in p_idx:
216
+ products_by_element.setdefault(elements_p[p], []).append(p)
217
+
218
+ for element, positions in positions_by_element.items():
219
+ pool = sorted(products_by_element.get(element, []))
220
+ if (
221
+ not pool
222
+ or len(pool) != len(positions)
223
+ or len(pool) > _MAX_CLOSED_ELEMENT_BLOCK
224
+ ):
225
+ continue
226
+ pool_set = set(pool)
227
+ if any(not set(out[a]) <= pool_set for a in positions):
228
+ continue
229
+ for a in positions:
230
+ out[a] = pool
231
+ return out
232
+
233
+
234
+ def apply_kernel_solution(kernel, sub_mapping):
235
+ """Combine a kernel sub-mapping with its fixed assignments into a full mapping.
236
+
237
+ :param kernel:
238
+ :type kernel: Kernel
239
+ :param sub_mapping: Mapping over kernel positions: either a dict ``a -> b`` (positions into
240
+ ``kernel.r_idx`` / ``kernel.p_idx``) or a sequence ``sub_mapping[a] = b``.
241
+ :type sub_mapping: dict[int, int] or sequence[int]
242
+
243
+ :return: ``mapping[i] = p`` over original atom indices.
244
+ :rtype: list[int]
245
+ """
246
+ n = len(kernel.lgp[0].labels)
247
+ mapping = [-1] * n
248
+ for i, p in kernel.fixed_mapping.items():
249
+ mapping[i] = p
250
+ if isinstance(sub_mapping, dict):
251
+ items = sub_mapping.items()
252
+ else:
253
+ items = enumerate(sub_mapping)
254
+ for a, b in items:
255
+ mapping[kernel.r_idx[a]] = kernel.p_idx[b]
256
+ return mapping
@@ -0,0 +1,194 @@
1
+ """
2
+ Exact MILP/QAP formulation of the kernel mapping problem (PuLP + CBC backend).
3
+
4
+ The chemical-distance objective is a quadratic assignment problem (QAP). Over the
5
+ small uncertainty-region kernel (:mod:`mapper.exact.kernel`) we write it as a
6
+ Koopmans-Beckmann QAP and linearise it to a mixed-integer linear program:
7
+
8
+ * binary ``x[a,b] = 1`` iff kernel reactant atom ``a`` maps to kernel product
9
+ atom ``b`` (created only for element-compatible pairs);
10
+ * assignment constraints ``sum_b x[a,b] = 1`` and ``sum_a x[a,b] = 1``;
11
+ * products ``y[a,b,a',b'] = x[a,b] * x[a',b']`` linearised by
12
+ ``y <= x[a,b]``, ``y <= x[a',b']``, ``y >= x[a,b] + x[a',b'] - 1``.
13
+
14
+ Writing the chemical distance ``1/2 * sum_{i,j} |A_R[i,j] - A_P[m(i),m(j)]|`` and
15
+ splitting atoms into certain/kernel, the certain-certain part is a constant, the
16
+ kernel-certain part becomes linear coefficients on ``x``, and the kernel-kernel
17
+ part becomes coefficients on ``y`` (the factor 1/2 and the two ordered directions
18
+ of each undirected edge cancel). CBC solves the MILP to proven optimality, so the
19
+ returned cost certifies the kernel optimum (see :mod:`mapper.exact.certificate`).
20
+
21
+ The solver is isolated behind :func:`solve_qap`; :func:`solve_kernel_milp` adapts
22
+ a :class:`~mapper.exact.kernel.Kernel` to it and returns the same
23
+ :class:`~mapper.exact.branching.KernelSolution` type as the orbital-branching
24
+ solver, so callers can use either interchangeably.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from typing import Dict, Tuple
30
+
31
+ import warnings
32
+
33
+ from ...slap.lap import _adjacency_and_elements
34
+ from .branching import KernelSolution
35
+
36
+ try:
37
+ import pulp
38
+
39
+ HAS_PULP = True
40
+ except Exception: # pragma: no cover - exercised only without pulp installed
41
+ HAS_PULP = False
42
+
43
+
44
+ def _cbc_solver():
45
+ """Return a silent CBC solver, preferring the non-deprecated entry point."""
46
+ with warnings.catch_warnings():
47
+ warnings.simplefilter("ignore", DeprecationWarning)
48
+ try:
49
+ solver = pulp.COIN_CMD(msg=False)
50
+ if solver.available():
51
+ return solver
52
+ except Exception:
53
+ pass
54
+ return pulp.PULP_CBC_CMD(msg=False)
55
+
56
+
57
+ def solve_qap(lin, quad, n, compatible, const=0.0):
58
+ """Solve a linearised QAP assignment with PuLP/CBC.
59
+
60
+ :param lin: Linear cost ``lin[a, b]`` for assigning ``a -> b``.
61
+ :type lin: dict[(int, int), float]
62
+ :param quad: Quadratic cost ``quad[a, b, a', b']`` (defined for ``a < a'``) added when
63
+ both ``a -> b`` and ``a' -> b'`` hold.
64
+ :type quad: dict[(int, int, int, int), float]
65
+ :param n: Number of reactant positions (== product positions).
66
+ :type n: int
67
+ :param compatible: Allowed ``(a, b)`` assignments (element-compatible pairs).
68
+ :type compatible: set[(int, int)]
69
+ :param const: Constant added to the objective.
70
+ :type const: float
71
+
72
+ :return: **(cost, mapping, proven)** -- Optimal cost, the assignment ``a -> b``, and whether CBC proved
73
+ optimality.
74
+ :rtype: (float, dict[int, int], bool)
75
+ """
76
+ if not HAS_PULP:
77
+ raise ImportError("pulp is required for the MILP/QAP solver")
78
+
79
+ prob = pulp.LpProblem("kernel_qap", pulp.LpMinimize)
80
+
81
+ # PuLP 3.x emits 4.0-migration DeprecationWarnings from the supported
82
+ # LpVariable API; silence that noise around variable construction.
83
+ with warnings.catch_warnings():
84
+ warnings.simplefilter("ignore", DeprecationWarning)
85
+ x = {
86
+ (a, b): pulp.LpVariable(f"x_{a}_{b}", cat="Binary") for (a, b) in compatible
87
+ }
88
+ y = {}
89
+ for (a, b, a2, b2), c in quad.items():
90
+ if (a, b) in x and (a2, b2) in x:
91
+ y[(a, b, a2, b2)] = pulp.LpVariable(
92
+ f"y_{a}_{b}_{a2}_{b2}", lowBound=0, upBound=1, cat="Continuous"
93
+ )
94
+
95
+ # Objective.
96
+ obj = [lin[(a, b)] * x[(a, b)] for (a, b) in x if lin.get((a, b))]
97
+ obj += [quad[k] * y[k] for k in y if quad.get(k)]
98
+ prob += pulp.lpSum(obj) + const
99
+
100
+ # Assignment constraints.
101
+ for a in range(n):
102
+ prob += pulp.lpSum(x[(a, b)] for b in range(n) if (a, b) in x) == 1
103
+ for b in range(n):
104
+ prob += pulp.lpSum(x[(a, b)] for a in range(n) if (a, b) in x) == 1
105
+
106
+ # McCormick linearisation of the products.
107
+ for (a, b, a2, b2), yv in y.items():
108
+ prob += yv <= x[(a, b)]
109
+ prob += yv <= x[(a2, b2)]
110
+ prob += yv >= x[(a, b)] + x[(a2, b2)] - 1
111
+
112
+ prob.solve(_cbc_solver())
113
+
114
+ proven = pulp.LpStatus[prob.status] == "Optimal"
115
+ mapping = {}
116
+ for (a, b), var in x.items():
117
+ if var.value() is not None and var.value() > 0.5:
118
+ mapping[a] = b
119
+ cost = pulp.value(prob.objective)
120
+ if cost is None and proven and not obj:
121
+ cost = float(const)
122
+ if cost is None:
123
+ raise RuntimeError(
124
+ "MILP solver returned no objective value — problem may be infeasible. "
125
+ "Verify that element-compatible assignments exist for all kernel atoms."
126
+ )
127
+ return float(cost), mapping, proven
128
+
129
+
130
+ def solve_kernel_milp(kernel): # noqa: C901
131
+ """Solve a :class:`~mapper.exact.kernel.Kernel` exactly via MILP/QAP.
132
+
133
+ :return: With ``proven_optimal`` reflecting CBC's status.
134
+ :rtype: KernelSolution
135
+ """
136
+ lgp = kernel.lgp
137
+ binary = kernel.binary
138
+ A, _ = _adjacency_and_elements(lgp[0], binary)
139
+ B, _ = _adjacency_and_elements(lgp[1], binary)
140
+ N = A.shape[0]
141
+ n = kernel.size
142
+
143
+ base_map = [-1] * N
144
+ for i, p in kernel.fixed_mapping.items():
145
+ base_map[i] = p
146
+ certain = [i for i in range(N) if base_map[i] != -1]
147
+
148
+ # Certain-certain constant (0.5 * ordered sum).
149
+ const = 0.0
150
+ for i in certain:
151
+ for j in certain:
152
+ const += abs(A[i, j] - B[base_map[i], base_map[j]])
153
+ const *= 0.5
154
+
155
+ if n == 0:
156
+ return KernelSolution(cost=const, sub_mappings=[{}], proven_optimal=True)
157
+
158
+ compatible = set()
159
+ lin: Dict[Tuple[int, int], float] = {}
160
+ for a in range(n):
161
+ i = kernel.r_idx[a]
162
+ for b in range(n):
163
+ if kernel.r_colors[a] != kernel.p_colors[b]:
164
+ continue
165
+ p = kernel.p_idx[b]
166
+ if (
167
+ kernel.candidate_images is not None
168
+ and p not in kernel.candidate_images[a]
169
+ ):
170
+ continue
171
+ compatible.add((a, b))
172
+ # Kernel-certain: 0.5 * (ordered i,j + ordered j,i) = sum_j |A-B|.
173
+ lin[(a, b)] = sum(abs(A[i, j] - B[p, base_map[j]]) for j in certain)
174
+
175
+ quad: Dict[Tuple[int, int, int, int], float] = {}
176
+ for a in range(n):
177
+ i = kernel.r_idx[a]
178
+ for a2 in range(a + 1, n):
179
+ i2 = kernel.r_idx[a2]
180
+ for b in range(n):
181
+ if (a, b) not in compatible:
182
+ continue
183
+ p = kernel.p_idx[b]
184
+ for b2 in range(n):
185
+ if (a2, b2) not in compatible or b2 == b:
186
+ continue
187
+ p2 = kernel.p_idx[b2]
188
+ # 0.5 * (ordered (i,i') + (i',i)) = |A_R[i,i'] - A_P[p,p']|.
189
+ c = abs(A[i, i2] - B[p, p2])
190
+ if c:
191
+ quad[(a, b, a2, b2)] = c
192
+
193
+ cost, mapping, proven = solve_qap(lin, quad, n, compatible, const=const)
194
+ return KernelSolution(cost=cost, sub_mappings=[mapping], proven_optimal=proven)
@@ -0,0 +1 @@
1
+ """Optional C++ search, explicit compilation, canonicalization, and orbit scheduling."""
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env python3
2
+ """Explicitly build the optional native solver into an immutable, hashed artifact."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import hashlib
8
+ import json
9
+ import os
10
+ import platform
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ import tempfile
15
+ from pathlib import Path
16
+
17
+
18
+ def _verify_library(library):
19
+ """Check loadability and ABI in a child so Windows can release the DLL file."""
20
+ script = (
21
+ "import ctypes, sys; library = ctypes.CDLL(sys.argv[1]); "
22
+ "abi = library.synkit_distance_abi(); "
23
+ "assert abi == 3, f'unsupported native ABI: {abi}'"
24
+ )
25
+ result = subprocess.run(
26
+ [sys.executable, "-c", script, str(library)], capture_output=True, text=True
27
+ )
28
+ if result.returncode:
29
+ raise RuntimeError(f"Native artifact cannot be loaded: {library}\n{result.stderr}")
30
+
31
+
32
+ def build_native(output_dir, compiler="g++", *, native_arch=False):
33
+ """Build and verify a source/compiler-bound optional C++ solver artifact."""
34
+ source = Path(__file__).with_name("native_distance.cpp")
35
+ compiler_path = shutil.which(compiler)
36
+ if compiler_path is None:
37
+ raise RuntimeError(f"C++17 compiler unavailable: {compiler}")
38
+ version = subprocess.run(
39
+ [compiler_path, "--version"], check=True, capture_output=True, text=True
40
+ ).stdout
41
+ flags = ["-std=c++17", "-O3", "-Wall", "-Wextra"]
42
+ if sys.platform == "win32":
43
+ # MinGW runtime DLLs on PATH are not searched by modern ctypes loaders.
44
+ # Link their code into the artifact and explicitly export the C ABI.
45
+ flags += ["-shared", "-static", "-Wl,--export-all-symbols"]
46
+ suffix = ".dll"
47
+ elif sys.platform == "darwin":
48
+ flags += ["-fPIC", "-dynamiclib"]
49
+ suffix = ".dylib"
50
+ else:
51
+ flags += ["-fPIC", "-shared"]
52
+ suffix = ".so"
53
+ target_options = None
54
+ if native_arch:
55
+ flags += ["-march=native", "-mtune=native"]
56
+ # Include resolved CPU options in the cache key: the same source and
57
+ # literal -march=native flags can generate different code on another host.
58
+ target_options = subprocess.run(
59
+ [
60
+ compiler_path,
61
+ *flags,
62
+ "-Q",
63
+ "--help=target",
64
+ "-x",
65
+ "c++",
66
+ "-c",
67
+ "-",
68
+ "-o",
69
+ os.devnull,
70
+ ],
71
+ check=True,
72
+ capture_output=True,
73
+ text=True,
74
+ input="",
75
+ ).stdout
76
+ provenance = {
77
+ "source_sha256": hashlib.sha256(source.read_bytes()).hexdigest(),
78
+ "compiler": compiler_path,
79
+ "compiler_version": version,
80
+ "flags": flags,
81
+ "platform": sys.platform,
82
+ "machine": platform.machine(),
83
+ }
84
+ if native_arch:
85
+ provenance["resolved_target_options"] = target_options
86
+ provenance["portability"] = (
87
+ "requires the recorded host CPU instruction features"
88
+ )
89
+ build_key = hashlib.sha256(
90
+ json.dumps(provenance, sort_keys=True).encode()
91
+ ).hexdigest()
92
+ output_dir = Path(output_dir).resolve()
93
+ output_dir.mkdir(parents=True, exist_ok=True)
94
+ library = output_dir / f"libsynkit_distance_{build_key[:20]}{suffix}"
95
+ manifest = library.with_suffix(".json")
96
+ if library.exists():
97
+ if not manifest.exists():
98
+ raise RuntimeError(f"Existing library lacks build provenance: {library}")
99
+ recorded = json.loads(manifest.read_text())
100
+ if (
101
+ recorded["build_key"] != build_key
102
+ or recorded["library_sha256"]
103
+ != hashlib.sha256(library.read_bytes()).hexdigest()
104
+ ):
105
+ raise RuntimeError(
106
+ f"Existing native artifact failed integrity verification: {library}"
107
+ )
108
+ _verify_library(library)
109
+ return library
110
+ # A distinct path avoids overwriting code already mapped into another process.
111
+ with tempfile.TemporaryDirectory(
112
+ prefix="synkit-build-", dir=output_dir
113
+ ) as temporary:
114
+ candidate = Path(temporary) / f"kernel{suffix}"
115
+ result = subprocess.run(
116
+ [compiler_path, *flags, str(source), "-o", str(candidate)],
117
+ check=True,
118
+ capture_output=True,
119
+ text=True,
120
+ )
121
+ _verify_library(candidate)
122
+ provenance.update(
123
+ build_key=build_key,
124
+ library=str(library),
125
+ library_sha256=hashlib.sha256(candidate.read_bytes()).hexdigest(),
126
+ compiler_stderr=result.stderr,
127
+ )
128
+ # Hard-link installation refuses to replace any concurrently installed binary.
129
+ try:
130
+ os.link(candidate, library)
131
+ except FileExistsError:
132
+ raise RuntimeError(
133
+ f"Concurrent build installed {library}; rerun to verify it"
134
+ )
135
+ manifest.write_text(json.dumps(provenance, indent=2) + "\n")
136
+ return library
137
+
138
+
139
+ def main():
140
+ """Build the optional C++ solver from an installed SynKit package."""
141
+ parser = argparse.ArgumentParser(description=__doc__)
142
+ parser.add_argument("--output-dir", type=Path, default=Path(tempfile.gettempdir()) / "synkit-native")
143
+ parser.add_argument("--compiler", default="g++")
144
+ parser.add_argument(
145
+ "--native-arch",
146
+ action="store_true",
147
+ help="Opt in to host CPU instructions; resulting library is host-specific.",
148
+ )
149
+ args = parser.parse_args()
150
+ print(build_native(args.output_dir, args.compiler, native_arch=args.native_arch))
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()