synkit 1.4.0__tar.gz → 1.4.1b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synkit-1.4.0 → synkit-1.4.1b1}/.gitignore +1 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/PKG-INFO +1 -1
- {synkit-1.4.0 → synkit-1.4.1b1}/pyproject.toml +1 -1
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/its_expand.py +55 -48
- synkit-1.4.1b1/synkit/Graph/Mech/__init__.py +26 -0
- synkit-1.4.1b1/synkit/Graph/Mech/electron_accounting.py +309 -0
- synkit-1.4.1b1/synkit/Graph/Mech/lwg_editor.py +403 -0
- synkit-1.4.1b1/synkit/Graph/Mech/lwg_ops.py +313 -0
- synkit-1.4.1b1/synkit/Vis/__init__.py +89 -0
- synkit-1.4.1b1/synkit/Vis/crn/__init__.py +5 -0
- synkit-1.4.0/synkit/Vis/crn_vis.py → synkit-1.4.1b1/synkit/Vis/crn/visualizer.py +1 -1
- synkit-1.4.1b1/synkit/Vis/epd/__init__.py +20 -0
- synkit-1.4.1b1/synkit/Vis/epd/arrows.py +425 -0
- synkit-1.4.1b1/synkit/Vis/epd/chem.py +197 -0
- synkit-1.4.1b1/synkit/Vis/epd/constants.py +83 -0
- synkit-1.4.1b1/synkit/Vis/epd/layout.py +283 -0
- synkit-1.4.1b1/synkit/Vis/epd/mapping.py +51 -0
- synkit-1.4.1b1/synkit/Vis/epd/models.py +107 -0
- synkit-1.4.1b1/synkit/Vis/epd/render.py +528 -0
- synkit-1.4.1b1/synkit/Vis/epd/utils.py +108 -0
- synkit-1.4.1b1/synkit/Vis/epd/visualizer.py +826 -0
- synkit-1.4.1b1/synkit/Vis/its/__init__.py +19 -0
- synkit-1.4.0/synkit/Vis/its_drawer.py → synkit-1.4.1b1/synkit/Vis/its/drawer.py +3 -3
- synkit-1.4.1b1/synkit/Vis/molecule/__init__.py +31 -0
- synkit-1.4.0/synkit/Vis/reaction_drawer.py → synkit-1.4.1b1/synkit/Vis/molecule/reaction.py +1 -1
- synkit-1.4.1b1/synkit/Vis/mtg/__init__.py +17 -0
- synkit-1.4.0/synkit/Vis/mtg_drawer.py → synkit-1.4.1b1/synkit/Vis/mtg/drawer.py +1 -1
- synkit-1.4.1b1/synkit/Vis/reaction/__init__.py +19 -0
- synkit-1.4.0/synkit/Vis/rxn_vis.py → synkit-1.4.1b1/synkit/Vis/reaction/rxn.py +1 -1
- synkit-1.4.1b1/synkit/Vis/space/__init__.py +17 -0
- synkit-1.4.0/synkit/Vis/chemical_space.py → synkit-1.4.1b1/synkit/Vis/space/chemical.py +0 -3
- synkit-1.4.0/synkit/Graph/Mech/electron_accounting.py +0 -64
- synkit-1.4.0/synkit/Utils/__init__.py +0 -0
- synkit-1.4.0/synkit/Vis/__init__.py +0 -50
- synkit-1.4.0/synkit/Vis/vis_synedu/Vis/__init__.py +0 -7
- synkit-1.4.0/synkit/Vis/vis_synedu/Vis/dpo.py +0 -862
- synkit-1.4.0/synkit/Vis/vis_synedu/rxn_vis.py +0 -382
- synkit-1.4.0/synkit/Vis/vis_synedu/vis.py +0 -501
- {synkit-1.4.0 → synkit-1.4.1b1}/LICENSE +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/README.md +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/DAG/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/DAG/crn.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/DAG/mod_crn.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/DAG/syncrn.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/abstract.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/arity.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/builder.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/derivation.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/flattener.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/keys.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/mixtures.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/smiles.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/state.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/strategy.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Construct/worker.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Pathway/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Pathway/_adapter.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Pathway/pathfinder.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Pathway/reachability.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Pathway/realizability.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Petrinet/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Petrinet/analyzer.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Petrinet/net.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Petrinet/persistence.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Petrinet/semiflows.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Petrinet/structure.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Props/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Props/dynamics.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Props/helper.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Props/stoich.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Props/thermo.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Query/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Query/kegg_api.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Query/kegg_extract.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Query/kegg_impute.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Query/kegg_parse.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Structure/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Structure/reaction.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Structure/rule.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Structure/species.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Structure/syncrn.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/_common.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/_ir.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/automorphism.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/canon.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/isomorphism.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/symmetry.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Symmetry/wl_canon.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Visualize/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Visualize/crn_vis.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Visualize/labels.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Visualize/layout.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Visualize/palette.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Visualize/validation.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/Visualize/vis.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Structure/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Structure/backend.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Structure/conversion.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Structure/hyperedge.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Structure/hypergraph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Structure/rxn.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Symmetry/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Symmetry/automorphism.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Symmetry/canon.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/Symmetry/wl_canon.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/configs/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/configs/loader.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/configs/models.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/constants.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/crn_formula.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/deficiency.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/enumerator.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/exceptions.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/explorer.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/helpers.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/injectivity.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/motif.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/network.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/pathway.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/properties.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/reaction.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/utils.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/CRN/dev_crn/viz.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Cluster/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Cluster/butina.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Fingerprint/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Fingerprint/transformation_fp.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Molecule/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Molecule/atom_features.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Molecule/descriptors.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Molecule/formula.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Molecule/graph_annotator.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Molecule/standardize.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Molecule/valence.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/Mapper/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/Mapper/wl_mapper.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/aam_validator.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/balance_check.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/cleaning.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/deionize.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/fix_aam.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/neutralize.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/radical_wildcard.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/standardize.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/Reaction/tautomerize.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Chem/utils.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Data/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Data/gen_partial_aam.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Canon/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Canon/canon_algs.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Canon/canon_graph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Canon/nauty.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Context/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Context/hier_context.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Context/radius_expand.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/FG/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/FG/api.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/FG/audit.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/FG/catalog.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/FG/detector.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/FG/model.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/FG/ring_system.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/Descriptors/topology.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/Fingerprint/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/graph_descriptors.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/graph_fps.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/graph_signature.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/hash_fps.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/morgan_fps.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/path_fps.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Feature/wl_hash.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Hyrogen/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Hyrogen/_misc.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Hyrogen/hextend.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/its_builder.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/its_construction.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/its_decompose.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/its_destruction.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/its_relabel.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/its_reverter.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/normalize_aam.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/partial_its.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/ITS/rc_extractor.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/MTG/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/MTG/mcs_matcher.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/MTG/mtg.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/MTG/mtg_explore.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/MTG/utils.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/approx_mcs.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/auto_est.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/automorphism.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/batch_cluster.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/dedup_matches.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/graph_cluster.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/graph_matcher.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/graph_morphism.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/orbit.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/partial_matcher.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/sing.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/subgraph_matcher.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/turbo_iso.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Matcher/wl_sel.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Mech/conversion.py +0 -0
- {synkit-1.4.0/synkit/Graph/Mech → synkit-1.4.1b1/synkit/Graph/Wildcard}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Wildcard/graph_wc.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Wildcard/its_merge.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Wildcard/radwc.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Wildcard/wc_matcher.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/Wildcard/wildcard.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/canon_graph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/syn_graph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Graph/utils.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/chem_converter.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/combinatorial/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/combinatorial/gml_to_graph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/combinatorial/graph_to_gml.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/combinatorial/graph_to_smarts.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/combinatorial/smarts_expander.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/combinatorial/smarts_generalizer.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/combinatorial/smarts_to_graph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/data_io.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/data_process.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/debug.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/dg_to_gml.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/gml_to_nx.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/graph_to_mol.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/mol_to_graph.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/nx_to_gml.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/IO/smiles_to_id.py +0 -0
- {synkit-1.4.0/synkit/Graph/Wildcard → synkit-1.4.1b1/synkit/Rule/Apply}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Apply/reactor_rule.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Apply/retro_reactor.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Apply/rule_matcher.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Apply/rule_rbl.py +0 -0
- {synkit-1.4.0/synkit/Rule/Apply → synkit-1.4.1b1/synkit/Rule/Compose}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Compose/compose_rule.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Compose/rule_compose.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Compose/rule_mapping.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Compose/seq_comp.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Compose/valence_constrain.py +0 -0
- {synkit-1.4.0/synkit/Rule/Compose → synkit-1.4.1b1/synkit/Rule/Modify}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Modify/implict_rule.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Modify/longest_path.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Modify/molecule_rule.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Modify/prune_templates.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Modify/rule_utils.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/Modify/strip_rule.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Rule/syn_rule.py +0 -0
- {synkit-1.4.0/synkit/Rule/Modify → synkit-1.4.1b1/synkit/Synthesis/MSR}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/MSR/multi_steps.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/MSR/path_finder.py +0 -0
- {synkit-1.4.0/synkit/Synthesis/MSR → synkit-1.4.1b1/synkit/Synthesis/Metrics}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Metrics/_base.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Metrics/_plot.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Metrics/_ranking.py +0 -0
- {synkit-1.4.0/synkit/Synthesis/Metrics → synkit-1.4.1b1/synkit/Synthesis/Reactor}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/batch_reactor.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/benchmark.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/imba_engine.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/post_syn.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/rbl_engine.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/strategy.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/Reactor/syn_reactor.py +0 -0
- {synkit-1.4.0/synkit/Synthesis/Reactor → synkit-1.4.1b1/synkit/Synthesis}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Synthesis/reactor_utils.py +0 -0
- {synkit-1.4.0/synkit/Synthesis → synkit-1.4.1b1/synkit/Utils}/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Utils/utils.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Vis/graph_visualizer.py +0 -0
- /synkit-1.4.0/synkit/Vis/molecule_drawer.py → /synkit-1.4.1b1/synkit/Vis/molecule/drawer.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Vis/pdf_writer.py +0 -0
- /synkit-1.4.0/synkit/Vis/rule_vis.py → /synkit-1.4.1b1/synkit/Vis/reaction/rule.py +0 -0
- {synkit-1.4.0/synkit/Vis → synkit-1.4.1b1/synkit/Vis/space}/embedding.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Vis/visual_drawer.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/Vis/visual_model.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/__init__.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/examples.py +0 -0
- {synkit-1.4.0 → synkit-1.4.1b1}/synkit/version.py +0 -0
|
@@ -100,31 +100,19 @@ class ITSExpand:
|
|
|
100
100
|
)
|
|
101
101
|
|
|
102
102
|
@staticmethod
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
n_nodes: int,
|
|
106
|
-
) -> None:
|
|
107
|
-
"""Validate that atom-map numbers can be used as contiguous node IDs.
|
|
108
|
-
|
|
109
|
-
In the side graph, we want final node IDs to remain exactly ``1..N``.
|
|
110
|
-
Therefore, a mapped atom can only be moved to its atom-map number if
|
|
111
|
-
that number is within ``1..N``.
|
|
103
|
+
def _validate_positive_atom_maps(atom_maps: list[int]) -> None:
|
|
104
|
+
"""Validate that atom-map numbers can be used as node IDs.
|
|
112
105
|
|
|
113
106
|
:param atom_maps: Nonzero atom-map numbers.
|
|
114
107
|
:type atom_maps: list[int]
|
|
115
|
-
:
|
|
116
|
-
:type n_nodes: int
|
|
117
|
-
:raises ValueError: If any atom-map number is outside ``1..N``.
|
|
108
|
+
:raises ValueError: If any atom-map number is not positive.
|
|
118
109
|
"""
|
|
119
|
-
bad_targets = [
|
|
120
|
-
atom_map for atom_map in atom_maps if atom_map < 1 or atom_map > n_nodes
|
|
121
|
-
]
|
|
110
|
+
bad_targets = [atom_map for atom_map in atom_maps if atom_map < 1]
|
|
122
111
|
|
|
123
112
|
if bad_targets:
|
|
124
113
|
raise ValueError(
|
|
125
|
-
"Cannot
|
|
126
|
-
f"
|
|
127
|
-
f"outside 1..{n_nodes}: {bad_targets}"
|
|
114
|
+
"Cannot use non-positive atom_map values as node ids. "
|
|
115
|
+
f"Invalid atom maps: {bad_targets}"
|
|
128
116
|
)
|
|
129
117
|
|
|
130
118
|
@staticmethod
|
|
@@ -153,17 +141,33 @@ class ITSExpand:
|
|
|
153
141
|
|
|
154
142
|
return mapping, used_ids
|
|
155
143
|
|
|
144
|
+
@staticmethod
|
|
145
|
+
def _next_free_positive_id(used_ids: set[int], start: int = 1) -> int:
|
|
146
|
+
"""Return the smallest positive integer not present in ``used_ids``.
|
|
147
|
+
|
|
148
|
+
:param used_ids: Node IDs already occupied by mapped atoms.
|
|
149
|
+
:type used_ids: set[int]
|
|
150
|
+
:param start: First candidate ID.
|
|
151
|
+
:type start: int
|
|
152
|
+
:returns: Available positive node ID.
|
|
153
|
+
:rtype: int
|
|
154
|
+
"""
|
|
155
|
+
candidate = max(1, start)
|
|
156
|
+
while candidate in used_ids:
|
|
157
|
+
candidate += 1
|
|
158
|
+
return candidate
|
|
159
|
+
|
|
156
160
|
@staticmethod
|
|
157
161
|
def _assign_unmapped_nodes(
|
|
158
162
|
graph,
|
|
159
163
|
mapping: dict,
|
|
160
164
|
used_ids: set[int],
|
|
161
165
|
) -> dict:
|
|
162
|
-
"""Assign unmapped atoms while
|
|
166
|
+
"""Assign unmapped atoms while avoiding mapped atom-map IDs.
|
|
163
167
|
|
|
164
168
|
Unmapped atoms keep their original node ID when possible. If an
|
|
165
|
-
unmapped atom's node ID conflicts with a mapped atom
|
|
166
|
-
moved into
|
|
169
|
+
unmapped atom's node ID conflicts with a mapped atom-map target, it is
|
|
170
|
+
moved into the smallest unused positive ID.
|
|
167
171
|
|
|
168
172
|
:param graph: Molecular side graph.
|
|
169
173
|
:type graph: networkx.Graph
|
|
@@ -174,9 +178,7 @@ class ITSExpand:
|
|
|
174
178
|
:returns: Complete old-node to new-node mapping.
|
|
175
179
|
:rtype: dict
|
|
176
180
|
"""
|
|
177
|
-
|
|
178
|
-
free_ids = set(range(1, n_nodes + 1)) - used_ids
|
|
179
|
-
pending_unmapped = []
|
|
181
|
+
next_candidate = 1
|
|
180
182
|
|
|
181
183
|
for node, data in graph.nodes(data=True):
|
|
182
184
|
atom_map = ITSExpand._atom_map(data)
|
|
@@ -184,36 +186,39 @@ class ITSExpand:
|
|
|
184
186
|
if atom_map != 0:
|
|
185
187
|
continue
|
|
186
188
|
|
|
187
|
-
if isinstance(node, int) and node in
|
|
189
|
+
if isinstance(node, int) and node > 0 and node not in used_ids:
|
|
188
190
|
mapping[node] = node
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
used_ids.add(node)
|
|
192
|
+
next_candidate = max(next_candidate, node + 1)
|
|
193
|
+
continue
|
|
192
194
|
|
|
193
|
-
|
|
194
|
-
mapping[
|
|
195
|
+
new_node = ITSExpand._next_free_positive_id(used_ids, next_candidate)
|
|
196
|
+
mapping[node] = new_node
|
|
197
|
+
used_ids.add(new_node)
|
|
198
|
+
next_candidate = new_node + 1
|
|
195
199
|
|
|
196
200
|
return mapping
|
|
197
201
|
|
|
198
202
|
@staticmethod
|
|
199
|
-
def
|
|
200
|
-
"""Validate that
|
|
203
|
+
def _validate_complete_unique_mapping(mapping: dict, n_nodes: int) -> None:
|
|
204
|
+
"""Validate that every source node maps to a unique target node.
|
|
201
205
|
|
|
202
206
|
:param mapping: Old-node to new-node mapping.
|
|
203
207
|
:type mapping: dict
|
|
204
208
|
:param n_nodes: Number of nodes in the graph.
|
|
205
209
|
:type n_nodes: int
|
|
206
|
-
:raises RuntimeError: If the
|
|
210
|
+
:raises RuntimeError: If the mapping is incomplete or has collisions.
|
|
207
211
|
"""
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
if len(mapping) != n_nodes:
|
|
213
|
+
raise RuntimeError(
|
|
214
|
+
f"Reindexing failed. Expected {n_nodes} mapped nodes; "
|
|
215
|
+
f"got {len(mapping)}."
|
|
216
|
+
)
|
|
210
217
|
|
|
211
|
-
if
|
|
212
|
-
missing = sorted(expected_ids - actual_ids)
|
|
213
|
-
extra = sorted(actual_ids - expected_ids)
|
|
218
|
+
if len(set(mapping.values())) != n_nodes:
|
|
214
219
|
raise RuntimeError(
|
|
215
|
-
|
|
216
|
-
|
|
220
|
+
"Reindexing failed. Multiple source nodes map to the same "
|
|
221
|
+
"target node id."
|
|
217
222
|
)
|
|
218
223
|
|
|
219
224
|
@staticmethod
|
|
@@ -224,25 +229,25 @@ class ITSExpand:
|
|
|
224
229
|
|
|
225
230
|
1. Every atom with ``atom_map != 0`` is assigned to node ID
|
|
226
231
|
``atom_map``.
|
|
227
|
-
2.
|
|
232
|
+
2. Every unmapped atom is assigned a unique positive node ID that does
|
|
233
|
+
not collide with preserved atom-map IDs.
|
|
228
234
|
|
|
229
235
|
:param graph: Molecular side graph.
|
|
230
236
|
:type graph: networkx.Graph
|
|
231
237
|
:returns: Old-node to new-node mapping.
|
|
232
238
|
:rtype: dict
|
|
233
|
-
:raises ValueError: If atom-map values are duplicated or
|
|
234
|
-
with contiguous node IDs.
|
|
239
|
+
:raises ValueError: If atom-map values are duplicated or non-positive.
|
|
235
240
|
"""
|
|
236
241
|
n_nodes = graph.number_of_nodes()
|
|
237
242
|
atom_maps = ITSExpand._nonzero_atom_maps(graph)
|
|
238
243
|
|
|
239
244
|
ITSExpand._validate_unique_atom_maps(atom_maps)
|
|
240
|
-
ITSExpand.
|
|
245
|
+
ITSExpand._validate_positive_atom_maps(atom_maps)
|
|
241
246
|
|
|
242
247
|
mapping, used_ids = ITSExpand._assign_mapped_nodes(graph)
|
|
243
248
|
mapping = ITSExpand._assign_unmapped_nodes(graph, mapping, used_ids)
|
|
244
249
|
|
|
245
|
-
ITSExpand.
|
|
250
|
+
ITSExpand._validate_complete_unique_mapping(mapping, n_nodes)
|
|
246
251
|
|
|
247
252
|
return mapping
|
|
248
253
|
|
|
@@ -317,7 +322,9 @@ class ITSExpand:
|
|
|
317
322
|
def reindex_side_graph_by_atom_map(graph):
|
|
318
323
|
"""Reindex a side graph so mapped atoms use ``atom_map`` as node ID.
|
|
319
324
|
|
|
320
|
-
The returned graph keeps node IDs
|
|
325
|
+
The returned graph keeps mapped atom node IDs equal to their atom-map
|
|
326
|
+
values. Unmapped atom node IDs remain compact where possible, but may
|
|
327
|
+
become sparse to avoid collisions with preserved atom maps.
|
|
321
328
|
|
|
322
329
|
This is useful because the reaction-center graph produced by
|
|
323
330
|
``ITSConstruction().ITSGraph(...)`` uses atom-map numbers as node IDs,
|
|
@@ -344,10 +351,10 @@ class ITSExpand:
|
|
|
344
351
|
|
|
345
352
|
:param graph: Molecular side graph.
|
|
346
353
|
:type graph: networkx.Graph
|
|
347
|
-
:returns: Reindexed side graph with
|
|
354
|
+
:returns: Reindexed side graph with preserved mapped node IDs.
|
|
348
355
|
:rtype: networkx.Graph
|
|
349
356
|
:raises ValueError: If atom-map numbers cannot be safely used as node
|
|
350
|
-
IDs
|
|
357
|
+
IDs.
|
|
351
358
|
"""
|
|
352
359
|
mapping = ITSExpand._build_side_graph_reindex_mapping(graph)
|
|
353
360
|
return ITSExpand._rebuild_graph_with_mapping(graph, mapping)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from synkit.Graph.Mech.lwg_editor import LWGEditResult, LWGEditor, LWGStepReport
|
|
7
|
+
|
|
8
|
+
__all__ = ["LWGEditor", "LWGEditResult", "LWGStepReport"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __getattr__(name: str):
|
|
12
|
+
if name in __all__:
|
|
13
|
+
from synkit.Graph.Mech.lwg_editor import (
|
|
14
|
+
LWGEditResult,
|
|
15
|
+
LWGEditor,
|
|
16
|
+
LWGStepReport,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
exports = {
|
|
20
|
+
"LWGEditor": LWGEditor,
|
|
21
|
+
"LWGEditResult": LWGEditResult,
|
|
22
|
+
"LWGStepReport": LWGStepReport,
|
|
23
|
+
}
|
|
24
|
+
return exports[name]
|
|
25
|
+
|
|
26
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import networkx as nx
|
|
7
|
+
from rdkit import Chem
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class ChargeRefresh:
|
|
12
|
+
"""VE/NBE/B charge refresh report for one atom map.
|
|
13
|
+
|
|
14
|
+
:param atom_map: Refreshed atom map.
|
|
15
|
+
:type atom_map: int
|
|
16
|
+
:param node: NetworkX node corresponding to ``atom_map``.
|
|
17
|
+
:type node: Any
|
|
18
|
+
:param previous_charge: Charge before refresh.
|
|
19
|
+
:type previous_charge: int | float
|
|
20
|
+
:param refreshed_charge: Charge computed by ``VE - NBE - B``.
|
|
21
|
+
:type refreshed_charge: int | float
|
|
22
|
+
:param valence_electrons: Atom valence electron count used as ``VE``.
|
|
23
|
+
:type valence_electrons: float
|
|
24
|
+
:param nonbonding_electrons: Nonbonding electron count used as ``NBE``.
|
|
25
|
+
:type nonbonding_electrons: float
|
|
26
|
+
:param bond_electrons: Bond electron count used as ``B``.
|
|
27
|
+
:type bond_electrons: float
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
atom_map: int
|
|
31
|
+
node: Any
|
|
32
|
+
previous_charge: int | float
|
|
33
|
+
refreshed_charge: int | float
|
|
34
|
+
valence_electrons: float
|
|
35
|
+
nonbonding_electrons: float
|
|
36
|
+
bond_electrons: float
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class ChargeEdit:
|
|
41
|
+
"""Incremental local charge edit for one atom map.
|
|
42
|
+
|
|
43
|
+
:param atom_map: Edited atom map.
|
|
44
|
+
:type atom_map: int
|
|
45
|
+
:param node: NetworkX node corresponding to ``atom_map``.
|
|
46
|
+
:type node: Any
|
|
47
|
+
:param delta: Applied formal-charge delta.
|
|
48
|
+
:type delta: int | float
|
|
49
|
+
:param previous_charge: Charge before the edit.
|
|
50
|
+
:type previous_charge: int | float
|
|
51
|
+
:param new_charge: Charge after the edit.
|
|
52
|
+
:type new_charge: int | float
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
atom_map: int
|
|
56
|
+
node: Any
|
|
57
|
+
delta: int | float
|
|
58
|
+
previous_charge: int | float
|
|
59
|
+
new_charge: int | float
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def bond_order_sum(graph: nx.Graph, node: Any) -> float:
|
|
63
|
+
"""Return the sigma-plus-pi bond-order sum around one node.
|
|
64
|
+
|
|
65
|
+
:param graph: Lewis graph.
|
|
66
|
+
:type graph: nx.Graph
|
|
67
|
+
:param node: Node whose incident bond orders should be summed.
|
|
68
|
+
:type node: Any
|
|
69
|
+
:returns: Sum of incident ``sigma_order + pi_order`` values.
|
|
70
|
+
:rtype: float
|
|
71
|
+
"""
|
|
72
|
+
total = 0.0
|
|
73
|
+
for _, _, data in graph.edges(node, data=True):
|
|
74
|
+
total += float(data.get("sigma_order", 0.0)) + float(data.get("pi_order", 0.0))
|
|
75
|
+
return total
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def recompute_charge(graph: nx.Graph, node: Any) -> int | float:
|
|
79
|
+
"""Recompute formal charge from stored electron-state fields.
|
|
80
|
+
|
|
81
|
+
This diagnostic helper uses ``Formal Charge = VE - NBE - B``. The
|
|
82
|
+
:class:`synkit.Graph.Mech.lwg_editor.LWGEditor` production edit path uses
|
|
83
|
+
local charge deltas instead.
|
|
84
|
+
|
|
85
|
+
:param graph: Lewis graph.
|
|
86
|
+
:type graph: nx.Graph
|
|
87
|
+
:param node: Node whose charge should be recomputed.
|
|
88
|
+
:type node: Any
|
|
89
|
+
:returns: Recomputed formal charge.
|
|
90
|
+
:rtype: int | float
|
|
91
|
+
"""
|
|
92
|
+
attrs = graph.nodes[node]
|
|
93
|
+
charge = (
|
|
94
|
+
float(attrs["valence_electrons"])
|
|
95
|
+
- nonbonding_electron_count(graph, node)
|
|
96
|
+
- bond_electron_count(graph, node)
|
|
97
|
+
)
|
|
98
|
+
return int(charge) if charge.is_integer() else charge
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def nonbonding_electron_count(graph: nx.Graph, node: Any) -> float:
|
|
102
|
+
"""Return NBE for one atom from stored lone-pair/radical fields.
|
|
103
|
+
|
|
104
|
+
:param graph: Lewis graph.
|
|
105
|
+
:type graph: nx.Graph
|
|
106
|
+
:param node: Node whose nonbonding electrons should be counted.
|
|
107
|
+
:type node: Any
|
|
108
|
+
:returns: ``2 * lone_pairs + radical``.
|
|
109
|
+
:rtype: float
|
|
110
|
+
"""
|
|
111
|
+
attrs = graph.nodes[node]
|
|
112
|
+
return 2 * float(attrs.get("lone_pairs", 0)) + float(attrs.get("radical", 0))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def bond_electron_count(graph: nx.Graph, node: Any) -> float:
|
|
116
|
+
"""Return B for one atom, including implicit hydrogen bonds.
|
|
117
|
+
|
|
118
|
+
:param graph: Lewis graph.
|
|
119
|
+
:type graph: nx.Graph
|
|
120
|
+
:param node: Node whose bonding electrons should be counted.
|
|
121
|
+
:type node: Any
|
|
122
|
+
:returns: ``hcount`` plus incident sigma/pi bond-order sum.
|
|
123
|
+
:rtype: float
|
|
124
|
+
"""
|
|
125
|
+
attrs = graph.nodes[node]
|
|
126
|
+
return float(attrs.get("hcount", 0)) + bond_order_sum(graph, node)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def atom_map_to_node(graph: nx.Graph) -> dict[int, Any]:
|
|
130
|
+
"""Build an atom-map to node lookup, ignoring unmapped atoms.
|
|
131
|
+
|
|
132
|
+
:param graph: Graph with node ``atom_map`` attributes.
|
|
133
|
+
:type graph: nx.Graph
|
|
134
|
+
:returns: Mapping from atom-map number to graph node.
|
|
135
|
+
:rtype: dict[int, Any]
|
|
136
|
+
:raises ValueError: If a nonzero atom map appears on multiple nodes.
|
|
137
|
+
"""
|
|
138
|
+
lookup: dict[int, Any] = {}
|
|
139
|
+
duplicates: dict[int, list[Any]] = {}
|
|
140
|
+
|
|
141
|
+
for node, attrs in graph.nodes(data=True):
|
|
142
|
+
atom_map = attrs.get("atom_map", node)
|
|
143
|
+
if atom_map in (None, 0, "0"):
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
atom_map_int = int(atom_map)
|
|
147
|
+
if atom_map_int in lookup:
|
|
148
|
+
duplicates.setdefault(atom_map_int, [lookup[atom_map_int]]).append(node)
|
|
149
|
+
continue
|
|
150
|
+
lookup[atom_map_int] = node
|
|
151
|
+
|
|
152
|
+
if duplicates:
|
|
153
|
+
raise ValueError(f"Duplicate atom maps in graph: {duplicates}")
|
|
154
|
+
|
|
155
|
+
return lookup
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def refresh_changed_atom_charge(
|
|
159
|
+
graph: nx.Graph,
|
|
160
|
+
atom_maps: list[int] | tuple[int, ...] | set[int],
|
|
161
|
+
) -> list[ChargeRefresh]:
|
|
162
|
+
"""Refresh formal charges only for selected atom maps.
|
|
163
|
+
|
|
164
|
+
Uses ``Formal Charge = VE - NBE - B`` where ``B`` includes implicit
|
|
165
|
+
hydrogens stored as ``hcount`` plus incident sigma/pi order.
|
|
166
|
+
|
|
167
|
+
:param graph: Lewis graph to mutate.
|
|
168
|
+
:type graph: nx.Graph
|
|
169
|
+
:param atom_maps: Atom maps whose ``charge`` field should be recomputed.
|
|
170
|
+
:type atom_maps: list[int] | tuple[int, ...] | set[int]
|
|
171
|
+
:returns: Per-atom refresh reports.
|
|
172
|
+
:rtype: list[ChargeRefresh]
|
|
173
|
+
:raises ValueError: If any atom map is missing or lacks
|
|
174
|
+
``valence_electrons``.
|
|
175
|
+
"""
|
|
176
|
+
lookup = atom_map_to_node(graph)
|
|
177
|
+
reports: list[ChargeRefresh] = []
|
|
178
|
+
|
|
179
|
+
for atom_map in sorted({int(value) for value in atom_maps}):
|
|
180
|
+
if atom_map not in lookup:
|
|
181
|
+
raise ValueError(f"Atom map {atom_map} is missing from graph.")
|
|
182
|
+
|
|
183
|
+
node = lookup[atom_map]
|
|
184
|
+
attrs = graph.nodes[node]
|
|
185
|
+
|
|
186
|
+
if "valence_electrons" not in attrs:
|
|
187
|
+
raise ValueError(f"Atom map {atom_map} has no valence_electrons field.")
|
|
188
|
+
|
|
189
|
+
previous_charge = attrs.get("charge", 0)
|
|
190
|
+
refreshed_charge = recompute_charge(graph, node)
|
|
191
|
+
attrs["charge"] = refreshed_charge
|
|
192
|
+
attrs["bond_order_sum"] = bond_order_sum(graph, node)
|
|
193
|
+
attrs["recomputed_charge"] = refreshed_charge
|
|
194
|
+
attrs["charge_mismatch"] = False
|
|
195
|
+
|
|
196
|
+
reports.append(
|
|
197
|
+
ChargeRefresh(
|
|
198
|
+
atom_map=atom_map,
|
|
199
|
+
node=node,
|
|
200
|
+
previous_charge=previous_charge,
|
|
201
|
+
refreshed_charge=refreshed_charge,
|
|
202
|
+
valence_electrons=float(attrs["valence_electrons"]),
|
|
203
|
+
nonbonding_electrons=nonbonding_electron_count(graph, node),
|
|
204
|
+
bond_electrons=bond_electron_count(graph, node),
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return reports
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def change_atom_charge(
|
|
212
|
+
graph: nx.Graph,
|
|
213
|
+
atom_maps: list[int] | tuple[int, ...] | set[int],
|
|
214
|
+
*,
|
|
215
|
+
delta: int | float,
|
|
216
|
+
) -> list[ChargeEdit]:
|
|
217
|
+
"""Apply a local formal-charge delta to selected atom maps.
|
|
218
|
+
|
|
219
|
+
This is the charge update primitive used by
|
|
220
|
+
:class:`synkit.Graph.Mech.lwg_editor.LWGEditor`.
|
|
221
|
+
|
|
222
|
+
:param graph: Lewis graph to mutate.
|
|
223
|
+
:type graph: nx.Graph
|
|
224
|
+
:param atom_maps: Atom maps whose formal charge should be edited.
|
|
225
|
+
:type atom_maps: list[int] | tuple[int, ...] | set[int]
|
|
226
|
+
:param delta: Formal-charge delta to add to each selected atom.
|
|
227
|
+
:type delta: int | float
|
|
228
|
+
:returns: Per-atom charge edit reports.
|
|
229
|
+
:rtype: list[ChargeEdit]
|
|
230
|
+
:raises ValueError: If any atom map is missing.
|
|
231
|
+
"""
|
|
232
|
+
lookup = atom_map_to_node(graph)
|
|
233
|
+
reports: list[ChargeEdit] = []
|
|
234
|
+
|
|
235
|
+
for atom_map in [int(value) for value in atom_maps]:
|
|
236
|
+
if atom_map not in lookup:
|
|
237
|
+
raise ValueError(f"Atom map {atom_map} is missing from graph.")
|
|
238
|
+
|
|
239
|
+
node = lookup[atom_map]
|
|
240
|
+
attrs = graph.nodes[node]
|
|
241
|
+
previous_charge = attrs.get("charge", 0)
|
|
242
|
+
new_charge = previous_charge + delta
|
|
243
|
+
if isinstance(new_charge, float) and new_charge.is_integer():
|
|
244
|
+
new_charge = int(new_charge)
|
|
245
|
+
|
|
246
|
+
attrs["charge"] = new_charge
|
|
247
|
+
reports.append(
|
|
248
|
+
ChargeEdit(
|
|
249
|
+
atom_map=atom_map,
|
|
250
|
+
node=node,
|
|
251
|
+
delta=delta,
|
|
252
|
+
previous_charge=previous_charge,
|
|
253
|
+
new_charge=new_charge,
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
return reports
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def refresh_electron_fields(graph: nx.Graph, *, in_place: bool = False) -> nx.Graph:
|
|
261
|
+
"""Refresh derived electron bookkeeping on a molecular graph.
|
|
262
|
+
|
|
263
|
+
The graph is expected to store scalar ``sigma_order`` and ``pi_order`` edge
|
|
264
|
+
fields plus node-level electron state. Presentation-facing ``order`` is not
|
|
265
|
+
rewritten here; RDKit reconstruction remains responsible for aromatic
|
|
266
|
+
re-perception at the product boundary.
|
|
267
|
+
|
|
268
|
+
:param graph: Lewis graph to refresh.
|
|
269
|
+
:type graph: nx.Graph
|
|
270
|
+
:param in_place: If ``True``, mutate ``graph`` directly.
|
|
271
|
+
:type in_place: bool
|
|
272
|
+
:returns: Graph with refreshed ``kekule_order``, ``bond_order_sum``,
|
|
273
|
+
``recomputed_charge``, and ``charge_mismatch`` fields.
|
|
274
|
+
:rtype: nx.Graph
|
|
275
|
+
"""
|
|
276
|
+
target = graph if in_place else graph.copy()
|
|
277
|
+
|
|
278
|
+
for _, _, data in target.edges(data=True):
|
|
279
|
+
sigma = float(data.get("sigma_order", 0.0))
|
|
280
|
+
pi = float(data.get("pi_order", 0.0))
|
|
281
|
+
data["kekule_order"] = sigma + pi
|
|
282
|
+
|
|
283
|
+
for node, attrs in target.nodes(data=True):
|
|
284
|
+
attrs["bond_order_sum"] = bond_order_sum(target, node)
|
|
285
|
+
if "valence_electrons" not in attrs:
|
|
286
|
+
continue
|
|
287
|
+
attrs["recomputed_charge"] = recompute_charge(target, node)
|
|
288
|
+
represented_charge = float(attrs.get("charge", 0))
|
|
289
|
+
attrs["charge_mismatch"] = represented_charge != attrs["recomputed_charge"]
|
|
290
|
+
|
|
291
|
+
return target
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def graph_to_sanitized_kekule_mol(graph: nx.Graph) -> Chem.Mol:
|
|
295
|
+
"""Reconstruct a product from ``kekule_order`` and let RDKit sanitize it.
|
|
296
|
+
|
|
297
|
+
:param graph: Lewis graph with ``kekule_order`` edge fields.
|
|
298
|
+
:type graph: nx.Graph
|
|
299
|
+
:returns: Sanitized RDKit molecule.
|
|
300
|
+
:rtype: Chem.Mol
|
|
301
|
+
"""
|
|
302
|
+
from synkit.IO.graph_to_mol import GraphToMol
|
|
303
|
+
|
|
304
|
+
refreshed = refresh_electron_fields(graph)
|
|
305
|
+
return GraphToMol(edge_attributes={"order": "kekule_order"}).graph_to_mol(
|
|
306
|
+
refreshed,
|
|
307
|
+
sanitize=True,
|
|
308
|
+
use_h_count=True,
|
|
309
|
+
)
|