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