synkit 0.0.9__tar.gz → 0.0.10__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 (266) hide show
  1. {synkit-0.0.9 → synkit-0.0.10}/PKG-INFO +1 -1
  2. synkit-0.0.10/Test/Chem/Reaction/test_radical_wildcard.py +51 -0
  3. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/ITS/test_its_expand.py +9 -1
  4. synkit-0.0.10/Test/Graph/ITS/test_its_relabel.py +61 -0
  5. synkit-0.0.10/Test/Rule/Apply/test_rule_matcher.py +94 -0
  6. synkit-0.0.10/Test/Synthesis/Reactor/test_partial_engine.py +48 -0
  7. synkit-0.0.10/Test/Synthesis/Reactor/test_rbl_reactor.py +52 -0
  8. {synkit-0.0.9 → synkit-0.0.10}/doc/api.rst +6 -0
  9. {synkit-0.0.9 → synkit-0.0.10}/doc/conf.py +17 -2
  10. synkit-0.0.10/doc/figures/context.png +0 -0
  11. {synkit-0.0.9 → synkit-0.0.10}/doc/getting_started.rst +1 -1
  12. {synkit-0.0.9 → synkit-0.0.10}/doc/graph.rst +44 -0
  13. {synkit-0.0.9 → synkit-0.0.10}/pyproject.toml +1 -1
  14. synkit-0.0.10/synkit/Chem/Reaction/radical_wildcard.py +165 -0
  15. synkit-0.0.10/synkit/Data/gen_partial_aam.py +148 -0
  16. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Canon/nauty.py +36 -8
  17. synkit-0.0.10/synkit/Graph/ITS/its_builder.py +114 -0
  18. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/ITS/its_construction.py +80 -71
  19. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/ITS/its_decompose.py +53 -20
  20. synkit-0.0.10/synkit/Graph/ITS/its_expand.py +84 -0
  21. synkit-0.0.10/synkit/Graph/ITS/its_relabel.py +191 -0
  22. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/graph_morphism.py +100 -2
  23. synkit-0.0.10/synkit/Graph/Wildcard/fuse_graph.py +158 -0
  24. synkit-0.0.10/synkit/Rule/Apply/rule_matcher.py +191 -0
  25. synkit-0.0.10/synkit/Synthesis/Reactor/partial_engine.py +71 -0
  26. synkit-0.0.10/synkit/Synthesis/Reactor/rbl_engine.py +123 -0
  27. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/syn_reactor.py +63 -45
  28. synkit-0.0.10/synkit/Utils/__init__.py +0 -0
  29. synkit-0.0.10/synkit/__init__.py +0 -0
  30. synkit-0.0.9/synkit/Graph/ITS/its_builder.py +0 -94
  31. synkit-0.0.9/synkit/Graph/ITS/its_expand.py +0 -88
  32. {synkit-0.0.9 → synkit-0.0.10}/.github/workflows/build-doc.yml +0 -0
  33. {synkit-0.0.9 → synkit-0.0.10}/.github/workflows/publish-package.yml +0 -0
  34. {synkit-0.0.9 → synkit-0.0.10}/.github/workflows/test-and-lint.yml +0 -0
  35. {synkit-0.0.9 → synkit-0.0.10}/.gitignore +0 -0
  36. {synkit-0.0.9 → synkit-0.0.10}/.readthedocs.yml +0 -0
  37. {synkit-0.0.9 → synkit-0.0.10}/Data/Figure/synkit.png +0 -0
  38. {synkit-0.0.9 → synkit-0.0.10}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
  39. {synkit-0.0.9 → synkit-0.0.10}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
  40. {synkit-0.0.9 → synkit-0.0.10}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
  41. {synkit-0.0.9 → synkit-0.0.10}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
  42. {synkit-0.0.9 → synkit-0.0.10}/LICENSE +0 -0
  43. {synkit-0.0.9 → synkit-0.0.10}/Makefile +0 -0
  44. {synkit-0.0.9 → synkit-0.0.10}/README.md +0 -0
  45. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Fingerprint/__init__.py +0 -0
  46. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
  47. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
  48. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
  49. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Molecule/__init__.py +0 -0
  50. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Molecule/test_standardize.py +0 -0
  51. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/__init__.py +0 -0
  52. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_aam_utils.py +0 -0
  53. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_aam_validator.py +0 -0
  54. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_balance_checker.py +0 -0
  55. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_canon_rsmi.py +0 -0
  56. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_cleanning.py +0 -0
  57. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_deionize.py +0 -0
  58. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_fix_aam.py +0 -0
  59. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_neutralize.py +0 -0
  60. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_rsmi_utils.py +0 -0
  61. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_standardize.py +0 -0
  62. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/Reaction/test_tautomerize.py +0 -0
  63. {synkit-0.0.9 → synkit-0.0.10}/Test/Chem/__init__.py +0 -0
  64. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Context/__init__.py +0 -0
  65. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Context/test_hier_context.py +0 -0
  66. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Context/test_radius_expand.py +0 -0
  67. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Feature/__init__.py +0 -0
  68. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
  69. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Feature/test_graph_fps.py +0 -0
  70. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Feature/test_graph_signature.py +0 -0
  71. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Feature/test_hash_fps.py +0 -0
  72. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Feature/test_morgan_fps.py +0 -0
  73. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Feature/test_path_fps.py +0 -0
  74. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Hydrogen/__init__.py +0 -0
  75. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Hydrogen/test_graph_hydrogen.py +0 -0
  76. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Hydrogen/test_hcomplete.py +0 -0
  77. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Hydrogen/test_misc.py +0 -0
  78. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/ITS/__init__.py +0 -0
  79. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/ITS/test_its_construction.py +0 -0
  80. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/ITS/test_normalize_aam.py +0 -0
  81. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/MTG/__init__.py +0 -0
  82. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/MTG/test_group_comp.py +0 -0
  83. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/MTG/test_groupoid.py +0 -0
  84. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/MTG/test_mtg.py +0 -0
  85. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Matcher/__init__.py +0 -0
  86. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Matcher/test_batch_cluster.py +0 -0
  87. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Matcher/test_graph_cluster.py +0 -0
  88. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Matcher/test_graph_matcher.py +0 -0
  89. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Matcher/test_graph_morphism.py +0 -0
  90. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/Matcher/test_subgraph_matcher.py +0 -0
  91. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/__init__.py +0 -0
  92. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/test_canon_graph.py +0 -0
  93. {synkit-0.0.9 → synkit-0.0.10}/Test/Graph/test_syn_graph.py +0 -0
  94. {synkit-0.0.9 → synkit-0.0.10}/Test/IO/__init__.py +0 -0
  95. {synkit-0.0.9 → synkit-0.0.10}/Test/IO/test_chemical_converter.py +0 -0
  96. {synkit-0.0.9 → synkit-0.0.10}/Test/IO/test_dg_to_gml.py +0 -0
  97. {synkit-0.0.9 → synkit-0.0.10}/Test/IO/test_gml_to_nx.py +0 -0
  98. {synkit-0.0.9 → synkit-0.0.10}/Test/IO/test_graph_to_mol.py +0 -0
  99. {synkit-0.0.9 → synkit-0.0.10}/Test/IO/test_mol_to_graph.py +0 -0
  100. {synkit-0.0.9 → synkit-0.0.10}/Test/IO/test_nx_to_gml.py +0 -0
  101. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Apply/__init__.py +0 -0
  102. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Apply/test_reactor_rule.py +0 -0
  103. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Apply/test_retro_reactor.py +0 -0
  104. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Apply/test_rule_rbl.py +0 -0
  105. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Compose/__init__.py +0 -0
  106. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Compose/test_rule_compose.py +0 -0
  107. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Compose/test_valance_constrain.py +0 -0
  108. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Modify/__init__.py +0 -0
  109. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Modify/test_molecule_rule.py +0 -0
  110. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/Modify/test_rule_utils.py +0 -0
  111. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/__init__.py +0 -0
  112. {synkit-0.0.9 → synkit-0.0.10}/Test/Rule/test_syn_rule.py +0 -0
  113. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/CRN/__init__.py +0 -0
  114. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/CRN/test_crn.py +0 -0
  115. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/CRN/test_mod_crn.py +0 -0
  116. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/MSR/__init__.py +0 -0
  117. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/MSR/test_multi_steps.py +0 -0
  118. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/MSR/test_path_finder.py +0 -0
  119. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/Reactor/__init__.py +0 -0
  120. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/Reactor/test_core_engine.py +0 -0
  121. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/Reactor/test_mod_aam.py +0 -0
  122. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/Reactor/test_mod_reactor.py +0 -0
  123. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/Reactor/test_strategy.py +0 -0
  124. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/__init__.py +0 -0
  125. {synkit-0.0.9 → synkit-0.0.10}/Test/Synthesis/test_reactor_utils.py +0 -0
  126. {synkit-0.0.9 → synkit-0.0.10}/Test/Vis/__init__.py +0 -0
  127. {synkit-0.0.9 → synkit-0.0.10}/Test/Vis/test_embedding.py +0 -0
  128. {synkit-0.0.9 → synkit-0.0.10}/Test/__init__.py +0 -0
  129. {synkit-0.0.9 → synkit-0.0.10}/build-doc.sh +0 -0
  130. {synkit-0.0.9 → synkit-0.0.10}/doc/changelog.rst +0 -0
  131. {synkit-0.0.9 → synkit-0.0.10}/doc/chem.rst +0 -0
  132. {synkit-0.0.9 → synkit-0.0.10}/doc/figures/aldol.png +0 -0
  133. {synkit-0.0.9 → synkit-0.0.10}/doc/figures/aldol_its.png +0 -0
  134. {synkit-0.0.9 → synkit-0.0.10}/doc/figures/mtg.png +0 -0
  135. {synkit-0.0.9 → synkit-0.0.10}/doc/index.rst +0 -0
  136. {synkit-0.0.9 → synkit-0.0.10}/doc/io.rst +0 -0
  137. {synkit-0.0.9 → synkit-0.0.10}/doc/reference.rst +0 -0
  138. {synkit-0.0.9 → synkit-0.0.10}/doc/refs.bib +0 -0
  139. {synkit-0.0.9 → synkit-0.0.10}/doc/requirements.txt +0 -0
  140. {synkit-0.0.9 → synkit-0.0.10}/doc/rule.rst +0 -0
  141. {synkit-0.0.9 → synkit-0.0.10}/doc/synthesis.rst +0 -0
  142. {synkit-0.0.9 → synkit-0.0.10}/environment.yml +0 -0
  143. {synkit-0.0.9 → synkit-0.0.10}/lint.sh +0 -0
  144. {synkit-0.0.9 → synkit-0.0.10}/make.bat +0 -0
  145. {synkit-0.0.9 → synkit-0.0.10}/pytest.sh +0 -0
  146. {synkit-0.0.9 → synkit-0.0.10}/requirements.txt +0 -0
  147. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Fingerprint/__init__.py +0 -0
  148. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
  149. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
  150. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  151. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Molecule/__init__.py +0 -0
  152. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Molecule/standardize.py +0 -0
  153. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/__init__.py +0 -0
  154. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/aam_utils.py +0 -0
  155. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/aam_validator.py +0 -0
  156. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/balance_check.py +0 -0
  157. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
  158. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/cleanning.py +0 -0
  159. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/deionize.py +0 -0
  160. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/fix_aam.py +0 -0
  161. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/neutralize.py +0 -0
  162. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/rsmi_utils.py +0 -0
  163. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/standardize.py +0 -0
  164. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/Reaction/tautomerize.py +0 -0
  165. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/__init__.py +0 -0
  166. {synkit-0.0.9 → synkit-0.0.10}/synkit/Chem/utils.py +0 -0
  167. {synkit-0.0.9/synkit/Graph/Context → synkit-0.0.10/synkit/Data}/__init__.py +0 -0
  168. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Canon/__init__.py +0 -0
  169. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Canon/canon_algs.py +0 -0
  170. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Canon/canon_graph.py +0 -0
  171. {synkit-0.0.9/synkit/Graph/Hyrogen → synkit-0.0.10/synkit/Graph/Context}/__init__.py +0 -0
  172. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Context/hier_context.py +0 -0
  173. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Context/radius_expand.py +0 -0
  174. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/__init__.py +0 -0
  175. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  176. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/graph_fps.py +0 -0
  177. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/graph_signature.py +0 -0
  178. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/hash_fps.py +0 -0
  179. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/morgan_fps.py +0 -0
  180. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/path_fps.py +0 -0
  181. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Feature/wl_hash.py +0 -0
  182. {synkit-0.0.9/synkit/Graph/MTG → synkit-0.0.10/synkit/Graph/Hyrogen}/__init__.py +0 -0
  183. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Hyrogen/_misc.py +0 -0
  184. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
  185. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Hyrogen/hextend.py +0 -0
  186. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/ITS/__init__.py +0 -0
  187. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/ITS/normalize_aam.py +0 -0
  188. {synkit-0.0.9/synkit/Rule/Apply → synkit-0.0.10/synkit/Graph/MTG}/__init__.py +0 -0
  189. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/MTG/group_comp.py +0 -0
  190. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/MTG/groupoid.py +0 -0
  191. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/MTG/mcs_matcher.py +0 -0
  192. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/MTG/mtg.py +0 -0
  193. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/__init__.py +0 -0
  194. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/batch_cluster.py +0 -0
  195. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/graph_cluster.py +0 -0
  196. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/graph_matcher.py +0 -0
  197. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
  198. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
  199. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/partial_matcher.py +0 -0
  200. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/sing.py +0 -0
  201. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/subgraph_matcher.py +0 -0
  202. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/Matcher/turbo_iso.py +0 -0
  203. {synkit-0.0.9/synkit/Rule/Compose → synkit-0.0.10/synkit/Graph/Wildcard}/__init__.py +0 -0
  204. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/__init__.py +0 -0
  205. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/canon_graph.py +0 -0
  206. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/syn_graph.py +0 -0
  207. {synkit-0.0.9 → synkit-0.0.10}/synkit/Graph/utils.py +0 -0
  208. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/__init__.py +0 -0
  209. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/chem_converter.py +0 -0
  210. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/data_io.py +0 -0
  211. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/data_process.py +0 -0
  212. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/debug.py +0 -0
  213. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/dg_to_gml.py +0 -0
  214. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/gml_to_nx.py +0 -0
  215. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/graph_to_mol.py +0 -0
  216. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/mol_to_graph.py +0 -0
  217. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/nx_to_gml.py +0 -0
  218. {synkit-0.0.9 → synkit-0.0.10}/synkit/IO/smiles_to_id.py +0 -0
  219. {synkit-0.0.9/synkit/Rule/Modify → synkit-0.0.10/synkit/Rule/Apply}/__init__.py +0 -0
  220. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Apply/reactor_rule.py +0 -0
  221. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Apply/retro_reactor.py +0 -0
  222. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Apply/rule_rbl.py +0 -0
  223. {synkit-0.0.9/synkit/Synthesis/CRN → synkit-0.0.10/synkit/Rule/Compose}/__init__.py +0 -0
  224. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Compose/compose_rule.py +0 -0
  225. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Compose/rule_compose.py +0 -0
  226. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Compose/rule_mapping.py +0 -0
  227. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Compose/seq_comp.py +0 -0
  228. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Compose/valence_constrain.py +0 -0
  229. {synkit-0.0.9/synkit/Synthesis/MSR → synkit-0.0.10/synkit/Rule/Modify}/__init__.py +0 -0
  230. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Modify/implict_rule.py +0 -0
  231. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Modify/longest_path.py +0 -0
  232. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Modify/molecule_rule.py +0 -0
  233. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Modify/prune_templates.py +0 -0
  234. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Modify/rule_utils.py +0 -0
  235. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/Modify/strip_rule.py +0 -0
  236. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/__init__.py +0 -0
  237. {synkit-0.0.9 → synkit-0.0.10}/synkit/Rule/syn_rule.py +0 -0
  238. {synkit-0.0.9/synkit/Synthesis/Metrics → synkit-0.0.10/synkit/Synthesis/CRN}/__init__.py +0 -0
  239. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/CRN/crn.py +0 -0
  240. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/CRN/dcrn.py +0 -0
  241. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/CRN/mod_crn.py +0 -0
  242. {synkit-0.0.9/synkit/Synthesis/Reactor → synkit-0.0.10/synkit/Synthesis/MSR}/__init__.py +0 -0
  243. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/MSR/multi_steps.py +0 -0
  244. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/MSR/path_finder.py +0 -0
  245. {synkit-0.0.9/synkit/Synthesis → synkit-0.0.10/synkit/Synthesis/Metrics}/__init__.py +0 -0
  246. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Metrics/_base.py +0 -0
  247. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Metrics/_plot.py +0 -0
  248. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Metrics/_ranking.py +0 -0
  249. {synkit-0.0.9/synkit/Utils → synkit-0.0.10/synkit/Synthesis/Reactor}/__init__.py +0 -0
  250. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/batch_reactor.py +0 -0
  251. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/core_engine.py +0 -0
  252. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
  253. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
  254. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
  255. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
  256. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/Reactor/strategy.py +0 -0
  257. {synkit-0.0.9/synkit → synkit-0.0.10/synkit/Synthesis}/__init__.py +0 -0
  258. {synkit-0.0.9 → synkit-0.0.10}/synkit/Synthesis/reactor_utils.py +0 -0
  259. {synkit-0.0.9 → synkit-0.0.10}/synkit/Utils/utils.py +0 -0
  260. {synkit-0.0.9 → synkit-0.0.10}/synkit/Vis/__init__.py +0 -0
  261. {synkit-0.0.9 → synkit-0.0.10}/synkit/Vis/chemical_space.py +0 -0
  262. {synkit-0.0.9 → synkit-0.0.10}/synkit/Vis/embedding.py +0 -0
  263. {synkit-0.0.9 → synkit-0.0.10}/synkit/Vis/graph_visualizer.py +0 -0
  264. {synkit-0.0.9 → synkit-0.0.10}/synkit/Vis/pdf_writer.py +0 -0
  265. {synkit-0.0.9 → synkit-0.0.10}/synkit/Vis/rule_vis.py +0 -0
  266. {synkit-0.0.9 → synkit-0.0.10}/synkit/Vis/rxn_vis.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synkit
3
- Version: 0.0.9
3
+ Version: 0.0.10
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
@@ -0,0 +1,51 @@
1
+ import unittest
2
+ from synkit.Chem.Reaction.radical_wildcard import RadicalWildcardAdder
3
+
4
+
5
+ class TestRadicalWildcardAdder(unittest.TestCase):
6
+ """
7
+ Unit tests for RadicalWildcardAdder using unittest.
8
+ """
9
+
10
+ def test_transform_given_rxn(self):
11
+ """
12
+ Ensure that transform() applies the wildcard_map correctly and preserves explicit H.
13
+ """
14
+ rxn_in = (
15
+ "[CH3:1][CH2:2][C:3](=[O:4])[OH:5]."
16
+ "[O:6][H:7]>>"
17
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:6]."
18
+ "[OH:5][H:7]"
19
+ )
20
+ adder = RadicalWildcardAdder()
21
+ result = adder.transform(rxn_in)
22
+
23
+ expected = (
24
+ "[CH3:1][CH2:2][C:3](=[O:4])[OH:5]."
25
+ "[O:6]([H:7])[*:8]>>"
26
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:6][*:8]."
27
+ "[OH:5][H:7]"
28
+ )
29
+ self.assertEqual(result, expected)
30
+
31
+ def test_auto_map_selection_and_repr_str(self):
32
+ """
33
+ Check that wildcard_map is auto-selected and repr/str methods report correctly.
34
+ """
35
+ rxn = (
36
+ "[CH3:1][CH2:2][C:3](=[O:4])[OH:5]."
37
+ "[O:6][H:7]>>"
38
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:6]."
39
+ "[OH:5][H:7]"
40
+ )
41
+ adder = RadicalWildcardAdder()
42
+ out = adder.transform(rxn)
43
+
44
+ # The output should contain exactly one [*:8] in reactants and one in products
45
+ reactants, products = out.split(">>")
46
+ self.assertEqual(reactants.count("[*:8]"), 1)
47
+ self.assertEqual(products.count("[*:8]"), 1)
48
+
49
+
50
+ if __name__ == "__main__":
51
+ unittest.main()
@@ -12,7 +12,15 @@ class TestPartialExpand(unittest.TestCase):
12
12
  "[CH3:1][CH2:2][CH2:3][Cl:4].[NH2:5][H:6]"
13
13
  + ">>[CH3:1][CH2:2][CH2:3][NH2:5].[Cl:4][H:6]"
14
14
  )
15
- print(output_rsmi)
15
+ self.assertTrue(AAMValidator.smiles_check(output_rsmi, expected_rsmi, "ITS"))
16
+
17
+ def test_expand_with_relabel(self):
18
+ input_rsmi = "CC[CH2:3][Cl:1].[NH2:2][H:4]>>CC[CH2:3][NH2:2].[Cl:1][H:4]"
19
+ output_rsmi = ITSExpand.expand_aam_with_its(input_rsmi, relabel=True)
20
+ expected_rsmi = (
21
+ "[CH3:1][CH2:2][CH2:3][Cl:4].[NH2:5][H:6]"
22
+ + ">>[CH3:1][CH2:2][CH2:3][NH2:5].[Cl:4][H:6]"
23
+ )
16
24
  self.assertTrue(AAMValidator.smiles_check(output_rsmi, expected_rsmi, "ITS"))
17
25
 
18
26
 
@@ -0,0 +1,61 @@
1
+ import unittest
2
+ from rdkit import Chem
3
+ from synkit.Graph.ITS.its_relabel import ITSRelabel
4
+ from synkit.Graph.syn_graph import SynGraph
5
+ from synkit.IO.chem_converter import smiles_to_graph
6
+
7
+
8
+ class TestITSRelabel(unittest.TestCase):
9
+ def setUp(self):
10
+ self.its = ITSRelabel()
11
+
12
+ def test_get_nodes_with_atom_map(self):
13
+ # Build a graph from SMILES with explicit atom mapping
14
+ raw = smiles_to_graph(
15
+ "[CH3:1][CH2:2][OH:3]", use_index_as_atom_map=True, drop_non_aam=False
16
+ )
17
+ sg = SynGraph(raw)
18
+ nodes = ITSRelabel._get_nodes_with_atom_map(sg)
19
+ # Every atom should have a non-zero atom_map
20
+ self.assertCountEqual(nodes, list(sg.raw.nodes()))
21
+
22
+ def test_remove_internal_edges(self):
23
+ # Linear 3‑carbon chain; after removing internal edges none remain
24
+ raw = smiles_to_graph("CCC", use_index_as_atom_map=True, drop_non_aam=False)
25
+ sg = SynGraph(raw)
26
+ all_nodes = list(sg.raw.nodes())
27
+ pruned = ITSRelabel._remove_internal_edges(sg, all_nodes)
28
+ self.assertEqual(pruned.raw.number_of_edges(), 0)
29
+
30
+ def test_dict_to_tuple_list_sorting(self):
31
+ mapping = {3: 1, 2: 2, 1: 3}
32
+ # Sort by key
33
+ by_key = ITSRelabel._dict_to_tuple_list(mapping, sort_by_key=True)
34
+ self.assertEqual(by_key, [(1, 3), (2, 2), (3, 1)])
35
+ # Sort by value
36
+ by_val = ITSRelabel._dict_to_tuple_list(mapping, sort_by_value=True)
37
+ self.assertEqual(by_val, [(3, 1), (2, 2), (1, 3)])
38
+ # No sorting
39
+ no_sort = ITSRelabel._dict_to_tuple_list(mapping)
40
+ self.assertCountEqual(no_sort, [(1, 3), (2, 2), (3, 1)])
41
+
42
+ def test_fit_simple_reaction(self):
43
+ # CCO to CC=O, mapping preserved
44
+ input_rsmi = "CC[CH2:3][Cl:1].[N:2]>>CC[CH2:3][N:2].[Cl:1]"
45
+ out = self.its.fit(input_rsmi)
46
+ react, prod = out.split(">>")
47
+ self.assertIsNotNone(Chem.MolFromSmiles(react))
48
+ self.assertIsNotNone(Chem.MolFromSmiles(prod))
49
+
50
+ def test_fit_invalid_format_raises(self):
51
+ with self.assertRaises(ValueError):
52
+ self.its.fit("invalid_format")
53
+
54
+ def test_fit_non_isomorphic_raises(self):
55
+ # Wrong reaction format
56
+ with self.assertRaises(ValueError):
57
+ self.its.fit("C:1>CC:1")
58
+
59
+
60
+ if __name__ == "__main__":
61
+ unittest.main()
@@ -0,0 +1,94 @@
1
+ import io
2
+ import unittest
3
+ from contextlib import redirect_stdout
4
+
5
+ import networkx as nx
6
+
7
+ from synkit.IO.chem_converter import rsmi_to_its
8
+ from synkit.Rule.Apply.rule_matcher import RuleMatcher
9
+ from synkit.Chem.Reaction.standardize import Standardize
10
+ from synkit.Chem.Reaction.aam_validator import AAMValidator
11
+ from synkit.Chem.Reaction.balance_check import BalanceReactionCheck
12
+
13
+
14
+ class TestRuleMatcher(unittest.TestCase):
15
+
16
+ def test_rule_match_balance(self):
17
+ """Balanced reaction should match directly and produce correct SMARTS."""
18
+ input_rsmi = "CC[CH2:3][Cl:1].[NH2:2][H:4]>>CC[CH2:3][NH2:2].[Cl:1][H:4]"
19
+ rule = rsmi_to_its(input_rsmi, core=True)
20
+ rsmi_std = Standardize().fit(input_rsmi)
21
+ expected_rsmi = (
22
+ "[CH3:1][CH2:2][CH2:3][Cl:4].[NH2:5][H:6]"
23
+ ">>[CH3:1][CH2:2][CH2:3][NH2:5].[Cl:4][H:6]"
24
+ )
25
+
26
+ matcher = RuleMatcher(rsmi_std, rule)
27
+ smarts, returned_rule = matcher.get_result()
28
+
29
+ # The returned SMARTS should regenerate the expected RSMI via AAMValidator
30
+ self.assertTrue(AAMValidator.smiles_check(smarts, expected_rsmi, "ITS"))
31
+ # The returned rule graph should be isomorphic to the input rule
32
+ self.assertTrue(nx.is_isomorphic(returned_rule, rule))
33
+
34
+ def test_rbl_missing_product(self):
35
+ """Partial (RBL) match when product fragments are missing in rule."""
36
+ rsmi = "CC(Br)C.CB(O)O>>CC(C)C"
37
+ template = "[CH3:1][Br:2].[BH2:3][CH3:4]>>[CH3:1][CH3:4].[BH2:3][Br:2]"
38
+ matcher = RuleMatcher(rsmi, template)
39
+ smarts, _ = matcher.get_result()
40
+ expect = "CB(O)O.CC(C)Br>>CC(C)C.OB(O)Br"
41
+ self.assertEqual(Standardize().fit(smarts), expect)
42
+
43
+ def test_rbl_missing_reactant(self):
44
+ """Partial (RBL) match when reactant fragments are missing in rule."""
45
+ rsmi = "CCC(=O)(O)>>CCC(=O)OC.O"
46
+ template = (
47
+ "[CH3:1][C:2](=[O:3])[OH:4].[CH3:5][O:6][H:7]"
48
+ ">>[CH3:1][C:2](=[O:3])[O:6][CH3:5].[H:7][OH:4]"
49
+ )
50
+ matcher = RuleMatcher(rsmi, template)
51
+ smarts, _ = matcher.get_result()
52
+ expect = "CCC(=O)O.CO>>CCC(=O)OC.O"
53
+ self.assertEqual(Standardize().fit(smarts), expect)
54
+
55
+ def test_no_match_raises(self):
56
+ """If no SMARTS reproduces the RSMI under the rule, a ValueError is raised."""
57
+ rsmi = "CCO>>CC=O"
58
+ # Use a completely unrelated template
59
+ bad_template = "[CH3:1][OH:2]>>[CH2:1]=O"
60
+ with self.assertRaises(ValueError):
61
+ RuleMatcher(rsmi, bad_template)
62
+
63
+ def test_str_and_repr(self):
64
+ """__str__ and __repr__ reflect the RSMI, balance status, and rule size."""
65
+ input_rsmi = "CC[CH2:3][Cl:1].[NH2:2][H:4]>>CC[CH2:3][NH2:2].[Cl:1][H:4]"
66
+ template = rsmi_to_its(input_rsmi, core=True)
67
+ rsmi = Standardize().fit(input_rsmi)
68
+ matcher = RuleMatcher(rsmi, template)
69
+ # str should mention balanced/unbalanced correctly
70
+ if BalanceReactionCheck(n_jobs=1).rsmi_balance_check(matcher.rsmi):
71
+ self.assertIn("(balanced)", str(matcher))
72
+ else:
73
+ self.assertIn("(unbalanced)", str(matcher))
74
+ # repr should include node/edge counts of the rule
75
+ rep = repr(matcher)
76
+ self.assertIn("RuleMatcher(rsmi=", rep)
77
+ self.assertIn("balanced=", rep)
78
+
79
+ def test_help_output(self):
80
+ """help() should print internal state and list candidate SMARTS patterns."""
81
+ input_rsmi = "CC[CH2:3][Cl:1].[NH2:2][H:4]>>CC[CH2:3][NH2:2].[Cl:1][H:4]"
82
+ template = rsmi_to_its(input_rsmi, core=True)
83
+ rsmi = Standardize().fit(input_rsmi)
84
+ matcher = RuleMatcher(rsmi, template)
85
+ buf = io.StringIO()
86
+ with redirect_stdout(buf):
87
+ matcher.help()
88
+ out = buf.getvalue()
89
+ self.assertIn("RuleMatcher for RSMI", out)
90
+ self.assertIn("Candidate SMARTS patterns:", out)
91
+
92
+
93
+ if __name__ == "__main__":
94
+ unittest.main()
@@ -0,0 +1,48 @@
1
+ import unittest
2
+ from synkit.Synthesis.Reactor.partial_engine import PartialEngine
3
+
4
+
5
+ class TestPartialEngine(unittest.TestCase):
6
+ def test_forward_direction_example(self):
7
+ """
8
+ Example 1:
9
+ PartialEngine(smi='CCC(=O)OC',
10
+ template='[C:1][O:2].[O:3][H:4]>>[C:1][O:3].[O:2][H:4]')
11
+ .fit(invert=False)
12
+ should return the two forward wildcarded SMARTS.
13
+ """
14
+ smi = "CCC(=O)OC"
15
+ template = "[C:1][O:2].[O:3][H:4]>>[C:1][O:3].[O:2][H:4]"
16
+ engine = PartialEngine(smi, template)
17
+ result = engine.fit(invert=False)
18
+ expected = [
19
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:5][CH3:6].[OH:7][*:8]>>"
20
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:7][*:8].[OH:5][CH3:6]",
21
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:5][CH3:6].[OH:7][*:8]>>"
22
+ "[CH3:1][CH2:2][C:3](=[O:4])[OH:5].[CH3:6][O:7][*:8]",
23
+ ]
24
+ self.assertEqual(result, expected)
25
+
26
+ def test_backward_direction_example(self):
27
+ """
28
+ Example 2:
29
+ PartialEngine(smi='CCC(=O)OCC',
30
+ template='[C:1][O:2].[O:3][H:4]>>[C:1][O:3].[O:2][H:4]')
31
+ .fit(invert=True)
32
+ should return the two backward wildcarded SMARTS.
33
+ """
34
+ smi = "CCC(=O)OCC"
35
+ template = "[C:1][O:2].[O:3][H:4]>>[C:1][O:3].[O:2][H:4]"
36
+ engine = PartialEngine(smi, template)
37
+ result = engine.fit(invert=True)
38
+ expected = [
39
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:8][*:9].[OH:5][CH2:6][CH3:7]>>"
40
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:5][CH2:6][CH3:7].[OH:8][*:9]",
41
+ "[CH2:6]([CH3:7])[O:8][*:9].[CH3:1][CH2:2][C:3](=[O:4])[OH:5]>>"
42
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:5][CH2:6][CH3:7].[OH:8][*:9]",
43
+ ]
44
+ self.assertEqual(result, expected)
45
+
46
+
47
+ if __name__ == "__main__":
48
+ unittest.main()
@@ -0,0 +1,52 @@
1
+ import unittest
2
+ from synkit.IO import its_to_rsmi, rsmi_to_its
3
+ from synkit.Synthesis.Reactor.rbl_engine import RBLEngine
4
+
5
+
6
+ class TestRBLEngine(unittest.TestCase):
7
+ def test_example1(self):
8
+ # Example 1
9
+ rsmi = "CCC(=O)(O)>>CCC(=O)OC"
10
+ raw_template = (
11
+ "[CH3:1][C:2](=[O:3])[OH:4]."
12
+ "[CH3:5][O:6][H:7]>>"
13
+ "[CH3:1][C:2](=[O:3])[O:6][CH3:5]."
14
+ "[H:7][OH:4]"
15
+ )
16
+ its = rsmi_to_its(raw_template, core=True)
17
+ template = its_to_rsmi(its)
18
+
19
+ engine = RBLEngine(rsmi, template)
20
+ result = engine.fit()
21
+
22
+ expected = [
23
+ (
24
+ "[CH3:1][CH2:2][C:3](=[O:4])[OH:7]."
25
+ "[OH:5][CH3:6]>>"
26
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:5][CH3:6]."
27
+ "[OH2:7]"
28
+ )
29
+ ]
30
+ self.assertEqual(result, expected)
31
+
32
+ def test_example2(self):
33
+ # Example 2
34
+ rsmi = "CCC(=O)OC>>CCC(=O)OCC"
35
+ template = "[C:1][O:2].[O:3][H:4]>>[C:1][O:3].[O:2][H:4]"
36
+
37
+ engine = RBLEngine(rsmi, template)
38
+ result = engine.fit()
39
+
40
+ expected = [
41
+ (
42
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:8][CH3:9]."
43
+ "[OH:5][CH2:6][CH3:7]>>"
44
+ "[CH3:1][CH2:2][C:3](=[O:4])[O:5][CH2:6][CH3:7]."
45
+ "[OH:8][CH3:9]"
46
+ )
47
+ ]
48
+ self.assertEqual(result, expected)
49
+
50
+
51
+ if __name__ == "__main__":
52
+ unittest.main()
@@ -48,6 +48,7 @@ The `ITS` submodule provides tools for constructing, decomposing, and validating
48
48
 
49
49
  - **its_construction**: Functions for constructing an ITS graph.
50
50
  - **its_decompose**: Functions for decomposing an ITS graph and extracting reaction center.
51
+ - **its_expand**: Functions for expanding partial ITS graphs into full ITS graphs.
51
52
 
52
53
  .. automodule:: synkit.Graph.ITS.its_construction
53
54
  :members:
@@ -55,6 +56,11 @@ The `ITS` submodule provides tools for constructing, decomposing, and validating
55
56
  :show-inheritance:
56
57
 
57
58
  .. automodule:: synkit.Graph.ITS.its_decompose
59
+ :members: get_rc, its_decompose
60
+ :undoc-members:
61
+ :show-inheritance:
62
+
63
+ .. automodule:: synkit.Graph.ITS.its_expand
58
64
  :members:
59
65
  :undoc-members:
60
66
  :show-inheritance:
@@ -1,13 +1,28 @@
1
1
  import os
2
2
  import sys
3
+ from importlib.metadata import version as _get_version, PackageNotFoundError
3
4
 
5
+ # -- Path setup --------------------------------------------------------------
6
+ # Add project root to sys.path to import the package
4
7
  sys.path.insert(0, os.path.abspath(".."))
5
8
 
6
9
  # -- Project information -----------------------------------------------------
7
10
  project = "synkit"
8
11
  author = "Tieu-Long Phan"
9
- release = "0.0.8"
10
- version = release
12
+
13
+
14
+ try:
15
+ release = _get_version("synkit")
16
+ except PackageNotFoundError:
17
+ try:
18
+ import synkit
19
+
20
+ release = synkit.__version__
21
+ except (ImportError, AttributeError):
22
+ # Fallback default
23
+ release = "0.0.10"
24
+ # Use only major.minor for short version
25
+ version = ".".join(release.split(".")[:2])
11
26
 
12
27
  # -- General configuration ---------------------------------------------------
13
28
  extensions = [
Binary file
@@ -66,7 +66,7 @@ After installation, verify that **synkit** is available and check its version:
66
66
 
67
67
  .. code-block:: bash
68
68
 
69
- python -c "import synkit; print(synkit.__version__)"
69
+ python -c "import importlib.metadata as m; print(m.version('synkit'))"
70
70
  # Should print the installed synkit version
71
71
 
72
72
  Further Resources
@@ -254,6 +254,50 @@ This example builds two reaction-center ITS graphs, computes their MCS mapping,
254
254
  (C) Composite ITS graph "gluing" both transformations
255
255
  (D) Mechanistic Transition Graph (MTG) showing step-wise mechanism
256
256
 
257
+ Context graph
258
+ -------------
259
+
260
+ The ``synkit.Graph.Context`` submodule provides tools for expanding reaction center graphs to include nearest neighbors, enabling context‑aware analysis of reaction networks.
261
+
262
+ .. code-block:: python
263
+ :caption: Context graph expansion example
264
+ :linenos:
265
+
266
+ from synkit.IO import rsmi_to_its
267
+ from synkit.Graph.Context.radius_expand import RadiusExpand
268
+ from synkit.Vis.graph_visualizer import GraphVisualizer
269
+
270
+ smart = (
271
+ '[CH3:1][O:2][C:3](=[O:4])[CH:5]([CH2:6][CH2:7][CH2:8][CH2:9]'
272
+ '[NH:10][C:11](=[O:12])[O:13][CH2:14][c:15]1[cH:16][cH:17]'
273
+ '[cH:18][cH:19][cH:20]1)[NH:21][C:22](=[O:23])[NH:24][c:25]1'
274
+ '[cH:26][c:27]([O:28][CH3:29])[cH:30][c:31]([C:32]([CH3:33])'
275
+ '([CH3:34])[CH3:35])[c:36]1[OH:37].[OH:38][H:39]>>'
276
+ '[C:11](=[O:12])([O:13][CH2:14][c:15]1[cH:16][cH:17][cH:18]'
277
+ '[cH:19][cH:20]1)[OH:38].[CH3:1][O:2][C:3](=[O:4])[CH:5]'
278
+ '([CH2:6][CH2:7][CH2:8][CH2:9][NH:10][H:39])[NH:21][C:22]'
279
+ '(=[O:23])[NH:24][c:25]1[cH:26][c:27]([O:28][CH3:29])[cH:30]'
280
+ '[c:31]([C:32]([CH3:33])([CH3:34])[CH3:35])[c:36]1[OH:37]'
281
+ )
282
+ its = rsmi_to_its(smart)
283
+ rc = rsmi_to_its(smart, core=True)
284
+ exp = RadiusExpand()
285
+ k1 = exp.extract_k(its, n_knn=1)
286
+
287
+ gv = GraphVisualizer()
288
+ gv.visualize_its_grid([rc, k1])
289
+
290
+ .. container:: figure
291
+
292
+ .. image:: ./figures/context.png
293
+ :alt: Context graph expansion example
294
+ :align: center
295
+ :width: 1000px
296
+
297
+ *Figure:*
298
+ (A) Minimal reaction center subgraph obtained by contracting all atoms that participate directly in bond‑order changes.
299
+ Nodes are colour‑coded by element; edges in **red** indicate bonds being broken, while edges in **blue** mark bonds being formed.
300
+ (B) First shell ($k=1$) context expansion: every reaction center atom is augmented with all of its immediate neighbours.
257
301
 
258
302
 
259
303
  See Also
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "synkit"
7
- version = "0.0.9"
7
+ version = "0.0.10"
8
8
  authors = [
9
9
  {name="Tieu Long Phan", email="tieu@bioinf.uni-leipzig.de"}
10
10
  ]
@@ -0,0 +1,165 @@
1
+ from rdkit import Chem
2
+ from rdkit.Chem import SanitizeFlags
3
+ import re
4
+ from typing import Tuple, List, Optional, Dict
5
+
6
+
7
+ class RadicalWildcardAdder:
8
+ """
9
+ A utility for adding wildcard dummy atoms ([*]) to radical centers in reaction SMILES,
10
+ with unique incremental atom-map indices and correct propagation into products.
11
+
12
+ Each reactive radical atom in the reactant block is identified by its unpaired electron count,
13
+ assigned one or more wildcard map indices, and recorded. The same wildcard(s) are then appended
14
+ to the corresponding atom(s) in the product block, ensuring consistent mapping.
15
+
16
+ :param start_map: If provided, this integer will be the first atom-map index
17
+ used for wildcard dummy atoms; subsequent radicals get incremented indices.
18
+ If None, the next unused index is auto-determined from the input SMILES.
19
+ :type start_map: Optional[int]
20
+
21
+ Example
22
+ -------
23
+ >>> adder = RadicalWildcardAdder(start_map=8)
24
+ >>> rxn = "[C:2][OH:4].[O:6][H:7]>>[C:2][O:6].[OH:4][H:7]"
25
+ >>> print(adder.transform(rxn))
26
+ [C:2]([OH:4])[*:8].[O:6]([H:7])[*:9]>>[C:2]([O:6][*:9])[*:8].[OH:4][H:7]
27
+ """
28
+
29
+ def __init__(self, start_map: Optional[int] = None) -> None:
30
+ """
31
+ Initialize the adder with an optional starting map index.
32
+
33
+ :param start_map: Starting atom-map index for wildcards or None to auto-pick.
34
+ :type start_map: Optional[int]
35
+ """
36
+ self.start_map = start_map
37
+
38
+ def __repr__(self) -> str:
39
+ """
40
+ Official representation.
41
+ """
42
+ return f"<RadicalWildcardAdder(start_map={self.start_map})>"
43
+
44
+ def __str__(self) -> str:
45
+ """
46
+ User-friendly description.
47
+ """
48
+ m = self.start_map if self.start_map is not None else "auto"
49
+ return f"RadicalWildcardAdder(start_map={m})"
50
+
51
+ def transform(self, rxn_smiles: str) -> str:
52
+ """
53
+ Append wildcard dummy atoms to each radical center in the reactant block
54
+ and propagate the same wildcards to the matching atoms in the product block.
55
+
56
+ :param rxn_smiles: Reaction SMILES string, two-component or three-component.
57
+ :type rxn_smiles: str
58
+ :returns: Modified reaction SMILES with consistent wildcard attachments.
59
+ :rtype: str
60
+ :raises ValueError: If the SMILES is not valid or fragments fail to parse.
61
+ """
62
+ # Split into reactants > agents? > products
63
+ react_blk, agents_blk, prod_blk = self._split_reaction(rxn_smiles)
64
+
65
+ # Determine first wildcard map index
66
+ existing = [int(n) for n in re.findall(r":(\d+)", rxn_smiles)]
67
+ next_map = (
68
+ self.start_map
69
+ if self.start_map is not None
70
+ else max(existing, default=0) + 1
71
+ )
72
+
73
+ # Record mapping: original atom-map -> list of wildcard_maps
74
+ wildcard_map_for: Dict[int, List[int]] = {}
75
+
76
+ # Build sanitizeOps mask (skip H-adjustment)
77
+ keep_ops = SanitizeFlags.SANITIZE_ALL & ~SanitizeFlags.SANITIZE_ADJUSTHS
78
+
79
+ # Process one block (helper)
80
+ def _process(frags: List[str], propagate: bool) -> List[str]:
81
+ nonlocal next_map
82
+ out = []
83
+ for smi in frags:
84
+ if not smi:
85
+ continue
86
+ # Load unsanitized then re-sanitize to preserve explicit H
87
+ mol = Chem.MolFromSmiles(smi, sanitize=False)
88
+ if mol is None:
89
+ raise ValueError(f"Cannot parse SMILES fragment: {smi}")
90
+ Chem.SanitizeMol(mol, sanitizeOps=keep_ops)
91
+ rw = Chem.RWMol(mol)
92
+
93
+ atoms = list(rw.GetAtoms())
94
+ changed = False
95
+
96
+ for atom in atoms:
97
+ rad = atom.GetNumRadicalElectrons()
98
+ orig_map = atom.GetAtomMapNum()
99
+ if rad > 0:
100
+ # Initialize list for this orig_map
101
+ if propagate and orig_map not in wildcard_map_for:
102
+ wildcard_map_for[orig_map] = []
103
+ # For each unpaired electron, attach a wildcard
104
+ for _ in range(rad):
105
+ if propagate:
106
+ wm = next_map
107
+ wildcard_map_for[orig_map].append(wm)
108
+ next_map += 1
109
+ else:
110
+ # in products, use already-recorded wm sequentially
111
+ wm_list = wildcard_map_for.get(orig_map, [])
112
+ if not wm_list:
113
+ continue
114
+ wm = wm_list.pop(0)
115
+ # add dummy wildcard
116
+ dummy = Chem.Atom(0)
117
+ dummy.SetAtomMapNum(wm)
118
+ dummy.SetNoImplicit(True)
119
+ rw.AddAtom(dummy)
120
+ rw.AddBond(
121
+ atom.GetIdx(),
122
+ rw.GetNumAtoms() - 1,
123
+ Chem.BondType.SINGLE,
124
+ )
125
+ changed = True
126
+
127
+ if changed:
128
+ Chem.SanitizeMol(rw.GetMol(), sanitizeOps=keep_ops)
129
+
130
+ out.append(
131
+ Chem.MolToSmiles(
132
+ rw.GetMol(), isomericSmiles=True, allHsExplicit=True
133
+ )
134
+ )
135
+ return out
136
+
137
+ react_frags = react_blk.split(".") if react_blk else []
138
+ new_reacts = _process(react_frags, propagate=True)
139
+
140
+ prod_frags = prod_blk.split(".") if prod_blk else []
141
+ new_prods = _process(prod_frags, propagate=False)
142
+
143
+ react_str = ".".join(new_reacts)
144
+ prod_str = ".".join(new_prods)
145
+ if agents_blk is None:
146
+ return f"{react_str}>>{prod_str}"
147
+ return f"{react_str}>{agents_blk}>{prod_str}"
148
+
149
+ @staticmethod
150
+ def _split_reaction(rxn: str) -> Tuple[str, Optional[str], str]:
151
+ """
152
+ Split a reaction SMILES into reactants, agents (optional), and products.
153
+
154
+ :param rxn: The reaction SMILES string.
155
+ :type rxn: str
156
+ :returns: Tuple of (reactants_block, agents_block or None, products_block).
157
+ :rtype: Tuple[str, Optional[str], str]
158
+ :raises ValueError: If the SMILES does not contain 2 or 3 '>' symbols.
159
+ """
160
+ parts = rxn.split(">")
161
+ if len(parts) == 2:
162
+ return parts[0], None, parts[1]
163
+ if len(parts) == 3:
164
+ return parts[0], parts[1], parts[2]
165
+ raise ValueError("Reaction SMILES must contain 2 or 3 '>' symbols")