synkit 0.0.5__tar.gz → 0.0.7__tar.gz

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 (270) hide show
  1. {synkit-0.0.5 → synkit-0.0.7}/.github/workflows/build-doc.yml +1 -0
  2. synkit-0.0.7/.github/workflows/test-and-lint.yml +68 -0
  3. {synkit-0.0.5 → synkit-0.0.7}/.gitignore +6 -6
  4. {synkit-0.0.5 → synkit-0.0.7}/PKG-INFO +2 -2
  5. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Molecule/test_standardize.py +1 -1
  6. synkit-0.0.7/Test/Chem/Reaction/test_aam_utils.py +52 -0
  7. {synkit-0.0.5/Test/Graph/ITS → synkit-0.0.7/Test/Chem/Reaction}/test_aam_validator.py +1 -1
  8. synkit-0.0.7/Test/Chem/Reaction/test_canon_rsmi.py +52 -0
  9. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_deionize.py +1 -1
  10. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Context/test_radius_expand.py +1 -1
  11. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Feature/test_graph_descriptors.py +1 -1
  12. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Hydrogen/test_graph_hydrogen.py +4 -4
  13. synkit-0.0.7/Test/Graph/Hydrogen/test_misc.py +92 -0
  14. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/ITS/test_its_expand.py +1 -1
  15. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/ITS/test_normalize_aam.py +1 -1
  16. synkit-0.0.7/Test/Graph/MTG/test_group_comp.py +52 -0
  17. synkit-0.0.7/Test/Graph/MTG/test_groupoid.py +181 -0
  18. synkit-0.0.7/Test/Graph/MTG/test_mtg.py +39 -0
  19. {synkit-0.0.5/Test/Graph/Cluster → synkit-0.0.7/Test/Graph/Matcher}/test_batch_cluster.py +1 -1
  20. {synkit-0.0.5/Test/Graph/Cluster → synkit-0.0.7/Test/Graph/Matcher}/test_graph_cluster.py +6 -3
  21. synkit-0.0.7/Test/Graph/Matcher/test_graph_matcher.py +158 -0
  22. {synkit-0.0.5/Test/Graph/Cluster → synkit-0.0.7/Test/Graph/Matcher}/test_graph_morphism.py +1 -1
  23. synkit-0.0.7/Test/Graph/Matcher/test_subgraph_matcher.py +136 -0
  24. synkit-0.0.7/Test/Graph/test_canon_graph.py +90 -0
  25. synkit-0.0.7/Test/Graph/test_syn_graph.py +69 -0
  26. {synkit-0.0.5 → synkit-0.0.7}/Test/IO/test_chemical_converter.py +122 -23
  27. {synkit-0.0.5 → synkit-0.0.7}/Test/IO/test_dg_to_gml.py +22 -15
  28. {synkit-0.0.5 → synkit-0.0.7}/Test/IO/test_gml_to_nx.py +1 -1
  29. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Apply}/test_reactor_rule.py +7 -2
  30. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Apply}/test_retro_reactor.py +5 -1
  31. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Apply}/test_rule_rbl.py +5 -1
  32. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Compose}/test_rule_compose.py +12 -6
  33. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Compose}/test_valance_constrain.py +12 -2
  34. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Modify}/test_molecule_rule.py +13 -4
  35. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Modify}/test_rule_utils.py +19 -5
  36. synkit-0.0.7/Test/Rule/test_syn_rule.py +99 -0
  37. synkit-0.0.7/Test/Synthesis/CRN/test_crn.py +54 -0
  38. synkit-0.0.7/Test/Synthesis/CRN/test_mod_crn.py +50 -0
  39. {synkit-0.0.5/Test/Reactor → synkit-0.0.7/Test/Synthesis/MSR}/test_multi_steps.py +6 -3
  40. {synkit-0.0.5/Test/Reactor → synkit-0.0.7/Test/Synthesis/MSR}/test_path_finder.py +7 -4
  41. synkit-0.0.7/Test/Synthesis/Reactor/test_core_engine.py +112 -0
  42. synkit-0.0.5/Test/Reactor/test_reactor_engine.py → synkit-0.0.7/Test/Synthesis/Reactor/test_mod_aam.py +45 -88
  43. synkit-0.0.7/Test/Synthesis/Reactor/test_mod_reactor.py +153 -0
  44. synkit-0.0.7/Test/Synthesis/Reactor/test_strategy.py +42 -0
  45. {synkit-0.0.5/Test/Reactor → synkit-0.0.7/Test/Synthesis}/test_reactor_utils.py +7 -2
  46. synkit-0.0.7/doc/api.rst +200 -0
  47. synkit-0.0.7/doc/changelog.rst +76 -0
  48. synkit-0.0.7/doc/chem.rst +69 -0
  49. {synkit-0.0.5 → synkit-0.0.7}/doc/conf.py +3 -2
  50. synkit-0.0.7/doc/figures/aldol.png +0 -0
  51. synkit-0.0.7/doc/figures/aldol_its.png +0 -0
  52. synkit-0.0.7/doc/figures/mtg.png +0 -0
  53. synkit-0.0.7/doc/getting_started.rst +84 -0
  54. synkit-0.0.7/doc/graph.rst +264 -0
  55. synkit-0.0.7/doc/index.rst +15 -0
  56. synkit-0.0.7/doc/io.rst +163 -0
  57. synkit-0.0.7/doc/reference.rst +6 -0
  58. synkit-0.0.7/doc/refs.bib +29 -0
  59. synkit-0.0.7/doc/rule.rst +2 -0
  60. synkit-0.0.7/doc/synthesis.rst +195 -0
  61. synkit-0.0.7/environment.yml +9 -0
  62. synkit-0.0.7/lint.sh +30 -0
  63. {synkit-0.0.5 → synkit-0.0.7}/pyproject.toml +2 -2
  64. {synkit-0.0.5 → synkit-0.0.7}/requirements.txt +1 -1
  65. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Molecule/standardize.py +39 -9
  66. synkit-0.0.7/synkit/Chem/Reaction/__init__.py +4 -0
  67. synkit-0.0.7/synkit/Chem/Reaction/aam_validator.py +253 -0
  68. synkit-0.0.7/synkit/Chem/Reaction/canon_rsmi.py +255 -0
  69. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Reaction/rsmi_utils.py +26 -0
  70. synkit-0.0.7/synkit/Chem/Reaction/standardize.py +151 -0
  71. synkit-0.0.7/synkit/Chem/__init__.py +1 -0
  72. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Context/hier_context.py +1 -1
  73. synkit-0.0.7/synkit/Graph/Feature/__init__.py +5 -0
  74. synkit-0.0.7/synkit/Graph/Feature/wl_hash.py +142 -0
  75. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Hyrogen/_misc.py +232 -90
  76. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Hyrogen/hextend.py +1 -1
  77. synkit-0.0.7/synkit/Graph/ITS/__init__.py +4 -0
  78. synkit-0.0.7/synkit/Graph/ITS/its_decompose.py +486 -0
  79. synkit-0.0.7/synkit/Graph/MTG/group_comp.py +158 -0
  80. synkit-0.0.7/synkit/Graph/MTG/groupoid.py +357 -0
  81. synkit-0.0.7/synkit/Graph/MTG/mcs_matcher.py +205 -0
  82. synkit-0.0.7/synkit/Graph/MTG/mtg.py +207 -0
  83. synkit-0.0.7/synkit/Graph/Matcher/__init__.py +10 -0
  84. {synkit-0.0.5/synkit/Graph/Cluster → synkit-0.0.7/synkit/Graph/Matcher}/batch_cluster.py +33 -12
  85. {synkit-0.0.5/synkit/Graph/Cluster → synkit-0.0.7/synkit/Graph/Matcher}/graph_cluster.py +46 -12
  86. synkit-0.0.7/synkit/Graph/Matcher/graph_matcher.py +320 -0
  87. synkit-0.0.7/synkit/Graph/Matcher/mcs_matcher.py +202 -0
  88. synkit-0.0.7/synkit/Graph/Matcher/sing.py +212 -0
  89. synkit-0.0.7/synkit/Graph/Matcher/subgraph_matcher.py +556 -0
  90. synkit-0.0.7/synkit/Graph/Matcher/turbo_iso.py +204 -0
  91. synkit-0.0.7/synkit/Graph/__init__.py +6 -0
  92. synkit-0.0.7/synkit/Graph/canon_algs.py +257 -0
  93. synkit-0.0.7/synkit/Graph/canon_graph.py +529 -0
  94. synkit-0.0.7/synkit/Graph/syn_graph.py +166 -0
  95. synkit-0.0.7/synkit/Graph/utils.py +159 -0
  96. synkit-0.0.7/synkit/IO/__init__.py +3 -0
  97. synkit-0.0.7/synkit/IO/chem_converter.py +421 -0
  98. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/data_io.py +55 -0
  99. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/debug.py +4 -2
  100. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/dg_to_gml.py +9 -1
  101. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/nx_to_gml.py +3 -2
  102. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Apply}/reactor_rule.py +13 -6
  103. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Apply}/retro_reactor.py +11 -3
  104. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Apply}/rule_rbl.py +11 -5
  105. synkit-0.0.7/synkit/Rule/Compose/compose_rule.py +228 -0
  106. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Compose}/rule_compose.py +10 -2
  107. synkit-0.0.7/synkit/Rule/Compose/rule_mapping.py +314 -0
  108. synkit-0.0.7/synkit/Rule/Compose/seq_comp.py +72 -0
  109. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Compose}/valence_constrain.py +8 -2
  110. synkit-0.0.7/synkit/Rule/Modify/implict_rule.py +66 -0
  111. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Modify}/molecule_rule.py +1 -1
  112. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Modify}/prune_templates.py +1 -1
  113. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Modify}/strip_rule.py +2 -2
  114. synkit-0.0.7/synkit/Rule/__init__.py +1 -0
  115. synkit-0.0.7/synkit/Rule/syn_rule.py +280 -0
  116. synkit-0.0.7/synkit/Synthesis/CRN/__init__.py +0 -0
  117. synkit-0.0.7/synkit/Synthesis/CRN/crn.py +207 -0
  118. {synkit-0.0.5/synkit/Reactor → synkit-0.0.7/synkit/Synthesis/CRN}/dcrn.py +2 -2
  119. synkit-0.0.7/synkit/Synthesis/CRN/mod_crn.py +169 -0
  120. synkit-0.0.7/synkit/Synthesis/MSR/__init__.py +0 -0
  121. {synkit-0.0.5/synkit/Reactor → synkit-0.0.7/synkit/Synthesis/MSR}/multi_steps.py +13 -9
  122. synkit-0.0.7/synkit/Synthesis/Metrics/__init__.py +0 -0
  123. {synkit-0.0.5/synkit → synkit-0.0.7/synkit/Synthesis}/Metrics/_base.py +1 -1
  124. synkit-0.0.7/synkit/Synthesis/Reactor/__init__.py +0 -0
  125. synkit-0.0.7/synkit/Synthesis/Reactor/core_engine.py +212 -0
  126. synkit-0.0.7/synkit/Synthesis/Reactor/mod_aam.py +283 -0
  127. synkit-0.0.7/synkit/Synthesis/Reactor/mod_reactor.py +446 -0
  128. synkit-0.0.7/synkit/Synthesis/Reactor/old_syn_reactor.py +443 -0
  129. {synkit-0.0.5/synkit → synkit-0.0.7/synkit/Synthesis}/Reactor/single_predictor.py +14 -14
  130. synkit-0.0.7/synkit/Synthesis/Reactor/strategy.py +51 -0
  131. synkit-0.0.7/synkit/Synthesis/Reactor/syn_reactor.py +561 -0
  132. synkit-0.0.7/synkit/Synthesis/__init__.py +0 -0
  133. {synkit-0.0.5/synkit/Reactor → synkit-0.0.7/synkit/Synthesis}/reactor_utils.py +2 -2
  134. synkit-0.0.7/synkit/Utils/__init__.py +0 -0
  135. synkit-0.0.7/synkit/Vis/__init__.py +5 -0
  136. synkit-0.0.7/synkit/Vis/graph_visualizer.py +376 -0
  137. synkit-0.0.7/synkit/Vis/rule_vis.py +185 -0
  138. synkit-0.0.7/synkit/Vis/rxn_vis.py +163 -0
  139. synkit-0.0.7/synkit/__init__.py +0 -0
  140. synkit-0.0.5/.github/workflows/test-and-lint.yml +0 -55
  141. synkit-0.0.5/CHANGELOG.md +0 -89
  142. synkit-0.0.5/Data/Benchmark/conversion_time.json.gz +0 -240767
  143. synkit-0.0.5/Data/Testcase/mech.json.gz +0 -1
  144. synkit-0.0.5/Data/Testcase/para_rule.json.gz +0 -1
  145. synkit-0.0.5/Data/Testcase/para_rule_retro.json.gz +0 -1
  146. synkit-0.0.5/Data/smart.json.gz +0 -1
  147. synkit-0.0.5/Test/Graph/Cluster/test_rule_morphism.py +0 -103
  148. synkit-0.0.5/Test/Reactor/test_core_engine.py +0 -112
  149. synkit-0.0.5/Test/Reactor/test_crn.py +0 -58
  150. synkit-0.0.5/Test/Vis/test_dpo_vis.py +0 -75
  151. synkit-0.0.5/doc/getting_started.rst +0 -66
  152. synkit-0.0.5/doc/index.rst +0 -23
  153. synkit-0.0.5/doc/references.rst +0 -113
  154. synkit-0.0.5/lint.sh +0 -7
  155. synkit-0.0.5/synkit/Chem/Reaction/standardize.py +0 -159
  156. synkit-0.0.5/synkit/Graph/Cluster/rule_morphism.py +0 -102
  157. synkit-0.0.5/synkit/Graph/Feature/wl_hash.py +0 -120
  158. synkit-0.0.5/synkit/Graph/ITS/aam_validator.py +0 -255
  159. synkit-0.0.5/synkit/Graph/ITS/its_decompose.py +0 -243
  160. synkit-0.0.5/synkit/IO/chem_converter.py +0 -386
  161. synkit-0.0.5/synkit/Reactor/core_engine.py +0 -213
  162. synkit-0.0.5/synkit/Reactor/crn.py +0 -118
  163. synkit-0.0.5/synkit/Reactor/reactor_engine.py +0 -228
  164. synkit-0.0.5/synkit/Rule/MaxValence.json.gz +0 -1
  165. synkit-0.0.5/synkit/Vis/chemical_graph_visualizer.py +0 -378
  166. synkit-0.0.5/synkit/Vis/chemical_reaction_visualizer.py +0 -133
  167. synkit-0.0.5/synkit/Vis/dpo_vis.py +0 -103
  168. synkit-0.0.5/synkit/Vis/graph_visualizer.py +0 -286
  169. synkit-0.0.5/synkit/Vis/rsmi_to_fig.py +0 -169
  170. {synkit-0.0.5 → synkit-0.0.7}/.github/workflows/publish-package.yml +0 -0
  171. {synkit-0.0.5 → synkit-0.0.7}/Data/Figure/synkit.png +0 -0
  172. {synkit-0.0.5 → synkit-0.0.7}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
  173. {synkit-0.0.5 → synkit-0.0.7}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
  174. {synkit-0.0.5 → synkit-0.0.7}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
  175. {synkit-0.0.5 → synkit-0.0.7}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
  176. {synkit-0.0.5 → synkit-0.0.7}/LICENSE +0 -0
  177. {synkit-0.0.5 → synkit-0.0.7}/Makefile +0 -0
  178. {synkit-0.0.5 → synkit-0.0.7}/README.md +0 -0
  179. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Fingerprint/__init__.py +0 -0
  180. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
  181. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
  182. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
  183. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Molecule/__init__.py +0 -0
  184. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/__init__.py +0 -0
  185. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_balance_checker.py +0 -0
  186. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_cleanning.py +0 -0
  187. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_fix_aam.py +0 -0
  188. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_neutralize.py +0 -0
  189. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_rsmi_utils.py +0 -0
  190. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_standardize.py +0 -0
  191. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/Reaction/test_tautomerize.py +0 -0
  192. {synkit-0.0.5 → synkit-0.0.7}/Test/Chem/__init__.py +0 -0
  193. {synkit-0.0.5/Test/Graph/Cluster → synkit-0.0.7/Test/Graph/Context}/__init__.py +0 -0
  194. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Context/test_hier_context.py +0 -0
  195. {synkit-0.0.5/Test/Graph/Context → synkit-0.0.7/Test/Graph/Feature}/__init__.py +0 -0
  196. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Feature/test_graph_fps.py +0 -0
  197. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Feature/test_graph_signature.py +0 -0
  198. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Feature/test_hash_fps.py +0 -0
  199. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Feature/test_morgan_fps.py +0 -0
  200. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Feature/test_path_fps.py +0 -0
  201. {synkit-0.0.5/Test/Graph/Feature → synkit-0.0.7/Test/Graph/Hydrogen}/__init__.py +0 -0
  202. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/Hydrogen/test_hcomplete.py +0 -0
  203. {synkit-0.0.5/Test/Graph/Hydrogen → synkit-0.0.7/Test/Graph/ITS}/__init__.py +0 -0
  204. {synkit-0.0.5 → synkit-0.0.7}/Test/Graph/ITS/test_its_construction.py +0 -0
  205. {synkit-0.0.5/Test/Graph/ITS → synkit-0.0.7/Test/Graph/MTG}/__init__.py +0 -0
  206. {synkit-0.0.5/Test/Graph → synkit-0.0.7/Test/Graph/Matcher}/__init__.py +0 -0
  207. {synkit-0.0.5/Test/IO → synkit-0.0.7/Test/Graph}/__init__.py +0 -0
  208. {synkit-0.0.5/Test/Reactor → synkit-0.0.7/Test/IO}/__init__.py +0 -0
  209. {synkit-0.0.5 → synkit-0.0.7}/Test/IO/test_graph_to_mol.py +0 -0
  210. {synkit-0.0.5 → synkit-0.0.7}/Test/IO/test_mol_to_graph.py +0 -0
  211. {synkit-0.0.5 → synkit-0.0.7}/Test/IO/test_nx_to_gml.py +0 -0
  212. {synkit-0.0.5/Test/Rule → synkit-0.0.7/Test/Rule/Apply}/__init__.py +0 -0
  213. {synkit-0.0.5/Test/Vis → synkit-0.0.7/Test/Rule/Compose}/__init__.py +0 -0
  214. {synkit-0.0.5/Test → synkit-0.0.7/Test/Rule/Modify}/__init__.py +0 -0
  215. {synkit-0.0.5/synkit/Chem/Fingerprint → synkit-0.0.7/Test/Rule}/__init__.py +0 -0
  216. {synkit-0.0.5/synkit/Chem/Molecule → synkit-0.0.7/Test/Synthesis/CRN}/__init__.py +0 -0
  217. {synkit-0.0.5/synkit/Chem/Reaction → synkit-0.0.7/Test/Synthesis/MSR}/__init__.py +0 -0
  218. {synkit-0.0.5/synkit/Graph/Cluster → synkit-0.0.7/Test/Synthesis/Reactor}/__init__.py +0 -0
  219. {synkit-0.0.5/synkit/Graph/Context → synkit-0.0.7/Test/Synthesis}/__init__.py +0 -0
  220. {synkit-0.0.5/synkit/Graph/Feature → synkit-0.0.7/Test/Vis}/__init__.py +0 -0
  221. {synkit-0.0.5 → synkit-0.0.7}/Test/Vis/test_embedding.py +0 -0
  222. {synkit-0.0.5/synkit/Graph/Hyrogen → synkit-0.0.7/Test}/__init__.py +0 -0
  223. {synkit-0.0.5 → synkit-0.0.7}/make.bat +0 -0
  224. {synkit-0.0.5 → synkit-0.0.7}/pytest.sh +0 -0
  225. {synkit-0.0.5/synkit/IO → synkit-0.0.7/synkit/Chem/Fingerprint}/__init__.py +0 -0
  226. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
  227. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
  228. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  229. {synkit-0.0.5/synkit/Metrics → synkit-0.0.7/synkit/Chem/Molecule}/__init__.py +0 -0
  230. {synkit-0.0.5/synkit/Graph/ITS → synkit-0.0.7/synkit/Chem/Reaction}/aam_utils.py +0 -0
  231. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Reaction/balance_check.py +0 -0
  232. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Reaction/cleanning.py +0 -0
  233. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Reaction/deionize.py +0 -0
  234. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Reaction/fix_aam.py +0 -0
  235. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Reaction/neutralize.py +0 -0
  236. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/Reaction/tautomerize.py +0 -0
  237. {synkit-0.0.5 → synkit-0.0.7}/synkit/Chem/utils.py +0 -0
  238. {synkit-0.0.5/synkit/Reactor → synkit-0.0.7/synkit/Graph/Context}/__init__.py +0 -0
  239. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Context/radius_expand.py +0 -0
  240. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  241. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Feature/graph_fps.py +0 -0
  242. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Feature/graph_signature.py +0 -0
  243. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Feature/hash_fps.py +0 -0
  244. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Feature/morgan_fps.py +0 -0
  245. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Feature/path_fps.py +0 -0
  246. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Graph/Hyrogen}/__init__.py +0 -0
  247. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
  248. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/ITS/its_builder.py +0 -0
  249. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/ITS/its_construction.py +0 -0
  250. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/ITS/its_expand.py +0 -0
  251. {synkit-0.0.5 → synkit-0.0.7}/synkit/Graph/ITS/normalize_aam.py +0 -0
  252. {synkit-0.0.5/synkit/Utils → synkit-0.0.7/synkit/Graph/MTG}/__init__.py +0 -0
  253. {synkit-0.0.5/synkit/Graph/Cluster → synkit-0.0.7/synkit/Graph/Matcher}/graph_morphism.py +0 -0
  254. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/data_process.py +0 -0
  255. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/gml_to_nx.py +0 -0
  256. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/graph_to_mol.py +0 -0
  257. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/mol_to_graph.py +0 -0
  258. {synkit-0.0.5 → synkit-0.0.7}/synkit/IO/smiles_to_id.py +0 -0
  259. {synkit-0.0.5/synkit/Vis → synkit-0.0.7/synkit/Rule/Apply}/__init__.py +0 -0
  260. {synkit-0.0.5/synkit → synkit-0.0.7/synkit/Rule/Compose}/__init__.py +0 -0
  261. /synkit-0.0.5/synkit/Graph/__init.py → /synkit-0.0.7/synkit/Rule/Modify/__init__.py +0 -0
  262. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Modify}/longest_path.py +0 -0
  263. {synkit-0.0.5/synkit/Rule → synkit-0.0.7/synkit/Rule/Modify}/rule_utils.py +0 -0
  264. {synkit-0.0.5/synkit/Reactor → synkit-0.0.7/synkit/Synthesis/MSR}/path_finder.py +0 -0
  265. {synkit-0.0.5/synkit → synkit-0.0.7/synkit/Synthesis}/Metrics/_plot.py +0 -0
  266. {synkit-0.0.5/synkit → synkit-0.0.7/synkit/Synthesis}/Metrics/_ranking.py +0 -0
  267. {synkit-0.0.5 → synkit-0.0.7}/synkit/Utils/utils.py +0 -0
  268. {synkit-0.0.5 → synkit-0.0.7}/synkit/Vis/chemical_space.py +0 -0
  269. {synkit-0.0.5 → synkit-0.0.7}/synkit/Vis/embedding.py +0 -0
  270. {synkit-0.0.5 → synkit-0.0.7}/synkit/Vis/pdf_writer.py +0 -0
@@ -23,6 +23,7 @@ jobs:
23
23
  cache: 'pip' # caching pip dependencies
24
24
  - run: pip install -r requirements.txt
25
25
  - run: pip install sphinx sphinx-rtd-theme
26
+ - run: pip install sphinxcontrib-bibtex
26
27
  - run: python3 -m sphinx ./doc docs
27
28
  - name: publish doc
28
29
  shell: bash
@@ -0,0 +1,68 @@
1
+ name: Test & Lint
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main", "dev", "maintain", "refractor" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test-lint:
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ os: [ubuntu-latest, macos-latest, windows-latest]
18
+
19
+ runs-on: ${{ matrix.os }}
20
+
21
+ steps:
22
+ # 0) Check out the code
23
+ - uses: actions/checkout@v3
24
+
25
+ # 1) Install Miniconda (downloaded — the “bundled” version was removed)
26
+ - name: Set up Miniconda
27
+ uses: conda-incubator/setup-miniconda@v2
28
+ with:
29
+ miniconda-version: "latest" # <<–‑‑ mandatory or the action fails
30
+ python-version: "3.11"
31
+ auto-update-conda: true
32
+ environment-file: environment.yml
33
+ activate-environment: synkit-env
34
+ use-mamba: true # optional, just faster
35
+
36
+ # 2) Extra Linux‑only dependency: mod ≥ 0.17
37
+ - name: Install mod (Linux only)
38
+ if: matrix.os == 'ubuntu-latest'
39
+ run: conda run -n synkit-env conda install -c jakobandersen -c conda-forge "mod>=0.17" -y
40
+
41
+ # 3) Optional extra project deps via pip (requirements.txt)
42
+ - name: Install project requirements
43
+ if: hashFiles('requirements.txt') != ''
44
+ run: conda run -n synkit-env pip install -r requirements.txt
45
+
46
+ # 4a) Lint on Linux/macOS
47
+ - name: Lint (Unix)
48
+ if: matrix.os != 'windows-latest'
49
+ shell: bash -l {0}
50
+ run: |
51
+ conda activate synkit-env
52
+ bash lint.sh
53
+
54
+ # 4b) Lint on Windows
55
+ - name: Lint (Windows)
56
+ if: matrix.os == 'windows-latest'
57
+ shell: bash -l {0}
58
+ run: |
59
+ conda activate synkit-env
60
+ bash lint.sh
61
+
62
+
63
+ # 5) Test
64
+ - name: Test
65
+ shell: bash -l {0}
66
+ run: |
67
+ conda activate synkit-env
68
+ bash pytest.sh
@@ -4,19 +4,19 @@
4
4
  */catboost_info/*
5
5
  *.ipynb
6
6
  *.json
7
- test_mod.py
8
- test_format.py
9
- *dev_zone
10
7
  *.pkl.gz
11
- dev_scripts/*
12
8
  *.pdf
13
9
  out
14
10
  summary
15
- Data/Benchmark/its.json.gz
16
- Data/Benchmark/rc.json.gz
17
11
  *.log
18
12
  .coverage
19
13
  dev/*
20
14
  *.rdf
21
15
  Data/Testcase/hydro/aam.json.gz
22
16
  synkit/Chem/FG/*
17
+ test_syn_reactor.py
18
+ Data/Benchmark/*
19
+ # *.png
20
+ *.json.gz
21
+ run.sh
22
+ docs/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synkit
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: Utility for reaction modeling using graph grammar
5
5
  Project-URL: homepage, https://github.com/TieuLongPhan/SynKit
6
6
  Project-URL: source, https://github.com/TieuLongPhan/SynKit
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3
14
14
  Requires-Python: >=3.11
15
15
  Requires-Dist: networkx>=3.3
16
16
  Requires-Dist: pandas>=1.5.3
17
- Requires-Dist: rdkit>=2024.3.3
17
+ Requires-Dist: rdkit>=2025.3.1
18
18
  Requires-Dist: regex>=2024.11.6
19
19
  Requires-Dist: requests>=2.32.3
20
20
  Requires-Dist: scikit-learn>=1.4.0
@@ -17,7 +17,7 @@ class TestMoleculeFunctions(unittest.TestCase):
17
17
 
18
18
  def test_normalize_molecule(self):
19
19
  smi = "[Na]OC(=O)c1ccc(C[S+2]([O-])([O-]))cc1"
20
- expect = "O=C(O[Na])c1ccc(C[S](=O)=O)cc1"
20
+ expect = "O=C([O][Na])c1ccc(C[S](=O)=O)cc1"
21
21
  mol = Chem.MolFromSmiles(smi)
22
22
  normalized_mol = normalize_molecule(mol)
23
23
  self.assertIsInstance(normalized_mol, Chem.Mol)
@@ -0,0 +1,52 @@
1
+ import unittest
2
+ from rdkit import Chem
3
+ from synkit.Chem.Reaction.aam_utils import enumerate_tautomers, mapping_success_rate
4
+
5
+
6
+ class TestChemUtils(unittest.TestCase):
7
+ def test_enumerate_tautomers_simple(self):
8
+ # A simple keto-enol tautomerism: acetylacetone (CC(=O)CC=O) -> same product
9
+ reaction = "CC(=O)CC=O>>O"
10
+ tautomers = enumerate_tautomers(reaction)
11
+ # Should return a list with at least the original reaction
12
+ self.assertIsInstance(tautomers, list)
13
+ self.assertIn(reaction, tautomers)
14
+ # Each entry should be a valid reaction SMILES
15
+ for rsmi in tautomers:
16
+ self.assertIsInstance(rsmi, str)
17
+ parts = rsmi.split(">>")
18
+ self.assertEqual(len(parts), 2)
19
+ # Reactant and product part parseable by RDKit
20
+ self.assertIsNotNone(Chem.MolFromSmiles(parts[0]))
21
+ self.assertIsNotNone(Chem.MolFromSmiles(parts[1]))
22
+
23
+ def test_enumerate_tautomers_invalid(self):
24
+ # Invalid SMILES input
25
+ bad = "INVALID>>SMILES"
26
+ result = enumerate_tautomers(bad)
27
+ # Should return list with original
28
+ self.assertEqual(result, [bad])
29
+
30
+ def test_mapping_success_rate_normal(self):
31
+ data = ["C:1CC", "CCC", "O:3=O", ":5", "N"]
32
+ rate = mapping_success_rate(data)
33
+ # Entries with mapping: 'C:1CC', 'O:3=O', ':5' => 3/5 = 60.0%
34
+ self.assertEqual(rate, 60.0)
35
+
36
+ def test_mapping_success_rate_empty(self):
37
+ with self.assertRaises(ValueError):
38
+ mapping_success_rate([])
39
+
40
+ def test_mapping_success_rate_all(self):
41
+ data = [":1C", ":2", "N:3"]
42
+ rate = mapping_success_rate(data)
43
+ self.assertEqual(rate, 100.0)
44
+
45
+ def test_mapping_success_rate_none(self):
46
+ data = ["C", "O", "N"]
47
+ rate = mapping_success_rate(data)
48
+ self.assertEqual(rate, 0.0)
49
+
50
+
51
+ if __name__ == "__main__":
52
+ unittest.main()
@@ -1,5 +1,5 @@
1
1
  import unittest
2
- from synkit.Graph.ITS.aam_validator import AAMValidator
2
+ from synkit.Chem.Reaction.aam_validator import AAMValidator
3
3
 
4
4
 
5
5
  class TestAMMValidator(unittest.TestCase):
@@ -0,0 +1,52 @@
1
+ import unittest
2
+ import networkx as nx
3
+ from synkit.Chem.Reaction.canon_rsmi import CanonRSMI
4
+ from synkit.IO.chem_converter import rsmi_to_graph
5
+
6
+
7
+ class TestCanonRSMI(unittest.TestCase):
8
+ def setUp(self):
9
+ # Use generic backend for deterministic behavior
10
+ self.canon = CanonRSMI(backend="wl", wl_iterations=5)
11
+ # Example reaction SMILES with atom-map labels
12
+ self.input_rsmi = "[CH3:3][CH2:5][OH:10]>>[CH2:3]=[CH2:5].[OH2:10]"
13
+ # After expand_aam and canonicalisation, since backend is generic,
14
+ # we expect the SMILES to remain unchanged (identity)
15
+ self.expected_canonical = "[CH2:1]([CH3:2])[OH:3]>>[CH2:1]=[CH2:2].[OH2:3]"
16
+
17
+ def test_raw_and_canonical_rsmi(self):
18
+ result = self.canon.canonicalise(self.input_rsmi)
19
+ self.assertEqual(result.raw_rsmi, self.input_rsmi)
20
+ self.assertEqual(result.canonical_rsmi, self.expected_canonical)
21
+
22
+ def test_mapping_pairs(self):
23
+ result = self.canon(self.input_rsmi)
24
+ # Mapping pairs should preserve identity mapping
25
+ # mapping_pairs will be matching (map_new, map_old)
26
+ self.assertEqual(sorted(result.mapping_pairs), [(1, 5), (2, 3), (3, 10)])
27
+
28
+ def test_graphs_and_hashes(self):
29
+ result = self.canon(self.input_rsmi)
30
+ # raw graphs should parse correctly
31
+ r_raw, p_raw = rsmi_to_graph(self.input_rsmi)
32
+ self.assertIsInstance(result.raw_reactant_graph, nx.Graph)
33
+ self.assertIsInstance(result.raw_product_graph, nx.Graph)
34
+ # canonical graphs should also be Graph
35
+ self.assertIsInstance(result.canonical_reactant_graph, nx.Graph)
36
+ self.assertIsInstance(result.canonical_product_graph, nx.Graph)
37
+ # reactant_hash and product_hash should match underlying canonicaliser
38
+ h_reac = self.canon._canon.canonical_signature(result.canonical_reactant_graph)
39
+ h_prod = self.canon._canon.canonical_signature(result.canonical_product_graph)
40
+ # canonical_hash is combined
41
+ self.assertEqual(result.canonical_hash, f"{h_reac}>>{h_prod}")
42
+
43
+ def test_idempotence(self):
44
+ # Applying canonicalise twice yields same result
45
+ result1 = self.canon(self.input_rsmi)
46
+ result2 = self.canon(result1.canonical_rsmi)
47
+ self.assertEqual(result1.canonical_rsmi, result2.canonical_rsmi)
48
+ self.assertEqual(result1.mapping_pairs, result2.mapping_pairs)
49
+
50
+
51
+ if __name__ == "__main__":
52
+ unittest.main()
@@ -34,7 +34,7 @@ class TestDeionize(unittest.TestCase):
34
34
  def test_uncharge_smiles(self):
35
35
  charge_smiles = "[Na+].[OH-]"
36
36
  uncharged_smiles = Deionize.uncharge_smiles(charge_smiles)
37
- self.assertEqual(uncharged_smiles, "O[Na]") # can not uncharge amonium
37
+ self.assertEqual(uncharged_smiles, "[OH][Na]")
38
38
 
39
39
  def test_apply_uncharge_smiles_to_reactions(self):
40
40
  reactions = [{"reactants": "[NH4+].[OH-]", "products": "N.O"}]
@@ -3,7 +3,7 @@ import networkx as nx
3
3
  from synkit.IO.data_io import load_from_pickle
4
4
  from synkit.Graph.ITS.its_decompose import get_rc
5
5
  from synkit.Graph.Context.radius_expand import RadiusExpand
6
- from synkit.Graph.Cluster.graph_morphism import graph_isomorphism, subgraph_isomorphism
6
+ from synkit.Graph.Matcher.graph_morphism import graph_isomorphism, subgraph_isomorphism
7
7
 
8
8
 
9
9
  class TestRadiusExpand(unittest.TestCase):
@@ -183,7 +183,7 @@ class TestGraphDescriptor(unittest.TestCase):
183
183
 
184
184
  # Run the descriptor function
185
185
  results = GraphDescriptor.process_entries_in_parallel(
186
- self.data_parallel, "GraphRules", "ITSGraph", n_jobs=4
186
+ self.data_parallel, "GraphRules", "ITSGraph", n_jobs=1
187
187
  )
188
188
  self.assertEqual(results[0]["topo"], expected_output["topo"])
189
189
  self.assertEqual(results[0]["cycle"], expected_output["cycle"])
@@ -1,8 +1,8 @@
1
1
  import unittest
2
- from synkit.Graph.ITS.aam_validator import AAMValidator
3
- from synkit.Graph.Cluster.graph_morphism import graph_isomorphism
2
+ from synkit.Chem.Reaction.aam_validator import AAMValidator
3
+ from synkit.Graph.Matcher.graph_morphism import graph_isomorphism
4
4
  from synkit.IO.chem_converter import rsmi_to_graph, graph_to_rsmi
5
- from synkit.Graph.Hyrogen._misc import implicit_hydrogen, explicit_hydrogen
5
+ from synkit.Graph.Hyrogen._misc import implicit_hydrogen, h_to_explicit
6
6
 
7
7
 
8
8
  class TestGraphH(unittest.TestCase):
@@ -53,7 +53,7 @@ class TestGraphH(unittest.TestCase):
53
53
 
54
54
  def test_explicit_hydrogen(self):
55
55
  r_imp, p_imp = rsmi_to_graph(self.implicit_rsmi)
56
- r_ex, p_ex = explicit_hydrogen(r_imp), explicit_hydrogen(p_imp)
56
+ r_ex, p_ex = h_to_explicit(r_imp), h_to_explicit(p_imp)
57
57
  r, p = rsmi_to_graph(self.rsmi)
58
58
  self.assertTrue(graph_isomorphism(r, r_ex))
59
59
  self.assertTrue(graph_isomorphism(p, p_ex))
@@ -0,0 +1,92 @@
1
+ import unittest
2
+
3
+ from synkit.Graph.Hyrogen._misc import (
4
+ has_XH,
5
+ h_to_implicit,
6
+ h_to_explicit,
7
+ check_explicit_hydrogen,
8
+ check_hcount_change,
9
+ )
10
+ from synkit.IO.chem_converter import rsmi_to_its
11
+ from synkit.Graph.ITS.its_decompose import its_decompose
12
+
13
+
14
+ class TestHydrogenUtilities(unittest.TestCase):
15
+
16
+ def setUp(self):
17
+ # Explicit hydrogen example
18
+ self.rsmi_explicit = "[C:1]=[C:2].[H:3][H:4]>>[C:1]([H:3])[C:2][H:4]"
19
+ self.its_explicit = rsmi_to_its(self.rsmi_explicit)
20
+ self.rc_explicit = self.its_explicit
21
+ self.r_explicit, self.p_explicit = its_decompose(self.rc_explicit)
22
+
23
+ # Implicit hydrogen example
24
+ self.rsmi_implicit = "[C:1]=[C:2].[H:3][H:4]>>[CH:1][CH:2]"
25
+ self.its_implicit = rsmi_to_its(self.rsmi_implicit)
26
+ self.rc_implicit = self.its_implicit
27
+ self.r_implicit, self.p_implicit = its_decompose(self.rc_implicit)
28
+
29
+ def test_has_XH_explicit(self):
30
+ self.assertTrue(
31
+ has_XH(self.rc_explicit), "Explicit hydrogen bonds should be detected."
32
+ )
33
+
34
+ def test_has_XH_implicit(self):
35
+ self.assertFalse(
36
+ has_XH(self.rc_implicit),
37
+ "Implicit hydrogen representation should have no explicit X-H bonds.",
38
+ )
39
+
40
+ def test_h_to_implicit_removes_H_nodes(self):
41
+ G = self.rc_explicit
42
+ heavy_atoms = [n for n, d in G.nodes(data=True) if d.get("element") != "H"]
43
+ G_implicit = h_to_implicit(G)
44
+ h_nodes = [n for n, d in G_implicit.nodes(data=True) if d.get("element") == "H"]
45
+ self.assertEqual(len(h_nodes), 0, "All hydrogen nodes should be removed.")
46
+ for n in heavy_atoms:
47
+ self.assertGreaterEqual(
48
+ G_implicit.nodes[n].get("hcount", 0),
49
+ 1,
50
+ "Heavy atoms should have hcount ≥ 1.",
51
+ )
52
+
53
+ def test_h_to_explicit_adds_H_nodes(self):
54
+ G = self.rc_explicit
55
+ G_implicit = h_to_implicit(G)
56
+ heavy_atoms = [
57
+ n for n, d in G_implicit.nodes(data=True) if d.get("element") != "H"
58
+ ]
59
+ G_expanded = h_to_explicit(G_implicit, heavy_atoms)
60
+ h_nodes = [n for n, d in G_expanded.nodes(data=True) if d.get("element") == "H"]
61
+ self.assertGreaterEqual(len(h_nodes), 2, "Hydrogen nodes should be added back.")
62
+
63
+ def test_roundtrip_hydrogen_conversion(self):
64
+ G = self.rc_explicit
65
+ heavy_atoms = [n for n, d in G.nodes(data=True) if d.get("element") != "H"]
66
+ G1 = h_to_implicit(G)
67
+ G2 = h_to_explicit(G1, heavy_atoms)
68
+ h_nodes = [n for n, d in G2.nodes(data=True) if d.get("element") == "H"]
69
+ self.assertGreaterEqual(
70
+ len(h_nodes),
71
+ 2,
72
+ "Hydrogens should be recoverable after roundtrip conversion.",
73
+ )
74
+
75
+ def test_check_explicit_hydrogen(self):
76
+ count, ids = check_explicit_hydrogen(self.rc_explicit)
77
+ self.assertEqual(count, 2, "Should detect 2 explicit hydrogens.")
78
+ self.assertEqual(len(ids), 2)
79
+ for node_id in ids:
80
+ self.assertEqual(self.rc_explicit.nodes[node_id]["element"], "H")
81
+
82
+ def test_check_hcount_change_explicit(self):
83
+ delta = check_hcount_change(self.r_explicit, self.p_explicit)
84
+ self.assertEqual(delta, 2, "Expected hydrogen movement in explicit graph.")
85
+
86
+ def test_check_hcount_change_implicit(self):
87
+ delta = check_hcount_change(self.r_implicit, self.p_implicit)
88
+ self.assertEqual(delta, 2, "Expected hydrogen movement in implicit graph.")
89
+
90
+
91
+ if __name__ == "__main__":
92
+ unittest.main()
@@ -1,5 +1,5 @@
1
1
  import unittest
2
- from synkit.Graph.ITS.aam_validator import AAMValidator
2
+ from synkit.Chem.Reaction.aam_validator import AAMValidator
3
3
  from synkit.Graph.ITS.its_expand import ITSExpand
4
4
 
5
5
 
@@ -1,7 +1,7 @@
1
1
  import unittest
2
2
  import networkx as nx
3
3
  from synkit.Graph.ITS.normalize_aam import NormalizeAAM
4
- from synkit.Graph.ITS.aam_validator import AAMValidator
4
+ from synkit.Chem.Reaction.aam_validator import AAMValidator
5
5
 
6
6
 
7
7
  class TestNormalizeAAM(unittest.TestCase):
@@ -0,0 +1,52 @@
1
+ import unittest
2
+ from synkit.IO.chem_converter import rsmi_to_its
3
+ from synkit.Graph.MTG.groupoid import node_constraint
4
+ from synkit.Graph.MTG.group_comp import GroupComp
5
+
6
+
7
+ class TestGroupComp(unittest.TestCase):
8
+
9
+ def setUp(self) -> None:
10
+ test_1 = [
11
+ "[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH:4]([H:8])=[CH:5][O:6]([H:7])",
12
+ "[CH3:1][CH:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:7])>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]",
13
+ ]
14
+ self.test_graph_1 = [rsmi_to_its(var) for var in test_1]
15
+ test_2 = [
16
+ "[CH2:1]=[CH:2]-[CH2+:3]>>[CH2+:1]-[CH:2]=[CH2:3]",
17
+ "[H:1]-[CH2:2]-[CH2+:3]>>[CH2:2]=[CH2:3].[H+:1]",
18
+ ]
19
+ self.test_graph_2 = [rsmi_to_its(var) for var in test_2]
20
+
21
+ def test_get_mapping(self):
22
+ g = GroupComp(self.test_graph_1[0], self.test_graph_1[1])
23
+ m = g.get_mapping(include_singleton=False)
24
+ self.assertEqual(len(m), 4)
25
+
26
+ def test_get_mapping_singleton(self):
27
+ g = GroupComp(self.test_graph_1[0], self.test_graph_1[1])
28
+ m = g.get_mapping(include_singleton=True)
29
+ self.assertEqual(len(m), 10)
30
+
31
+ def test_get_mapping_from_nodes(self):
32
+ m0 = node_constraint(
33
+ self.test_graph_2[0].nodes(data=True), self.test_graph_2[1].nodes(data=True)
34
+ )
35
+ g = GroupComp(self.test_graph_2[0], self.test_graph_2[1])
36
+ m = g.get_mapping_from_nodes(
37
+ m0,
38
+ self.test_graph_2[0].edges(data=True),
39
+ self.test_graph_2[1].edges(data=True),
40
+ )
41
+ self.assertEqual(len(m), 1)
42
+
43
+ def test_get_mapping_fallback(self):
44
+ g = GroupComp(self.test_graph_2[0], self.test_graph_2[1])
45
+ m = g.get_mapping(
46
+ include_singleton=False
47
+ ) # even False if cannot find candidate will fall back
48
+ self.assertEqual(len(m), 1)
49
+
50
+
51
+ if __name__ == "__main__":
52
+ unittest.main()
@@ -0,0 +1,181 @@
1
+ import unittest
2
+ from synkit.IO.chem_converter import rsmi_to_its
3
+ from synkit.Graph.MTG.groupoid import charge_tuple, node_constraint, edge_constraint
4
+
5
+
6
+ class TestGroupoid(unittest.TestCase):
7
+
8
+ def setUp(self) -> None:
9
+ test_1 = [
10
+ "[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH:4]([H:8])=[CH:5][O:6]([H:7])",
11
+ "[CH3:1][CH:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:7])>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]",
12
+ ]
13
+ self.test_graph_1 = [rsmi_to_its(var) for var in test_1]
14
+ test_2 = [
15
+ "[CH2:1]=[CH:2]-[CH2+:3]>>[CH2+:1]-[CH:2]=[CH2:3]",
16
+ "[H:1]-[CH2:2]-[CH2+:3]>>[CH2:2]=[CH2:3].[H+:1]",
17
+ ]
18
+ self.test_graph_2 = [rsmi_to_its(var) for var in test_2]
19
+
20
+ def test_direct_charges(self):
21
+ attrs = {"charges": (0, 1)}
22
+ self.assertEqual(charge_tuple(attrs), (0, 1))
23
+
24
+ def test_both_fields_prioritize_charges(self):
25
+ attrs = {
26
+ "charges": (1, 2),
27
+ "typesGH": ((None, None, None, 3, None), (None, None, None, 4, None)),
28
+ }
29
+ # 'charges' should take precedence over 'typesGH'
30
+ self.assertEqual(charge_tuple(attrs), (1, 2))
31
+
32
+ def test_charges_not_tuple(self):
33
+ attrs = {
34
+ "charges": [0, 1], # not a tuple
35
+ "typesGH": ((None, None, None, 2, None), (None, None, None, 3, None)),
36
+ }
37
+ # Non-tuple 'charges' should be ignored in favor of typesGH
38
+ self.assertEqual(charge_tuple(attrs), (2, 3))
39
+
40
+ def test_charges_wrong_length(self):
41
+ attrs = {
42
+ "charges": (0,), # length != 2
43
+ "typesGH": ((None, None, None, 5, None), (None, None, None, 6, None)),
44
+ }
45
+ # Invalid 'charges' length => fallback to typesGH
46
+ self.assertEqual(charge_tuple(attrs), (5, 6))
47
+
48
+ def test_typesGH_valid(self):
49
+ attrs = {"typesGH": ((None, None, None, 7, None), (None, None, None, 8, None))}
50
+ self.assertEqual(charge_tuple(attrs), (7, 8))
51
+
52
+ def test_typesGH_too_short(self):
53
+ attrs = {"typesGH": ((None, None, None, 9, None),)} # only one tuple
54
+ # Not enough entries => (None, None)
55
+ self.assertEqual(charge_tuple(attrs), (None, None))
56
+
57
+ def test_typesGH_inner_exception(self):
58
+ attrs = {"typesGH": ((None, None), (None,))} # inner tuples too short
59
+ # Should catch exception and return (None, None)
60
+ self.assertEqual(charge_tuple(attrs), (None, None))
61
+
62
+ def test_no_fields(self):
63
+ # No relevant keys => (None, None)
64
+ self.assertEqual(charge_tuple({}), (None, None))
65
+
66
+ def test_node_constraint(self):
67
+ m1 = node_constraint(
68
+ self.test_graph_1[0].nodes(data=True), self.test_graph_1[1].nodes(data=True)
69
+ )
70
+ self.assertEqual(len(m1.keys()), 5)
71
+
72
+ m2 = node_constraint(
73
+ self.test_graph_2[0].nodes(data=True), self.test_graph_2[1].nodes(data=True)
74
+ )
75
+ self.assertEqual(len(m2.keys()), 3)
76
+
77
+ def test_edge_constraint_no_map(self):
78
+ m1 = edge_constraint(
79
+ self.test_graph_1[0].edges(data=True),
80
+ self.test_graph_1[1].edges(data=True),
81
+ algorithm="bt",
82
+ )
83
+ self.assertEqual(len(m1), 46) # backtracking
84
+
85
+ self.assertEqual(
86
+ len(
87
+ edge_constraint(
88
+ self.test_graph_1[0].edges(data=True),
89
+ self.test_graph_1[1].edges(data=True),
90
+ algorithm="vf2",
91
+ )
92
+ ),
93
+ 30,
94
+ ) # vf2
95
+
96
+ self.assertEqual(
97
+ len(
98
+ edge_constraint(
99
+ self.test_graph_1[0].edges(data=True),
100
+ self.test_graph_1[1].edges(data=True),
101
+ algorithm="vf3",
102
+ )
103
+ ),
104
+ 30,
105
+ ) # vf3
106
+
107
+ m2 = edge_constraint(
108
+ self.test_graph_2[0].edges(data=True), self.test_graph_2[1].edges(data=True)
109
+ )
110
+ self.assertEqual(len(m2), 2) # backtracking
111
+
112
+ self.assertEqual(
113
+ len(
114
+ edge_constraint(
115
+ self.test_graph_2[0].edges(data=True),
116
+ self.test_graph_2[1].edges(data=True),
117
+ algorithm="vf2",
118
+ )
119
+ ),
120
+ 2,
121
+ )
122
+
123
+ self.assertEqual(
124
+ len(
125
+ edge_constraint(
126
+ self.test_graph_2[0].edges(data=True),
127
+ self.test_graph_2[1].edges(data=True),
128
+ algorithm="vf3",
129
+ )
130
+ ),
131
+ 2,
132
+ )
133
+
134
+ def test_edge_constraint_map(self):
135
+ m0 = node_constraint(
136
+ self.test_graph_1[0].nodes(data=True), self.test_graph_1[1].nodes(data=True)
137
+ )
138
+ m1 = edge_constraint(
139
+ self.test_graph_1[0].edges(data=True),
140
+ self.test_graph_1[1].edges(data=True),
141
+ m0,
142
+ )
143
+ self.assertEqual(len(m1), 4)
144
+
145
+ self.assertEqual(
146
+ len(
147
+ edge_constraint(
148
+ self.test_graph_1[0].edges(data=True),
149
+ self.test_graph_1[1].edges(data=True),
150
+ m0,
151
+ algorithm="vf2",
152
+ )
153
+ ),
154
+ 1,
155
+ )
156
+
157
+ self.assertEqual(
158
+ len(
159
+ edge_constraint(
160
+ self.test_graph_1[0].edges(data=True),
161
+ self.test_graph_1[1].edges(data=True),
162
+ m0,
163
+ algorithm="vf3",
164
+ )
165
+ ),
166
+ 1,
167
+ )
168
+
169
+ m0 = node_constraint(
170
+ self.test_graph_2[0].nodes(data=True), self.test_graph_2[1].nodes(data=True)
171
+ )
172
+ m2 = edge_constraint(
173
+ self.test_graph_2[0].edges(data=True),
174
+ self.test_graph_2[1].edges(data=True),
175
+ m0,
176
+ )
177
+ self.assertEqual(len(m2), 0)
178
+
179
+
180
+ if __name__ == "__main__":
181
+ unittest.main()
@@ -0,0 +1,39 @@
1
+ import unittest
2
+ from synkit.IO.chem_converter import rsmi_to_its
3
+ from synkit.Graph.ITS.its_decompose import get_rc
4
+ from synkit.Graph.MTG.group_comp import GroupComp
5
+ from synkit.Graph.MTG.mtg import MTG
6
+
7
+
8
+ class TestMTG(unittest.TestCase):
9
+
10
+ def setUp(self) -> None:
11
+ test_1 = [
12
+ "[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH:4]([H:8])=[CH:5][O:6]([H:7])",
13
+ "[CH3:1][CH:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:7])>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]",
14
+ ]
15
+ self.test_graph_1 = [get_rc(rsmi_to_its(var)) for var in test_1]
16
+ test_2 = [
17
+ "[CH2:1]=[CH:2]-[CH2+:3]>>[CH2+:1]-[CH:2]=[CH2:3]",
18
+ "[H:1]-[CH2:2]-[CH2+:3]>>[CH2:2]=[CH2:3].[H+:1]",
19
+ ]
20
+ self.test_graph_2 = [get_rc(rsmi_to_its(var)) for var in test_2]
21
+
22
+ def test_MTG_1(self):
23
+ grp = GroupComp(self.test_graph_1[0], self.test_graph_1[1])
24
+ candidates = grp.get_mapping()
25
+ print(candidates)
26
+ mtg = MTG(self.test_graph_1[0], self.test_graph_1[1], candidates[0])
27
+ self.assertEqual(len(mtg.get_nodes()), 6)
28
+ self.assertEqual(len(mtg.get_edges()), 7)
29
+
30
+ def test_MTG_2(self):
31
+ grp = GroupComp(self.test_graph_2[0], self.test_graph_2[1])
32
+ candidates = grp.get_mapping()
33
+ mtg = MTG(self.test_graph_2[0], self.test_graph_2[1], candidates[0])
34
+ self.assertEqual(len(mtg.get_nodes()), 5)
35
+ self.assertEqual(len(mtg.get_edges()), 4)
36
+
37
+
38
+ if __name__ == "__main__":
39
+ unittest.main()