synkit 1.2__tar.gz → 1.2.2__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 (261) hide show
  1. {synkit-1.2 → synkit-1.2.2}/.gitignore +2 -0
  2. {synkit-1.2 → synkit-1.2.2}/PKG-INFO +1 -1
  3. {synkit-1.2 → synkit-1.2.2}/pyproject.toml +1 -1
  4. synkit-1.2.2/synkit/CRN/Construct/abstract.py +681 -0
  5. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Props/helper.py +0 -83
  6. synkit-1.2.2/synkit/CRN/Query/kegg_api.py +102 -0
  7. synkit-1.2.2/synkit/CRN/Query/kegg_extract.py +698 -0
  8. synkit-1.2.2/synkit/CRN/Query/kegg_impute.py +716 -0
  9. synkit-1.2.2/synkit/CRN/Query/kegg_parse.py +394 -0
  10. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Structure/syncrn.py +1 -1
  11. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/automorphism.py +1 -1
  12. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/canon.py +1 -1
  13. synkit-1.2.2/synkit/Utils/__init__.py +0 -0
  14. {synkit-1.2 → synkit-1.2.2}/LICENSE +0 -0
  15. {synkit-1.2 → synkit-1.2.2}/README.md +0 -0
  16. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/DAG/__init__.py +0 -0
  17. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/DAG/crn.py +0 -0
  18. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/DAG/mod_crn.py +0 -0
  19. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/DAG/syncrn.py +0 -0
  20. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/__init__.py +0 -0
  21. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/arity.py +0 -0
  22. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/builder.py +0 -0
  23. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/derivation.py +0 -0
  24. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/flattener.py +0 -0
  25. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/keys.py +0 -0
  26. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/mixtures.py +0 -0
  27. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/smiles.py +0 -0
  28. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/state.py +0 -0
  29. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/strategy.py +0 -0
  30. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Construct/worker.py +0 -0
  31. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Pathway/__init__.py +0 -0
  32. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Pathway/_adapter.py +0 -0
  33. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Pathway/pathfinder.py +0 -0
  34. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Pathway/reachability.py +0 -0
  35. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Pathway/realizability.py +0 -0
  36. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Petrinet/__init__.py +0 -0
  37. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Petrinet/analyzer.py +0 -0
  38. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Petrinet/net.py +0 -0
  39. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Petrinet/persistence.py +0 -0
  40. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Petrinet/semiflows.py +0 -0
  41. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Petrinet/structure.py +0 -0
  42. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Props/__init__.py +0 -0
  43. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Props/dynamics.py +0 -0
  44. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Props/stoich.py +0 -0
  45. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Props/thermo.py +0 -0
  46. {synkit-1.2/synkit/CRN/dev_crn/Symmetry → synkit-1.2.2/synkit/CRN/Query}/__init__.py +0 -0
  47. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Structure/__init__.py +0 -0
  48. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Structure/reaction.py +0 -0
  49. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Structure/rule.py +0 -0
  50. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Structure/species.py +0 -0
  51. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/__init__.py +0 -0
  52. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/_common.py +0 -0
  53. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/_ir.py +0 -0
  54. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/isomorphism.py +0 -0
  55. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/symmetry.py +0 -0
  56. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Symmetry/wl_canon.py +0 -0
  57. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Visualize/__init__.py +0 -0
  58. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Visualize/crn_vis.py +0 -0
  59. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Visualize/labels.py +0 -0
  60. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Visualize/layout.py +0 -0
  61. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Visualize/palette.py +0 -0
  62. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Visualize/validation.py +0 -0
  63. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/Visualize/vis.py +0 -0
  64. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/__init__.py +0 -0
  65. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Structure/__init__.py +0 -0
  66. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Structure/backend.py +0 -0
  67. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Structure/conversion.py +0 -0
  68. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Structure/hyperedge.py +0 -0
  69. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Structure/hypergraph.py +0 -0
  70. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Structure/rxn.py +0 -0
  71. {synkit-1.2/synkit/CRN/dev_crn/configs → synkit-1.2.2/synkit/CRN/dev_crn/Symmetry}/__init__.py +0 -0
  72. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Symmetry/automorphism.py +0 -0
  73. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Symmetry/canon.py +0 -0
  74. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/Symmetry/wl_canon.py +0 -0
  75. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/__init__.py +0 -0
  76. {synkit-1.2/synkit/Chem/Cluster → synkit-1.2.2/synkit/CRN/dev_crn/configs}/__init__.py +0 -0
  77. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/configs/loader.py +0 -0
  78. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/configs/models.py +0 -0
  79. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/constants.py +0 -0
  80. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/crn_formula.py +0 -0
  81. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/deficiency.py +0 -0
  82. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/enumerator.py +0 -0
  83. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/exceptions.py +0 -0
  84. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/explorer.py +0 -0
  85. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/helpers.py +0 -0
  86. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/injectivity.py +0 -0
  87. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/motif.py +0 -0
  88. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/network.py +0 -0
  89. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/pathway.py +0 -0
  90. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/properties.py +0 -0
  91. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/reaction.py +0 -0
  92. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/utils.py +0 -0
  93. {synkit-1.2 → synkit-1.2.2}/synkit/CRN/dev_crn/viz.py +0 -0
  94. {synkit-1.2/synkit/Chem/Fingerprint → synkit-1.2.2/synkit/Chem/Cluster}/__init__.py +0 -0
  95. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Cluster/butina.py +0 -0
  96. {synkit-1.2/synkit/Chem/Molecule → synkit-1.2.2/synkit/Chem/Fingerprint}/__init__.py +0 -0
  97. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
  98. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
  99. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
  100. {synkit-1.2/synkit/Data → synkit-1.2.2/synkit/Chem/Molecule}/__init__.py +0 -0
  101. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Molecule/atom_features.py +0 -0
  102. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Molecule/descriptors.py +0 -0
  103. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Molecule/formula.py +0 -0
  104. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Molecule/graph_annotator.py +0 -0
  105. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Molecule/standardize.py +0 -0
  106. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Molecule/valence.py +0 -0
  107. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/Mapper/__init__.py +0 -0
  108. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/Mapper/wl_mapper.py +0 -0
  109. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/__init__.py +0 -0
  110. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/aam_validator.py +0 -0
  111. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/balance_check.py +0 -0
  112. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
  113. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/cleaning.py +0 -0
  114. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/deionize.py +0 -0
  115. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/fix_aam.py +0 -0
  116. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/neutralize.py +0 -0
  117. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/radical_wildcard.py +0 -0
  118. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/standardize.py +0 -0
  119. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/Reaction/tautomerize.py +0 -0
  120. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/__init__.py +0 -0
  121. {synkit-1.2 → synkit-1.2.2}/synkit/Chem/utils.py +0 -0
  122. {synkit-1.2/synkit/Graph/Context → synkit-1.2.2/synkit/Data}/__init__.py +0 -0
  123. {synkit-1.2 → synkit-1.2.2}/synkit/Data/gen_partial_aam.py +0 -0
  124. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Canon/__init__.py +0 -0
  125. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Canon/canon_algs.py +0 -0
  126. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Canon/canon_graph.py +0 -0
  127. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Canon/nauty.py +0 -0
  128. {synkit-1.2/synkit/Graph/Feature/Fingerprint → synkit-1.2.2/synkit/Graph/Context}/__init__.py +0 -0
  129. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Context/hier_context.py +0 -0
  130. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Context/radius_expand.py +0 -0
  131. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/Descriptors/topology.py +0 -0
  132. {synkit-1.2/synkit/Graph/Hyrogen → synkit-1.2.2/synkit/Graph/Feature/Fingerprint}/__init__.py +0 -0
  133. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +0 -0
  134. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/__init__.py +0 -0
  135. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/graph_descriptors.py +0 -0
  136. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/graph_fps.py +0 -0
  137. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/graph_signature.py +0 -0
  138. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/hash_fps.py +0 -0
  139. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/morgan_fps.py +0 -0
  140. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/path_fps.py +0 -0
  141. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Feature/wl_hash.py +0 -0
  142. {synkit-1.2/synkit/Graph/MTG → synkit-1.2.2/synkit/Graph/Hyrogen}/__init__.py +0 -0
  143. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Hyrogen/_misc.py +0 -0
  144. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
  145. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Hyrogen/hextend.py +0 -0
  146. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/__init__.py +0 -0
  147. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/its_builder.py +0 -0
  148. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/its_construction.py +0 -0
  149. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/its_decompose.py +0 -0
  150. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/its_destruction.py +0 -0
  151. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/its_expand.py +0 -0
  152. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/its_relabel.py +0 -0
  153. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/normalize_aam.py +0 -0
  154. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/ITS/partial_its.py +0 -0
  155. {synkit-1.2/synkit/Graph/Wildcard → synkit-1.2.2/synkit/Graph/MTG}/__init__.py +0 -0
  156. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/MTG/group_comp.py +0 -0
  157. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/MTG/groupoid.py +0 -0
  158. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/MTG/mcs_matcher.py +0 -0
  159. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/MTG/mtg.py +0 -0
  160. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/MTG/mtg_explore.py +0 -0
  161. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/MTG/utils.py +0 -0
  162. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/__init__.py +0 -0
  163. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/approx_mcs.py +0 -0
  164. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/auto_est.py +0 -0
  165. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/automorphism.py +0 -0
  166. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/batch_cluster.py +0 -0
  167. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/dedup_matches.py +0 -0
  168. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/graph_cluster.py +0 -0
  169. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/graph_matcher.py +0 -0
  170. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/graph_morphism.py +0 -0
  171. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
  172. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
  173. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/orbit.py +0 -0
  174. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/partial_matcher.py +0 -0
  175. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/sing.py +0 -0
  176. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/subgraph_matcher.py +0 -0
  177. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/turbo_iso.py +0 -0
  178. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Matcher/wl_sel.py +0 -0
  179. {synkit-1.2/synkit/Rule/Apply → synkit-1.2.2/synkit/Graph/Wildcard}/__init__.py +0 -0
  180. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
  181. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Wildcard/graph_wc.py +0 -0
  182. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Wildcard/its_merge.py +0 -0
  183. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Wildcard/radwc.py +0 -0
  184. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Wildcard/wc_matcher.py +0 -0
  185. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/Wildcard/wildcard.py +0 -0
  186. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/__init__.py +0 -0
  187. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/canon_graph.py +0 -0
  188. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/syn_graph.py +0 -0
  189. {synkit-1.2 → synkit-1.2.2}/synkit/Graph/utils.py +0 -0
  190. {synkit-1.2 → synkit-1.2.2}/synkit/IO/__init__.py +0 -0
  191. {synkit-1.2 → synkit-1.2.2}/synkit/IO/chem_converter.py +0 -0
  192. {synkit-1.2 → synkit-1.2.2}/synkit/IO/combinatorial/__init__.py +0 -0
  193. {synkit-1.2 → synkit-1.2.2}/synkit/IO/combinatorial/gml_to_graph.py +0 -0
  194. {synkit-1.2 → synkit-1.2.2}/synkit/IO/combinatorial/graph_to_gml.py +0 -0
  195. {synkit-1.2 → synkit-1.2.2}/synkit/IO/combinatorial/graph_to_smarts.py +0 -0
  196. {synkit-1.2 → synkit-1.2.2}/synkit/IO/combinatorial/smarts_expander.py +0 -0
  197. {synkit-1.2 → synkit-1.2.2}/synkit/IO/combinatorial/smarts_generalizer.py +0 -0
  198. {synkit-1.2 → synkit-1.2.2}/synkit/IO/combinatorial/smarts_to_graph.py +0 -0
  199. {synkit-1.2 → synkit-1.2.2}/synkit/IO/data_io.py +0 -0
  200. {synkit-1.2 → synkit-1.2.2}/synkit/IO/data_process.py +0 -0
  201. {synkit-1.2 → synkit-1.2.2}/synkit/IO/debug.py +0 -0
  202. {synkit-1.2 → synkit-1.2.2}/synkit/IO/dg_to_gml.py +0 -0
  203. {synkit-1.2 → synkit-1.2.2}/synkit/IO/gml_to_nx.py +0 -0
  204. {synkit-1.2 → synkit-1.2.2}/synkit/IO/graph_to_mol.py +0 -0
  205. {synkit-1.2 → synkit-1.2.2}/synkit/IO/mol_to_graph.py +0 -0
  206. {synkit-1.2 → synkit-1.2.2}/synkit/IO/nx_to_gml.py +0 -0
  207. {synkit-1.2 → synkit-1.2.2}/synkit/IO/smiles_to_id.py +0 -0
  208. {synkit-1.2/synkit/Rule/Compose → synkit-1.2.2/synkit/Rule/Apply}/__init__.py +0 -0
  209. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Apply/reactor_rule.py +0 -0
  210. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Apply/retro_reactor.py +0 -0
  211. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Apply/rule_matcher.py +0 -0
  212. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Apply/rule_rbl.py +0 -0
  213. {synkit-1.2/synkit/Rule/Modify → synkit-1.2.2/synkit/Rule/Compose}/__init__.py +0 -0
  214. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Compose/compose_rule.py +0 -0
  215. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Compose/rule_compose.py +0 -0
  216. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Compose/rule_mapping.py +0 -0
  217. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Compose/seq_comp.py +0 -0
  218. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Compose/valence_constrain.py +0 -0
  219. {synkit-1.2/synkit/Synthesis/MSR → synkit-1.2.2/synkit/Rule/Modify}/__init__.py +0 -0
  220. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Modify/implict_rule.py +0 -0
  221. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Modify/longest_path.py +0 -0
  222. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Modify/molecule_rule.py +0 -0
  223. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Modify/prune_templates.py +0 -0
  224. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Modify/rule_utils.py +0 -0
  225. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/Modify/strip_rule.py +0 -0
  226. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/__init__.py +0 -0
  227. {synkit-1.2 → synkit-1.2.2}/synkit/Rule/syn_rule.py +0 -0
  228. {synkit-1.2/synkit/Synthesis/Metrics → synkit-1.2.2/synkit/Synthesis/MSR}/__init__.py +0 -0
  229. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/MSR/multi_steps.py +0 -0
  230. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/MSR/path_finder.py +0 -0
  231. {synkit-1.2/synkit/Synthesis/Reactor → synkit-1.2.2/synkit/Synthesis/Metrics}/__init__.py +0 -0
  232. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Metrics/_base.py +0 -0
  233. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Metrics/_plot.py +0 -0
  234. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Metrics/_ranking.py +0 -0
  235. {synkit-1.2/synkit/Synthesis → synkit-1.2.2/synkit/Synthesis/Reactor}/__init__.py +0 -0
  236. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/batch_reactor.py +0 -0
  237. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/benchmark.py +0 -0
  238. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/imba_engine.py +0 -0
  239. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
  240. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
  241. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
  242. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/post_syn.py +0 -0
  243. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
  244. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
  245. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
  246. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/strategy.py +0 -0
  247. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/Reactor/syn_reactor.py +0 -0
  248. {synkit-1.2/synkit/Utils → synkit-1.2.2/synkit/Synthesis}/__init__.py +0 -0
  249. {synkit-1.2 → synkit-1.2.2}/synkit/Synthesis/reactor_utils.py +0 -0
  250. {synkit-1.2 → synkit-1.2.2}/synkit/Utils/utils.py +0 -0
  251. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/__init__.py +0 -0
  252. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/chemical_space.py +0 -0
  253. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/crn_vis.py +0 -0
  254. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/embedding.py +0 -0
  255. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/graph_visualizer.py +0 -0
  256. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/pdf_writer.py +0 -0
  257. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/rule_vis.py +0 -0
  258. {synkit-1.2 → synkit-1.2.2}/synkit/Vis/rxn_vis.py +0 -0
  259. {synkit-1.2 → synkit-1.2.2}/synkit/__init__.py +0 -0
  260. {synkit-1.2 → synkit-1.2.2}/synkit/examples.py +0 -0
  261. {synkit-1.2 → synkit-1.2.2}/synkit/version.py +0 -0
@@ -25,3 +25,5 @@ Data/Benchmark/synthesis/*
25
25
  Data/USPTO/*
26
26
  crn/*
27
27
  test_run.py
28
+ Data/Study/CRN/case_formose/*
29
+ Data/Study/CRN/case_glycolysis/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synkit
3
- Version: 1.2
3
+ Version: 1.2.2
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "synkit"
7
- version = "1.2"
7
+ version = "1.2.2"
8
8
  description = "Utility for reaction modeling using graph grammar"
9
9
  readme = "README.md"
10
10
  long-description = { file = "CHANGELOG.md" }
@@ -0,0 +1,681 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass, field
5
+ from pathlib import Path
6
+ from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple, Union
7
+
8
+ PathLike = Union[str, Path]
9
+ ReactionSides = Tuple[List[str], List[str]]
10
+
11
+
12
+ def _split_reaction_smiles(reaction_smiles: str) -> ReactionSides:
13
+ """
14
+ Split a reaction SMILES string into reactant and product molecule lists.
15
+
16
+ The expected format is ``"A.B>>C.D"``. Empty left or right sides are allowed.
17
+
18
+ :param reaction_smiles:
19
+ Reaction SMILES string.
20
+ :type reaction_smiles: str
21
+
22
+ :returns:
23
+ Tuple of reactant and product molecule lists.
24
+ :rtype: Tuple[List[str], List[str]]
25
+
26
+ :raises ValueError:
27
+ If the reaction string does not contain ``">>"``.
28
+
29
+ Example
30
+ -------
31
+ .. code-block:: python
32
+
33
+ reactants, products = _split_reaction_smiles("CCO.O>>CC=O")
34
+ """
35
+ value = reaction_smiles.strip()
36
+ if ">>" not in value:
37
+ raise ValueError(f"Invalid reaction SMILES (missing '>>'): {reaction_smiles}")
38
+
39
+ left, right = value.split(">>", 1)
40
+ reactants = [token.strip() for token in left.split(".") if token.strip()]
41
+ products = [token.strip() for token in right.split(".") if token.strip()]
42
+ return reactants, products
43
+
44
+
45
+ def _excel_label(index: int) -> str:
46
+ """
47
+ Convert a zero-based integer index into an Excel-style alphabetic label.
48
+
49
+ Examples include ``0 -> "A"``, ``25 -> "Z"``, and ``26 -> "AA"``.
50
+
51
+ :param index:
52
+ Zero-based index.
53
+ :type index: int
54
+
55
+ :returns:
56
+ Excel-style alphabetic label.
57
+ :rtype: str
58
+
59
+ :raises ValueError:
60
+ If ``index`` is negative.
61
+
62
+ Example
63
+ -------
64
+ .. code-block:: python
65
+
66
+ label = _excel_label(27) # "AB"
67
+ """
68
+ if index < 0:
69
+ raise ValueError("Index must be non-negative")
70
+
71
+ label = ""
72
+ value = index + 1
73
+ while value > 0:
74
+ value, remainder = divmod(value - 1, 26)
75
+ label = chr(ord("A") + remainder) + label
76
+ return label
77
+
78
+
79
+ def _normalize_abstract_side(side: str) -> List[str]:
80
+ """
81
+ Normalize one abstract reaction side by splitting on ``"+"`` and sorting tokens.
82
+
83
+ :param side:
84
+ Abstract reaction side.
85
+ :type side: str
86
+
87
+ :returns:
88
+ Sorted abstract token list.
89
+ :rtype: List[str]
90
+
91
+ Example
92
+ -------
93
+ .. code-block:: python
94
+
95
+ tokens = _normalize_abstract_side("B+A+C")
96
+ """
97
+ return sorted(token.strip() for token in side.split("+") if token.strip())
98
+
99
+
100
+ def _first_present(
101
+ record: Mapping[str, Any],
102
+ keys: Sequence[str],
103
+ ) -> Optional[Any]:
104
+ """
105
+ Return the first non-``None`` value found in a mapping for the given keys.
106
+
107
+ :param record:
108
+ Input mapping.
109
+ :type record: Mapping[str, Any]
110
+
111
+ :param keys:
112
+ Candidate keys to try in order.
113
+ :type keys: Sequence[str]
114
+
115
+ :returns:
116
+ First matching value, or ``None`` if none are present.
117
+ :rtype: Optional[Any]
118
+
119
+ Example
120
+ -------
121
+ .. code-block:: python
122
+
123
+ value = _first_present(record, ["smiles", "reaction", "rxn_smiles"])
124
+ """
125
+ for key in keys:
126
+ if key in record and record[key] is not None:
127
+ return record[key]
128
+ return None
129
+
130
+
131
+ def deduplicate_abstract_reactions(reactions: Sequence[str]) -> List[str]:
132
+ """
133
+ Remove identity reactions and duplicate abstract reactions.
134
+
135
+ Reactant and product order are normalized internally before comparison.
136
+ The original retained representative is the first encountered entry.
137
+
138
+ :param reactions:
139
+ Abstract reactions such as ``"A+B>>C+D"``.
140
+ :type reactions: Sequence[str]
141
+
142
+ :returns:
143
+ Filtered abstract reactions.
144
+ :rtype: List[str]
145
+
146
+ Example
147
+ -------
148
+ .. code-block:: python
149
+
150
+ filtered = deduplicate_abstract_reactions(
151
+ ["A+B>>C", "B+A>>C", "A>>A"]
152
+ )
153
+ """
154
+ seen: set[str] = set()
155
+ filtered: List[str] = []
156
+
157
+ for reaction in reactions:
158
+ if ">>" not in reaction:
159
+ continue
160
+
161
+ left, right = reaction.split(">>", 1)
162
+ reactants = _normalize_abstract_side(left)
163
+ products = _normalize_abstract_side(right)
164
+
165
+ if reactants == products:
166
+ continue
167
+
168
+ normalized = ">>".join(["+".join(reactants), "+".join(products)])
169
+ if normalized in seen:
170
+ continue
171
+
172
+ seen.add(normalized)
173
+ filtered.append(reaction)
174
+
175
+ return filtered
176
+
177
+
178
+ @dataclass(frozen=True)
179
+ class AbstractReactionNetwork:
180
+ """
181
+ Symbolic abstraction of a reaction network.
182
+
183
+ :param molecule_pool:
184
+ Unique molecule pool in the original full representation.
185
+ :type molecule_pool: List[str]
186
+
187
+ :param reactions:
188
+ Abstract symbolic reactions such as ``"A+B>>C+D"``.
189
+ :type reactions: List[str]
190
+
191
+ :param templates:
192
+ Optional mapping from reaction identifiers to rule or template strings.
193
+ :type templates: Dict[str, str]
194
+
195
+ :param label_to_molecule:
196
+ Mapping from abstract labels back to original molecule strings.
197
+ :type label_to_molecule: Dict[str, str]
198
+ """
199
+
200
+ molecule_pool: List[str]
201
+ reactions: List[str]
202
+ templates: Dict[str, str] = field(default_factory=dict)
203
+ label_to_molecule: Dict[str, str] = field(default_factory=dict)
204
+
205
+ def to_dict(self) -> Dict[str, Any]:
206
+ """
207
+ Convert the abstract network to a plain dictionary.
208
+
209
+ :returns:
210
+ Dictionary representation of the abstract network.
211
+ :rtype: Dict[str, Any]
212
+
213
+ Example
214
+ -------
215
+ .. code-block:: python
216
+
217
+ payload = network.to_dict()
218
+ """
219
+ return {
220
+ "molecule_pool": list(self.molecule_pool),
221
+ "reactions": list(self.reactions),
222
+ "templates": dict(self.templates),
223
+ "label_to_molecule": dict(self.label_to_molecule),
224
+ }
225
+
226
+ def to_json_payload(
227
+ self, name: str = "abstract_reaction_network"
228
+ ) -> Dict[str, Any]:
229
+ """
230
+ Convert the abstract network into a SynKit-style JSON payload.
231
+
232
+ :param name:
233
+ Name stored in the metadata block.
234
+ :type name: str
235
+
236
+ :returns:
237
+ JSON-serializable payload.
238
+ :rtype: Dict[str, Any]
239
+
240
+ Example
241
+ -------
242
+ .. code-block:: python
243
+
244
+ payload = network.to_json_payload(name="glycolysis_abstract")
245
+ """
246
+ return {
247
+ "meta": {"name": name, "version": 1},
248
+ "examples": [
249
+ {
250
+ "molecule_pool": list(self.molecule_pool),
251
+ "reactions": list(self.reactions),
252
+ "templates": dict(self.templates),
253
+ "label_to_molecule": dict(self.label_to_molecule),
254
+ }
255
+ ],
256
+ }
257
+
258
+ def save_json(self, path: PathLike, *, name: Optional[str] = None) -> None:
259
+ """
260
+ Save the abstract network as a JSON file.
261
+
262
+ :param path:
263
+ Output JSON path.
264
+ :type path: PathLike
265
+
266
+ :param name:
267
+ Optional metadata name. If omitted, the filename stem is used.
268
+ :type name: Optional[str]
269
+
270
+ :returns:
271
+ ``None``
272
+ :rtype: None
273
+
274
+ Example
275
+ -------
276
+ .. code-block:: python
277
+
278
+ network.save_json("abstract_network.json")
279
+ """
280
+ output_path = Path(path)
281
+ payload = self.to_json_payload(name=name or output_path.stem)
282
+ output_path.parent.mkdir(parents=True, exist_ok=True)
283
+ with output_path.open("w", encoding="utf-8") as handle:
284
+ json.dump(payload, handle, indent=4)
285
+
286
+
287
+ @dataclass
288
+ class AbstractReactionExtractor:
289
+ """
290
+ Build abstract symbolic reaction networks from reaction SMILES lists or
291
+ SynKit-style module/pathway JSON blocks.
292
+
293
+ This class supports configurable field names when extracting reactions from
294
+ JSON-like input records.
295
+
296
+ Example
297
+ -------
298
+ .. code-block:: python
299
+
300
+ extractor = KEGGExtractor()
301
+ data = extractor.build_module_json(
302
+ "M00001",
303
+ with_compounds=True,
304
+ with_atom_maps=False,
305
+ )
306
+
307
+ abstractor = AbstractReactionExtractor()
308
+ network = abstractor.build(
309
+ data=data,
310
+ deduplicate=True,
311
+ order="appearance",
312
+ )
313
+ """
314
+
315
+ def iter_reaction_records(
316
+ self,
317
+ data: Mapping[str, Any],
318
+ ) -> Iterable[Tuple[Mapping[str, Any], str]]:
319
+ """
320
+ Iterate over reaction records from a module-like or pathway-like JSON block.
321
+
322
+ Supported structures include:
323
+
324
+ - module-like: ``{"reactions": [...]}`
325
+ - pathway-like: ``{"by_module": {"M00001": {"reactions": [...]}, ...}}``
326
+
327
+ :param data:
328
+ Input JSON-like mapping.
329
+ :type data: Mapping[str, Any]
330
+
331
+ :yields:
332
+ Tuples of ``(reaction_record, module_id)``.
333
+ :rtype: Iterable[Tuple[Mapping[str, Any], str]]
334
+
335
+ Example
336
+ -------
337
+ .. code-block:: python
338
+
339
+ abstractor = AbstractReactionExtractor()
340
+ for record, module_id in abstractor.iter_reaction_records(data):
341
+ print(module_id, record.get("id"))
342
+ """
343
+ by_module = data.get("by_module")
344
+ if isinstance(by_module, Mapping):
345
+ for module_id, block in by_module.items():
346
+ if not isinstance(block, Mapping):
347
+ continue
348
+ for reaction in block.get("reactions", []) or []:
349
+ if isinstance(reaction, Mapping):
350
+ yield reaction, str(module_id)
351
+ return
352
+
353
+ for reaction in data.get("reactions", []) or []:
354
+ if isinstance(reaction, Mapping):
355
+ yield reaction, ""
356
+
357
+ def extract_reactions_and_templates(
358
+ self,
359
+ reactions: Optional[Sequence[str]] = None,
360
+ *,
361
+ data: Optional[Mapping[str, Any]] = None,
362
+ templates: Optional[Mapping[str, str]] = None,
363
+ drop_missing_smiles_reactions: bool = True,
364
+ prefix_module_in_reaction_id: bool = True,
365
+ reaction_id_keys: Optional[Sequence[str]] = None,
366
+ reaction_smiles_keys: Optional[Sequence[str]] = None,
367
+ template_keys: Optional[Sequence[str]] = None,
368
+ ) -> Tuple[List[str], Dict[str, str]]:
369
+ """
370
+ Extract reaction SMILES and rule-template mappings from raw inputs.
371
+
372
+ Either a direct list of reaction SMILES or a JSON data block may be
373
+ provided. If both are given, the explicit ``reactions`` list takes
374
+ precedence for reaction extraction, while ``templates`` is still merged.
375
+
376
+ When ``data`` is used, the user may customize which keys are searched
377
+ for reaction identifiers, reaction SMILES strings, and templates.
378
+
379
+ :param reactions:
380
+ Direct reaction SMILES list.
381
+ :type reactions: Optional[Sequence[str]]
382
+
383
+ :param data:
384
+ Module-like or pathway-like JSON block.
385
+ :type data: Optional[Mapping[str, Any]]
386
+
387
+ :param templates:
388
+ Optional external mapping from reaction identifiers to templates.
389
+ :type templates: Optional[Mapping[str, str]]
390
+
391
+ :param drop_missing_smiles_reactions:
392
+ Whether to skip records that do not contain a reaction SMILES string.
393
+ :type drop_missing_smiles_reactions: bool
394
+
395
+ :param prefix_module_in_reaction_id:
396
+ Whether to prefix reaction identifiers with module IDs in pathway-style
397
+ inputs.
398
+ :type prefix_module_in_reaction_id: bool
399
+
400
+ :param reaction_id_keys:
401
+ Candidate keys used to find reaction identifiers in each reaction record.
402
+ The keys are tried in order.
403
+ :type reaction_id_keys: Optional[Sequence[str]]
404
+
405
+ :param reaction_smiles_keys:
406
+ Candidate keys used to find reaction SMILES strings in each reaction
407
+ record. The keys are tried in order.
408
+ :type reaction_smiles_keys: Optional[Sequence[str]]
409
+
410
+ :param template_keys:
411
+ Candidate keys used to find rule or template strings in each reaction
412
+ record. The keys are tried in order.
413
+ :type template_keys: Optional[Sequence[str]]
414
+
415
+ :returns:
416
+ Tuple of reaction SMILES list and template mapping.
417
+ :rtype: Tuple[List[str], Dict[str, str]]
418
+
419
+ Example
420
+ -------
421
+ .. code-block:: python
422
+
423
+ extractor = KEGGExtractor()
424
+ data = extractor.build_module_json(
425
+ "M00001",
426
+ with_compounds=True,
427
+ with_atom_maps=False,
428
+ )
429
+
430
+ abstractor = AbstractReactionExtractor()
431
+ reactions, templates = abstractor.extract_reactions_and_templates(
432
+ data=data,
433
+ reaction_id_keys=["id", "kegg_id", "rid"],
434
+ reaction_smiles_keys=["smiles", "reaction", "rxn_smiles"],
435
+ template_keys=["rule", "template", "smirks"],
436
+ )
437
+ """
438
+ reaction_id_keys = list(reaction_id_keys or ["id", "kegg_id"])
439
+ reaction_smiles_keys = list(
440
+ reaction_smiles_keys or ["smiles", "reaction", "rxn_smiles"]
441
+ )
442
+ template_keys = list(template_keys or ["rule", "template", "smirks"])
443
+
444
+ reaction_list = list(reactions or [])
445
+ template_pool: Dict[str, str] = dict(templates or {})
446
+
447
+ if reaction_list:
448
+ return reaction_list, template_pool
449
+
450
+ if not data:
451
+ return [], template_pool
452
+
453
+ extracted_reactions: List[str] = []
454
+ extracted_templates: Dict[str, str] = {}
455
+
456
+ for reaction_record, module_id in self.iter_reaction_records(data):
457
+ reaction_id = _first_present(reaction_record, reaction_id_keys)
458
+ reaction_smiles = _first_present(reaction_record, reaction_smiles_keys)
459
+ template = _first_present(reaction_record, template_keys)
460
+
461
+ if not reaction_smiles:
462
+ if drop_missing_smiles_reactions:
463
+ continue
464
+ reaction_smiles = ""
465
+
466
+ extracted_reactions.append(str(reaction_smiles))
467
+
468
+ if reaction_id is not None and template is not None:
469
+ key = str(reaction_id)
470
+ if module_id and prefix_module_in_reaction_id:
471
+ key = f"{module_id}:{key}"
472
+ extracted_templates[key] = str(template)
473
+
474
+ extracted_templates.update(template_pool)
475
+ return extracted_reactions, extracted_templates
476
+
477
+ def build_molecule_pool(
478
+ self,
479
+ parsed_reactions: Sequence[ReactionSides],
480
+ *,
481
+ order: str = "appearance",
482
+ ) -> List[str]:
483
+ """
484
+ Build the unique molecule pool from parsed reactions.
485
+
486
+ :param parsed_reactions:
487
+ Parsed reaction sides as ``(reactants, products)`` tuples.
488
+ :type parsed_reactions: Sequence[Tuple[List[str], List[str]]]
489
+
490
+ :param order:
491
+ Molecule ordering mode. Supported values are ``"appearance"`` and
492
+ ``"sorted"``.
493
+ :type order: str
494
+
495
+ :returns:
496
+ Ordered unique molecule pool.
497
+ :rtype: List[str]
498
+
499
+ :raises ValueError:
500
+ If ``order`` is not supported.
501
+
502
+ Example
503
+ -------
504
+ .. code-block:: python
505
+
506
+ molecule_pool = abstractor.build_molecule_pool(
507
+ parsed_reactions,
508
+ order="appearance",
509
+ )
510
+ """
511
+ if order == "appearance":
512
+ seen: set[str] = set()
513
+ molecule_pool: List[str] = []
514
+ for reactants, products in parsed_reactions:
515
+ for molecule in reactants + products:
516
+ if molecule not in seen:
517
+ seen.add(molecule)
518
+ molecule_pool.append(molecule)
519
+ return molecule_pool
520
+
521
+ if order == "sorted":
522
+ unique_molecules: set[str] = set()
523
+ for reactants, products in parsed_reactions:
524
+ unique_molecules.update(reactants)
525
+ unique_molecules.update(products)
526
+ return sorted(unique_molecules)
527
+
528
+ raise ValueError("order must be 'appearance' or 'sorted'")
529
+
530
+ def build(
531
+ self,
532
+ reactions: Optional[Sequence[str]] = None,
533
+ *,
534
+ data: Optional[Mapping[str, Any]] = None,
535
+ drop_missing_smiles_reactions: bool = True,
536
+ deduplicate: bool = False,
537
+ templates: Optional[Mapping[str, str]] = None,
538
+ order: str = "appearance",
539
+ reactant_join: str = "+",
540
+ product_join: str = "+",
541
+ prefix_module_in_reaction_id: bool = True,
542
+ reaction_id_keys: Optional[Sequence[str]] = None,
543
+ reaction_smiles_keys: Optional[Sequence[str]] = None,
544
+ template_keys: Optional[Sequence[str]] = None,
545
+ save_as: Optional[PathLike] = None,
546
+ ) -> AbstractReactionNetwork:
547
+ """
548
+ Convert full reaction SMILES into an abstract symbolic reaction network.
549
+
550
+ You may provide either a direct list of reaction SMILES or a module/pathway
551
+ JSON block.
552
+
553
+ If ``data`` is provided, field names for reaction identifiers, reaction
554
+ SMILES strings, and templates may be customized.
555
+
556
+ :param reactions:
557
+ Direct reaction SMILES list.
558
+ :type reactions: Optional[Sequence[str]]
559
+
560
+ :param data:
561
+ Module-like or pathway-like reaction JSON block.
562
+ :type data: Optional[Mapping[str, Any]]
563
+
564
+ :param drop_missing_smiles_reactions:
565
+ Whether to skip records missing reaction SMILES.
566
+ :type drop_missing_smiles_reactions: bool
567
+
568
+ :param deduplicate:
569
+ Whether to remove identity and duplicate abstract reactions.
570
+ :type deduplicate: bool
571
+
572
+ :param templates:
573
+ Optional external mapping from reaction identifiers to templates.
574
+ :type templates: Optional[Mapping[str, str]]
575
+
576
+ :param order:
577
+ Molecule ordering strategy, one of ``"appearance"`` or ``"sorted"``.
578
+ :type order: str
579
+
580
+ :param reactant_join:
581
+ Join token for abstract reactants.
582
+ :type reactant_join: str
583
+
584
+ :param product_join:
585
+ Join token for abstract products.
586
+ :type product_join: str
587
+
588
+ :param prefix_module_in_reaction_id:
589
+ Whether to prefix pathway reaction IDs with module IDs.
590
+ :type prefix_module_in_reaction_id: bool
591
+
592
+ :param reaction_id_keys:
593
+ Candidate keys used to find reaction identifiers in each reaction record.
594
+ :type reaction_id_keys: Optional[Sequence[str]]
595
+
596
+ :param reaction_smiles_keys:
597
+ Candidate keys used to find reaction SMILES strings in each reaction
598
+ record.
599
+ :type reaction_smiles_keys: Optional[Sequence[str]]
600
+
601
+ :param template_keys:
602
+ Candidate keys used to find rule or template strings in each reaction
603
+ record.
604
+ :type template_keys: Optional[Sequence[str]]
605
+
606
+ :param save_as:
607
+ Optional JSON output path.
608
+ :type save_as: Optional[PathLike]
609
+
610
+ :returns:
611
+ Abstract symbolic reaction network.
612
+ :rtype: AbstractReactionNetwork
613
+
614
+ Example
615
+ -------
616
+ .. code-block:: python
617
+
618
+ extractor = KEGGExtractor()
619
+ data = extractor.build_module_json(
620
+ "M00001",
621
+ with_compounds=True,
622
+ with_atom_maps=False,
623
+ )
624
+
625
+ abstractor = AbstractReactionExtractor()
626
+ network = abstractor.build(
627
+ data=data,
628
+ deduplicate=True,
629
+ order="appearance",
630
+ reaction_id_keys=["id", "kegg_id", "rid"],
631
+ reaction_smiles_keys=["smiles", "reaction", "rxn_smiles"],
632
+ template_keys=["rule", "template", "smirks"],
633
+ save_as="M00001_abstract.json",
634
+ )
635
+ """
636
+ full_reactions, template_pool = self.extract_reactions_and_templates(
637
+ reactions=reactions,
638
+ data=data,
639
+ templates=templates,
640
+ drop_missing_smiles_reactions=drop_missing_smiles_reactions,
641
+ prefix_module_in_reaction_id=prefix_module_in_reaction_id,
642
+ reaction_id_keys=reaction_id_keys,
643
+ reaction_smiles_keys=reaction_smiles_keys,
644
+ template_keys=template_keys,
645
+ )
646
+
647
+ parsed_reactions: List[ReactionSides] = [
648
+ _split_reaction_smiles(reaction_smiles)
649
+ for reaction_smiles in full_reactions
650
+ ]
651
+
652
+ molecule_pool = self.build_molecule_pool(parsed_reactions, order=order)
653
+
654
+ molecule_to_label = {
655
+ molecule: _excel_label(index)
656
+ for index, molecule in enumerate(molecule_pool)
657
+ }
658
+ label_to_molecule = {
659
+ label: molecule for molecule, label in molecule_to_label.items()
660
+ }
661
+
662
+ abstracted_reactions: List[str] = []
663
+ for reactants, products in parsed_reactions:
664
+ left = reactant_join.join(molecule_to_label[mol] for mol in reactants)
665
+ right = product_join.join(molecule_to_label[mol] for mol in products)
666
+ abstracted_reactions.append(f"{left}>>{right}")
667
+
668
+ if deduplicate:
669
+ abstracted_reactions = deduplicate_abstract_reactions(abstracted_reactions)
670
+
671
+ network = AbstractReactionNetwork(
672
+ molecule_pool=molecule_pool,
673
+ reactions=abstracted_reactions,
674
+ templates=dict(template_pool),
675
+ label_to_molecule=label_to_molecule,
676
+ )
677
+
678
+ if save_as is not None:
679
+ network.save_json(save_as)
680
+
681
+ return network