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,390 @@
1
+ """Schedule disjoint resumable native subtrees and merge exact records online."""
2
+
3
+ import ctypes
4
+ import math
5
+ import multiprocessing as mp
6
+ import os
7
+ import time
8
+ from collections import deque
9
+ from concurrent.futures import FIRST_COMPLETED, ProcessPoolExecutor, wait
10
+ from pathlib import Path
11
+
12
+ from .candidates import (
13
+ NativeEnumerationStop,
14
+ enumerate_native_candidates,
15
+ prepare_native_candidates,
16
+ )
17
+ from .canonical import compact_code_identifier, transport_canonical_key
18
+ from .orbit_aggregation import OrbitAccumulator
19
+ from .runtime import limit_worker_address_space, pin_worker_cpu, worker_cpus
20
+
21
+ try:
22
+ import resource
23
+ except ImportError: # Windows has no Unix resource module.
24
+ resource = None
25
+
26
+ _STATE = None
27
+ _ADDRESS_SPACE_LIMIT = None
28
+
29
+
30
+ def _init(
31
+ lgp,
32
+ target,
33
+ config,
34
+ seed,
35
+ library,
36
+ counter,
37
+ deadline,
38
+ barrier,
39
+ cpu_index,
40
+ cpus,
41
+ prepared_data,
42
+ ):
43
+ global _STATE, _ADDRESS_SPACE_LIMIT
44
+ from ....reporting.analysis import _BlindShellObserver, _property_vectors
45
+ from ...slap.lap import _adjacency_and_elements
46
+
47
+ _ADDRESS_SPACE_LIMIT = limit_worker_address_space(resource)
48
+ with cpu_index.get_lock():
49
+ index = cpu_index.value
50
+ cpu_index.value += 1
51
+ pin_worker_cpu(cpus[index % len(cpus)] if cpus else None)
52
+ a, labels = _adjacency_and_elements(lgp[0], False)
53
+ b, _ = _adjacency_and_elements(lgp[1], False)
54
+ observer = _BlindShellObserver(
55
+ a, b, labels, _property_vectors(lgp, config.reaction_center_properties), config
56
+ )
57
+ # Validated once before spawning; each worker receives private arrays and
58
+ # attaches its own library handle. Side-group completeness is not re-inferred.
59
+ prepared = (*prepared_data, ctypes.CDLL(str(Path(library).resolve())))
60
+ rg, _pg, ro, po = prepared[4:8]
61
+ accumulator = OrbitAccumulator(
62
+ observer,
63
+ rg[1:],
64
+ ro,
65
+ po,
66
+ library_path=library,
67
+ record_only=True,
68
+ )
69
+ _STATE = (
70
+ lgp,
71
+ target,
72
+ config,
73
+ seed,
74
+ library,
75
+ counter,
76
+ deadline,
77
+ barrier,
78
+ accumulator,
79
+ prepared,
80
+ )
81
+
82
+
83
+ def _ready():
84
+ barrier = _STATE[7]
85
+ barrier.wait(timeout=30)
86
+ barrier.wait(timeout=30)
87
+ return _ADDRESS_SPACE_LIMIT
88
+
89
+
90
+ def _slice(prefixes, slice_nodes): # noqa: C901
91
+ from ....reporting.analysis import _mapping_key
92
+
93
+ lgp, target, config, _seed, library, counter, deadline, _, accumulator, prepared = (
94
+ _STATE
95
+ )
96
+ records, hashes = {}, set()
97
+ # The parent fixes this value before releasing the readiness barrier and
98
+ # never mutates it during search. Snapshot it once per slice: acquiring a
99
+ # shared process lock at every candidate serializes otherwise independent
100
+ # callbacks without improving deadline enforcement.
101
+ slice_deadline = deadline.value
102
+ counter_value = counter.get_obj()
103
+ profile = bool(os.environ.get("SYNKIT_NATIVE_PROFILE"))
104
+ before_timings = dict(accumulator.its_canonicalizer.timings) if profile else {}
105
+ callback_seconds = identifier_seconds = 0.0
106
+ pattern_hits_before = accumulator.pattern_hits
107
+
108
+ def receive_impl(mapping, cost):
109
+ nonlocal identifier_seconds
110
+ if time.perf_counter() >= slice_deadline:
111
+ raise NativeEnumerationStop("time_limit")
112
+ hashes.add(_mapping_key(mapping))
113
+ before = len(accumulator.seen)
114
+ accumulator.observe(mapping, cost)
115
+ if len(accumulator.seen) == before:
116
+ return
117
+ key = next(reversed(accumulator.seen))
118
+ with counter.get_lock():
119
+ if config.max_mappings is not None and counter_value.value >= config.max_mappings:
120
+ accumulator.seen.pop(key)
121
+ raise NativeEnumerationStop("mapping_limit")
122
+ counter_value.value += 1
123
+ accumulator.its_canonicalizer.remember_pattern()
124
+ # IDs depend only on exact certificates, so formatting can run while
125
+ # other workers enumerate. Cached fields never participate in equality.
126
+ identifier_started = time.perf_counter() if profile else 0
127
+ object.__setattr__(key, "identifier", compact_code_identifier(key))
128
+ template_key = accumulator.seen[key][3]
129
+ if template_key is not None:
130
+ object.__setattr__(
131
+ template_key, "identifier", compact_code_identifier(template_key)
132
+ )
133
+ if profile:
134
+ identifier_seconds += time.perf_counter() - identifier_started
135
+ value = accumulator.seen[key]
136
+ records[transport_canonical_key(key)] = (
137
+ *value[:3],
138
+ transport_canonical_key(template_key),
139
+ *value[4:],
140
+ )
141
+ # Membership alone is sufficient to deduplicate subsequent slices.
142
+ accumulator.seen[key] = None
143
+
144
+ def profiled_receive(mapping, cost):
145
+ nonlocal callback_seconds
146
+ started = time.perf_counter()
147
+ try:
148
+ return receive_impl(mapping, cost)
149
+ finally:
150
+ callback_seconds += time.perf_counter() - started
151
+
152
+ receive = profiled_receive if profile else receive_impl
153
+ remaining = deque(prefixes)
154
+ frontier = []
155
+ totals = {
156
+ "candidate_count": 0,
157
+ "visited_nodes": 0,
158
+ "visited_leaves": 0,
159
+ "pruned": 0,
160
+ "prefix_replay_nodes": 0,
161
+ "new_search_nodes": 0,
162
+ }
163
+ batch_started = time.perf_counter()
164
+ reason = None
165
+ while remaining:
166
+ batched = hasattr(prepared[-1], "synkit_distance_frontier_batch")
167
+ options = {}
168
+ node_budget = slice_nodes
169
+ if batched:
170
+ options["prefixes"] = tuple(remaining)
171
+ node_budget *= min(4, len(remaining))
172
+ remaining.clear()
173
+ prefix = ()
174
+ else:
175
+ prefix = remaining.popleft()
176
+ result = enumerate_native_candidates(
177
+ lgp,
178
+ target,
179
+ library_path=library,
180
+ time_limit_seconds=max(0, slice_deadline - time.perf_counter()),
181
+ max_mappings=None,
182
+ callback=receive,
183
+ prefix=prefix,
184
+ slice_nodes=node_budget,
185
+ _prepared=prepared,
186
+ **options,
187
+ )
188
+ for name in totals:
189
+ totals[name] += result[name] or 0
190
+ frontier.extend(result["frontier"])
191
+ if result["reason"] not in (None, "work_slice"):
192
+ reason = result["reason"]
193
+ break
194
+ if time.perf_counter() - batch_started >= 0.25:
195
+ break
196
+ frontier.extend(remaining)
197
+ if reason is None and frontier:
198
+ reason = "work_slice"
199
+ totals.update(
200
+ complete=reason is None,
201
+ reason=reason,
202
+ frontier=frontier,
203
+ retained_classes=len(records),
204
+ pattern_cache_hits=accumulator.pattern_hits - pattern_hits_before,
205
+ elapsed_seconds=time.perf_counter() - batch_started,
206
+ )
207
+ if profile:
208
+ totals["profile"] = {
209
+ key: value - before_timings.get(key, 0)
210
+ for key, value in accumulator.its_canonicalizer.timings.items()
211
+ }
212
+ totals["profile"].update(
213
+ callback_seconds=callback_seconds,
214
+ identifier_seconds=identifier_seconds,
215
+ other_batch_seconds=totals["elapsed_seconds"] - callback_seconds,
216
+ )
217
+ return totals, records, hashes
218
+
219
+
220
+ def frontier_orbit_search( # noqa: C901
221
+ lgp,
222
+ target,
223
+ config,
224
+ seed,
225
+ observer,
226
+ *,
227
+ library_path,
228
+ workers=8,
229
+ slice_nodes=8192,
230
+ absolute_deadline=None,
231
+ ):
232
+ """Consume every emitted subtree before claiming complete coverage.
233
+
234
+ The shared cap counts unique records retained within each worker, including
235
+ duplicate classes held by different workers. Prefix replay is deterministic
236
+ and preserves the original two-sided pruning state.
237
+ """
238
+
239
+ if (
240
+ isinstance(workers, bool)
241
+ or not isinstance(workers, int)
242
+ or workers < 1
243
+ or isinstance(slice_nodes, bool)
244
+ or not isinstance(slice_nodes, int)
245
+ or not 0 < slice_nodes < (1 << 63)
246
+ ):
247
+ raise ValueError("workers and slice_nodes must be positive integers")
248
+ if (
249
+ config.binary
250
+ or set(config.symmetry_node_properties)
251
+ != set(config.reaction_center_properties)
252
+ or config.time_limit_seconds is None
253
+ or not math.isfinite(config.time_limit_seconds)
254
+ ):
255
+ raise ValueError(
256
+ "frontier search requires weighted aligned properties and a finite deadline"
257
+ )
258
+ if not hasattr(
259
+ ctypes.CDLL(str(Path(library_path).resolve())), "synkit_distance_frontier"
260
+ ):
261
+ raise ValueError(
262
+ "native library lacks frontier support; rebuild it or use the static scheduler"
263
+ )
264
+ prepared = prepare_native_candidates(
265
+ lgp,
266
+ target,
267
+ library_path=library_path,
268
+ initial_mapping=seed,
269
+ node_properties=config.symmetry_node_properties,
270
+ )
271
+ rg, _pg, ro, po = prepared[4:8]
272
+ merged = OrbitAccumulator(
273
+ observer,
274
+ rg[1:],
275
+ ro,
276
+ po,
277
+ library_path=library_path,
278
+ transport_keys=True,
279
+ )
280
+ context = mp.get_context("spawn")
281
+ counter, deadline = context.Value("q", 0), context.Value("d", 0)
282
+ cpu_index = context.Value("i", 0)
283
+ barrier = context.Barrier(workers + 1)
284
+ cpus = worker_cpus(workers)
285
+ started = time.perf_counter()
286
+ coordinator_cpu_started = time.process_time()
287
+ merge_seconds = wait_seconds = 0.0
288
+ pending, running = deque([()]), set()
289
+ records, results, reasons = {}, [], set()
290
+ peak_pending = 1
291
+ profile_totals = {}
292
+ with ProcessPoolExecutor(
293
+ max_workers=workers,
294
+ mp_context=context,
295
+ initializer=_init,
296
+ initargs=(
297
+ lgp,
298
+ target,
299
+ config,
300
+ seed,
301
+ str(library_path),
302
+ counter,
303
+ deadline,
304
+ barrier,
305
+ cpu_index,
306
+ cpus,
307
+ prepared[:-1],
308
+ ),
309
+ ) as pool:
310
+ ready = [pool.submit(_ready) for _ in range(workers)]
311
+ barrier.wait(timeout=30)
312
+ search_started = time.perf_counter()
313
+ deadline.value = search_started + config.time_limit_seconds
314
+ if absolute_deadline is not None:
315
+ deadline.value = min(deadline.value, absolute_deadline)
316
+ barrier.wait(timeout=30)
317
+ for future in ready:
318
+ future.result()
319
+ while pending or running:
320
+ if time.perf_counter() >= deadline.value:
321
+ reasons.add("time_limit")
322
+ while pending and len(running) < workers and not reasons:
323
+ # Share sparse frontiers across idle workers before batching.
324
+ # Fixed groups of 16 can put all remaining work in one process.
325
+ idle = workers - len(running)
326
+ batch_size = min(64, max(1, len(pending) // idle))
327
+ prefixes = [pending.popleft() for _ in range(batch_size)]
328
+ running.add(pool.submit(_slice, prefixes, slice_nodes))
329
+ if not running:
330
+ break
331
+ wait_started = time.perf_counter()
332
+ finished, running = wait(running, timeout=0.25, return_when=FIRST_COMPLETED)
333
+ merge_started = time.perf_counter()
334
+ wait_seconds += merge_started - wait_started
335
+ for future in finished:
336
+ result, found, hashes = future.result()
337
+ observer.mapping_hashes.update(hashes)
338
+ new_tasks = result.pop("frontier")
339
+ if result["reason"] not in (None, "work_slice"):
340
+ reasons.add(result["reason"])
341
+ else:
342
+ pending.extend(new_tasks)
343
+ peak_pending = max(peak_pending, len(pending))
344
+ for key, value in result.pop("profile", {}).items():
345
+ profile_totals[key] = profile_totals.get(key, 0) + value
346
+ results.append(result)
347
+ for key, value in found.items():
348
+ old = records.get(key)
349
+ if old is not None and (
350
+ old[:2] != value[:2] or old[3:] != value[3:]
351
+ ):
352
+ raise RuntimeError(
353
+ "inconsistent exact records across frontier workers"
354
+ )
355
+ if old is None:
356
+ records[key] = value
357
+ merged.merge_record(key, value)
358
+ merge_seconds += time.perf_counter() - merge_started
359
+ merged.finish()
360
+ shutdown_started = time.perf_counter()
361
+ if time.perf_counter() > deadline.value:
362
+ reasons.add("time_limit")
363
+ return {
364
+ "complete": not reasons and not pending,
365
+ "profile_totals": profile_totals,
366
+ "coordinator_cpu_seconds": time.process_time() - coordinator_cpu_started,
367
+ "coordinator_merge_seconds": merge_seconds,
368
+ "coordinator_wait_seconds": wait_seconds,
369
+ "worker_shutdown_seconds": time.perf_counter() - shutdown_started,
370
+ "worker_startup_seconds": search_started - started,
371
+ "reasons": sorted(reasons),
372
+ "timed_phase_seconds": time.perf_counter() - search_started,
373
+ "retained_worker_records": counter.value,
374
+ "candidate_count": sum(item["candidate_count"] for item in results),
375
+ "pattern_cache_hits": sum(item["pattern_cache_hits"] for item in results),
376
+ "mapping_limit_scope": "retained_worker_double_orbit_representatives",
377
+ "workers": workers,
378
+ "scheduler": "resumable_subtree_queue",
379
+ "slice_nodes": slice_nodes,
380
+ "completed_batches": len(results),
381
+ "prefix_replay_nodes": sum(item["prefix_replay_nodes"] for item in results),
382
+ "new_search_nodes": sum(item["new_search_nodes"] for item in results),
383
+ "pending_subtrees": len(pending),
384
+ "peak_pending_subtrees": peak_pending,
385
+ "shards": results,
386
+ "unique_classes": len(records),
387
+ "weighted_product_representatives": observer.count,
388
+ "labeled_mappings": observer.count * merged.product_order,
389
+ "elapsed_seconds": time.perf_counter() - started,
390
+ }, merged
@@ -0,0 +1,213 @@
1
+ """Experimental exact terminal-H quotient for minimum Lewis mapping classes.
2
+
3
+ For a fixed heavy-atom bijection f, neutral monovalent terminal hydrogens have
4
+ minimum bond cost sum_i |h_R[i] - h_P[f(i)]|. This remains true with unequal H
5
+ inventories when isolated zero-state H placeholders form the missing-side
6
+ reservoir. Adding this term before heavy-map optimization gives a global
7
+ explicit-H Lewis minimum, unlike optimizing heavy bonds and lifting afterward.
8
+
9
+ Only minimum queries use the quotient. Numeric queries, nonordinary H atoms,
10
+ and annotated stereochemistry fall back to the full explicit-H native solver.
11
+ Every optimal integer H transport is lifted, then the common chemical frontend
12
+ performs exact full-ITS classification. This is an experimental reduction, not
13
+ a replacement for the public default or a polynomial all-class enumerator.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from dataclasses import replace
19
+ import time
20
+
21
+ from ....graph.labeled_graph import LabeledGraph
22
+ from ....reaction_search import _map_reaction_with_search
23
+ from ...slap.lap import chemical_distance
24
+ from ..hydrogen import enumerate_minimal_hydrogen_transfers
25
+ from .candidates import NativeEnumerationStop
26
+ from .search import enumerate_cpp_mappings
27
+
28
+
29
+ # Each unsupported hydrogen state needs a distinct, stable fallback reason.
30
+ def _quotient_graph(graph): # noqa: C901
31
+ """Return a cost-preserving reduced graph and original-index H groups."""
32
+ hydrogen = [i for i, label in enumerate(graph.labels) if label[0] == 1]
33
+ if not hydrogen:
34
+ return None, "no_hydrogen_reduction"
35
+ if any(graph.labels[i] != (1, 0) for i in hydrogen):
36
+ return None, "isotopic_hydrogen"
37
+ if any(graph.props["stereo"]) or any(graph.props["_stereo_anchor"]):
38
+ return None, "annotated_stereochemistry"
39
+ hydrogen_set = set(hydrogen)
40
+ heavy = [i for i in range(len(graph.labels)) if i not in hydrogen_set]
41
+ positions = {atom: i for i, atom in enumerate(heavy)}
42
+ groups = [[] for _ in heavy]
43
+ reservoir = []
44
+ for atom in hydrogen:
45
+ if graph.props["dummy"][atom]:
46
+ if graph.graph[atom]:
47
+ return None, "bonded_hydrogen_placeholder"
48
+ reservoir.append(atom)
49
+ continue
50
+ if graph.props["charge"][atom] or graph.props["radical"][atom]:
51
+ return None, "nonneutral_or_radical_hydrogen"
52
+ neighbors = graph.graph[atom]
53
+ if len(neighbors) != 1:
54
+ return None, "nonterminal_hydrogen"
55
+ parent, weight = next(iter(neighbors.items()))
56
+ if weight != 1 or parent not in positions or graph.labels[parent][0] == 0:
57
+ return None, "nonordinary_hydrogen_bond"
58
+ groups[positions[parent]].append(atom)
59
+ labels = [graph.labels[i] for i in heavy]
60
+ h_anchor = len(labels)
61
+ labels.append((0, -2))
62
+ adjacency = {
63
+ positions[i]: {positions[j]: weight for j, weight in graph.graph[i].items()
64
+ if j in positions}
65
+ for i in heavy
66
+ }
67
+ adjacency[h_anchor] = {}
68
+ for i, children in enumerate(groups):
69
+ if children:
70
+ adjacency[i][h_anchor] = adjacency[h_anchor][i] = len(children)
71
+ levels = {0} | {value for neighbors in adjacency.values() for value in neighbors.values()}
72
+ if len(levels) > 16:
73
+ return None, "quotient_bond_level_limit"
74
+ reduced = LabeledGraph(adjacency, labels)
75
+ for name in ("charge", "radical", "hcount", "stereo", "dummy", "_stereo_anchor"):
76
+ values = graph.props[name]
77
+ reduced.props[name] = [values[i] for i in heavy] + [
78
+ True if name == "dummy" else "" if name == "stereo" else 0
79
+ ]
80
+ return (reduced, heavy, groups, reservoir), None
81
+
82
+
83
+ # Quotient search and explicit-H fallback must share the remaining deadline.
84
+ def map_reaction_hydrogen_quotient( # noqa: C901
85
+ reaction,
86
+ *,
87
+ library_path,
88
+ CD="minimal",
89
+ CD_mode="exact",
90
+ balance="strict",
91
+ time_limit_seconds=10.0,
92
+ max_mappings=100_000,
93
+ max_bijections=None,
94
+ max_hydrogen_plans=10_000,
95
+ classification_max_search_nodes=100_000,
96
+ ):
97
+ """Map all minimum Lewis classes through a checked H-leaf quotient.
98
+
99
+ Full graph electron-anchor weights are retained verbatim, so heavy-atom
100
+ nonbonding electrons are never re-estimated after hydrogens are removed.
101
+ Unsupported inputs and numeric CD queries use the full explicit-H engine.
102
+ Assignment-space cardinality and its optional admission cap concern the
103
+ reduced problem; full labeled multiplicities remain explicitly unknown.
104
+ """
105
+
106
+ # Some RDKit stereo classes have no CIP label. Preserve the full search
107
+ # contract by declining reduction for every explicit chiral annotation.
108
+ raw_stereo = False
109
+ if isinstance(reaction, str) and ("@" in reaction or "|" in reaction):
110
+ raw_stereo = True
111
+
112
+ def search(graphs, **options):
113
+ elapsed_start = time.perf_counter()
114
+ seconds = options["time_limit_seconds"]
115
+ deadline = None if seconds is None else elapsed_start + seconds
116
+ options.pop("backend")
117
+ if options["CD"] != "minimal":
118
+ result = enumerate_cpp_mappings(graphs, **options)
119
+ statistics = dict(result.backend_statistics or {})
120
+ statistics["hydrogen_quotient_fallback"] = "numeric_distance_query"
121
+ return replace(result, backend_statistics=statistics)
122
+ prepared = [_quotient_graph(graph) for graph in graphs]
123
+ failure = next((reason for _, reason in prepared if reason), None)
124
+ if raw_stereo:
125
+ failure = "annotated_stereochemistry"
126
+ if failure:
127
+ options["time_limit_seconds"] = (
128
+ None if deadline is None else max(0, deadline - time.perf_counter())
129
+ )
130
+ result = enumerate_cpp_mappings(graphs, **options)
131
+ statistics = dict(result.backend_statistics or {})
132
+ statistics["hydrogen_quotient_fallback"] = failure
133
+ return replace(result, backend_statistics=statistics)
134
+ left, right = [value for value, _ in prepared]
135
+ reduced = left[0], right[0]
136
+ callback = options["mapping_callback"]
137
+ observed = plans_observed = 0
138
+
139
+ def remaining():
140
+ return None if deadline is None else max(0, deadline - time.perf_counter())
141
+
142
+ def lift(mapping, cost):
143
+ nonlocal observed, plans_observed
144
+ original_mapping = [-1] * len(graphs[0].labels)
145
+ left_heavy, right_heavy = left[1], right[1]
146
+ for i, atom in enumerate(left_heavy):
147
+ original_mapping[atom] = right_heavy[mapping[i]]
148
+ reactant_groups = [list(group) for group in left[2]] + [list(left[3])]
149
+ product_groups = [list(right[2][mapping[i]]) for i in range(len(left_heavy))]
150
+ product_groups.append(list(right[3]))
151
+ rcounts = [len(group) for group in reactant_groups]
152
+ pcounts = [len(group) for group in product_groups]
153
+ plans = enumerate_minimal_hydrogen_transfers(
154
+ rcounts, pcounts, range(len(rcounts)), max_plans=max_hydrogen_plans,
155
+ time_limit_seconds=remaining(),
156
+ )
157
+ for plan in plans.plans:
158
+ if deadline is not None and time.perf_counter() >= deadline:
159
+ raise NativeEnumerationStop("hydrogen_quotient:time_limit")
160
+ source = [list(group) for group in reactant_groups]
161
+ target = [list(group) for group in product_groups]
162
+ lifted = list(original_mapping)
163
+
164
+ def transfer(i, j, count):
165
+ for atom, image in zip(source[i][:count], target[j][:count]):
166
+ lifted[atom] = image
167
+ del source[i][:count]
168
+ del target[j][:count]
169
+
170
+ for i, count in enumerate(plan.preserved):
171
+ transfer(i, i, count)
172
+ for i, j, count in plan.transfers:
173
+ transfer(i, j, count)
174
+ if sorted(lifted) != list(range(len(lifted))):
175
+ raise RuntimeError("H quotient did not lift a compatible bijection")
176
+ if chemical_distance(graphs, lifted, False) != cost:
177
+ raise RuntimeError("H quotient cost differs from full Lewis cost")
178
+ observed += 1
179
+ callback(lifted, cost)
180
+ if options["max_mappings"] is not None and observed >= options["max_mappings"]:
181
+ raise NativeEnumerationStop("mapping_limit")
182
+ plans_observed += plans.observed_plan_count
183
+ if not plans.complete:
184
+ raise NativeEnumerationStop("hydrogen_quotient:" + plans.truncation_reason)
185
+
186
+ query = dict(options)
187
+ query.update(
188
+ mapping_callback=lift, collect_mappings=False, time_limit_seconds=remaining()
189
+ )
190
+ result = enumerate_cpp_mappings(reduced, **query)
191
+ statistics = dict(result.backend_statistics or {})
192
+ statistics.update(
193
+ hydrogen_quotient=True, full_nodes=len(graphs[0].labels),
194
+ quotient_nodes=len(reduced[0].labels), hydrogen_transport_plans=plans_observed,
195
+ mapping_count_scope="lifted_hydrogen_transport_candidates",
196
+ )
197
+ return replace(
198
+ result, selected_mapping_count=observed,
199
+ selected_labeled_mapping_count=None,
200
+ scope="full_lewis_minimum_via_terminal_hydrogen_quotient",
201
+ backend_statistics=statistics,
202
+ )
203
+
204
+ return _map_reaction_with_search(
205
+ reaction, search_function=search, backend="cpp", library_path=library_path,
206
+ CD=CD, CD_mode=CD_mode, objective="lewis", hydrogen="explicit", balance=balance,
207
+ time_limit_seconds=time_limit_seconds, max_mappings=max_mappings,
208
+ max_bijections=max_bijections, max_hydrogen_plans=max_hydrogen_plans,
209
+ classification_max_search_nodes=classification_max_search_nodes,
210
+ )
211
+
212
+
213
+ __all__ = ["map_reaction_hydrogen_quotient"]