synkit 0.0.14__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 (295) hide show
  1. {synkit-0.0.14 → synkit-0.0.15}/.gitignore +1 -4
  2. {synkit-0.0.14 → synkit-0.0.15}/PKG-INFO +1 -1
  3. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/ITS/test_its_construction.py +14 -14
  4. {synkit-0.0.14 → synkit-0.0.15}/doc/graph.rst +1 -1
  5. {synkit-0.0.14 → synkit-0.0.15}/lint.sh +3 -1
  6. {synkit-0.0.14 → synkit-0.0.15}/pyproject.toml +1 -1
  7. {synkit-0.0.14 → synkit-0.0.15}/recipe/meta.yaml +1 -1
  8. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/utils.py +39 -0
  9. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Hyrogen/_misc.py +51 -90
  10. synkit-0.0.15/synkit/Graph/ITS/its_construction.py +316 -0
  11. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/ITS/its_decompose.py +2 -2
  12. synkit-0.0.15/synkit/Graph/ITS/its_destruction.py +302 -0
  13. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/subgraph_matcher.py +428 -143
  14. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/__init__.py +1 -0
  15. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/utils.py +25 -0
  16. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/chem_converter.py +11 -2
  17. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/nx_to_gml.py +2 -1
  18. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/syn_rule.py +3 -1
  19. synkit-0.0.15/synkit/Synthesis/Reactor/batch_reactor.py +462 -0
  20. synkit-0.0.15/synkit/Synthesis/Reactor/benchmark.py +152 -0
  21. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/imba_engine.py +8 -0
  22. synkit-0.0.15/synkit/Synthesis/Reactor/post_syn.py +267 -0
  23. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/syn_reactor.py +24 -3
  24. synkit-0.0.14/synkit/Graph/ITS/its_construction.py +0 -325
  25. synkit-0.0.14/synkit/Synthesis/Reactor/batch_reactor.py +0 -225
  26. {synkit-0.0.14 → synkit-0.0.15}/.github/dependabot.yml +0 -0
  27. {synkit-0.0.14 → synkit-0.0.15}/.github/workflows/build-doc.yml +0 -0
  28. {synkit-0.0.14 → synkit-0.0.15}/.github/workflows/conda-forge-publish.yml +0 -0
  29. {synkit-0.0.14 → synkit-0.0.15}/.github/workflows/docker-publish.yml +0 -0
  30. {synkit-0.0.14 → synkit-0.0.15}/.github/workflows/publish-package.yml +0 -0
  31. {synkit-0.0.14 → synkit-0.0.15}/.github/workflows/test-and-lint.yml +0 -0
  32. {synkit-0.0.14 → synkit-0.0.15}/.github/workflows/verify-pypi-install.yml +0 -0
  33. {synkit-0.0.14 → synkit-0.0.15}/.readthedocs.yml +0 -0
  34. {synkit-0.0.14 → synkit-0.0.15}/Data/Figure/synkit.png +0 -0
  35. {synkit-0.0.14 → synkit-0.0.15}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
  36. {synkit-0.0.14 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
  37. {synkit-0.0.14 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
  38. {synkit-0.0.14 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
  39. {synkit-0.0.14 → synkit-0.0.15}/Dockerfile +0 -0
  40. {synkit-0.0.14 → synkit-0.0.15}/LICENSE +0 -0
  41. {synkit-0.0.14 → synkit-0.0.15}/README.md +0 -0
  42. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Fingerprint/__init__.py +0 -0
  43. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
  44. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
  45. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
  46. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Molecule/__init__.py +0 -0
  47. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Molecule/test_standardize.py +0 -0
  48. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/__init__.py +0 -0
  49. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_aam_validator.py +0 -0
  50. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_balance_checker.py +0 -0
  51. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_canon_rsmi.py +0 -0
  52. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_cleanning.py +0 -0
  53. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_deionize.py +0 -0
  54. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_fix_aam.py +0 -0
  55. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_neutralize.py +0 -0
  56. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_radical_wildcard.py +0 -0
  57. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_standardize.py +0 -0
  58. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/Reaction/test_tautomerize.py +0 -0
  59. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/__init__.py +0 -0
  60. {synkit-0.0.14 → synkit-0.0.15}/Test/Chem/test_utils.py +0 -0
  61. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Context/__init__.py +0 -0
  62. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Context/test_hier_context.py +0 -0
  63. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Context/test_radius_expand.py +0 -0
  64. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Feature/__init__.py +0 -0
  65. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
  66. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Feature/test_graph_fps.py +0 -0
  67. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Feature/test_graph_signature.py +0 -0
  68. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Feature/test_hash_fps.py +0 -0
  69. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Feature/test_morgan_fps.py +0 -0
  70. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Feature/test_path_fps.py +0 -0
  71. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Hydrogen/__init__.py +0 -0
  72. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Hydrogen/test_graph_hydrogen.py +0 -0
  73. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Hydrogen/test_hcomplete.py +0 -0
  74. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Hydrogen/test_misc.py +0 -0
  75. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/ITS/__init__.py +0 -0
  76. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/ITS/test_its_expand.py +0 -0
  77. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/ITS/test_its_relabel.py +0 -0
  78. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/ITS/test_normalize_aam.py +0 -0
  79. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/MTG/__init__.py +0 -0
  80. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/MTG/test_group_comp.py +0 -0
  81. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/MTG/test_groupoid.py +0 -0
  82. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/MTG/test_mtg.py +0 -0
  83. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Matcher/__init__.py +0 -0
  84. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Matcher/test_batch_cluster.py +0 -0
  85. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_cluster.py +0 -0
  86. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_matcher.py +0 -0
  87. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_morphism.py +0 -0
  88. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Matcher/test_subgraph_matcher.py +0 -0
  89. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Wildcard/__init__.py +0 -0
  90. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Wildcard/test_radwc.py +0 -0
  91. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/Wildcard/test_wildcard.py +0 -0
  92. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/__init__.py +0 -0
  93. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/test_canon_graph.py +0 -0
  94. {synkit-0.0.14 → synkit-0.0.15}/Test/Graph/test_syn_graph.py +0 -0
  95. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/__init__.py +0 -0
  96. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/combinatorial/__init__.py +0 -0
  97. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/combinatorial/test_smarts_expander.py +0 -0
  98. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/combinatorial/test_smarts_generalizer.py +0 -0
  99. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/combinatorial/test_smarts_to_graph.py +0 -0
  100. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/test_chemical_converter.py +0 -0
  101. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/test_dg_to_gml.py +0 -0
  102. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/test_gml_to_nx.py +0 -0
  103. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/test_graph_to_mol.py +0 -0
  104. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/test_mol_to_graph.py +0 -0
  105. {synkit-0.0.14 → synkit-0.0.15}/Test/IO/test_nx_to_gml.py +0 -0
  106. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Apply/__init__.py +0 -0
  107. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Apply/test_reactor_rule.py +0 -0
  108. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Apply/test_retro_reactor.py +0 -0
  109. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Apply/test_rule_matcher.py +0 -0
  110. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Apply/test_rule_rbl.py +0 -0
  111. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Compose/__init__.py +0 -0
  112. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Compose/test_rule_compose.py +0 -0
  113. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Compose/test_valance_constrain.py +0 -0
  114. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Modify/__init__.py +0 -0
  115. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Modify/test_molecule_rule.py +0 -0
  116. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/Modify/test_rule_utils.py +0 -0
  117. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/__init__.py +0 -0
  118. {synkit-0.0.14 → synkit-0.0.15}/Test/Rule/test_syn_rule.py +0 -0
  119. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/CRN/__init__.py +0 -0
  120. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/CRN/test_crn.py +0 -0
  121. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/CRN/test_mod_crn.py +0 -0
  122. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/MSR/__init__.py +0 -0
  123. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/MSR/test_multi_steps.py +0 -0
  124. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/MSR/test_path_finder.py +0 -0
  125. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/Reactor/__init__.py +0 -0
  126. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/Reactor/test_imba_engine.py +0 -0
  127. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/Reactor/test_mod_aam.py +0 -0
  128. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/Reactor/test_mod_reactor.py +0 -0
  129. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/Reactor/test_partial_engine.py +0 -0
  130. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/Reactor/test_rbl_reactor.py +0 -0
  131. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/Reactor/test_strategy.py +0 -0
  132. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/__init__.py +0 -0
  133. {synkit-0.0.14 → synkit-0.0.15}/Test/Synthesis/test_reactor_utils.py +0 -0
  134. {synkit-0.0.14 → synkit-0.0.15}/Test/Vis/__init__.py +0 -0
  135. {synkit-0.0.14 → synkit-0.0.15}/Test/Vis/test_embedding.py +0 -0
  136. {synkit-0.0.14 → synkit-0.0.15}/Test/__init__.py +0 -0
  137. {synkit-0.0.14 → synkit-0.0.15}/build-doc.sh +0 -0
  138. {synkit-0.0.14 → synkit-0.0.15}/doc/api.rst +0 -0
  139. {synkit-0.0.14 → synkit-0.0.15}/doc/changelog.rst +0 -0
  140. {synkit-0.0.14 → synkit-0.0.15}/doc/chem.rst +0 -0
  141. {synkit-0.0.14 → synkit-0.0.15}/doc/conf.py +0 -0
  142. {synkit-0.0.14 → synkit-0.0.15}/doc/figures/aldol.png +0 -0
  143. {synkit-0.0.14 → synkit-0.0.15}/doc/figures/aldol_its.png +0 -0
  144. {synkit-0.0.14 → synkit-0.0.15}/doc/figures/context.png +0 -0
  145. {synkit-0.0.14 → synkit-0.0.15}/doc/figures/mtg.png +0 -0
  146. {synkit-0.0.14 → synkit-0.0.15}/doc/figures/mtg_mechanism.png +0 -0
  147. {synkit-0.0.14 → synkit-0.0.15}/doc/getting_started.rst +0 -0
  148. {synkit-0.0.14 → synkit-0.0.15}/doc/index.rst +0 -0
  149. {synkit-0.0.14 → synkit-0.0.15}/doc/io.rst +0 -0
  150. {synkit-0.0.14 → synkit-0.0.15}/doc/reference.rst +0 -0
  151. {synkit-0.0.14 → synkit-0.0.15}/doc/refs.bib +0 -0
  152. {synkit-0.0.14 → synkit-0.0.15}/doc/requirements.txt +0 -0
  153. {synkit-0.0.14 → synkit-0.0.15}/doc/rule.rst +0 -0
  154. {synkit-0.0.14 → synkit-0.0.15}/doc/synthesis.rst +0 -0
  155. {synkit-0.0.14 → synkit-0.0.15}/environment.yml +0 -0
  156. {synkit-0.0.14 → synkit-0.0.15}/pytest.sh +0 -0
  157. {synkit-0.0.14 → synkit-0.0.15}/requirements.txt +0 -0
  158. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Cluster/__init__.py +0 -0
  159. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Cluster/butina.py +0 -0
  160. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Fingerprint/__init__.py +0 -0
  161. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
  162. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
  163. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  164. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Molecule/__init__.py +0 -0
  165. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Molecule/standardize.py +0 -0
  166. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/__init__.py +0 -0
  167. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/aam_validator.py +0 -0
  168. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/balance_check.py +0 -0
  169. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
  170. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/cleaning.py +0 -0
  171. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/deionize.py +0 -0
  172. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/fix_aam.py +0 -0
  173. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/neutralize.py +0 -0
  174. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/radical_wildcard.py +0 -0
  175. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/standardize.py +0 -0
  176. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/Reaction/tautomerize.py +0 -0
  177. {synkit-0.0.14 → synkit-0.0.15}/synkit/Chem/__init__.py +0 -0
  178. {synkit-0.0.14 → synkit-0.0.15}/synkit/Data/__init__.py +0 -0
  179. {synkit-0.0.14 → synkit-0.0.15}/synkit/Data/gen_partial_aam.py +0 -0
  180. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Canon/__init__.py +0 -0
  181. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Canon/canon_algs.py +0 -0
  182. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Canon/canon_graph.py +0 -0
  183. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Canon/nauty.py +0 -0
  184. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Context/__init__.py +0 -0
  185. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Context/hier_context.py +0 -0
  186. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Context/radius_expand.py +0 -0
  187. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/Fingerprint/__init__.py +0 -0
  188. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +0 -0
  189. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/__init__.py +0 -0
  190. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  191. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/graph_fps.py +0 -0
  192. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/graph_signature.py +0 -0
  193. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/hash_fps.py +0 -0
  194. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/morgan_fps.py +0 -0
  195. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/path_fps.py +0 -0
  196. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Feature/wl_hash.py +0 -0
  197. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Hyrogen/__init__.py +0 -0
  198. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
  199. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Hyrogen/hextend.py +0 -0
  200. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/ITS/__init__.py +0 -0
  201. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/ITS/its_builder.py +0 -0
  202. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/ITS/its_expand.py +0 -0
  203. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/ITS/its_relabel.py +0 -0
  204. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/ITS/normalize_aam.py +0 -0
  205. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/ITS/partial_its.py +0 -0
  206. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/MTG/__init__.py +0 -0
  207. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/MTG/group_comp.py +0 -0
  208. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/MTG/groupoid.py +0 -0
  209. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/MTG/mcs_matcher.py +0 -0
  210. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/MTG/mtg.py +0 -0
  211. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/MTG/mtg_explore.py +0 -0
  212. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/MTG/utils.py +0 -0
  213. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/__init__.py +0 -0
  214. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/batch_cluster.py +0 -0
  215. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/graph_cluster.py +0 -0
  216. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/graph_matcher.py +0 -0
  217. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/graph_morphism.py +0 -0
  218. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
  219. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
  220. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/partial_matcher.py +0 -0
  221. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/sing.py +0 -0
  222. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Matcher/turbo_iso.py +0 -0
  223. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Wildcard/__init__.py +0 -0
  224. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
  225. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Wildcard/radwc.py +0 -0
  226. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/Wildcard/wildcard.py +0 -0
  227. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/canon_graph.py +0 -0
  228. {synkit-0.0.14 → synkit-0.0.15}/synkit/Graph/syn_graph.py +0 -0
  229. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/__init__.py +0 -0
  230. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/combinatorial/__init__.py +0 -0
  231. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/combinatorial/gml_to_graph.py +0 -0
  232. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/combinatorial/graph_to_gml.py +0 -0
  233. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/combinatorial/graph_to_smarts.py +0 -0
  234. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/combinatorial/smarts_expander.py +0 -0
  235. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/combinatorial/smarts_generalizer.py +0 -0
  236. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/combinatorial/smarts_to_graph.py +0 -0
  237. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/data_io.py +0 -0
  238. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/data_process.py +0 -0
  239. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/debug.py +0 -0
  240. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/dg_to_gml.py +0 -0
  241. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/gml_to_nx.py +0 -0
  242. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/graph_to_mol.py +0 -0
  243. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/mol_to_graph.py +0 -0
  244. {synkit-0.0.14 → synkit-0.0.15}/synkit/IO/smiles_to_id.py +0 -0
  245. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Apply/__init__.py +0 -0
  246. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Apply/reactor_rule.py +0 -0
  247. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Apply/retro_reactor.py +0 -0
  248. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Apply/rule_matcher.py +0 -0
  249. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Apply/rule_rbl.py +0 -0
  250. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Compose/__init__.py +0 -0
  251. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Compose/compose_rule.py +0 -0
  252. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Compose/rule_compose.py +0 -0
  253. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Compose/rule_mapping.py +0 -0
  254. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Compose/seq_comp.py +0 -0
  255. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Compose/valence_constrain.py +0 -0
  256. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Modify/__init__.py +0 -0
  257. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Modify/implict_rule.py +0 -0
  258. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Modify/longest_path.py +0 -0
  259. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Modify/molecule_rule.py +0 -0
  260. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Modify/prune_templates.py +0 -0
  261. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Modify/rule_utils.py +0 -0
  262. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/Modify/strip_rule.py +0 -0
  263. {synkit-0.0.14 → synkit-0.0.15}/synkit/Rule/__init__.py +0 -0
  264. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/CRN/__init__.py +0 -0
  265. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/CRN/crn.py +0 -0
  266. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/CRN/dcrn.py +0 -0
  267. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/CRN/mod_crn.py +0 -0
  268. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/MSR/__init__.py +0 -0
  269. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/MSR/multi_steps.py +0 -0
  270. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/MSR/path_finder.py +0 -0
  271. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Metrics/__init__.py +0 -0
  272. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Metrics/_base.py +0 -0
  273. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Metrics/_plot.py +0 -0
  274. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Metrics/_ranking.py +0 -0
  275. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/__init__.py +0 -0
  276. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
  277. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
  278. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
  279. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
  280. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
  281. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
  282. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/Reactor/strategy.py +0 -0
  283. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/__init__.py +0 -0
  284. {synkit-0.0.14 → synkit-0.0.15}/synkit/Synthesis/reactor_utils.py +0 -0
  285. {synkit-0.0.14 → synkit-0.0.15}/synkit/Utils/__init__.py +0 -0
  286. {synkit-0.0.14 → synkit-0.0.15}/synkit/Utils/utils.py +0 -0
  287. {synkit-0.0.14 → synkit-0.0.15}/synkit/Vis/__init__.py +0 -0
  288. {synkit-0.0.14 → synkit-0.0.15}/synkit/Vis/chemical_space.py +0 -0
  289. {synkit-0.0.14 → synkit-0.0.15}/synkit/Vis/embedding.py +0 -0
  290. {synkit-0.0.14 → synkit-0.0.15}/synkit/Vis/graph_visualizer.py +0 -0
  291. {synkit-0.0.14 → synkit-0.0.15}/synkit/Vis/pdf_writer.py +0 -0
  292. {synkit-0.0.14 → synkit-0.0.15}/synkit/Vis/rule_vis.py +0 -0
  293. {synkit-0.0.14 → synkit-0.0.15}/synkit/Vis/rxn_vis.py +0 -0
  294. {synkit-0.0.14 → synkit-0.0.15}/synkit/__init__.py +0 -0
  295. {synkit-0.0.14 → synkit-0.0.15}/synkit/examples.py +0 -0
@@ -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
@@ -24,3 +20,4 @@ run_rdcanon.py
24
20
  Data/Fragment/*
25
21
  test_partial.py
26
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.14
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
@@ -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__":
@@ -22,7 +22,7 @@ The class :py:class:`~synkit.Graph.canon_graph.GraphCanonicaliser` canonicalises
22
22
 
23
23
  from synkit.IO import rsmi_to_its
24
24
  from synkit.Graph.canon_graph import GraphCanonicaliser
25
- from synkit.Graph.matcher.graph_matcher import GraphMatcherEngine
25
+ from synkit.Graph.Matcher.graph_matcher import GraphMatcherEngine
26
26
 
27
27
  canon = GraphCanonicaliser(backend='wl', wl_iterations=3)
28
28
  rsmi = (
@@ -20,7 +20,8 @@ sing.py:C901,\
20
20
  turbo_iso.py:C901,\
21
21
  rule_vis.py:C901,
22
22
  gml_to_graph.py:C901,
23
- wildcard.py:C901" \
23
+ wildcard.py:C901,
24
+ its_destruction.py:C901" \
24
25
  --exclude=venv,\
25
26
  core_engine.py,\
26
27
  rule_apply.py,\
@@ -28,5 +29,6 @@ reactor_engine.py,\
28
29
  groupoid.py,\
29
30
  syn_rule.py,\
30
31
  __init__.py,\
32
+ dev/*,\
31
33
  Data \
32
34
  --statistics
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "synkit"
7
- version = "0.0.14"
7
+ version = "0.0.15"
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.14
3
+ version: 0.0.15
4
4
 
5
5
  source:
6
6
  path: ..
@@ -5,6 +5,45 @@ import re
5
5
  from typing import List, Optional, Tuple, Union
6
6
 
7
7
 
8
+ def clean_radical_rsmi(rsmi: str) -> str:
9
+ """
10
+ Load each side of a reaction SMILES (rSMI) into RDKit, split into disconnected fragments,
11
+ remove any fragment that contains an atom with nonzero radical electrons,
12
+ then reassemble back into a cleaned reaction SMILES.
13
+
14
+ :param rsmi: Reaction SMILES string, e.g.
15
+ 'A>>B.C'
16
+ :type rsmi: str
17
+ :returns: Cleaned reaction SMILES with radical-containing fragments removed.
18
+ :rtype: str
19
+
20
+ Example:
21
+ >>> clean_radical_rsmi(
22
+ ... 'COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(=O)Nc1cc(OC)cc(C(C)(C)C)c1O'
23
+ ... '>>COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(N)=O.COc1c[c]c(O)c(C(C)(C)C)c1'
24
+ ... )
25
+ 'COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(=O)Nc1cc(OC)cc(C(C)(C)C)c1O'
26
+ '>>COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(N)=O'
27
+ """
28
+ if ">>" not in rsmi:
29
+ return rsmi
30
+
31
+ def _clean_side(side: str) -> str:
32
+ mol = Chem.MolFromSmiles(side)
33
+ if mol is None:
34
+ return ""
35
+ frags = Chem.GetMolFrags(mol, asMols=True)
36
+ kept = []
37
+ for frag in frags:
38
+ if any(atom.GetNumRadicalElectrons() > 0 for atom in frag.GetAtoms()):
39
+ continue
40
+ kept.append(Chem.MolToSmiles(frag, isomericSmiles=True))
41
+ return ".".join(kept)
42
+
43
+ reac, prod = rsmi.split(">>", 1)
44
+ return f"{_clean_side(reac)}>>{_clean_side(prod)}"
45
+
46
+
8
47
  def enumerate_tautomers(reaction_smiles: str) -> Optional[List[str]]:
9
48
  """Enumerate possible tautomers of reactants while canonicalizing products.
10
49
 
@@ -1,7 +1,7 @@
1
1
  from copy import copy
2
2
  import networkx as nx
3
3
  from operator import eq
4
- from typing import List, Set, Any, Tuple
4
+ from typing import List, Set, Any, Tuple, Iterable, Optional
5
5
  from networkx.algorithms.isomorphism import generic_node_match, generic_edge_match
6
6
 
7
7
  from synkit.Graph.Feature.graph_descriptors import GraphDescriptor
@@ -233,95 +233,56 @@ def implicit_hydrogen(
233
233
  return new_graph
234
234
 
235
235
 
236
- # def explicit_hydrogen(graph: nx.Graph) -> nx.Graph:
237
- # """
238
- # Adds explicit hydrogens to the molecular graph based on hydrogen counts ('hcount') for non-hydrogen
239
- # atoms and increases the 'atom_map' attribute for each hydrogen added. This function assumes that
240
- # 'hcount' is present for each atom (representing how many hydrogens should be added) and that the
241
- # 'atom_map' for existing atoms is valid.
242
-
243
- # Parameters:
244
- # - graph (nx.Graph): A NetworkX graph representing the molecule, where each node has an 'element'
245
- # attribute for the element type (e.g., 'C', 'H'), 'hcount' for the number of hydrogens to add,
246
- # and 'atom_map' for atom mapping.
247
-
248
- # Returns:
249
- # - nx.Graph: A new NetworkX graph with explicit hydrogen atoms added and 'atom_map' updated.
250
- # """
251
- # warnings.warn(
252
- # "This function can only work with single graph and cannot guarantee the mapping between G and H"
253
- # )
254
- # # Create a deep copy of the graph to avoid in-place modifications
255
- # new_graph = copy(graph)
256
-
257
- # # Find the maximum atom_map currently in the graph
258
- # max_atom_map = max(
259
- # [
260
- # data["atom_map"]
261
- # for node, data in new_graph.nodes(data=True)
262
- # if "atom_map" in data
263
- # ],
264
- # default=0,
265
- # )
266
-
267
- # # Prepare a list of nodes that will need explicit hydrogens
268
- # hydrogen_id = max_atom_map + 1 # Start adding hydrogens from max atom_map + 1
269
- # hydrogen_additions = [] # To keep track of hydrogens to add
270
-
271
- # # First, collect all nodes that need hydrogens
272
- # for node, data in new_graph.nodes(data=True):
273
- # if data["element"] != "H": # Skip hydrogens
274
- # hcount = data.get("hcount", 0) # Number of hydrogens to add
275
- # for _ in range(hcount):
276
- # hydrogen_additions.append((node, hydrogen_id))
277
- # hydrogen_id += 1 # Increment for next hydrogen
278
-
279
- # # Now, add the hydrogens and update the graph
280
- # for parent, hydrogen_atom_map in hydrogen_additions:
281
- # hydrogen_node = f"H_{hydrogen_atom_map}"
282
- # new_graph.add_node(hydrogen_node, element="H", atom_map=hydrogen_atom_map)
283
- # new_graph.add_edge(
284
- # parent, hydrogen_node
285
- # ) # Connect the hydrogen to its parent atom
286
-
287
- # return new_graph
288
-
289
-
290
- # def expand_hydrogens(graph: nx.Graph) -> nx.Graph:
291
- # """
292
- # For each node in the graph that has an 'hcount' attribute greater than zero,
293
- # adds the specified number of hydrogen nodes and connects them with edges that
294
- # have specific attributes.
295
-
296
- # Parameters
297
- # - graph (nx.Graph): A graph representing a molecule with nodes that can
298
- # include 'element', 'hcount', 'charge', and 'atom_map' attributes.
299
-
300
- # Returns:
301
- # - nx.Graph: A new graph with hydrogen atoms expanded.
302
- # """
303
- # new_graph = graph.copy() # Create a copy to modify and return
304
- # atom_map = (
305
- # max(data["atom_map"] for _, data in graph.nodes(data=True))
306
- # if graph.nodes
307
- # else 0
308
- # )
309
-
310
- # # Iterate through each node to process potential hydrogens
311
- # for node, data in graph.nodes(data=True):
312
- # hcount = data.get("hcount", 0)
313
- # if hcount > 0:
314
- # for _ in range(hcount):
315
- # atom_map += 1
316
- # hydrogen_node = {
317
- # "element": "H",
318
- # "charge": 0,
319
- # "atom_map": atom_map,
320
- # }
321
- # new_graph.add_node(atom_map, **hydrogen_node)
322
- # new_graph.add_edge(node, atom_map, order=(1.0, 1.0), standard_order=0.0)
323
-
324
- # return new_graph
236
+ def _normalize_sequence_at_index(
237
+ seqs: Iterable[Any], index: int = 2, target_min: int = 0
238
+ ) -> Optional[Tuple[Any, ...]]:
239
+ """
240
+ If possible, shift the integer at `index` in each sequence so that the minimum becomes
241
+ `target_min`. Returns a new tuple of sequences if any change is needed, else None.
242
+ """
243
+ # collect valid ints at the index
244
+ valid_vals = [
245
+ t[index]
246
+ for t in seqs
247
+ if isinstance(t, (list, tuple)) and len(t) > index and isinstance(t[index], int)
248
+ ]
249
+ if not valid_vals:
250
+ return None
251
+
252
+ offset = min(valid_vals) - target_min
253
+ if offset == 0:
254
+ return None # already normalized
255
+
256
+ def normalize(t):
257
+ if (
258
+ isinstance(t, (list, tuple))
259
+ and len(t) > index
260
+ and isinstance(t[index], int)
261
+ ):
262
+ t_list = list(t)
263
+ t_list[index] = t_list[index] - offset
264
+ return tuple(t_list)
265
+ return t # leave untouched
266
+
267
+ return tuple(normalize(t) for t in seqs)
268
+
269
+
270
+ def standardize_hydrogen(G: nx.Graph, in_place: bool = False) -> nx.Graph:
271
+ """
272
+ For each node, shift the third element (index 2) of each tuple in 'typesGH' so that the
273
+ minimum among those values becomes zero. Nonconforming entries are preserved.
274
+ """
275
+ target = G if in_place else G.copy()
276
+
277
+ for node, data in target.nodes(data=True):
278
+ typesGH = data.get("typesGH")
279
+ if not typesGH:
280
+ continue
281
+ normalized = _normalize_sequence_at_index(typesGH, index=2, target_min=0)
282
+ if normalized is not None:
283
+ target.nodes[node]["typesGH"] = normalized
284
+
285
+ return target
325
286
 
326
287
 
327
288
  def check_equivariant_graph(
@@ -0,0 +1,316 @@
1
+ import networkx as nx
2
+ from typing import Tuple, Dict, Any, Optional, List, Hashable
3
+ from copy import deepcopy
4
+
5
+
6
+ class ITSConstruction:
7
+ # Core defaults; mutable ones (like neighbors) are factories to avoid shared state.
8
+ CORE_NODE_DEFAULTS: Dict[str, Any] = {
9
+ "element": "*",
10
+ "charge": 0,
11
+ "atom_map": 0,
12
+ "hcount": 0,
13
+ "aromatic": False,
14
+ "neighbors": lambda: ["", ""],
15
+ }
16
+
17
+ CORE_EDGE_DEFAULTS: Dict[str, Any] = {
18
+ "order": 0.0,
19
+ "ez_isomer": "",
20
+ "bond_type": "",
21
+ "conjugated": False,
22
+ "in_ring": False,
23
+ }
24
+
25
+ @staticmethod
26
+ def _resolve_defaults(
27
+ user_defaults: Optional[Dict[str, Any]], core_defaults: Dict[str, Any]
28
+ ) -> Dict[str, Any]:
29
+ """
30
+ Merge user-specified defaults with core defaults, producing fresh copies for mutables.
31
+
32
+ :param user_defaults: Overrides provided by the caller.
33
+ :type user_defaults: dict[str, Any] or None
34
+ :param core_defaults: The built-in default mapping; values may be factories.
35
+ :type core_defaults: dict[str, Any]
36
+ :returns: Fully resolved defaults with user values taking precedence and fresh instances for factories.
37
+ :rtype: dict[str, Any]
38
+ """
39
+ resolved: Dict[str, Any] = {}
40
+ user_defaults = user_defaults or {}
41
+ for key, core_val in core_defaults.items():
42
+ if key in user_defaults:
43
+ resolved[key] = deepcopy(user_defaults[key])
44
+ else:
45
+ if callable(core_val):
46
+ resolved[key] = core_val()
47
+ else:
48
+ resolved[key] = deepcopy(core_val)
49
+ return resolved
50
+
51
+ @staticmethod
52
+ def _compute_standard_order(
53
+ its: nx.Graph, ignore_aromaticity: bool = False
54
+ ) -> None:
55
+ """
56
+ In-place compute and assign 'standard_order' on each edge of the ITS graph.
57
+
58
+ :param its: ITS graph whose edges have an 'order' tuple.
59
+ :type its: nx.Graph
60
+ :param ignore_aromaticity: If True, absolute differences < 1 are zeroed.
61
+ :type ignore_aromaticity: bool
62
+ """
63
+ for u, v, data in its.edges(data=True):
64
+ order_tuple = data.get("order", (0.0, 0.0))
65
+ try:
66
+ o_g, o_h = order_tuple
67
+ except Exception:
68
+ o_g, o_h = 0.0, 0.0
69
+ standard_order = o_g - o_h
70
+ if ignore_aromaticity and abs(standard_order) < 1:
71
+ standard_order = 0
72
+ its[u][v]["standard_order"] = standard_order
73
+
74
+ @staticmethod
75
+ def construct(
76
+ G: nx.Graph,
77
+ H: nx.Graph,
78
+ *,
79
+ ignore_aromaticity: bool = False,
80
+ balance_its: bool = True,
81
+ store: bool = True,
82
+ node_attrs: Optional[List[str]] = None,
83
+ edge_attrs: Optional[List[str]] = None,
84
+ attributes_defaults: Optional[Dict[str, Any]] = None,
85
+ ) -> nx.Graph:
86
+ """
87
+ Construct an ITS graph by merging nodes and edges from G and H, preserving nodes
88
+ present only in one graph and filling missing-side attributes with defaults.
89
+
90
+ Node-level attributes are always reflected in `typesGH` as ((G_tuple), (H_tuple))
91
+ over `node_attrs`. If `store=True`, the individual attributes are stored as
92
+ (G_value, H_value) tuples under their own keys; if `store=False`, only the G-side
93
+ value is stored under each attribute key.
94
+
95
+ :param G: The first input graph (reactant-like).
96
+ :type G: nx.Graph
97
+ :param H: The second input graph (product-like).
98
+ :type H: nx.Graph
99
+ :param ignore_aromaticity: If True, small differences in bond order (<1)
100
+ are treated as zero.
101
+ :type ignore_aromaticity: bool
102
+ :param balance_its: If True, choose the smaller graph (by node count)
103
+ as the base; otherwise the larger.
104
+ :type balance_its: bool
105
+ :param store: If True, keep per-attribute (G,H) tuples; if False, keep only the G-side value per attribute.
106
+ :type store: bool
107
+ :param node_attrs: Ordered list of node attribute names to include in the node-level `typesGH` tuples.
108
+ Defaults to ["element", "aromatic", "hcount", "charge", "neighbors"].
109
+ :type node_attrs: list[str] or None
110
+ :param edge_attrs: (Legacy) ordered list of edge attribute names; not used for core behavior.
111
+ :type edge_attrs: list[str] or None
112
+ :param attributes_defaults: Optional overrides for default node attribute values.
113
+ :type attributes_defaults: dict[str, Any] or None
114
+ :returns: ITS graph with merged node and edge annotations, including `typesGH`, `order`, and `standard_order`.
115
+ :rtype: nx.Graph
116
+ """
117
+ # typesGH attribute order
118
+ if node_attrs is None:
119
+ node_attrs = ["element", "aromatic", "hcount", "charge", "neighbors"]
120
+ if edge_attrs is None:
121
+ edge_attrs = ["order"]
122
+
123
+ node_defaults = ITSConstruction._resolve_defaults(
124
+ attributes_defaults, ITSConstruction.CORE_NODE_DEFAULTS
125
+ )
126
+
127
+ # Select base graph depending on balancing policy
128
+ if (balance_its and len(G.nodes) <= len(H.nodes)) or (
129
+ not balance_its and len(G.nodes) >= len(H.nodes)
130
+ ):
131
+ base = G
132
+ else:
133
+ base = H
134
+
135
+ ITS = deepcopy(base)
136
+ ITS.remove_edges_from(list(ITS.edges()))
137
+
138
+ # Ensure union of nodes exists
139
+ all_nodes = set(G.nodes()) | set(H.nodes())
140
+ for n in all_nodes:
141
+ if n not in ITS:
142
+ source_attrs = {}
143
+ if n in G:
144
+ source_attrs = deepcopy(G.nodes[n])
145
+ elif n in H:
146
+ source_attrs = deepcopy(H.nodes[n])
147
+ ITS.add_node(n, **source_attrs)
148
+
149
+ # Populate node-level per-attribute tuples and typesGH
150
+ for n in ITS.nodes():
151
+ g_tuple = tuple(
152
+ (
153
+ G.nodes[n].get(attr, node_defaults.get(attr))
154
+ if n in G
155
+ else node_defaults.get(attr)
156
+ )
157
+ for attr in node_attrs
158
+ )
159
+ h_tuple = tuple(
160
+ (
161
+ H.nodes[n].get(attr, node_defaults.get(attr))
162
+ if n in H
163
+ else node_defaults.get(attr)
164
+ )
165
+ for attr in node_attrs
166
+ )
167
+ ITS.nodes[n]["typesGH"] = (g_tuple, h_tuple)
168
+
169
+ for i, attr in enumerate(node_attrs):
170
+ if store:
171
+ ITS.nodes[n][attr] = (g_tuple[i], h_tuple[i])
172
+ else:
173
+ ITS.nodes[n][attr] = g_tuple[i]
174
+
175
+ # Union of edges: build order only (no edge typesGH)
176
+ edge_keys = {frozenset((u, v)) for u, v in G.edges()} | {
177
+ frozenset((u, v)) for u, v in H.edges()
178
+ }
179
+ for fs in edge_keys:
180
+ u, v = tuple(fs)
181
+ order_G = G[u][v].get("order", 0.0) if G.has_edge(u, v) else 0.0
182
+ order_H = H[u][v].get("order", 0.0) if H.has_edge(u, v) else 0.0
183
+ ITS.add_edge(u, v, order=(order_G, order_H))
184
+ # intentionally do NOT add edge-level typesGH per request
185
+
186
+ # Compute derived standard_order
187
+ ITSConstruction._compute_standard_order(
188
+ ITS, ignore_aromaticity=ignore_aromaticity
189
+ )
190
+
191
+ return ITS
192
+
193
+ @staticmethod
194
+ def ITSGraph(
195
+ G: nx.Graph,
196
+ H: nx.Graph,
197
+ ignore_aromaticity: bool = False,
198
+ attributes_defaults: Optional[Dict[str, Any]] = None,
199
+ balance_its: bool = False,
200
+ store: bool = False,
201
+ ) -> nx.Graph:
202
+ """
203
+ Backward-compatible wrapper that replicates the original ITSGraph signature while delegating
204
+ to the improved `construct` implementation.
205
+
206
+ :param G: The first input graph (reactant).
207
+ :type G: nx.Graph
208
+ :param H: The second input graph (product).
209
+ :type H: nx.Graph
210
+ :param ignore_aromaticity: If True, small order differences are treated as zero.
211
+ :type ignore_aromaticity: bool
212
+ :param attributes_defaults: Defaults to use when a node attribute is missing.
213
+ :type attributes_defaults: dict[str, Any] or None
214
+ :param balance_its: If True, base selection is balanced toward the smaller graph.
215
+ :type balance_its: bool
216
+ :param store: If True, keep full per-attribute tuples; if False, keep only G-side values.
217
+ :type store: bool
218
+ :returns: Constructed ITS graph with legacy node attribute ordering.
219
+ :rtype: nx.Graph
220
+ """
221
+ node_attrs = ["element", "aromatic", "hcount", "charge", "neighbors"]
222
+ edge_attrs = ["order"]
223
+ return ITSConstruction.construct(
224
+ G,
225
+ H,
226
+ ignore_aromaticity=ignore_aromaticity,
227
+ balance_its=balance_its,
228
+ store=store,
229
+ node_attrs=node_attrs,
230
+ edge_attrs=edge_attrs,
231
+ attributes_defaults=attributes_defaults,
232
+ )
233
+
234
+ @staticmethod
235
+ def typesGH_info(
236
+ node_attrs: Optional[List[str]] = None, edge_attrs: Optional[List[str]] = None
237
+ ) -> Dict[str, Dict[str, Tuple[type, Any]]]:
238
+ """
239
+ Provide expected types and default values for interpreting `typesGH` tuples.
240
+
241
+ :param node_attrs: List of node attributes used in the node-level typesGH.
242
+ :type node_attrs: list[str] or None
243
+ :param edge_attrs: List of edge attributes used in the edge-level typesGH.
244
+ :type edge_attrs: list[str] or None
245
+ :returns: Nested dict describing (type, default) for each selected attribute.
246
+ :rtype: dict[str, dict[str, tuple[type, Any]]]
247
+ """
248
+ if node_attrs is None:
249
+ node_attrs = ["element", "aromatic", "hcount", "charge", "neighbors"]
250
+ if edge_attrs is None:
251
+ edge_attrs = ["order"]
252
+
253
+ node_prop_types: Dict[str, type] = {
254
+ "element": str,
255
+ "aromatic": bool,
256
+ "hcount": int,
257
+ "charge": int,
258
+ "neighbors": list,
259
+ }
260
+ edge_prop_types: Dict[str, type] = {
261
+ "order": float,
262
+ "ez_isomer": str,
263
+ "bond_type": str,
264
+ "conjugated": bool,
265
+ "in_ring": bool,
266
+ }
267
+
268
+ node_defaults = {
269
+ attr: (
270
+ node_prop_types.get(attr, object),
271
+ (
272
+ ITSConstruction.CORE_NODE_DEFAULTS.get(attr)()
273
+ if callable(ITSConstruction.CORE_NODE_DEFAULTS.get(attr))
274
+ else ITSConstruction.CORE_NODE_DEFAULTS.get(attr)
275
+ ),
276
+ )
277
+ for attr in node_attrs
278
+ }
279
+ edge_defaults = {
280
+ attr: (
281
+ edge_prop_types.get(attr, object),
282
+ ITSConstruction.CORE_EDGE_DEFAULTS.get(attr),
283
+ )
284
+ for attr in edge_attrs
285
+ }
286
+
287
+ return {"node": node_defaults, "edge": edge_defaults}
288
+
289
+ # Legacy helpers kept for compatibility:
290
+ @staticmethod
291
+ def get_node_attribute(
292
+ graph: nx.Graph, node: Hashable, attribute: str, default: Any
293
+ ) -> Any:
294
+ """Retrieve a node attribute or return a default if missing."""
295
+ try:
296
+ return graph.nodes[node][attribute]
297
+ except KeyError:
298
+ return default
299
+
300
+ @staticmethod
301
+ def get_node_attributes_with_defaults(
302
+ graph: nx.Graph, node: Hashable, attributes_defaults: Dict[str, Any] = None
303
+ ) -> Tuple:
304
+ """Retrieve multiple node attributes applying provided simple defaults."""
305
+ if attributes_defaults is None:
306
+ attributes_defaults = {
307
+ "element": "*",
308
+ "aromatic": False,
309
+ "hcount": 0,
310
+ "charge": 0,
311
+ "neighbors": ["", ""],
312
+ }
313
+ return tuple(
314
+ ITSConstruction.get_node_attribute(graph, node, attr, default)
315
+ for attr, default in attributes_defaults.items()
316
+ )
@@ -404,7 +404,7 @@ def its_decompose(its_graph: nx.Graph, nodes_share="typesGH", edges_share="order
404
404
  aromatic=node_attr_g[1],
405
405
  hcount=node_attr_g[2],
406
406
  charge=node_attr_g[3],
407
- neighbors=node_attr_g[4],
407
+ # neighbors=node_attr_g[4],
408
408
  atom_map=node,
409
409
  )
410
410
  if len(node_attr_h) > 0:
@@ -415,7 +415,7 @@ def its_decompose(its_graph: nx.Graph, nodes_share="typesGH", edges_share="order
415
415
  aromatic=node_attr_h[1],
416
416
  hcount=node_attr_h[2],
417
417
  charge=node_attr_h[3],
418
- neighbors=node_attr_h[4],
418
+ # neighbors=node_attr_h[4],
419
419
  atom_map=node,
420
420
  )
421
421