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,532 @@
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ from typing import Dict, FrozenSet, List, Optional, Set, Tuple, Union
5
+
6
+ from PIL import Image, ImageDraw
7
+ from rdkit import Chem
8
+ from rdkit.Chem import AllChem, rdChemReactions, rdmolfiles
9
+ from rdkit.Chem.Draw import rdMolDraw2D
10
+
11
+
12
+ class RXNVis:
13
+ """Visualize molecules and reactions as PIL images.
14
+
15
+ When ``highlight_reaction_center=True`` and the input is an atom-mapped
16
+ reaction SMILES, each molecule is drawn individually so that changed atoms
17
+ and bonds can be color-coded:
18
+
19
+ * **Broken bonds** (present in reactants only) — ``rc_broken_color``
20
+ * **Formed bonds** (present in products only) — ``rc_formed_color``
21
+ * **Order-changed bonds** (present on both sides with different order) — ``rc_atom_color``
22
+ * **Reaction-center atoms** (any atom involved in the above) — ``rc_atom_color``
23
+
24
+ Reactions without atom mapping fall back to the standard ``DrawReaction``
25
+ layout automatically.
26
+
27
+ :param width: Canvas width in pixels.
28
+ :type width: int
29
+ :param height: Canvas height in pixels.
30
+ :type height: int
31
+ :param dpi: DPI scaling factor (72 = 1×, i.e. no scaling).
32
+ :type dpi: int
33
+ :param background_colour: RGBA background color, each channel in [0, 1].
34
+ Defaults to opaque white.
35
+ :type background_colour: Optional[Tuple[float, float, float, float]]
36
+ :param highlight_by_reactant: Color each reactant differently in the
37
+ standard (non-RC) ``DrawReaction`` path.
38
+ :type highlight_by_reactant: bool
39
+ :param bond_line_width: Bond line width in points.
40
+ :type bond_line_width: float
41
+ :param atom_label_font_size: Base font size for atom labels (maps to
42
+ ``MolDrawOptions.baseFontSize``).
43
+ :type atom_label_font_size: int
44
+ :param show_atom_map: Overlay atom-map numbers as atom labels.
45
+ :type show_atom_map: bool
46
+ :param highlight_reaction_center: When ``True``, detect and highlight the
47
+ reaction center in atom-mapped reaction SMILES.
48
+ :type highlight_reaction_center: bool
49
+ :param rc_atom_color: RGB color for reaction-center atoms and
50
+ order-changed bonds (values in [0, 1]).
51
+ :type rc_atom_color: Tuple[float, float, float]
52
+ :param rc_broken_color: RGB color for bonds broken in the reaction.
53
+ :type rc_broken_color: Tuple[float, float, float]
54
+ :param rc_formed_color: RGB color for bonds formed in the reaction.
55
+ :type rc_formed_color: Tuple[float, float, float]
56
+
57
+ .. code-block:: python
58
+
59
+ from synkit.Vis.reaction import RXNVis
60
+
61
+ vis = RXNVis(
62
+ width=1200,
63
+ height=400,
64
+ highlight_reaction_center=True,
65
+ show_atom_map=True,
66
+ )
67
+ rsmi = (
68
+ "[CH3:1][C:2](=[O:3])[O:4][CH3:5].[O:6]([H:7])[H:8]"
69
+ ">>"
70
+ "[CH3:1][C:2](=[O:3])[O:6][H:7].[CH3:5][O:4][H:8]"
71
+ )
72
+ vis.save_png(rsmi, "transesterification.png")
73
+ """
74
+
75
+ def __init__(
76
+ self,
77
+ width: int = 800,
78
+ height: int = 450,
79
+ dpi: int = 96,
80
+ background_colour: Optional[Tuple[float, float, float, float]] = None,
81
+ highlight_by_reactant: bool = True,
82
+ bond_line_width: float = 2.0,
83
+ atom_label_font_size: int = 12,
84
+ show_atom_map: bool = False,
85
+ highlight_reaction_center: bool = False,
86
+ rc_atom_color: Tuple[float, float, float] = (1.0, 0.5, 0.0),
87
+ rc_broken_color: Tuple[float, float, float] = (0.9, 0.2, 0.2),
88
+ rc_formed_color: Tuple[float, float, float] = (0.0, 0.75, 0.0),
89
+ ) -> None:
90
+ self.width = width
91
+ self.height = height
92
+ self.dpi = dpi
93
+ self.background_colour = background_colour or (1.0, 1.0, 1.0, 1.0)
94
+ self.highlight_by_reactant = highlight_by_reactant
95
+ self.bond_line_width = bond_line_width
96
+ self.atom_label_font_size = atom_label_font_size
97
+ self.show_atom_map = show_atom_map
98
+ self.highlight_reaction_center = highlight_reaction_center
99
+ self.rc_atom_color = rc_atom_color
100
+ self.rc_broken_color = rc_broken_color
101
+ self.rc_formed_color = rc_formed_color
102
+
103
+ # ------------------------------------------------------------------
104
+ # Public API
105
+ # ------------------------------------------------------------------
106
+
107
+ def render(
108
+ self, smiles: str, return_bytes: bool = False
109
+ ) -> Union[Image.Image, bytes]:
110
+ """Render a molecule or reaction SMILES to a PIL image or PNG bytes.
111
+
112
+ When ``highlight_reaction_center=True`` and ``smiles`` is an
113
+ atom-mapped reaction SMILES, the reaction center is highlighted.
114
+ Falls back to the standard ``DrawReaction`` layout when no atom
115
+ mapping is present.
116
+
117
+ :param smiles: Molecule SMILES or reaction SMILES containing ``">>"``.
118
+ :type smiles: str
119
+ :param return_bytes: If ``True``, return raw PNG bytes instead of a
120
+ ``PIL.Image.Image``.
121
+ :type return_bytes: bool
122
+ :return: Rendered image or PNG bytes.
123
+ :rtype: Union[PIL.Image.Image, bytes]
124
+ :raises ValueError: If the input cannot be parsed.
125
+ """
126
+ if ">>" in smiles and self.highlight_reaction_center:
127
+ img = self._render_rc(smiles)
128
+ else:
129
+ img = self._render_standard(smiles)
130
+
131
+ if self.dpi != 72:
132
+ scale = self.dpi / 72.0
133
+ img = img.resize(
134
+ (int(img.width * scale), int(img.height * scale)), Image.LANCZOS
135
+ )
136
+
137
+ if return_bytes:
138
+ buf = io.BytesIO()
139
+ img.save(buf, format="PNG")
140
+ return buf.getvalue()
141
+ return img
142
+
143
+ def save_png(self, smiles: str, path: str) -> None:
144
+ """Render and save as a PNG file.
145
+
146
+ :param smiles: Molecule or reaction SMILES.
147
+ :type smiles: str
148
+ :param path: Output path (should end in ``.png``).
149
+ :type path: str
150
+ """
151
+ self.render(smiles, return_bytes=False).save(path, format="PNG")
152
+
153
+ def save_pdf(self, smiles: str, path: str, resolution: float = 300.0) -> None:
154
+ """Render and save as a single-page PDF.
155
+
156
+ :param smiles: Molecule or reaction SMILES.
157
+ :type smiles: str
158
+ :param path: Output path (should end in ``.pdf``).
159
+ :type path: str
160
+ :param resolution: DPI metadata for the PDF.
161
+ :type resolution: float
162
+ """
163
+ self.render(smiles, return_bytes=False).convert("RGB").save(
164
+ path, format="PDF", resolution=resolution
165
+ )
166
+
167
+ # ------------------------------------------------------------------
168
+ # Standard (non-RC) rendering
169
+ # ------------------------------------------------------------------
170
+
171
+ def _render_standard(self, smiles: str) -> Image.Image:
172
+ """Render using RDKit's ``DrawReaction`` / ``DrawMolecule``.
173
+
174
+ :param smiles: Molecule or reaction SMILES.
175
+ :type smiles: str
176
+ :return: Rendered and cropped image.
177
+ :rtype: PIL.Image.Image
178
+ """
179
+ drawer = rdMolDraw2D.MolDraw2DCairo(self.width, self.height, 0, 0)
180
+ opts = drawer.drawOptions()
181
+ opts.bondLineWidth = self.bond_line_width
182
+ opts.baseFontSize = self.atom_label_font_size
183
+ opts.setBackgroundColour(self.background_colour)
184
+ opts.includeAtomTags = self.show_atom_map
185
+
186
+ try:
187
+ if ">>" in smiles:
188
+ rxn = rdChemReactions.ReactionFromSmarts(smiles, useSmiles=True)
189
+ rdChemReactions.PreprocessReaction(rxn)
190
+
191
+ if self.show_atom_map:
192
+ for mol in list(rxn.GetReactants()) + list(rxn.GetProducts()):
193
+ for atom in mol.GetAtoms():
194
+ if atom.HasProp("molAtomMapNumber"):
195
+ atom.SetProp(
196
+ "atomLabel", atom.GetProp("molAtomMapNumber")
197
+ )
198
+
199
+ drawer.DrawReaction(rxn, self.highlight_by_reactant, None, None)
200
+ else:
201
+ mol = rdmolfiles.MolFromSmiles(smiles) or rdmolfiles.MolFromSmarts(
202
+ smiles
203
+ )
204
+ if mol is None:
205
+ raise ValueError(f"Could not parse SMILES/SMARTS: {smiles!r}")
206
+ drawer.DrawMolecule(mol)
207
+ finally:
208
+ drawer.FinishDrawing()
209
+
210
+ img = Image.open(io.BytesIO(drawer.GetDrawingText())).convert("RGBA")
211
+ bbox = img.split()[-1].getbbox()
212
+ if bbox:
213
+ img = img.crop(bbox)
214
+ return img
215
+
216
+ # ------------------------------------------------------------------
217
+ # Reaction-center detection
218
+ # ------------------------------------------------------------------
219
+
220
+ @staticmethod
221
+ def _find_reaction_center(
222
+ rsmi: str,
223
+ ) -> Tuple[
224
+ Set[int],
225
+ Set[FrozenSet[int]],
226
+ Set[FrozenSet[int]],
227
+ Set[FrozenSet[int]],
228
+ ]:
229
+ """Detect reaction-center atoms and bonds from a mapped reaction SMILES.
230
+
231
+ Compares the bond connectivity on the reactant and product sides. Only
232
+ bonds where **both** endpoints carry atom-map numbers are considered.
233
+
234
+ :param rsmi: Atom-mapped reaction SMILES containing ``">>"``.
235
+ :type rsmi: str
236
+ :return: A 4-tuple ``(changed_atom_maps, formed_bonds,
237
+ broken_bonds, changed_order_bonds)`` where each bond set contains
238
+ ``frozenset({map_a, map_b})`` pairs.
239
+ :rtype: Tuple[Set[int], Set[FrozenSet[int]], Set[FrozenSet[int]], Set[FrozenSet[int]]]
240
+ """
241
+ reactants_smi, products_smi = rsmi.split(">>", 1)
242
+
243
+ # Keep explicit H atoms that carry atom-map numbers (e.g. [H:7]) so
244
+ # proton-transfer bonds are correctly detected.
245
+ _h_params = Chem.SmilesParserParams()
246
+ _h_params.removeHs = False
247
+
248
+ def _bond_map(side_smi: str) -> Dict[FrozenSet[int], float]:
249
+ bonds: Dict[FrozenSet[int], float] = {}
250
+ for smi in side_smi.split("."):
251
+ smi = smi.strip()
252
+ if not smi:
253
+ continue
254
+ mol = Chem.MolFromSmiles(smi, _h_params)
255
+ if mol is None:
256
+ continue
257
+ for bond in mol.GetBonds():
258
+ a = bond.GetBeginAtom().GetAtomMapNum()
259
+ b = bond.GetEndAtom().GetAtomMapNum()
260
+ if a and b:
261
+ bonds[frozenset({a, b})] = bond.GetBondTypeAsDouble()
262
+ return bonds
263
+
264
+ r_bonds = _bond_map(reactants_smi)
265
+ p_bonds = _bond_map(products_smi)
266
+
267
+ formed: Set[FrozenSet[int]] = set()
268
+ broken: Set[FrozenSet[int]] = set()
269
+ changed_order: Set[FrozenSet[int]] = set()
270
+ changed_maps: Set[int] = set()
271
+
272
+ for pair in set(r_bonds) | set(p_bonds):
273
+ r_ord = r_bonds.get(pair)
274
+ p_ord = p_bonds.get(pair)
275
+
276
+ if r_ord is None:
277
+ formed.add(pair)
278
+ changed_maps.update(pair)
279
+ elif p_ord is None:
280
+ broken.add(pair)
281
+ changed_maps.update(pair)
282
+ elif abs(r_ord - p_ord) > 1e-3:
283
+ changed_order.add(pair)
284
+ changed_maps.update(pair)
285
+
286
+ return changed_maps, formed, broken, changed_order
287
+
288
+ # ------------------------------------------------------------------
289
+ # Per-molecule drawing with highlights
290
+ # ------------------------------------------------------------------
291
+
292
+ def _draw_mol(
293
+ self,
294
+ mol: Chem.Mol,
295
+ mol_w: int,
296
+ mol_h: int,
297
+ changed_maps: Set[int],
298
+ formed: Set[FrozenSet[int]],
299
+ broken: Set[FrozenSet[int]],
300
+ changed_order: Set[FrozenSet[int]],
301
+ ) -> Image.Image:
302
+ """Draw one molecule with reaction-center highlights.
303
+
304
+ :param mol: RDKit molecule (2D coords are computed if absent).
305
+ :type mol: Chem.Mol
306
+ :param mol_w: Canvas width in pixels.
307
+ :type mol_w: int
308
+ :param mol_h: Canvas height in pixels.
309
+ :type mol_h: int
310
+ :param changed_maps: Atom-map numbers of reaction-center atoms.
311
+ :type changed_maps: Set[int]
312
+ :param formed: Bond pairs (atom-map frozensets) formed in the reaction.
313
+ :type formed: Set[FrozenSet[int]]
314
+ :param broken: Bond pairs broken in the reaction.
315
+ :type broken: Set[FrozenSet[int]]
316
+ :param changed_order: Bond pairs whose order changed.
317
+ :type changed_order: Set[FrozenSet[int]]
318
+ :return: Rendered molecule image.
319
+ :rtype: PIL.Image.Image
320
+ """
321
+ if mol.GetNumConformers() == 0:
322
+ AllChem.Compute2DCoords(mol)
323
+
324
+ h_atoms: List[int] = []
325
+ h_atom_colors: Dict[int, Tuple[float, float, float]] = {}
326
+ for atom in mol.GetAtoms():
327
+ amap = atom.GetAtomMapNum()
328
+ if amap and amap in changed_maps:
329
+ idx = atom.GetIdx()
330
+ h_atoms.append(idx)
331
+ h_atom_colors[idx] = self.rc_atom_color
332
+
333
+ h_bonds: List[int] = []
334
+ h_bond_colors: Dict[int, Tuple[float, float, float]] = {}
335
+ for bond in mol.GetBonds():
336
+ a = bond.GetBeginAtom().GetAtomMapNum()
337
+ b = bond.GetEndAtom().GetAtomMapNum()
338
+ if not (a and b):
339
+ continue
340
+ pair = frozenset({a, b})
341
+ if pair in formed:
342
+ color = self.rc_formed_color
343
+ elif pair in broken:
344
+ color = self.rc_broken_color
345
+ elif pair in changed_order:
346
+ color = self.rc_atom_color
347
+ else:
348
+ continue
349
+ idx = bond.GetIdx()
350
+ h_bonds.append(idx)
351
+ h_bond_colors[idx] = color
352
+
353
+ drawer = rdMolDraw2D.MolDraw2DCairo(mol_w, mol_h)
354
+ opts = drawer.drawOptions()
355
+ opts.bondLineWidth = self.bond_line_width
356
+ opts.baseFontSize = self.atom_label_font_size
357
+ opts.setBackgroundColour(self.background_colour)
358
+
359
+ if self.show_atom_map:
360
+ for atom in mol.GetAtoms():
361
+ if atom.GetAtomMapNum():
362
+ atom.SetProp("atomLabel", str(atom.GetAtomMapNum()))
363
+
364
+ drawer.DrawMolecule(
365
+ mol,
366
+ highlightAtoms=h_atoms or None,
367
+ highlightAtomColors=h_atom_colors or None,
368
+ highlightBonds=h_bonds or None,
369
+ highlightBondColors=h_bond_colors or None,
370
+ )
371
+ drawer.FinishDrawing()
372
+ return Image.open(io.BytesIO(drawer.GetDrawingText())).convert("RGBA")
373
+
374
+ # ------------------------------------------------------------------
375
+ # Reaction-center rendering (compose per-molecule images)
376
+ # ------------------------------------------------------------------
377
+
378
+ def _render_rc(self, smiles: str) -> Image.Image:
379
+ """Render an atom-mapped reaction SMILES with reaction-center highlights.
380
+
381
+ If no reaction center is detected (no atom mapping), falls back to
382
+ :meth:`_render_standard`.
383
+
384
+ :param smiles: Atom-mapped reaction SMILES.
385
+ :type smiles: str
386
+ :return: Composed reaction image.
387
+ :rtype: PIL.Image.Image
388
+ """
389
+ reactants_smi, products_smi = smiles.split(">>", 1)
390
+
391
+ h_params = Chem.SmilesParserParams()
392
+ h_params.removeHs = False
393
+
394
+ def _parse_mols(side_smi: str) -> List[Chem.Mol]:
395
+ mols: List[Chem.Mol] = []
396
+ for smi in side_smi.split("."):
397
+ smi = smi.strip()
398
+ if not smi:
399
+ continue
400
+ mol = Chem.MolFromSmiles(smi, h_params)
401
+ if mol is not None:
402
+ AllChem.Compute2DCoords(mol)
403
+ mols.append(mol)
404
+ return mols
405
+
406
+ reactants = _parse_mols(reactants_smi)
407
+ products = _parse_mols(products_smi)
408
+
409
+ if not reactants and not products:
410
+ raise ValueError(f"Could not parse any molecules from: {smiles!r}")
411
+
412
+ changed_maps, formed, broken, changed_order = self._find_reaction_center(smiles)
413
+
414
+ # Fall back to standard layout when nothing is mapped.
415
+ if not changed_maps:
416
+ return self._render_standard(smiles)
417
+
418
+ # Per-molecule canvas dimensions.
419
+ n_mols = max(1, len(reactants) + len(products))
420
+ n_plus_r = max(0, len(reactants) - 1)
421
+ n_plus_p = max(0, len(products) - 1)
422
+ arrow_w = max(60, self.width // 10)
423
+ plus_w = max(30, self.width // 20)
424
+ total_sep_w = arrow_w + (n_plus_r + n_plus_p) * plus_w
425
+ mol_w = max(120, (self.width - total_sep_w) // n_mols)
426
+ mol_h = self.height
427
+
428
+ r_imgs = [
429
+ self._draw_mol(m, mol_w, mol_h, changed_maps, formed, broken, changed_order)
430
+ for m in reactants
431
+ ]
432
+ p_imgs = [
433
+ self._draw_mol(m, mol_w, mol_h, changed_maps, formed, broken, changed_order)
434
+ for m in products
435
+ ]
436
+
437
+ return self._compose_reaction_image(r_imgs, p_imgs, arrow_w, plus_w)
438
+
439
+ # ------------------------------------------------------------------
440
+ # Image composition helpers
441
+ # ------------------------------------------------------------------
442
+
443
+ def _make_separator(self, kind: str, width: int, height: int) -> Image.Image:
444
+ """Create a '+' or arrow separator image.
445
+
446
+ :param kind: ``"plus"`` for a '+' label, ``"arrow"`` for a drawn arrow.
447
+ :type kind: str
448
+ :param width: Image width in pixels.
449
+ :type width: int
450
+ :param height: Image height in pixels.
451
+ :type height: int
452
+ :return: Separator image.
453
+ :rtype: PIL.Image.Image
454
+ """
455
+ bg = tuple(int(c * 255) for c in self.background_colour)
456
+ img = Image.new("RGBA", (width, height), bg)
457
+ draw = ImageDraw.Draw(img)
458
+ cx, cy = width // 2, height // 2
459
+ ink = (80, 80, 80, 255)
460
+
461
+ if kind == "arrow":
462
+ half = width * 3 // 8
463
+ lw = max(2, height // 40)
464
+ ah = max(6, height // 15)
465
+ draw.line([(cx - half, cy), (cx + half, cy)], fill=ink, width=lw)
466
+ draw.polygon(
467
+ [(cx + half, cy), (cx + half - ah, cy - ah), (cx + half - ah, cy + ah)],
468
+ fill=ink,
469
+ )
470
+ else:
471
+ font_size = max(16, height // 8)
472
+ try:
473
+ from PIL import ImageFont
474
+
475
+ font = ImageFont.load_default(size=font_size)
476
+ except TypeError:
477
+ from PIL import ImageFont
478
+
479
+ font = ImageFont.load_default()
480
+ draw.text((cx, cy), "+", fill=ink, anchor="mm", font=font)
481
+
482
+ return img
483
+
484
+ def _compose_reaction_image(
485
+ self,
486
+ r_imgs: List[Image.Image],
487
+ p_imgs: List[Image.Image],
488
+ arrow_w: int,
489
+ plus_w: int,
490
+ ) -> Image.Image:
491
+ """Horizontally compose reactant and product images.
492
+
493
+ Inserts ``'+'`` separators between molecules on each side and an
494
+ arrow between the two sides.
495
+
496
+ :param r_imgs: Per-reactant images.
497
+ :type r_imgs: List[PIL.Image.Image]
498
+ :param p_imgs: Per-product images.
499
+ :type p_imgs: List[PIL.Image.Image]
500
+ :param arrow_w: Width in pixels for the reaction arrow.
501
+ :type arrow_w: int
502
+ :param plus_w: Width in pixels for each '+' separator.
503
+ :type plus_w: int
504
+ :return: Final composed reaction image.
505
+ :rtype: PIL.Image.Image
506
+ """
507
+ all_mol_imgs = r_imgs + p_imgs
508
+ h = max(img.height for img in all_mol_imgs) if all_mol_imgs else self.height
509
+
510
+ pieces: List[Image.Image] = []
511
+
512
+ for i, img in enumerate(r_imgs):
513
+ if i > 0:
514
+ pieces.append(self._make_separator("plus", plus_w, h))
515
+ pieces.append(img)
516
+
517
+ pieces.append(self._make_separator("arrow", arrow_w, h))
518
+
519
+ for i, img in enumerate(p_imgs):
520
+ if i > 0:
521
+ pieces.append(self._make_separator("plus", plus_w, h))
522
+ pieces.append(img)
523
+
524
+ total_w = sum(p.width for p in pieces)
525
+ bg = tuple(int(c * 255) for c in self.background_colour)
526
+ result = Image.new("RGBA", (total_w, h), bg)
527
+ x = 0
528
+ for piece in pieces:
529
+ result.paste(piece, (x, 0))
530
+ x += piece.width
531
+
532
+ return result
@@ -0,0 +1,41 @@
1
+ """Compatibility import for :mod:`synkit.Vis.molecule.reaction`.
2
+
3
+ New code should use :mod:`synkit.Vis.molecule`. This module remains available
4
+ so existing imports continue to work.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from synkit.Vis.molecule import reaction as _implementation
10
+
11
+ ReactionHighlights = _implementation.ReactionHighlights
12
+ draw_reaction_graph = _implementation.draw_reaction_graph
13
+ draw_reaction_graphs = _implementation.draw_reaction_graphs
14
+ find_reaction_highlights = _implementation.find_reaction_highlights
15
+
16
+ __all__ = [
17
+ "ReactionHighlights",
18
+ "draw_reaction_graph",
19
+ "draw_reaction_graphs",
20
+ "find_reaction_highlights",
21
+ ]
22
+
23
+
24
+ def __getattr__(name: str) -> object:
25
+ """Delegate legacy module attributes to the canonical implementation.
26
+
27
+ :param name: Attribute requested by the caller.
28
+ :type name: str
29
+ :return: Attribute from :mod:`synkit.Vis.molecule.reaction`.
30
+ :rtype: object
31
+ """
32
+ return getattr(_implementation, name)
33
+
34
+
35
+ def __dir__() -> list[str]:
36
+ """Return canonical and compatibility-module attributes.
37
+
38
+ :return: Sorted attribute names.
39
+ :rtype: list[str]
40
+ """
41
+ return sorted(set(globals()) | set(dir(_implementation)))
@@ -0,0 +1,17 @@
1
+ """Chemical-space visualization namespace.
2
+
3
+ This namespace contains chemical-space scatter plotting and embedding helpers.
4
+
5
+ .. rubric:: Example
6
+
7
+ .. code-block:: python
8
+
9
+ from synkit.Vis.space import Embedding, scatter_plot
10
+
11
+ embedding = Embedding("./cachedir")
12
+ """
13
+
14
+ from synkit.Vis.space.chemical import adjust_legend_handles, scatter_plot
15
+ from synkit.Vis.space.embedding import Embedding
16
+
17
+ __all__ = ["Embedding", "scatter_plot", "adjust_legend_handles"]
@@ -0,0 +1,80 @@
1
+ import pandas as pd
2
+ import matplotlib.pyplot as plt
3
+ import matplotlib.patches as mpatches
4
+
5
+
6
+ def scatter_plot(
7
+ data_train,
8
+ data_test,
9
+ size_train=10,
10
+ size_test=10,
11
+ title=None,
12
+ ax=None,
13
+ xlabel="Coordinate 1",
14
+ ylabel="Coordinate 2",
15
+ ):
16
+ # Check if data is empty
17
+ if data_train.empty or data_test.empty:
18
+ raise ValueError("Input data frames cannot be empty.")
19
+
20
+ # Check for necessary columns
21
+ if data_train.columns.size < 3 or data_test.columns.size < 3:
22
+ raise ValueError("Data frames must have at least three columns.")
23
+
24
+ # Adding 'Type' column to differentiate between train and test data
25
+ data_train["Type"] = "Train"
26
+ data_test["Type"] = "Test"
27
+
28
+ # Combine the datasets
29
+ data_combined = pd.concat([data_train, data_test])
30
+
31
+ # If no axes object is passed, create one
32
+ if ax is None:
33
+ fig, ax = plt.subplots(figsize=(12, 8))
34
+
35
+ # Define a more distinct color palette
36
+ pastel_palette = {
37
+ "Train": "deepskyblue",
38
+ "Test": "magenta",
39
+ } # Using deepskyblue and magenta for better distinction
40
+
41
+ # Create scatter plots with specified sizes
42
+ for dtype, color in pastel_palette.items():
43
+ subset = data_combined[data_combined["Type"] == dtype]
44
+ ax.scatter(
45
+ subset[subset.columns[1]],
46
+ subset[subset.columns[2]],
47
+ color=color,
48
+ label=dtype,
49
+ s=size_train if dtype == "Train" else size_test,
50
+ alpha=0.1,
51
+ edgecolor="none",
52
+ )
53
+
54
+ # Set the title if provided
55
+ if title:
56
+ ax.set_title(rf"{title}", fontsize=24, fontweight="bold")
57
+
58
+ # Set labels
59
+ ax.set_xlabel(xlabel, fontsize=18)
60
+ ax.set_ylabel(ylabel, fontsize=18)
61
+
62
+ # Enhance grid and layout
63
+ ax.grid(True, which="both", linestyle="--", linewidth=0.5)
64
+ ax.set_axisbelow(True)
65
+
66
+ # Get legend handles and labels for external usage
67
+ handles, labels = ax.get_legend_handles_labels()
68
+
69
+ # Return the axes, handles, and labels for further customization outside the function
70
+ return ax, handles, labels
71
+
72
+
73
+ # Define a function that modifies the legend handles to full opacity for better visibility in the legend
74
+ def adjust_legend_handles(handles, colors):
75
+ new_handles = []
76
+ for handle, color in zip(handles, colors):
77
+ # Create a new handle with the same properties but with full alpha for the legend
78
+ new_handle = mpatches.Patch(color=color, label=handle.get_label())
79
+ new_handles.append(new_handle)
80
+ return new_handles