synkit 0.0.12__tar.gz → 0.0.14__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 (292) hide show
  1. {synkit-0.0.12 → synkit-0.0.14}/.github/workflows/test-and-lint.yml +1 -1
  2. {synkit-0.0.12 → synkit-0.0.14}/.gitignore +2 -0
  3. {synkit-0.0.12 → synkit-0.0.14}/PKG-INFO +12 -1
  4. {synkit-0.0.12 → synkit-0.0.14}/README.md +11 -0
  5. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/MTG/test_mtg.py +7 -9
  6. synkit-0.0.14/Test/Graph/Wildcard/test_radwc.py +55 -0
  7. synkit-0.0.14/Test/Graph/Wildcard/test_wildcard.py +81 -0
  8. synkit-0.0.14/Test/IO/combinatorial/test_smarts_expander.py +38 -0
  9. synkit-0.0.14/Test/IO/combinatorial/test_smarts_generalizer.py +62 -0
  10. synkit-0.0.14/Test/IO/combinatorial/test_smarts_to_graph.py +85 -0
  11. synkit-0.0.14/Test/Synthesis/Reactor/test_imba_engine.py +94 -0
  12. synkit-0.0.14/doc/figures/mtg_mechanism.png +0 -0
  13. {synkit-0.0.12 → synkit-0.0.14}/doc/graph.rst +63 -31
  14. {synkit-0.0.12 → synkit-0.0.14}/lint.sh +3 -1
  15. {synkit-0.0.12 → synkit-0.0.14}/pyproject.toml +1 -1
  16. {synkit-0.0.12 → synkit-0.0.14}/recipe/meta.yaml +1 -1
  17. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/radical_wildcard.py +57 -0
  18. synkit-0.0.14/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +231 -0
  19. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Hyrogen/hcomplete.py +1 -0
  20. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/its_decompose.py +281 -222
  21. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/MTG/mcs_matcher.py +67 -25
  22. synkit-0.0.14/synkit/Graph/MTG/mtg.py +886 -0
  23. synkit-0.0.14/synkit/Graph/MTG/mtg_explore.py +74 -0
  24. synkit-0.0.14/synkit/Graph/MTG/utils.py +425 -0
  25. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/subgraph_matcher.py +327 -0
  26. synkit-0.0.14/synkit/Graph/Wildcard/radwc.py +117 -0
  27. synkit-0.0.14/synkit/Graph/Wildcard/wildcard.py +230 -0
  28. synkit-0.0.14/synkit/IO/combinatorial/__init__.py +8 -0
  29. synkit-0.0.14/synkit/IO/combinatorial/gml_to_graph.py +254 -0
  30. synkit-0.0.14/synkit/IO/combinatorial/graph_to_gml.py +291 -0
  31. synkit-0.0.14/synkit/IO/combinatorial/graph_to_smarts.py +189 -0
  32. synkit-0.0.14/synkit/IO/combinatorial/smarts_expander.py +152 -0
  33. synkit-0.0.14/synkit/IO/combinatorial/smarts_generalizer.py +134 -0
  34. synkit-0.0.14/synkit/IO/combinatorial/smarts_to_graph.py +183 -0
  35. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Apply/rule_matcher.py +15 -5
  36. synkit-0.0.14/synkit/Synthesis/Reactor/imba_engine.py +165 -0
  37. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/syn_reactor.py +2 -2
  38. synkit-0.0.14/synkit/Synthesis/__init__.py +0 -0
  39. synkit-0.0.14/synkit/Utils/__init__.py +0 -0
  40. {synkit-0.0.12 → synkit-0.0.14}/synkit/Vis/graph_visualizer.py +9 -1
  41. {synkit-0.0.12 → synkit-0.0.14}/synkit/Vis/rule_vis.py +2 -2
  42. synkit-0.0.14/synkit/__init__.py +0 -0
  43. synkit-0.0.14/synkit/examples.py +50 -0
  44. synkit-0.0.12/Test/Synthesis/Reactor/test_core_engine.py +0 -112
  45. synkit-0.0.12/synkit/Graph/MTG/mtg.py +0 -208
  46. {synkit-0.0.12 → synkit-0.0.14}/.github/dependabot.yml +0 -0
  47. {synkit-0.0.12 → synkit-0.0.14}/.github/workflows/build-doc.yml +0 -0
  48. {synkit-0.0.12 → synkit-0.0.14}/.github/workflows/conda-forge-publish.yml +0 -0
  49. {synkit-0.0.12 → synkit-0.0.14}/.github/workflows/docker-publish.yml +0 -0
  50. {synkit-0.0.12 → synkit-0.0.14}/.github/workflows/publish-package.yml +0 -0
  51. {synkit-0.0.12 → synkit-0.0.14}/.github/workflows/verify-pypi-install.yml +0 -0
  52. {synkit-0.0.12 → synkit-0.0.14}/.readthedocs.yml +0 -0
  53. {synkit-0.0.12 → synkit-0.0.14}/Data/Figure/synkit.png +0 -0
  54. {synkit-0.0.12 → synkit-0.0.14}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
  55. {synkit-0.0.12 → synkit-0.0.14}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
  56. {synkit-0.0.12 → synkit-0.0.14}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
  57. {synkit-0.0.12 → synkit-0.0.14}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
  58. {synkit-0.0.12 → synkit-0.0.14}/Dockerfile +0 -0
  59. {synkit-0.0.12 → synkit-0.0.14}/LICENSE +0 -0
  60. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Fingerprint/__init__.py +0 -0
  61. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
  62. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
  63. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
  64. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Molecule/__init__.py +0 -0
  65. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Molecule/test_standardize.py +0 -0
  66. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/__init__.py +0 -0
  67. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_aam_validator.py +0 -0
  68. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_balance_checker.py +0 -0
  69. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_canon_rsmi.py +0 -0
  70. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_cleanning.py +0 -0
  71. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_deionize.py +0 -0
  72. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_fix_aam.py +0 -0
  73. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_neutralize.py +0 -0
  74. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_radical_wildcard.py +0 -0
  75. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_standardize.py +0 -0
  76. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/Reaction/test_tautomerize.py +0 -0
  77. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/__init__.py +0 -0
  78. {synkit-0.0.12 → synkit-0.0.14}/Test/Chem/test_utils.py +0 -0
  79. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Context/__init__.py +0 -0
  80. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Context/test_hier_context.py +0 -0
  81. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Context/test_radius_expand.py +0 -0
  82. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Feature/__init__.py +0 -0
  83. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
  84. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Feature/test_graph_fps.py +0 -0
  85. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Feature/test_graph_signature.py +0 -0
  86. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Feature/test_hash_fps.py +0 -0
  87. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Feature/test_morgan_fps.py +0 -0
  88. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Feature/test_path_fps.py +0 -0
  89. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Hydrogen/__init__.py +0 -0
  90. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Hydrogen/test_graph_hydrogen.py +0 -0
  91. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Hydrogen/test_hcomplete.py +0 -0
  92. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Hydrogen/test_misc.py +0 -0
  93. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/ITS/__init__.py +0 -0
  94. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/ITS/test_its_construction.py +0 -0
  95. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/ITS/test_its_expand.py +0 -0
  96. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/ITS/test_its_relabel.py +0 -0
  97. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/ITS/test_normalize_aam.py +0 -0
  98. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/MTG/__init__.py +0 -0
  99. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/MTG/test_group_comp.py +0 -0
  100. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/MTG/test_groupoid.py +0 -0
  101. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Matcher/__init__.py +0 -0
  102. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Matcher/test_batch_cluster.py +0 -0
  103. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Matcher/test_graph_cluster.py +0 -0
  104. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Matcher/test_graph_matcher.py +0 -0
  105. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Matcher/test_graph_morphism.py +0 -0
  106. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/Matcher/test_subgraph_matcher.py +0 -0
  107. {synkit-0.0.12/Test/Graph → synkit-0.0.14/Test/Graph/Wildcard}/__init__.py +0 -0
  108. {synkit-0.0.12/Test/IO → synkit-0.0.14/Test/Graph}/__init__.py +0 -0
  109. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/test_canon_graph.py +0 -0
  110. {synkit-0.0.12 → synkit-0.0.14}/Test/Graph/test_syn_graph.py +0 -0
  111. {synkit-0.0.12/Test/Rule/Apply → synkit-0.0.14/Test/IO}/__init__.py +0 -0
  112. {synkit-0.0.12/Test/Rule/Compose → synkit-0.0.14/Test/IO/combinatorial}/__init__.py +0 -0
  113. {synkit-0.0.12 → synkit-0.0.14}/Test/IO/test_chemical_converter.py +0 -0
  114. {synkit-0.0.12 → synkit-0.0.14}/Test/IO/test_dg_to_gml.py +0 -0
  115. {synkit-0.0.12 → synkit-0.0.14}/Test/IO/test_gml_to_nx.py +0 -0
  116. {synkit-0.0.12 → synkit-0.0.14}/Test/IO/test_graph_to_mol.py +0 -0
  117. {synkit-0.0.12 → synkit-0.0.14}/Test/IO/test_mol_to_graph.py +0 -0
  118. {synkit-0.0.12 → synkit-0.0.14}/Test/IO/test_nx_to_gml.py +0 -0
  119. {synkit-0.0.12/Test/Rule/Modify → synkit-0.0.14/Test/Rule/Apply}/__init__.py +0 -0
  120. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Apply/test_reactor_rule.py +0 -0
  121. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Apply/test_retro_reactor.py +0 -0
  122. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Apply/test_rule_matcher.py +0 -0
  123. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Apply/test_rule_rbl.py +0 -0
  124. {synkit-0.0.12/Test/Rule → synkit-0.0.14/Test/Rule/Compose}/__init__.py +0 -0
  125. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Compose/test_rule_compose.py +0 -0
  126. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Compose/test_valance_constrain.py +0 -0
  127. {synkit-0.0.12/Test/Synthesis/CRN → synkit-0.0.14/Test/Rule/Modify}/__init__.py +0 -0
  128. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Modify/test_molecule_rule.py +0 -0
  129. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/Modify/test_rule_utils.py +0 -0
  130. {synkit-0.0.12/Test/Synthesis/MSR → synkit-0.0.14/Test/Rule}/__init__.py +0 -0
  131. {synkit-0.0.12 → synkit-0.0.14}/Test/Rule/test_syn_rule.py +0 -0
  132. {synkit-0.0.12/Test/Synthesis/Reactor → synkit-0.0.14/Test/Synthesis/CRN}/__init__.py +0 -0
  133. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/CRN/test_crn.py +0 -0
  134. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/CRN/test_mod_crn.py +0 -0
  135. {synkit-0.0.12/Test/Synthesis → synkit-0.0.14/Test/Synthesis/MSR}/__init__.py +0 -0
  136. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/MSR/test_multi_steps.py +0 -0
  137. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/MSR/test_path_finder.py +0 -0
  138. {synkit-0.0.12/Test/Vis → synkit-0.0.14/Test/Synthesis/Reactor}/__init__.py +0 -0
  139. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/Reactor/test_mod_aam.py +0 -0
  140. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/Reactor/test_mod_reactor.py +0 -0
  141. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/Reactor/test_partial_engine.py +0 -0
  142. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/Reactor/test_rbl_reactor.py +0 -0
  143. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/Reactor/test_strategy.py +0 -0
  144. {synkit-0.0.12/Test → synkit-0.0.14/Test/Synthesis}/__init__.py +0 -0
  145. {synkit-0.0.12 → synkit-0.0.14}/Test/Synthesis/test_reactor_utils.py +0 -0
  146. {synkit-0.0.12/synkit/Chem/Cluster → synkit-0.0.14/Test/Vis}/__init__.py +0 -0
  147. {synkit-0.0.12 → synkit-0.0.14}/Test/Vis/test_embedding.py +0 -0
  148. {synkit-0.0.12/synkit/Chem/Fingerprint → synkit-0.0.14/Test}/__init__.py +0 -0
  149. {synkit-0.0.12 → synkit-0.0.14}/build-doc.sh +0 -0
  150. {synkit-0.0.12 → synkit-0.0.14}/doc/api.rst +0 -0
  151. {synkit-0.0.12 → synkit-0.0.14}/doc/changelog.rst +0 -0
  152. {synkit-0.0.12 → synkit-0.0.14}/doc/chem.rst +0 -0
  153. {synkit-0.0.12 → synkit-0.0.14}/doc/conf.py +0 -0
  154. {synkit-0.0.12 → synkit-0.0.14}/doc/figures/aldol.png +0 -0
  155. {synkit-0.0.12 → synkit-0.0.14}/doc/figures/aldol_its.png +0 -0
  156. {synkit-0.0.12 → synkit-0.0.14}/doc/figures/context.png +0 -0
  157. {synkit-0.0.12 → synkit-0.0.14}/doc/figures/mtg.png +0 -0
  158. {synkit-0.0.12 → synkit-0.0.14}/doc/getting_started.rst +0 -0
  159. {synkit-0.0.12 → synkit-0.0.14}/doc/index.rst +0 -0
  160. {synkit-0.0.12 → synkit-0.0.14}/doc/io.rst +0 -0
  161. {synkit-0.0.12 → synkit-0.0.14}/doc/reference.rst +0 -0
  162. {synkit-0.0.12 → synkit-0.0.14}/doc/refs.bib +0 -0
  163. {synkit-0.0.12 → synkit-0.0.14}/doc/requirements.txt +0 -0
  164. {synkit-0.0.12 → synkit-0.0.14}/doc/rule.rst +0 -0
  165. {synkit-0.0.12 → synkit-0.0.14}/doc/synthesis.rst +0 -0
  166. {synkit-0.0.12 → synkit-0.0.14}/environment.yml +0 -0
  167. {synkit-0.0.12 → synkit-0.0.14}/pytest.sh +0 -0
  168. {synkit-0.0.12 → synkit-0.0.14}/requirements.txt +0 -0
  169. {synkit-0.0.12/synkit/Chem/Molecule → synkit-0.0.14/synkit/Chem/Cluster}/__init__.py +0 -0
  170. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Cluster/butina.py +0 -0
  171. {synkit-0.0.12/synkit/Data → synkit-0.0.14/synkit/Chem/Fingerprint}/__init__.py +0 -0
  172. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
  173. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
  174. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  175. {synkit-0.0.12/synkit/Graph/Context → synkit-0.0.14/synkit/Chem/Molecule}/__init__.py +0 -0
  176. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Molecule/standardize.py +0 -0
  177. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/__init__.py +0 -0
  178. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/aam_validator.py +0 -0
  179. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/balance_check.py +0 -0
  180. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
  181. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/cleaning.py +0 -0
  182. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/deionize.py +0 -0
  183. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/fix_aam.py +0 -0
  184. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/neutralize.py +0 -0
  185. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/standardize.py +0 -0
  186. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/Reaction/tautomerize.py +0 -0
  187. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/__init__.py +0 -0
  188. {synkit-0.0.12 → synkit-0.0.14}/synkit/Chem/utils.py +0 -0
  189. {synkit-0.0.12/synkit/Graph/Hyrogen → synkit-0.0.14/synkit/Data}/__init__.py +0 -0
  190. {synkit-0.0.12 → synkit-0.0.14}/synkit/Data/gen_partial_aam.py +0 -0
  191. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Canon/__init__.py +0 -0
  192. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Canon/canon_algs.py +0 -0
  193. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Canon/canon_graph.py +0 -0
  194. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Canon/nauty.py +0 -0
  195. {synkit-0.0.12/synkit/Graph/MTG → synkit-0.0.14/synkit/Graph/Context}/__init__.py +0 -0
  196. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Context/hier_context.py +0 -0
  197. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Context/radius_expand.py +0 -0
  198. {synkit-0.0.12/synkit/Graph/Wildcard → synkit-0.0.14/synkit/Graph/Feature/Fingerprint}/__init__.py +0 -0
  199. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/__init__.py +0 -0
  200. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  201. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/graph_fps.py +0 -0
  202. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/graph_signature.py +0 -0
  203. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/hash_fps.py +0 -0
  204. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/morgan_fps.py +0 -0
  205. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/path_fps.py +0 -0
  206. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Feature/wl_hash.py +0 -0
  207. {synkit-0.0.12/synkit/Rule/Apply → synkit-0.0.14/synkit/Graph/Hyrogen}/__init__.py +0 -0
  208. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Hyrogen/_misc.py +0 -0
  209. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Hyrogen/hextend.py +0 -0
  210. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/__init__.py +0 -0
  211. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/its_builder.py +0 -0
  212. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/its_construction.py +0 -0
  213. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/its_expand.py +0 -0
  214. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/its_relabel.py +0 -0
  215. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/normalize_aam.py +0 -0
  216. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/ITS/partial_its.py +0 -0
  217. {synkit-0.0.12/synkit/Rule/Compose → synkit-0.0.14/synkit/Graph/MTG}/__init__.py +0 -0
  218. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/MTG/group_comp.py +0 -0
  219. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/MTG/groupoid.py +0 -0
  220. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/__init__.py +0 -0
  221. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/batch_cluster.py +0 -0
  222. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/graph_cluster.py +0 -0
  223. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/graph_matcher.py +0 -0
  224. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/graph_morphism.py +0 -0
  225. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
  226. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
  227. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/partial_matcher.py +0 -0
  228. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/sing.py +0 -0
  229. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Matcher/turbo_iso.py +0 -0
  230. {synkit-0.0.12/synkit/Rule/Modify → synkit-0.0.14/synkit/Graph/Wildcard}/__init__.py +0 -0
  231. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
  232. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/__init__.py +0 -0
  233. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/canon_graph.py +0 -0
  234. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/syn_graph.py +0 -0
  235. {synkit-0.0.12 → synkit-0.0.14}/synkit/Graph/utils.py +0 -0
  236. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/__init__.py +0 -0
  237. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/chem_converter.py +0 -0
  238. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/data_io.py +0 -0
  239. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/data_process.py +0 -0
  240. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/debug.py +0 -0
  241. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/dg_to_gml.py +0 -0
  242. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/gml_to_nx.py +0 -0
  243. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/graph_to_mol.py +0 -0
  244. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/mol_to_graph.py +0 -0
  245. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/nx_to_gml.py +0 -0
  246. {synkit-0.0.12 → synkit-0.0.14}/synkit/IO/smiles_to_id.py +0 -0
  247. {synkit-0.0.12/synkit/Synthesis/CRN → synkit-0.0.14/synkit/Rule/Apply}/__init__.py +0 -0
  248. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Apply/reactor_rule.py +0 -0
  249. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Apply/retro_reactor.py +0 -0
  250. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Apply/rule_rbl.py +0 -0
  251. {synkit-0.0.12/synkit/Synthesis/MSR → synkit-0.0.14/synkit/Rule/Compose}/__init__.py +0 -0
  252. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Compose/compose_rule.py +0 -0
  253. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Compose/rule_compose.py +0 -0
  254. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Compose/rule_mapping.py +0 -0
  255. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Compose/seq_comp.py +0 -0
  256. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Compose/valence_constrain.py +0 -0
  257. {synkit-0.0.12/synkit/Synthesis/Metrics → synkit-0.0.14/synkit/Rule/Modify}/__init__.py +0 -0
  258. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Modify/implict_rule.py +0 -0
  259. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Modify/longest_path.py +0 -0
  260. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Modify/molecule_rule.py +0 -0
  261. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Modify/prune_templates.py +0 -0
  262. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Modify/rule_utils.py +0 -0
  263. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/Modify/strip_rule.py +0 -0
  264. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/__init__.py +0 -0
  265. {synkit-0.0.12 → synkit-0.0.14}/synkit/Rule/syn_rule.py +0 -0
  266. {synkit-0.0.12/synkit/Synthesis/Reactor → synkit-0.0.14/synkit/Synthesis/CRN}/__init__.py +0 -0
  267. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/CRN/crn.py +0 -0
  268. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/CRN/dcrn.py +0 -0
  269. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/CRN/mod_crn.py +0 -0
  270. {synkit-0.0.12/synkit/Synthesis → synkit-0.0.14/synkit/Synthesis/MSR}/__init__.py +0 -0
  271. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/MSR/multi_steps.py +0 -0
  272. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/MSR/path_finder.py +0 -0
  273. {synkit-0.0.12/synkit/Utils → synkit-0.0.14/synkit/Synthesis/Metrics}/__init__.py +0 -0
  274. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Metrics/_base.py +0 -0
  275. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Metrics/_plot.py +0 -0
  276. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Metrics/_ranking.py +0 -0
  277. {synkit-0.0.12/synkit → synkit-0.0.14/synkit/Synthesis/Reactor}/__init__.py +0 -0
  278. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/batch_reactor.py +0 -0
  279. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
  280. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
  281. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
  282. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
  283. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
  284. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
  285. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/Reactor/strategy.py +0 -0
  286. {synkit-0.0.12 → synkit-0.0.14}/synkit/Synthesis/reactor_utils.py +0 -0
  287. {synkit-0.0.12 → synkit-0.0.14}/synkit/Utils/utils.py +0 -0
  288. {synkit-0.0.12 → synkit-0.0.14}/synkit/Vis/__init__.py +0 -0
  289. {synkit-0.0.12 → synkit-0.0.14}/synkit/Vis/chemical_space.py +0 -0
  290. {synkit-0.0.12 → synkit-0.0.14}/synkit/Vis/embedding.py +0 -0
  291. {synkit-0.0.12 → synkit-0.0.14}/synkit/Vis/pdf_writer.py +0 -0
  292. {synkit-0.0.12 → synkit-0.0.14}/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
 
@@ -22,3 +22,5 @@ run.sh
22
22
  docs/*
23
23
  run_rdcanon.py
24
24
  Data/Fragment/*
25
+ test_partial.py
26
+ Data/Benchmark/synthesis/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synkit
3
- Version: 0.0.12
3
+ Version: 0.0.14
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
 
@@ -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()
@@ -200,42 +200,77 @@ This example builds two reaction-center ITS graphs, computes their MCS mapping,
200
200
  :caption: Building and visualizing an MTG with composite ITS
201
201
  :linenos:
202
202
 
203
- from synkit.IO.chem_converter import rsmi_to_its
204
- from synkit.Graph.MTG.mcs_matcher import MCSMatcher
205
203
  from synkit.Graph.MTG.mtg import MTG
206
- from synkit.Graph import clean_graph_keep_largest_component
207
- from synkit.Vis import GraphVisualizer
204
+ from synkit.Graph.ITS.its_decompose import get_rc
205
+ from synkit.examples import list_examples, load_example
208
206
  import matplotlib.pyplot as plt
207
+ from synkit.Vis.graph_visualizer import GraphVisualizer
209
208
 
210
- # 1) Define two related reaction SMILES and build their reaction-center ITS graphs
211
- rsmi_list = [
212
- '[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH:4]([H:8])=[CH:5][O:6]([H:7])', # tautomerization
213
- '[CH3:1][C:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:7])>>'
214
- '[CH3:1][C:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]' # nucleophilic addition
215
- ]
216
- rc_graphs = [rsmi_to_its(r, core=True) for r in rsmi_list]
217
209
 
218
- # 2) Find MCS mapping between the two ITS graphs
219
- mcs = MCSMatcher(node_label_names=['element', 'charge'], edge_attribute='order')
220
- mcs.find_rc_mapping(rc_graphs[0], rc_graphs[1], mcs=True)
221
- mapping = mcs.get_mappings()[0]
210
+ data = load_example("aldol")
222
211
 
223
- # 3) Build the Mechanistic Transition Graph (MTG)
224
- mtg = MTG(rc_graphs[0], rc_graphs[1], mapping)
225
- mtg_graph = mtg.get_graph()
212
+ mech_neutral = data[0]['mechanisms'][1]['steps']
213
+ smart_neutral = [i['smart_string'] for i in mech_neutral]
226
214
 
227
- # 4) Also build the composite ITS by directly gluing the two RC graphs
228
- its_composite = clean_graph_keep_largest_component(mtg_graph)
215
+ mech_acid = data[0]['mechanisms'][2]['steps']
216
+ smart_acid = [i['smart_string'] for i in mech_acid]
229
217
 
218
+ # neutral
219
+ mtg = MTG(smart_neutral, mcs_mol=True)
220
+ mtg_its_neutral = mtg.get_compose_its()
221
+ mtg_rc_neutral = get_rc(mtg_its_neutral, keep_mtg=True)
222
+ rc_neutral = get_rc(mtg_its_neutral, keep_mtg=False)
230
223
 
231
- # 5) Visualize all four graphs: two RCs, the composite ITS, and the MTG
232
- fig, axes = plt.subplots(2, 2, figsize=(14, 6))
224
+ # acid
225
+ mtg = MTG(smart_acid, mcs_mol=True)
226
+ mtg_its_acid = mtg.get_compose_its()
227
+ mtg_rc_acid = get_rc(mtg_its_acid, keep_mtg=True)
228
+ rc_acid = get_rc(mtg_its_acid, keep_mtg=False)
229
+
230
+ # Visualize
231
+ fig, ax = plt.subplots(2, 2, figsize=(16, 8))
233
232
  vis = GraphVisualizer()
234
233
 
235
- vis.plot_its(rc_graphs[0], axes[0, 0], use_edge_color=True, title='A. Tautomerization RC')
236
- vis.plot_its(rc_graphs[1], axes[0, 1], use_edge_color=True, title='B. Nucleophilic Addition RC')
237
- vis.plot_its(its_composite, axes[1, 0], use_edge_color=True, title='C. Composite ITS')
238
- vis.plot_its(mtg_graph, axes[1, 1], use_edge_color=True, title='D. Mechanistic TG', og=True)
234
+ vis.plot_its(
235
+ mtg_rc_neutral,
236
+ ax=ax[0, 0],
237
+ use_edge_color=True,
238
+ og=True,
239
+ title='A. MTG for aldol addition (neutral)',
240
+ title_font_size=20,
241
+ title_font_weight='medium',
242
+ title_font_style='normal'
243
+ )
244
+ vis.plot_its(
245
+ rc_neutral,
246
+ ax=ax[0, 1],
247
+ use_edge_color=True,
248
+ og=True,
249
+ title='B. Reaction center (neutral)',
250
+ title_font_size=20,
251
+ title_font_weight='medium',
252
+ title_font_style='normal'
253
+ )
254
+ vis.plot_its(
255
+ mtg_rc_acid,
256
+ ax=ax[1, 0],
257
+ use_edge_color=True,
258
+ og=True,
259
+ title='C. MTG for aldol addition (acid)',
260
+ title_font_size=20,
261
+ title_font_weight='medium',
262
+ title_font_style='normal'
263
+ )
264
+ vis.plot_its(
265
+ rc_acid,
266
+ ax=ax[1, 1],
267
+ use_edge_color=True,
268
+ og=True,
269
+ title='D. Reaction center (acid)',
270
+ title_font_size=20,
271
+ title_font_weight='medium',
272
+ title_font_style='normal'
273
+ )
239
274
 
240
275
  plt.tight_layout()
241
276
  plt.show()
@@ -243,16 +278,13 @@ This example builds two reaction-center ITS graphs, computes their MCS mapping,
243
278
 
244
279
  .. container:: figure
245
280
 
246
- .. image:: ./figures/mtg.png
281
+ .. image:: ./figures/mtg_mechanism.png
247
282
  :alt: Composite ITS and MTG visualization
248
283
  :align: center
249
284
  :width: 1000px
250
285
 
251
286
  *Figure:*
252
- (A) Reaction‐center graph for the tautomerization step
253
- (B) Reaction‐center graph for the nucleophilic addition step
254
- (C) Composite ITS graph "gluing" both transformations
255
- (D) Mechanistic Transition Graph (MTG) showing step-wise mechanism
287
+ Composition of the mechanistic sequences for aldol addition under neutral and acidic conditions, showing the composite MTG (left column) and the reaction center (right column).
256
288
 
257
289
  Context graph
258
290
  -------------
@@ -18,7 +18,9 @@ benchmark_reactor.py:W292,C901,\
18
18
  syn_reactor.py:C901,\
19
19
  sing.py:C901,\
20
20
  turbo_iso.py:C901,\
21
- rule_vis.py:C901" \
21
+ rule_vis.py:C901,
22
+ gml_to_graph.py:C901,
23
+ wildcard.py:C901" \
22
24
  --exclude=venv,\
23
25
  core_engine.py,\
24
26
  rule_apply.py,\
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "synkit"
7
- version = "0.0.12"
7
+ version = "0.0.14"
8
8
  license = { text = "MIT" }
9
9
  license-files = ["LICENSE"]
10
10
  authors = [
@@ -1,6 +1,6 @@
1
1
  package:
2
2
  name: synkit
3
- version: 0.0.12
3
+ version: 0.0.14
4
4
 
5
5
  source:
6
6
  path: ..