synkit 0.0.13__tar.gz → 0.0.15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/test-and-lint.yml +1 -1
- {synkit-0.0.13 → synkit-0.0.15}/.gitignore +3 -4
- {synkit-0.0.13 → synkit-0.0.15}/PKG-INFO +12 -1
- {synkit-0.0.13 → synkit-0.0.15}/README.md +11 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_its_construction.py +14 -14
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/test_mtg.py +7 -9
- synkit-0.0.15/Test/Graph/Wildcard/test_radwc.py +55 -0
- synkit-0.0.15/Test/Graph/Wildcard/test_wildcard.py +81 -0
- synkit-0.0.15/Test/IO/combinatorial/test_smarts_expander.py +38 -0
- synkit-0.0.15/Test/IO/combinatorial/test_smarts_generalizer.py +62 -0
- synkit-0.0.15/Test/IO/combinatorial/test_smarts_to_graph.py +85 -0
- synkit-0.0.15/Test/Synthesis/Reactor/test_imba_engine.py +94 -0
- synkit-0.0.15/doc/figures/mtg_mechanism.png +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/graph.rst +64 -32
- {synkit-0.0.13 → synkit-0.0.15}/lint.sh +5 -1
- {synkit-0.0.13 → synkit-0.0.15}/pyproject.toml +1 -1
- {synkit-0.0.13 → synkit-0.0.15}/recipe/meta.yaml +1 -1
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/radical_wildcard.py +57 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/utils.py +39 -0
- synkit-0.0.15/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +231 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Hyrogen/_misc.py +51 -90
- synkit-0.0.15/synkit/Graph/ITS/its_construction.py +316 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_decompose.py +283 -224
- synkit-0.0.15/synkit/Graph/ITS/its_destruction.py +302 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/MTG/mcs_matcher.py +67 -25
- synkit-0.0.15/synkit/Graph/MTG/mtg.py +886 -0
- synkit-0.0.15/synkit/Graph/MTG/mtg_explore.py +74 -0
- synkit-0.0.15/synkit/Graph/MTG/utils.py +425 -0
- synkit-0.0.15/synkit/Graph/Matcher/subgraph_matcher.py +1162 -0
- synkit-0.0.15/synkit/Graph/Wildcard/radwc.py +117 -0
- synkit-0.0.15/synkit/Graph/Wildcard/wildcard.py +230 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/__init__.py +1 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/utils.py +25 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/chem_converter.py +11 -2
- synkit-0.0.15/synkit/IO/combinatorial/__init__.py +8 -0
- synkit-0.0.15/synkit/IO/combinatorial/gml_to_graph.py +254 -0
- synkit-0.0.15/synkit/IO/combinatorial/graph_to_gml.py +291 -0
- synkit-0.0.15/synkit/IO/combinatorial/graph_to_smarts.py +189 -0
- synkit-0.0.15/synkit/IO/combinatorial/smarts_expander.py +152 -0
- synkit-0.0.15/synkit/IO/combinatorial/smarts_generalizer.py +134 -0
- synkit-0.0.15/synkit/IO/combinatorial/smarts_to_graph.py +183 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/nx_to_gml.py +2 -1
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/rule_matcher.py +15 -5
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/syn_rule.py +3 -1
- synkit-0.0.15/synkit/Synthesis/Reactor/batch_reactor.py +462 -0
- synkit-0.0.15/synkit/Synthesis/Reactor/benchmark.py +152 -0
- synkit-0.0.15/synkit/Synthesis/Reactor/imba_engine.py +173 -0
- synkit-0.0.15/synkit/Synthesis/Reactor/post_syn.py +267 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/syn_reactor.py +26 -5
- synkit-0.0.15/synkit/Synthesis/__init__.py +0 -0
- synkit-0.0.15/synkit/Utils/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/graph_visualizer.py +9 -1
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/rule_vis.py +2 -2
- synkit-0.0.15/synkit/__init__.py +0 -0
- synkit-0.0.15/synkit/examples.py +50 -0
- synkit-0.0.13/Test/Synthesis/Reactor/test_core_engine.py +0 -112
- synkit-0.0.13/synkit/Graph/ITS/its_construction.py +0 -325
- synkit-0.0.13/synkit/Graph/MTG/mtg.py +0 -208
- synkit-0.0.13/synkit/Graph/Matcher/subgraph_matcher.py +0 -550
- synkit-0.0.13/synkit/Synthesis/Reactor/batch_reactor.py +0 -225
- {synkit-0.0.13 → synkit-0.0.15}/.github/dependabot.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/build-doc.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/conda-forge-publish.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/docker-publish.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/publish-package.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/.github/workflows/verify-pypi-install.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/.readthedocs.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Data/Figure/synkit.png +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/ComposeRule/data.txt +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/0.gml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/1.gml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Data/Testcase/Compose/SingleRule/R0/2.gml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Dockerfile +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/LICENSE +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/test_fp_calculator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/test_smiles_featurizer.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Fingerprint/test_transformation_fp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Molecule/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Molecule/test_standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_aam_validator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_balance_checker.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_canon_rsmi.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_cleanning.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_deionize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_fix_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_neutralize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_radical_wildcard.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/Reaction/test_tautomerize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Chem/test_utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Context/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Context/test_hier_context.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Context/test_radius_expand.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_graph_descriptors.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_graph_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_graph_signature.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_hash_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_morgan_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Feature/test_path_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/test_graph_hydrogen.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/test_hcomplete.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Hydrogen/test_misc.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_its_expand.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_its_relabel.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/ITS/test_normalize_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/test_group_comp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/MTG/test_groupoid.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_batch_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_graph_morphism.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/Matcher/test_subgraph_matcher.py +0 -0
- {synkit-0.0.13/Test/Graph → synkit-0.0.15/Test/Graph/Wildcard}/__init__.py +0 -0
- {synkit-0.0.13/Test/IO → synkit-0.0.15/Test/Graph}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/test_canon_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Graph/test_syn_graph.py +0 -0
- {synkit-0.0.13/Test/Rule/Apply → synkit-0.0.15/Test/IO}/__init__.py +0 -0
- {synkit-0.0.13/Test/Rule/Compose → synkit-0.0.15/Test/IO/combinatorial}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_chemical_converter.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_dg_to_gml.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_gml_to_nx.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_graph_to_mol.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_mol_to_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/IO/test_nx_to_gml.py +0 -0
- {synkit-0.0.13/Test/Rule/Modify → synkit-0.0.15/Test/Rule/Apply}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_reactor_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_retro_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_rule_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Apply/test_rule_rbl.py +0 -0
- {synkit-0.0.13/Test/Rule → synkit-0.0.15/Test/Rule/Compose}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Compose/test_rule_compose.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Compose/test_valance_constrain.py +0 -0
- {synkit-0.0.13/Test/Synthesis/CRN → synkit-0.0.15/Test/Rule/Modify}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Modify/test_molecule_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/Modify/test_rule_utils.py +0 -0
- {synkit-0.0.13/Test/Synthesis/MSR → synkit-0.0.15/Test/Rule}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Rule/test_syn_rule.py +0 -0
- {synkit-0.0.13/Test/Synthesis/Reactor → synkit-0.0.15/Test/Synthesis/CRN}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/CRN/test_crn.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/CRN/test_mod_crn.py +0 -0
- {synkit-0.0.13/Test/Synthesis → synkit-0.0.15/Test/Synthesis/MSR}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/MSR/test_multi_steps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/MSR/test_path_finder.py +0 -0
- {synkit-0.0.13/Test/Vis → synkit-0.0.15/Test/Synthesis/Reactor}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_mod_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_mod_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_partial_engine.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_rbl_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/Reactor/test_strategy.py +0 -0
- {synkit-0.0.13/Test → synkit-0.0.15/Test/Synthesis}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Synthesis/test_reactor_utils.py +0 -0
- {synkit-0.0.13/synkit/Chem/Cluster → synkit-0.0.15/Test/Vis}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/Test/Vis/test_embedding.py +0 -0
- {synkit-0.0.13/synkit/Chem/Fingerprint → synkit-0.0.15/Test}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/build-doc.sh +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/api.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/changelog.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/chem.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/conf.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/figures/aldol.png +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/figures/aldol_its.png +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/figures/context.png +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/figures/mtg.png +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/getting_started.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/index.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/io.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/reference.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/refs.bib +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/requirements.txt +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/rule.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/doc/synthesis.rst +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/environment.yml +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/pytest.sh +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/requirements.txt +0 -0
- {synkit-0.0.13/synkit/Chem/Molecule → synkit-0.0.15/synkit/Chem/Cluster}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Cluster/butina.py +0 -0
- {synkit-0.0.13/synkit/Data → synkit-0.0.15/synkit/Chem/Fingerprint}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
- {synkit-0.0.13/synkit/Graph/Context → synkit-0.0.15/synkit/Chem/Molecule}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Molecule/standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/aam_validator.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/balance_check.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/cleaning.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/deionize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/fix_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/neutralize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/standardize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/Reaction/tautomerize.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Chem/__init__.py +0 -0
- {synkit-0.0.13/synkit/Graph/Hyrogen → synkit-0.0.15/synkit/Data}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Data/gen_partial_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/canon_algs.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/canon_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Canon/nauty.py +0 -0
- {synkit-0.0.13/synkit/Graph/MTG → synkit-0.0.15/synkit/Graph/Context}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Context/hier_context.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Context/radius_expand.py +0 -0
- {synkit-0.0.13/synkit/Graph/Wildcard → synkit-0.0.15/synkit/Graph/Feature/Fingerprint}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/graph_descriptors.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/graph_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/graph_signature.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/hash_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/morgan_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/path_fps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Feature/wl_hash.py +0 -0
- {synkit-0.0.13/synkit/Rule/Apply → synkit-0.0.15/synkit/Graph/Hyrogen}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Hyrogen/hextend.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_builder.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_expand.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/its_relabel.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/normalize_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/ITS/partial_its.py +0 -0
- {synkit-0.0.13/synkit/Rule/Compose → synkit-0.0.15/synkit/Graph/MTG}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/MTG/group_comp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/MTG/groupoid.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/batch_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/graph_cluster.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/graph_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/graph_morphism.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/partial_matcher.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/sing.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Matcher/turbo_iso.py +0 -0
- {synkit-0.0.13/synkit/Rule/Modify → synkit-0.0.15/synkit/Graph/Wildcard}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/canon_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Graph/syn_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/data_io.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/data_process.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/debug.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/dg_to_gml.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/gml_to_nx.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/graph_to_mol.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/mol_to_graph.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/IO/smiles_to_id.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/CRN → synkit-0.0.15/synkit/Rule/Apply}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/reactor_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/retro_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Apply/rule_rbl.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/MSR → synkit-0.0.15/synkit/Rule/Compose}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/compose_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/rule_compose.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/rule_mapping.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/seq_comp.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Compose/valence_constrain.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/Metrics → synkit-0.0.15/synkit/Rule/Modify}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/implict_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/longest_path.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/molecule_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/prune_templates.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/rule_utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/Modify/strip_rule.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Rule/__init__.py +0 -0
- {synkit-0.0.13/synkit/Synthesis/Reactor → synkit-0.0.15/synkit/Synthesis/CRN}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/CRN/crn.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/CRN/dcrn.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/CRN/mod_crn.py +0 -0
- {synkit-0.0.13/synkit/Synthesis → synkit-0.0.15/synkit/Synthesis/MSR}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/MSR/multi_steps.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/MSR/path_finder.py +0 -0
- {synkit-0.0.13/synkit/Utils → synkit-0.0.15/synkit/Synthesis/Metrics}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Metrics/_base.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Metrics/_plot.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Metrics/_ranking.py +0 -0
- {synkit-0.0.13/synkit → synkit-0.0.15/synkit/Synthesis/Reactor}/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/Reactor/strategy.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Synthesis/reactor_utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Utils/utils.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/__init__.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/chemical_space.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/embedding.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/pdf_writer.py +0 -0
- {synkit-0.0.13 → synkit-0.0.15}/synkit/Vis/rxn_vis.py +0 -0
|
@@ -6,14 +6,10 @@
|
|
|
6
6
|
*.json
|
|
7
7
|
*.pkl.gz
|
|
8
8
|
*.pdf
|
|
9
|
-
out
|
|
10
|
-
summary
|
|
11
9
|
*.log
|
|
12
10
|
.coverage
|
|
13
11
|
dev/*
|
|
14
12
|
*.rdf
|
|
15
|
-
Data/Testcase/hydro/aam.json.gz
|
|
16
|
-
synkit/Chem/FG/*
|
|
17
13
|
test_syn_reactor.py
|
|
18
14
|
Data/Benchmark/*
|
|
19
15
|
# *.png
|
|
@@ -22,3 +18,6 @@ run.sh
|
|
|
22
18
|
docs/*
|
|
23
19
|
run_rdcanon.py
|
|
24
20
|
Data/Fragment/*
|
|
21
|
+
test_partial.py
|
|
22
|
+
Data/Benchmark/synthesis/*
|
|
23
|
+
Data/USPTO/*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synkit
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.15
|
|
4
4
|
Summary: Utility for reaction modeling using graph grammar
|
|
5
5
|
Project-URL: homepage, https://github.com/TieuLongPhan/SynKit
|
|
6
6
|
Project-URL: source, https://github.com/TieuLongPhan/SynKit
|
|
@@ -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
|
|
|
@@ -31,20 +31,20 @@ class TestITSConstruction(unittest.TestCase):
|
|
|
31
31
|
attributes = ITSConstruction().get_node_attributes_with_defaults(self.G, 1)
|
|
32
32
|
self.assertEqual(attributes, ("C", False, 2, 0, ["", ""]))
|
|
33
33
|
|
|
34
|
-
def test_add_edges_to_ITS(self):
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def test_add_standard_order_attribute(self):
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
# def test_add_edges_to_ITS(self):
|
|
35
|
+
# ITS = nx.Graph()
|
|
36
|
+
# ITS.add_node(1, element="C", aromatic=False, hcount=3, charge=0)
|
|
37
|
+
# ITS.add_node(2, element="C", aromatic=False, hcount=3, charge=0)
|
|
38
|
+
# new_ITS = ITSConstruction().add_edges_to_ITS(ITS, self.G, self.H)
|
|
39
|
+
# self.assertTrue(isinstance(new_ITS, nx.Graph))
|
|
40
|
+
# self.assertEqual(len(new_ITS.edges()), 1)
|
|
41
|
+
# self.assertEqual(new_ITS[1][2]["order"], (2, 1))
|
|
42
|
+
|
|
43
|
+
# def test_add_standard_order_attribute(self):
|
|
44
|
+
# graph = nx.Graph()
|
|
45
|
+
# graph.add_edge(1, 2, order=(1, 2))
|
|
46
|
+
# updated_graph = ITSConstruction().add_standard_order_attribute(graph)
|
|
47
|
+
# self.assertEqual(updated_graph[1][2]["standard_order"], -1)
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
if __name__ == "__main__":
|
|
@@ -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
|