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,611 @@
1
+ #include "synkit_mapper/reaction/reaction.h"
2
+ #include "synkit_mapper/core/runtime.h"
3
+ #include "synkit_mapper/assignment/assignment.h"
4
+ #include "synkit_mapper/hydrogen/hydrogen.h"
5
+ #include "synkit_mapper/symmetry/classification.h"
6
+ #include <GraphMol/MolOps.h>
7
+ #include <GraphMol/PeriodicTable.h>
8
+ #include <GraphMol/SmilesParse/SmilesParse.h>
9
+ #include <GraphMol/SmilesParse/SmilesWrite.h>
10
+ #include <RDGeneral/versions.h>
11
+ #include <cmath>
12
+ #include <cstring>
13
+ #include <iomanip>
14
+ #include <map>
15
+ #include <memory>
16
+ #include <set>
17
+ #include <sstream>
18
+ #include <string>
19
+
20
+ namespace {
21
+ using namespace synkit_mapper;
22
+ using Identity = std::pair<int, int>;
23
+ thread_local std::string last_error;
24
+
25
+ std::string quote(const std::string &s) {
26
+ std::ostringstream out;
27
+ out << '"';
28
+ for (unsigned char c : s) {
29
+ if (c == '"' || c == '\\')
30
+ out << '\\' << c;
31
+ else if (c < 32)
32
+ out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(c)
33
+ << std::dec;
34
+ else
35
+ out << c;
36
+ }
37
+ out << '"';
38
+ return out.str();
39
+ }
40
+
41
+ // Exact _encode_color-compatible token trees. Every primitive needed here is
42
+ // encoded as a string (numeric attributes are wrapped by the legacy _typed).
43
+ struct Token {
44
+ bool sequence = false;
45
+ std::string text;
46
+ std::vector<Token> items;
47
+ bool operator<(const Token &other) const {
48
+ if (sequence != other.sequence)
49
+ return sequence < other.sequence;
50
+ return sequence ? items < other.items : text < other.text;
51
+ }
52
+ std::string json() const {
53
+ if (!sequence)
54
+ return quote(text);
55
+ std::string result = "[";
56
+ for (size_t i = 0; i < items.size(); ++i)
57
+ result += (i ? "," : "") + items[i].json();
58
+ return result + "]";
59
+ }
60
+ };
61
+ Token literal(const std::string &s) { return {false, s, {}}; }
62
+ Token tuple(std::vector<Token> values) {
63
+ values.insert(values.begin(), literal("tuple"));
64
+ return {true, "", std::move(values)};
65
+ }
66
+ Token string_token(const std::string &s) {
67
+ return {true, "", {literal("str"), literal(s)}};
68
+ }
69
+ Token typed(const std::string &type, const std::string &representation) {
70
+ return tuple({string_token("builtins." + type), string_token(representation)});
71
+ }
72
+ Token weight_token(int64_t doubled) {
73
+ std::string value = "0x0.0p+0";
74
+ if (doubled) {
75
+ double weight = doubled / 2.0;
76
+ int exponent;
77
+ double fraction = std::frexp(weight, &exponent) * 2 - 1;
78
+ auto mantissa = static_cast<uint64_t>(std::llround(std::ldexp(fraction, 52)));
79
+ std::ostringstream stream;
80
+ stream << "0x1." << std::hex << std::setw(13) << std::setfill('0') << mantissa
81
+ << "p" << std::dec << (exponent - 1 >= 0 ? "+" : "") << exponent - 1;
82
+ value = stream.str();
83
+ }
84
+ return {true, "", {literal("float"), literal(value)}};
85
+ }
86
+
87
+ struct Endpoint {
88
+ std::unique_ptr<RDKit::RWMol> molecule;
89
+ std::vector<Identity> labels;
90
+ std::vector<int32_t> charges, radicals, hcounts, dummies, stereo_anchors;
91
+ std::vector<std::string> stereo;
92
+ std::vector<int64_t> weights;
93
+ std::vector<std::string> bond_stereo;
94
+ std::vector<int32_t> types, node_colors, edge_colors, atomic_numbers, isotopes;
95
+ };
96
+ std::string stereo_name(RDKit::Bond::BondStereo value) {
97
+ switch (value) {
98
+ case RDKit::Bond::STEREONONE: return "STEREONONE";
99
+ case RDKit::Bond::STEREOANY: return "STEREOANY";
100
+ case RDKit::Bond::STEREOZ: return "STEREOZ";
101
+ case RDKit::Bond::STEREOE: return "STEREOE";
102
+ case RDKit::Bond::STEREOCIS: return "STEREOCIS";
103
+ case RDKit::Bond::STEREOTRANS: return "STEREOTRANS";
104
+ default: throw std::invalid_argument("Unsupported RDKit stereobond state");
105
+ }
106
+ }
107
+ int nonbonding(const RDKit::Atom &atom) {
108
+ double bond_sum = 0;
109
+ if (atom.getIsAromatic())
110
+ bond_sum = atom.getTotalValence() - atom.getTotalNumHs(false);
111
+ else
112
+ for (auto bond : atom.getOwningMol().atomBonds(&atom))
113
+ bond_sum += bond->getBondTypeAsDouble();
114
+ double available = RDKit::PeriodicTable::getTable()->getNouterElecs(atom.getAtomicNum())
115
+ - atom.getFormalCharge() - atom.getNumRadicalElectrons()
116
+ - bond_sum - atom.getTotalNumHs(false);
117
+ int lone_pairs = std::max(0, static_cast<int>(std::floor((available + 1e-8) / 2)));
118
+ return 2 * lone_pairs + atom.getNumRadicalElectrons();
119
+ }
120
+ Endpoint parse(const std::string &smiles, bool explicit_hydrogens, const Budget &budget) {
121
+ budget.check();
122
+ RDKit::SmilesParserParams params;
123
+ params.removeHs = false;
124
+ Endpoint result;
125
+ result.molecule.reset(RDKit::SmilesToMol(smiles, params));
126
+ if (!result.molecule)
127
+ throw std::invalid_argument("Could not parse reaction endpoint");
128
+ budget.check();
129
+ for (auto atom : result.molecule->atoms()) {
130
+ if (!atom->getAtomicNum())
131
+ throw std::invalid_argument("Wildcard atoms require a concrete element");
132
+ atom->setAtomMapNum(0);
133
+ }
134
+ budget.check();
135
+ if (explicit_hydrogens)
136
+ RDKit::MolOps::addHs(*result.molecule);
137
+ else {
138
+ RDKit::MolOps::removeHs(*result.molecule);
139
+ for (auto atom : result.molecule->atoms())
140
+ if (atom->getAtomicNum() == 1)
141
+ throw std::invalid_argument("Free or isotopic H requires explicit hydrogens");
142
+ }
143
+ RDKit::MolOps::assignStereochemistry(*result.molecule, true, true);
144
+ budget.check();
145
+ for (auto atom : result.molecule->atoms()) {
146
+ result.labels.emplace_back(atom->getAtomicNum(), atom->getIsotope());
147
+ result.charges.push_back(atom->getFormalCharge());
148
+ result.radicals.push_back(atom->getNumRadicalElectrons());
149
+ result.hcounts.push_back(atom->getTotalNumHs());
150
+ result.dummies.push_back(0);
151
+ result.stereo_anchors.push_back(0);
152
+ std::string cip;
153
+ atom->getPropIfPresent("_CIPCode", cip);
154
+ result.stereo.push_back(cip);
155
+ }
156
+ return result;
157
+ }
158
+ void append_node(Endpoint &e, Identity type, bool dummy) {
159
+ e.labels.push_back(type);
160
+ e.charges.push_back(0); e.radicals.push_back(0); e.hcounts.push_back(0);
161
+ e.dummies.push_back(dummy); e.stereo.push_back(""); e.stereo_anchors.push_back(0);
162
+ }
163
+ void prepare(Endpoint &left, Endpoint &right, const SynkitReactionOptions &o,
164
+ const Budget &budget) {
165
+ std::map<Identity, int> counts[2], universe;
166
+ Endpoint *sides[] = {&left, &right};
167
+ for (int side = 0; side < 2; ++side)
168
+ for (auto type : sides[side]->labels)
169
+ ++counts[side][type];
170
+ if (!o.dummy_balance && counts[0] != counts[1])
171
+ throw std::invalid_argument("Unbalanced atom inventories; enable dummy balance explicitly");
172
+ for (auto &count : counts)
173
+ for (auto [type, n] : count)
174
+ universe[type] = std::max(universe[type], n);
175
+ std::map<Identity, int> hard_ids;
176
+ std::map<std::string, int> node_ids, edge_ids;
177
+ for (int side = 0; side < 2; ++side) {
178
+ budget.check();
179
+ auto &e = *sides[side];
180
+ for (auto [type, maximum] : universe)
181
+ for (int i = counts[side][type]; i < maximum; ++i)
182
+ append_node(e, type, true);
183
+ if (o.lewis_objective)
184
+ append_node(e, {0, -1}, true);
185
+ int n = static_cast<int>(e.labels.size());
186
+ if (n < 1 || n > synkit_mapper_classification_max_vertices())
187
+ throw std::invalid_argument("Native reaction classification capacity exceeded");
188
+ e.weights.assign(n * n, 0); e.bond_stereo.assign(n * n, "");
189
+ e.edge_colors.assign(n * n, 0);
190
+ for (auto bond : e.molecule->bonds()) {
191
+ int i = bond->getBeginAtomIdx(), j = bond->getEndAtomIdx();
192
+ double order = o.binary ? 1 : bond->getBondTypeAsDouble();
193
+ if (!std::isfinite(order) || order < 0 || std::abs(order * 2 - std::round(order * 2)) > 1e-9)
194
+ throw std::invalid_argument("Unsupported bond weight lattice");
195
+ e.weights[i * n + j] = e.weights[j * n + i] = std::llround(order * 2);
196
+ auto name = stereo_name(bond->getStereo());
197
+ e.bond_stereo[i * n + j] = e.bond_stereo[j * n + i] = name;
198
+ if (bond->getStereo() != RDKit::Bond::STEREONONE) {
199
+ e.stereo_anchors[i] = i + 1; e.stereo_anchors[j] = j + 1;
200
+ }
201
+ int color = edge_ids.emplace(name, edge_ids.size() + 1).first->second;
202
+ e.edge_colors[i * n + j] = e.edge_colors[j * n + i] = color;
203
+ }
204
+ if (o.lewis_objective)
205
+ for (auto atom : e.molecule->atoms()) {
206
+ int i = atom->getIdx(), electrons = nonbonding(*atom);
207
+ e.weights[i * n + n - 1] = e.weights[(n - 1) * n + i] = electrons;
208
+ }
209
+ for (int i = 0; i < n; ++i) {
210
+ auto type = e.labels[i];
211
+ e.types.push_back(hard_ids.emplace(type, hard_ids.size()).first->second);
212
+ e.atomic_numbers.push_back(type.first); e.isotopes.push_back(type.second);
213
+ auto key = std::to_string(type.first) + ":" + std::to_string(type.second) + ":" +
214
+ std::to_string(e.charges[i]) + ":" + std::to_string(e.radicals[i]) + ":" +
215
+ std::to_string(e.hcounts[i]) + ":" + e.stereo[i] + ":" +
216
+ std::to_string(e.dummies[i]) + ":" + std::to_string(e.stereo_anchors[i]);
217
+ e.node_colors.push_back(node_ids.emplace(key, node_ids.size()).first->second);
218
+ }
219
+ }
220
+ }
221
+
222
+ struct Palette {
223
+ std::vector<int32_t> nodes, edges_r, edges_p, edge_pairs;
224
+ std::vector<std::string> json;
225
+ std::vector<const char *> pointers;
226
+ SynkitClassProblem problem{};
227
+ Palette(const Endpoint &a, const Endpoint &b, const Budget &budget) {
228
+ int n = a.labels.size();
229
+ std::vector<const Token *> node_tokens;
230
+ std::set<Token> all;
231
+ for (int i = 0; i < n; ++i) {
232
+ budget.check();
233
+ for (int j = 0; j < n; ++j) {
234
+ std::vector<Token> props;
235
+ const char *names[] = {"charge", "radical", "hcount", "stereo", "dummy"};
236
+ for (int k = 0; k < 5; ++k) {
237
+ Token left, right;
238
+ if (k == 3) {
239
+ left = typed("str", "'" + a.stereo[i] + "'");
240
+ right = typed("str", "'" + b.stereo[j] + "'");
241
+ } else if (k == 4) {
242
+ left = typed("bool", a.dummies[i] ? "True" : "False");
243
+ right = typed("bool", b.dummies[j] ? "True" : "False");
244
+ } else {
245
+ const std::vector<int32_t> *pa[] = {&a.charges, &a.radicals, &a.hcounts};
246
+ const std::vector<int32_t> *pb[] = {&b.charges, &b.radicals, &b.hcounts};
247
+ left = typed("int", std::to_string((*pa[k])[i]));
248
+ right = typed("int", std::to_string((*pb[k])[j]));
249
+ }
250
+ props.push_back(tuple({string_token(names[k]), left, right}));
251
+ }
252
+ auto id = a.labels[i];
253
+ auto value = tuple({typed("tuple", "(" + std::to_string(id.first) + ", " +
254
+ std::to_string(id.second) + ")"), tuple(props), tuple({})});
255
+ // Most explicit H atoms share attributes. Retain interned tokens rather
256
+ // than n*n deep copies of their nested legacy certificate structure.
257
+ node_tokens.push_back(&*all.insert(value).first);
258
+ }
259
+ }
260
+ using State = std::pair<int64_t, std::string>;
261
+ std::vector<State> states[2];
262
+ std::vector<int32_t> *matrices[] = {&edges_r, &edges_p};
263
+ const Endpoint *sides[] = {&a, &b};
264
+ for (int side = 0; side < 2; ++side) {
265
+ std::map<Token, State> unique;
266
+ const auto &e = *sides[side];
267
+ for (int k = 0; k < n * n; ++k)
268
+ unique.emplace(tuple({weight_token(e.weights[k]), string_token(e.bond_stereo[k])}),
269
+ State{e.weights[k], e.bond_stereo[k]});
270
+ std::map<State, int> ids;
271
+ for (const auto &[token, state] : unique) {
272
+ (void)token;
273
+ ids[state] = states[side].size(); states[side].push_back(state);
274
+ }
275
+ for (int k = 0; k < n * n; ++k)
276
+ matrices[side]->push_back(ids.at({e.weights[k], e.bond_stereo[k]}));
277
+ }
278
+ std::vector<Token> edge_tokens;
279
+ std::vector<bool> present;
280
+ for (auto left : states[0])
281
+ for (auto right : states[1]) {
282
+ auto token = tuple({weight_token(left.first), weight_token(right.first),
283
+ string_token(left.second), string_token(right.second)});
284
+ edge_tokens.push_back(token);
285
+ present.push_back(left.first || right.first);
286
+ if (present.back()) all.insert(token);
287
+ }
288
+ if (all.size() >= 65536)
289
+ throw std::invalid_argument("Native reaction class palette exceeds uint16");
290
+ std::map<Token, int> ranks;
291
+ for (const auto &token : all) {
292
+ ranks[token] = json.size(); json.push_back(token.json());
293
+ }
294
+ for (const auto *token : node_tokens) nodes.push_back(ranks.at(*token));
295
+ for (size_t k = 0; k < edge_tokens.size(); ++k)
296
+ edge_pairs.push_back(present[k] ? ranks.at(edge_tokens[k]) + 1 : 0);
297
+ for (const auto &value : json) pointers.push_back(value.c_str());
298
+ problem = {n, nodes.data(), edges_r.data(), edges_p.data(),
299
+ static_cast<int32_t>(states[0].size()), static_cast<int32_t>(states[1].size()),
300
+ edge_pairs.data(), static_cast<int32_t>(pointers.size()), pointers.data()};
301
+ }
302
+ };
303
+
304
+ struct OutputRecord {
305
+ std::string before_count, after_count;
306
+ int64_t count;
307
+ };
308
+ struct Output {
309
+ const Endpoint &a, &b;
310
+ const Budget &budget;
311
+ std::vector<OutputRecord> records;
312
+ std::map<std::string,size_t> positions;
313
+ bool timed_out = false;
314
+ std::exception_ptr error;
315
+ };
316
+ struct KernelCandidates {
317
+ int n;
318
+ std::vector<int32_t> mappings;
319
+ std::exception_ptr error;
320
+ };
321
+ int remember_kernel_candidate(const int32_t *mapping, int64_t, void *data) {
322
+ auto &buffer = *static_cast<KernelCandidates *>(data);
323
+ try {
324
+ buffer.mappings.insert(buffer.mappings.end(), mapping, mapping + buffer.n);
325
+ return 0;
326
+ } catch (...) { buffer.error = std::current_exception(); return 1; }
327
+ }
328
+ int receive_class(const int32_t *mapping, int64_t cost2, int64_t count,
329
+ const unsigned char *packed, int64_t size, const char *identifier, void *data) {
330
+ auto &out = *static_cast<Output *>(data);
331
+ try {
332
+ if (out.budget.reason()) { out.timed_out = true; return 1; }
333
+ std::string key(reinterpret_cast<const char*>(packed),static_cast<size_t>(size));
334
+ auto existing=out.positions.find(key);
335
+ if(existing!=out.positions.end()) {
336
+ auto &record=out.records[existing->second];
337
+ if(count<record.count) throw std::runtime_error("Nonmonotone native class count");
338
+ record.count=count;
339
+ return 0;
340
+ }
341
+ RDKit::RWMol left(*out.a.molecule), right(*out.b.molecule);
342
+ int n = out.a.labels.size();
343
+ std::vector<int> inverse(n, -1);
344
+ for (int i = 0; i < n; ++i) inverse[mapping[i]] = i + 1;
345
+ for (auto atom : left.atoms()) atom->setAtomMapNum(atom->getIdx() + 1);
346
+ for (auto atom : right.atoms()) atom->setAtomMapNum(inverse[atom->getIdx()]);
347
+ std::ostringstream record;
348
+ record << "{\"mapping\":[";
349
+ for (int i = 0; i < n; ++i) record << (i ? "," : "") << mapping[i];
350
+ record << "],\"distance\":" << cost2 / 2.0
351
+ << ",\"class_id\":" << quote(identifier)
352
+ << ",\"observed_mapping_count\":";
353
+ const std::string before_count=record.str();
354
+ record.str("");record.clear();
355
+ record << ",\"mapped_reaction\":"
356
+ << quote(RDKit::MolToSmiles(left) + ">>" + RDKit::MolToSmiles(right));
357
+ for (int side = 0; side < 2; ++side) {
358
+ record << (side == 0 ? ",\"reactant_only_maps\":[" : ",\"product_only_maps\":[");
359
+ bool first = true;
360
+ for (int i = 0; i < n; ++i) {
361
+ bool selected = side == 0 ? i < static_cast<int>(left.getNumAtoms()) && mapping[i] >= static_cast<int>(right.getNumAtoms())
362
+ : i >= static_cast<int>(left.getNumAtoms()) && mapping[i] < static_cast<int>(right.getNumAtoms());
363
+ if (selected) { record << (first ? "" : ",") << i + 1; first = false; }
364
+ }
365
+ record << ']';
366
+ }
367
+ record << '}';
368
+ if (out.budget.reason()) { out.timed_out = true; return 1; }
369
+ out.positions.emplace(std::move(key),out.records.size());
370
+ out.records.push_back({before_count,record.str(),count});
371
+ return 0;
372
+ } catch (...) { out.error = std::current_exception(); return 1; }
373
+ }
374
+
375
+ std::string statistics_json(const Statistics &s) {
376
+ std::ostringstream out;
377
+ out << '{';
378
+ #define FIELD(name) out << "\"" #name "\":" << s.name << ',';
379
+ FIELD(backend) FIELD(status) FIELD(reason) FIELD(minimum_proven)
380
+ FIELD(search_complete) FIELD(classification_complete) FIELD(hydrogen_complete)
381
+ FIELD(heuristic_finished) FIELD(minimum2) FIELD(incumbent2) FIELD(lower_bound2)
382
+ FIELD(nodes) FIELD(leaves) FIELD(candidate_count) FIELD(class_count)
383
+ FIELD(labeled_count) FIELD(peak_frontier) FIELD(peak_frontier_bytes)
384
+ FIELD(peak_memory_bytes) FIELD(model_seconds) FIELD(optimization_seconds)
385
+ FIELD(enumeration_seconds) FIELD(classification_seconds) FIELD(callback_seconds)
386
+ #undef FIELD
387
+ out << "\"elapsed_seconds\":" << s.elapsed_seconds << '}';
388
+ return out.str();
389
+ }
390
+ } // namespace
391
+
392
+ extern "C" {
393
+ int synkit_mapper_reaction_abi(void) { return 1; }
394
+ const char *synkit_mapper_reaction_error(void) { return last_error.c_str(); }
395
+ const char *synkit_mapper_rdkit_version(void) { return RDKit::rdkitVersion; }
396
+ void synkit_mapper_reaction_free(char *p) { delete[] p; }
397
+ int synkit_mapper_reaction_solve(const char *reaction, const SynkitReactionOptions *o,
398
+ const SynkitMapperRequest *request, char **output) {
399
+ const auto invocation_start = Clock::now();
400
+ double preparation_time = -1;
401
+ int augmented_nodes = 0;
402
+ std::string agents;
403
+ last_error.clear();
404
+ if (output) *output = nullptr;
405
+ try {
406
+ if (!reaction || !o || !request || !output || o->abi_version != 1 ||
407
+ request->abi_version != 1 || !std::isfinite(request->seconds) ||
408
+ (request->seconds < 0 && request->seconds != -1) ||
409
+ (o->lewis_objective && (!o->explicit_hydrogens || o->binary)) ||
410
+ (o->max_canonical_nodes != -1 && o->max_canonical_nodes < 1) ||
411
+ (o->max_hydrogen_plans != -1 && o->max_hydrogen_plans < 1))
412
+ throw std::invalid_argument("Invalid standalone reaction options");
413
+ for (int flag : {o->explicit_hydrogens, o->lewis_objective, o->dummy_balance,
414
+ o->binary, o->hydrogen_quotient, o->empty_numeric_shell})
415
+ if (flag != 0 && flag != 1) throw std::invalid_argument("Reaction flags must be boolean");
416
+ Budget budget(*request);
417
+ std::string input(reaction);
418
+ size_t first = input.find('>'), last = input.rfind('>');
419
+ if (first == std::string::npos || first == last || first == 0 ||
420
+ last + 1 == input.size() || input.find('>', first + 1) != last)
421
+ throw std::invalid_argument("Expected reactants>agents>products");
422
+ agents = input.substr(first + 1, last - first - 1);
423
+ Endpoint a = parse(input.substr(0, first), o->explicit_hydrogens, budget);
424
+ Endpoint b = parse(input.substr(last + 1), o->explicit_hydrogens, budget);
425
+ prepare(a, b, *o, budget);
426
+ augmented_nodes = a.labels.size();
427
+ Problem problem{1, static_cast<int32_t>(a.labels.size()), a.weights.data(), b.weights.data(),
428
+ a.types.data(), b.types.data(), a.node_colors.data(), b.node_colors.data(),
429
+ a.edge_colors.data(), b.edge_colors.data()};
430
+ validate(problem, *request);
431
+ Palette palette(a, b, budget);
432
+ const double preparation_seconds = budget.seconds();
433
+ preparation_time = preparation_seconds;
434
+ Request adjusted = *request;
435
+ adjusted.seconds = request->seconds < 0 ? -1 : budget.remaining();
436
+ adjusted.classify = request->query != SYNKIT_MINIMUM_PROOF;
437
+ Statistics stats{};
438
+ SynkitClassStatistics class_stats{};
439
+ SynkitHydrogenStatistics hstats{};
440
+ bool hydrogen_fallback = false;
441
+ int64_t kernel_seed_candidates = 0;
442
+ int kernel_seed_finished = 0;
443
+ double kernel_seed_seconds = 0;
444
+ Output records{a, b, budget, {}, {}, false, nullptr};
445
+ int result;
446
+ if (o->empty_numeric_shell) {
447
+ if (request->query != SYNKIT_EXACT_DISTANCE)
448
+ throw std::invalid_argument("Empty-shell flag requires an exact numeric query");
449
+ initialize(stats, adjusted);
450
+ stats.search_complete = stats.classification_complete = stats.hydrogen_complete = 1;
451
+ stats.class_count = stats.labeled_count = 0;
452
+ } else if (request->backend == SYNKIT_KBB || request->backend == SYNKIT_KMILP) {
453
+ if (o->hydrogen_quotient)
454
+ throw std::invalid_argument("Hydrogen quotient is a full-space exact option, not a kernel restriction");
455
+ Request seed = adjusted;
456
+ seed.backend = SYNKIT_SLAP; seed.query = SYNKIT_MINIMUM_CLASSES;
457
+ seed.symmetry = seed.classify = 0;
458
+ seed.max_classes = seed.max_frontier = seed.memory_limit_bytes = -1;
459
+ seed.max_candidates = 10000;
460
+ std::vector<int32_t> flags(problem.n, 0);
461
+ seed.slap_stop_flags = flags.data();
462
+ KernelCandidates candidates{problem.n, {}, nullptr};
463
+ Statistics seed_stats{};
464
+ result = synkit_mapper_solve(&problem, &seed, remember_kernel_candidate, &candidates, &seed_stats);
465
+ if (candidates.error) std::rethrow_exception(candidates.error);
466
+ if (result) throw std::runtime_error(synkit_mapper_core_error());
467
+ kernel_seed_seconds = seed_stats.elapsed_seconds;
468
+ kernel_seed_finished = seed_stats.heuristic_finished;
469
+ kernel_seed_candidates = candidates.mappings.size() / problem.n;
470
+ if (!kernel_seed_candidates) {
471
+ stats = seed_stats; stats.backend = request->backend;
472
+ stats.classification_complete = stats.search_complete = 0;
473
+ } else {
474
+ std::vector<uint8_t> allowed(problem.n * problem.n);
475
+ result = synkit_mapper_kernel_domains(problem.n, kernel_seed_candidates,
476
+ candidates.mappings.data(), problem.types_r, problem.types_p, 4, allowed.data());
477
+ if (result) throw std::runtime_error(synkit_mapper_assignment_error());
478
+ adjusted.seconds = request->seconds < 0 ? -1 : budget.remaining();
479
+ if (request->max_states >= 0) {
480
+ if (seed_stats.nodes >= request->max_states)
481
+ throw Stop{SYNKIT_STOP_STATES};
482
+ adjusted.max_states = request->max_states - seed_stats.nodes;
483
+ }
484
+ if (request->query == SYNKIT_MINIMUM_PROOF)
485
+ result = synkit_mapper_solve_domains(&problem, &adjusted, allowed.data(), nullptr, nullptr, &stats);
486
+ else
487
+ result = synkit_mapper_solve_domains_classified_updates(&problem, &adjusted, allowed.data(),
488
+ &palette.problem, o->max_canonical_nodes, receive_class, &records, &stats, &class_stats);
489
+ if (result) {
490
+ std::string message = synkit_mapper_core_error();
491
+ if (message.empty()) message = synkit_mapper_classification_error();
492
+ throw std::runtime_error(message.empty() ? "Native kernel pipeline failed" : message);
493
+ }
494
+ stats.nodes += seed_stats.nodes;
495
+ }
496
+ } else if (o->hydrogen_quotient) {
497
+ std::vector<int32_t> sa(a.labels.size()), sb(b.labels.size());
498
+ for (size_t i = 0; i < sa.size(); ++i) {
499
+ sa[i] = !a.stereo[i].empty() || a.stereo_anchors[i];
500
+ sb[i] = !b.stereo[i].empty() || b.stereo_anchors[i];
501
+ if (input.find('@') != std::string::npos || input.find('|') != std::string::npos)
502
+ sa[i] = sb[i] = 1;
503
+ }
504
+ SynkitHydrogenAttributes attrs{a.atomic_numbers.data(), b.atomic_numbers.data(),
505
+ a.isotopes.data(), b.isotopes.data(), a.charges.data(), b.charges.data(),
506
+ a.radicals.data(), b.radicals.data(), a.dummies.data(), b.dummies.data(),
507
+ sa.data(), sb.data()};
508
+ if (request->query == SYNKIT_MINIMUM_PROOF)
509
+ result = synkit_mapper_hydrogen_solve(&problem, &attrs, &adjusted,
510
+ o->max_hydrogen_plans, nullptr, nullptr, &stats, &hstats);
511
+ else
512
+ result = synkit_mapper_hydrogen_solve_classified_updates(&problem, &attrs, &adjusted,
513
+ &palette.problem, o->max_hydrogen_plans, o->max_canonical_nodes,
514
+ receive_class, &records, &stats, &hstats, &class_stats);
515
+ if (result) throw std::runtime_error(synkit_mapper_hydrogen_error());
516
+ if (stats.status == SYNKIT_UNSUPPORTED) {
517
+ hydrogen_fallback = true;
518
+ adjusted.seconds = request->seconds < 0 ? -1 : budget.remaining();
519
+ if (hstats.eligibility_reason == SYNKIT_H_CLASS_ATTRIBUTES)
520
+ adjusted.symmetry = 0;
521
+ if (request->query == SYNKIT_MINIMUM_PROOF)
522
+ result = synkit_mapper_solve(&problem, &adjusted, nullptr, nullptr, &stats);
523
+ else
524
+ result = synkit_mapper_solve_classified_updates(&problem, &adjusted, &palette.problem,
525
+ o->max_canonical_nodes, receive_class, &records, &stats, &class_stats);
526
+ if (result) {
527
+ std::string message = synkit_mapper_core_error();
528
+ if (message.empty()) message = synkit_mapper_classification_error();
529
+ throw std::runtime_error(message.empty() ? "Native H fallback failed" : message);
530
+ }
531
+ }
532
+ } else if (request->query == SYNKIT_MINIMUM_PROOF) {
533
+ result = synkit_mapper_solve(&problem, &adjusted, nullptr, nullptr, &stats);
534
+ if (result) throw std::runtime_error(synkit_mapper_core_error());
535
+ } else {
536
+ result = synkit_mapper_solve_classified_updates(&problem, &adjusted, &palette.problem,
537
+ o->max_canonical_nodes, receive_class, &records, &stats, &class_stats);
538
+ if (result) {
539
+ std::string message = synkit_mapper_core_error();
540
+ if (message.empty()) message = synkit_mapper_classification_error();
541
+ throw std::runtime_error(message.empty() ? "Native reaction pipeline failed" : message);
542
+ }
543
+ }
544
+ if (records.error) std::rethrow_exception(records.error);
545
+ if (records.timed_out) {
546
+ stats.status = SYNKIT_LIMIT; stats.reason = SYNKIT_STOP_TIME;
547
+ stats.classification_complete = 0;
548
+ }
549
+ stats.elapsed_seconds = budget.seconds();
550
+ bool complete = stats.search_complete && stats.classification_complete &&
551
+ stats.hydrogen_complete && request->query != SYNKIT_MINIMUM_PROOF &&
552
+ request->backend != SYNKIT_SLAP;
553
+ std::ostringstream json;
554
+ json << std::setprecision(17) << "{\"pipeline\":\"rdkit_cpp\",\"rdkit_version\":"
555
+ << quote(RDKit::rdkitVersion) << ",\"complete\":" << (complete ? "true" : "false")
556
+ << ",\"minimum_proven\":" << (stats.minimum_proven ? "true" : "false")
557
+ << ",\"minimum_cost\":";
558
+ if (stats.minimum_proven) json << stats.minimum2 / 2.0; else json << "null";
559
+ json << ",\"objective\":" << quote(o->lewis_objective ? "lewis" : "bond")
560
+ << ",\"balance\":" << quote(o->dummy_balance ? "dummy" : "strict")
561
+ << ",\"hydrogen\":" << quote(o->explicit_hydrogens ? "explicit" : "heavy")
562
+ << ",\"agents\":" << quote(agents)
563
+ << ",\"hydrogen_quotient\":" << (o->hydrogen_quotient ? "true" : "false")
564
+ << ",\"hydrogen_quotient_used\":" << (o->hydrogen_quotient && !hydrogen_fallback && hstats.supported ? "true" : "false")
565
+ << ",\"hydrogen_quotient_fallback\":" << (hydrogen_fallback ? "true" : "false")
566
+ << ",\"hydrogen_supported\":" << (hstats.supported ? "true" : "false")
567
+ << ",\"hydrogen_eligibility_reason\":" << hstats.eligibility_reason
568
+ << ",\"augmented_nodes\":" << problem.n
569
+ << ",\"search_scope\":" << quote(request->backend == SYNKIT_KBB || request->backend == SYNKIT_KMILP ? "restricted_kernel" : request->backend == SYNKIT_SLAP ? "heuristic_candidates" : "full_space")
570
+ << ",\"kernel_seed_candidates\":" << kernel_seed_candidates
571
+ << ",\"kernel_seed_finished\":" << (kernel_seed_finished ? "true" : "false")
572
+ << ",\"kernel_seed_seconds\":" << kernel_seed_seconds
573
+ << ",\"preparation_seconds\":" << preparation_seconds
574
+ << ",\"statistics\":" << statistics_json(stats) << ",\"classes\":[";
575
+ for (size_t i = 0; i < records.records.size(); ++i) {
576
+ const auto &record=records.records[i];
577
+ json << (i ? "," : "") << record.before_count << record.count << record.after_count;
578
+ }
579
+ json << "]}";
580
+ auto text = json.str();
581
+ *output = new char[text.size() + 1];
582
+ std::memcpy(*output, text.c_str(), text.size() + 1);
583
+ return 0;
584
+ } catch (const Stop &stop) {
585
+ Statistics stats{};
586
+ initialize(stats, *request);
587
+ stopped(stats, stop.reason);
588
+ stats.classification_complete = stats.hydrogen_complete = 0;
589
+ stats.elapsed_seconds = elapsed(invocation_start);
590
+ std::ostringstream json;
591
+ json << std::setprecision(17) << "{\"pipeline\":\"rdkit_cpp\",\"rdkit_version\":"
592
+ << quote(RDKit::rdkitVersion) << ",\"complete\":false,\"minimum_proven\":false,\"minimum_cost\":null"
593
+ << ",\"objective\":" << quote(o->lewis_objective ? "lewis" : "bond")
594
+ << ",\"balance\":" << quote(o->dummy_balance ? "dummy" : "strict")
595
+ << ",\"hydrogen\":" << quote(o->explicit_hydrogens ? "explicit" : "heavy")
596
+ << ",\"agents\":" << quote(agents)
597
+ << ",\"hydrogen_quotient\":" << (o->hydrogen_quotient ? "true" : "false")
598
+ << ",\"hydrogen_quotient_used\":false,\"hydrogen_quotient_fallback\":false,\"hydrogen_supported\":false"
599
+ << ",\"hydrogen_eligibility_reason\":0,\"augmented_nodes\":" << augmented_nodes
600
+ << ",\"search_scope\":" << quote(request->backend == SYNKIT_KBB || request->backend == SYNKIT_KMILP ? "restricted_kernel" : request->backend == SYNKIT_SLAP ? "heuristic_candidates" : "full_space")
601
+ << ",\"kernel_seed_candidates\":0,\"kernel_seed_finished\":false,\"kernel_seed_seconds\":0"
602
+ << ",\"preparation_seconds\":" << (preparation_time < 0 ? stats.elapsed_seconds : preparation_time)
603
+ << ",\"classes\":[],\"statistics\":" << statistics_json(stats) << '}';
604
+ std::string text = json.str();
605
+ if (!output) return -1;
606
+ *output = new char[text.size() + 1];
607
+ std::memcpy(*output, text.c_str(), text.size() + 1);
608
+ return 0;
609
+ } catch (const std::exception &e) { last_error = e.what(); return -1; }
610
+ }
611
+ }
@@ -0,0 +1,61 @@
1
+ # Exact separator-table mapping DP
2
+
3
+ This is a native separator/path-decomposition dynamic program for the complete
4
+ weighted compatible-bijection problem. It is not the earlier bounded residual
5
+ oracle, and it does not borrow a polynomial-time or SR-GED complexity claim.
6
+ All arbitrary weighted endpoint graphs are supported within this implementation's
7
+ explicit `n <= 63` target-bitmask domain. Larger inputs return unsupported.
8
+
9
+ Let `M` be the sum of all unordered edge weights in A2 and B2. For a bijection f,
10
+
11
+ D2(f) = M - 2 * sum_{i<k, A2[i,k]>0} min(A2[i,k], B2[f(i),f(k)]).
12
+
13
+ This follows from `|a-b| = a+b-2min(a,b)` and from a bijection preserving the
14
+ sum of all target edge weights. It is why missing source edges do not require
15
+ pair interactions in the DP: their contribution is already in the global
16
+ constant, not dropped. Unary Lewis electron anchors remain ordinary forced
17
+ vertices, included exactly once by this identity.
18
+
19
+ Fix a deterministic source order. After each prefix, its separator consists
20
+ of processed source vertices with a positive A2 edge to an unprocessed source
21
+ vertex. A state records:
22
+
23
+ 1. the entire set of already used target vertices, as a global bitmask;
24
+ 2. the images of every current separator vertex;
25
+ 3. the accumulated overlap of source edges with both ends already processed.
26
+
27
+ A transition assigns the next source vertex to an unused compatible target,
28
+ and adds its overlap with earlier separator vertices. Every earlier neighbor
29
+ must be in the separator by definition. Every source edge is therefore counted
30
+ once at its later endpoint, including edges crossing any intermediate separator.
31
+ Forgotten target images remain present in the global mask, so target injectivity
32
+ is enforced across components, separators and forgotten parts of the graph.
33
+ There is no independent-subproblem matching that could accidentally reuse a
34
+ target vertex across components.
35
+
36
+ Two prefixes with equal state keys admit exactly the same future compatible
37
+ targets and edge-overlap increments. Merging them is therefore exact. All
38
+ predecessor arcs are retained, including multiple histories for the same state.
39
+ Accumulated overlap is part of the key even for numeric shells: retaining only
40
+ an optimum prefix would lose legitimate higher-cost exact/at-most mappings.
41
+ Every compatible full mapping gives one path in this layered DAG, and every
42
+ reconstructed path gives one compatible mapping. This proves complete labeled
43
+ minimum, exact and at-most output after all selected final states are traversed.
44
+
45
+ The optimum is proved only after all DP layers are exhausted. A completed final
46
+ state reached just before a limit supplies an incumbent but no minimum proof.
47
+ Interruption during predecessor reconstruction preserves an already proved
48
+ minimum while enumeration remains incomplete. Numeric thresholds never shrink.
49
+ Proof-only queries skip reconstruction and do not pretend to enumerate classes.
50
+ The shared class pipeline supplies exact ITS classes from the labeled mappings.
51
+
52
+ Complexity is exponential in the global target-subset space, separator width,
53
+ and retained overlap levels; predecessor reconstruction is output-sensitive.
54
+ Dense source graphs can retain almost all processed vertices in the separator.
55
+ The method consequently has no general polynomial scaling guarantee. State,
56
+ frontier, candidate and soft deadline/cancellation limits are enforced. The
57
+ memory cap and `peak_frontier_bytes` measure a conservative accounted retained
58
+ table/key/predecessor payload, including released per-layer keys conservatively;
59
+ allocator/block/hash-bucket overhead and transient scratch are excluded. This
60
+ is not process RSS or an operating-system memory limit. The additional profile
61
+ reports width, state/arc/transition counts and accounted table bytes.