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,995 @@
1
+ import networkx as nx
2
+ from dataclasses import dataclass
3
+ from copy import copy
4
+ from joblib import Parallel, delayed
5
+ from typing import (
6
+ Any,
7
+ Dict,
8
+ List,
9
+ Tuple,
10
+ Iterable,
11
+ Optional,
12
+ )
13
+
14
+ from synkit.IO.debug import setup_logging
15
+ from synkit.IO.chem_converter import detect_its_format
16
+ from synkit.Graph.ITS.its_construction import ITSConstruction
17
+ from synkit.Graph.ITS.its_decompose import get_rc, its_decompose
18
+ from synkit.Graph.ITS.its_reverter import ITSReverter
19
+ from synkit.Graph.ITS.rc_extractor import RCExtractor
20
+ from synkit.Graph.Hyrogen._misc import (
21
+ check_hcount_change,
22
+ get_priority,
23
+ )
24
+ from synkit.Graph.Hyrogen.hcompletion_algorithms import (
25
+ HydrogenCompletionAlgorithms,
26
+ ITSFormat,
27
+ ITSFormatInput,
28
+ _HydrogenTransferPlan,
29
+ )
30
+
31
+ logger = setup_logging()
32
+
33
+
34
+ @dataclass
35
+ class HCompletionResult:
36
+ """Container returned by graph-first hydrogen completion."""
37
+
38
+ its: Optional[nx.Graph]
39
+ rc: Optional[nx.Graph]
40
+ reactant: Optional[nx.Graph] = None
41
+ product: Optional[nx.Graph] = None
42
+ signature: Optional[str] = None
43
+ candidates: int = 0
44
+ reason: str = ""
45
+ format: str = "typesGH"
46
+ exhaustive: bool = True
47
+
48
+ @property
49
+ def ok(self) -> bool:
50
+ return (
51
+ isinstance(self.its, nx.Graph)
52
+ and isinstance(self.rc, nx.Graph)
53
+ and self.rc.number_of_nodes() > 0
54
+ )
55
+
56
+
57
+ class HComplete(HydrogenCompletionAlgorithms):
58
+ """Complete reaction-centre hydrogens in ITS graphs."""
59
+
60
+ TUPLE_NODE_ATTRS = [
61
+ "element",
62
+ "aromatic",
63
+ "hcount",
64
+ "charge",
65
+ "atom_map",
66
+ "lone_pairs",
67
+ "radical",
68
+ "valence_electrons",
69
+ ]
70
+ TUPLE_EDGE_ATTRS = [
71
+ "order",
72
+ "kekule_order",
73
+ "sigma_order",
74
+ "pi_order",
75
+ ]
76
+
77
+ @staticmethod
78
+ def process_single_graph_data(
79
+ graph_data: Dict[str, nx.Graph],
80
+ its_key: str = "ITS",
81
+ rc_key: str = "RC",
82
+ ignore_aromaticity: bool = False,
83
+ balance_its: bool = True,
84
+ get_priority_graph: bool = False,
85
+ max_hydrogen: int = 7,
86
+ format: ITSFormatInput = "auto",
87
+ max_candidates: Optional[int] = None,
88
+ ) -> Dict[str, Optional[nx.Graph]]:
89
+ """Processes a single graph data dictionary by modifying hydrogen
90
+ counts and other features based on configuration settings.
91
+
92
+ :param graph_data: Dictionary containing the graph data.
93
+ :type graph_data: Dict[str, nx.Graph]
94
+ :param its_key: Key where the ITS graph is stored.
95
+ :type its_key: str
96
+ :param rc_key: Key where the RC graph is stored.
97
+ :type rc_key: str
98
+ :param ignore_aromaticity: If True, aromaticity is ignored during processing. Default is False.
99
+ :type ignore_aromaticity: bool
100
+ :param balance_its: If True, the ITS is balanced. Default is True.
101
+ :type balance_its: bool
102
+ :param get_priority_graph: If True, priority is given to graph data during processing. Default is False.
103
+ :type get_priority_graph: bool
104
+ :param max_hydrogen: Maximum number of hydrogens that can be handled in the inference step.
105
+ :type max_hydrogen: int
106
+ :param format: ITS representation: "auto", "typesGH", or "tuple".
107
+ :type format: str
108
+ :param max_candidates: Optional cap for enumerated hydrogen assignments.
109
+ :type max_candidates: Optional[int]
110
+
111
+ :return: Dictionary with updated ITS and RC graph data, or None if processing fails.
112
+ :rtype: Dict[str, Optional[nx.Graph]]
113
+ """
114
+ graphs = copy(graph_data)
115
+ its = graphs.get(its_key, None)
116
+ result = HComplete.complete_its(
117
+ its,
118
+ ignore_aromaticity=ignore_aromaticity,
119
+ balance_its=balance_its,
120
+ get_priority_graph=get_priority_graph,
121
+ max_hydrogen=max_hydrogen,
122
+ format=format,
123
+ max_candidates=max_candidates,
124
+ )
125
+ graphs[its_key] = result.its if result.ok else None
126
+ graphs[rc_key] = result.rc if result.ok else None
127
+ return graphs
128
+
129
+ def process_graph_data_parallel(
130
+ self,
131
+ graph_data_list: List[Dict[str, nx.Graph]],
132
+ its_key: str = "ITS",
133
+ rc_key: str = "RC",
134
+ n_jobs: int = 1,
135
+ verbose: int = 0,
136
+ ignore_aromaticity: bool = False,
137
+ balance_its: bool = True,
138
+ get_priority_graph: bool = False,
139
+ max_hydrogen: int = 7,
140
+ format: ITSFormatInput = "auto",
141
+ max_candidates: Optional[int] = None,
142
+ ) -> List[Dict[str, Optional[nx.Graph]]]:
143
+ """Processes a list of graph data dictionaries in parallel to optimize
144
+ the hydrogen completion and other graph modifications.
145
+
146
+ :param graph_data_list: List of dictionaries containing the graph data.
147
+ :type graph_data_list: List[Dict[str, nx.Graph]]
148
+ :param its_key: Key where the ITS graph is stored.
149
+ :type its_key: str
150
+ :param rc_key: Key where the RC graph is stored.
151
+ :type rc_key: str
152
+ :param n_jobs: Number of parallel jobs to run.
153
+ :type n_jobs: int
154
+ :param verbose: Verbosity level for the parallel process.
155
+ :type verbose: int
156
+ :param ignore_aromaticity: If True, aromaticity is ignored during processing. Default is False.
157
+ :type ignore_aromaticity: bool
158
+ :param balance_its: If True, the ITS is balanced. Default is True.
159
+ :type balance_its: bool
160
+ :param get_priority_graph: If True, priority is given to graph data during processing. Default is False.
161
+ :type get_priority_graph: bool
162
+ :param max_hydrogen: Maximum number of hydrogens that can be handled in the inference step.
163
+ :type max_hydrogen: int
164
+ :param format: ITS representation: "auto", "typesGH", or "tuple".
165
+ :type format: str
166
+ :param max_candidates: Optional cap for enumerated hydrogen assignments.
167
+ :type max_candidates: Optional[int]
168
+
169
+ :return: List of dictionaries with updated ITS and RC graph data, or None if processing fails.
170
+ :rtype: List[Dict[str, Optional[nx.Graph]]]
171
+ """
172
+ processed_data = Parallel(n_jobs=n_jobs, verbose=verbose)(
173
+ delayed(self.process_single_graph_data)(
174
+ graph_data,
175
+ its_key,
176
+ rc_key,
177
+ ignore_aromaticity,
178
+ balance_its,
179
+ get_priority_graph,
180
+ max_hydrogen,
181
+ format,
182
+ max_candidates,
183
+ )
184
+ for graph_data in graph_data_list
185
+ )
186
+
187
+ return processed_data
188
+
189
+ @staticmethod
190
+ def complete_its(
191
+ its: nx.Graph,
192
+ ignore_aromaticity: bool = False,
193
+ balance_its: bool = True,
194
+ get_priority_graph: bool = False,
195
+ max_hydrogen: int = 7,
196
+ format: ITSFormatInput = "auto",
197
+ max_candidates: Optional[int] = None,
198
+ ) -> HCompletionResult:
199
+ """Complete hydrogens for a bare ITS graph.
200
+
201
+ This is the graph-first API. It accepts either legacy ``typesGH`` ITS
202
+ graphs or tuple ITS graphs and returns completed ITS/RC graphs without
203
+ requiring a dictionary entry wrapper.
204
+ """
205
+ if not isinstance(its, nx.Graph) or its.number_of_nodes() == 0:
206
+ return HCompletionResult(None, None, reason="invalid_its")
207
+
208
+ resolved_format = HComplete._resolve_format(its, format)
209
+ react_graph, prod_graph = HComplete._decompose_its(its, resolved_format)
210
+ hcount_change = check_hcount_change(react_graph, prod_graph)
211
+
212
+ if hcount_change == 0:
213
+ rc = HComplete._extract_rc(its, resolved_format)
214
+ if not HComplete._valid_rc(rc):
215
+ return HCompletionResult(
216
+ None,
217
+ None,
218
+ react_graph,
219
+ prod_graph,
220
+ candidates=1,
221
+ reason="empty_rc",
222
+ format=resolved_format,
223
+ )
224
+ return HCompletionResult(
225
+ its,
226
+ rc,
227
+ react_graph,
228
+ prod_graph,
229
+ signature=HComplete._rc_signature(rc, resolved_format),
230
+ candidates=1,
231
+ format=resolved_format,
232
+ )
233
+
234
+ if hcount_change > max_hydrogen:
235
+ return HCompletionResult(
236
+ None,
237
+ None,
238
+ react_graph,
239
+ prod_graph,
240
+ reason="max_hydrogen_exceeded",
241
+ format=resolved_format,
242
+ )
243
+
244
+ return HComplete._complete_from_side_graphs(
245
+ react_graph,
246
+ prod_graph,
247
+ ignore_aromaticity=ignore_aromaticity,
248
+ balance_its=balance_its,
249
+ get_priority_graph=get_priority_graph,
250
+ format=resolved_format,
251
+ max_candidates=max_candidates,
252
+ )
253
+
254
+ @staticmethod
255
+ def process_multiple_hydrogens(
256
+ graph_data: Dict[str, nx.Graph],
257
+ its_key: str,
258
+ rc_key: str,
259
+ react_graph: nx.Graph,
260
+ prod_graph: nx.Graph,
261
+ ignore_aromaticity: bool,
262
+ balance_its: bool,
263
+ get_priority_graph: bool = False,
264
+ format: ITSFormatInput = "typesGH",
265
+ max_candidates: Optional[int] = None,
266
+ ) -> Dict[str, Optional[nx.Graph]]:
267
+ """Handles significant hydrogen count changes between reactant and
268
+ product graphs, adjusting hydrogen nodes accordingly and assessing
269
+ graph equivalence.
270
+
271
+ :param graph_data: Dictionary containing the graph data.
272
+ :type graph_data: Dict[str, nx.Graph]
273
+ :param its_key: Key for the ITS graph in the dictionary.
274
+ :type its_key: str
275
+ :param rc_key: Key for the RC graph in the dictionary.
276
+ :type rc_key: str
277
+ :param react_graph: Graph representing the reactants.
278
+ :type react_graph: nx.Graph
279
+ :param prod_graph: Graph representing the products.
280
+ :type prod_graph: nx.Graph
281
+ :param ignore_aromaticity: If True, aromaticity will not be considered in processing.
282
+ :type ignore_aromaticity: bool
283
+ :param balance_its: If True, balances the ITS graph.
284
+ :type balance_its: bool
285
+ :param get_priority_graph: If True, processes graphs with priority considerations.
286
+ :type get_priority_graph: bool
287
+ :param format: ITS representation: "auto", "typesGH", or "tuple".
288
+ :type format: str
289
+ :param max_candidates: Optional cap for enumerated hydrogen assignments.
290
+ :type max_candidates: Optional[int]
291
+
292
+ :return: Updated graph dictionary with potentially modified ITS and RC graphs.
293
+ :rtype: Dict[str, Optional[nx.Graph]]
294
+ """
295
+ result = HComplete._complete_from_side_graphs(
296
+ react_graph,
297
+ prod_graph,
298
+ ignore_aromaticity=ignore_aromaticity,
299
+ balance_its=balance_its,
300
+ get_priority_graph=get_priority_graph,
301
+ format=HComplete._resolve_graph_pair_format(
302
+ react_graph, prod_graph, format
303
+ ),
304
+ max_candidates=max_candidates,
305
+ )
306
+ graph_data[its_key] = result.its if result.ok else None
307
+ graph_data[rc_key] = result.rc if result.ok else None
308
+ return graph_data
309
+
310
+ @staticmethod
311
+ def _complete_from_side_graphs(
312
+ react_graph: nx.Graph,
313
+ prod_graph: nx.Graph,
314
+ ignore_aromaticity: bool,
315
+ balance_its: bool,
316
+ get_priority_graph: bool,
317
+ format: ITSFormat,
318
+ max_candidates: Optional[int] = None,
319
+ ) -> HCompletionResult:
320
+ if get_priority_graph:
321
+ return HComplete._complete_priority_from_transfer_plans(
322
+ react_graph,
323
+ prod_graph,
324
+ ignore_aromaticity,
325
+ balance_its,
326
+ format,
327
+ max_candidates,
328
+ )
329
+ if format == "typesGH":
330
+ return HComplete._complete_typesgh_from_transfer_plans(
331
+ react_graph,
332
+ prod_graph,
333
+ ignore_aromaticity,
334
+ balance_its,
335
+ max_candidates,
336
+ )
337
+ return HComplete._complete_exact_from_transfer_plans(
338
+ react_graph,
339
+ prod_graph,
340
+ ignore_aromaticity,
341
+ balance_its,
342
+ format,
343
+ max_candidates,
344
+ )
345
+
346
+ @staticmethod
347
+ def _complete_typesgh_from_transfer_plans(
348
+ react_graph: nx.Graph,
349
+ prod_graph: nx.Graph,
350
+ ignore_aromaticity: bool,
351
+ balance_its: bool,
352
+ max_candidates: Optional[int],
353
+ ) -> HCompletionResult:
354
+ """Decide typesGH completion using an exact necessary RC projection."""
355
+ first_plan = None
356
+ first_projection = None
357
+ first_candidate = None
358
+ valid_seen = 0
359
+
360
+ for candidate_index, plan in enumerate(
361
+ HComplete._iter_hydrogen_transfer_plans(react_graph, prod_graph)
362
+ ):
363
+ if max_candidates is not None and candidate_index >= max_candidates:
364
+ return HComplete._candidate_limit_result(
365
+ react_graph, prod_graph, valid_seen, "typesGH"
366
+ )
367
+
368
+ projection = HComplete._typesgh_plan_comparison_graph(
369
+ react_graph,
370
+ prod_graph,
371
+ plan,
372
+ ignore_aromaticity,
373
+ )
374
+ if projection.number_of_nodes() == 0:
375
+ continue
376
+
377
+ valid_seen += 1
378
+ if first_plan is None:
379
+ first_plan = plan
380
+ first_projection = projection
381
+ continue
382
+
383
+ # Full typesGH RC isomorphism preserves this labeled projection.
384
+ # Its non-isomorphism is therefore a conclusive ambiguity witness.
385
+ if not HComplete._comparison_graphs_isomorphic(
386
+ first_projection, projection
387
+ ):
388
+ return HCompletionResult(
389
+ None,
390
+ None,
391
+ react_graph,
392
+ prod_graph,
393
+ candidates=valid_seen,
394
+ reason="non_equivariant_rc",
395
+ format="typesGH",
396
+ )
397
+
398
+ if first_candidate is None:
399
+ first_candidate = HComplete._materialize_transfer_candidate(
400
+ react_graph,
401
+ prod_graph,
402
+ first_plan,
403
+ ignore_aromaticity,
404
+ balance_its,
405
+ "typesGH",
406
+ )
407
+ if not HComplete._valid_rc(first_candidate[3]):
408
+ first_candidate = None
409
+ continue
410
+
411
+ candidate = HComplete._materialize_transfer_candidate(
412
+ react_graph,
413
+ prod_graph,
414
+ plan,
415
+ ignore_aromaticity,
416
+ balance_its,
417
+ "typesGH",
418
+ )
419
+ if not HComplete._valid_rc(candidate[3]):
420
+ continue
421
+ if (
422
+ HComplete._equivariant_count(
423
+ [first_candidate[3], candidate[3]], "typesGH"
424
+ )
425
+ != 1
426
+ ):
427
+ return HCompletionResult(
428
+ None,
429
+ None,
430
+ react_graph,
431
+ prod_graph,
432
+ candidates=valid_seen,
433
+ reason="non_equivariant_rc",
434
+ format="typesGH",
435
+ )
436
+
437
+ if first_plan is None:
438
+ return HCompletionResult(
439
+ None,
440
+ None,
441
+ react_graph,
442
+ prod_graph,
443
+ candidates=valid_seen,
444
+ reason="no_valid_candidate",
445
+ format="typesGH",
446
+ )
447
+ if first_candidate is None:
448
+ first_candidate = HComplete._materialize_transfer_candidate(
449
+ react_graph,
450
+ prod_graph,
451
+ first_plan,
452
+ ignore_aromaticity,
453
+ balance_its,
454
+ "typesGH",
455
+ )
456
+ return HComplete._completion_result(
457
+ first_candidate,
458
+ react_graph,
459
+ prod_graph,
460
+ valid_seen,
461
+ "typesGH",
462
+ )
463
+
464
+ @staticmethod
465
+ def _complete_exact_from_transfer_plans(
466
+ react_graph: nx.Graph,
467
+ prod_graph: nx.Graph,
468
+ ignore_aromaticity: bool,
469
+ balance_its: bool,
470
+ format: ITSFormat,
471
+ max_candidates: Optional[int],
472
+ ) -> HCompletionResult:
473
+ """Decide completion solely through materialized exact RC isomorphism."""
474
+ first = None
475
+ valid_seen = 0
476
+ for candidate_index, plan in enumerate(
477
+ HComplete._iter_hydrogen_transfer_plans(react_graph, prod_graph)
478
+ ):
479
+ if max_candidates is not None and candidate_index >= max_candidates:
480
+ return HComplete._candidate_limit_result(
481
+ react_graph, prod_graph, valid_seen, format
482
+ )
483
+ candidate = HComplete._materialize_transfer_candidate(
484
+ react_graph,
485
+ prod_graph,
486
+ plan,
487
+ ignore_aromaticity,
488
+ balance_its,
489
+ format,
490
+ )
491
+ if not HComplete._valid_rc(candidate[3]):
492
+ continue
493
+ valid_seen += 1
494
+ if first is None:
495
+ first = candidate
496
+ continue
497
+ if HComplete._equivariant_count([first[3], candidate[3]], format) != 1:
498
+ return HCompletionResult(
499
+ None,
500
+ None,
501
+ react_graph,
502
+ prod_graph,
503
+ candidates=valid_seen,
504
+ reason="non_equivariant_rc",
505
+ format=format,
506
+ )
507
+ return HComplete._completion_result(
508
+ first,
509
+ react_graph,
510
+ prod_graph,
511
+ valid_seen,
512
+ format,
513
+ )
514
+
515
+ @staticmethod
516
+ def _complete_priority_from_transfer_plans(
517
+ react_graph: nx.Graph,
518
+ prod_graph: nx.Graph,
519
+ ignore_aromaticity: bool,
520
+ balance_its: bool,
521
+ format: ITSFormat,
522
+ max_candidates: Optional[int],
523
+ ) -> HCompletionResult:
524
+ candidates = []
525
+ valid_seen = 0
526
+ for candidate_index, plan in enumerate(
527
+ HComplete._iter_hydrogen_transfer_plans(react_graph, prod_graph)
528
+ ):
529
+ if max_candidates is not None and candidate_index >= max_candidates:
530
+ return HComplete._candidate_limit_result(
531
+ react_graph, prod_graph, valid_seen, format
532
+ )
533
+ candidate = HComplete._materialize_transfer_candidate(
534
+ react_graph,
535
+ prod_graph,
536
+ plan,
537
+ ignore_aromaticity,
538
+ balance_its,
539
+ format,
540
+ )
541
+ if not HComplete._valid_rc(candidate[3]):
542
+ continue
543
+ valid_seen += 1
544
+ candidates.append(candidate)
545
+ selected = HComplete._select_priority_candidate(candidates, format)
546
+ return HComplete._completion_result(
547
+ selected,
548
+ react_graph,
549
+ prod_graph,
550
+ valid_seen,
551
+ format,
552
+ )
553
+
554
+ @staticmethod
555
+ def _materialize_transfer_candidate(
556
+ react_graph: nx.Graph,
557
+ prod_graph: nx.Graph,
558
+ plan: _HydrogenTransferPlan,
559
+ ignore_aromaticity: bool,
560
+ balance_its: bool,
561
+ format: ITSFormat,
562
+ ) -> Tuple[nx.Graph, nx.Graph, nx.Graph, nx.Graph, str]:
563
+ react, prod = HComplete._realize_hydrogen_transfer_plan(
564
+ react_graph, prod_graph, plan
565
+ )
566
+ its = HComplete._construct_its(
567
+ react,
568
+ prod,
569
+ ignore_aromaticity,
570
+ balance_its,
571
+ format,
572
+ )
573
+ rc = HComplete._extract_rc(its, format)
574
+ signature = HComplete._rc_signature(rc, format)
575
+ return react, prod, its, rc, signature
576
+
577
+ @staticmethod
578
+ def _candidate_limit_result(
579
+ react_graph: nx.Graph,
580
+ prod_graph: nx.Graph,
581
+ candidates: int,
582
+ format: ITSFormat,
583
+ ) -> HCompletionResult:
584
+ return HCompletionResult(
585
+ None,
586
+ None,
587
+ react_graph,
588
+ prod_graph,
589
+ candidates=candidates,
590
+ reason="max_candidates_reached",
591
+ format=format,
592
+ exhaustive=False,
593
+ )
594
+
595
+ @staticmethod
596
+ def _completion_result(
597
+ selected: Optional[Tuple[nx.Graph, nx.Graph, nx.Graph, nx.Graph, str]],
598
+ react_graph: nx.Graph,
599
+ prod_graph: nx.Graph,
600
+ candidates: int,
601
+ format: ITSFormat,
602
+ ) -> HCompletionResult:
603
+ if selected is None or not HComplete._valid_rc(selected[3]):
604
+ return HCompletionResult(
605
+ None,
606
+ None,
607
+ react_graph,
608
+ prod_graph,
609
+ candidates=candidates,
610
+ reason="no_valid_candidate",
611
+ format=format,
612
+ )
613
+ react, prod, its, rc, sig = selected
614
+ return HCompletionResult(
615
+ its,
616
+ rc,
617
+ react,
618
+ prod,
619
+ signature=sig,
620
+ candidates=candidates,
621
+ format=format,
622
+ )
623
+
624
+ @staticmethod
625
+ def _select_priority_candidate(
626
+ candidates: List[Tuple[nx.Graph, nx.Graph, nx.Graph, nx.Graph, str]],
627
+ format: ITSFormat,
628
+ ) -> Optional[Tuple[nx.Graph, nx.Graph, nx.Graph, nx.Graph, str]]:
629
+ if not candidates:
630
+ return None
631
+
632
+ rc_list = [candidate[3] for candidate in candidates]
633
+ rc_sig = [candidate[4] for candidate in candidates]
634
+
635
+ if len(set(rc_sig)) == 1:
636
+ equivariant = HComplete._equivariant_count(rc_list, format)
637
+ else:
638
+ equivariant = 0
639
+
640
+ if equivariant == len(rc_list) - 1:
641
+ return candidates[0]
642
+
643
+ priority_indices = get_priority(rc_list)
644
+ priority_candidates = [candidates[i] for i in priority_indices]
645
+ priority_rc = [candidate[3] for candidate in priority_candidates]
646
+ priority_sig = [candidate[4] for candidate in priority_candidates]
647
+
648
+ if len(set(priority_sig)) == 1:
649
+ equivariant = HComplete._equivariant_count(priority_rc, format)
650
+ if equivariant == len(priority_rc) - 1:
651
+ return priority_candidates[0]
652
+ return None
653
+
654
+ @staticmethod
655
+ def add_hydrogen_nodes_multiple(
656
+ react_graph: nx.Graph,
657
+ prod_graph: nx.Graph,
658
+ ignore_aromaticity: bool,
659
+ balance_its: bool,
660
+ get_priority_graph: bool = False,
661
+ format: ITSFormatInput = "typesGH",
662
+ max_candidates: Optional[int] = None,
663
+ ) -> List[Tuple[nx.Graph, nx.Graph, nx.Graph, nx.Graph, str]]:
664
+ """Generates multiple permutations of reactant and product graphs by
665
+ adjusting hydrogen counts, exploring all possible configurations of
666
+ hydrogen node additions or removals.
667
+
668
+ :param react_graph: The reactant graph.
669
+ :type react_graph: nx.Graph
670
+ :param prod_graph: The product graph.
671
+ :type prod_graph: nx.Graph
672
+ :param ignore_aromaticity: If True, aromaticity is ignored.
673
+ :type ignore_aromaticity: bool
674
+ :param balance_its: If True, attempts to balance the ITS by adjusting hydrogen nodes.
675
+ :type balance_its: bool
676
+ :param get_priority_graph: If True, additional priority-based processing
677
+ is applied to select optimal graph configurations.
678
+ :type get_priority_graph: bool
679
+ :param format: ITS representation: "auto", "typesGH", or "tuple".
680
+ :type format: str
681
+ :param max_candidates: Optional cap for enumerated hydrogen assignments.
682
+ :type max_candidates: Optional[int]
683
+
684
+ :return: Candidate reactant, product, ITS, RC, and RC-signature tuples.
685
+ :rtype: List[Tuple[nx.Graph, nx.Graph, nx.Graph, nx.Graph, str]]
686
+ """
687
+ resolved_format = HComplete._resolve_graph_pair_format(
688
+ react_graph, prod_graph, format
689
+ )
690
+ updated_graphs = []
691
+
692
+ for candidate in HComplete._iter_hydrogen_node_completions(
693
+ react_graph,
694
+ prod_graph,
695
+ ignore_aromaticity,
696
+ balance_its,
697
+ format=resolved_format,
698
+ max_candidates=max_candidates,
699
+ ):
700
+ if get_priority_graph is False and updated_graphs:
701
+ previous = updated_graphs[-1]
702
+ if (
703
+ candidate[-1] != previous[-1]
704
+ or HComplete._equivariant_count(
705
+ [previous[3], candidate[3]], resolved_format
706
+ )
707
+ != 1
708
+ ):
709
+ return []
710
+ updated_graphs.append(candidate)
711
+ return updated_graphs
712
+
713
+ @staticmethod
714
+ def _hcount_change_atoms(
715
+ react_graph: nx.Graph,
716
+ prod_graph: nx.Graph,
717
+ ) -> Tuple[List[int], List[int]]:
718
+ hydrogen_nodes_break: List[int] = []
719
+ hydrogen_nodes_form: List[int] = []
720
+
721
+ for node_id in sorted(set(react_graph.nodes) & set(prod_graph.nodes)):
722
+ react_hcount = react_graph.nodes[node_id].get("hcount", 0)
723
+ prod_hcount = prod_graph.nodes[node_id].get("hcount", 0)
724
+ hcount_diff = react_hcount - prod_hcount
725
+
726
+ if hcount_diff > 0:
727
+ hydrogen_nodes_break.extend([node_id] * hcount_diff)
728
+ elif hcount_diff < 0:
729
+ hydrogen_nodes_form.extend([node_id] * -hcount_diff)
730
+
731
+ return hydrogen_nodes_break, hydrogen_nodes_form
732
+
733
+ @staticmethod
734
+ def _resolve_format(its: nx.Graph, format: ITSFormatInput) -> ITSFormat:
735
+ if format == "auto":
736
+ return detect_its_format(its)
737
+ if format in ("typesGH", "tuple"):
738
+ return format
739
+ raise ValueError("format must be one of 'auto', 'typesGH', or 'tuple'.")
740
+
741
+ @staticmethod
742
+ def _resolve_graph_pair_format(
743
+ react_graph: nx.Graph,
744
+ prod_graph: nx.Graph,
745
+ format: ITSFormatInput,
746
+ ) -> ITSFormat:
747
+ if format == "auto":
748
+ return "typesGH"
749
+ if format in ("typesGH", "tuple"):
750
+ return format
751
+ raise ValueError("format must be one of 'auto', 'typesGH', or 'tuple'.")
752
+
753
+ @staticmethod
754
+ def _decompose_its(its: nx.Graph, format: ITSFormat) -> Tuple[nx.Graph, nx.Graph]:
755
+ if format == "typesGH":
756
+ return its_decompose(its)
757
+
758
+ reverter = ITSReverter(its)
759
+ return reverter.to_reactant_graph(), reverter.to_product_graph()
760
+
761
+ @staticmethod
762
+ def _construct_its(
763
+ react_graph: nx.Graph,
764
+ prod_graph: nx.Graph,
765
+ ignore_aromaticity: bool,
766
+ balance_its: bool,
767
+ format: ITSFormat,
768
+ ) -> nx.Graph:
769
+ if format == "typesGH":
770
+ return ITSConstruction().ITSGraph(
771
+ react_graph,
772
+ prod_graph,
773
+ ignore_aromaticity=ignore_aromaticity,
774
+ balance_its=balance_its,
775
+ )
776
+
777
+ return ITSConstruction.construct(
778
+ react_graph,
779
+ prod_graph,
780
+ ignore_aromaticity=ignore_aromaticity,
781
+ balance_its=balance_its,
782
+ store=True,
783
+ node_attrs=HComplete.TUPLE_NODE_ATTRS,
784
+ edge_attrs=HComplete.TUPLE_EDGE_ATTRS,
785
+ )
786
+
787
+ @staticmethod
788
+ def _extract_rc(its: nx.Graph, format: ITSFormat) -> nx.Graph:
789
+ if format == "typesGH":
790
+ return get_rc(its)
791
+ return RCExtractor(preserve_full_attrs=True).extract(its)
792
+
793
+ @staticmethod
794
+ def _valid_rc(rc: Optional[nx.Graph]) -> bool:
795
+ return isinstance(rc, nx.Graph) and rc.number_of_nodes() > 0
796
+
797
+ @staticmethod
798
+ def _candidate_edge_keys(
799
+ react_graph: nx.Graph, prod_graph: nx.Graph
800
+ ) -> List[Tuple[int, int]]:
801
+ edge_keys = {tuple(sorted(edge)) for edge in react_graph.edges}
802
+ edge_keys.update(tuple(sorted(edge)) for edge in prod_graph.edges)
803
+ return sorted(edge_keys)
804
+
805
+ @staticmethod
806
+ def _candidate_edge_order_pair(
807
+ react_graph: nx.Graph,
808
+ prod_graph: nx.Graph,
809
+ u: int,
810
+ v: int,
811
+ ) -> Tuple[Any, Any]:
812
+ return (
813
+ HComplete._candidate_edge_attr(react_graph, u, v, "order", 0.0),
814
+ HComplete._candidate_edge_attr(prod_graph, u, v, "order", 0.0),
815
+ )
816
+
817
+ @staticmethod
818
+ def _candidate_standard_order(
819
+ react_order: Any,
820
+ prod_order: Any,
821
+ ignore_aromaticity: bool,
822
+ ) -> Any:
823
+ try:
824
+ standard_order = react_order - prod_order
825
+ except TypeError:
826
+ return 0
827
+ if ignore_aromaticity and abs(standard_order) < 1:
828
+ return 0
829
+ return standard_order
830
+
831
+ @staticmethod
832
+ def _candidate_typesgh_hh_pair(react_graph: nx.Graph, u: int, v: int) -> bool:
833
+ return (
834
+ HComplete._candidate_node_attr(react_graph, u, "element") == "H"
835
+ and HComplete._candidate_node_attr(react_graph, v, "element") == "H"
836
+ )
837
+
838
+ @staticmethod
839
+ def _candidate_tuple_node_changed(
840
+ react_graph: nx.Graph, prod_graph: nx.Graph, node_id: int
841
+ ) -> bool:
842
+ for attr in ("element", "charge", "radical", "valence_electrons"):
843
+ pair = (
844
+ HComplete._candidate_node_attr(react_graph, node_id, attr),
845
+ HComplete._candidate_node_attr(prod_graph, node_id, attr),
846
+ )
847
+ if RCExtractor._pair_diff(pair):
848
+ return True
849
+
850
+ hcount_pair = (
851
+ HComplete._candidate_node_attr(react_graph, node_id, "hcount"),
852
+ HComplete._candidate_node_attr(prod_graph, node_id, "hcount"),
853
+ )
854
+ if RCExtractor._hcount_diff(hcount_pair):
855
+ return True
856
+
857
+ lp_pair = (
858
+ HComplete._candidate_lone_pair_attr(react_graph, node_id),
859
+ HComplete._candidate_lone_pair_attr(prod_graph, node_id),
860
+ )
861
+ return RCExtractor._pair_diff(lp_pair)
862
+
863
+ @staticmethod
864
+ def _add_candidate_comparison_node(
865
+ graph: nx.Graph,
866
+ react_graph: nx.Graph,
867
+ prod_graph: nx.Graph,
868
+ node_id: int,
869
+ format: ITSFormat,
870
+ ) -> None:
871
+ if graph.has_node(node_id):
872
+ return
873
+
874
+ if format == "typesGH":
875
+ element = HComplete._candidate_node_attr(react_graph, node_id, "element")
876
+ charge = HComplete._candidate_node_attr(react_graph, node_id, "charge")
877
+ else:
878
+ element = (
879
+ HComplete._candidate_node_attr(react_graph, node_id, "element"),
880
+ HComplete._candidate_node_attr(prod_graph, node_id, "element"),
881
+ )
882
+ charge = (
883
+ HComplete._candidate_node_attr(react_graph, node_id, "charge"),
884
+ HComplete._candidate_node_attr(prod_graph, node_id, "charge"),
885
+ )
886
+
887
+ cmp_element = HComplete._comparison_node_value(element)
888
+ cmp_charge = HComplete._comparison_pair_value(charge)
889
+ graph.add_node(
890
+ node_id,
891
+ cmp_element=cmp_element,
892
+ cmp_charge=cmp_charge,
893
+ cmp_node=f"{cmp_element}|{cmp_charge}",
894
+ )
895
+
896
+ @staticmethod
897
+ def _add_candidate_comparison_edge(
898
+ graph: nx.Graph,
899
+ u: int,
900
+ v: int,
901
+ react_order: Any,
902
+ prod_order: Any,
903
+ ) -> None:
904
+ graph.add_edge(
905
+ u,
906
+ v,
907
+ cmp_order=HComplete._comparison_pair_value((react_order, prod_order)),
908
+ )
909
+
910
+ @staticmethod
911
+ def _candidate_node_attr(
912
+ graph: nx.Graph, node_id: int, attr: str, default: Any = None
913
+ ) -> Any:
914
+ if default is None:
915
+ default = HComplete._candidate_default(
916
+ ITSConstruction.CORE_NODE_DEFAULTS, attr
917
+ )
918
+ if node_id not in graph:
919
+ return default
920
+ return graph.nodes[node_id].get(attr, default)
921
+
922
+ @staticmethod
923
+ def _candidate_edge_attr(
924
+ graph: nx.Graph,
925
+ u: int,
926
+ v: int,
927
+ attr: str,
928
+ default: Any = None,
929
+ ) -> Any:
930
+ if default is None:
931
+ default = HComplete._candidate_default(
932
+ ITSConstruction.CORE_EDGE_DEFAULTS, attr
933
+ )
934
+ if not graph.has_edge(u, v):
935
+ return default
936
+ return graph.edges[u, v].get(attr, default)
937
+
938
+ @staticmethod
939
+ def _candidate_lone_pair_attr(graph: nx.Graph, node_id: int) -> Any:
940
+ if node_id not in graph:
941
+ return HComplete._candidate_default(
942
+ ITSConstruction.CORE_NODE_DEFAULTS, "lone_pairs"
943
+ )
944
+ attrs = graph.nodes[node_id]
945
+ return attrs.get("lone_pairs", attrs.get("lp", 0))
946
+
947
+ @staticmethod
948
+ def _candidate_default(defaults: Dict[str, Any], attr: str) -> Any:
949
+ value = defaults.get(attr)
950
+ return value() if callable(value) else value
951
+
952
+ @staticmethod
953
+ def add_hydrogen_nodes_multiple_utils(
954
+ graph: nx.Graph,
955
+ node_id_pairs: Iterable[Tuple[int, int]],
956
+ atom_map_update: bool = True,
957
+ ) -> nx.Graph:
958
+ """Creates and returns a new graph with added hydrogen nodes based on
959
+ the input graph and node ID pairs.
960
+
961
+ :param graph: The base graph to which the nodes will be added.
962
+ :type graph: nx.Graph
963
+ :param node_id_pairs: Pairs of node IDs (original node, new
964
+ hydrogen node) to link with hydrogen.
965
+ :type node_id_pairs: Iterable[Tuple[int, int]]
966
+ :param atom_map_update: If True, update the 'atom_map' attribute with the new
967
+ hydrogen node ID; otherwise, retain the original node's 'atom_map'.
968
+ :type atom_map_update: bool
969
+
970
+ :return: A new graph instance with the added hydrogen nodes.
971
+ :rtype: nx.Graph
972
+ """
973
+ new_graph = graph.copy()
974
+ for node_id, new_hydrogen_node_id in node_id_pairs:
975
+ atom_map_val = (
976
+ new_hydrogen_node_id
977
+ if atom_map_update
978
+ else new_graph.nodes[node_id].get("atom_map", 0)
979
+ )
980
+ new_graph.add_node(
981
+ new_hydrogen_node_id,
982
+ charge=0,
983
+ hcount=0,
984
+ aromatic=False,
985
+ element="H",
986
+ atom_map=atom_map_val,
987
+ )
988
+ new_graph.add_edge(
989
+ node_id,
990
+ new_hydrogen_node_id,
991
+ order=1.0,
992
+ bond_type="SINGLE",
993
+ )
994
+ new_graph.nodes[node_id]["hcount"] -= 1
995
+ return new_graph