synkit 1.4.2b1__tar.gz → 1.5.0__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.2b1 → synkit-1.5.0}/.gitignore +0 -3
- {synkit-1.4.2b1 → synkit-1.5.0}/PKG-INFO +2 -1
- {synkit-1.4.2b1 → synkit-1.5.0}/pyproject.toml +2 -1
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Fingerprint/transformation_fp.py +8 -4
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/__init__.py +2 -1
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/tautomerize.py +1 -4
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/its_expand.py +115 -46
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/subgraph_matcher.py +0 -4
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Mech/conversion.py +162 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Mech/electron_accounting.py +25 -138
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Wildcard/its_merge.py +6 -6
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/__init__.py +1 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/chem_converter.py +17 -22
- synkit-1.5.0/synkit/IO/conversion.py +42 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/rbl_engine.py +258 -859
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/syn_reactor.py +4 -23
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/render.py +29 -14
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/visualizer.py +24 -12
- synkit-1.5.0/synkit/Vis/its_drawer.py +615 -0
- synkit-1.5.0/synkit/Vis/molecule_drawer.py +565 -0
- synkit-1.5.0/synkit/Vis/mtg_drawer.py +769 -0
- synkit-1.5.0/synkit/Vis/reaction_drawer.py +285 -0
- synkit-1.5.0/synkit/Vis/vis_synedu/Vis/__init__.py +7 -0
- synkit-1.5.0/synkit/Vis/vis_synedu/Vis/dpo.py +862 -0
- synkit-1.5.0/synkit/Vis/vis_synedu/rxn_vis.py +382 -0
- synkit-1.5.0/synkit/Vis/vis_synedu/vis.py +501 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/LICENSE +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/README.md +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/DAG/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/DAG/crn.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/DAG/mod_crn.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/DAG/syncrn.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/abstract.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/arity.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/builder.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/derivation.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/flattener.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/keys.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/mixtures.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/smiles.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/state.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/strategy.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Construct/worker.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Pathway/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Pathway/_adapter.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Pathway/pathfinder.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Pathway/reachability.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Pathway/realizability.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Petrinet/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Petrinet/analyzer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Petrinet/net.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Petrinet/persistence.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Petrinet/semiflows.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Petrinet/structure.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Props/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Props/dynamics.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Props/helper.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Props/stoich.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Props/thermo.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Query/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Query/kegg_api.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Query/kegg_extract.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Query/kegg_impute.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Query/kegg_parse.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Structure/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Structure/reaction.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Structure/rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Structure/species.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Structure/syncrn.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/_common.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/_ir.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/automorphism.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/canon.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/isomorphism.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/symmetry.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Symmetry/wl_canon.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Visualize/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Visualize/crn_vis.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Visualize/labels.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Visualize/layout.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Visualize/palette.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Visualize/validation.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/Visualize/vis.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Structure/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Structure/backend.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Structure/conversion.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Structure/hyperedge.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Structure/hypergraph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Structure/rxn.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Symmetry/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Symmetry/automorphism.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Symmetry/canon.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/Symmetry/wl_canon.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/configs/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/configs/loader.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/configs/models.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/constants.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/crn_formula.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/deficiency.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/enumerator.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/exceptions.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/explorer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/helpers.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/injectivity.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/motif.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/network.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/pathway.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/properties.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/reaction.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/CRN/dev_crn/viz.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Cluster/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Cluster/butina.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Fingerprint/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Fingerprint/fp_calculator.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Fingerprint/smiles_featurizer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Molecule/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Molecule/atom_features.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Molecule/descriptors.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Molecule/formula.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Molecule/graph_annotator.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Molecule/standardize.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Molecule/valence.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/aam_validator.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/chem/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/chem/aam.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/chem/its.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/chem/smiles.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/exact/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/exact/branching.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/exact/certificate.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/exact/enumerate.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/exact/exhaustive.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/exact/kernel.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/exact/milp.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/graph/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/graph/automorphism.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/graph/blockcut.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/graph/labeled_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/graph/refine.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/graph/synkit_adapter.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/io/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/io/idxmapstr.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/slap/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/slap/lap.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/Mapper/slap/sequential.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/aam_validator.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/balance_check.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/canon_rsmi.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/cleaning.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/deionize.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/fix_aam.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/neutralize.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/radical_wildcard.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/Reaction/standardize.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Chem/utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Data/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Data/gen_partial_aam.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Canon/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Canon/canon_algs.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Canon/canon_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Canon/nauty.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Context/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Context/hier_context.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Context/radius_expand.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/FG/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/FG/api.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/FG/audit.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/FG/catalog.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/FG/detector.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/FG/model.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/FG/ring_system.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/Descriptors/topology.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/Fingerprint/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/graph_descriptors.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/graph_fps.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/graph_signature.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/hash_fps.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/morgan_fps.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/path_fps.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Feature/wl_hash.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Hyrogen/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Hyrogen/_misc.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Hyrogen/hcomplete.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Hyrogen/hextend.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/its_builder.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/its_construction.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/its_decompose.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/its_destruction.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/its_relabel.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/its_reverter.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/normalize_aam.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/partial_its.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/ITS/rc_extractor.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/MTG/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/MTG/mcs_matcher.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/MTG/mtg.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/MTG/mtg_explore.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/MTG/utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/approx_mcs.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/auto_est.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/automorphism.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/batch_cluster.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/dedup_matches.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/graph_cluster.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/graph_matcher.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/graph_morphism.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/mcs_matcher.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/multi_turbo_iso.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/orbit.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/partial_matcher.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/sing.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/turbo_iso.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Matcher/wl_sel.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Mech/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Mech/lwg_editor.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Mech/lwg_ops.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Wildcard/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Wildcard/fuse_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Wildcard/graph_wc.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Wildcard/radwc.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Wildcard/wc_matcher.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/Wildcard/wildcard.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/canon_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/syn_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Graph/utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/combinatorial/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/combinatorial/gml_to_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/combinatorial/graph_to_gml.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/combinatorial/graph_to_smarts.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/combinatorial/smarts_expander.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/combinatorial/smarts_generalizer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/combinatorial/smarts_to_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/data_io.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/data_process.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/debug.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/dg_to_gml.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/gml_to_nx.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/graph_to_mol.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/mol_to_graph.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/nx_to_gml.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/IO/smiles_to_id.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Apply/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Apply/reactor_rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Apply/retro_reactor.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Apply/rule_matcher.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Apply/rule_rbl.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Compose/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Compose/compose_rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Compose/rule_compose.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Compose/rule_mapping.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Compose/seq_comp.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Compose/valence_constrain.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Modify/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Modify/implict_rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Modify/longest_path.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Modify/molecule_rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Modify/prune_templates.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Modify/rule_utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/Modify/strip_rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Rule/syn_rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/MSR/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/MSR/multi_steps.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/MSR/path_finder.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Metrics/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Metrics/_base.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Metrics/_plot.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Metrics/_ranking.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/batch_reactor.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/benchmark.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/imba_engine.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/mod_aam.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/mod_reactor.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/partial_engine.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/post_syn.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/rule_filter.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/single_predictor.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/Reactor/strategy.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Synthesis/reactor_utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Utils/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Utils/utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/crn/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/crn/visualizer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/arrows.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/chem.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/constants.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/layout.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/mapping.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/models.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/epd/utils.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/graph_visualizer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/its/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/its/drawer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/molecule/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/molecule/drawer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/molecule/reaction.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/mtg/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/mtg/drawer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/pdf_writer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/reaction/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/reaction/rule.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/reaction/rxn.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/space/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/space/chemical.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/space/embedding.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/visual_drawer.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/Vis/visual_model.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/__init__.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/examples.py +0 -0
- {synkit-1.4.2b1 → synkit-1.5.0}/synkit/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synkit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
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
|
|
@@ -19,6 +19,7 @@ Requires-Dist: rdkit>=2025.3.1
|
|
|
19
19
|
Requires-Dist: regex>=2024.11.6
|
|
20
20
|
Requires-Dist: requests>=2.32.3
|
|
21
21
|
Requires-Dist: scikit-learn>=1.4.0
|
|
22
|
+
Requires-Dist: scipy>=1.14.0
|
|
22
23
|
Requires-Dist: seaborn>=0.13.2
|
|
23
24
|
Provides-Extra: all
|
|
24
25
|
Requires-Dist: numpy>=2.2.0; extra == 'all'
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "synkit"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.5.0"
|
|
8
8
|
description = "Utility for reaction modeling using graph grammar"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
long-description = { file = "CHANGELOG.md" }
|
|
@@ -25,6 +25,7 @@ dependencies = [
|
|
|
25
25
|
"pandas>=1.5.3",
|
|
26
26
|
"rdkit>=2025.3.1",
|
|
27
27
|
"networkx>=3.3",
|
|
28
|
+
"scipy>=1.14.0",
|
|
28
29
|
"seaborn>=0.13.2",
|
|
29
30
|
"requests>=2.32.3",
|
|
30
31
|
"regex>=2024.11.6",
|
|
@@ -36,14 +36,18 @@ class TransformationFP:
|
|
|
36
36
|
|
|
37
37
|
@staticmethod
|
|
38
38
|
def convert_arr2vec(arr: np.ndarray) -> cDataStructs.ExplicitBitVect:
|
|
39
|
-
"""Convert a NumPy array
|
|
39
|
+
"""Convert a non-negative NumPy array into an RDKit bit vector.
|
|
40
40
|
|
|
41
|
-
:param arr: Array of
|
|
41
|
+
:param arr: Array of non-negative fingerprint values.
|
|
42
42
|
:type arr: np.ndarray
|
|
43
|
-
:returns: RDKit bit vector
|
|
43
|
+
:returns: RDKit bit vector with positive values encoded as 1.
|
|
44
44
|
:rtype: cDataStructs.ExplicitBitVect
|
|
45
|
+
:raises ValueError: If the array contains negative or non-finite values.
|
|
45
46
|
"""
|
|
46
|
-
|
|
47
|
+
flat = np.asarray(arr).flatten()
|
|
48
|
+
if not np.isfinite(flat).all() or (flat < 0).any():
|
|
49
|
+
raise ValueError("Bit-vector output requires non-negative finite values.")
|
|
50
|
+
bitstr = "".join("1" if x else "0" for x in flat)
|
|
47
51
|
return cDataStructs.CreateFromBitString(bitstr)
|
|
48
52
|
|
|
49
53
|
def fit(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .aam_validator import AAMValidator
|
|
2
|
+
from synkit.Chem.utils import remove_explicit_H_from_rsmi
|
|
2
3
|
|
|
3
4
|
# from .standardize import Standardize
|
|
4
5
|
# from .canon_rsmi import CanonRSMI
|
|
5
6
|
|
|
6
|
-
__all__ = ["AAMValidator"]
|
|
7
|
+
__all__ = ["AAMValidator", "remove_explicit_H_from_rsmi"]
|
|
@@ -121,10 +121,7 @@ class Tautomerize:
|
|
|
121
121
|
mol = Chem.MolFromSmiles(smiles)
|
|
122
122
|
if mol is None:
|
|
123
123
|
return []
|
|
124
|
-
|
|
125
|
-
graph, groups = smiles_to_graph_and_functional_groups(smiles)
|
|
126
|
-
except ValueError:
|
|
127
|
-
return []
|
|
124
|
+
graph, groups = smiles_to_graph_and_functional_groups(smiles)
|
|
128
125
|
node_to_idx = {
|
|
129
126
|
(
|
|
130
127
|
atom.GetAtomMapNum() if atom.GetAtomMapNum() else atom.GetIdx() + 1
|
|
@@ -100,13 +100,36 @@ class ITSExpand:
|
|
|
100
100
|
)
|
|
101
101
|
|
|
102
102
|
@staticmethod
|
|
103
|
-
def
|
|
104
|
-
|
|
103
|
+
def _validate_atom_maps_within_range(
|
|
104
|
+
atom_maps: list[int],
|
|
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``.
|
|
105
112
|
|
|
106
113
|
:param atom_maps: Nonzero atom-map numbers.
|
|
107
114
|
:type atom_maps: list[int]
|
|
108
|
-
:
|
|
115
|
+
:param n_nodes: Number of nodes in the side graph.
|
|
116
|
+
:type n_nodes: int
|
|
117
|
+
:raises ValueError: If any atom-map number is outside ``1..N``.
|
|
109
118
|
"""
|
|
119
|
+
bad_targets = [
|
|
120
|
+
atom_map for atom_map in atom_maps if atom_map < 1 or atom_map > n_nodes
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
if bad_targets:
|
|
124
|
+
raise ValueError(
|
|
125
|
+
"Cannot keep side graph node ids contiguous from 1..N while "
|
|
126
|
+
f"also using atom_map as node id. The following atom maps are "
|
|
127
|
+
f"outside 1..{n_nodes}: {bad_targets}"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def _validate_positive_atom_maps(atom_maps: list[int]) -> None:
|
|
132
|
+
"""Validate that atom-map numbers are valid positive node IDs."""
|
|
110
133
|
bad_targets = [atom_map for atom_map in atom_maps if atom_map < 1]
|
|
111
134
|
|
|
112
135
|
if bad_targets:
|
|
@@ -141,33 +164,17 @@ class ITSExpand:
|
|
|
141
164
|
|
|
142
165
|
return mapping, used_ids
|
|
143
166
|
|
|
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
|
-
|
|
160
167
|
@staticmethod
|
|
161
168
|
def _assign_unmapped_nodes(
|
|
162
169
|
graph,
|
|
163
170
|
mapping: dict,
|
|
164
171
|
used_ids: set[int],
|
|
165
172
|
) -> dict:
|
|
166
|
-
"""Assign unmapped atoms while
|
|
173
|
+
"""Assign unmapped atoms while preserving contiguous node IDs.
|
|
167
174
|
|
|
168
175
|
Unmapped atoms keep their original node ID when possible. If an
|
|
169
|
-
unmapped atom's node ID conflicts with a mapped atom
|
|
170
|
-
moved into the
|
|
176
|
+
unmapped atom's node ID conflicts with a mapped atom's target ID, it is
|
|
177
|
+
moved into one of the remaining free IDs inside ``1..N``.
|
|
171
178
|
|
|
172
179
|
:param graph: Molecular side graph.
|
|
173
180
|
:type graph: networkx.Graph
|
|
@@ -178,7 +185,9 @@ class ITSExpand:
|
|
|
178
185
|
:returns: Complete old-node to new-node mapping.
|
|
179
186
|
:rtype: dict
|
|
180
187
|
"""
|
|
181
|
-
|
|
188
|
+
n_nodes = graph.number_of_nodes()
|
|
189
|
+
free_ids = set(range(1, n_nodes + 1)) - used_ids
|
|
190
|
+
pending_unmapped = []
|
|
182
191
|
|
|
183
192
|
for node, data in graph.nodes(data=True):
|
|
184
193
|
atom_map = ITSExpand._atom_map(data)
|
|
@@ -186,29 +195,68 @@ class ITSExpand:
|
|
|
186
195
|
if atom_map != 0:
|
|
187
196
|
continue
|
|
188
197
|
|
|
198
|
+
if isinstance(node, int) and node in free_ids:
|
|
199
|
+
mapping[node] = node
|
|
200
|
+
free_ids.remove(node)
|
|
201
|
+
else:
|
|
202
|
+
pending_unmapped.append(node)
|
|
203
|
+
|
|
204
|
+
for old_node, new_node in zip(pending_unmapped, sorted(free_ids)):
|
|
205
|
+
mapping[old_node] = new_node
|
|
206
|
+
|
|
207
|
+
return mapping
|
|
208
|
+
|
|
209
|
+
@staticmethod
|
|
210
|
+
def _assign_unmapped_nodes_sparse(
|
|
211
|
+
graph,
|
|
212
|
+
mapping: dict,
|
|
213
|
+
used_ids: set[int],
|
|
214
|
+
) -> dict:
|
|
215
|
+
"""Assign unmapped atoms without changing preserved sparse map IDs."""
|
|
216
|
+
next_candidate = 1
|
|
217
|
+
|
|
218
|
+
for node, data in graph.nodes(data=True):
|
|
219
|
+
if ITSExpand._atom_map(data) != 0:
|
|
220
|
+
continue
|
|
221
|
+
|
|
189
222
|
if isinstance(node, int) and node > 0 and node not in used_ids:
|
|
190
223
|
mapping[node] = node
|
|
191
224
|
used_ids.add(node)
|
|
192
225
|
next_candidate = max(next_candidate, node + 1)
|
|
193
226
|
continue
|
|
194
227
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
next_candidate
|
|
228
|
+
while next_candidate in used_ids:
|
|
229
|
+
next_candidate += 1
|
|
230
|
+
mapping[node] = next_candidate
|
|
231
|
+
used_ids.add(next_candidate)
|
|
232
|
+
next_candidate += 1
|
|
199
233
|
|
|
200
234
|
return mapping
|
|
201
235
|
|
|
202
236
|
@staticmethod
|
|
203
|
-
def
|
|
204
|
-
"""Validate that
|
|
237
|
+
def _validate_contiguous_mapping(mapping: dict, n_nodes: int) -> None:
|
|
238
|
+
"""Validate that a mapping produces exactly node IDs ``1..N``.
|
|
205
239
|
|
|
206
240
|
:param mapping: Old-node to new-node mapping.
|
|
207
241
|
:type mapping: dict
|
|
208
242
|
:param n_nodes: Number of nodes in the graph.
|
|
209
243
|
:type n_nodes: int
|
|
210
|
-
:raises RuntimeError: If the
|
|
244
|
+
:raises RuntimeError: If the mapped node IDs are not exactly ``1..N``.
|
|
211
245
|
"""
|
|
246
|
+
expected_ids = set(range(1, n_nodes + 1))
|
|
247
|
+
actual_ids = set(mapping.values())
|
|
248
|
+
|
|
249
|
+
if actual_ids != expected_ids:
|
|
250
|
+
missing = sorted(expected_ids - actual_ids)
|
|
251
|
+
extra = sorted(actual_ids - expected_ids)
|
|
252
|
+
raise RuntimeError(
|
|
253
|
+
f"Reindexing failed. Missing node ids: {missing}; "
|
|
254
|
+
f"extra node ids: {extra}"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
@staticmethod
|
|
258
|
+
def _validate_complete_unique_mapping(mapping: dict, n_nodes: int) -> None:
|
|
259
|
+
"""Validate that every source node maps to one unique target node."""
|
|
212
260
|
if len(mapping) != n_nodes:
|
|
213
261
|
raise RuntimeError(
|
|
214
262
|
f"Reindexing failed. Expected {n_nodes} mapped nodes; "
|
|
@@ -222,32 +270,48 @@ class ITSExpand:
|
|
|
222
270
|
)
|
|
223
271
|
|
|
224
272
|
@staticmethod
|
|
225
|
-
def _build_side_graph_reindex_mapping(
|
|
273
|
+
def _build_side_graph_reindex_mapping(
|
|
274
|
+
graph,
|
|
275
|
+
contiguous: bool = True,
|
|
276
|
+
) -> dict:
|
|
226
277
|
"""Build an old-node to new-node mapping for a side graph.
|
|
227
278
|
|
|
228
279
|
The mapping satisfies two conditions:
|
|
229
280
|
|
|
230
281
|
1. Every atom with ``atom_map != 0`` is assigned to node ID
|
|
231
282
|
``atom_map``.
|
|
232
|
-
2.
|
|
233
|
-
|
|
283
|
+
2. The final node IDs are exactly contiguous from ``1..N`` when
|
|
284
|
+
``contiguous=True``. With ``contiguous=False``, existing positive,
|
|
285
|
+
sparse atom-map IDs are retained and unmapped atoms receive unused
|
|
286
|
+
positive IDs.
|
|
234
287
|
|
|
235
288
|
:param graph: Molecular side graph.
|
|
236
289
|
:type graph: networkx.Graph
|
|
237
290
|
:returns: Old-node to new-node mapping.
|
|
238
291
|
:rtype: dict
|
|
239
|
-
:raises ValueError: If atom-map values are duplicated or
|
|
292
|
+
:raises ValueError: If atom-map values are duplicated or incompatible
|
|
293
|
+
with contiguous node IDs.
|
|
240
294
|
"""
|
|
241
295
|
n_nodes = graph.number_of_nodes()
|
|
242
296
|
atom_maps = ITSExpand._nonzero_atom_maps(graph)
|
|
243
297
|
|
|
244
298
|
ITSExpand._validate_unique_atom_maps(atom_maps)
|
|
245
|
-
|
|
299
|
+
if contiguous:
|
|
300
|
+
ITSExpand._validate_atom_maps_within_range(atom_maps, n_nodes)
|
|
301
|
+
else:
|
|
302
|
+
ITSExpand._validate_positive_atom_maps(atom_maps)
|
|
246
303
|
|
|
247
304
|
mapping, used_ids = ITSExpand._assign_mapped_nodes(graph)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
305
|
+
if contiguous:
|
|
306
|
+
mapping = ITSExpand._assign_unmapped_nodes(graph, mapping, used_ids)
|
|
307
|
+
ITSExpand._validate_contiguous_mapping(mapping, n_nodes)
|
|
308
|
+
else:
|
|
309
|
+
mapping = ITSExpand._assign_unmapped_nodes_sparse(
|
|
310
|
+
graph,
|
|
311
|
+
mapping,
|
|
312
|
+
used_ids,
|
|
313
|
+
)
|
|
314
|
+
ITSExpand._validate_complete_unique_mapping(mapping, n_nodes)
|
|
251
315
|
|
|
252
316
|
return mapping
|
|
253
317
|
|
|
@@ -319,12 +383,12 @@ class ITSExpand:
|
|
|
319
383
|
return new_graph
|
|
320
384
|
|
|
321
385
|
@staticmethod
|
|
322
|
-
def reindex_side_graph_by_atom_map(graph):
|
|
386
|
+
def reindex_side_graph_by_atom_map(graph, contiguous: bool = True):
|
|
323
387
|
"""Reindex a side graph so mapped atoms use ``atom_map`` as node ID.
|
|
324
388
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
389
|
+
By default, the returned graph keeps node IDs contiguous from ``1..N``.
|
|
390
|
+
Set ``contiguous=False`` to preserve valid sparse map IDs such as
|
|
391
|
+
``:10`` and ``:21``; this is needed by the EF-SMIRKS expansion path.
|
|
328
392
|
|
|
329
393
|
This is useful because the reaction-center graph produced by
|
|
330
394
|
``ITSConstruction().ITSGraph(...)`` uses atom-map numbers as node IDs,
|
|
@@ -351,12 +415,14 @@ class ITSExpand:
|
|
|
351
415
|
|
|
352
416
|
:param graph: Molecular side graph.
|
|
353
417
|
:type graph: networkx.Graph
|
|
354
|
-
:
|
|
418
|
+
:param contiguous: Whether the returned node IDs must be ``1..N``.
|
|
419
|
+
:type contiguous: bool
|
|
420
|
+
:returns: Reindexed side graph.
|
|
355
421
|
:rtype: networkx.Graph
|
|
356
422
|
:raises ValueError: If atom-map numbers cannot be safely used as node
|
|
357
|
-
IDs.
|
|
423
|
+
IDs while preserving ``1..N`` indexing.
|
|
358
424
|
"""
|
|
359
|
-
mapping = ITSExpand._build_side_graph_reindex_mapping(graph)
|
|
425
|
+
mapping = ITSExpand._build_side_graph_reindex_mapping(graph, contiguous)
|
|
360
426
|
return ITSExpand._rebuild_graph_with_mapping(graph, mapping)
|
|
361
427
|
|
|
362
428
|
@staticmethod
|
|
@@ -431,7 +497,10 @@ class ITSExpand:
|
|
|
431
497
|
|
|
432
498
|
# Node IDs remain contiguous from 1..N.
|
|
433
499
|
if preserve_older_map:
|
|
434
|
-
side_graph = ITSExpand.reindex_side_graph_by_atom_map(
|
|
500
|
+
side_graph = ITSExpand.reindex_side_graph_by_atom_map(
|
|
501
|
+
side_graph,
|
|
502
|
+
contiguous=False,
|
|
503
|
+
)
|
|
435
504
|
|
|
436
505
|
# Reconstruct the full ITS graph.
|
|
437
506
|
its_graph = ITSBuilder().ITSGraph(side_graph, rc_graph)
|
|
@@ -128,10 +128,6 @@ def electron_aware_node_match(
|
|
|
128
128
|
attr, 0 if attr in {"hcount", "lone_pairs"} else None
|
|
129
129
|
)
|
|
130
130
|
if attr in {"hcount", "lone_pairs"}:
|
|
131
|
-
if host_value is None:
|
|
132
|
-
host_value = 0
|
|
133
|
-
if pattern_value is None:
|
|
134
|
-
pattern_value = 0
|
|
135
131
|
if host_value < pattern_value:
|
|
136
132
|
return False
|
|
137
133
|
continue
|
|
@@ -73,6 +73,69 @@ def split_arrow_code(arrow_code: str) -> list[str]:
|
|
|
73
73
|
return [s.strip() for s in arrow_code.split(";") if s.strip()]
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
def ef_arrow_code_to_arrow_code(ef_arrow_code: str) -> str:
|
|
77
|
+
"""Normalize an EF-SMIRKS hyphen-form flow code for SynKit conversion.
|
|
78
|
+
|
|
79
|
+
EF-SMIRKS stores one electron-flow step as ``source-target``; SynKit's
|
|
80
|
+
internal arrow-code helpers use ``source=target``. For example,
|
|
81
|
+
``"10,20-20,21"`` becomes ``"10,20=20,21"``.
|
|
82
|
+
|
|
83
|
+
:param ef_arrow_code: Semicolon-separated hyphen-form electron-flow code.
|
|
84
|
+
:type ef_arrow_code: str
|
|
85
|
+
:returns: Equivalent equals-form SynKit arrow code.
|
|
86
|
+
:rtype: str
|
|
87
|
+
:raises ValueError: If a step does not contain exactly one ``"-"``.
|
|
88
|
+
"""
|
|
89
|
+
normalized_steps = []
|
|
90
|
+
for step in split_arrow_code(ef_arrow_code):
|
|
91
|
+
if step.count("-") != 1:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
"An EF-SMIRKS electron-flow step must contain exactly one '-': "
|
|
94
|
+
f"{step!r}"
|
|
95
|
+
)
|
|
96
|
+
lhs, rhs = step.split("-", 1)
|
|
97
|
+
normalized_steps.append(f"{lhs.strip()}={rhs.strip()}")
|
|
98
|
+
if not normalized_steps:
|
|
99
|
+
raise ValueError("EF-SMIRKS electron-flow code is empty.")
|
|
100
|
+
return ";".join(normalized_steps)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def arrow_code_to_ef_arrow_code(arrow_code: str) -> str:
|
|
104
|
+
"""Convert internal equals-form arrow code to EF-SMIRKS hyphen form.
|
|
105
|
+
|
|
106
|
+
:param arrow_code: Semicolon-separated SynKit arrow code.
|
|
107
|
+
:type arrow_code: str
|
|
108
|
+
:returns: Equivalent hyphen-form electron-flow code.
|
|
109
|
+
:rtype: str
|
|
110
|
+
"""
|
|
111
|
+
return ";".join(step.replace("=", "-", 1) for step in split_arrow_code(arrow_code))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def split_ef_smirks(ef_smirks: str) -> tuple[str, str]:
|
|
115
|
+
"""Split an EF-SMIRKS string into reaction SMILES and flow code.
|
|
116
|
+
|
|
117
|
+
An EF-SMIRKS is an atom-mapped reaction SMILES followed by whitespace and
|
|
118
|
+
a hyphen-form electron-flow code, e.g. ``"[CH3:1][Cl:2]>>[CH3:1].[Cl-:2]
|
|
119
|
+
1-1,2"``.
|
|
120
|
+
|
|
121
|
+
:param ef_smirks: RSMI followed by whitespace and electron-flow code.
|
|
122
|
+
:type ef_smirks: str
|
|
123
|
+
:returns: Reaction SMILES and hyphen-form electron-flow code.
|
|
124
|
+
:rtype: tuple[str, str]
|
|
125
|
+
:raises ValueError: If the input lacks an RSMI arrow or flow code.
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
reaction_smiles, ef_arrow_code = ef_smirks.strip().rsplit(None, 1)
|
|
129
|
+
except ValueError as exc:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
"EF-SMIRKS must contain reaction SMILES followed by whitespace and "
|
|
132
|
+
"an electron-flow code."
|
|
133
|
+
) from exc
|
|
134
|
+
if ">>" not in reaction_smiles:
|
|
135
|
+
raise ValueError("EF-SMIRKS reaction component must contain '>>'.")
|
|
136
|
+
return reaction_smiles, ef_arrow_code
|
|
137
|
+
|
|
138
|
+
|
|
76
139
|
def arrow_atom_maps(arrow_code: str) -> set[int]:
|
|
77
140
|
"""Return all atom maps used in an arrow code.
|
|
78
141
|
|
|
@@ -946,6 +1009,105 @@ def convert_reaction_arrow(
|
|
|
946
1009
|
return result
|
|
947
1010
|
|
|
948
1011
|
|
|
1012
|
+
def ef_smirks_to_epd(
|
|
1013
|
+
ef_smirks: str,
|
|
1014
|
+
orbital_class: Optional[str] = None,
|
|
1015
|
+
strict_bond_lookup: bool = True,
|
|
1016
|
+
) -> dict[str, Any]:
|
|
1017
|
+
"""Complete AAM and convert an EF-SMIRKS string into EPD data.
|
|
1018
|
+
|
|
1019
|
+
The supplied atom maps used by the electron-flow code are preserved. All
|
|
1020
|
+
remaining atoms receive maps through :class:`ITSExpand`. The result
|
|
1021
|
+
contains both generic ``epd`` and Lewis-wavefunction-aware ``epd_lw``
|
|
1022
|
+
records; the latter uses local ITS bond orders to distinguish sigma and pi
|
|
1023
|
+
edits.
|
|
1024
|
+
|
|
1025
|
+
:param ef_smirks: RSMI followed by a hyphen-form electron-flow code.
|
|
1026
|
+
:type ef_smirks: str
|
|
1027
|
+
:param orbital_class: Optional source-dataset metadata.
|
|
1028
|
+
:type orbital_class: Optional[str]
|
|
1029
|
+
:param strict_bond_lookup: Whether missing ITS bond lookups should raise.
|
|
1030
|
+
:type strict_bond_lookup: bool
|
|
1031
|
+
:returns: Completed AAM, generic EPD, typed ``epd_lw``, and diagnostics.
|
|
1032
|
+
:rtype: dict[str, Any]
|
|
1033
|
+
"""
|
|
1034
|
+
reaction_smiles, ef_arrow_code = split_ef_smirks(ef_smirks)
|
|
1035
|
+
arrow_code = ef_arrow_code_to_arrow_code(ef_arrow_code)
|
|
1036
|
+
result = convert_reaction_arrow(
|
|
1037
|
+
reaction_smiles=reaction_smiles,
|
|
1038
|
+
arrow_code=arrow_code,
|
|
1039
|
+
orbital_class=orbital_class,
|
|
1040
|
+
expand_aam=True,
|
|
1041
|
+
remove_non_arrow_maps=True,
|
|
1042
|
+
strict_bond_lookup=strict_bond_lookup,
|
|
1043
|
+
)
|
|
1044
|
+
result.update(
|
|
1045
|
+
{
|
|
1046
|
+
"ef_smirks": ef_smirks,
|
|
1047
|
+
"ef_arrow_code": ef_arrow_code,
|
|
1048
|
+
"complete_aam": result["expanded_rsmi"],
|
|
1049
|
+
"epd": result["converted"],
|
|
1050
|
+
"epd_lw": result["typed_converted"],
|
|
1051
|
+
}
|
|
1052
|
+
)
|
|
1053
|
+
return result
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
def epd_to_ef_smirks(complete_aam: str, epd: list[list[Any]]) -> str:
|
|
1057
|
+
"""Reconstruct an EF-SMIRKS string from complete AAM and EPD records.
|
|
1058
|
+
|
|
1059
|
+
``epd`` may contain generic action names (``"B-/B+"``) or typed Lewis
|
|
1060
|
+
names (``"Sigma-/Pi+"``). Only action direction and source/target map
|
|
1061
|
+
lists are needed to reconstruct the electron-flow code.
|
|
1062
|
+
|
|
1063
|
+
:param complete_aam: Fully atom-mapped reaction SMILES.
|
|
1064
|
+
:type complete_aam: str
|
|
1065
|
+
:param epd: Generic or typed EPD records of ``[action, source, target]``.
|
|
1066
|
+
:type epd: list[list[Any]]
|
|
1067
|
+
:returns: EF-SMIRKS text with a hyphen-form electron-flow code.
|
|
1068
|
+
:rtype: str
|
|
1069
|
+
:raises ValueError: If AAM or EPD records are malformed or unsupported.
|
|
1070
|
+
"""
|
|
1071
|
+
if complete_aam.count(">>") != 1:
|
|
1072
|
+
raise ValueError("complete_aam must contain exactly one '>>'.")
|
|
1073
|
+
if not epd:
|
|
1074
|
+
raise ValueError("epd must contain at least one electron-pushing step.")
|
|
1075
|
+
|
|
1076
|
+
def format_maps(atom_maps: list[int]) -> str:
|
|
1077
|
+
"""Format an atom-map sequence for one EF-SMIRKS endpoint."""
|
|
1078
|
+
return ",".join(str(atom_map) for atom_map in atom_maps)
|
|
1079
|
+
|
|
1080
|
+
steps = []
|
|
1081
|
+
for record in epd:
|
|
1082
|
+
if len(record) < 3:
|
|
1083
|
+
raise ValueError(f"Invalid EPD record: {record!r}")
|
|
1084
|
+
action, source, target = record[:3]
|
|
1085
|
+
action_parts = str(action).split("/", 1)
|
|
1086
|
+
if len(action_parts) != 2:
|
|
1087
|
+
raise ValueError(f"Invalid EPD action: {action!r}")
|
|
1088
|
+
consumed, produced = action_parts
|
|
1089
|
+
source = [int(atom_map) for atom_map in source]
|
|
1090
|
+
target = [int(atom_map) for atom_map in target]
|
|
1091
|
+
|
|
1092
|
+
if consumed == "LP-" and len(source) == 1 and len(target) in {1, 2}:
|
|
1093
|
+
lhs, rhs = source, target
|
|
1094
|
+
elif produced == "LP+" and len(source) == 2 and len(target) == 1:
|
|
1095
|
+
lhs, rhs = source, target
|
|
1096
|
+
elif (
|
|
1097
|
+
consumed.endswith("-")
|
|
1098
|
+
and produced.endswith("+")
|
|
1099
|
+
and len(source) == 2
|
|
1100
|
+
and len(target) == 2
|
|
1101
|
+
):
|
|
1102
|
+
lhs, rhs = source, target
|
|
1103
|
+
else:
|
|
1104
|
+
raise ValueError(f"Unsupported EPD record for EF-SMIRKS: {record!r}")
|
|
1105
|
+
|
|
1106
|
+
steps.append(f"{format_maps(lhs)}-{format_maps(rhs)}")
|
|
1107
|
+
|
|
1108
|
+
return f"{complete_aam} {';'.join(steps)}"
|
|
1109
|
+
|
|
1110
|
+
|
|
949
1111
|
def convert_record(
|
|
950
1112
|
record: dict[str, Any],
|
|
951
1113
|
reaction_key: str = "SMIRKS",
|