synkit 1.2__tar.gz → 1.3__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.
- {synkit-1.2 → synkit-1.3}/.gitignore +3 -0
- {synkit-1.2 → synkit-1.3}/PKG-INFO +1 -1
- {synkit-1.2 → synkit-1.3}/pyproject.toml +1 -1
- synkit-1.3/synkit/CRN/Construct/abstract.py +681 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Props/helper.py +0 -83
- synkit-1.3/synkit/CRN/Query/kegg_api.py +102 -0
- synkit-1.3/synkit/CRN/Query/kegg_extract.py +698 -0
- synkit-1.3/synkit/CRN/Query/kegg_impute.py +716 -0
- synkit-1.3/synkit/CRN/Query/kegg_parse.py +394 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Structure/syncrn.py +1 -1
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/automorphism.py +1 -1
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/canon.py +1 -1
- synkit-1.3/synkit/Graph/ITS/its_construction.py +660 -0
- synkit-1.3/synkit/Graph/ITS/its_reverter.py +330 -0
- synkit-1.3/synkit/Graph/ITS/rc_extractor.py +615 -0
- synkit-1.3/synkit/IO/chem_converter.py +892 -0
- synkit-1.3/synkit/IO/mol_to_graph.py +1117 -0
- synkit-1.3/synkit/Utils/__init__.py +0 -0
- synkit-1.2/synkit/Graph/ITS/its_construction.py +0 -316
- synkit-1.2/synkit/IO/chem_converter.py +0 -621
- synkit-1.2/synkit/IO/mol_to_graph.py +0 -480
- {synkit-1.2 → synkit-1.3}/LICENSE +0 -0
- {synkit-1.2 → synkit-1.3}/README.md +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/DAG/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/DAG/crn.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/DAG/mod_crn.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/DAG/syncrn.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/arity.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/builder.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/derivation.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/flattener.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/keys.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/mixtures.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/smiles.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/state.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/strategy.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Construct/worker.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Pathway/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Pathway/_adapter.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Pathway/pathfinder.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Pathway/reachability.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Pathway/realizability.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Petrinet/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Petrinet/analyzer.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Petrinet/net.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Petrinet/persistence.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Petrinet/semiflows.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Petrinet/structure.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Props/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Props/dynamics.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Props/stoich.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Props/thermo.py +0 -0
- {synkit-1.2/synkit/CRN/dev_crn/Symmetry → synkit-1.3/synkit/CRN/Query}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Structure/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Structure/reaction.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Structure/rule.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Structure/species.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/_common.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/_ir.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/isomorphism.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/symmetry.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Symmetry/wl_canon.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Visualize/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Visualize/crn_vis.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Visualize/labels.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Visualize/layout.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Visualize/palette.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Visualize/validation.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/Visualize/vis.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Structure/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Structure/backend.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Structure/conversion.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Structure/hyperedge.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Structure/hypergraph.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Structure/rxn.py +0 -0
- {synkit-1.2/synkit/CRN/dev_crn/configs → synkit-1.3/synkit/CRN/dev_crn/Symmetry}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Symmetry/automorphism.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Symmetry/canon.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/Symmetry/wl_canon.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/__init__.py +0 -0
- {synkit-1.2/synkit/Chem/Cluster → synkit-1.3/synkit/CRN/dev_crn/configs}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/configs/loader.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/configs/models.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/constants.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/crn_formula.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/deficiency.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/enumerator.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/exceptions.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/explorer.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/helpers.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/injectivity.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/motif.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/network.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/pathway.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/properties.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/reaction.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/utils.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/CRN/dev_crn/viz.py +0 -0
- {synkit-1.2/synkit/Chem/Fingerprint → synkit-1.3/synkit/Chem/Cluster}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Cluster/butina.py +0 -0
- {synkit-1.2/synkit/Chem/Molecule → synkit-1.3/synkit/Chem/Fingerprint}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
- {synkit-1.2/synkit/Data → synkit-1.3/synkit/Chem/Molecule}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Molecule/atom_features.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Molecule/descriptors.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Molecule/formula.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Molecule/graph_annotator.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Molecule/standardize.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Molecule/valence.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/Mapper/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/Mapper/wl_mapper.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/aam_validator.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/balance_check.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/cleaning.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/deionize.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/fix_aam.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/neutralize.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/radical_wildcard.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/standardize.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/Reaction/tautomerize.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Chem/utils.py +0 -0
- {synkit-1.2/synkit/Graph/Context → synkit-1.3/synkit/Data}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Data/gen_partial_aam.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Canon/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Canon/canon_algs.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Canon/canon_graph.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Canon/nauty.py +0 -0
- {synkit-1.2/synkit/Graph/Feature/Fingerprint → synkit-1.3/synkit/Graph/Context}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Context/hier_context.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Context/radius_expand.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/Descriptors/topology.py +0 -0
- {synkit-1.2/synkit/Graph/Hyrogen → synkit-1.3/synkit/Graph/Feature/Fingerprint}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/graph_descriptors.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/graph_fps.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/graph_signature.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/hash_fps.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/morgan_fps.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/path_fps.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Feature/wl_hash.py +0 -0
- {synkit-1.2/synkit/Graph/MTG → synkit-1.3/synkit/Graph/Hyrogen}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Hyrogen/_misc.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Hyrogen/hextend.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/its_builder.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/its_decompose.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/its_destruction.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/its_expand.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/its_relabel.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/normalize_aam.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/ITS/partial_its.py +0 -0
- {synkit-1.2/synkit/Graph/Wildcard → synkit-1.3/synkit/Graph/MTG}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/MTG/group_comp.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/MTG/groupoid.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/MTG/mcs_matcher.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/MTG/mtg.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/MTG/mtg_explore.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/MTG/utils.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/approx_mcs.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/auto_est.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/automorphism.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/batch_cluster.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/dedup_matches.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/graph_cluster.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/graph_matcher.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/graph_morphism.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/orbit.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/partial_matcher.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/sing.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/subgraph_matcher.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/turbo_iso.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Matcher/wl_sel.py +0 -0
- {synkit-1.2/synkit/Rule/Apply → synkit-1.3/synkit/Graph/Wildcard}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Wildcard/graph_wc.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Wildcard/its_merge.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Wildcard/radwc.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Wildcard/wc_matcher.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/Wildcard/wildcard.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/canon_graph.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/syn_graph.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Graph/utils.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/combinatorial/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/combinatorial/gml_to_graph.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/combinatorial/graph_to_gml.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/combinatorial/graph_to_smarts.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/combinatorial/smarts_expander.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/combinatorial/smarts_generalizer.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/combinatorial/smarts_to_graph.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/data_io.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/data_process.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/debug.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/dg_to_gml.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/gml_to_nx.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/graph_to_mol.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/nx_to_gml.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/IO/smiles_to_id.py +0 -0
- {synkit-1.2/synkit/Rule/Compose → synkit-1.3/synkit/Rule/Apply}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Apply/reactor_rule.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Apply/retro_reactor.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Apply/rule_matcher.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Apply/rule_rbl.py +0 -0
- {synkit-1.2/synkit/Rule/Modify → synkit-1.3/synkit/Rule/Compose}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Compose/compose_rule.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Compose/rule_compose.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Compose/rule_mapping.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Compose/seq_comp.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Compose/valence_constrain.py +0 -0
- {synkit-1.2/synkit/Synthesis/MSR → synkit-1.3/synkit/Rule/Modify}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Modify/implict_rule.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Modify/longest_path.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Modify/molecule_rule.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Modify/prune_templates.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Modify/rule_utils.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/Modify/strip_rule.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Rule/syn_rule.py +0 -0
- {synkit-1.2/synkit/Synthesis/Metrics → synkit-1.3/synkit/Synthesis/MSR}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/MSR/multi_steps.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/MSR/path_finder.py +0 -0
- {synkit-1.2/synkit/Synthesis/Reactor → synkit-1.3/synkit/Synthesis/Metrics}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Metrics/_base.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Metrics/_plot.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Metrics/_ranking.py +0 -0
- {synkit-1.2/synkit/Synthesis → synkit-1.3/synkit/Synthesis/Reactor}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/batch_reactor.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/benchmark.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/imba_engine.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/post_syn.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/strategy.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/Reactor/syn_reactor.py +0 -0
- {synkit-1.2/synkit/Utils → synkit-1.3/synkit/Synthesis}/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Synthesis/reactor_utils.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Utils/utils.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/chemical_space.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/crn_vis.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/embedding.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/graph_visualizer.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/pdf_writer.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/rule_vis.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/Vis/rxn_vis.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/__init__.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/examples.py +0 -0
- {synkit-1.2 → synkit-1.3}/synkit/version.py +0 -0
|
@@ -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
|