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,205 @@
|
|
|
1
|
+
from typing import List, Dict, Optional
|
|
2
|
+
from rdkit import Chem
|
|
3
|
+
from joblib import Parallel, delayed
|
|
4
|
+
|
|
5
|
+
from synkit.Graph.FG import smiles_to_graph_and_functional_groups
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Tautomerize:
|
|
9
|
+
"""Standardize molecules by converting enol and hemiketal tautomers into
|
|
10
|
+
their more stable carbonyl forms, and apply these corrections to individual
|
|
11
|
+
SMILES or collections of reaction data."""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def standardize_enol(smiles: str, atom_indices: Optional[List[int]] = None) -> str:
|
|
15
|
+
"""Convert an enol tautomer into its corresponding carbonyl form.
|
|
16
|
+
|
|
17
|
+
:param smiles: SMILES string of the enol-containing molecule.
|
|
18
|
+
:type smiles: str
|
|
19
|
+
:param atom_indices: List of three atom indices [C1, C2, O]
|
|
20
|
+
defining the enol. If None, defaults to [0, 1, 2].
|
|
21
|
+
:type atom_indices: List[int] or None
|
|
22
|
+
:return: SMILES of the molecule after enol→carbonyl conversion,
|
|
23
|
+
or an error message if the input is invalid or indices fail.
|
|
24
|
+
:rtype: str
|
|
25
|
+
"""
|
|
26
|
+
if atom_indices is None:
|
|
27
|
+
atom_indices = [0, 1, 2]
|
|
28
|
+
|
|
29
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
30
|
+
if mol is None:
|
|
31
|
+
return "Invalid SMILES format."
|
|
32
|
+
emol = Chem.EditableMol(mol)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
c_idxs = [
|
|
36
|
+
i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "C"
|
|
37
|
+
]
|
|
38
|
+
c1_idx, c2_idx = c_idxs[:2]
|
|
39
|
+
o_idx = next(
|
|
40
|
+
i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "O"
|
|
41
|
+
)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
return f"Error processing indices: {e}"
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
emol.RemoveBond(c1_idx, c2_idx)
|
|
47
|
+
emol.RemoveBond(c2_idx, o_idx)
|
|
48
|
+
emol.AddBond(c1_idx, c2_idx, Chem.rdchem.BondType.SINGLE)
|
|
49
|
+
emol.AddBond(c2_idx, o_idx, Chem.rdchem.BondType.DOUBLE)
|
|
50
|
+
new_mol = emol.GetMol()
|
|
51
|
+
Chem.SanitizeMol(new_mol)
|
|
52
|
+
return Chem.MolToSmiles(new_mol)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
return f"Error in modifying molecule: {e}"
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def standardize_hemiketal(smiles: str, atom_indices: List[int]) -> str:
|
|
58
|
+
"""Convert a hemiketal tautomer into its corresponding carbonyl form.
|
|
59
|
+
|
|
60
|
+
:param smiles: SMILES string of the hemiketal-containing
|
|
61
|
+
molecule.
|
|
62
|
+
:type smiles: str
|
|
63
|
+
:param atom_indices: List of atom indices [C, O1, O2] defining
|
|
64
|
+
the hemiketal.
|
|
65
|
+
:type atom_indices: List[int]
|
|
66
|
+
:return: SMILES of the molecule after hemiketal→carbonyl
|
|
67
|
+
conversion, or an error message if the input is invalid.
|
|
68
|
+
:rtype: str
|
|
69
|
+
"""
|
|
70
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
71
|
+
if mol is None:
|
|
72
|
+
return "Invalid SMILES format."
|
|
73
|
+
emol = Chem.EditableMol(mol)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
c_idx = next(
|
|
77
|
+
i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "C"
|
|
78
|
+
)
|
|
79
|
+
o_idxs = [
|
|
80
|
+
i for i in atom_indices if mol.GetAtomWithIdx(i).GetSymbol() == "O"
|
|
81
|
+
]
|
|
82
|
+
o1_idx = o_idxs[0]
|
|
83
|
+
except Exception as e:
|
|
84
|
+
return f"Error processing indices: {e}"
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
emol.RemoveBond(c_idx, o1_idx)
|
|
88
|
+
if len(o_idxs) > 1:
|
|
89
|
+
emol.RemoveBond(c_idx, o_idxs[1])
|
|
90
|
+
emol.AddBond(c_idx, o1_idx, Chem.rdchem.BondType.DOUBLE)
|
|
91
|
+
new_mol = emol.GetMol()
|
|
92
|
+
Chem.SanitizeMol(new_mol)
|
|
93
|
+
return Chem.MolToSmiles(new_mol)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
return f"Error in modifying molecule: {e}"
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def fix_smiles(smiles: str) -> str:
|
|
99
|
+
"""Iteratively apply enol and hemiketal standardizations until no
|
|
100
|
+
further changes, then return the canonical SMILES.
|
|
101
|
+
|
|
102
|
+
:param smiles: SMILES string to standardize.
|
|
103
|
+
:type smiles: str
|
|
104
|
+
:return: Canonical SMILES of the standardized molecule.
|
|
105
|
+
:rtype: str
|
|
106
|
+
"""
|
|
107
|
+
while True:
|
|
108
|
+
targets = Tautomerize._tautomer_targets(smiles)
|
|
109
|
+
if not targets:
|
|
110
|
+
break
|
|
111
|
+
label, indices = targets[0]
|
|
112
|
+
if label == "hemiketal":
|
|
113
|
+
smiles = Tautomerize.standardize_hemiketal(smiles, indices)
|
|
114
|
+
elif label == "enol":
|
|
115
|
+
smiles = Tautomerize.standardize_enol(smiles, indices)
|
|
116
|
+
return Chem.CanonSmiles(smiles)
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def _tautomer_targets(smiles: str) -> list[tuple[str, List[int]]]:
|
|
120
|
+
"""Return RDKit-index targets used by the tautomer repair helpers."""
|
|
121
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
122
|
+
if mol is None:
|
|
123
|
+
return []
|
|
124
|
+
graph, groups = smiles_to_graph_and_functional_groups(smiles)
|
|
125
|
+
node_to_idx = {
|
|
126
|
+
(
|
|
127
|
+
atom.GetAtomMapNum() if atom.GetAtomMapNum() else atom.GetIdx() + 1
|
|
128
|
+
): atom.GetIdx()
|
|
129
|
+
for atom in mol.GetAtoms()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
targets = [
|
|
133
|
+
(label, [node_to_idx[node] for node in nodes])
|
|
134
|
+
for label, nodes in groups
|
|
135
|
+
if label in {"hemiketal", "enol"}
|
|
136
|
+
]
|
|
137
|
+
targets.extend(
|
|
138
|
+
("hemiketal", [node_to_idx[node] for node in nodes])
|
|
139
|
+
for nodes in Tautomerize._geminal_diol_nodes(graph)
|
|
140
|
+
)
|
|
141
|
+
return targets
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def _geminal_diol_nodes(graph) -> list[tuple[int, ...]]:
|
|
145
|
+
"""Legacy tautomerization compatibility for hydrated carbonyls."""
|
|
146
|
+
targets: list[tuple[int, ...]] = []
|
|
147
|
+
for carbon, data in graph.nodes(data=True):
|
|
148
|
+
if data.get("element") != "C":
|
|
149
|
+
continue
|
|
150
|
+
hydroxyls = [
|
|
151
|
+
neighbor
|
|
152
|
+
for neighbor in graph.neighbors(carbon)
|
|
153
|
+
if graph.nodes[neighbor].get("element") == "O"
|
|
154
|
+
and graph.nodes[neighbor].get("hcount", 0) >= 1
|
|
155
|
+
and graph.edges[carbon, neighbor].get("order") == 1.0
|
|
156
|
+
]
|
|
157
|
+
if len(hydroxyls) >= 2:
|
|
158
|
+
targets.append((carbon, hydroxyls[0], hydroxyls[1]))
|
|
159
|
+
return targets
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def fix_dict(data: Dict[str, str], reaction_column: str) -> Dict[str, str]:
|
|
163
|
+
"""Standardize the reactant and product SMILES in a reaction
|
|
164
|
+
dictionary.
|
|
165
|
+
|
|
166
|
+
:param data: Dictionary containing a reaction SMILES under `reaction_column`.
|
|
167
|
+
:type data: Dict[str, str]
|
|
168
|
+
:param reaction_column: Key in `data` where the reaction SMILES is stored.
|
|
169
|
+
:type reaction_column: str
|
|
170
|
+
:return: The same dictionary with standardized reaction SMILES.
|
|
171
|
+
:rtype: Dict[str, str]
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
react, prod = data[reaction_column].split(">>")
|
|
175
|
+
data[reaction_column] = (
|
|
176
|
+
f"{Tautomerize.fix_smiles(react)}>>{Tautomerize.fix_smiles(prod)}"
|
|
177
|
+
)
|
|
178
|
+
except ValueError:
|
|
179
|
+
data[reaction_column] = Tautomerize.fix_smiles(data[reaction_column])
|
|
180
|
+
return data
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def fix_dicts(
|
|
184
|
+
data: List[Dict[str, str]],
|
|
185
|
+
reaction_column: str,
|
|
186
|
+
n_jobs: int = 4,
|
|
187
|
+
verbose: int = 0,
|
|
188
|
+
) -> List[Dict[str, str]]:
|
|
189
|
+
"""Standardize multiple reaction dictionaries in parallel.
|
|
190
|
+
|
|
191
|
+
:param data: List of dictionaries containing reaction SMILES under `reaction_column`.
|
|
192
|
+
:type data: List[Dict[str, str]]
|
|
193
|
+
:param reaction_column: Key in each dictionary for the reaction SMILES.
|
|
194
|
+
:type reaction_column: str
|
|
195
|
+
:param n_jobs: Number of parallel jobs to run. Defaults to 4.
|
|
196
|
+
:type n_jobs: int
|
|
197
|
+
:param verbose: Verbosity level for the joblib Parallel call. Defaults to 0.
|
|
198
|
+
:type verbose: int
|
|
199
|
+
:return: List of dictionaries with standardized SMILES.
|
|
200
|
+
:rtype: List[Dict[str, str]]
|
|
201
|
+
"""
|
|
202
|
+
results = Parallel(n_jobs=n_jobs, verbose=verbose)(
|
|
203
|
+
delayed(Tautomerize.fix_dict)(d, reaction_column) for d in data
|
|
204
|
+
)
|
|
205
|
+
return results
|
synkit/Chem/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# from .Reaction import CanonRSMI, AAMValidator
|
synkit/Chem/utils.py
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
from rdkit import Chem
|
|
2
|
+
from rdkit.Chem.MolStandardize import rdMolStandardize
|
|
3
|
+
from rdkit.Chem import rdChemReactions
|
|
4
|
+
import re
|
|
5
|
+
from typing import List, Optional, Tuple, Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def clean_radical_rsmi(rsmi: str) -> str:
|
|
9
|
+
"""Load each side of a reaction SMILES (rSMI) into RDKit, split into disconnected fragments,
|
|
10
|
+
remove any fragment that contains an atom with nonzero radical electrons,
|
|
11
|
+
then reassemble back into a cleaned reaction SMILES.
|
|
12
|
+
|
|
13
|
+
:param rsmi: Reaction SMILES string, e.g.
|
|
14
|
+
'A>>B.C'
|
|
15
|
+
:type rsmi: str
|
|
16
|
+
:return: Cleaned reaction SMILES with radical-containing fragments removed.
|
|
17
|
+
:rtype: str
|
|
18
|
+
|
|
19
|
+
.. rubric:: Examples
|
|
20
|
+
|
|
21
|
+
>>> clean_radical_rsmi(
|
|
22
|
+
... 'COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(=O)Nc1cc(OC)cc(C(C)(C)C)c1O'
|
|
23
|
+
... '>>COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(N)=O.COc1c[c]c(O)c(C(C)(C)C)c1'
|
|
24
|
+
... )
|
|
25
|
+
'COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(=O)Nc1cc(OC)cc(C(C)(C)C)c1O'
|
|
26
|
+
'>>COC(=O)C(CCCCNC(=O)OCc1ccccc1)NC(N)=O'
|
|
27
|
+
"""
|
|
28
|
+
if ">>" not in rsmi:
|
|
29
|
+
return rsmi
|
|
30
|
+
|
|
31
|
+
def _clean_side(side: str) -> str:
|
|
32
|
+
mol = Chem.MolFromSmiles(side)
|
|
33
|
+
if mol is None:
|
|
34
|
+
return ""
|
|
35
|
+
frags = Chem.GetMolFrags(mol, asMols=True)
|
|
36
|
+
kept = []
|
|
37
|
+
for frag in frags:
|
|
38
|
+
if any(atom.GetNumRadicalElectrons() > 0 for atom in frag.GetAtoms()):
|
|
39
|
+
continue
|
|
40
|
+
kept.append(Chem.MolToSmiles(frag, isomericSmiles=True))
|
|
41
|
+
return ".".join(kept)
|
|
42
|
+
|
|
43
|
+
reac, prod = rsmi.split(">>", 1)
|
|
44
|
+
return f"{_clean_side(reac)}>>{_clean_side(prod)}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def enumerate_tautomers(reaction_smiles: str) -> Optional[List[str]]:
|
|
48
|
+
"""Enumerate possible tautomers of reactants while canonicalizing products.
|
|
49
|
+
|
|
50
|
+
:param reaction_smiles: Reaction SMILES in 'reactants>>products'
|
|
51
|
+
format.
|
|
52
|
+
:type reaction_smiles: str
|
|
53
|
+
:return: List of reaction SMILES for each reactant tautomer
|
|
54
|
+
(including the original), or None on error.
|
|
55
|
+
:rtype: Optional[List[str]]
|
|
56
|
+
:raises ValueError: If reactant or product SMILES are invalid.
|
|
57
|
+
"""
|
|
58
|
+
try:
|
|
59
|
+
reactants_smiles, products_smiles = reaction_smiles.split(">>")
|
|
60
|
+
reactants_mol = Chem.MolFromSmiles(reactants_smiles)
|
|
61
|
+
products_mol = Chem.MolFromSmiles(products_smiles)
|
|
62
|
+
if reactants_mol is None or products_mol is None:
|
|
63
|
+
raise ValueError("Invalid reactant or product SMILES.")
|
|
64
|
+
enumerator = rdMolStandardize.TautomerEnumerator()
|
|
65
|
+
reactants_tautos = enumerator.Enumerate(reactants_mol) or [reactants_mol]
|
|
66
|
+
prod_can = Chem.MolToSmiles(products_mol, canonical=True)
|
|
67
|
+
rsmi_list = [Chem.MolToSmiles(m) + ">>" + prod_can for m in reactants_tautos]
|
|
68
|
+
rsmi_list.insert(0, reaction_smiles)
|
|
69
|
+
return rsmi_list
|
|
70
|
+
except ValueError:
|
|
71
|
+
raise
|
|
72
|
+
except Exception:
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def mapping_success_rate(list_mapping_data: List[str]) -> float:
|
|
77
|
+
"""Calculate percentage of entries containing atom‑mapping annotations.
|
|
78
|
+
|
|
79
|
+
:param list_mapping_data: List of strings to search for mappings.
|
|
80
|
+
:type list_mapping_data: List[str]
|
|
81
|
+
:return: Percentage of entries containing `:<digits>` patterns,
|
|
82
|
+
rounded to two decimals.
|
|
83
|
+
:rtype: float
|
|
84
|
+
:raises ValueError: If input list is empty.
|
|
85
|
+
"""
|
|
86
|
+
if not list_mapping_data:
|
|
87
|
+
raise ValueError("The input list is empty, cannot calculate success rate.")
|
|
88
|
+
pattern = re.compile(r":\d+")
|
|
89
|
+
success = sum(1 for entry in list_mapping_data if pattern.search(entry))
|
|
90
|
+
return round(100 * success / len(list_mapping_data), 2)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def count_carbons(smiles: str) -> int:
|
|
94
|
+
"""Count the number of carbon atoms in a molecule.
|
|
95
|
+
|
|
96
|
+
:param smiles: SMILES string of the molecule.
|
|
97
|
+
:type smiles: str
|
|
98
|
+
:return: Number of carbon atoms, or raises ValueError if SMILES
|
|
99
|
+
invalid.
|
|
100
|
+
:rtype: int
|
|
101
|
+
:raises ValueError: If the SMILES string is invalid.
|
|
102
|
+
"""
|
|
103
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
104
|
+
if mol is None:
|
|
105
|
+
raise ValueError(f"Invalid SMILES string: {smiles}")
|
|
106
|
+
return sum(1 for atom in mol.GetAtoms() if atom.GetSymbol() == "C")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_max_fragment(smiles: Union[str, List[str]]) -> str:
|
|
110
|
+
"""Return the largest fragment by atom count from SMILES.
|
|
111
|
+
|
|
112
|
+
:param smiles: SMILES string(s), possibly with '.' separators.
|
|
113
|
+
:type smiles: str or List[str]
|
|
114
|
+
:return: SMILES of the fragment with the most atoms, or empty
|
|
115
|
+
string if none valid.
|
|
116
|
+
:rtype: str
|
|
117
|
+
"""
|
|
118
|
+
if isinstance(smiles, str):
|
|
119
|
+
fragments = smiles.split(".")
|
|
120
|
+
else:
|
|
121
|
+
fragments = [frag for s in smiles for frag in s.split(".")]
|
|
122
|
+
mols = [Chem.MolFromSmiles(f) for f in fragments if f]
|
|
123
|
+
mols = [m for m in mols if m]
|
|
124
|
+
if not mols:
|
|
125
|
+
return ""
|
|
126
|
+
max_mol = max(mols, key=lambda m: m.GetNumAtoms())
|
|
127
|
+
return Chem.MolToSmiles(max_mol)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def filter_smiles(smiles_list: List[str], target_smiles: str) -> List[str]:
|
|
131
|
+
"""Filter SMILES list to those containing carbon and not equal to a target.
|
|
132
|
+
|
|
133
|
+
:param smiles_list: List of SMILES strings to filter.
|
|
134
|
+
:type smiles_list: List[str]
|
|
135
|
+
:param target_smiles: SMILES string to exclude.
|
|
136
|
+
:type target_smiles: str
|
|
137
|
+
:return: Filtered list containing SMILES with at least one carbon atom
|
|
138
|
+
and not matching `target_smiles`.
|
|
139
|
+
:rtype: List[str]
|
|
140
|
+
"""
|
|
141
|
+
target_mol = Chem.MolFromSmiles(target_smiles)
|
|
142
|
+
target_can = Chem.MolToSmiles(target_mol) if target_mol else ""
|
|
143
|
+
result: List[str] = []
|
|
144
|
+
for smi in smiles_list:
|
|
145
|
+
mol = Chem.MolFromSmiles(smi)
|
|
146
|
+
if mol and any(atom.GetSymbol() == "C" for atom in mol.GetAtoms()):
|
|
147
|
+
can = Chem.MolToSmiles(mol)
|
|
148
|
+
if can != target_can:
|
|
149
|
+
result.append(smi)
|
|
150
|
+
return result
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def remove_atom_mappings(mol: Chem.Mol) -> Chem.Mol:
|
|
154
|
+
"""Strip atom‑mapping numbers from a molecule.
|
|
155
|
+
|
|
156
|
+
:param mol: RDKit Mol object.
|
|
157
|
+
:type mol: Chem.Mol
|
|
158
|
+
:return: The same Mol with all atom‑map numbers set to zero.
|
|
159
|
+
:rtype: Chem.Mol
|
|
160
|
+
"""
|
|
161
|
+
for atom in mol.GetAtoms():
|
|
162
|
+
atom.SetAtomMapNum(0)
|
|
163
|
+
return mol
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_sanitized_smiles(smiles_list: List[str]) -> List[str]:
|
|
167
|
+
"""Sanitize SMILES list by removing mappings and invalid entries.
|
|
168
|
+
|
|
169
|
+
:param smiles_list: List of SMILES strings to sanitize.
|
|
170
|
+
:type smiles_list: List[str]
|
|
171
|
+
:return: List of sanitized, isomeric SMILES of the largest
|
|
172
|
+
fragments only.
|
|
173
|
+
:rtype: List[str]
|
|
174
|
+
"""
|
|
175
|
+
sanitized: List[str] = []
|
|
176
|
+
for smiles in smiles_list:
|
|
177
|
+
if "->" in smiles:
|
|
178
|
+
continue
|
|
179
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
180
|
+
if not mol:
|
|
181
|
+
continue
|
|
182
|
+
mol = remove_atom_mappings(mol)
|
|
183
|
+
try:
|
|
184
|
+
Chem.SanitizeMol(mol)
|
|
185
|
+
sanitized.append(Chem.MolToSmiles(mol, isomericSmiles=True))
|
|
186
|
+
except Exception:
|
|
187
|
+
continue
|
|
188
|
+
# keep only the largest fragment across all
|
|
189
|
+
if sanitized:
|
|
190
|
+
sanitized = [get_max_fragment(sanitized)]
|
|
191
|
+
return sanitized
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def remove_duplicates(smiles_list: List[str]) -> List[str]:
|
|
195
|
+
"""Remove duplicate strings from a list, preserving first occurrence.
|
|
196
|
+
|
|
197
|
+
:param smiles_list: List of strings (e.g., SMILES) possibly with
|
|
198
|
+
duplicates.
|
|
199
|
+
:type smiles_list: List[str]
|
|
200
|
+
:return: List with duplicates removed in original order.
|
|
201
|
+
:rtype: List[str]
|
|
202
|
+
"""
|
|
203
|
+
seen = set()
|
|
204
|
+
unique: List[str] = []
|
|
205
|
+
for s in smiles_list:
|
|
206
|
+
if s not in seen:
|
|
207
|
+
unique.append(s)
|
|
208
|
+
seen.add(s)
|
|
209
|
+
return unique
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def process_smiles_list(smiles_list: List[str]) -> List[str]:
|
|
213
|
+
"""Split dot‑connected SMILES into individual components.
|
|
214
|
+
|
|
215
|
+
:param smiles_list: List of SMILES strings, some containing '.'
|
|
216
|
+
separators.
|
|
217
|
+
:type smiles_list: List[str]
|
|
218
|
+
:return: Flattened list of component SMILES strings.
|
|
219
|
+
:rtype: List[str]
|
|
220
|
+
"""
|
|
221
|
+
new_list: List[str] = []
|
|
222
|
+
for smiles in smiles_list:
|
|
223
|
+
if "." in smiles:
|
|
224
|
+
new_list.extend(smiles.split("."))
|
|
225
|
+
else:
|
|
226
|
+
new_list.append(smiles)
|
|
227
|
+
return new_list
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def remove_explicit_H_from_rsmi(rsmi: str) -> str:
|
|
231
|
+
"""Remove explicit H atoms from a reaction SMILES, preserving AAM.
|
|
232
|
+
|
|
233
|
+
:param rsmi: Atom‑mapped reaction SMILES with explicit hydrogens.
|
|
234
|
+
:type rsmi: str
|
|
235
|
+
:return: Simplified reaction SMILES with implicit hydrogens.
|
|
236
|
+
:rtype: str
|
|
237
|
+
"""
|
|
238
|
+
rxn = rdChemReactions.ReactionFromSmarts(rsmi, useSmiles=True)
|
|
239
|
+
|
|
240
|
+
def cleaned(mols):
|
|
241
|
+
return ".".join(
|
|
242
|
+
Chem.MolToSmiles(Chem.RemoveHs(m), isomericSmiles=True) for m in mols
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
react = cleaned(rxn.GetReactants())
|
|
246
|
+
prod = cleaned(rxn.GetProducts())
|
|
247
|
+
return f"{react}>>{prod}"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def remove_common_reagents(reaction_smiles: str) -> Tuple[Optional[str], Optional[str]]:
|
|
251
|
+
"""Remove reagents present on both sides of a reaction SMILES.
|
|
252
|
+
|
|
253
|
+
:param reaction_smiles: Reaction SMILES 'reactants>>products'.
|
|
254
|
+
:type reaction_smiles: str
|
|
255
|
+
:return: Tuple(cleaned_reaction, list_of_removed_reagents or None
|
|
256
|
+
if none found).
|
|
257
|
+
:rtype: Tuple[str, Optional[List[str]]]
|
|
258
|
+
"""
|
|
259
|
+
reactants, products = reaction_smiles.split(">>")
|
|
260
|
+
reactant_list = reactants.split(".")
|
|
261
|
+
product_list = products.split(".")
|
|
262
|
+
common_reagents = set(reactant_list) & set(product_list)
|
|
263
|
+
|
|
264
|
+
filtered_reactants = [r for r in reactant_list if r not in common_reagents]
|
|
265
|
+
filtered_products = [p for p in product_list if p not in common_reagents]
|
|
266
|
+
cleaned_reaction_smiles = (
|
|
267
|
+
".".join(filtered_reactants) + ">>" + ".".join(filtered_products)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
return cleaned_reaction_smiles
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def reverse_reaction(rsmi: str) -> str:
|
|
274
|
+
"""Reverse a reaction SMILES.
|
|
275
|
+
|
|
276
|
+
:param rsmi: Reaction SMILES 'reactants>>products'.
|
|
277
|
+
:type rsmi: str
|
|
278
|
+
:return: Reaction SMILES 'products>>reactants'.
|
|
279
|
+
:rtype: str
|
|
280
|
+
"""
|
|
281
|
+
parts = rsmi.split(">>")
|
|
282
|
+
return f"{parts[1]}>>{parts[0]}" if len(parts) == 2 else rsmi
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def merge_reaction(rsmi_1: str, rsmi_2: str) -> Optional[str]:
|
|
286
|
+
"""Merge two reaction SMILES into a single combined reaction.
|
|
287
|
+
|
|
288
|
+
:param rsmi_1: First reaction SMILES.
|
|
289
|
+
:type rsmi_1: str
|
|
290
|
+
:param rsmi_2: Second reaction SMILES.
|
|
291
|
+
:type rsmi_2: str
|
|
292
|
+
:return: Merged reaction SMILES or None if inputs invalid.
|
|
293
|
+
:rtype: Optional[str]
|
|
294
|
+
"""
|
|
295
|
+
try:
|
|
296
|
+
r1, p1 = rsmi_1.split(">>")
|
|
297
|
+
r2, p2 = rsmi_2.split(">>")
|
|
298
|
+
except ValueError:
|
|
299
|
+
return None
|
|
300
|
+
if not all([r1, p1, r2, p2]):
|
|
301
|
+
return None
|
|
302
|
+
return f"{r1}.{r2}>>{p1}.{p2}"
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def find_longest_fragment(input_list: List[str]) -> Optional[str]:
|
|
306
|
+
"""Find the longest string in a list.
|
|
307
|
+
|
|
308
|
+
:param input_list: List of strings to search.
|
|
309
|
+
:type input_list: List[str]
|
|
310
|
+
:return: Longest string or None if list empty.
|
|
311
|
+
:rtype: Optional[str]
|
|
312
|
+
"""
|
|
313
|
+
if not input_list:
|
|
314
|
+
return None
|
|
315
|
+
return max(input_list, key=len)
|
synkit/Data/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"R-id": "Mech-1", "reaction": "[CH3:1][CH:2]=[O:3].[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[O:3]([H:7])([H:8])", "mechanisms": [{"mech_name": "Aldol reaction (base cat)", "steps": [{"description": "Base abstracts H from substrate", "smart_string": "[CH:4]([H:7])([H:8])[CH:5]=[O:6].[*-:9]>>[CH-:4]([H:8])[CH:5]=[O:6].[*:9][H:7]", "step": 1, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Base abstracts H from substrate"}, {"description": "Nucleophilic addition fro substrate", "smart_string": "[CH3:1][CH:2]=[O:3].[CH-:4]([H:8])[CH:5]=[O:6]>>[CH3:1][CH:2]([O-:3])[CH:4]([H:8])[CH:5]=[O:6]", "step": 2, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"=\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n ]\n right [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n node [ id 2 label \"O-\" ]\n node [ id 3 label \"C\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]=[O:2].[CH3-:3]>>[CH2:1]([O-:2])[CH3:3]", "step_desc": "Nucleophilic addition fro substrate"}, {"description": "Neutralize substrate", "smart_string": "[CH3:1][CH:2]([O-:3])[CH:4]([H:8])[CH:5]=[O:6].[*:9][H:7]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[*-:9]", "step": 3, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Neutralize substrate"}, {"description": "Base abstracts H from substrate", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[*-:9]>>[CH3:1][CH:2]([O:3][H:7])[CH-:4][CH:5]=[O:6].[*:9][H:8]", "step": 4, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Base abstracts H from substrate"}, {"description": "Elimination Unimolecular Conjugate Base", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH-:4][CH:5]=[O:6]>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[O-:3][H:7]", "step": 5, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n ]\n right [\n edge [ source 1 target 3 label \"=\" ]\n node [ id 2 label \"O-\" ]\n node [ id 3 label \"C\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]([OH:2])[CH2-:3]>>[CH2:1]=[CH2:3].[OH-:2]", "step_desc": "Elimination Unimolecular Conjugate Base"}, {"description": "Neutralize Hydroxide", "smart_string": "[O-:3][H:7].[*:9][H:8]>>[O:3]([H:7])([H:8]).[*-:9]", "step": 6, "step_gml": "rule [\n ruleID \"2\"\n left [\n ]\n context [\n ]\n right [\n ]\n]", "step_dfs": null, "step_smart": ">>", "step_desc": "Neutralize Hydroxide"}], "cat": "[*-]"}, {"mech_name": "Aldol reaction (neutral cat)", "steps": [{"description": "Tautomerization of substrate", "smart_string": "[CH:4]([H:7])([H:8])[CH:5]=[O:6]>>[CH:4]([H:8])=[CH:5][O:6]([H:7])", "step": 1, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 2 target 3 label \"=\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"C\" ]\n node [ id 3 label \"O\" ]\n ]\n right [\n edge [ source 1 target 2 label \"=\" ]\n edge [ source 2 target 3 label \"-\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH3:1][CH:2]=[O:3]>>[CH2:1]=[CH:2][OH:3]", "step_desc": "Tautomerization of substrate"}, {"description": "Nucleophilic addition from substrate", "smart_string": "[CH3:1][CH:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:7])>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]", "step": 2, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"=\" ]\n edge [ source 3 target 4 label \"=\" ]\n edge [ source 4 target 5 label \"-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C\" ]\n node [ id 4 label \"C\" ]\n node [ id 5 label \"O\" ]\n ]\n right [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n edge [ source 3 target 4 label \"-\" ]\n edge [ source 4 target 5 label \"=\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]=[O:2].[CH2:3]=[CH:4][OH:5]>>[CH2:1]([OH:2])[CH2:3][CH:4]=[O:5]", "step_desc": "Nucleophilic addition from substrate"}, {"description": "Tautomerization of substrate", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:8])", "step": 3, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 2 target 3 label \"=\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"C\" ]\n node [ id 3 label \"O\" ]\n ]\n right [\n edge [ source 1 target 2 label \"=\" ]\n edge [ source 2 target 3 label \"-\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH3:1][CH:2]=[O:3]>>[CH2:1]=[CH:2][OH:3]", "step_desc": "Tautomerization of substrate"}, {"description": "Elimination", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:8])>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[O:3]([H:7])([H:8])", "step": 4, "step_gml": "rule [\n ruleID \"2\"\n left [\n edge [ source 1 target 2 label \"-\" ]\n edge [ source 1 target 3 label \"-\" ]\n edge [ source 3 target 4 label \"=\" ]\n edge [ source 4 target 5 label \"-\" ]\n ]\n context [\n node [ id 1 label \"C\" ]\n node [ id 2 label \"O\" ]\n node [ id 3 label \"C\" ]\n node [ id 4 label \"C\" ]\n node [ id 5 label \"O\" ]\n ]\n right [\n edge [ source 1 target 3 label \"=\" ]\n edge [ source 3 target 4 label \"-\" ]\n edge [ source 4 target 5 label \"=\" ]\n ]\n]", "step_dfs": null, "step_smart": "[CH2:1]([OH:2])[CH:3]=[CH:4][OH:5]>>[CH2:1]=[CH:3][CH:4]=[O:5].[OH2:2]", "step_desc": "Elimination"}], "cat": ""}, {"mech_name": "Aldol reaction (acid cat)", "steps": [{"description": "Tautomerization of substrate with acid cat", "smart_string": "[CH:4]([H:7])([H:8])[CH:5]=[O:6].[H+:9]>>[CH:4]([H:8])=[CH:5][O:6]([H:9]).[H+:7]", "step": 1}, {"description": "Nucleophilic addition from substrate", "smart_string": "[CH3:1][CH:2]=[O:3].[CH:4]([H:8])=[CH:5][O:6]([H:9]).[H+:7]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[H+:9]", "step": 2}, {"description": "Tautomerization of substrate with acid cat", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]([H:8])[CH:5]=[O:6].[H+:9]>>[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:9]).[H+:8]", "step": 3}, {"description": "Elimination", "smart_string": "[CH3:1][CH:2]([O:3][H:7])[CH:4]=[CH:5][O:6]([H:9]).[H+:8]>>[CH3:1][CH:2]=[CH:4][CH:5]=[O:6].[H+:9].[O:3]([H:7])([H:8])", "step": 4}], "cat": "[H+]"}]}]
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from rdkit import Chem
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from synkit.IO.graph_to_mol import GraphToMol
|
|
5
|
+
from synkit.Graph.ITS.its_construction import ITSConstruction
|
|
6
|
+
from synkit.Graph.ITS.its_decompose import get_rc
|
|
7
|
+
from synkit.IO.chem_converter import smiles_to_graph
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _get_partial_aam(smart: str) -> str:
|
|
11
|
+
"""Generate a partial atom‐atom mapping (AAM) SMILES string from a reactant
|
|
12
|
+
SMARTS.
|
|
13
|
+
|
|
14
|
+
This function:
|
|
15
|
+
1. Parses the forward (“reactant”) and backward (“product”) halves of `smart`.
|
|
16
|
+
2. Builds RDKit graphs (with atom_map indices) for each side.
|
|
17
|
+
3. Constructs the integrated transition state (ITS) graph.
|
|
18
|
+
4. Identifies the reactant core (rc) subgraph.
|
|
19
|
+
5. Zeroes out `atom_map` on any atom not in the reactant core.
|
|
20
|
+
6. Converts the modified graphs to RDKit Mol objects (respecting H‐counts).
|
|
21
|
+
7. Emits SMILES for the “retained” and “partial” graphs, joined with '>>'.
|
|
22
|
+
|
|
23
|
+
:param smart: A reaction SMARTS of the form "R>>P".
|
|
24
|
+
:type smart: str
|
|
25
|
+
:return: An unbalanced, partial‐AAM reaction SMILES "retained>>partial".
|
|
26
|
+
:rtype: str
|
|
27
|
+
:raises RuntimeError:
|
|
28
|
+
If graph decomposition, molecule conversion, or SMILES generation fails.
|
|
29
|
+
"""
|
|
30
|
+
# split into reactant/product SMARTS
|
|
31
|
+
r_smi, p_smi = smart.split(">>")
|
|
32
|
+
# build graphs with atom_map indices from the SMILES strings
|
|
33
|
+
r_graph = smiles_to_graph(r_smi, use_index_as_atom_map=True)
|
|
34
|
+
p_graph = smiles_to_graph(p_smi, use_index_as_atom_map=True)
|
|
35
|
+
|
|
36
|
+
# construct and decompose ITS
|
|
37
|
+
its = ITSConstruction.ITSGraph(r_graph, p_graph)
|
|
38
|
+
rc = get_rc(its)
|
|
39
|
+
rc_nodes = set(rc.nodes())
|
|
40
|
+
|
|
41
|
+
# zero out maps for atoms not in the reactant core
|
|
42
|
+
for node in r_graph.nodes():
|
|
43
|
+
if node not in rc_nodes:
|
|
44
|
+
r_graph.nodes[node]["atom_map"] = 0
|
|
45
|
+
for node in p_graph.nodes():
|
|
46
|
+
if node not in rc_nodes:
|
|
47
|
+
p_graph.nodes[node]["atom_map"] = 0
|
|
48
|
+
|
|
49
|
+
converter = GraphToMol()
|
|
50
|
+
try:
|
|
51
|
+
retained_mol = converter.graph_to_mol(r_graph, use_h_count=True)
|
|
52
|
+
partial_mol = converter.graph_to_mol(p_graph, use_h_count=True)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
raise RuntimeError(f"Error converting graphs to molecules: {e}") from e
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
retained_smiles = Chem.MolToSmiles(retained_mol)
|
|
58
|
+
partial_smiles = Chem.MolToSmiles(partial_mol)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
raise RuntimeError(f"Error generating SMILES: {e}") from e
|
|
61
|
+
|
|
62
|
+
return f"{retained_smiles}>>{partial_smiles}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _remove_small_smiles(smiles: str) -> str:
|
|
66
|
+
"""Return the canonical SMILES of the largest fragment from an input
|
|
67
|
+
SMILES.
|
|
68
|
+
|
|
69
|
+
This function:
|
|
70
|
+
1. Parses `smiles` to an RDKit Mol without sanitization.
|
|
71
|
+
2. Sanitizes the Mol.
|
|
72
|
+
3. Splits into fragments, picks the one with the most heavy atoms.
|
|
73
|
+
4. Sanitizes that fragment and returns its canonical SMILES.
|
|
74
|
+
|
|
75
|
+
:param smiles: The input SMILES string.
|
|
76
|
+
:type smiles: str
|
|
77
|
+
:return: Canonical SMILES of the largest fragment.
|
|
78
|
+
:rtype: str
|
|
79
|
+
:raises ValueError:
|
|
80
|
+
- If `smiles` is invalid.
|
|
81
|
+
- If sanitization of the whole or fragment fails.
|
|
82
|
+
- If no fragments are found.
|
|
83
|
+
"""
|
|
84
|
+
mol: Optional[Chem.Mol] = Chem.MolFromSmiles(smiles, sanitize=False)
|
|
85
|
+
if mol is None:
|
|
86
|
+
raise ValueError(f"Invalid SMILES string: '{smiles}'")
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
Chem.SanitizeMol(mol)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
raise ValueError(f"Sanitization failed for '{smiles}': {e}")
|
|
92
|
+
|
|
93
|
+
fragments = Chem.GetMolFrags(mol, asMols=True, sanitizeFrags=False)
|
|
94
|
+
if not fragments:
|
|
95
|
+
raise ValueError(f"No fragments found for SMILES '{smiles}'")
|
|
96
|
+
|
|
97
|
+
largest = max(fragments, key=lambda m: m.GetNumHeavyAtoms())
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
Chem.SanitizeMol(largest)
|
|
101
|
+
except Exception as e:
|
|
102
|
+
raise ValueError(f"Sanitization failed for largest fragment: {e}")
|
|
103
|
+
|
|
104
|
+
return Chem.MolToSmiles(largest)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _create_unbalanced_aam(rsmi: str, side: str = "right") -> str:
|
|
108
|
+
"""Produce an unbalanced AAM reaction SMILES by keeping only the largest
|
|
109
|
+
fragment on the specified side(s) of the reaction.
|
|
110
|
+
|
|
111
|
+
:param rsmi: A reaction SMILES "reactant>>product".
|
|
112
|
+
:type rsmi: str
|
|
113
|
+
:param side:
|
|
114
|
+
Which side(s) to process:
|
|
115
|
+
- "left" : clean only the reactant side,
|
|
116
|
+
- "right": clean only the product side,
|
|
117
|
+
- "both" : clean both sides.
|
|
118
|
+
:type side: str
|
|
119
|
+
:return: A new reaction SMILES "reactant>>product" with small fragments removed.
|
|
120
|
+
:rtype: str
|
|
121
|
+
:raises ValueError:
|
|
122
|
+
- If `rsmi` doesn’t contain exactly one ">>".
|
|
123
|
+
- If `side` is not one of "left", "right", "both".
|
|
124
|
+
- If fragment processing fails on the chosen side(s).
|
|
125
|
+
"""
|
|
126
|
+
parts = [p.strip() for p in rsmi.split(">>")]
|
|
127
|
+
if len(parts) != 2:
|
|
128
|
+
raise ValueError(f"Expected single '>>' in reaction SMILES: '{rsmi}'")
|
|
129
|
+
|
|
130
|
+
r_smi, p_smi = parts
|
|
131
|
+
side_l = side.lower()
|
|
132
|
+
if side_l not in ("left", "right", "both"):
|
|
133
|
+
raise ValueError(f"Invalid side '{side}'; must be 'left', 'right', or 'both'")
|
|
134
|
+
|
|
135
|
+
if side_l in ("left", "both"):
|
|
136
|
+
try:
|
|
137
|
+
r_smi = _remove_small_smiles(r_smi)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
raise ValueError(f"Error processing left side: {e}")
|
|
140
|
+
|
|
141
|
+
if side_l in ("right", "both"):
|
|
142
|
+
try:
|
|
143
|
+
p_smi = _remove_small_smiles(p_smi)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
raise ValueError(f"Error processing right side: {e}")
|
|
146
|
+
|
|
147
|
+
return f"{r_smi}>>{p_smi}"
|