synkit 0.0.13__tar.gz → 0.0.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synkit-0.0.13 → synkit-0.0.14}/.github/workflows/test-and-lint.yml +1 -1
- {synkit-0.0.13 → synkit-0.0.14}/.gitignore +2 -0
- {synkit-0.0.13 → synkit-0.0.14}/PKG-INFO +12 -1
- {synkit-0.0.13 → synkit-0.0.14}/README.md +11 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/MTG/test_mtg.py +7 -9
- synkit-0.0.14/Test/Graph/Wildcard/test_radwc.py +55 -0
- synkit-0.0.14/Test/Graph/Wildcard/test_wildcard.py +81 -0
- synkit-0.0.14/Test/IO/combinatorial/test_smarts_expander.py +38 -0
- synkit-0.0.14/Test/IO/combinatorial/test_smarts_generalizer.py +62 -0
- synkit-0.0.14/Test/IO/combinatorial/test_smarts_to_graph.py +85 -0
- synkit-0.0.14/Test/Synthesis/Reactor/test_imba_engine.py +94 -0
- synkit-0.0.14/doc/figures/mtg_mechanism.png +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/graph.rst +63 -31
- {synkit-0.0.13 → synkit-0.0.14}/lint.sh +3 -1
- {synkit-0.0.13 → synkit-0.0.14}/pyproject.toml +1 -1
- {synkit-0.0.13 → synkit-0.0.14}/recipe/meta.yaml +1 -1
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/radical_wildcard.py +57 -0
- synkit-0.0.14/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +231 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/its_decompose.py +281 -222
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/MTG/mcs_matcher.py +67 -25
- synkit-0.0.14/synkit/Graph/MTG/mtg.py +886 -0
- synkit-0.0.14/synkit/Graph/MTG/mtg_explore.py +74 -0
- synkit-0.0.14/synkit/Graph/MTG/utils.py +425 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/subgraph_matcher.py +327 -0
- synkit-0.0.14/synkit/Graph/Wildcard/radwc.py +117 -0
- synkit-0.0.14/synkit/Graph/Wildcard/wildcard.py +230 -0
- synkit-0.0.14/synkit/IO/combinatorial/__init__.py +8 -0
- synkit-0.0.14/synkit/IO/combinatorial/gml_to_graph.py +254 -0
- synkit-0.0.14/synkit/IO/combinatorial/graph_to_gml.py +291 -0
- synkit-0.0.14/synkit/IO/combinatorial/graph_to_smarts.py +189 -0
- synkit-0.0.14/synkit/IO/combinatorial/smarts_expander.py +152 -0
- synkit-0.0.14/synkit/IO/combinatorial/smarts_generalizer.py +134 -0
- synkit-0.0.14/synkit/IO/combinatorial/smarts_to_graph.py +183 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Apply/rule_matcher.py +15 -5
- synkit-0.0.14/synkit/Synthesis/Reactor/imba_engine.py +165 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/syn_reactor.py +2 -2
- synkit-0.0.14/synkit/Synthesis/__init__.py +0 -0
- synkit-0.0.14/synkit/Utils/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Vis/graph_visualizer.py +9 -1
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Vis/rule_vis.py +2 -2
- synkit-0.0.14/synkit/__init__.py +0 -0
- synkit-0.0.14/synkit/examples.py +50 -0
- synkit-0.0.13/Test/Synthesis/Reactor/test_core_engine.py +0 -112
- synkit-0.0.13/synkit/Graph/MTG/mtg.py +0 -208
- {synkit-0.0.13 → synkit-0.0.14}/.github/dependabot.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/.github/workflows/build-doc.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/.github/workflows/conda-forge-publish.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/.github/workflows/docker-publish.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/.github/workflows/publish-package.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/.github/workflows/verify-pypi-install.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/.readthedocs.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Data/Figure/synkit.png +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Dockerfile +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/LICENSE +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Fingerprint/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Molecule/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Molecule/test_standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_aam_validator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_balance_checker.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_canon_rsmi.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_cleanning.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_deionize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_fix_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_neutralize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_radical_wildcard.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/Reaction/test_tautomerize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Chem/test_utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Context/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Context/test_hier_context.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Context/test_radius_expand.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Feature/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Feature/test_graph_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Feature/test_graph_signature.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Feature/test_hash_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Feature/test_morgan_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Feature/test_path_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Hydrogen/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Hydrogen/test_graph_hydrogen.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Hydrogen/test_hcomplete.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Hydrogen/test_misc.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/ITS/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/ITS/test_its_construction.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/ITS/test_its_expand.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/ITS/test_its_relabel.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/ITS/test_normalize_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/MTG/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/MTG/test_group_comp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/MTG/test_groupoid.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Matcher/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Matcher/test_batch_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Matcher/test_graph_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Matcher/test_graph_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Matcher/test_graph_morphism.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/Matcher/test_subgraph_matcher.py +0 -0
- {synkit-0.0.13/Test/Graph → synkit-0.0.14/Test/Graph/Wildcard}/__init__.py +0 -0
- {synkit-0.0.13/Test/IO → synkit-0.0.14/Test/Graph}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/test_canon_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Graph/test_syn_graph.py +0 -0
- {synkit-0.0.13/Test/Rule/Apply → synkit-0.0.14/Test/IO}/__init__.py +0 -0
- {synkit-0.0.13/Test/Rule/Compose → synkit-0.0.14/Test/IO/combinatorial}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/IO/test_chemical_converter.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/IO/test_dg_to_gml.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/IO/test_gml_to_nx.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/IO/test_graph_to_mol.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/IO/test_mol_to_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/IO/test_nx_to_gml.py +0 -0
- {synkit-0.0.13/Test/Rule/Modify → synkit-0.0.14/Test/Rule/Apply}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Apply/test_reactor_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Apply/test_retro_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Apply/test_rule_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Apply/test_rule_rbl.py +0 -0
- {synkit-0.0.13/Test/Rule → synkit-0.0.14/Test/Rule/Compose}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Compose/test_rule_compose.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Compose/test_valance_constrain.py +0 -0
- {synkit-0.0.13/Test/Synthesis/CRN → synkit-0.0.14/Test/Rule/Modify}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Modify/test_molecule_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/Modify/test_rule_utils.py +0 -0
- {synkit-0.0.13/Test/Synthesis/MSR → synkit-0.0.14/Test/Rule}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Rule/test_syn_rule.py +0 -0
- {synkit-0.0.13/Test/Synthesis/Reactor → synkit-0.0.14/Test/Synthesis/CRN}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/CRN/test_crn.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/CRN/test_mod_crn.py +0 -0
- {synkit-0.0.13/Test/Synthesis → synkit-0.0.14/Test/Synthesis/MSR}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/MSR/test_multi_steps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/MSR/test_path_finder.py +0 -0
- {synkit-0.0.13/Test/Vis → synkit-0.0.14/Test/Synthesis/Reactor}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/Reactor/test_mod_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/Reactor/test_mod_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/Reactor/test_partial_engine.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/Reactor/test_rbl_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/Reactor/test_strategy.py +0 -0
- {synkit-0.0.13/Test → synkit-0.0.14/Test/Synthesis}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Synthesis/test_reactor_utils.py +0 -0
- {synkit-0.0.13/synkit/Chem/Cluster → synkit-0.0.14/Test/Vis}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/Test/Vis/test_embedding.py +0 -0
- {synkit-0.0.13/synkit/Chem/Fingerprint → synkit-0.0.14/Test}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/build-doc.sh +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/api.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/changelog.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/chem.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/conf.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/figures/aldol.png +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/figures/aldol_its.png +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/figures/context.png +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/figures/mtg.png +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/getting_started.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/index.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/io.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/reference.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/refs.bib +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/requirements.txt +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/rule.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/doc/synthesis.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/environment.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/pytest.sh +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/requirements.txt +0 -0
- {synkit-0.0.13/synkit/Chem/Molecule → synkit-0.0.14/synkit/Chem/Cluster}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Cluster/butina.py +0 -0
- {synkit-0.0.13/synkit/Data → synkit-0.0.14/synkit/Chem/Fingerprint}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
- {synkit-0.0.13/synkit/Graph/Context → synkit-0.0.14/synkit/Chem/Molecule}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Molecule/standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/aam_validator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/balance_check.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/cleaning.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/deionize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/fix_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/neutralize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/Reaction/tautomerize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Chem/utils.py +0 -0
- {synkit-0.0.13/synkit/Graph/Hyrogen → synkit-0.0.14/synkit/Data}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Data/gen_partial_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Canon/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Canon/canon_algs.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Canon/canon_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Canon/nauty.py +0 -0
- {synkit-0.0.13/synkit/Graph/MTG → synkit-0.0.14/synkit/Graph/Context}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Context/hier_context.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Context/radius_expand.py +0 -0
- {synkit-0.0.13/synkit/Graph/Wildcard → synkit-0.0.14/synkit/Graph/Feature/Fingerprint}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/graph_descriptors.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/graph_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/graph_signature.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/hash_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/morgan_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/path_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Feature/wl_hash.py +0 -0
- {synkit-0.0.13/synkit/Rule/Apply → synkit-0.0.14/synkit/Graph/Hyrogen}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Hyrogen/_misc.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Hyrogen/hextend.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/its_builder.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/its_construction.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/its_expand.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/its_relabel.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/normalize_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/ITS/partial_its.py +0 -0
- {synkit-0.0.13/synkit/Rule/Compose → synkit-0.0.14/synkit/Graph/MTG}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/MTG/group_comp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/MTG/groupoid.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/batch_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/graph_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/graph_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/graph_morphism.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/partial_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/sing.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Matcher/turbo_iso.py +0 -0
- {synkit-0.0.13/synkit/Rule/Modify → synkit-0.0.14/synkit/Graph/Wildcard}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/canon_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/syn_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Graph/utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/chem_converter.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/data_io.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/data_process.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/debug.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/dg_to_gml.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/gml_to_nx.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/graph_to_mol.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/mol_to_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/nx_to_gml.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/IO/smiles_to_id.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/CRN → synkit-0.0.14/synkit/Rule/Apply}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Apply/reactor_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Apply/retro_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Apply/rule_rbl.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/MSR → synkit-0.0.14/synkit/Rule/Compose}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Compose/compose_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Compose/rule_compose.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Compose/rule_mapping.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Compose/seq_comp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Compose/valence_constrain.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/Metrics → synkit-0.0.14/synkit/Rule/Modify}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Modify/implict_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Modify/longest_path.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Modify/molecule_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Modify/prune_templates.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Modify/rule_utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/Modify/strip_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Rule/syn_rule.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/Reactor → synkit-0.0.14/synkit/Synthesis/CRN}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/CRN/crn.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/CRN/dcrn.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/CRN/mod_crn.py +0 -0
- {synkit-0.0.13/synkit/Synthesis → synkit-0.0.14/synkit/Synthesis/MSR}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/MSR/multi_steps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/MSR/path_finder.py +0 -0
- {synkit-0.0.13/synkit/Utils → synkit-0.0.14/synkit/Synthesis/Metrics}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Metrics/_base.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Metrics/_plot.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Metrics/_ranking.py +0 -0
- {synkit-0.0.13/synkit → synkit-0.0.14/synkit/Synthesis/Reactor}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/batch_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/Reactor/strategy.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Synthesis/reactor_utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Utils/utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Vis/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Vis/chemical_space.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Vis/embedding.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Vis/pdf_writer.py +0 -0
- {synkit-0.0.13 → synkit-0.0.14}/synkit/Vis/rxn_vis.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synkit
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.14
|
|
4
4
|
Summary: Utility for reaction modeling using graph grammar
|
|
5
5
|
Project-URL: homepage, https://github.com/TieuLongPhan/SynKit
|
|
6
6
|
Project-URL: source, https://github.com/TieuLongPhan/SynKit
|
|
@@ -30,6 +30,17 @@ Requires-Dist: sphinxcontrib-bibtex; extra == 'docs'
|
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
|
|
32
32
|
# SynKit
|
|
33
|
+
[](https://pypi.org/project/synkit/)
|
|
34
|
+
[](https://anaconda.org/tieulongphan/synkit)
|
|
35
|
+
[](https://hub.docker.com/r/tieulongphan/synkit)
|
|
36
|
+
[](https://hub.docker.com/r/tieulongphan/synkit)
|
|
37
|
+
[](https://github.com/tieulongphan/synkit/blob/main/LICENSE)
|
|
38
|
+
[](https://github.com/tieulongphan/synkit/releases)
|
|
39
|
+
[](https://github.com/tieulongphan/synkit/commits)
|
|
40
|
+
[](https://doi.org/10.5281/zenodo.15269901)
|
|
41
|
+
[](https://github.com/tieulongphan/synkit/actions/workflows/test-and-lint.yml)
|
|
42
|
+
[](https://github.com/tieulongphan/synkit/pulls?q=is%3Apr+label%3Adependencies)
|
|
43
|
+
[](https://github.com/tieulongphan/synkit/stargazers)
|
|
33
44
|
|
|
34
45
|
**Toolkit for Synthesis Planning**
|
|
35
46
|
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
# SynKit
|
|
2
|
+
[](https://pypi.org/project/synkit/)
|
|
3
|
+
[](https://anaconda.org/tieulongphan/synkit)
|
|
4
|
+
[](https://hub.docker.com/r/tieulongphan/synkit)
|
|
5
|
+
[](https://hub.docker.com/r/tieulongphan/synkit)
|
|
6
|
+
[](https://github.com/tieulongphan/synkit/blob/main/LICENSE)
|
|
7
|
+
[](https://github.com/tieulongphan/synkit/releases)
|
|
8
|
+
[](https://github.com/tieulongphan/synkit/commits)
|
|
9
|
+
[](https://doi.org/10.5281/zenodo.15269901)
|
|
10
|
+
[](https://github.com/tieulongphan/synkit/actions/workflows/test-and-lint.yml)
|
|
11
|
+
[](https://github.com/tieulongphan/synkit/pulls?q=is%3Apr+label%3Adependencies)
|
|
12
|
+
[](https://github.com/tieulongphan/synkit/stargazers)
|
|
2
13
|
|
|
3
14
|
**Toolkit for Synthesis Planning**
|
|
4
15
|
|
|
@@ -20,19 +20,17 @@ class TestMTG(unittest.TestCase):
|
|
|
20
20
|
self.test_graph_2 = [get_rc(rsmi_to_its(var)) for var in test_2]
|
|
21
21
|
|
|
22
22
|
def test_MTG_1(self):
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
mtg = MTG(self.test_graph_1[0], self.test_graph_1[1], candidates[0])
|
|
27
|
-
self.assertEqual(len(mtg.get_nodes()), 6)
|
|
28
|
-
self.assertEqual(len(mtg.get_edges()), 7)
|
|
23
|
+
mtg = MTG(self.test_graph_1[0:2], mcs_mol=True)
|
|
24
|
+
self.assertEqual(mtg._graph.number_of_nodes(), 6)
|
|
25
|
+
self.assertEqual(mtg._graph.number_of_edges(), 7)
|
|
29
26
|
|
|
30
27
|
def test_MTG_2(self):
|
|
31
28
|
grp = GroupComp(self.test_graph_2[0], self.test_graph_2[1])
|
|
32
29
|
candidates = grp.get_mapping()
|
|
33
|
-
|
|
34
|
-
self.
|
|
35
|
-
self.assertEqual(
|
|
30
|
+
# print(candidates)
|
|
31
|
+
mtg = MTG(self.test_graph_2[0:], candidates)
|
|
32
|
+
self.assertEqual(mtg._graph.number_of_nodes(), 5)
|
|
33
|
+
self.assertEqual(mtg._graph.number_of_edges(), 4)
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from synkit.Graph.Wildcard.radwc import RadWC
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestRadWC(unittest.TestCase):
|
|
6
|
+
def test_no_product_radicals(self):
|
|
7
|
+
"""If product has no radicals, output should be unchanged."""
|
|
8
|
+
rxn = "[CH3:1][OH:2]>>[CH3:1][OH:2]"
|
|
9
|
+
self.assertEqual(RadWC.transform(rxn), rxn)
|
|
10
|
+
|
|
11
|
+
def test_single_radical_in_product(self):
|
|
12
|
+
"""A single radical in product gets a wildcard."""
|
|
13
|
+
rxn = "[CH3:1][OH:2]>>[CH2:1].[OH:2]"
|
|
14
|
+
out = RadWC.transform(rxn)
|
|
15
|
+
# Check [*:3] is attached to [CH2:1]
|
|
16
|
+
self.assertIn("[CH2:1]([*:3])", out) # Atom-maps: 1,2 exist, so 3 is next
|
|
17
|
+
|
|
18
|
+
def test_multiple_radicals_in_product(self):
|
|
19
|
+
"""Multiple radicals in product get multiple wildcards."""
|
|
20
|
+
rxn = "[CH3:1][OH:2]>>[CH2:1].[O:2]"
|
|
21
|
+
out = RadWC.transform(rxn)
|
|
22
|
+
# [CH2:1] has *:3 and *:4, [O:2] has *:5
|
|
23
|
+
self.assertIn("[CH2:1]([*:3])", out)
|
|
24
|
+
self.assertIn("[O:2]([*:5])", out)
|
|
25
|
+
|
|
26
|
+
def test_radical_and_nonradical_mixture(self):
|
|
27
|
+
"""Mixed radical/non-radical product fragments, only radicals get wildcard."""
|
|
28
|
+
rxn = "[CH3:1][OH:2]>>[CH2:1].[OH:2]"
|
|
29
|
+
out = RadWC.transform(rxn)
|
|
30
|
+
# [CH2:1] gets *:3, [OH:2] is unchanged
|
|
31
|
+
self.assertIn("[CH2:1]([*:3])", out)
|
|
32
|
+
self.assertIn("[OH:2]", out)
|
|
33
|
+
|
|
34
|
+
def test_user_start_map(self):
|
|
35
|
+
"""User-supplied map index is used for wildcards."""
|
|
36
|
+
rxn = "[CH3:7][OH:8]>>[CH2:7].[OH:8]"
|
|
37
|
+
out = RadWC.transform(rxn, start_map=50)
|
|
38
|
+
self.assertIn("[CH2:7]([*:50])", out)
|
|
39
|
+
|
|
40
|
+
def test_empty_reaction(self):
|
|
41
|
+
"""Empty input should raise ValueError."""
|
|
42
|
+
with self.assertRaises(ValueError):
|
|
43
|
+
RadWC.transform("")
|
|
44
|
+
|
|
45
|
+
def test_three_component(self):
|
|
46
|
+
"""Agent block is preserved."""
|
|
47
|
+
rxn = "[CH3:1][OH:2]>[Na+]>[CH2:1].[OH:2]"
|
|
48
|
+
out = RadWC.transform(rxn)
|
|
49
|
+
self.assertTrue(out.startswith("[CH3:1][OH:2]>[Na+]>"))
|
|
50
|
+
self.assertIn("[CH2:1]([*:3])", out)
|
|
51
|
+
self.assertIn("[OH:2]", out)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
unittest.main()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from synkit.IO import rsmi_to_graph
|
|
3
|
+
from synkit.Graph.Wildcard.wildcard import WildCard
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestWildCard(unittest.TestCase):
|
|
7
|
+
def setUp(self):
|
|
8
|
+
# The main, complex test case with atom mapping
|
|
9
|
+
self.rsmi_main = (
|
|
10
|
+
"[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4][c:31]2[NH2:28]."
|
|
11
|
+
"[cH:2]1[c:20]([C:22]([OH:7])=[O:21])[s:18][c:24]([S:6][c:29]2[c:15]([Cl:26])[cH:8]"
|
|
12
|
+
"[n:19][cH:9][c:16]2[Cl:27])[c:30]1[N+:5]([O-:3])=[O:13]>>"
|
|
13
|
+
"[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4][c:31]2[NH:28]"
|
|
14
|
+
"[C:22]([c:20]1[cH:2][c:30]([N+:5]([O-:3])=[O:13])[c:24]([S:6][c:29]2[c:15]([Cl:26])"
|
|
15
|
+
"[cH:8][n:19][cH:9][c:16]2[Cl:27])[s:18]1)=[O:21]"
|
|
16
|
+
)
|
|
17
|
+
# No atoms lost: R == P, should not add wildcards
|
|
18
|
+
self.rsmi_no_loss = "CCO>>CCO"
|
|
19
|
+
# All atoms lost: RSMI that loses everything (nonsense, but good test)
|
|
20
|
+
self.rsmi_all_lost = "CCO>>"
|
|
21
|
+
# Empty
|
|
22
|
+
self.rsmi_empty = ""
|
|
23
|
+
# Wildcard already present
|
|
24
|
+
self.rsmi_existing_wildcard = "[CH3:1][CH2:2][OH:3]>>[CH2:1][CH2:2].[*:4][OH:3]"
|
|
25
|
+
# No atom map (should raise error)
|
|
26
|
+
self.rsmi_no_atom_map = "C(C)Cl>>CC"
|
|
27
|
+
|
|
28
|
+
def test_main_case_wildcard_added(self):
|
|
29
|
+
"""Complex case: output product contains wildcard and roundtrip is valid."""
|
|
30
|
+
out_rsmi = WildCard.rsmi_with_wildcards(self.rsmi_main)
|
|
31
|
+
_, product = out_rsmi.split(">>")
|
|
32
|
+
self.assertIsInstance(out_rsmi, str)
|
|
33
|
+
self.assertIn(
|
|
34
|
+
"*", product, "Wildcard '*' should be present in the product side."
|
|
35
|
+
)
|
|
36
|
+
# Roundtrip: should parse back without error
|
|
37
|
+
r, p = rsmi_to_graph(out_rsmi)
|
|
38
|
+
self.assertTrue(r.number_of_nodes() > 0)
|
|
39
|
+
self.assertTrue(p.number_of_nodes() > 0)
|
|
40
|
+
|
|
41
|
+
def test_no_atoms_lost(self):
|
|
42
|
+
"""No atoms lost: should raise ValueError if input is not atom-mapped."""
|
|
43
|
+
with self.assertRaises(ValueError):
|
|
44
|
+
WildCard.rsmi_with_wildcards(self.rsmi_no_loss)
|
|
45
|
+
|
|
46
|
+
def test_all_atoms_lost(self):
|
|
47
|
+
"""All atoms lost: should raise ValueError if input is not atom-mapped."""
|
|
48
|
+
with self.assertRaises(ValueError):
|
|
49
|
+
WildCard.rsmi_with_wildcards(self.rsmi_all_lost)
|
|
50
|
+
|
|
51
|
+
def test_empty_input(self):
|
|
52
|
+
"""Empty input: should raise ValueError."""
|
|
53
|
+
with self.assertRaises(ValueError):
|
|
54
|
+
WildCard.rsmi_with_wildcards(self.rsmi_empty)
|
|
55
|
+
|
|
56
|
+
def test_wildcard_not_duplicated(self):
|
|
57
|
+
"""Existing wildcards: should not create duplicate wildcards for same lost bond."""
|
|
58
|
+
out_rsmi = WildCard.rsmi_with_wildcards(self.rsmi_existing_wildcard)
|
|
59
|
+
_, product = out_rsmi.split(">>")
|
|
60
|
+
# At least one '*' in the product SMILES string
|
|
61
|
+
self.assertIn("*", product)
|
|
62
|
+
|
|
63
|
+
def test_no_false_positive_wildcards(self):
|
|
64
|
+
"""Wildcards are only added if there are truly lost subgraphs; non-atom-mapped input raises."""
|
|
65
|
+
rsmi = "C>>C"
|
|
66
|
+
with self.assertRaises(ValueError):
|
|
67
|
+
WildCard.rsmi_with_wildcards(rsmi)
|
|
68
|
+
|
|
69
|
+
def test_output_is_str_and_split(self):
|
|
70
|
+
"""Should raise ValueError if input is not atom-mapped."""
|
|
71
|
+
with self.assertRaises(ValueError):
|
|
72
|
+
WildCard.rsmi_with_wildcards(self.rsmi_no_loss)
|
|
73
|
+
|
|
74
|
+
def test_missing_atom_map_raises(self):
|
|
75
|
+
"""Should raise ValueError if atom_map attributes are missing."""
|
|
76
|
+
with self.assertRaises(ValueError):
|
|
77
|
+
WildCard.rsmi_with_wildcards(self.rsmi_no_atom_map)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
unittest.main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from synkit.IO.combinatorial.smarts_expander import SMARTSExpander
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestSMARTSExpander(unittest.TestCase):
|
|
6
|
+
|
|
7
|
+
def test_no_placeholders(self):
|
|
8
|
+
s = "CCO"
|
|
9
|
+
self.assertEqual(list(SMARTSExpander.expand_iter(s)), ["CCO"])
|
|
10
|
+
self.assertEqual(SMARTSExpander.expand(s), ["CCO"])
|
|
11
|
+
|
|
12
|
+
def test_simple_expansion(self):
|
|
13
|
+
s = "[C,N:1][O,P:2]"
|
|
14
|
+
result = SMARTSExpander.expand(s)
|
|
15
|
+
self.assertEqual(
|
|
16
|
+
set(result), {"[C:1][O:2]", "[C:1][P:2]", "[N:1][O:2]", "[N:1][P:2]"}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def test_disjoint_constraint(self):
|
|
20
|
+
s = "[C,N:1][O:1]"
|
|
21
|
+
with self.assertRaises(ValueError):
|
|
22
|
+
list(SMARTSExpander.expand_iter(s))
|
|
23
|
+
|
|
24
|
+
def test_realistic_reaction(self):
|
|
25
|
+
rxn = (
|
|
26
|
+
"[H+:6].[C:7](-[O:8](-[H:12]))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])."
|
|
27
|
+
"[C:2](-[S:4](-[C,N,O,P,S:5]))(-[C,N,O,P,S:1])(=[O:3])>>"
|
|
28
|
+
"[S:4](-[H:6])(-[C,N,O,P,S:5]).[H+:12]."
|
|
29
|
+
"[C:7](-[O:8](-[C:2](-[C,N,O,P,S:1])(=[O:3])))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])"
|
|
30
|
+
)
|
|
31
|
+
ex_list = list(SMARTSExpander.expand_iter(rxn))
|
|
32
|
+
self.assertEqual(len(ex_list), 625)
|
|
33
|
+
# Optional: Just check format or count, not endswith
|
|
34
|
+
self.assertTrue(ex_list[0].startswith("[H+:6]"))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
unittest.main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from rdkit import Chem
|
|
3
|
+
from synkit.IO.combinatorial.smarts_generalizer import SMARTSGeneralizer
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestSMARTSGeneralizer(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.gen = SMARTSGeneralizer(sanity_check=True)
|
|
10
|
+
|
|
11
|
+
def test_basic_generalization(self):
|
|
12
|
+
inputs = [
|
|
13
|
+
"[C:1]-[N:2]>>[N:1]-[C:2]",
|
|
14
|
+
"[N:1]-[N:2]>>[N:1]-[N:2]",
|
|
15
|
+
"[O:1]-[N:2]>>[N:1]-[N:2]",
|
|
16
|
+
]
|
|
17
|
+
output = self.gen.generalize(inputs)
|
|
18
|
+
# Instead of strict string match, check correct mapped elements
|
|
19
|
+
self.assertIn("[C,N,O:1]", output)
|
|
20
|
+
self.assertIn("[N:2]", output)
|
|
21
|
+
self.assertIn(">>", output)
|
|
22
|
+
|
|
23
|
+
def test_single_smarts(self):
|
|
24
|
+
inputs = ["[C:1]-[N:2]>>[N:1]-[C:2]"]
|
|
25
|
+
output = self.gen.generalize(inputs)
|
|
26
|
+
# Should match input exactly
|
|
27
|
+
self.assertEqual(output, "[C:1]-[N:2]>>[N:1]-[C:2]")
|
|
28
|
+
|
|
29
|
+
def test_different_topology_raises(self):
|
|
30
|
+
inputs = ["[C:1]-[N:2]>>[N:1]-[C:2]", "[N:1]-[N:2]-[C:3]>>[N:1]-[N:2]-[C:3]"]
|
|
31
|
+
with self.assertRaises(ValueError):
|
|
32
|
+
self.gen.generalize(inputs)
|
|
33
|
+
|
|
34
|
+
def test_empty_input_raises(self):
|
|
35
|
+
with self.assertRaises(ValueError):
|
|
36
|
+
self.gen.generalize([])
|
|
37
|
+
|
|
38
|
+
def test_molecule_smarts(self):
|
|
39
|
+
gen = SMARTSGeneralizer(sanity_check=True)
|
|
40
|
+
inputs = ["[C:1]-[N:2]", "[N:1]-[N:2]", "[O:1]-[N:2]"]
|
|
41
|
+
out = gen.generalize(inputs)
|
|
42
|
+
self.assertEqual(out, "[C,N,O:1]-[N:2]")
|
|
43
|
+
|
|
44
|
+
mol = Chem.MolFromSmarts(out)
|
|
45
|
+
self.assertIsNotNone(mol)
|
|
46
|
+
|
|
47
|
+
def test_invalid_sanity_check(self):
|
|
48
|
+
gen = SMARTSGeneralizer(sanity_check=True)
|
|
49
|
+
# Using an obviously broken SMARTS (bad bracket placement)
|
|
50
|
+
_ = [
|
|
51
|
+
"[C:1]-[N:2]>>[N:1]-[X:2]"
|
|
52
|
+
] # 'X' is a valid SMARTS wildcard! Use a real error
|
|
53
|
+
with self.assertRaises(ValueError):
|
|
54
|
+
gen.generalize(["[C:1][C:2]>>[N:1][C:2]["]) # broken SMARTS
|
|
55
|
+
|
|
56
|
+
def test_repr(self):
|
|
57
|
+
gen = SMARTSGeneralizer()
|
|
58
|
+
self.assertIn("sanity_check", repr(gen))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
unittest.main()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import networkx as nx
|
|
3
|
+
|
|
4
|
+
from synkit.IO.combinatorial.smarts_to_graph import SMARTSToGraph
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestSMARTSToGraph(unittest.TestCase):
|
|
8
|
+
|
|
9
|
+
def setUp(self):
|
|
10
|
+
self.stg = SMARTSToGraph()
|
|
11
|
+
|
|
12
|
+
def test_smarts_to_graph_simple(self):
|
|
13
|
+
g = self.stg.smarts_to_graph("[C:1]-[O:2]")
|
|
14
|
+
self.assertIsInstance(g, nx.Graph)
|
|
15
|
+
self.assertEqual(set(g.nodes), {1, 2})
|
|
16
|
+
self.assertEqual(g.nodes[1]["element"], "C")
|
|
17
|
+
self.assertEqual(g.nodes[2]["element"], "O")
|
|
18
|
+
self.assertIsNone(g.nodes[1]["constraint"])
|
|
19
|
+
|
|
20
|
+
def test_smarts_to_graph_constraint(self):
|
|
21
|
+
g = self.stg.smarts_to_graph("[C,N,O:1]-[N:2]")
|
|
22
|
+
# Node 1 should be placeholder
|
|
23
|
+
self.assertEqual(g.nodes[1]["element"], "*")
|
|
24
|
+
self.assertIsInstance(g.nodes[1]["constraint"], list)
|
|
25
|
+
self.assertIn("C", g.nodes[1]["constraint"])
|
|
26
|
+
self.assertEqual(g.nodes[2]["element"], "N")
|
|
27
|
+
self.assertIsNone(g.nodes[2]["constraint"])
|
|
28
|
+
|
|
29
|
+
def test_smarts_to_graph_hcount(self):
|
|
30
|
+
g = self.stg.smarts_to_graph("[CH3:1]-[O:2]")
|
|
31
|
+
# For SMARTS as written, RDKit returns 0 hydrogens for both
|
|
32
|
+
self.assertEqual(g.nodes[1]["hcount"], 0)
|
|
33
|
+
self.assertEqual(g.nodes[2]["hcount"], 0)
|
|
34
|
+
|
|
35
|
+
def test_invalid_smarts(self):
|
|
36
|
+
with self.assertRaises(ValueError):
|
|
37
|
+
self.stg.smarts_to_graph("[C:1]-[N")
|
|
38
|
+
|
|
39
|
+
def test_missing_atom_map(self):
|
|
40
|
+
with self.assertRaises(ValueError):
|
|
41
|
+
self.stg.smarts_to_graph("[C]-[O:2]")
|
|
42
|
+
|
|
43
|
+
def test_rxn_smarts_to_graphs(self):
|
|
44
|
+
rxn = (
|
|
45
|
+
"[H+:6].[C:7](-[O:8](-[H:12]))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])."
|
|
46
|
+
"[C:2](-[S:4](-[C,N,O,P,S:5]))(-[C,N,O,P,S:1])(=[O:3])>>"
|
|
47
|
+
"[S:4](-[H:6])(-[C,N,O,P,S:5]).[H+:12]."
|
|
48
|
+
"[C:7](-[O:8](-[C:2](-[C,N,O,P,S:1])(=[O:3])))(-[C,N,O,P,S:9])(-[C,N,O,P,S:10])(-[H:11])"
|
|
49
|
+
)
|
|
50
|
+
g_react, _ = self.stg.rxn_smarts_to_graphs(rxn)
|
|
51
|
+
|
|
52
|
+
# These are the atom_map indices that should have constraint (from SMARTS [C,N,O,P,S:idx])
|
|
53
|
+
expected_constraint_nodes = {1, 5, 9, 10}
|
|
54
|
+
for idx in expected_constraint_nodes:
|
|
55
|
+
self.assertIn(idx, g_react.nodes)
|
|
56
|
+
self.assertIsNotNone(
|
|
57
|
+
g_react.nodes[idx]["constraint"],
|
|
58
|
+
f"Node {idx} should have a constraint list but does not",
|
|
59
|
+
)
|
|
60
|
+
self.assertEqual(
|
|
61
|
+
set(g_react.nodes[idx]["constraint"]),
|
|
62
|
+
{"C", "N", "O", "P", "S"},
|
|
63
|
+
f"Node {idx} has incorrect constraint list",
|
|
64
|
+
)
|
|
65
|
+
# All other nodes should NOT have a constraint
|
|
66
|
+
for idx in set(g_react.nodes) - expected_constraint_nodes:
|
|
67
|
+
self.assertIsNone(
|
|
68
|
+
g_react.nodes[idx]["constraint"],
|
|
69
|
+
f"Node {idx} should NOT have a constraint list",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def test_rxn_separator(self):
|
|
73
|
+
with self.assertRaises(ValueError):
|
|
74
|
+
self.stg.rxn_smarts_to_graphs("[C:1]-[O:2]") # no '>>'
|
|
75
|
+
|
|
76
|
+
def test_repr_and_describe(self):
|
|
77
|
+
r = repr(self.stg)
|
|
78
|
+
self.assertIn("placeholders", r)
|
|
79
|
+
desc = self.stg.describe()
|
|
80
|
+
self.assertIn("smarts_to_graph", desc)
|
|
81
|
+
self.assertIn("rxn_smarts_to_graphs", desc)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
unittest.main()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from synkit.IO import rsmi_to_its
|
|
3
|
+
from synkit.Graph.Wildcard.wildcard import WildCard
|
|
4
|
+
from synkit.Chem.Reaction.standardize import Standardize
|
|
5
|
+
from synkit.Synthesis.Reactor.imba_engine import ImbaEngine
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestImbaEngine(unittest.TestCase):
|
|
9
|
+
def setUp(self):
|
|
10
|
+
# A complex standardized RSMI from your example
|
|
11
|
+
self.smart = (
|
|
12
|
+
"[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4][c:31]2[NH2:28]."
|
|
13
|
+
"[cH:2]1[c:20]([C:22]([OH:7])=[O:21])[s:18][c:24]([S:6][c:29]2[c:15]"
|
|
14
|
+
"([Cl:26])[cH:8][n:19][cH:9][c:16]2[Cl:27])[c:30]1[N+:5]([O-:3])=[O:13]>>"
|
|
15
|
+
"[cH:1]1[cH:14][c:10]2[c:23]([cH:11][n:25]1)[cH:17][cH:12][cH:4]"
|
|
16
|
+
"[c:31]2[NH:28][C:22]([c:20]1[cH:2][c:30]([N+:5]([O-:3])=[O:13])[c:24]([S:6]"
|
|
17
|
+
"[c:29]2[c:15]([Cl:26])[cH:8][n:19][cH:9][c:16]2[Cl:27])[s:18]1)=[O:21]"
|
|
18
|
+
)
|
|
19
|
+
# Standardize removes AAM
|
|
20
|
+
self.rsmi = Standardize().fit(self.smart, remove_aam=True)
|
|
21
|
+
|
|
22
|
+
def test_pipeline_forward(self):
|
|
23
|
+
"""Test forward ImbaEngine pipeline end-to-end."""
|
|
24
|
+
# Apply wildcard insertion
|
|
25
|
+
wild_smart = WildCard().rsmi_with_wildcards(self.smart)
|
|
26
|
+
# Build ITS graphs
|
|
27
|
+
temp = rsmi_to_its(wild_smart, core=True)
|
|
28
|
+
# substrate split from standardized RSMI
|
|
29
|
+
substrate_r, _ = self.rsmi.split(">>")
|
|
30
|
+
# Run engine forward with template without cleaning fragments
|
|
31
|
+
engine = ImbaEngine(substrate_r, temp, add_wildcard=True, clean_fragments=False)
|
|
32
|
+
out = engine.smarts_list
|
|
33
|
+
self.assertEqual(len(out), 1)
|
|
34
|
+
out_rsmi = Standardize().fit(out[0], remove_aam=True)
|
|
35
|
+
|
|
36
|
+
self.assertIn("*", out_rsmi)
|
|
37
|
+
self.assertNotEqual(out_rsmi, self.rsmi)
|
|
38
|
+
|
|
39
|
+
# Run engine forward with template with cleaning fragments
|
|
40
|
+
engine = ImbaEngine(substrate_r, temp, add_wildcard=True, clean_fragments=True)
|
|
41
|
+
out = engine.smarts_list
|
|
42
|
+
outs = [Standardize().fit(o, remove_aam=True) for o in out]
|
|
43
|
+
self.assertIn(self.rsmi, outs)
|
|
44
|
+
|
|
45
|
+
def test_pipeline_backward(self):
|
|
46
|
+
"""Test backward ImbaEngine pipeline end-to-end with and without fragment cleaning."""
|
|
47
|
+
# Prepare wildcard and ITS template
|
|
48
|
+
wild_rsmi = WildCard().rsmi_with_wildcards(self.smart)
|
|
49
|
+
its = rsmi_to_its(wild_rsmi, core=True)
|
|
50
|
+
|
|
51
|
+
_, substrate_p = self.rsmi.split(">>")
|
|
52
|
+
|
|
53
|
+
# 1. Without fragment cleaning
|
|
54
|
+
engine = ImbaEngine(
|
|
55
|
+
substrate_p,
|
|
56
|
+
its,
|
|
57
|
+
add_wildcard=True,
|
|
58
|
+
clean_fragments=False,
|
|
59
|
+
invert=True,
|
|
60
|
+
partial=True,
|
|
61
|
+
)
|
|
62
|
+
out = engine.smarts_list
|
|
63
|
+
self.assertEqual(len(out), 2)
|
|
64
|
+
out_rsmi = Standardize().fit(out[0], remove_aam=True)
|
|
65
|
+
self.assertIn("*", out_rsmi)
|
|
66
|
+
self.assertNotEqual(out_rsmi, self.rsmi)
|
|
67
|
+
|
|
68
|
+
# 2. With fragment cleaning
|
|
69
|
+
engine_clean = ImbaEngine(
|
|
70
|
+
substrate_p,
|
|
71
|
+
its,
|
|
72
|
+
add_wildcard=True,
|
|
73
|
+
clean_fragments=True,
|
|
74
|
+
invert=True,
|
|
75
|
+
partial=True,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
out_clean = engine_clean.smarts_list
|
|
79
|
+
self.assertEqual(len(out_clean), 2)
|
|
80
|
+
outs = [Standardize().fit(o, remove_aam=True) for o in out_clean]
|
|
81
|
+
self.assertIn(self.rsmi, outs)
|
|
82
|
+
|
|
83
|
+
def test_invalid_rsmi(self):
|
|
84
|
+
"""Invalid RSMI pipeline should raise an exception at Standardize or ITS step."""
|
|
85
|
+
# Standardize should fail for invalid RSMI
|
|
86
|
+
with self.assertRaises(Exception):
|
|
87
|
+
# Attempt full pipeline
|
|
88
|
+
rsmi = Standardize().fit("not_a_rsmi", remove_aam=True)
|
|
89
|
+
wild = WildCard().rsmi_with_wildcards(rsmi)
|
|
90
|
+
_ = rsmi_to_its(wild, core=True)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == "__main__":
|
|
94
|
+
unittest.main()
|
|
Binary file
|
|
@@ -200,42 +200,77 @@ This example builds two reaction-center ITS graphs, computes their MCS mapping,
|
|
|
200
200
|
:caption: Building and visualizing an MTG with composite ITS
|
|
201
201
|
:linenos:
|
|
202
202
|
|
|
203
|
-
from synkit.IO.chem_converter import rsmi_to_its
|
|
204
|
-
from synkit.Graph.MTG.mcs_matcher import MCSMatcher
|
|
205
203
|
from synkit.Graph.MTG.mtg import MTG
|
|
206
|
-
from synkit.Graph import
|
|
207
|
-
from synkit.
|
|
204
|
+
from synkit.Graph.ITS.its_decompose import get_rc
|
|
205
|
+
from synkit.examples import list_examples, load_example
|
|
208
206
|
import matplotlib.pyplot as plt
|
|
207
|
+
from synkit.Vis.graph_visualizer import GraphVisualizer
|
|
209
208
|
|
|
210
|
-
# 1) Define two related reaction SMILES and build their reaction-center ITS graphs
|
|
211
|
-
rsmi_list = [
|
|
212
|
-
'[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH:4]([H:8])=[CH:5][O:6]([H:7])', # tautomerization
|
|
213
|
-
'[CH3:1][C:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:7])>>'
|
|
214
|
-
'[CH3:1][C:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]' # nucleophilic addition
|
|
215
|
-
]
|
|
216
|
-
rc_graphs = [rsmi_to_its(r, core=True) for r in rsmi_list]
|
|
217
209
|
|
|
218
|
-
|
|
219
|
-
mcs = MCSMatcher(node_label_names=['element', 'charge'], edge_attribute='order')
|
|
220
|
-
mcs.find_rc_mapping(rc_graphs[0], rc_graphs[1], mcs=True)
|
|
221
|
-
mapping = mcs.get_mappings()[0]
|
|
210
|
+
data = load_example("aldol")
|
|
222
211
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
mtg_graph = mtg.get_graph()
|
|
212
|
+
mech_neutral = data[0]['mechanisms'][1]['steps']
|
|
213
|
+
smart_neutral = [i['smart_string'] for i in mech_neutral]
|
|
226
214
|
|
|
227
|
-
|
|
228
|
-
|
|
215
|
+
mech_acid = data[0]['mechanisms'][2]['steps']
|
|
216
|
+
smart_acid = [i['smart_string'] for i in mech_acid]
|
|
229
217
|
|
|
218
|
+
# neutral
|
|
219
|
+
mtg = MTG(smart_neutral, mcs_mol=True)
|
|
220
|
+
mtg_its_neutral = mtg.get_compose_its()
|
|
221
|
+
mtg_rc_neutral = get_rc(mtg_its_neutral, keep_mtg=True)
|
|
222
|
+
rc_neutral = get_rc(mtg_its_neutral, keep_mtg=False)
|
|
230
223
|
|
|
231
|
-
#
|
|
232
|
-
|
|
224
|
+
# acid
|
|
225
|
+
mtg = MTG(smart_acid, mcs_mol=True)
|
|
226
|
+
mtg_its_acid = mtg.get_compose_its()
|
|
227
|
+
mtg_rc_acid = get_rc(mtg_its_acid, keep_mtg=True)
|
|
228
|
+
rc_acid = get_rc(mtg_its_acid, keep_mtg=False)
|
|
229
|
+
|
|
230
|
+
# Visualize
|
|
231
|
+
fig, ax = plt.subplots(2, 2, figsize=(16, 8))
|
|
233
232
|
vis = GraphVisualizer()
|
|
234
233
|
|
|
235
|
-
vis.plot_its(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
234
|
+
vis.plot_its(
|
|
235
|
+
mtg_rc_neutral,
|
|
236
|
+
ax=ax[0, 0],
|
|
237
|
+
use_edge_color=True,
|
|
238
|
+
og=True,
|
|
239
|
+
title='A. MTG for aldol addition (neutral)',
|
|
240
|
+
title_font_size=20,
|
|
241
|
+
title_font_weight='medium',
|
|
242
|
+
title_font_style='normal'
|
|
243
|
+
)
|
|
244
|
+
vis.plot_its(
|
|
245
|
+
rc_neutral,
|
|
246
|
+
ax=ax[0, 1],
|
|
247
|
+
use_edge_color=True,
|
|
248
|
+
og=True,
|
|
249
|
+
title='B. Reaction center (neutral)',
|
|
250
|
+
title_font_size=20,
|
|
251
|
+
title_font_weight='medium',
|
|
252
|
+
title_font_style='normal'
|
|
253
|
+
)
|
|
254
|
+
vis.plot_its(
|
|
255
|
+
mtg_rc_acid,
|
|
256
|
+
ax=ax[1, 0],
|
|
257
|
+
use_edge_color=True,
|
|
258
|
+
og=True,
|
|
259
|
+
title='C. MTG for aldol addition (acid)',
|
|
260
|
+
title_font_size=20,
|
|
261
|
+
title_font_weight='medium',
|
|
262
|
+
title_font_style='normal'
|
|
263
|
+
)
|
|
264
|
+
vis.plot_its(
|
|
265
|
+
rc_acid,
|
|
266
|
+
ax=ax[1, 1],
|
|
267
|
+
use_edge_color=True,
|
|
268
|
+
og=True,
|
|
269
|
+
title='D. Reaction center (acid)',
|
|
270
|
+
title_font_size=20,
|
|
271
|
+
title_font_weight='medium',
|
|
272
|
+
title_font_style='normal'
|
|
273
|
+
)
|
|
239
274
|
|
|
240
275
|
plt.tight_layout()
|
|
241
276
|
plt.show()
|
|
@@ -243,16 +278,13 @@ This example builds two reaction-center ITS graphs, computes their MCS mapping,
|
|
|
243
278
|
|
|
244
279
|
.. container:: figure
|
|
245
280
|
|
|
246
|
-
.. image:: ./figures/
|
|
281
|
+
.. image:: ./figures/mtg_mechanism.png
|
|
247
282
|
:alt: Composite ITS and MTG visualization
|
|
248
283
|
:align: center
|
|
249
284
|
:width: 1000px
|
|
250
285
|
|
|
251
286
|
*Figure:*
|
|
252
|
-
|
|
253
|
-
(B) Reaction‐center graph for the nucleophilic addition step
|
|
254
|
-
(C) Composite ITS graph "gluing" both transformations
|
|
255
|
-
(D) Mechanistic Transition Graph (MTG) showing step-wise mechanism
|
|
287
|
+
Composition of the mechanistic sequences for aldol addition under neutral and acidic conditions, showing the composite MTG (left column) and the reaction center (right column).
|
|
256
288
|
|
|
257
289
|
Context graph
|
|
258
290
|
-------------
|