synkit 1.6.5__py3-none-win_amd64.whl
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/CRN/Benchmark/__init__.py +53 -0
- synkit/CRN/Benchmark/crosschecks.py +13 -0
- synkit/CRN/Benchmark/kegg.py +12 -0
- synkit/CRN/Benchmark/networks.py +13 -0
- synkit/CRN/Benchmark/scaling.py +13 -0
- synkit/CRN/Benchmark/validate.py +13 -0
- synkit/CRN/Construct/__init__.py +16 -0
- synkit/CRN/Construct/abstract.py +668 -0
- synkit/CRN/Construct/arity.py +36 -0
- synkit/CRN/Construct/builder.py +619 -0
- synkit/CRN/Construct/derivation.py +63 -0
- synkit/CRN/Construct/flattener.py +100 -0
- synkit/CRN/Construct/keys.py +14 -0
- synkit/CRN/Construct/mixtures.py +117 -0
- synkit/CRN/Construct/rdkit_worker.py +69 -0
- synkit/CRN/Construct/smiles.py +99 -0
- synkit/CRN/Construct/state.py +31 -0
- synkit/CRN/Construct/strategy.py +75 -0
- synkit/CRN/Construct/worker.py +42 -0
- synkit/CRN/IO/__init__.py +36 -0
- synkit/CRN/IO/sbml.py +925 -0
- synkit/CRN/Pathway/__init__.py +72 -0
- synkit/CRN/Pathway/_adapter.py +333 -0
- synkit/CRN/Pathway/pathfinder.py +500 -0
- synkit/CRN/Pathway/reachability.py +796 -0
- synkit/CRN/Pathway/realizability.py +1322 -0
- synkit/CRN/Petrinet/__init__.py +93 -0
- synkit/CRN/Petrinet/analyzer.py +486 -0
- synkit/CRN/Petrinet/minimal_semiflows.py +436 -0
- synkit/CRN/Petrinet/net.py +1041 -0
- synkit/CRN/Petrinet/persistence.py +97 -0
- synkit/CRN/Petrinet/semiflows.py +483 -0
- synkit/CRN/Petrinet/siphon_search.py +402 -0
- synkit/CRN/Petrinet/structure.py +286 -0
- synkit/CRN/Props/__init__.py +130 -0
- synkit/CRN/Props/_rank.py +29 -0
- synkit/CRN/Props/deficiency.py +953 -0
- synkit/CRN/Props/dynamics.py +832 -0
- synkit/CRN/Props/helper.py +151 -0
- synkit/CRN/Props/stoich.py +751 -0
- synkit/CRN/Props/thermo.py +584 -0
- synkit/CRN/Query/__init__.py +66 -0
- synkit/CRN/Query/cache.py +41 -0
- synkit/CRN/Query/kegg_api.py +99 -0
- synkit/CRN/Query/kegg_extract.py +714 -0
- synkit/CRN/Query/kegg_impute.py +707 -0
- synkit/CRN/Query/kegg_parse.py +480 -0
- synkit/CRN/Query/to_syncrn.py +290 -0
- synkit/CRN/README.md +234 -0
- synkit/CRN/Structure/__init__.py +12 -0
- synkit/CRN/Structure/_graph_io.py +713 -0
- synkit/CRN/Structure/_matrices.py +88 -0
- synkit/CRN/Structure/_parse.py +544 -0
- synkit/CRN/Structure/reaction.py +237 -0
- synkit/CRN/Structure/rule.py +55 -0
- synkit/CRN/Structure/species.py +46 -0
- synkit/CRN/Structure/syncrn.py +929 -0
- synkit/CRN/Symmetry/__init__.py +20 -0
- synkit/CRN/Symmetry/_common.py +1059 -0
- synkit/CRN/Symmetry/_ir.py +940 -0
- synkit/CRN/Symmetry/automorphism.py +472 -0
- synkit/CRN/Symmetry/canon.py +441 -0
- synkit/CRN/Symmetry/isomorphism.py +412 -0
- synkit/CRN/Symmetry/symmetry.py +61 -0
- synkit/CRN/Symmetry/wl_canon.py +967 -0
- synkit/CRN/Visualize/__init__.py +11 -0
- synkit/CRN/Visualize/labels.py +113 -0
- synkit/CRN/Visualize/layout.py +708 -0
- synkit/CRN/Visualize/palette.py +292 -0
- synkit/CRN/Visualize/validation.py +122 -0
- synkit/CRN/Visualize/vis.py +1152 -0
- synkit/CRN/__init__.py +233 -0
- synkit/CRN/_build.py +50 -0
- synkit/CRN/_native_build.py +175 -0
- synkit/CRN/analysis.py +629 -0
- synkit/CRN/backend.py +74 -0
- synkit/CRN/cpp/CMakeLists.txt +58 -0
- synkit/CRN/cpp/algebra.cpp +46 -0
- synkit/CRN/cpp/closure.cpp +29 -0
- synkit/CRN/cpp/exact.h +55 -0
- synkit/CRN/cpp/pathway.cpp +46 -0
- synkit/CRN/cpp/semiflows.cpp +209 -0
- synkit/CRN/cpp/structural.cpp +240 -0
- synkit/CRN/cpp/symmetry.cpp +45 -0
- synkit/CRN/cpp/write_manifest.cmake +28 -0
- synkit/CRN/kinds.py +137 -0
- synkit/CRN/lib/synkit_crn.dll +0 -0
- synkit/CRN/lib/synkit_crn.json +33 -0
- synkit/CRN/native.py +388 -0
- synkit/CRN/native_analysis.py +459 -0
- synkit/Chem/Cluster/__init__.py +0 -0
- synkit/Chem/Cluster/butina.py +140 -0
- synkit/Chem/Fingerprint/__init__.py +0 -0
- synkit/Chem/Fingerprint/fp_calculator.py +155 -0
- synkit/Chem/Fingerprint/smiles_featurizer.py +258 -0
- synkit/Chem/Fingerprint/transformation_fp.py +139 -0
- synkit/Chem/Mapper/README.rst +43 -0
- synkit/Chem/Mapper/__init__.py +255 -0
- synkit/Chem/Mapper/_compat.py +130 -0
- synkit/Chem/Mapper/adapters/__init__.py +1 -0
- synkit/Chem/Mapper/adapters/prediction.py +126 -0
- synkit/Chem/Mapper/adapters/template.py +412 -0
- synkit/Chem/Mapper/chem/__init__.py +52 -0
- synkit/Chem/Mapper/chem/aam.py +673 -0
- synkit/Chem/Mapper/chem/blind.py +160 -0
- synkit/Chem/Mapper/chem/its.py +309 -0
- synkit/Chem/Mapper/chem/smiles.py +564 -0
- synkit/Chem/Mapper/graph/__init__.py +66 -0
- synkit/Chem/Mapper/graph/automorphism.py +395 -0
- synkit/Chem/Mapper/graph/blockcut.py +98 -0
- synkit/Chem/Mapper/graph/labeled_graph.py +104 -0
- synkit/Chem/Mapper/graph/refine.py +176 -0
- synkit/Chem/Mapper/graph/synkit_adapter.py +110 -0
- synkit/Chem/Mapper/io/__init__.py +13 -0
- synkit/Chem/Mapper/io/idxmapstr.py +82 -0
- synkit/Chem/Mapper/mapper.py +685 -0
- synkit/Chem/Mapper/reaction_search.py +554 -0
- synkit/Chem/Mapper/reporting/__init__.py +1 -0
- synkit/Chem/Mapper/reporting/alternatives.py +451 -0
- synkit/Chem/Mapper/reporting/analysis.py +681 -0
- synkit/Chem/Mapper/reporting/native_analysis.py +485 -0
- synkit/Chem/Mapper/reporting/spectrum.py +642 -0
- synkit/Chem/Mapper/solvers/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/__init__.py +141 -0
- synkit/Chem/Mapper/solvers/exact/assignment/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/assignment/assignment_certificate.py +113 -0
- synkit/Chem/Mapper/solvers/exact/assignment/assignment_domains.py +162 -0
- synkit/Chem/Mapper/solvers/exact/assignment/assignment_verification.py +58 -0
- synkit/Chem/Mapper/solvers/exact/assignment/incremental_assignment.py +247 -0
- synkit/Chem/Mapper/solvers/exact/certificate.py +128 -0
- synkit/Chem/Mapper/solvers/exact/core.py +183 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/bounds.py +319 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/exhaustive.py +179 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/inputs.py +283 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/records.py +242 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/search.py +1025 -0
- synkit/Chem/Mapper/solvers/exact/distance_search/verify.py +378 -0
- synkit/Chem/Mapper/solvers/exact/edit_support.py +375 -0
- synkit/Chem/Mapper/solvers/exact/hybrid.py +224 -0
- synkit/Chem/Mapper/solvers/exact/hydrogen.py +264 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/branching.py +499 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumerate.py +1050 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/enumeration_result.py +30 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/kernel.py +256 -0
- synkit/Chem/Mapper/solvers/exact/kernel_solvers/milp.py +194 -0
- synkit/Chem/Mapper/solvers/exact/native/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/native/build.py +154 -0
- synkit/Chem/Mapper/solvers/exact/native/candidates.py +593 -0
- synkit/Chem/Mapper/solvers/exact/native/canonical.py +396 -0
- synkit/Chem/Mapper/solvers/exact/native/disk_frontier.py +415 -0
- synkit/Chem/Mapper/solvers/exact/native/frontier.py +390 -0
- synkit/Chem/Mapper/solvers/exact/native/hydrogen_quotient.py +213 -0
- synkit/Chem/Mapper/solvers/exact/native/its.py +528 -0
- synkit/Chem/Mapper/solvers/exact/native/native_distance.cpp +2803 -0
- synkit/Chem/Mapper/solvers/exact/native/orbit_aggregation.py +328 -0
- synkit/Chem/Mapper/solvers/exact/native/parallel.py +205 -0
- synkit/Chem/Mapper/solvers/exact/native/pmcd.py +203 -0
- synkit/Chem/Mapper/solvers/exact/native/runtime.py +40 -0
- synkit/Chem/Mapper/solvers/exact/native/search.py +294 -0
- synkit/Chem/Mapper/solvers/exact/native/separator.py +224 -0
- synkit/Chem/Mapper/solvers/exact/native_build.py +12 -0
- synkit/Chem/Mapper/solvers/exact/pabs/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/pabs/cost_lattice.py +43 -0
- synkit/Chem/Mapper/solvers/exact/pabs/factor_spectrum.py +110 -0
- synkit/Chem/Mapper/solvers/exact/pabs/multi_shell.py +368 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation.py +679 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_bounds.py +436 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_limits.py +13 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_search.py +856 -0
- synkit/Chem/Mapper/solvers/exact/pabs/propagation_symmetry.py +66 -0
- synkit/Chem/Mapper/solvers/exact/pabs/residual_typed_bonds.py +136 -0
- synkit/Chem/Mapper/solvers/exact/pabs/reward_frontier.py +201 -0
- synkit/Chem/Mapper/solvers/exact/pabs/separator_bound.py +258 -0
- synkit/Chem/Mapper/solvers/exact/pabs/separator_spectrum.py +308 -0
- synkit/Chem/Mapper/solvers/exact/pabs/sparse_reward_bound.py +207 -0
- synkit/Chem/Mapper/solvers/exact/pabs/suffix_spectrum.py +299 -0
- synkit/Chem/Mapper/solvers/exact/search.py +55 -0
- synkit/Chem/Mapper/solvers/exact/seeds/__init__.py +1 -0
- synkit/Chem/Mapper/solvers/exact/seeds/seed.py +104 -0
- synkit/Chem/Mapper/solvers/exact/seeds/seed_fragments.py +148 -0
- synkit/Chem/Mapper/solvers/exact/seeds/seed_relaxation.py +181 -0
- synkit/Chem/Mapper/solvers/exact/symmetry.py +223 -0
- synkit/Chem/Mapper/solvers/native/__init__.py +4 -0
- synkit/Chem/Mapper/solvers/native/assignment.py +99 -0
- synkit/Chem/Mapper/solvers/native/astar.py +22 -0
- synkit/Chem/Mapper/solvers/native/build.py +385 -0
- synkit/Chem/Mapper/solvers/native/classification.py +641 -0
- synkit/Chem/Mapper/solvers/native/core.py +378 -0
- synkit/Chem/Mapper/solvers/native/cpp/CMakeLists.txt +120 -0
- synkit/Chem/Mapper/solvers/native/cpp/examples/map_reaction.cpp +29 -0
- synkit/Chem/Mapper/solvers/native/cpp/examples/slap_graph.cpp +63 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/assignment/assignment.h +18 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/astar.h +18 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/astar/bound.h +33 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/pmcd_witness.h +16 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/core/runtime.h +130 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/hydrogen/hydrogen.h +73 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mapper.h +175 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/mces/mces.h +9 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/pabs/seed.h +34 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/reaction/reaction.h +30 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/sepdp/sepdp.h +21 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/slap.h +72 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/solvers/solvers.h +16 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/classification.h +140 -0
- synkit/Chem/Mapper/solvers/native/cpp/include/synkit_mapper/symmetry/cpabs.h +33 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/assignment/abb.cpp +302 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/CMakeLists.txt +12 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/README.md +47 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar.cpp +228 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/astar_graph.cpp +37 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/astar/bound.cpp +212 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/core/dispatch.cpp +272 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/core/pmcd_witness.cpp +36 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/hydrogen/hydrogen.cpp +380 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/mces/README.md +57 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/mces/mces.cpp +287 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/bridge.cpp +334 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/chemical_floor.inc +181 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/endpoint_symmetry.inc +104 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/exact_helpers.h +20 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_assignment_certificate.h +165 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_certificate.h +125 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_model.h +365 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_provider.inc +319 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_rounding.inc +170 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/lp_test_hooks.h +342 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/objective_reduction.inc +137 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/overlap_dual.inc +351 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/profile_seed.inc +214 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/seed_heuristics.inc +640 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_embedding.cpp +430 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/sparse_root_lp.h +176 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/pabs/test_hooks.inc +262 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/reaction/reaction.cpp +611 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/README.md +61 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/sepdp/sepdp.cpp +253 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/slap.cpp +810 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/formulation.h +138 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/maxsat.cpp +526 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/ortools.cpp +475 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/seed.h +80 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/solvers/weighted_cnf.h +256 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/supporting/README.md +43 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/supporting/dfs.cpp +123 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/supporting/edit_support.cpp +224 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification.cpp +553 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_pipeline.cpp +124 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/classification_stream.h +13 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/cpabs.cpp +455 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/edge_deck.cpp +743 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naugraph.c +732 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.c +94 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naurng.h +49 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.c +1771 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nausparse.h +133 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nautil.c +750 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/naututil.h +355 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.c +1256 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/nauty.h +1544 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.c +1131 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/schreier.h +70 -0
- synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/sorttemplates.c +580 -0
- synkit/Chem/Mapper/solvers/native/hydrogen.py +711 -0
- synkit/Chem/Mapper/solvers/native/implicit.py +506 -0
- synkit/Chem/Mapper/solvers/native/implicit_certificate.py +187 -0
- synkit/Chem/Mapper/solvers/native/implicit_preparation.py +309 -0
- synkit/Chem/Mapper/solvers/native/lib/synkit_mapper.dll +0 -0
- synkit/Chem/Mapper/solvers/native/mces.py +16 -0
- synkit/Chem/Mapper/solvers/native/reaction.py +179 -0
- synkit/Chem/Mapper/solvers/native/reaction_build.py +277 -0
- synkit/Chem/Mapper/solvers/native/reaction_text.py +26 -0
- synkit/Chem/Mapper/solvers/native/runtime.py +242 -0
- synkit/Chem/Mapper/solvers/native/sepdp.py +47 -0
- synkit/Chem/Mapper/solvers/native/slap.py +266 -0
- synkit/Chem/Mapper/solvers/native/solvers.py +67 -0
- synkit/Chem/Mapper/solvers/native/stereo.py +86 -0
- synkit/Chem/Mapper/solvers/native/supporting.py +34 -0
- synkit/Chem/Mapper/solvers/native/symmetry.py +91 -0
- synkit/Chem/Mapper/solvers/slap/__init__.py +29 -0
- synkit/Chem/Mapper/solvers/slap/lap.py +174 -0
- synkit/Chem/Mapper/solvers/slap/sequential.py +696 -0
- synkit/Chem/Mapper/validation/__init__.py +1 -0
- synkit/Chem/Mapper/validation/aam_validator.py +442 -0
- synkit/Chem/Mapper/validation/annotations.py +274 -0
- synkit/Chem/Mapper/validation/bonds.py +124 -0
- synkit/Chem/Mapper/validation/cohorts.py +72 -0
- synkit/Chem/Mapper/validation/identifiability.py +150 -0
- synkit/Chem/Mapper/validation/numerical_scope.py +36 -0
- synkit/Chem/Mapper/validation/orbits.py +137 -0
- synkit/Chem/Molecule/__init__.py +0 -0
- synkit/Chem/Molecule/_chirality_loci.py +165 -0
- synkit/Chem/Molecule/_chirality_types.py +231 -0
- synkit/Chem/Molecule/_helical_loci.py +167 -0
- synkit/Chem/Molecule/_stereo_axis_evidence.py +202 -0
- synkit/Chem/Molecule/_stereo_element_detection.py +193 -0
- synkit/Chem/Molecule/_stereo_orientation_constraints.py +511 -0
- synkit/Chem/Molecule/_tetrahedral_carriers.py +63 -0
- synkit/Chem/Molecule/atom_features.py +379 -0
- synkit/Chem/Molecule/chirality.py +1041 -0
- synkit/Chem/Molecule/cip_assignment.py +569 -0
- synkit/Chem/Molecule/cip_ranking.py +843 -0
- synkit/Chem/Molecule/coordinate_stereo.py +503 -0
- synkit/Chem/Molecule/derived_stereo_naming.py +165 -0
- synkit/Chem/Molecule/descriptors.py +452 -0
- synkit/Chem/Molecule/formula.py +249 -0
- synkit/Chem/Molecule/global_stereo.py +175 -0
- synkit/Chem/Molecule/graph_annotator.py +300 -0
- synkit/Chem/Molecule/standardize.py +817 -0
- synkit/Chem/Molecule/stereo_evidence.py +179 -0
- synkit/Chem/Molecule/stereo_perception.py +1032 -0
- synkit/Chem/Molecule/valence.py +123 -0
- synkit/Chem/Reaction/Mapper/__init__.py +44 -0
- synkit/Chem/Reaction/__init__.py +20 -0
- synkit/Chem/Reaction/aam_validator.py +5 -0
- synkit/Chem/Reaction/balance_check.py +138 -0
- synkit/Chem/Reaction/canon_rsmi.py +249 -0
- synkit/Chem/Reaction/cleaning.py +58 -0
- synkit/Chem/Reaction/deionize.py +199 -0
- synkit/Chem/Reaction/explicit_h_audit.py +232 -0
- synkit/Chem/Reaction/fix_aam.py +64 -0
- synkit/Chem/Reaction/neutralize.py +207 -0
- synkit/Chem/Reaction/radical_wildcard.py +224 -0
- synkit/Chem/Reaction/standardize.py +203 -0
- synkit/Chem/Reaction/tautomerize.py +205 -0
- synkit/Chem/__init__.py +1 -0
- synkit/Chem/utils.py +315 -0
- synkit/Data/__init__.py +0 -0
- synkit/Data/aldol.json.gz +1 -0
- synkit/Data/gen_partial_aam.py +147 -0
- synkit/Data/paracetamol.json.gz +1 -0
- synkit/Graph/Canon/__init__.py +16 -0
- synkit/Graph/Canon/_native/__init__.py +13 -0
- synkit/Graph/Canon/_native/build.py +93 -0
- synkit/Graph/Canon/_native/lib/synkit_canon.dll +0 -0
- synkit/Graph/Canon/_native/prepare.py +49 -0
- synkit/Graph/Canon/_native/runtime.py +140 -0
- synkit/Graph/Canon/_native/search.py +137 -0
- synkit/Graph/Canon/canon_algs.py +228 -0
- synkit/Graph/Canon/canon_graph.py +531 -0
- synkit/Graph/Canon/cpp/CMakeLists.txt +42 -0
- synkit/Graph/Canon/cpp/README.md +123 -0
- synkit/Graph/Canon/cpp/examples/canonical_graph.cpp +16 -0
- synkit/Graph/Canon/cpp/include/synkit_canon/canon.h +85 -0
- synkit/Graph/Canon/cpp/src/canonical.cpp +391 -0
- synkit/Graph/Canon/cpp/src/symmetry.h +83 -0
- synkit/Graph/Canon/cpp/tests/symmetry.cpp +93 -0
- synkit/Graph/Canon/exact.py +1020 -0
- synkit/Graph/Canon/exact_refinement.py +368 -0
- synkit/Graph/Canon/nauty.py +143 -0
- synkit/Graph/Context/__init__.py +0 -0
- synkit/Graph/Context/hier_context.py +244 -0
- synkit/Graph/Context/radius_expand.py +252 -0
- synkit/Graph/FG/__init__.py +19 -0
- synkit/Graph/FG/api.py +39 -0
- synkit/Graph/FG/audit.py +155 -0
- synkit/Graph/FG/catalog.py +919 -0
- synkit/Graph/FG/detector.py +288 -0
- synkit/Graph/FG/model.py +97 -0
- synkit/Graph/FG/recognizers.py +274 -0
- synkit/Graph/FG/ring_system.py +154 -0
- synkit/Graph/Feature/Descriptors/topology.py +857 -0
- synkit/Graph/Feature/Fingerprint/__init__.py +0 -0
- synkit/Graph/Feature/Fingerprint/wl_rxn_fps.py +222 -0
- synkit/Graph/Feature/__init__.py +5 -0
- synkit/Graph/Feature/graph_descriptors.py +323 -0
- synkit/Graph/Feature/graph_fps.py +96 -0
- synkit/Graph/Feature/graph_signature.py +239 -0
- synkit/Graph/Feature/hash_fps.py +133 -0
- synkit/Graph/Feature/morgan_fps.py +88 -0
- synkit/Graph/Feature/path_fps.py +81 -0
- synkit/Graph/Feature/wl_hash.py +134 -0
- synkit/Graph/Fusion/__init__.py +85 -0
- synkit/Graph/Fusion/candidate.py +559 -0
- synkit/Graph/Fusion/constructor.py +877 -0
- synkit/Graph/Fusion/identity.py +184 -0
- synkit/Graph/Fusion/interface.py +700 -0
- synkit/Graph/Fusion/search.py +241 -0
- synkit/Graph/Hyrogen/__init__.py +6 -0
- synkit/Graph/Hyrogen/_misc.py +591 -0
- synkit/Graph/Hyrogen/hcomplete.py +995 -0
- synkit/Graph/Hyrogen/hcomplete_legacy.py +368 -0
- synkit/Graph/Hyrogen/hcompletion_algorithms.py +713 -0
- synkit/Graph/Hyrogen/hextend.py +570 -0
- synkit/Graph/Hyrogen/hextend_legacy.py +215 -0
- synkit/Graph/ITS/__init__.py +4 -0
- synkit/Graph/ITS/its_builder.py +120 -0
- synkit/Graph/ITS/its_construction.py +719 -0
- synkit/Graph/ITS/its_decompose.py +572 -0
- synkit/Graph/ITS/its_destruction.py +331 -0
- synkit/Graph/ITS/its_expand.py +1251 -0
- synkit/Graph/ITS/its_relabel.py +187 -0
- synkit/Graph/ITS/its_reverter.py +356 -0
- synkit/Graph/ITS/normalize_aam.py +145 -0
- synkit/Graph/ITS/partial_its.py +238 -0
- synkit/Graph/ITS/rc_extractor.py +656 -0
- synkit/Graph/ITS/stereo.py +74 -0
- synkit/Graph/MTG/__init__.py +107 -0
- synkit/Graph/MTG/gluing.py +451 -0
- synkit/Graph/MTG/history.py +928 -0
- synkit/Graph/MTG/inventory.py +118 -0
- synkit/Graph/MTG/local.py +447 -0
- synkit/Graph/MTG/mcs_matcher.py +246 -0
- synkit/Graph/MTG/mtg.py +611 -0
- synkit/Graph/MTG/mtg_explore.py +74 -0
- synkit/Graph/MTG/observation.py +209 -0
- synkit/Graph/MTG/process.py +577 -0
- synkit/Graph/MTG/queries.py +66 -0
- synkit/Graph/MTG/realization.py +95 -0
- synkit/Graph/MTG/series_parallel.py +107 -0
- synkit/Graph/MTG/utils.py +424 -0
- synkit/Graph/Matcher/__init__.py +10 -0
- synkit/Graph/Matcher/approx_mcs.py +1264 -0
- synkit/Graph/Matcher/auto_est.py +736 -0
- synkit/Graph/Matcher/automorphism.py +314 -0
- synkit/Graph/Matcher/batch_cluster.py +224 -0
- synkit/Graph/Matcher/dedup_matches.py +247 -0
- synkit/Graph/Matcher/graph_cluster.py +191 -0
- synkit/Graph/Matcher/graph_matcher.py +312 -0
- synkit/Graph/Matcher/graph_morphism.py +421 -0
- synkit/Graph/Matcher/mcs_matcher.py +804 -0
- synkit/Graph/Matcher/multi_turbo_iso.py +186 -0
- synkit/Graph/Matcher/orbit.py +284 -0
- synkit/Graph/Matcher/partial_matcher.py +694 -0
- synkit/Graph/Matcher/sing.py +544 -0
- synkit/Graph/Matcher/subgraph_matcher.py +812 -0
- synkit/Graph/Matcher/turbo_iso.py +272 -0
- synkit/Graph/Matcher/wl_sel.py +537 -0
- synkit/Graph/Mech/__init__.py +26 -0
- synkit/Graph/Mech/conversion.py +1466 -0
- synkit/Graph/Mech/conversion_debug.py +237 -0
- synkit/Graph/Mech/electron_accounting.py +200 -0
- synkit/Graph/Mech/lwg_editor.py +406 -0
- synkit/Graph/Mech/lwg_ops.py +313 -0
- synkit/Graph/Morphism/__init__.py +99 -0
- synkit/Graph/Morphism/constraints.py +460 -0
- synkit/Graph/Morphism/llg.py +999 -0
- synkit/Graph/Morphism/morphism.py +349 -0
- synkit/Graph/Morphism/stereo.py +258 -0
- synkit/Graph/Morphism/stereo_morphism.py +383 -0
- synkit/Graph/Morphism/stereo_morphism_helpers.py +957 -0
- synkit/Graph/Morphism/stereo_morphism_types.py +117 -0
- synkit/Graph/Stereo/__init__.py +376 -0
- synkit/Graph/Stereo/_descriptor_core.py +207 -0
- synkit/Graph/Stereo/_descriptor_serialization.py +138 -0
- synkit/Graph/Stereo/canonical.py +648 -0
- synkit/Graph/Stereo/changes.py +964 -0
- synkit/Graph/Stereo/composition.py +391 -0
- synkit/Graph/Stereo/configured.py +946 -0
- synkit/Graph/Stereo/couplings.py +245 -0
- synkit/Graph/Stereo/descriptors.py +1030 -0
- synkit/Graph/Stereo/enumeration.py +597 -0
- synkit/Graph/Stereo/extended_descriptors.py +406 -0
- synkit/Graph/Stereo/global_configured.py +152 -0
- synkit/Graph/Stereo/global_stereo.py +304 -0
- synkit/Graph/Stereo/identity.py +648 -0
- synkit/Graph/Stereo/interchange.py +496 -0
- synkit/Graph/Stereo/legacy.py +460 -0
- synkit/Graph/Stereo/matching.py +899 -0
- synkit/Graph/Stereo/native.py +154 -0
- synkit/Graph/Stereo/orbits.py +668 -0
- synkit/Graph/Stereo/outcomes.py +99 -0
- synkit/Graph/Stereo/pairwise.py +350 -0
- synkit/Graph/Stereo/rdkit_adapter.py +662 -0
- synkit/Graph/Stereo/rdkit_identity.py +118 -0
- synkit/Graph/Stereo/rdkit_resonance.py +140 -0
- synkit/Graph/Stereo/reaction_interchange.py +266 -0
- synkit/Graph/Stereo/semantics.py +296 -0
- synkit/Graph/Stereo/supports.py +257 -0
- synkit/Graph/Stereo/wire.py +339 -0
- synkit/Graph/Wildcard/__init__.py +0 -0
- synkit/Graph/Wildcard/fuse_graph.py +156 -0
- synkit/Graph/Wildcard/graph_wc.py +303 -0
- synkit/Graph/Wildcard/its_merge.py +857 -0
- synkit/Graph/Wildcard/radwc.py +114 -0
- synkit/Graph/Wildcard/wc_matcher.py +576 -0
- synkit/Graph/Wildcard/wildcard.py +225 -0
- synkit/Graph/__init__.py +17 -0
- synkit/Graph/canon_graph.py +14 -0
- synkit/Graph/syn_graph.py +199 -0
- synkit/Graph/utils.py +324 -0
- synkit/IO/__init__.py +4 -0
- synkit/IO/chem_converter.py +1059 -0
- synkit/IO/combinatorial/__init__.py +8 -0
- synkit/IO/combinatorial/gml_to_graph.py +223 -0
- synkit/IO/combinatorial/graph_to_gml.py +280 -0
- synkit/IO/combinatorial/graph_to_smarts.py +186 -0
- synkit/IO/combinatorial/smarts_expander.py +147 -0
- synkit/IO/combinatorial/smarts_generalizer.py +133 -0
- synkit/IO/combinatorial/smarts_to_graph.py +179 -0
- synkit/IO/conversion.py +42 -0
- synkit/IO/data_io.py +269 -0
- synkit/IO/data_process.py +143 -0
- synkit/IO/debug.py +73 -0
- synkit/IO/gml_to_nx.py +150 -0
- synkit/IO/graph_to_mol.py +199 -0
- synkit/IO/mol_to_graph.py +1105 -0
- synkit/IO/mol_to_graph_chemistry.py +545 -0
- synkit/IO/nx_to_gml.py +209 -0
- synkit/IO/smiles_to_id.py +123 -0
- synkit/Mechanism/__init__.py +151 -0
- synkit/Mechanism/adapters.py +136 -0
- synkit/Mechanism/audit.py +323 -0
- synkit/Mechanism/benchmark.py +638 -0
- synkit/Mechanism/compatibility.py +13 -0
- synkit/Mechanism/drawing.py +85 -0
- synkit/Mechanism/electron_models.py +725 -0
- synkit/Mechanism/equivalence.py +521 -0
- synkit/Mechanism/evidence.py +262 -0
- synkit/Mechanism/interchange.py +141 -0
- synkit/Mechanism/model.py +918 -0
- synkit/Mechanism/neighbor_stereo.py +312 -0
- synkit/Mechanism/radical_data.py +740 -0
- synkit/Mechanism/replay.py +986 -0
- synkit/Mechanism/schema.py +264 -0
- synkit/Mechanism/stereo_state.py +416 -0
- synkit/Mechanism/symbols.py +91 -0
- synkit/Rule/Apply/__init__.py +35 -0
- synkit/Rule/Apply/conditions.py +45 -0
- synkit/Rule/Apply/dpo.py +998 -0
- synkit/Rule/Apply/rule_matcher.py +218 -0
- synkit/Rule/Apply/validation.py +354 -0
- synkit/Rule/Compose/__init__.py +106 -0
- synkit/Rule/Compose/_identity.py +43 -0
- synkit/Rule/Compose/composition.py +976 -0
- synkit/Rule/Compose/laws.py +439 -0
- synkit/Rule/Compose/search.py +771 -0
- synkit/Rule/Compose/synrule.py +170 -0
- synkit/Rule/Modify/__init__.py +0 -0
- synkit/Rule/Modify/implict_rule.py +56 -0
- synkit/Rule/Modify/longest_path.py +92 -0
- synkit/Rule/Modify/molecule_rule.py +115 -0
- synkit/Rule/Modify/prune_templates.py +78 -0
- synkit/Rule/Modify/rule_utils.py +194 -0
- synkit/Rule/Modify/strip_rule.py +97 -0
- synkit/Rule/__init__.py +28 -0
- synkit/Rule/generic_stereo.py +884 -0
- synkit/Rule/stereo_identity.py +271 -0
- synkit/Rule/syn_rule.py +1090 -0
- synkit/Synthesis/MSR/__init__.py +0 -0
- synkit/Synthesis/MSR/path_finder.py +239 -0
- synkit/Synthesis/Metrics/__init__.py +0 -0
- synkit/Synthesis/Metrics/_base.py +52 -0
- synkit/Synthesis/Metrics/_plot.py +129 -0
- synkit/Synthesis/Metrics/_ranking.py +179 -0
- synkit/Synthesis/RBL/__init__.py +80 -0
- synkit/Synthesis/RBL/engine.py +1249 -0
- synkit/Synthesis/RBL/matching.py +652 -0
- synkit/Synthesis/RBL/overlap.py +336 -0
- synkit/Synthesis/RBL/pipeline.py +376 -0
- synkit/Synthesis/RBL/policy.py +139 -0
- synkit/Synthesis/RBL/proof.py +370 -0
- synkit/Synthesis/RBL/reaction.py +506 -0
- synkit/Synthesis/RBL/state.py +169 -0
- synkit/Synthesis/RBL/validation.py +1060 -0
- synkit/Synthesis/Reactor/__init__.py +60 -0
- synkit/Synthesis/Reactor/core/__init__.py +1 -0
- synkit/Synthesis/Reactor/core/engine.py +967 -0
- synkit/Synthesis/Reactor/core/product.py +621 -0
- synkit/Synthesis/Reactor/core/rewrite.py +561 -0
- synkit/Synthesis/Reactor/core/strategy.py +44 -0
- synkit/Synthesis/Reactor/matching/__init__.py +1 -0
- synkit/Synthesis/Reactor/matching/mixin.py +920 -0
- synkit/Synthesis/Reactor/matching/policy.py +345 -0
- synkit/Synthesis/Reactor/matching/symmetry.py +309 -0
- synkit/Synthesis/Reactor/output/__init__.py +1 -0
- synkit/Synthesis/Reactor/output/deduplication.py +703 -0
- synkit/Synthesis/Reactor/output/policy.py +16 -0
- synkit/Synthesis/Reactor/output/serialization.py +485 -0
- synkit/Synthesis/Reactor/output/structural.py +893 -0
- synkit/Synthesis/Reactor/stereo/__init__.py +1 -0
- synkit/Synthesis/Reactor/stereo/assignment.py +35 -0
- synkit/Synthesis/Reactor/stereo/mixin.py +780 -0
- synkit/Synthesis/Reactor/variants/__init__.py +1 -0
- synkit/Synthesis/Reactor/variants/imbalanced.py +180 -0
- synkit/Synthesis/Reactor/variants/partial.py +84 -0
- synkit/Synthesis/Reactor/workflow/__init__.py +1 -0
- synkit/Synthesis/Reactor/workflow/batch.py +550 -0
- synkit/Synthesis/Reactor/workflow/benchmark.py +143 -0
- synkit/Synthesis/Reactor/workflow/postprocess.py +267 -0
- synkit/Synthesis/Reactor/workflow/rule_filter.py +210 -0
- synkit/Synthesis/__init__.py +0 -0
- synkit/Synthesis/reactor_utils.py +316 -0
- synkit/Utils/__init__.py +0 -0
- synkit/Utils/utils.py +172 -0
- synkit/Vis/__init__.py +91 -0
- synkit/Vis/crn/__init__.py +40 -0
- synkit/Vis/epd/__init__.py +20 -0
- synkit/Vis/epd/arrows.py +425 -0
- synkit/Vis/epd/chem.py +197 -0
- synkit/Vis/epd/constants.py +83 -0
- synkit/Vis/epd/layout.py +283 -0
- synkit/Vis/epd/mapping.py +51 -0
- synkit/Vis/epd/models.py +136 -0
- synkit/Vis/epd/render.py +543 -0
- synkit/Vis/epd/utils.py +108 -0
- synkit/Vis/epd/visualizer.py +811 -0
- synkit/Vis/graph_visualizer.py +375 -0
- synkit/Vis/its/__init__.py +19 -0
- synkit/Vis/its/drawer.py +615 -0
- synkit/Vis/its_drawer.py +35 -0
- synkit/Vis/molecule/__init__.py +31 -0
- synkit/Vis/molecule/drawer.py +563 -0
- synkit/Vis/molecule/reaction.py +285 -0
- synkit/Vis/molecule_drawer.py +33 -0
- synkit/Vis/mtg/__init__.py +17 -0
- synkit/Vis/mtg/drawer.py +769 -0
- synkit/Vis/mtg_drawer.py +34 -0
- synkit/Vis/pdf_writer.py +146 -0
- synkit/Vis/reaction/__init__.py +19 -0
- synkit/Vis/reaction/rule.py +141 -0
- synkit/Vis/reaction/rxn.py +532 -0
- synkit/Vis/reaction_drawer.py +41 -0
- synkit/Vis/space/__init__.py +17 -0
- synkit/Vis/space/chemical.py +80 -0
- synkit/Vis/space/embedding.py +87 -0
- synkit/Vis/vis_synedu/Vis/__init__.py +7 -0
- synkit/Vis/vis_synedu/Vis/dpo.py +862 -0
- synkit/Vis/vis_synedu/rxn_vis.py +377 -0
- synkit/Vis/vis_synedu/vis.py +501 -0
- synkit/Vis/visual_drawer.py +215 -0
- synkit/Vis/visual_model.py +520 -0
- synkit/__init__.py +3 -0
- synkit/_native.py +91 -0
- synkit/examples.py +50 -0
- synkit/version.py +23 -0
- synkit-1.6.5.dist-info/METADATA +156 -0
- synkit-1.6.5.dist-info/RECORD +636 -0
- synkit-1.6.5.dist-info/WHEEL +4 -0
- synkit-1.6.5.dist-info/licenses/LICENSE +21 -0
- synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/COPYRIGHT +44 -0
- synkit-1.6.5.dist-info/licenses/synkit/Chem/Mapper/solvers/native/cpp/src/symmetry/vendor/nauty/LICENSE-2.0.txt +202 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any, Dict, Iterable, List, Tuple
|
|
3
|
+
from rdkit import Chem
|
|
4
|
+
|
|
5
|
+
from synkit.Graph.syn_graph import SynGraph
|
|
6
|
+
from synkit.IO import setup_logging
|
|
7
|
+
from synkit.IO.graph_to_mol import GraphToMol
|
|
8
|
+
from synkit.IO.chem_converter import smiles_to_graph
|
|
9
|
+
from synkit.Graph.Matcher.graph_morphism import find_graph_isomorphism
|
|
10
|
+
|
|
11
|
+
logger = setup_logging("INFO")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ITSRelabel:
|
|
15
|
+
"""Extend reaction SMILES through atom-map alignment between reactant and
|
|
16
|
+
product SynGraphs.
|
|
17
|
+
|
|
18
|
+
:cvar logger: Logger instance for debug and info messages.
|
|
19
|
+
:type logger: logging.Logger
|
|
20
|
+
:ivar graph_to_mol: Converter from SynGraph to RDKit Mol.
|
|
21
|
+
:type graph_to_mol: GraphToMol
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self) -> None:
|
|
25
|
+
"""Initialize ITSRelabel with default GraphToMol converter."""
|
|
26
|
+
self.graph_to_mol = GraphToMol()
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _get_nodes_with_atom_map(graph: SynGraph) -> List[Any]:
|
|
30
|
+
"""Extract node IDs with a non-zero atom_map attribute from a SynGraph.
|
|
31
|
+
|
|
32
|
+
:param graph: Input SynGraph with 'atom_map' on nodes.
|
|
33
|
+
:type graph: SynGraph
|
|
34
|
+
:return: List of node identifiers where 'atom_map' != 0.
|
|
35
|
+
:rtype: List[Any]
|
|
36
|
+
"""
|
|
37
|
+
return [
|
|
38
|
+
n
|
|
39
|
+
for n, attrs in graph.raw.nodes(data=True)
|
|
40
|
+
if attrs.get("atom_map", 0) != 0
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _remove_internal_edges(graph: SynGraph, nodes: List[Any]) -> SynGraph:
|
|
45
|
+
"""Remove edges connecting nodes in the given list from a SynGraph.
|
|
46
|
+
|
|
47
|
+
:param graph: Input SynGraph to prune.
|
|
48
|
+
:type graph: SynGraph
|
|
49
|
+
:param nodes: Node IDs whose connecting edges will be removed.
|
|
50
|
+
:type nodes: List[Any]
|
|
51
|
+
:return: A new SynGraph with specified edges removed.
|
|
52
|
+
:rtype: SynGraph
|
|
53
|
+
"""
|
|
54
|
+
G_copy = SynGraph(graph.raw.copy())
|
|
55
|
+
node_set = set(nodes)
|
|
56
|
+
edges_to_remove = [
|
|
57
|
+
(u, v) for u, v in G_copy.raw.edges() if u in node_set and v in node_set
|
|
58
|
+
]
|
|
59
|
+
G_copy.raw.remove_edges_from(edges_to_remove)
|
|
60
|
+
return G_copy
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _dict_to_tuple_list(
|
|
64
|
+
mapping: Dict[Any, Any], sort_by_key: bool = False, sort_by_value: bool = False
|
|
65
|
+
) -> List[Tuple[Any, Any]]:
|
|
66
|
+
"""Convert a mapping dict into a sorted list of tuples.
|
|
67
|
+
|
|
68
|
+
:param mapping: Dictionary to convert.
|
|
69
|
+
:type mapping: Dict[Any, Any]
|
|
70
|
+
:param sort_by_key: Sort tuples by key if True.
|
|
71
|
+
:type sort_by_key: bool
|
|
72
|
+
:param sort_by_value: Sort tuples by value if True.
|
|
73
|
+
:type sort_by_value: bool
|
|
74
|
+
:return: List of (key, value) tuples.
|
|
75
|
+
:rtype: List[Tuple[Any, Any]]
|
|
76
|
+
"""
|
|
77
|
+
items = list(mapping.items())
|
|
78
|
+
if sort_by_key:
|
|
79
|
+
items.sort(key=lambda x: x[0])
|
|
80
|
+
elif sort_by_value:
|
|
81
|
+
items.sort(key=lambda x: x[1])
|
|
82
|
+
return items
|
|
83
|
+
|
|
84
|
+
def _update_mapping(
|
|
85
|
+
self,
|
|
86
|
+
G: SynGraph,
|
|
87
|
+
H: SynGraph,
|
|
88
|
+
mapping: Iterable[Tuple[Any, Any]],
|
|
89
|
+
aam_key: str = "atom_map",
|
|
90
|
+
) -> Tuple[SynGraph, SynGraph]:
|
|
91
|
+
"""Update node attributes in two SynGraphs based on a sequential
|
|
92
|
+
mapping.
|
|
93
|
+
|
|
94
|
+
This method resets the specified atom-map attribute for all
|
|
95
|
+
nodes in both graphs to 0, then assigns a new atom-map value
|
|
96
|
+
(i+1) for each mapped pair: G.nodes[g_node][aam_key] = i + 1
|
|
97
|
+
H.nodes[h_node][aam_key] = i + 1
|
|
98
|
+
|
|
99
|
+
:param G: First SynGraph to update (reactant).
|
|
100
|
+
:type G: SynGraph
|
|
101
|
+
:param H: Second SynGraph to update (product).
|
|
102
|
+
:type H: SynGraph
|
|
103
|
+
:param mapping: Iterable of (g_node, h_node) tuples defining
|
|
104
|
+
node correspondence.
|
|
105
|
+
:type mapping: Iterable[Tuple[Any, Any]]
|
|
106
|
+
:param aam_key: Name of the atom-map attribute on each node.
|
|
107
|
+
:type aam_key: str
|
|
108
|
+
:return: Tuple of updated SynGraphs (G_updated, H_updated).
|
|
109
|
+
:rtype: Tuple[SynGraph, SynGraph]
|
|
110
|
+
"""
|
|
111
|
+
# Create deep copies of raw graphs
|
|
112
|
+
G_copy = SynGraph(G.raw.copy())
|
|
113
|
+
H_copy = SynGraph(H.raw.copy())
|
|
114
|
+
|
|
115
|
+
# Reset atom-map to zero for all nodes
|
|
116
|
+
for node in G_copy.raw.nodes():
|
|
117
|
+
G_copy.raw.nodes[node][aam_key] = 0
|
|
118
|
+
for node in H_copy.raw.nodes():
|
|
119
|
+
H_copy.raw.nodes[node][aam_key] = 0
|
|
120
|
+
|
|
121
|
+
# Assign new sequential mapping values
|
|
122
|
+
for i, (g_node, h_node) in enumerate(mapping):
|
|
123
|
+
value = i + 1
|
|
124
|
+
if g_node in G_copy.raw:
|
|
125
|
+
G_copy.raw.nodes[g_node][aam_key] = value
|
|
126
|
+
else:
|
|
127
|
+
logger.warning(f"Node {g_node} not found in reactant graph")
|
|
128
|
+
if h_node in H_copy.raw:
|
|
129
|
+
H_copy.raw.nodes[h_node][aam_key] = value
|
|
130
|
+
else:
|
|
131
|
+
logger.warning(f"Node {h_node} not found in product graph")
|
|
132
|
+
|
|
133
|
+
return G_copy, H_copy
|
|
134
|
+
|
|
135
|
+
def fit(self, rsmi: str) -> str:
|
|
136
|
+
"""Generate an extended reaction SMILES by aligning atom maps of
|
|
137
|
+
reactant and product.
|
|
138
|
+
|
|
139
|
+
:param rsmi: Reaction SMILES string formatted as 'reactant>>product'.
|
|
140
|
+
:type rsmi: str
|
|
141
|
+
:return: Extended reaction SMILES after remapping.
|
|
142
|
+
:rtype: str
|
|
143
|
+
:raises ValueError: If input format is invalid or graphs are not isomorphic.
|
|
144
|
+
|
|
145
|
+
.. rubric:: Examples
|
|
146
|
+
|
|
147
|
+
>>> its = ITSRelabel()
|
|
148
|
+
>>> its.fit('CCO:1>>CC=O:1')
|
|
149
|
+
'CCO>>CC=O'
|
|
150
|
+
"""
|
|
151
|
+
# Parse reaction SMILES
|
|
152
|
+
try:
|
|
153
|
+
react_smiles, prod_smiles = rsmi.split(">>")
|
|
154
|
+
except ValueError as e:
|
|
155
|
+
raise ValueError("Expected 'reactant>>product' format") from e
|
|
156
|
+
|
|
157
|
+
# Convert to SynGraphs
|
|
158
|
+
G = SynGraph(
|
|
159
|
+
smiles_to_graph(
|
|
160
|
+
react_smiles, drop_non_aam=False, use_index_as_atom_map=False
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
H = SynGraph(
|
|
164
|
+
smiles_to_graph(
|
|
165
|
+
prod_smiles, drop_non_aam=False, use_index_as_atom_map=False
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Identify and prune mapped nodes
|
|
170
|
+
R_nodes = self._get_nodes_with_atom_map(G)
|
|
171
|
+
P_nodes = self._get_nodes_with_atom_map(H)
|
|
172
|
+
Gp = self._remove_internal_edges(G, R_nodes)
|
|
173
|
+
Hp = self._remove_internal_edges(H, P_nodes)
|
|
174
|
+
|
|
175
|
+
# Find isomorphism mapping
|
|
176
|
+
mapping = find_graph_isomorphism(Gp.raw, Hp.raw, use_defaults=True)
|
|
177
|
+
if not mapping:
|
|
178
|
+
raise ValueError("No isomorphism found between pruned graphs")
|
|
179
|
+
|
|
180
|
+
# Update atom_map attributes
|
|
181
|
+
mapped_pairs = self._dict_to_tuple_list(mapping, sort_by_key=True)
|
|
182
|
+
G_new, H_new = self._update_mapping(G, H, mapped_pairs, aam_key="atom_map")
|
|
183
|
+
|
|
184
|
+
# Convert back to molecules and SMILES
|
|
185
|
+
mol_G = self.graph_to_mol.graph_to_mol(G_new.raw, use_h_count=True)
|
|
186
|
+
mol_H = self.graph_to_mol.graph_to_mol(H_new.raw, use_h_count=True)
|
|
187
|
+
return f"{Chem.MolToSmiles(mol_G)}>>{Chem.MolToSmiles(mol_H)}"
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal, Optional
|
|
4
|
+
import networkx as nx
|
|
5
|
+
|
|
6
|
+
Side = Literal["reactant", "product", "r", "p", 0, 1]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ITSReverter:
|
|
10
|
+
"""Reconstruct reactant/product molecular graphs from an ITS-style graph.
|
|
11
|
+
|
|
12
|
+
Expected ITS format
|
|
13
|
+
-------------------
|
|
14
|
+
Nodes store paired attributes such as::
|
|
15
|
+
|
|
16
|
+
element=('C', 'C')
|
|
17
|
+
charge=(0, 0)
|
|
18
|
+
hcount=(3, 3)
|
|
19
|
+
|
|
20
|
+
Edges store paired attributes such as::
|
|
21
|
+
|
|
22
|
+
order=(1.0, 0.0)
|
|
23
|
+
bond_type=('SINGLE', '')
|
|
24
|
+
conjugated=(False, False)
|
|
25
|
+
|
|
26
|
+
where index 0 = reactant side and index 1 = product side.
|
|
27
|
+
|
|
28
|
+
.. rubric:: Notes
|
|
29
|
+
|
|
30
|
+
- ``typesGH`` is not required for reconstruction.
|
|
31
|
+
- ``standard_order`` is a reaction-delta attribute and is not used to rebuild
|
|
32
|
+
a side-specific molecular graph.
|
|
33
|
+
- By default, isolated atoms are kept. This is important for cases where a
|
|
34
|
+
mapped atom becomes disconnected on one side.
|
|
35
|
+
|
|
36
|
+
.. rubric:: Example
|
|
37
|
+
|
|
38
|
+
.. code-block:: python
|
|
39
|
+
|
|
40
|
+
reverter = ITSReverter(its)
|
|
41
|
+
|
|
42
|
+
g_reactant = reverter.to_reactant_graph()
|
|
43
|
+
g_product = reverter.to_product_graph()
|
|
44
|
+
|
|
45
|
+
print(g_reactant.nodes(data=True))
|
|
46
|
+
print(g_product.edges(data=True))
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
#: node attributes commonly stored in ITS and worth restoring
|
|
50
|
+
DEFAULT_NODE_ATTRS = (
|
|
51
|
+
"element",
|
|
52
|
+
"aromatic",
|
|
53
|
+
"hcount",
|
|
54
|
+
"charge",
|
|
55
|
+
"hybridization",
|
|
56
|
+
"neighbors",
|
|
57
|
+
"atom_map",
|
|
58
|
+
"lone_pairs",
|
|
59
|
+
"radical",
|
|
60
|
+
"valence_electrons",
|
|
61
|
+
"h_pairs",
|
|
62
|
+
"h_pairs_left",
|
|
63
|
+
"h_pairs_right",
|
|
64
|
+
"h_pair_atom_maps",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
#: edge attributes commonly stored in ITS and worth restoring
|
|
68
|
+
DEFAULT_EDGE_ATTRS = (
|
|
69
|
+
"kekule_order",
|
|
70
|
+
"sigma_order",
|
|
71
|
+
"pi_order",
|
|
72
|
+
"order",
|
|
73
|
+
"bond_type",
|
|
74
|
+
"conjugated",
|
|
75
|
+
"in_ring",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def __init__(self, its_graph: nx.Graph):
|
|
79
|
+
"""
|
|
80
|
+
:param its_graph: ITS graph with paired node/edge attributes.
|
|
81
|
+
:type its_graph: nx.Graph
|
|
82
|
+
"""
|
|
83
|
+
self.its_graph = its_graph
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def _side_to_index(side: Side) -> int:
|
|
87
|
+
"""Convert side identifier to tuple index.
|
|
88
|
+
|
|
89
|
+
:param side: Side specifier.
|
|
90
|
+
:type side: Side
|
|
91
|
+
:return: 0 for reactant, 1 for product.
|
|
92
|
+
:rtype: int
|
|
93
|
+
:raises ValueError: If the side is invalid.
|
|
94
|
+
"""
|
|
95
|
+
if side in ("reactant", "r", 0):
|
|
96
|
+
return 0
|
|
97
|
+
if side in ("product", "p", 1):
|
|
98
|
+
return 1
|
|
99
|
+
raise ValueError(f"Unsupported side: {side!r}")
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def _pick_side_value(value: Any, idx: int) -> Any:
|
|
103
|
+
"""Pick one side from a paired ITS attribute.
|
|
104
|
+
|
|
105
|
+
If ``value`` is a 2-tuple, return the selected side.
|
|
106
|
+
Otherwise return the value unchanged.
|
|
107
|
+
|
|
108
|
+
:param value: Attribute value.
|
|
109
|
+
:type value: Any
|
|
110
|
+
:param idx: Side index.
|
|
111
|
+
:type idx: int
|
|
112
|
+
:return: Side-specific value.
|
|
113
|
+
:rtype: Any
|
|
114
|
+
"""
|
|
115
|
+
if isinstance(value, tuple) and len(value) == 2:
|
|
116
|
+
return value[idx]
|
|
117
|
+
return value
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def _node_exists_on_side(cls, attrs: dict[str, Any], idx: int) -> bool:
|
|
121
|
+
"""Decide whether a node exists on a given side.
|
|
122
|
+
|
|
123
|
+
A node is treated as present if its side-specific ``element`` is not empty.
|
|
124
|
+
|
|
125
|
+
:param attrs: ITS node attributes.
|
|
126
|
+
:type attrs: dict[str, Any]
|
|
127
|
+
:param idx: Side index.
|
|
128
|
+
:type idx: int
|
|
129
|
+
:return: Whether the node exists on that side.
|
|
130
|
+
:rtype: bool
|
|
131
|
+
"""
|
|
132
|
+
present = attrs.get("present")
|
|
133
|
+
if isinstance(present, tuple) and len(present) == 2:
|
|
134
|
+
return bool(present[idx])
|
|
135
|
+
|
|
136
|
+
element = cls._pick_side_value(attrs.get("element"), idx)
|
|
137
|
+
return element not in (None, "")
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def _edge_exists_on_side(cls, attrs: dict[str, Any], idx: int) -> bool:
|
|
141
|
+
"""Decide whether an edge exists on a given side.
|
|
142
|
+
|
|
143
|
+
Priority:
|
|
144
|
+
1. ``order``
|
|
145
|
+
2. ``kekule_order``
|
|
146
|
+
3. ``bond_type``
|
|
147
|
+
|
|
148
|
+
:param attrs: ITS edge attributes.
|
|
149
|
+
:type attrs: dict[str, Any]
|
|
150
|
+
:param idx: Side index.
|
|
151
|
+
:type idx: int
|
|
152
|
+
:return: Whether the bond exists on that side.
|
|
153
|
+
:rtype: bool
|
|
154
|
+
"""
|
|
155
|
+
order = cls._pick_side_value(attrs.get("order"), idx)
|
|
156
|
+
if order not in (None, "", 0, 0.0):
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
kekule_order = cls._pick_side_value(attrs.get("kekule_order"), idx)
|
|
160
|
+
if kekule_order not in (None, "", 0, 0.0):
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
bond_type = cls._pick_side_value(attrs.get("bond_type"), idx)
|
|
164
|
+
if bond_type not in (None, ""):
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def _extract_side_attrs(
|
|
171
|
+
cls,
|
|
172
|
+
attrs: dict[str, Any],
|
|
173
|
+
idx: int,
|
|
174
|
+
keep_keys: Optional[tuple[str, ...]] = None,
|
|
175
|
+
exclude_keys: Optional[set[str]] = None,
|
|
176
|
+
) -> dict[str, Any]:
|
|
177
|
+
"""Extract side-specific attributes from a paired ITS attribute dict.
|
|
178
|
+
|
|
179
|
+
:param attrs: ITS attribute dictionary.
|
|
180
|
+
:type attrs: dict[str, Any]
|
|
181
|
+
:param idx: Side index.
|
|
182
|
+
:type idx: int
|
|
183
|
+
:param keep_keys: Optional whitelist of keys to keep.
|
|
184
|
+
:type keep_keys: Optional[tuple[str, ...]]
|
|
185
|
+
:param exclude_keys: Optional set of keys to exclude.
|
|
186
|
+
:type exclude_keys: Optional[set[str]]
|
|
187
|
+
:return: Flattened side-specific attribute dictionary.
|
|
188
|
+
:rtype: dict[str, Any]
|
|
189
|
+
"""
|
|
190
|
+
out: dict[str, Any] = {}
|
|
191
|
+
exclude_keys = exclude_keys or set()
|
|
192
|
+
|
|
193
|
+
for key, value in attrs.items():
|
|
194
|
+
if keep_keys is not None and key not in keep_keys:
|
|
195
|
+
continue
|
|
196
|
+
if key in exclude_keys:
|
|
197
|
+
continue
|
|
198
|
+
out[key] = cls._pick_side_value(value, idx)
|
|
199
|
+
|
|
200
|
+
return out
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def _recompute_neighbors(graph: nx.Graph) -> None:
|
|
204
|
+
"""
|
|
205
|
+
Recompute ``neighbors`` attribute from graph connectivity.
|
|
206
|
+
|
|
207
|
+
The value is stored as a sorted list of neighboring element symbols.
|
|
208
|
+
|
|
209
|
+
:param graph: Molecular graph.
|
|
210
|
+
:type graph: nx.Graph
|
|
211
|
+
"""
|
|
212
|
+
for node in graph.nodes:
|
|
213
|
+
neighbor_elements = []
|
|
214
|
+
for nb in graph.neighbors(node):
|
|
215
|
+
elem = graph.nodes[nb].get("element")
|
|
216
|
+
neighbor_elements.append(elem)
|
|
217
|
+
graph.nodes[node]["neighbors"] = sorted(neighbor_elements)
|
|
218
|
+
|
|
219
|
+
def to_graph(
|
|
220
|
+
self,
|
|
221
|
+
side: Side,
|
|
222
|
+
node_attrs: Optional[tuple[str, ...]] = None,
|
|
223
|
+
edge_attrs: Optional[tuple[str, ...]] = None,
|
|
224
|
+
recompute_neighbors: bool = False,
|
|
225
|
+
drop_isolated: bool = False,
|
|
226
|
+
) -> nx.Graph:
|
|
227
|
+
"""Reconstruct one side-specific molecular graph.
|
|
228
|
+
|
|
229
|
+
:param side:
|
|
230
|
+
Which side to reconstruct.
|
|
231
|
+
Accepted values: ``"reactant"``, ``"product"``, ``"r"``, ``"p"``, ``0``, ``1``.
|
|
232
|
+
:type side: Side
|
|
233
|
+
:param node_attrs:
|
|
234
|
+
Node attribute keys to keep. If ``None``, uses ``DEFAULT_NODE_ATTRS``.
|
|
235
|
+
:type node_attrs: Optional[tuple[str, ...]]
|
|
236
|
+
:param edge_attrs:
|
|
237
|
+
Edge attribute keys to keep. If ``None``, uses ``DEFAULT_EDGE_ATTRS``.
|
|
238
|
+
:type edge_attrs: Optional[tuple[str, ...]]
|
|
239
|
+
:param recompute_neighbors:
|
|
240
|
+
If ``True``, overwrite the stored ``neighbors`` attribute using the
|
|
241
|
+
reconstructed graph connectivity.
|
|
242
|
+
:type recompute_neighbors: bool
|
|
243
|
+
:param drop_isolated:
|
|
244
|
+
If ``True``, remove nodes with degree 0 after reconstruction.
|
|
245
|
+
Default is ``False`` so disconnected mapped atoms are preserved.
|
|
246
|
+
:type drop_isolated: bool
|
|
247
|
+
:return:
|
|
248
|
+
Side-specific molecular graph.
|
|
249
|
+
:rtype: nx.Graph
|
|
250
|
+
"""
|
|
251
|
+
idx = self._side_to_index(side)
|
|
252
|
+
node_attrs = node_attrs or self.DEFAULT_NODE_ATTRS
|
|
253
|
+
edge_attrs = edge_attrs or self.DEFAULT_EDGE_ATTRS
|
|
254
|
+
|
|
255
|
+
g = nx.Graph()
|
|
256
|
+
|
|
257
|
+
# nodes
|
|
258
|
+
for node, attrs in self.its_graph.nodes(data=True):
|
|
259
|
+
if not self._node_exists_on_side(attrs, idx):
|
|
260
|
+
continue
|
|
261
|
+
|
|
262
|
+
flat_node_attrs = self._extract_side_attrs(
|
|
263
|
+
attrs=attrs,
|
|
264
|
+
idx=idx,
|
|
265
|
+
keep_keys=node_attrs,
|
|
266
|
+
exclude_keys={"typesGH"},
|
|
267
|
+
)
|
|
268
|
+
g.add_node(node, **flat_node_attrs)
|
|
269
|
+
|
|
270
|
+
# edges
|
|
271
|
+
for u, v, attrs in self.its_graph.edges(data=True):
|
|
272
|
+
if u not in g or v not in g:
|
|
273
|
+
continue
|
|
274
|
+
if not self._edge_exists_on_side(attrs, idx):
|
|
275
|
+
continue
|
|
276
|
+
|
|
277
|
+
flat_edge_attrs = self._extract_side_attrs(
|
|
278
|
+
attrs=attrs,
|
|
279
|
+
idx=idx,
|
|
280
|
+
keep_keys=edge_attrs,
|
|
281
|
+
exclude_keys={"standard_order"},
|
|
282
|
+
)
|
|
283
|
+
g.add_edge(u, v, **flat_edge_attrs)
|
|
284
|
+
|
|
285
|
+
if recompute_neighbors:
|
|
286
|
+
self._recompute_neighbors(g)
|
|
287
|
+
|
|
288
|
+
if drop_isolated:
|
|
289
|
+
isolates = list(nx.isolates(g))
|
|
290
|
+
g.remove_nodes_from(isolates)
|
|
291
|
+
|
|
292
|
+
stereo = self.its_graph.graph.get("stereo_descriptors", {})
|
|
293
|
+
if isinstance(stereo, dict) and ("reactant" in stereo or "product" in stereo):
|
|
294
|
+
side_name = "reactant" if idx == 0 else "product"
|
|
295
|
+
g.graph["stereo_descriptors"] = dict(stereo.get(side_name, {}))
|
|
296
|
+
g.graph["stereo_projection"] = side_name
|
|
297
|
+
elif isinstance(stereo, dict):
|
|
298
|
+
g.graph["stereo_descriptors"] = dict(stereo)
|
|
299
|
+
|
|
300
|
+
return g
|
|
301
|
+
|
|
302
|
+
def to_reactant_graph(
|
|
303
|
+
self,
|
|
304
|
+
recompute_neighbors: bool = False,
|
|
305
|
+
drop_isolated: bool = False,
|
|
306
|
+
) -> nx.Graph:
|
|
307
|
+
"""Reconstruct the reactant-side graph.
|
|
308
|
+
|
|
309
|
+
:param recompute_neighbors: Whether to recompute ``neighbors``.
|
|
310
|
+
:type recompute_neighbors: bool
|
|
311
|
+
:param drop_isolated: Whether to remove isolated nodes.
|
|
312
|
+
:type drop_isolated: bool
|
|
313
|
+
:return: Reactant molecular graph.
|
|
314
|
+
:rtype: nx.Graph
|
|
315
|
+
"""
|
|
316
|
+
return self.to_graph(
|
|
317
|
+
side="reactant",
|
|
318
|
+
recompute_neighbors=recompute_neighbors,
|
|
319
|
+
drop_isolated=drop_isolated,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def to_product_graph(
|
|
323
|
+
self,
|
|
324
|
+
recompute_neighbors: bool = False,
|
|
325
|
+
drop_isolated: bool = False,
|
|
326
|
+
) -> nx.Graph:
|
|
327
|
+
"""Reconstruct the product-side graph.
|
|
328
|
+
|
|
329
|
+
:param recompute_neighbors: Whether to recompute ``neighbors``.
|
|
330
|
+
:type recompute_neighbors: bool
|
|
331
|
+
:param drop_isolated: Whether to remove isolated nodes.
|
|
332
|
+
:type drop_isolated: bool
|
|
333
|
+
:return: Product molecular graph.
|
|
334
|
+
:rtype: nx.Graph
|
|
335
|
+
"""
|
|
336
|
+
return self.to_graph(
|
|
337
|
+
side="product",
|
|
338
|
+
recompute_neighbors=recompute_neighbors,
|
|
339
|
+
drop_isolated=drop_isolated,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def to_transition_state_graph(self) -> nx.Graph:
|
|
343
|
+
"""Return an ITS projection carrying only transition-state stereo.
|
|
344
|
+
|
|
345
|
+
Structural node and edge attributes remain paired because the ITS is
|
|
346
|
+
SynKit's transition-state representation. The stereo registry is
|
|
347
|
+
flattened to the optional ``transition`` descriptors so ordinary
|
|
348
|
+
registry consumers can inspect fleeting stereo without confusing it
|
|
349
|
+
with either stable endpoint.
|
|
350
|
+
"""
|
|
351
|
+
graph = self.its_graph.copy()
|
|
352
|
+
stereo = self.its_graph.graph.get("stereo_descriptors", {})
|
|
353
|
+
transition = stereo.get("transition", {}) if isinstance(stereo, dict) else {}
|
|
354
|
+
graph.graph["stereo_descriptors"] = dict(transition)
|
|
355
|
+
graph.graph["stereo_projection"] = "transition"
|
|
356
|
+
return graph
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import networkx as nx
|
|
2
|
+
from rdkit import Chem
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from synkit.IO.chem_converter import rsmi_to_graph
|
|
6
|
+
from synkit.IO.graph_to_mol import GraphToMol
|
|
7
|
+
from synkit.Chem.Reaction.fix_aam import FixAAM
|
|
8
|
+
from synkit.Graph.Hyrogen._misc import implicit_hydrogen
|
|
9
|
+
from synkit.Graph.ITS.its_construction import ITSConstruction
|
|
10
|
+
from synkit.Graph.ITS.its_decompose import get_rc
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NormalizeAAM:
|
|
14
|
+
"""Provides functionalities to normalize atom mappings in SMILES
|
|
15
|
+
representations, extract and process reaction centers from ITS graphs, and
|
|
16
|
+
convert between graph representations and molecular models."""
|
|
17
|
+
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
"""Initializes the NormalizeAAM class."""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def fix_rsmi_kekulize(rsmi: str) -> str:
|
|
24
|
+
"""Filters the reactants and products of a reaction SMILES string.
|
|
25
|
+
|
|
26
|
+
:param rsmi: A string representing the reaction SMILES in the form of "reactants >> products".
|
|
27
|
+
:type rsmi: str
|
|
28
|
+
|
|
29
|
+
:return: A filtered reaction SMILES string where invalid reactants/products are removed.
|
|
30
|
+
:rtype: str
|
|
31
|
+
"""
|
|
32
|
+
# Split the reaction into reactants and products
|
|
33
|
+
reactants, products = rsmi.split(">>")
|
|
34
|
+
|
|
35
|
+
# Filter valid reactants and products
|
|
36
|
+
filtered_reactants = NormalizeAAM.fix_kekulize(reactants)
|
|
37
|
+
filtered_products = NormalizeAAM.fix_kekulize(products)
|
|
38
|
+
|
|
39
|
+
# Return the filtered reaction SMILES
|
|
40
|
+
return f"{filtered_reactants}>>{filtered_products}"
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def fix_kekulize(smiles: str) -> str:
|
|
44
|
+
"""Filters and returns valid SMILES strings from a string of SMILES,
|
|
45
|
+
joined by '.'.
|
|
46
|
+
|
|
47
|
+
This function processes a string of SMILES separated by periods (e.g., "CCO.CC=O"),
|
|
48
|
+
filters out invalid SMILES, and returns a string of valid SMILES joined by periods.
|
|
49
|
+
|
|
50
|
+
:param smiles: A string containing SMILES strings separated by periods ('.').
|
|
51
|
+
:type smiles: str
|
|
52
|
+
|
|
53
|
+
:return: A string of valid SMILES, joined by periods ('.').
|
|
54
|
+
:rtype: str
|
|
55
|
+
"""
|
|
56
|
+
smiles_list = smiles.split(".") # Split SMILES by period
|
|
57
|
+
valid_smiles = [] # List to store valid SMILES strings
|
|
58
|
+
|
|
59
|
+
for smile in smiles_list:
|
|
60
|
+
mol = Chem.MolFromSmiles(smile, sanitize=False)
|
|
61
|
+
if mol: # Check if molecule is valid
|
|
62
|
+
valid_smiles.append(
|
|
63
|
+
Chem.MolToSmiles(
|
|
64
|
+
mol, canonical=True, kekuleSmiles=True, allHsExplicit=True
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
return ".".join(valid_smiles) # Return valid SMILES joined by '.'
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def extract_subgraph(graph: nx.Graph, indices: List[int]) -> nx.Graph:
|
|
71
|
+
"""Extracts a subgraph from a given graph based on a list of node
|
|
72
|
+
indices.
|
|
73
|
+
|
|
74
|
+
:param graph: The original graph from which to extract the subgraph.
|
|
75
|
+
:type graph: nx.Graph
|
|
76
|
+
:param indices: A list of node indices that define the subgraph.
|
|
77
|
+
:type indices: List[int]
|
|
78
|
+
|
|
79
|
+
:return: The extracted subgraph.
|
|
80
|
+
:rtype: nx.Graph
|
|
81
|
+
"""
|
|
82
|
+
return graph.subgraph(indices).copy()
|
|
83
|
+
|
|
84
|
+
def reset_indices_and_atom_map(
|
|
85
|
+
self, subgraph: nx.Graph, aam_key: str = "atom_map"
|
|
86
|
+
) -> nx.Graph:
|
|
87
|
+
"""Resets the node indices and the atom_map of the subgraph to be
|
|
88
|
+
continuous from 1 onwards.
|
|
89
|
+
|
|
90
|
+
:param subgraph: The subgraph with possibly non-continuous indices.
|
|
91
|
+
:type subgraph: nx.Graph
|
|
92
|
+
:param aam_key: The attribute key for atom mapping. Defaults to 'atom_map'.
|
|
93
|
+
:type aam_key: str
|
|
94
|
+
|
|
95
|
+
:return: A new subgraph with continuous indices and adjusted atom_map.
|
|
96
|
+
:rtype: nx.Graph
|
|
97
|
+
"""
|
|
98
|
+
new_graph = nx.Graph()
|
|
99
|
+
node_id_mapping = {
|
|
100
|
+
old_id: new_id for new_id, old_id in enumerate(subgraph.nodes(), 1)
|
|
101
|
+
}
|
|
102
|
+
for old_id, new_id in node_id_mapping.items():
|
|
103
|
+
node_data = subgraph.nodes[old_id].copy()
|
|
104
|
+
node_data[aam_key] = new_id
|
|
105
|
+
new_graph.add_node(new_id, **node_data)
|
|
106
|
+
for u, v, data in subgraph.edges(data=True):
|
|
107
|
+
new_graph.add_edge(node_id_mapping[u], node_id_mapping[v], **data)
|
|
108
|
+
return new_graph
|
|
109
|
+
|
|
110
|
+
def fit(self, rsmi: str, fix_aam_indice: bool = True) -> str:
|
|
111
|
+
"""Processes a reaction SMILES (RSMI) to adjust atom mappings, extract
|
|
112
|
+
reaction centers, decompose into separate reactant and product graphs,
|
|
113
|
+
and generate the corresponding SMILES.
|
|
114
|
+
|
|
115
|
+
:param rsmi: The reaction SMILES string to be processed.
|
|
116
|
+
:type rsmi: str
|
|
117
|
+
:param fix_aam_indice: Whether to fix the atom mapping numbers.
|
|
118
|
+
Defaults to True.
|
|
119
|
+
:type fix_aam_indice: bool
|
|
120
|
+
|
|
121
|
+
:return: The resulting reaction SMILES string with updated atom mappings.
|
|
122
|
+
:rtype: str
|
|
123
|
+
"""
|
|
124
|
+
rsmi = self.fix_rsmi_kekulize(rsmi)
|
|
125
|
+
if fix_aam_indice:
|
|
126
|
+
rsmi = FixAAM().fix_aam_rsmi(rsmi)
|
|
127
|
+
r_graph, p_graph = rsmi_to_graph(
|
|
128
|
+
rsmi,
|
|
129
|
+
sanitize=True,
|
|
130
|
+
use_index_as_atom_map=True,
|
|
131
|
+
drop_non_aam=True,
|
|
132
|
+
)
|
|
133
|
+
its = ITSConstruction().ITSGraph(r_graph, p_graph)
|
|
134
|
+
rc = get_rc(its)
|
|
135
|
+
list_hydrogen = []
|
|
136
|
+
for _, value in rc.nodes(data=True):
|
|
137
|
+
if value["element"] == "H":
|
|
138
|
+
list_hydrogen.append(value["atom_map"])
|
|
139
|
+
r_graph = implicit_hydrogen(r_graph, list_hydrogen)
|
|
140
|
+
p_graph = implicit_hydrogen(p_graph, list_hydrogen)
|
|
141
|
+
|
|
142
|
+
r_mol, p_mol = GraphToMol().graph_to_mol(
|
|
143
|
+
r_graph, sanitize=True, use_h_count=True
|
|
144
|
+
), GraphToMol().graph_to_mol(p_graph, sanitize=True, use_h_count=True)
|
|
145
|
+
return f"{Chem.MolToSmiles(r_mol)}>>{Chem.MolToSmiles(p_mol)}"
|