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,662 @@
1
+ """RDKit conversion for SynKit relative stereo descriptors."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Iterable
6
+
7
+ from rdkit import Chem
8
+
9
+ from .descriptors import (
10
+ AtropBondStereo,
11
+ OctahedralStereo,
12
+ PlanarBondStereo,
13
+ SquarePlanarStereo,
14
+ StereoValue,
15
+ TetrahedralStereo,
16
+ TrigonalBipyramidalStereo,
17
+ descriptor_id,
18
+ virtual_reference,
19
+ )
20
+
21
+ # RDKit's square-planar permutation numbers describe arrangements relative to
22
+ # the atom's current local neighbor order; they are not global stereo labels.
23
+ # These position maps are the SP1/SP2/SP3 ligand-numbering table from RDKit's
24
+ # non-tetrahedral SMILES documentation. SynKit stores the resulting cyclic
25
+ # ligand order, whose identity is invariant under D4 rotations/reflections.
26
+ _SQUARE_PLANAR_POSITION_MAPS = {
27
+ 1: (0, 1, 2, 3),
28
+ 2: (0, 2, 1, 3),
29
+ 3: (0, 1, 3, 2),
30
+ }
31
+
32
+ # A/B are the two axial positions and C/D/E are the cyclic equatorial
33
+ # positions in RDKit's documented TB table. Each value maps RDKit's current
34
+ # local neighbor order into that positional SynKit representation.
35
+ _TRIGONAL_BIPYRAMIDAL_POSITION_MAPS = {
36
+ 1: (0, 4, 1, 2, 3),
37
+ 2: (0, 4, 1, 3, 2),
38
+ 3: (0, 3, 1, 2, 4),
39
+ 4: (0, 3, 1, 4, 2),
40
+ 5: (0, 2, 1, 3, 4),
41
+ 6: (0, 2, 1, 4, 3),
42
+ 7: (0, 1, 2, 3, 4),
43
+ 8: (0, 1, 2, 4, 3),
44
+ 9: (1, 4, 0, 2, 3),
45
+ 10: (1, 3, 0, 2, 4),
46
+ 11: (1, 4, 0, 3, 2),
47
+ 12: (1, 3, 0, 4, 2),
48
+ 13: (1, 2, 0, 3, 4),
49
+ 14: (1, 2, 0, 4, 3),
50
+ 15: (2, 4, 0, 1, 3),
51
+ 16: (2, 3, 0, 1, 4),
52
+ 17: (3, 4, 0, 1, 2),
53
+ 18: (3, 4, 0, 2, 1),
54
+ 19: (2, 3, 0, 4, 1),
55
+ 20: (2, 4, 0, 3, 1),
56
+ }
57
+
58
+ # A/B are an opposite pair and C/D/E/F are the cyclic square in RDKit's
59
+ # documented OH table. As for SP/TBP, these are local-order transforms, not
60
+ # global labels and not SynKit descriptor identity.
61
+ _OCTAHEDRAL_POSITION_MAPS = {
62
+ 1: (0, 5, 1, 2, 3, 4),
63
+ 2: (0, 5, 1, 4, 3, 2),
64
+ 3: (0, 4, 1, 2, 3, 5),
65
+ 4: (0, 5, 1, 2, 4, 3),
66
+ 5: (0, 4, 1, 2, 5, 3),
67
+ 6: (0, 3, 1, 2, 4, 5),
68
+ 7: (0, 3, 1, 2, 5, 4),
69
+ 8: (0, 5, 1, 3, 2, 4),
70
+ 9: (0, 4, 1, 3, 2, 5),
71
+ 10: (0, 5, 1, 4, 2, 3),
72
+ 11: (0, 4, 1, 5, 2, 3),
73
+ 12: (0, 3, 1, 4, 2, 5),
74
+ 13: (0, 3, 1, 5, 2, 4),
75
+ 14: (0, 5, 1, 3, 4, 2),
76
+ 15: (0, 4, 1, 3, 5, 2),
77
+ 16: (0, 4, 1, 5, 3, 2),
78
+ 17: (0, 3, 1, 4, 5, 2),
79
+ 18: (0, 3, 1, 5, 4, 2),
80
+ 19: (0, 2, 1, 3, 4, 5),
81
+ 20: (0, 2, 1, 3, 5, 4),
82
+ 21: (0, 2, 1, 4, 3, 5),
83
+ 22: (0, 2, 1, 5, 3, 4),
84
+ 23: (0, 2, 1, 4, 5, 3),
85
+ 24: (0, 2, 1, 5, 4, 3),
86
+ 25: (0, 1, 2, 3, 4, 5),
87
+ 26: (0, 1, 2, 3, 5, 4),
88
+ 27: (0, 1, 2, 4, 3, 5),
89
+ 28: (0, 1, 2, 5, 3, 4),
90
+ 29: (0, 1, 2, 4, 5, 3),
91
+ 30: (0, 1, 2, 5, 4, 3),
92
+ }
93
+
94
+
95
+ def _square_planar_permutations(mol: Chem.Mol) -> dict[int, int]:
96
+ """Return RDKit square-planar permutations keyed by center atom index."""
97
+ return {
98
+ int(info.centeredOn): int(info.permutation)
99
+ for info in Chem.FindPotentialStereo(mol)
100
+ if info.type == Chem.StereoType.Atom_SquarePlanar
101
+ and mol.GetAtomWithIdx(int(info.centeredOn)).GetChiralTag()
102
+ == Chem.ChiralType.CHI_SQUAREPLANAR
103
+ }
104
+
105
+
106
+ def _trigonal_bipyramidal_permutations(mol: Chem.Mol) -> dict[int, int]:
107
+ """Return RDKit TBP permutations keyed by center atom index."""
108
+ return {
109
+ int(info.centeredOn): int(info.permutation)
110
+ for info in Chem.FindPotentialStereo(mol)
111
+ if info.type == Chem.StereoType.Atom_TrigonalBipyramidal
112
+ and mol.GetAtomWithIdx(int(info.centeredOn)).GetChiralTag()
113
+ == Chem.ChiralType.CHI_TRIGONALBIPYRAMIDAL
114
+ }
115
+
116
+
117
+ def _octahedral_permutations(mol: Chem.Mol) -> dict[int, int]:
118
+ """Return RDKit octahedral permutations keyed by center atom index."""
119
+ return {
120
+ int(info.centeredOn): int(info.permutation)
121
+ for info in Chem.FindPotentialStereo(mol)
122
+ if info.type == Chem.StereoType.Atom_Octahedral
123
+ and mol.GetAtomWithIdx(int(info.centeredOn)).GetChiralTag()
124
+ == Chem.ChiralType.CHI_OCTAHEDRAL
125
+ }
126
+
127
+
128
+ def _non_tetrahedral_local_references(
129
+ atom: Chem.Atom,
130
+ ids: dict[int, int],
131
+ center: int,
132
+ coordination: int,
133
+ descriptor_name: str,
134
+ ) -> tuple[int | str, ...]:
135
+ """Resolve a local ligand order without inventing vacant sites."""
136
+ refs: list[int | str] = [ids[neighbor.GetIdx()] for neighbor in atom.GetNeighbors()]
137
+ if len(refs) == coordination:
138
+ return tuple(refs)
139
+ hidden_hydrogens = int(atom.GetNumExplicitHs()) + int(atom.GetNumImplicitHs())
140
+ if len(refs) + hidden_hydrogens != coordination:
141
+ raise ValueError(
142
+ f"{descriptor_name} center {center} requires exactly "
143
+ f"{coordination} represented "
144
+ "or hydrogen ligands; vacant or otherwise missing coordination "
145
+ "sites cannot be inferred safely."
146
+ )
147
+ refs.extend(virtual_reference("H", center) for _ in range(hidden_hydrogens))
148
+ return tuple(refs)
149
+
150
+
151
+ def _atrop_end_references(
152
+ atom: Chem.Atom,
153
+ other_index: int,
154
+ ids: dict[int, int],
155
+ center: int,
156
+ ) -> tuple[int | str, int | str]:
157
+ """Resolve two local atrop substituents without guessing vacant sites."""
158
+ refs: list[int | str] = [
159
+ ids[neighbor.GetIdx()]
160
+ for neighbor in atom.GetNeighbors()
161
+ if neighbor.GetIdx() != other_index
162
+ ]
163
+ if len(refs) == 2:
164
+ return tuple(refs) # type: ignore[return-value]
165
+ hidden_hydrogens = int(atom.GetNumExplicitHs()) + int(atom.GetNumImplicitHs())
166
+ if len(refs) + hidden_hydrogens != 2:
167
+ raise ValueError(
168
+ f"Atrop-bond end {center} requires exactly two represented or "
169
+ "hydrogen substituents."
170
+ )
171
+ refs.extend(virtual_reference("H", center) for _ in range(hidden_hydrogens))
172
+ return tuple(refs) # type: ignore[return-value]
173
+
174
+
175
+ def _atom_ids(mol: Chem.Mol, *, require_maps: bool) -> dict[int, int]:
176
+ values = {}
177
+ for atom in mol.GetAtoms():
178
+ value = int(atom.GetAtomMapNum()) if require_maps else atom.GetIdx() + 1
179
+ if value <= 0:
180
+ raise ValueError("Stereo conversion requires non-zero atom maps.")
181
+ if value in values.values():
182
+ raise ValueError("Stereo conversion requires unique atom maps.")
183
+ values[atom.GetIdx()] = value
184
+ return values
185
+
186
+
187
+ def _virtual_ligand(atom: Chem.Atom, center: int) -> str:
188
+ """Resolve RDKit's unrepresented ligand without guessing its identity."""
189
+ hidden_hydrogens = int(atom.GetNumExplicitHs()) + int(atom.GetNumImplicitHs())
190
+ if hidden_hydrogens > 0:
191
+ return virtual_reference("H", center)
192
+
193
+ # Keep this import local: MolToGraph calls this adapter while constructing
194
+ # its registry. Its Lewis estimate supplies the electronic distinction
195
+ # that RDKit's absent-neighbor representation itself does not encode.
196
+ from synkit.IO.mol_to_graph import MolToGraph
197
+
198
+ if MolToGraph.estimate_lone_pairs(atom) > 0:
199
+ return virtual_reference("LP", center)
200
+ raise ValueError(
201
+ f"Missing stereo ligand at atom {center} is neither an unrepresented "
202
+ "hydrogen nor a Lewis lone pair."
203
+ )
204
+
205
+
206
+ def descriptors_from_rdkit(
207
+ mol: Chem.Mol, *, require_atom_maps: bool = True
208
+ ) -> dict[str, StereoValue]:
209
+ """Extract SynKit's RDKit-interoperable stereo descriptor subset."""
210
+ Chem.AssignStereochemistry(mol, cleanIt=False, force=True)
211
+ ids = _atom_ids(mol, require_maps=require_atom_maps)
212
+ descriptors: dict[str, StereoValue] = {}
213
+ square_planar_permutations = _square_planar_permutations(mol)
214
+ trigonal_bipyramidal_permutations = _trigonal_bipyramidal_permutations(mol)
215
+ octahedral_permutations = _octahedral_permutations(mol)
216
+ tetra_tags = {
217
+ Chem.ChiralType.CHI_TETRAHEDRAL_CW: 1,
218
+ Chem.ChiralType.CHI_TETRAHEDRAL_CCW: -1,
219
+ }
220
+ for atom in mol.GetAtoms():
221
+ tag = atom.GetChiralTag()
222
+ if tag == Chem.ChiralType.CHI_SQUAREPLANAR:
223
+ center = ids[atom.GetIdx()]
224
+ permutation = square_planar_permutations.get(atom.GetIdx())
225
+ local_refs = _non_tetrahedral_local_references(
226
+ atom,
227
+ ids,
228
+ center,
229
+ 4,
230
+ "Square-planar",
231
+ )
232
+ if permutation == 0:
233
+ descriptor = SquarePlanarStereo(
234
+ (center, *local_refs),
235
+ None,
236
+ "rdkit",
237
+ )
238
+ descriptors[descriptor_id(descriptor)] = descriptor
239
+ continue
240
+ positions = _SQUARE_PLANAR_POSITION_MAPS.get(permutation)
241
+ if positions is None:
242
+ raise ValueError(
243
+ f"Square-planar center {center} has unsupported RDKit "
244
+ f"permutation {permutation!r}."
245
+ )
246
+ cyclic_refs = tuple(local_refs[index] for index in positions)
247
+ descriptor = SquarePlanarStereo(
248
+ (center, *cyclic_refs),
249
+ 0,
250
+ "rdkit",
251
+ )
252
+ descriptors[descriptor_id(descriptor)] = descriptor
253
+ continue
254
+ if tag == Chem.ChiralType.CHI_TRIGONALBIPYRAMIDAL:
255
+ center = ids[atom.GetIdx()]
256
+ permutation = trigonal_bipyramidal_permutations.get(atom.GetIdx())
257
+ local_refs = _non_tetrahedral_local_references(
258
+ atom,
259
+ ids,
260
+ center,
261
+ 5,
262
+ "Trigonal-bipyramidal",
263
+ )
264
+ if permutation == 0:
265
+ descriptor = TrigonalBipyramidalStereo(
266
+ (center, *local_refs),
267
+ None,
268
+ "rdkit",
269
+ )
270
+ descriptors[descriptor_id(descriptor)] = descriptor
271
+ continue
272
+ positions = _TRIGONAL_BIPYRAMIDAL_POSITION_MAPS.get(permutation)
273
+ if positions is None:
274
+ raise ValueError(
275
+ f"Trigonal-bipyramidal center {center} has unsupported "
276
+ f"RDKit permutation {permutation!r}."
277
+ )
278
+ positional_refs = tuple(local_refs[index] for index in positions)
279
+ descriptor = TrigonalBipyramidalStereo(
280
+ (center, *positional_refs),
281
+ 1,
282
+ "rdkit",
283
+ )
284
+ descriptors[descriptor_id(descriptor)] = descriptor
285
+ continue
286
+ if tag == Chem.ChiralType.CHI_OCTAHEDRAL:
287
+ center = ids[atom.GetIdx()]
288
+ permutation = octahedral_permutations.get(atom.GetIdx())
289
+ local_refs = _non_tetrahedral_local_references(
290
+ atom,
291
+ ids,
292
+ center,
293
+ 6,
294
+ "Octahedral",
295
+ )
296
+ if permutation == 0:
297
+ descriptor = OctahedralStereo(
298
+ (center, *local_refs),
299
+ None,
300
+ "rdkit",
301
+ )
302
+ descriptors[descriptor_id(descriptor)] = descriptor
303
+ continue
304
+ positions = _OCTAHEDRAL_POSITION_MAPS.get(permutation)
305
+ if positions is None:
306
+ raise ValueError(
307
+ f"Octahedral center {center} has unsupported RDKit "
308
+ f"permutation {permutation!r}."
309
+ )
310
+ positional_refs = tuple(local_refs[index] for index in positions)
311
+ descriptor = OctahedralStereo(
312
+ (center, *positional_refs),
313
+ 1,
314
+ "rdkit",
315
+ )
316
+ descriptors[descriptor_id(descriptor)] = descriptor
317
+ continue
318
+ if tag not in tetra_tags:
319
+ continue
320
+ center = ids[atom.GetIdx()]
321
+ refs: list[int | str] = [
322
+ ids[neighbor.GetIdx()] for neighbor in atom.GetNeighbors()
323
+ ]
324
+ if len(refs) == 3:
325
+ refs.append(_virtual_ligand(atom, center))
326
+ if len(refs) != 4:
327
+ continue
328
+ descriptor = TetrahedralStereo((center, *refs), tetra_tags[tag], "rdkit")
329
+ descriptors[descriptor_id(descriptor)] = descriptor
330
+
331
+ for bond in mol.GetBonds():
332
+ stereo = bond.GetStereo()
333
+ if stereo in {
334
+ Chem.BondStereo.STEREOATROPCW,
335
+ Chem.BondStereo.STEREOATROPCCW,
336
+ }:
337
+ left_index = bond.GetBeginAtomIdx()
338
+ right_index = bond.GetEndAtomIdx()
339
+ left = ids[left_index]
340
+ right = ids[right_index]
341
+ left_refs = _atrop_end_references(
342
+ mol.GetAtomWithIdx(left_index),
343
+ right_index,
344
+ ids,
345
+ left,
346
+ )
347
+ right_refs = _atrop_end_references(
348
+ mol.GetAtomWithIdx(right_index),
349
+ left_index,
350
+ ids,
351
+ right,
352
+ )
353
+ parity = {
354
+ Chem.BondStereo.STEREOATROPCW: 1,
355
+ Chem.BondStereo.STEREOATROPCCW: -1,
356
+ }[stereo]
357
+ descriptor = AtropBondStereo(
358
+ (*left_refs, left, right, *right_refs),
359
+ parity,
360
+ "rdkit",
361
+ )
362
+ descriptors[descriptor_id(descriptor)] = descriptor
363
+ continue
364
+ if bond.GetBondType() != Chem.BondType.DOUBLE:
365
+ continue
366
+ if stereo not in {Chem.BondStereo.STEREOE, Chem.BondStereo.STEREOZ}:
367
+ continue
368
+ left_idx, right_idx = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
369
+ left, right = ids[left_idx], ids[right_idx]
370
+ stereo_atoms = tuple(bond.GetStereoAtoms())
371
+ if len(stereo_atoms) != 2:
372
+ continue
373
+ selected_left, selected_right = stereo_atoms
374
+ left_refs: list[int | str] = [ids[selected_left]]
375
+ left_refs.extend(
376
+ ids[neighbor.GetIdx()]
377
+ for neighbor in mol.GetAtomWithIdx(left_idx).GetNeighbors()
378
+ if neighbor.GetIdx() not in {right_idx, selected_left}
379
+ )
380
+ right_refs: list[int | str] = [ids[selected_right]]
381
+ right_refs.extend(
382
+ ids[neighbor.GetIdx()]
383
+ for neighbor in mol.GetAtomWithIdx(right_idx).GetNeighbors()
384
+ if neighbor.GetIdx() not in {left_idx, selected_right}
385
+ )
386
+ if len(left_refs) == 1:
387
+ left_refs.append(_virtual_ligand(mol.GetAtomWithIdx(left_idx), left))
388
+ if len(right_refs) == 1:
389
+ right_refs.append(_virtual_ligand(mol.GetAtomWithIdx(right_idx), right))
390
+ if len(left_refs) != 2 or len(right_refs) != 2:
391
+ continue
392
+ if stereo == Chem.BondStereo.STEREOE:
393
+ right_refs.reverse()
394
+ descriptor = PlanarBondStereo(
395
+ (*left_refs, left, right, *right_refs), 0, "rdkit"
396
+ )
397
+ descriptors[descriptor_id(descriptor)] = descriptor
398
+ return descriptors
399
+
400
+
401
+ def apply_stereo_to_rdkit(
402
+ mol: Chem.Mol, descriptors: Iterable[StereoValue]
403
+ ) -> Chem.Mol:
404
+ """Apply the RDKit-interoperable descriptor subset in place.
405
+
406
+ Unsupported descriptors remain valid SynKit graph/rule values, but their
407
+ RDKit projection is not silently discarded: conversion raises until a
408
+ class-specific, round-trip-tested adapter is available.
409
+ """
410
+ ids = _atom_ids(mol, require_maps=True)
411
+ by_map = {atom_map: index for index, atom_map in ids.items()}
412
+ for descriptor in descriptors:
413
+ if isinstance(descriptor, TetrahedralStereo):
414
+ if (
415
+ not isinstance(descriptor.center, int)
416
+ or descriptor.center not in by_map
417
+ ):
418
+ raise ValueError("Tetrahedral center is absent from RDKit molecule.")
419
+ atom = mol.GetAtomWithIdx(by_map[descriptor.center])
420
+ refs = tuple(ids[neighbor.GetIdx()] for neighbor in atom.GetNeighbors())
421
+ if len(refs) == 3:
422
+ refs = (*refs, _virtual_ligand(atom, descriptor.center))
423
+ probe = TetrahedralStereo((descriptor.center, *refs), 1)
424
+ desired = descriptor.canonical_form()[-1]
425
+ probe_parity = probe.canonical_form()[-1]
426
+ raw_parity = None if desired is None else int(desired) * int(probe_parity)
427
+ tag = {
428
+ 1: Chem.ChiralType.CHI_TETRAHEDRAL_CW,
429
+ -1: Chem.ChiralType.CHI_TETRAHEDRAL_CCW,
430
+ None: Chem.ChiralType.CHI_UNSPECIFIED,
431
+ }[raw_parity]
432
+ atom.SetChiralTag(tag)
433
+ elif isinstance(descriptor, SquarePlanarStereo):
434
+ if (
435
+ not isinstance(descriptor.center, int)
436
+ or descriptor.center not in by_map
437
+ ):
438
+ raise ValueError("Square-planar center is absent from RDKit molecule.")
439
+ if descriptor.parity is None:
440
+ raise NotImplementedError(
441
+ "RDKit projection of an unknown square-planar orientation "
442
+ "would lose the distinction between an unknown descriptor "
443
+ "and no descriptor."
444
+ )
445
+ atom = mol.GetAtomWithIdx(by_map[descriptor.center])
446
+ local_refs = _non_tetrahedral_local_references(
447
+ atom,
448
+ ids,
449
+ descriptor.center,
450
+ 4,
451
+ "Square-planar",
452
+ )
453
+ matching_permutations = []
454
+ for permutation, positions in _SQUARE_PLANAR_POSITION_MAPS.items():
455
+ cyclic_refs = tuple(local_refs[index] for index in positions)
456
+ candidate = SquarePlanarStereo(
457
+ (descriptor.center, *cyclic_refs),
458
+ 0,
459
+ )
460
+ if candidate == descriptor:
461
+ matching_permutations.append(permutation)
462
+ if not matching_permutations:
463
+ raise ValueError(
464
+ "Square-planar descriptor ligands do not match the RDKit "
465
+ f"coordination sphere at center {descriptor.center}."
466
+ )
467
+
468
+ # Duplicate ligands can make more than one raw SP permutation encode
469
+ # the same cyclic identity. The lowest matching value is a stable
470
+ # serialization choice; descriptor identity does not depend on it.
471
+ permutation = min(matching_permutations)
472
+ atom.SetChiralTag(Chem.ChiralType.CHI_SQUAREPLANAR)
473
+ # RDKit 2026.03 exposes permutation reads through StereoInfo but no
474
+ # Atom setter in Python. This is RDKit's own integer property; the
475
+ # public FindPotentialStereo() round-trip is exercised by tests.
476
+ atom.SetIntProp("_chiralPermutation", permutation)
477
+ elif isinstance(descriptor, TrigonalBipyramidalStereo):
478
+ if (
479
+ not isinstance(descriptor.center, int)
480
+ or descriptor.center not in by_map
481
+ ):
482
+ raise ValueError(
483
+ "Trigonal-bipyramidal center is absent from RDKit molecule."
484
+ )
485
+ if descriptor.parity is None:
486
+ raise NotImplementedError(
487
+ "RDKit projection of an unknown trigonal-bipyramidal "
488
+ "orientation would lose the distinction between an "
489
+ "unknown descriptor and no descriptor."
490
+ )
491
+ atom = mol.GetAtomWithIdx(by_map[descriptor.center])
492
+ local_refs = _non_tetrahedral_local_references(
493
+ atom,
494
+ ids,
495
+ descriptor.center,
496
+ 5,
497
+ "Trigonal-bipyramidal",
498
+ )
499
+ matching_permutations = []
500
+ for (
501
+ permutation,
502
+ positions,
503
+ ) in _TRIGONAL_BIPYRAMIDAL_POSITION_MAPS.items():
504
+ positional_refs = tuple(local_refs[index] for index in positions)
505
+ candidate = TrigonalBipyramidalStereo(
506
+ (descriptor.center, *positional_refs),
507
+ 1,
508
+ )
509
+ if candidate == descriptor:
510
+ matching_permutations.append(permutation)
511
+ if not matching_permutations:
512
+ raise ValueError(
513
+ "Trigonal-bipyramidal descriptor ligands do not match the "
514
+ f"RDKit coordination sphere at center {descriptor.center}."
515
+ )
516
+
517
+ permutation = min(matching_permutations)
518
+ atom.SetChiralTag(Chem.ChiralType.CHI_TRIGONALBIPYRAMIDAL)
519
+ atom.SetIntProp("_chiralPermutation", permutation)
520
+ elif isinstance(descriptor, OctahedralStereo):
521
+ if (
522
+ not isinstance(descriptor.center, int)
523
+ or descriptor.center not in by_map
524
+ ):
525
+ raise ValueError("Octahedral center is absent from RDKit molecule.")
526
+ if descriptor.parity is None:
527
+ raise NotImplementedError(
528
+ "RDKit projection of an unknown octahedral orientation "
529
+ "would lose the distinction between an unknown descriptor "
530
+ "and no descriptor."
531
+ )
532
+ atom = mol.GetAtomWithIdx(by_map[descriptor.center])
533
+ local_refs = _non_tetrahedral_local_references(
534
+ atom,
535
+ ids,
536
+ descriptor.center,
537
+ 6,
538
+ "Octahedral",
539
+ )
540
+ matching_permutations = []
541
+ for permutation, positions in _OCTAHEDRAL_POSITION_MAPS.items():
542
+ positional_refs = tuple(local_refs[index] for index in positions)
543
+ candidate = OctahedralStereo(
544
+ (descriptor.center, *positional_refs),
545
+ 1,
546
+ )
547
+ if candidate == descriptor:
548
+ matching_permutations.append(permutation)
549
+ if not matching_permutations:
550
+ raise ValueError(
551
+ "Octahedral descriptor ligands do not match the RDKit "
552
+ f"coordination sphere at center {descriptor.center}."
553
+ )
554
+
555
+ permutation = min(matching_permutations)
556
+ atom.SetChiralTag(Chem.ChiralType.CHI_OCTAHEDRAL)
557
+ atom.SetIntProp("_chiralPermutation", permutation)
558
+ elif isinstance(descriptor, AtropBondStereo):
559
+ left, right = descriptor.atoms[2:4]
560
+ if (
561
+ not isinstance(left, int)
562
+ or not isinstance(right, int)
563
+ or left not in by_map
564
+ or right not in by_map
565
+ ):
566
+ raise ValueError("Atrop-bond centers must be mapped RDKit atoms.")
567
+ bond = mol.GetBondBetweenAtoms(by_map[left], by_map[right])
568
+ if bond is None or bond.GetBondType() != Chem.BondType.SINGLE:
569
+ raise ValueError("Atrop descriptor central single bond is absent.")
570
+ if descriptor.parity is None:
571
+ raise NotImplementedError(
572
+ "RDKit projection of an unknown atrop-bond orientation "
573
+ "would lose the distinction between an unknown descriptor "
574
+ "and no descriptor."
575
+ )
576
+
577
+ left_index = bond.GetBeginAtomIdx()
578
+ right_index = bond.GetEndAtomIdx()
579
+ local_left = ids[left_index]
580
+ local_right = ids[right_index]
581
+ left_refs = _atrop_end_references(
582
+ mol.GetAtomWithIdx(left_index),
583
+ right_index,
584
+ ids,
585
+ local_left,
586
+ )
587
+ right_refs = _atrop_end_references(
588
+ mol.GetAtomWithIdx(right_index),
589
+ left_index,
590
+ ids,
591
+ local_right,
592
+ )
593
+ candidates = {
594
+ AtropBondStereo(
595
+ (*left_refs, local_left, local_right, *right_refs),
596
+ parity,
597
+ ).canonical_form(): stereo
598
+ for stereo, parity in (
599
+ (Chem.BondStereo.STEREOATROPCW, 1),
600
+ (Chem.BondStereo.STEREOATROPCCW, -1),
601
+ )
602
+ }
603
+ selected = candidates.get(descriptor.canonical_form())
604
+ if selected is None:
605
+ raise ValueError(
606
+ "Atrop descriptor substituents do not match the RDKit "
607
+ f"bond {left}-{right}."
608
+ )
609
+ bond.SetStereo(selected)
610
+ elif isinstance(descriptor, PlanarBondStereo):
611
+ left, right = descriptor.atoms[2:4]
612
+ if not isinstance(left, int) or not isinstance(right, int):
613
+ raise ValueError("Planar-bond centers must be mapped atoms.")
614
+ bond = mol.GetBondBetweenAtoms(by_map[left], by_map[right])
615
+ if bond is None:
616
+ raise ValueError("Planar descriptor central bond is absent.")
617
+ if descriptor.parity is None:
618
+ # A present descriptor with undefined orientation must remain
619
+ # distinct from a specified E/Z arrangement at the RDKit
620
+ # boundary. Clear neighboring slash/backslash directions too,
621
+ # otherwise AssignStereochemistry reconstructs the old label.
622
+ bond.SetStereo(Chem.BondStereo.STEREONONE)
623
+ for center in (by_map[left], by_map[right]):
624
+ atom = mol.GetAtomWithIdx(center)
625
+ for adjacent in atom.GetBonds():
626
+ if adjacent.GetIdx() != bond.GetIdx():
627
+ adjacent.SetBondDir(Chem.BondDir.NONE)
628
+ continue
629
+ left_refs = descriptor.atoms[:2]
630
+ right_refs = descriptor.atoms[4:]
631
+ real_left = next(
632
+ (value for value in left_refs if isinstance(value, int)), None
633
+ )
634
+ real_right = next(
635
+ (value for value in right_refs if isinstance(value, int)), None
636
+ )
637
+ if real_left is None or real_right is None:
638
+ continue
639
+ other_left = next(value for value in left_refs if value != real_left)
640
+ other_right = next(value for value in right_refs if value != real_right)
641
+ z_atoms = (real_left, other_left, left, right, real_right, other_right)
642
+ e_atoms = (real_left, other_left, left, right, other_right, real_right)
643
+ candidates = {}
644
+ for stereo, atoms in (
645
+ (Chem.BondStereo.STEREOZ, z_atoms),
646
+ (Chem.BondStereo.STEREOE, e_atoms),
647
+ ):
648
+ candidate = PlanarBondStereo(atoms, descriptor.parity)
649
+ candidates[candidate.canonical_form()] = (stereo, atoms)
650
+ selected = candidates.get(descriptor.canonical_form())
651
+ if selected is None:
652
+ continue
653
+ stereo, atoms = selected
654
+ bond.SetStereoAtoms(by_map[real_left], by_map[real_right])
655
+ bond.SetStereo(stereo)
656
+ else:
657
+ raise NotImplementedError(
658
+ f"RDKit conversion for {descriptor.descriptor_class!r} stereo "
659
+ "is not implemented; keep this descriptor in SynKit graph metadata."
660
+ )
661
+ Chem.AssignStereochemistry(mol, cleanIt=False, force=True)
662
+ return mol