synkit 0.0.13__tar.gz → 0.0.15__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 (298) hide show
  1. {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/test-and-lint.yml +1 -1
  2. {synkit-0.0.13 → synkit-0.0.15}/.gitignore +3 -4
  3. {synkit-0.0.13 → synkit-0.0.15}/PKG-INFO +12 -1
  4. {synkit-0.0.13 → synkit-0.0.15}/README.md +11 -0
  5. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_its_construction.py +14 -14
  6. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/test_mtg.py +7 -9
  7. synkit-0.0.15/Test/Graph/Wildcard/test_radwc.py +55 -0
  8. synkit-0.0.15/Test/Graph/Wildcard/test_wildcard.py +81 -0
  9. synkit-0.0.15/Test/IO/combinatorial/test_smarts_expander.py +38 -0
  10. synkit-0.0.15/Test/IO/combinatorial/test_smarts_generalizer.py +62 -0
  11. synkit-0.0.15/Test/IO/combinatorial/test_smarts_to_graph.py +85 -0
  12. synkit-0.0.15/Test/Synthesis/Reactor/test_imba_engine.py +94 -0
  13. synkit-0.0.15/doc/figures/mtg_mechanism.png +0 -0
  14. {synkit-0.0.13 → synkit-0.0.15}/doc/graph.rst +64 -32
  15. {synkit-0.0.13 → synkit-0.0.15}/lint.sh +5 -1
  16. {synkit-0.0.13 → synkit-0.0.15}/pyproject.toml +1 -1
  17. {synkit-0.0.13 → synkit-0.0.15}/recipe/meta.yaml +1 -1
  18. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/radical_wildcard.py +57 -0
  19. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/utils.py +39 -0
  20. synkit-0.0.15/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +231 -0
  21. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Hyrogen/_misc.py +51 -90
  22. synkit-0.0.15/synkit/Graph/ITS/its_construction.py +316 -0
  23. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_decompose.py +283 -224
  24. synkit-0.0.15/synkit/Graph/ITS/its_destruction.py +302 -0
  25. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/MTG/mcs_matcher.py +67 -25
  26. synkit-0.0.15/synkit/Graph/MTG/mtg.py +886 -0
  27. synkit-0.0.15/synkit/Graph/MTG/mtg_explore.py +74 -0
  28. synkit-0.0.15/synkit/Graph/MTG/utils.py +425 -0
  29. synkit-0.0.15/synkit/Graph/Matcher/subgraph_matcher.py +1162 -0
  30. synkit-0.0.15/synkit/Graph/Wildcard/radwc.py +117 -0
  31. synkit-0.0.15/synkit/Graph/Wildcard/wildcard.py +230 -0
  32. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/__init__.py +1 -0
  33. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/utils.py +25 -0
  34. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/chem_converter.py +11 -2
  35. synkit-0.0.15/synkit/IO/combinatorial/__init__.py +8 -0
  36. synkit-0.0.15/synkit/IO/combinatorial/gml_to_graph.py +254 -0
  37. synkit-0.0.15/synkit/IO/combinatorial/graph_to_gml.py +291 -0
  38. synkit-0.0.15/synkit/IO/combinatorial/graph_to_smarts.py +189 -0
  39. synkit-0.0.15/synkit/IO/combinatorial/smarts_expander.py +152 -0
  40. synkit-0.0.15/synkit/IO/combinatorial/smarts_generalizer.py +134 -0
  41. synkit-0.0.15/synkit/IO/combinatorial/smarts_to_graph.py +183 -0
  42. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/nx_to_gml.py +2 -1
  43. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/rule_matcher.py +15 -5
  44. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/syn_rule.py +3 -1
  45. synkit-0.0.15/synkit/Synthesis/Reactor/batch_reactor.py +462 -0
  46. synkit-0.0.15/synkit/Synthesis/Reactor/benchmark.py +152 -0
  47. synkit-0.0.15/synkit/Synthesis/Reactor/imba_engine.py +173 -0
  48. synkit-0.0.15/synkit/Synthesis/Reactor/post_syn.py +267 -0
  49. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/syn_reactor.py +26 -5
  50. synkit-0.0.15/synkit/Synthesis/__init__.py +0 -0
  51. synkit-0.0.15/synkit/Utils/__init__.py +0 -0
  52. {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/graph_visualizer.py +9 -1
  53. {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/rule_vis.py +2 -2
  54. synkit-0.0.15/synkit/__init__.py +0 -0
  55. synkit-0.0.15/synkit/examples.py +50 -0
  56. synkit-0.0.13/Test/Synthesis/Reactor/test_core_engine.py +0 -112
  57. synkit-0.0.13/synkit/Graph/ITS/its_construction.py +0 -325
  58. synkit-0.0.13/synkit/Graph/MTG/mtg.py +0 -208
  59. synkit-0.0.13/synkit/Graph/Matcher/subgraph_matcher.py +0 -550
  60. synkit-0.0.13/synkit/Synthesis/Reactor/batch_reactor.py +0 -225
  61. {synkit-0.0.13 → synkit-0.0.15}/.github/dependabot.yml +0 -0
  62. {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/build-doc.yml +0 -0
  63. {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/conda-forge-publish.yml +0 -0
  64. {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/docker-publish.yml +0 -0
  65. {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/publish-package.yml +0 -0
  66. {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/verify-pypi-install.yml +0 -0
  67. {synkit-0.0.13 → synkit-0.0.15}/.readthedocs.yml +0 -0
  68. {synkit-0.0.13 → synkit-0.0.15}/Data/Figure/synkit.png +0 -0
  69. {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
  70. {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
  71. {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
  72. {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
  73. {synkit-0.0.13 → synkit-0.0.15}/Dockerfile +0 -0
  74. {synkit-0.0.13 → synkit-0.0.15}/LICENSE +0 -0
  75. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/__init__.py +0 -0
  76. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
  77. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
  78. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
  79. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Molecule/__init__.py +0 -0
  80. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Molecule/test_standardize.py +0 -0
  81. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/__init__.py +0 -0
  82. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_aam_validator.py +0 -0
  83. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_balance_checker.py +0 -0
  84. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_canon_rsmi.py +0 -0
  85. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_cleanning.py +0 -0
  86. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_deionize.py +0 -0
  87. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_fix_aam.py +0 -0
  88. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_neutralize.py +0 -0
  89. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_radical_wildcard.py +0 -0
  90. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_standardize.py +0 -0
  91. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_tautomerize.py +0 -0
  92. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/__init__.py +0 -0
  93. {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/test_utils.py +0 -0
  94. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Context/__init__.py +0 -0
  95. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Context/test_hier_context.py +0 -0
  96. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Context/test_radius_expand.py +0 -0
  97. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/__init__.py +0 -0
  98. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
  99. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_graph_fps.py +0 -0
  100. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_graph_signature.py +0 -0
  101. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_hash_fps.py +0 -0
  102. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_morgan_fps.py +0 -0
  103. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_path_fps.py +0 -0
  104. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/__init__.py +0 -0
  105. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/test_graph_hydrogen.py +0 -0
  106. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/test_hcomplete.py +0 -0
  107. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/test_misc.py +0 -0
  108. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/__init__.py +0 -0
  109. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_its_expand.py +0 -0
  110. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_its_relabel.py +0 -0
  111. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_normalize_aam.py +0 -0
  112. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/__init__.py +0 -0
  113. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/test_group_comp.py +0 -0
  114. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/test_groupoid.py +0 -0
  115. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/__init__.py +0 -0
  116. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_batch_cluster.py +0 -0
  117. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_cluster.py +0 -0
  118. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_matcher.py +0 -0
  119. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_morphism.py +0 -0
  120. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_subgraph_matcher.py +0 -0
  121. {synkit-0.0.13/Test/Graph → synkit-0.0.15/Test/Graph/Wildcard}/__init__.py +0 -0
  122. {synkit-0.0.13/Test/IO → synkit-0.0.15/Test/Graph}/__init__.py +0 -0
  123. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/test_canon_graph.py +0 -0
  124. {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/test_syn_graph.py +0 -0
  125. {synkit-0.0.13/Test/Rule/Apply → synkit-0.0.15/Test/IO}/__init__.py +0 -0
  126. {synkit-0.0.13/Test/Rule/Compose → synkit-0.0.15/Test/IO/combinatorial}/__init__.py +0 -0
  127. {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_chemical_converter.py +0 -0
  128. {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_dg_to_gml.py +0 -0
  129. {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_gml_to_nx.py +0 -0
  130. {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_graph_to_mol.py +0 -0
  131. {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_mol_to_graph.py +0 -0
  132. {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_nx_to_gml.py +0 -0
  133. {synkit-0.0.13/Test/Rule/Modify → synkit-0.0.15/Test/Rule/Apply}/__init__.py +0 -0
  134. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_reactor_rule.py +0 -0
  135. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_retro_reactor.py +0 -0
  136. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_rule_matcher.py +0 -0
  137. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_rule_rbl.py +0 -0
  138. {synkit-0.0.13/Test/Rule → synkit-0.0.15/Test/Rule/Compose}/__init__.py +0 -0
  139. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Compose/test_rule_compose.py +0 -0
  140. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Compose/test_valance_constrain.py +0 -0
  141. {synkit-0.0.13/Test/Synthesis/CRN → synkit-0.0.15/Test/Rule/Modify}/__init__.py +0 -0
  142. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Modify/test_molecule_rule.py +0 -0
  143. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Modify/test_rule_utils.py +0 -0
  144. {synkit-0.0.13/Test/Synthesis/MSR → synkit-0.0.15/Test/Rule}/__init__.py +0 -0
  145. {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/test_syn_rule.py +0 -0
  146. {synkit-0.0.13/Test/Synthesis/Reactor → synkit-0.0.15/Test/Synthesis/CRN}/__init__.py +0 -0
  147. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/CRN/test_crn.py +0 -0
  148. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/CRN/test_mod_crn.py +0 -0
  149. {synkit-0.0.13/Test/Synthesis → synkit-0.0.15/Test/Synthesis/MSR}/__init__.py +0 -0
  150. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/MSR/test_multi_steps.py +0 -0
  151. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/MSR/test_path_finder.py +0 -0
  152. {synkit-0.0.13/Test/Vis → synkit-0.0.15/Test/Synthesis/Reactor}/__init__.py +0 -0
  153. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_mod_aam.py +0 -0
  154. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_mod_reactor.py +0 -0
  155. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_partial_engine.py +0 -0
  156. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_rbl_reactor.py +0 -0
  157. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_strategy.py +0 -0
  158. {synkit-0.0.13/Test → synkit-0.0.15/Test/Synthesis}/__init__.py +0 -0
  159. {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/test_reactor_utils.py +0 -0
  160. {synkit-0.0.13/synkit/Chem/Cluster → synkit-0.0.15/Test/Vis}/__init__.py +0 -0
  161. {synkit-0.0.13 → synkit-0.0.15}/Test/Vis/test_embedding.py +0 -0
  162. {synkit-0.0.13/synkit/Chem/Fingerprint → synkit-0.0.15/Test}/__init__.py +0 -0
  163. {synkit-0.0.13 → synkit-0.0.15}/build-doc.sh +0 -0
  164. {synkit-0.0.13 → synkit-0.0.15}/doc/api.rst +0 -0
  165. {synkit-0.0.13 → synkit-0.0.15}/doc/changelog.rst +0 -0
  166. {synkit-0.0.13 → synkit-0.0.15}/doc/chem.rst +0 -0
  167. {synkit-0.0.13 → synkit-0.0.15}/doc/conf.py +0 -0
  168. {synkit-0.0.13 → synkit-0.0.15}/doc/figures/aldol.png +0 -0
  169. {synkit-0.0.13 → synkit-0.0.15}/doc/figures/aldol_its.png +0 -0
  170. {synkit-0.0.13 → synkit-0.0.15}/doc/figures/context.png +0 -0
  171. {synkit-0.0.13 → synkit-0.0.15}/doc/figures/mtg.png +0 -0
  172. {synkit-0.0.13 → synkit-0.0.15}/doc/getting_started.rst +0 -0
  173. {synkit-0.0.13 → synkit-0.0.15}/doc/index.rst +0 -0
  174. {synkit-0.0.13 → synkit-0.0.15}/doc/io.rst +0 -0
  175. {synkit-0.0.13 → synkit-0.0.15}/doc/reference.rst +0 -0
  176. {synkit-0.0.13 → synkit-0.0.15}/doc/refs.bib +0 -0
  177. {synkit-0.0.13 → synkit-0.0.15}/doc/requirements.txt +0 -0
  178. {synkit-0.0.13 → synkit-0.0.15}/doc/rule.rst +0 -0
  179. {synkit-0.0.13 → synkit-0.0.15}/doc/synthesis.rst +0 -0
  180. {synkit-0.0.13 → synkit-0.0.15}/environment.yml +0 -0
  181. {synkit-0.0.13 → synkit-0.0.15}/pytest.sh +0 -0
  182. {synkit-0.0.13 → synkit-0.0.15}/requirements.txt +0 -0
  183. {synkit-0.0.13/synkit/Chem/Molecule → synkit-0.0.15/synkit/Chem/Cluster}/__init__.py +0 -0
  184. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Cluster/butina.py +0 -0
  185. {synkit-0.0.13/synkit/Data → synkit-0.0.15/synkit/Chem/Fingerprint}/__init__.py +0 -0
  186. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
  187. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
  188. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  189. {synkit-0.0.13/synkit/Graph/Context → synkit-0.0.15/synkit/Chem/Molecule}/__init__.py +0 -0
  190. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Molecule/standardize.py +0 -0
  191. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/__init__.py +0 -0
  192. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/aam_validator.py +0 -0
  193. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/balance_check.py +0 -0
  194. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
  195. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/cleaning.py +0 -0
  196. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/deionize.py +0 -0
  197. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/fix_aam.py +0 -0
  198. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/neutralize.py +0 -0
  199. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/standardize.py +0 -0
  200. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/tautomerize.py +0 -0
  201. {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/__init__.py +0 -0
  202. {synkit-0.0.13/synkit/Graph/Hyrogen → synkit-0.0.15/synkit/Data}/__init__.py +0 -0
  203. {synkit-0.0.13 → synkit-0.0.15}/synkit/Data/gen_partial_aam.py +0 -0
  204. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/__init__.py +0 -0
  205. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/canon_algs.py +0 -0
  206. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/canon_graph.py +0 -0
  207. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/nauty.py +0 -0
  208. {synkit-0.0.13/synkit/Graph/MTG → synkit-0.0.15/synkit/Graph/Context}/__init__.py +0 -0
  209. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Context/hier_context.py +0 -0
  210. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Context/radius_expand.py +0 -0
  211. {synkit-0.0.13/synkit/Graph/Wildcard → synkit-0.0.15/synkit/Graph/Feature/Fingerprint}/__init__.py +0 -0
  212. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/__init__.py +0 -0
  213. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  214. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/graph_fps.py +0 -0
  215. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/graph_signature.py +0 -0
  216. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/hash_fps.py +0 -0
  217. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/morgan_fps.py +0 -0
  218. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/path_fps.py +0 -0
  219. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/wl_hash.py +0 -0
  220. {synkit-0.0.13/synkit/Rule/Apply → synkit-0.0.15/synkit/Graph/Hyrogen}/__init__.py +0 -0
  221. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
  222. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Hyrogen/hextend.py +0 -0
  223. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/__init__.py +0 -0
  224. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_builder.py +0 -0
  225. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_expand.py +0 -0
  226. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_relabel.py +0 -0
  227. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/normalize_aam.py +0 -0
  228. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/partial_its.py +0 -0
  229. {synkit-0.0.13/synkit/Rule/Compose → synkit-0.0.15/synkit/Graph/MTG}/__init__.py +0 -0
  230. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/MTG/group_comp.py +0 -0
  231. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/MTG/groupoid.py +0 -0
  232. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/__init__.py +0 -0
  233. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/batch_cluster.py +0 -0
  234. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/graph_cluster.py +0 -0
  235. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/graph_matcher.py +0 -0
  236. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/graph_morphism.py +0 -0
  237. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
  238. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
  239. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/partial_matcher.py +0 -0
  240. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/sing.py +0 -0
  241. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/turbo_iso.py +0 -0
  242. {synkit-0.0.13/synkit/Rule/Modify → synkit-0.0.15/synkit/Graph/Wildcard}/__init__.py +0 -0
  243. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
  244. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/canon_graph.py +0 -0
  245. {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/syn_graph.py +0 -0
  246. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/__init__.py +0 -0
  247. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/data_io.py +0 -0
  248. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/data_process.py +0 -0
  249. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/debug.py +0 -0
  250. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/dg_to_gml.py +0 -0
  251. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/gml_to_nx.py +0 -0
  252. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/graph_to_mol.py +0 -0
  253. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/mol_to_graph.py +0 -0
  254. {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/smiles_to_id.py +0 -0
  255. {synkit-0.0.13/synkit/Synthesis/CRN → synkit-0.0.15/synkit/Rule/Apply}/__init__.py +0 -0
  256. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/reactor_rule.py +0 -0
  257. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/retro_reactor.py +0 -0
  258. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/rule_rbl.py +0 -0
  259. {synkit-0.0.13/synkit/Synthesis/MSR → synkit-0.0.15/synkit/Rule/Compose}/__init__.py +0 -0
  260. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/compose_rule.py +0 -0
  261. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/rule_compose.py +0 -0
  262. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/rule_mapping.py +0 -0
  263. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/seq_comp.py +0 -0
  264. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/valence_constrain.py +0 -0
  265. {synkit-0.0.13/synkit/Synthesis/Metrics → synkit-0.0.15/synkit/Rule/Modify}/__init__.py +0 -0
  266. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/implict_rule.py +0 -0
  267. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/longest_path.py +0 -0
  268. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/molecule_rule.py +0 -0
  269. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/prune_templates.py +0 -0
  270. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/rule_utils.py +0 -0
  271. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/strip_rule.py +0 -0
  272. {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/__init__.py +0 -0
  273. {synkit-0.0.13/synkit/Synthesis/Reactor → synkit-0.0.15/synkit/Synthesis/CRN}/__init__.py +0 -0
  274. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/CRN/crn.py +0 -0
  275. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/CRN/dcrn.py +0 -0
  276. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/CRN/mod_crn.py +0 -0
  277. {synkit-0.0.13/synkit/Synthesis → synkit-0.0.15/synkit/Synthesis/MSR}/__init__.py +0 -0
  278. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/MSR/multi_steps.py +0 -0
  279. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/MSR/path_finder.py +0 -0
  280. {synkit-0.0.13/synkit/Utils → synkit-0.0.15/synkit/Synthesis/Metrics}/__init__.py +0 -0
  281. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Metrics/_base.py +0 -0
  282. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Metrics/_plot.py +0 -0
  283. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Metrics/_ranking.py +0 -0
  284. {synkit-0.0.13/synkit → synkit-0.0.15/synkit/Synthesis/Reactor}/__init__.py +0 -0
  285. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
  286. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
  287. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
  288. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
  289. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
  290. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
  291. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/strategy.py +0 -0
  292. {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/reactor_utils.py +0 -0
  293. {synkit-0.0.13 → synkit-0.0.15}/synkit/Utils/utils.py +0 -0
  294. {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/__init__.py +0 -0
  295. {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/chemical_space.py +0 -0
  296. {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/embedding.py +0 -0
  297. {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/pdf_writer.py +0 -0
  298. {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/rxn_vis.py +0 -0
@@ -2,7 +2,7 @@ name: Test & Lint
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [ "main", "dev", "staging", "refractor" ]
5
+ branches: [ "main", "partialits", "staging", "refractor" ]
6
6
  pull_request:
7
7
  branches: [ "main" ]
8
8
 
@@ -6,14 +6,10 @@
6
6
  *.json
7
7
  *.pkl.gz
8
8
  *.pdf
9
- out
10
- summary
11
9
  *.log
12
10
  .coverage
13
11
  dev/*
14
12
  *.rdf
15
- Data/Testcase/hydro/aam.json.gz
16
- synkit/Chem/FG/*
17
13
  test_syn_reactor.py
18
14
  Data/Benchmark/*
19
15
  # *.png
@@ -22,3 +18,6 @@ run.sh
22
18
  docs/*
23
19
  run_rdcanon.py
24
20
  Data/Fragment/*
21
+ test_partial.py
22
+ Data/Benchmark/synthesis/*
23
+ Data/USPTO/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synkit
3
- Version: 0.0.13
3
+ Version: 0.0.15
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
@@ -30,6 +30,17 @@ Requires-Dist: sphinxcontrib-bibtex; extra == 'docs'
30
30
  Description-Content-Type: text/markdown
31
31
 
32
32
  # SynKit
33
+ [![PyPI version](https://img.shields.io/pypi/v/synkit.svg)](https://pypi.org/project/synkit/)
34
+ [![Conda version](https://img.shields.io/conda/vn/tieulongphan/synkit.svg)](https://anaconda.org/tieulongphan/synkit)
35
+ [![Docker Pulls](https://img.shields.io/docker/pulls/tieulongphan/synkit.svg)](https://hub.docker.com/r/tieulongphan/synkit)
36
+ [![Docker Image Version](https://img.shields.io/docker/v/tieulongphan/synkit/latest?label=container)](https://hub.docker.com/r/tieulongphan/synkit)
37
+ [![License](https://img.shields.io/github/license/tieulongphan/synkit.svg)](https://github.com/tieulongphan/synkit/blob/main/LICENSE)
38
+ [![Release](https://img.shields.io/github/v/release/tieulongphan/synkit.svg)](https://github.com/tieulongphan/synkit/releases)
39
+ [![Last Commit](https://img.shields.io/github/last-commit/tieulongphan/synkit.svg)](https://github.com/tieulongphan/synkit/commits)
40
+ [![Zenodo](https://zenodo.org/badge/DOI/10.5281/zenodo.15269901.svg)](https://doi.org/10.5281/zenodo.15269901)
41
+ [![CI](https://github.com/tieulongphan/synkit/actions/workflows/test-and-lint.yml/badge.svg?branch=main)](https://github.com/tieulongphan/synkit/actions/workflows/test-and-lint.yml)
42
+ [![Dependency PRs](https://img.shields.io/github/issues-pr-raw/tieulongphan/synkit?label=dependency%20PRs)](https://github.com/tieulongphan/synkit/pulls?q=is%3Apr+label%3Adependencies)
43
+ [![Stars](https://img.shields.io/github/stars/tieulongphan/synkit.svg?style=social&label=Star)](https://github.com/tieulongphan/synkit/stargazers)
33
44
 
34
45
  **Toolkit for Synthesis Planning**
35
46
 
@@ -1,4 +1,15 @@
1
1
  # SynKit
2
+ [![PyPI version](https://img.shields.io/pypi/v/synkit.svg)](https://pypi.org/project/synkit/)
3
+ [![Conda version](https://img.shields.io/conda/vn/tieulongphan/synkit.svg)](https://anaconda.org/tieulongphan/synkit)
4
+ [![Docker Pulls](https://img.shields.io/docker/pulls/tieulongphan/synkit.svg)](https://hub.docker.com/r/tieulongphan/synkit)
5
+ [![Docker Image Version](https://img.shields.io/docker/v/tieulongphan/synkit/latest?label=container)](https://hub.docker.com/r/tieulongphan/synkit)
6
+ [![License](https://img.shields.io/github/license/tieulongphan/synkit.svg)](https://github.com/tieulongphan/synkit/blob/main/LICENSE)
7
+ [![Release](https://img.shields.io/github/v/release/tieulongphan/synkit.svg)](https://github.com/tieulongphan/synkit/releases)
8
+ [![Last Commit](https://img.shields.io/github/last-commit/tieulongphan/synkit.svg)](https://github.com/tieulongphan/synkit/commits)
9
+ [![Zenodo](https://zenodo.org/badge/DOI/10.5281/zenodo.15269901.svg)](https://doi.org/10.5281/zenodo.15269901)
10
+ [![CI](https://github.com/tieulongphan/synkit/actions/workflows/test-and-lint.yml/badge.svg?branch=main)](https://github.com/tieulongphan/synkit/actions/workflows/test-and-lint.yml)
11
+ [![Dependency PRs](https://img.shields.io/github/issues-pr-raw/tieulongphan/synkit?label=dependency%20PRs)](https://github.com/tieulongphan/synkit/pulls?q=is%3Apr+label%3Adependencies)
12
+ [![Stars](https://img.shields.io/github/stars/tieulongphan/synkit.svg?style=social&label=Star)](https://github.com/tieulongphan/synkit/stargazers)
2
13
 
3
14
  **Toolkit for Synthesis Planning**
4
15
 
@@ -31,20 +31,20 @@ class TestITSConstruction(unittest.TestCase):
31
31
  attributes = ITSConstruction().get_node_attributes_with_defaults(self.G, 1)
32
32
  self.assertEqual(attributes, ("C", False, 2, 0, ["", ""]))
33
33
 
34
- def test_add_edges_to_ITS(self):
35
- ITS = nx.Graph()
36
- ITS.add_node(1, element="C", aromatic=False, hcount=3, charge=0)
37
- ITS.add_node(2, element="C", aromatic=False, hcount=3, charge=0)
38
- new_ITS = ITSConstruction().add_edges_to_ITS(ITS, self.G, self.H)
39
- self.assertTrue(isinstance(new_ITS, nx.Graph))
40
- self.assertEqual(len(new_ITS.edges()), 1)
41
- self.assertEqual(new_ITS[1][2]["order"], (2, 1))
42
-
43
- def test_add_standard_order_attribute(self):
44
- graph = nx.Graph()
45
- graph.add_edge(1, 2, order=(1, 2))
46
- updated_graph = ITSConstruction().add_standard_order_attribute(graph)
47
- self.assertEqual(updated_graph[1][2]["standard_order"], -1)
34
+ # def test_add_edges_to_ITS(self):
35
+ # ITS = nx.Graph()
36
+ # ITS.add_node(1, element="C", aromatic=False, hcount=3, charge=0)
37
+ # ITS.add_node(2, element="C", aromatic=False, hcount=3, charge=0)
38
+ # new_ITS = ITSConstruction().add_edges_to_ITS(ITS, self.G, self.H)
39
+ # self.assertTrue(isinstance(new_ITS, nx.Graph))
40
+ # self.assertEqual(len(new_ITS.edges()), 1)
41
+ # self.assertEqual(new_ITS[1][2]["order"], (2, 1))
42
+
43
+ # def test_add_standard_order_attribute(self):
44
+ # graph = nx.Graph()
45
+ # graph.add_edge(1, 2, order=(1, 2))
46
+ # updated_graph = ITSConstruction().add_standard_order_attribute(graph)
47
+ # self.assertEqual(updated_graph[1][2]["standard_order"], -1)
48
48
 
49
49
 
50
50
  if __name__ == "__main__":
@@ -20,19 +20,17 @@ class TestMTG(unittest.TestCase):
20
20
  self.test_graph_2 = [get_rc(rsmi_to_its(var)) for var in test_2]
21
21
 
22
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)
23
+ mtg = MTG(self.test_graph_1[0:2], mcs_mol=True)
24
+ self.assertEqual(mtg._graph.number_of_nodes(), 6)
25
+ self.assertEqual(mtg._graph.number_of_edges(), 7)
29
26
 
30
27
  def test_MTG_2(self):
31
28
  grp = GroupComp(self.test_graph_2[0], self.test_graph_2[1])
32
29
  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)
30
+ # print(candidates)
31
+ mtg = MTG(self.test_graph_2[0:], candidates)
32
+ self.assertEqual(mtg._graph.number_of_nodes(), 5)
33
+ self.assertEqual(mtg._graph.number_of_edges(), 4)
36
34
 
37
35
 
38
36
  if __name__ == "__main__":
@@ -0,0 +1,55 @@
1
+ import unittest
2
+ from synkit.Graph.Wildcard.radwc import RadWC
3
+
4
+
5
+ class TestRadWC(unittest.TestCase):
6
+ def test_no_product_radicals(self):
7
+ """If product has no radicals, output should be unchanged."""
8
+ rxn = "[CH3:1][OH:2]>>[CH3:1][OH:2]"
9
+ self.assertEqual(RadWC.transform(rxn), rxn)
10
+
11
+ def test_single_radical_in_product(self):
12
+ """A single radical in product gets a wildcard."""
13
+ rxn = "[CH3:1][OH:2]>>[CH2:1].[OH:2]"
14
+ out = RadWC.transform(rxn)
15
+ # Check [*:3] is attached to [CH2:1]
16
+ self.assertIn("[CH2:1]([*:3])", out) # Atom-maps: 1,2 exist, so 3 is next
17
+
18
+ def test_multiple_radicals_in_product(self):
19
+ """Multiple radicals in product get multiple wildcards."""
20
+ rxn = "[CH3:1][OH:2]>>[CH2:1].[O:2]"
21
+ out = RadWC.transform(rxn)
22
+ # [CH2:1] has *:3 and *:4, [O:2] has *:5
23
+ self.assertIn("[CH2:1]([*:3])", out)
24
+ self.assertIn("[O:2]([*:5])", out)
25
+
26
+ def test_radical_and_nonradical_mixture(self):
27
+ """Mixed radical/non-radical product fragments, only radicals get wildcard."""
28
+ rxn = "[CH3:1][OH:2]>>[CH2:1].[OH:2]"
29
+ out = RadWC.transform(rxn)
30
+ # [CH2:1] gets *:3, [OH:2] is unchanged
31
+ self.assertIn("[CH2:1]([*:3])", out)
32
+ self.assertIn("[OH:2]", out)
33
+
34
+ def test_user_start_map(self):
35
+ """User-supplied map index is used for wildcards."""
36
+ rxn = "[CH3:7][OH:8]>>[CH2:7].[OH:8]"
37
+ out = RadWC.transform(rxn, start_map=50)
38
+ self.assertIn("[CH2:7]([*:50])", out)
39
+
40
+ def test_empty_reaction(self):
41
+ """Empty input should raise ValueError."""
42
+ with self.assertRaises(ValueError):
43
+ RadWC.transform("")
44
+
45
+ def test_three_component(self):
46
+ """Agent block is preserved."""
47
+ rxn = "[CH3:1][OH:2]>[Na+]>[CH2:1].[OH:2]"
48
+ out = RadWC.transform(rxn)
49
+ self.assertTrue(out.startswith("[CH3:1][OH:2]>[Na+]>"))
50
+ self.assertIn("[CH2:1]([*:3])", out)
51
+ self.assertIn("[OH:2]", out)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ unittest.main()
@@ -0,0 +1,81 @@
1
+ import unittest
2
+ from synkit.IO import rsmi_to_graph
3
+ from synkit.Graph.Wildcard.wildcard import WildCard
4
+
5
+
6
+ class TestWildCard(unittest.TestCase):
7
+ def setUp(self):
8
+ # The main, complex test case with atom mapping
9
+ self.rsmi_main = (
10
+ "[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4][c:31]2[NH2:28]."
11
+ "[cH:2]1[c:20]([C:22]([OH:7])=[O:21])[s:18][c:24]([S:6][c:29]2[c:15]([Cl:26])[cH:8]"
12
+ "[n:19][cH:9][c:16]2[Cl:27])[c:30]1[N+:5]([O-:3])=[O:13]>>"
13
+ "[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4][c:31]2[NH:28]"
14
+ "[C:22]([c:20]1[cH:2][c:30]([N+:5]([O-:3])=[O:13])[c:24]([S:6][c:29]2[c:15]([Cl:26])"
15
+ "[cH:8][n:19][cH:9][c:16]2[Cl:27])[s:18]1)=[O:21]"
16
+ )
17
+ # No atoms lost: R == P, should not add wildcards
18
+ self.rsmi_no_loss = "CCO>>CCO"
19
+ # All atoms lost: RSMI that loses everything (nonsense, but good test)
20
+ self.rsmi_all_lost = "CCO>>"
21
+ # Empty
22
+ self.rsmi_empty = ""
23
+ # Wildcard already present
24
+ self.rsmi_existing_wildcard = "[CH3:1][CH2:2][OH:3]>>[CH2:1][CH2:2].[*:4][OH:3]"
25
+ # No atom map (should raise error)
26
+ self.rsmi_no_atom_map = "C(C)Cl>>CC"
27
+
28
+ def test_main_case_wildcard_added(self):
29
+ """Complex case: output product contains wildcard and roundtrip is valid."""
30
+ out_rsmi = WildCard.rsmi_with_wildcards(self.rsmi_main)
31
+ _, product = out_rsmi.split(">>")
32
+ self.assertIsInstance(out_rsmi, str)
33
+ self.assertIn(
34
+ "*", product, "Wildcard '*' should be present in the product side."
35
+ )
36
+ # Roundtrip: should parse back without error
37
+ r, p = rsmi_to_graph(out_rsmi)
38
+ self.assertTrue(r.number_of_nodes() > 0)
39
+ self.assertTrue(p.number_of_nodes() > 0)
40
+
41
+ def test_no_atoms_lost(self):
42
+ """No atoms lost: should raise ValueError if input is not atom-mapped."""
43
+ with self.assertRaises(ValueError):
44
+ WildCard.rsmi_with_wildcards(self.rsmi_no_loss)
45
+
46
+ def test_all_atoms_lost(self):
47
+ """All atoms lost: should raise ValueError if input is not atom-mapped."""
48
+ with self.assertRaises(ValueError):
49
+ WildCard.rsmi_with_wildcards(self.rsmi_all_lost)
50
+
51
+ def test_empty_input(self):
52
+ """Empty input: should raise ValueError."""
53
+ with self.assertRaises(ValueError):
54
+ WildCard.rsmi_with_wildcards(self.rsmi_empty)
55
+
56
+ def test_wildcard_not_duplicated(self):
57
+ """Existing wildcards: should not create duplicate wildcards for same lost bond."""
58
+ out_rsmi = WildCard.rsmi_with_wildcards(self.rsmi_existing_wildcard)
59
+ _, product = out_rsmi.split(">>")
60
+ # At least one '*' in the product SMILES string
61
+ self.assertIn("*", product)
62
+
63
+ def test_no_false_positive_wildcards(self):
64
+ """Wildcards are only added if there are truly lost subgraphs; non-atom-mapped input raises."""
65
+ rsmi = "C>>C"
66
+ with self.assertRaises(ValueError):
67
+ WildCard.rsmi_with_wildcards(rsmi)
68
+
69
+ def test_output_is_str_and_split(self):
70
+ """Should raise ValueError if input is not atom-mapped."""
71
+ with self.assertRaises(ValueError):
72
+ WildCard.rsmi_with_wildcards(self.rsmi_no_loss)
73
+
74
+ def test_missing_atom_map_raises(self):
75
+ """Should raise ValueError if atom_map attributes are missing."""
76
+ with self.assertRaises(ValueError):
77
+ WildCard.rsmi_with_wildcards(self.rsmi_no_atom_map)
78
+
79
+
80
+ if __name__ == "__main__":
81
+ unittest.main()
@@ -0,0 +1,38 @@
1
+ import unittest
2
+ from synkit.IO.combinatorial.smarts_expander import SMARTSExpander
3
+
4
+
5
+ class TestSMARTSExpander(unittest.TestCase):
6
+
7
+ def test_no_placeholders(self):
8
+ s = "CCO"
9
+ self.assertEqual(list(SMARTSExpander.expand_iter(s)), ["CCO"])
10
+ self.assertEqual(SMARTSExpander.expand(s), ["CCO"])
11
+
12
+ def test_simple_expansion(self):
13
+ s = "[C,N:1][O,P:2]"
14
+ result = SMARTSExpander.expand(s)
15
+ self.assertEqual(
16
+ set(result), {"[C:1][O:2]", "[C:1][P:2]", "[N:1][O:2]", "[N:1][P:2]"}
17
+ )
18
+
19
+ def test_disjoint_constraint(self):
20
+ s = "[C,N:1][O:1]"
21
+ with self.assertRaises(ValueError):
22
+ list(SMARTSExpander.expand_iter(s))
23
+
24
+ def test_realistic_reaction(self):
25
+ rxn = (
26
+ "[H+:6].[C:7](-[O:8](-[H:12]))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])."
27
+ "[C:2](-[S:4](-[C,N,O,P,S:5]))(-[C,N,O,P,S:1])(=[O:3])>>"
28
+ "[S:4](-[H:6])(-[C,N,O,P,S:5]).[H+:12]."
29
+ "[C:7](-[O:8](-[C:2](-[C,N,O,P,S:1])(=[O:3])))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])"
30
+ )
31
+ ex_list = list(SMARTSExpander.expand_iter(rxn))
32
+ self.assertEqual(len(ex_list), 625)
33
+ # Optional: Just check format or count, not endswith
34
+ self.assertTrue(ex_list[0].startswith("[H+:6]"))
35
+
36
+
37
+ if __name__ == "__main__":
38
+ unittest.main()
@@ -0,0 +1,62 @@
1
+ import unittest
2
+ from rdkit import Chem
3
+ from synkit.IO.combinatorial.smarts_generalizer import SMARTSGeneralizer
4
+
5
+
6
+ class TestSMARTSGeneralizer(unittest.TestCase):
7
+
8
+ def setUp(self):
9
+ self.gen = SMARTSGeneralizer(sanity_check=True)
10
+
11
+ def test_basic_generalization(self):
12
+ inputs = [
13
+ "[C:1]-[N:2]>>[N:1]-[C:2]",
14
+ "[N:1]-[N:2]>>[N:1]-[N:2]",
15
+ "[O:1]-[N:2]>>[N:1]-[N:2]",
16
+ ]
17
+ output = self.gen.generalize(inputs)
18
+ # Instead of strict string match, check correct mapped elements
19
+ self.assertIn("[C,N,O:1]", output)
20
+ self.assertIn("[N:2]", output)
21
+ self.assertIn(">>", output)
22
+
23
+ def test_single_smarts(self):
24
+ inputs = ["[C:1]-[N:2]>>[N:1]-[C:2]"]
25
+ output = self.gen.generalize(inputs)
26
+ # Should match input exactly
27
+ self.assertEqual(output, "[C:1]-[N:2]>>[N:1]-[C:2]")
28
+
29
+ def test_different_topology_raises(self):
30
+ inputs = ["[C:1]-[N:2]>>[N:1]-[C:2]", "[N:1]-[N:2]-[C:3]>>[N:1]-[N:2]-[C:3]"]
31
+ with self.assertRaises(ValueError):
32
+ self.gen.generalize(inputs)
33
+
34
+ def test_empty_input_raises(self):
35
+ with self.assertRaises(ValueError):
36
+ self.gen.generalize([])
37
+
38
+ def test_molecule_smarts(self):
39
+ gen = SMARTSGeneralizer(sanity_check=True)
40
+ inputs = ["[C:1]-[N:2]", "[N:1]-[N:2]", "[O:1]-[N:2]"]
41
+ out = gen.generalize(inputs)
42
+ self.assertEqual(out, "[C,N,O:1]-[N:2]")
43
+
44
+ mol = Chem.MolFromSmarts(out)
45
+ self.assertIsNotNone(mol)
46
+
47
+ def test_invalid_sanity_check(self):
48
+ gen = SMARTSGeneralizer(sanity_check=True)
49
+ # Using an obviously broken SMARTS (bad bracket placement)
50
+ _ = [
51
+ "[C:1]-[N:2]>>[N:1]-[X:2]"
52
+ ] # 'X' is a valid SMARTS wildcard! Use a real error
53
+ with self.assertRaises(ValueError):
54
+ gen.generalize(["[C:1][C:2]>>[N:1][C:2]["]) # broken SMARTS
55
+
56
+ def test_repr(self):
57
+ gen = SMARTSGeneralizer()
58
+ self.assertIn("sanity_check", repr(gen))
59
+
60
+
61
+ if __name__ == "__main__":
62
+ unittest.main()
@@ -0,0 +1,85 @@
1
+ import unittest
2
+ import networkx as nx
3
+
4
+ from synkit.IO.combinatorial.smarts_to_graph import SMARTSToGraph
5
+
6
+
7
+ class TestSMARTSToGraph(unittest.TestCase):
8
+
9
+ def setUp(self):
10
+ self.stg = SMARTSToGraph()
11
+
12
+ def test_smarts_to_graph_simple(self):
13
+ g = self.stg.smarts_to_graph("[C:1]-[O:2]")
14
+ self.assertIsInstance(g, nx.Graph)
15
+ self.assertEqual(set(g.nodes), {1, 2})
16
+ self.assertEqual(g.nodes[1]["element"], "C")
17
+ self.assertEqual(g.nodes[2]["element"], "O")
18
+ self.assertIsNone(g.nodes[1]["constraint"])
19
+
20
+ def test_smarts_to_graph_constraint(self):
21
+ g = self.stg.smarts_to_graph("[C,N,O:1]-[N:2]")
22
+ # Node 1 should be placeholder
23
+ self.assertEqual(g.nodes[1]["element"], "*")
24
+ self.assertIsInstance(g.nodes[1]["constraint"], list)
25
+ self.assertIn("C", g.nodes[1]["constraint"])
26
+ self.assertEqual(g.nodes[2]["element"], "N")
27
+ self.assertIsNone(g.nodes[2]["constraint"])
28
+
29
+ def test_smarts_to_graph_hcount(self):
30
+ g = self.stg.smarts_to_graph("[CH3:1]-[O:2]")
31
+ # For SMARTS as written, RDKit returns 0 hydrogens for both
32
+ self.assertEqual(g.nodes[1]["hcount"], 0)
33
+ self.assertEqual(g.nodes[2]["hcount"], 0)
34
+
35
+ def test_invalid_smarts(self):
36
+ with self.assertRaises(ValueError):
37
+ self.stg.smarts_to_graph("[C:1]-[N")
38
+
39
+ def test_missing_atom_map(self):
40
+ with self.assertRaises(ValueError):
41
+ self.stg.smarts_to_graph("[C]-[O:2]")
42
+
43
+ def test_rxn_smarts_to_graphs(self):
44
+ rxn = (
45
+ "[H+:6].[C:7](-[O:8](-[H:12]))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])."
46
+ "[C:2](-[S:4](-[C,N,O,P,S:5]))(-[C,N,O,P,S:1])(=[O:3])>>"
47
+ "[S:4](-[H:6])(-[C,N,O,P,S:5]).[H+:12]."
48
+ "[C:7](-[O:8](-[C:2](-[C,N,O,P,S:1])(=[O:3])))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])"
49
+ )
50
+ g_react, _ = self.stg.rxn_smarts_to_graphs(rxn)
51
+
52
+ # These are the atom_map indices that should have constraint (from SMARTS [C,N,O,P,S:idx])
53
+ expected_constraint_nodes = {1, 5, 9, 10}
54
+ for idx in expected_constraint_nodes:
55
+ self.assertIn(idx, g_react.nodes)
56
+ self.assertIsNotNone(
57
+ g_react.nodes[idx]["constraint"],
58
+ f"Node {idx} should have a constraint list but does not",
59
+ )
60
+ self.assertEqual(
61
+ set(g_react.nodes[idx]["constraint"]),
62
+ {"C", "N", "O", "P", "S"},
63
+ f"Node {idx} has incorrect constraint list",
64
+ )
65
+ # All other nodes should NOT have a constraint
66
+ for idx in set(g_react.nodes) - expected_constraint_nodes:
67
+ self.assertIsNone(
68
+ g_react.nodes[idx]["constraint"],
69
+ f"Node {idx} should NOT have a constraint list",
70
+ )
71
+
72
+ def test_rxn_separator(self):
73
+ with self.assertRaises(ValueError):
74
+ self.stg.rxn_smarts_to_graphs("[C:1]-[O:2]") # no '>>'
75
+
76
+ def test_repr_and_describe(self):
77
+ r = repr(self.stg)
78
+ self.assertIn("placeholders", r)
79
+ desc = self.stg.describe()
80
+ self.assertIn("smarts_to_graph", desc)
81
+ self.assertIn("rxn_smarts_to_graphs", desc)
82
+
83
+
84
+ if __name__ == "__main__":
85
+ unittest.main()
@@ -0,0 +1,94 @@
1
+ import unittest
2
+ from synkit.IO import rsmi_to_its
3
+ from synkit.Graph.Wildcard.wildcard import WildCard
4
+ from synkit.Chem.Reaction.standardize import Standardize
5
+ from synkit.Synthesis.Reactor.imba_engine import ImbaEngine
6
+
7
+
8
+ class TestImbaEngine(unittest.TestCase):
9
+ def setUp(self):
10
+ # A complex standardized RSMI from your example
11
+ self.smart = (
12
+ "[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4][c:31]2[NH2:28]."
13
+ "[cH:2]1[c:20]([C:22]([OH:7])=[O:21])[s:18][c:24]([S:6][c:29]2[c:15]"
14
+ "([Cl:26])[cH:8][n:19][cH:9][c:16]2[Cl:27])[c:30]1[N+:5]([O-:3])=[O:13]>>"
15
+ "[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4]"
16
+ "[c:31]2[NH:28][C:22]([c:20]1[cH:2][c:30]([N+:5]([O-:3])=[O:13])[c:24]([S:6]"
17
+ "[c:29]2[c:15]([Cl:26])[cH:8][n:19][cH:9][c:16]2[Cl:27])[s:18]1)=[O:21]"
18
+ )
19
+ # Standardize removes AAM
20
+ self.rsmi = Standardize().fit(self.smart, remove_aam=True)
21
+
22
+ def test_pipeline_forward(self):
23
+ """Test forward ImbaEngine pipeline end-to-end."""
24
+ # Apply wildcard insertion
25
+ wild_smart = WildCard().rsmi_with_wildcards(self.smart)
26
+ # Build ITS graphs
27
+ temp = rsmi_to_its(wild_smart, core=True)
28
+ # substrate split from standardized RSMI
29
+ substrate_r, _ = self.rsmi.split(">>")
30
+ # Run engine forward with template without cleaning fragments
31
+ engine = ImbaEngine(substrate_r, temp, add_wildcard=True, clean_fragments=False)
32
+ out = engine.smarts_list
33
+ self.assertEqual(len(out), 1)
34
+ out_rsmi = Standardize().fit(out[0], remove_aam=True)
35
+
36
+ self.assertIn("*", out_rsmi)
37
+ self.assertNotEqual(out_rsmi, self.rsmi)
38
+
39
+ # Run engine forward with template with cleaning fragments
40
+ engine = ImbaEngine(substrate_r, temp, add_wildcard=True, clean_fragments=True)
41
+ out = engine.smarts_list
42
+ outs = [Standardize().fit(o, remove_aam=True) for o in out]
43
+ self.assertIn(self.rsmi, outs)
44
+
45
+ def test_pipeline_backward(self):
46
+ """Test backward ImbaEngine pipeline end-to-end with and without fragment cleaning."""
47
+ # Prepare wildcard and ITS template
48
+ wild_rsmi = WildCard().rsmi_with_wildcards(self.smart)
49
+ its = rsmi_to_its(wild_rsmi, core=True)
50
+
51
+ _, substrate_p = self.rsmi.split(">>")
52
+
53
+ # 1. Without fragment cleaning
54
+ engine = ImbaEngine(
55
+ substrate_p,
56
+ its,
57
+ add_wildcard=True,
58
+ clean_fragments=False,
59
+ invert=True,
60
+ partial=True,
61
+ )
62
+ out = engine.smarts_list
63
+ self.assertEqual(len(out), 2)
64
+ out_rsmi = Standardize().fit(out[0], remove_aam=True)
65
+ self.assertIn("*", out_rsmi)
66
+ self.assertNotEqual(out_rsmi, self.rsmi)
67
+
68
+ # 2. With fragment cleaning
69
+ engine_clean = ImbaEngine(
70
+ substrate_p,
71
+ its,
72
+ add_wildcard=True,
73
+ clean_fragments=True,
74
+ invert=True,
75
+ partial=True,
76
+ )
77
+
78
+ out_clean = engine_clean.smarts_list
79
+ self.assertEqual(len(out_clean), 2)
80
+ outs = [Standardize().fit(o, remove_aam=True) for o in out_clean]
81
+ self.assertIn(self.rsmi, outs)
82
+
83
+ def test_invalid_rsmi(self):
84
+ """Invalid RSMI pipeline should raise an exception at Standardize or ITS step."""
85
+ # Standardize should fail for invalid RSMI
86
+ with self.assertRaises(Exception):
87
+ # Attempt full pipeline
88
+ rsmi = Standardize().fit("not_a_rsmi", remove_aam=True)
89
+ wild = WildCard().rsmi_with_wildcards(rsmi)
90
+ _ = rsmi_to_its(wild, core=True)
91
+
92
+
93
+ if __name__ == "__main__":
94
+ unittest.main()