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,1059 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Optional, Pattern, Sequence
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
import networkx as nx
|
|
7
|
+
from rdkit import Chem
|
|
8
|
+
from rdkit.Chem import rdChemReactions
|
|
9
|
+
|
|
10
|
+
from synkit.IO.debug import setup_logging
|
|
11
|
+
from synkit.IO.mol_to_graph import MolToGraph
|
|
12
|
+
from synkit.IO.graph_to_mol import GraphToMol
|
|
13
|
+
from synkit.IO.nx_to_gml import NXToGML
|
|
14
|
+
from synkit.IO.gml_to_nx import GMLToNX
|
|
15
|
+
from synkit.Graph.ITS.its_construction import ITSConstruction
|
|
16
|
+
from synkit.Graph.ITS.its_decompose import get_rc, its_decompose
|
|
17
|
+
from synkit.Graph.ITS.rc_extractor import RCExtractor
|
|
18
|
+
from synkit.Graph.ITS.its_reverter import ITSReverter
|
|
19
|
+
|
|
20
|
+
_BRACKET_DIGIT_PATTERN: Pattern[str] = re.compile(r"\[([^\]]*?)\](\d+)")
|
|
21
|
+
_BRACKET_MAP_PATTERN: Pattern[str] = re.compile(r"\[([^\]]+):(\d+)\]")
|
|
22
|
+
|
|
23
|
+
ITSFormat = Literal["typesGH", "tuple"]
|
|
24
|
+
|
|
25
|
+
DEFAULT_NODE_ATTRS = (
|
|
26
|
+
"element",
|
|
27
|
+
"aromatic",
|
|
28
|
+
"hcount",
|
|
29
|
+
"charge",
|
|
30
|
+
"radical",
|
|
31
|
+
"lone_pairs",
|
|
32
|
+
"valence_electrons",
|
|
33
|
+
"neighbors",
|
|
34
|
+
"atom_map",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
DEFAULT_EDGE_ATTRS = (
|
|
38
|
+
"order",
|
|
39
|
+
"kekule_order",
|
|
40
|
+
"sigma_order",
|
|
41
|
+
"pi_order",
|
|
42
|
+
"aromatic",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
logger = setup_logging()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _resolve_graph_attrs(
|
|
49
|
+
node_attrs: Optional[Sequence[str]] = None,
|
|
50
|
+
edge_attrs: Optional[Sequence[str]] = None,
|
|
51
|
+
) -> tuple[list[str], list[str]]:
|
|
52
|
+
"""
|
|
53
|
+
Resolve node and edge attribute lists.
|
|
54
|
+
|
|
55
|
+
:param node_attrs: Optional node attribute names.
|
|
56
|
+
:type node_attrs: Optional[Sequence[str]]
|
|
57
|
+
:param edge_attrs: Optional edge attribute names.
|
|
58
|
+
:type edge_attrs: Optional[Sequence[str]]
|
|
59
|
+
:return: Normalized node and edge attribute lists.
|
|
60
|
+
:rtype: tuple[list[str], list[str]]
|
|
61
|
+
"""
|
|
62
|
+
resolved_node_attrs = list(node_attrs or DEFAULT_NODE_ATTRS)
|
|
63
|
+
resolved_edge_attrs = list(edge_attrs or DEFAULT_EDGE_ATTRS)
|
|
64
|
+
return resolved_node_attrs, resolved_edge_attrs
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _validate_its_format(format: str) -> ITSFormat:
|
|
68
|
+
"""
|
|
69
|
+
Validate ITS serialization format.
|
|
70
|
+
|
|
71
|
+
:param format: Requested ITS format.
|
|
72
|
+
:type format: str
|
|
73
|
+
:return: Validated ITS format.
|
|
74
|
+
:rtype: ITSFormat
|
|
75
|
+
:raises ValueError: If the format is unsupported.
|
|
76
|
+
"""
|
|
77
|
+
valid_formats = ("typesGH", "tuple")
|
|
78
|
+
if format not in valid_formats:
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"Unsupported format: {format!r}. " f"Expected one of {valid_formats}."
|
|
81
|
+
)
|
|
82
|
+
return format
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def detect_its_format(graph: nx.Graph) -> ITSFormat:
|
|
86
|
+
"""
|
|
87
|
+
Detect the ITS storage representation used by a graph.
|
|
88
|
+
|
|
89
|
+
Legacy ITS graphs keep scalar node attributes and store side-specific
|
|
90
|
+
values only in ``typesGH``. Tuple ITS graphs store direct paired node and
|
|
91
|
+
edge attributes such as ``element=("C", "C")`` or
|
|
92
|
+
``sigma_order=(1.0, 1.0)``.
|
|
93
|
+
|
|
94
|
+
:param graph: ITS-like graph to inspect.
|
|
95
|
+
:type graph: nx.Graph
|
|
96
|
+
:return: Detected ITS format.
|
|
97
|
+
:rtype: ITSFormat
|
|
98
|
+
"""
|
|
99
|
+
tuple_node_keys = (
|
|
100
|
+
"element",
|
|
101
|
+
"aromatic",
|
|
102
|
+
"hcount",
|
|
103
|
+
"charge",
|
|
104
|
+
"radical",
|
|
105
|
+
"lone_pairs",
|
|
106
|
+
"valence_electrons",
|
|
107
|
+
)
|
|
108
|
+
tuple_edge_keys = ("kekule_order", "sigma_order", "pi_order")
|
|
109
|
+
|
|
110
|
+
for _, attrs in graph.nodes(data=True):
|
|
111
|
+
if any(_is_pair(attrs.get(key)) for key in tuple_node_keys):
|
|
112
|
+
return "tuple"
|
|
113
|
+
|
|
114
|
+
for _, _, attrs in graph.edges(data=True):
|
|
115
|
+
if any(_is_pair(attrs.get(key)) for key in tuple_edge_keys):
|
|
116
|
+
return "tuple"
|
|
117
|
+
|
|
118
|
+
return "typesGH"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _split_rsmi(rsmi: str) -> tuple[str, str]:
|
|
122
|
+
"""
|
|
123
|
+
Split a reaction SMILES string into reactant and product parts.
|
|
124
|
+
|
|
125
|
+
:param rsmi: Reaction SMILES string in ``reactants>>products`` format.
|
|
126
|
+
:type rsmi: str
|
|
127
|
+
:return: Tuple of reactant SMILES and product SMILES.
|
|
128
|
+
:rtype: tuple[str, str]
|
|
129
|
+
:raises ValueError: If the string is not a valid reaction SMILES.
|
|
130
|
+
"""
|
|
131
|
+
parts = rsmi.split(">>")
|
|
132
|
+
if len(parts) != 2:
|
|
133
|
+
raise ValueError(f"Invalid RSMI format: {rsmi}")
|
|
134
|
+
return parts[0], parts[1]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _require_graph(graph: Optional[nx.Graph], label: str) -> nx.Graph:
|
|
138
|
+
"""
|
|
139
|
+
Require a graph object to be present.
|
|
140
|
+
|
|
141
|
+
:param graph: Graph to validate.
|
|
142
|
+
:type graph: Optional[nx.Graph]
|
|
143
|
+
:param label: Human-readable graph label.
|
|
144
|
+
:type label: str
|
|
145
|
+
:return: Validated graph.
|
|
146
|
+
:rtype: nx.Graph
|
|
147
|
+
:raises ValueError: If the graph is ``None``.
|
|
148
|
+
"""
|
|
149
|
+
if graph is None:
|
|
150
|
+
raise ValueError(f"Failed to build {label} graph.")
|
|
151
|
+
return graph
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _is_pair(value: object) -> bool:
|
|
155
|
+
"""
|
|
156
|
+
Check whether a value is a 2-item pair.
|
|
157
|
+
|
|
158
|
+
:param value: Value to inspect.
|
|
159
|
+
:type value: object
|
|
160
|
+
:return: ``True`` if the value is a tuple or list of length 2.
|
|
161
|
+
:rtype: bool
|
|
162
|
+
"""
|
|
163
|
+
return isinstance(value, (tuple, list)) and len(value) == 2
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _extract_scalar(value: object) -> object:
|
|
167
|
+
"""
|
|
168
|
+
Extract a representative scalar value.
|
|
169
|
+
|
|
170
|
+
If the input is a paired value, the first entry is returned.
|
|
171
|
+
|
|
172
|
+
:param value: Input value.
|
|
173
|
+
:type value: object
|
|
174
|
+
:return: Scalar-like representative value.
|
|
175
|
+
:rtype: object
|
|
176
|
+
"""
|
|
177
|
+
if _is_pair(value):
|
|
178
|
+
return value[0]
|
|
179
|
+
return value
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _local_charge_model_consistent(graph: nx.Graph, node: object) -> bool:
|
|
183
|
+
"""Test the local ``VE - NBE - B`` formal-charge identity."""
|
|
184
|
+
if node not in graph:
|
|
185
|
+
return False
|
|
186
|
+
attrs = graph.nodes[node]
|
|
187
|
+
required = ("valence_electrons", "lone_pairs", "radical", "hcount", "charge")
|
|
188
|
+
if any(attrs.get(name) is None for name in required):
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
bond_sum = 0.0
|
|
192
|
+
for _, _, edge_attrs in graph.edges(node, data=True):
|
|
193
|
+
sigma = edge_attrs.get("sigma_order")
|
|
194
|
+
pi = edge_attrs.get("pi_order")
|
|
195
|
+
if sigma is not None and pi is not None:
|
|
196
|
+
bond_sum += float(sigma) + float(pi)
|
|
197
|
+
continue
|
|
198
|
+
order = edge_attrs.get("kekule_order", edge_attrs.get("order"))
|
|
199
|
+
if order is None:
|
|
200
|
+
return False
|
|
201
|
+
bond_sum += float(order)
|
|
202
|
+
|
|
203
|
+
modeled_charge = (
|
|
204
|
+
float(attrs["valence_electrons"])
|
|
205
|
+
- 2.0 * float(attrs["lone_pairs"])
|
|
206
|
+
- float(attrs["radical"])
|
|
207
|
+
- float(attrs["hcount"])
|
|
208
|
+
- bond_sum
|
|
209
|
+
)
|
|
210
|
+
return modeled_charge == float(attrs["charge"])
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _extract_atom_map(attrs: dict) -> Optional[int]:
|
|
214
|
+
"""
|
|
215
|
+
Extract atom-map value from a node attribute dictionary.
|
|
216
|
+
|
|
217
|
+
:param attrs: Node attribute dictionary.
|
|
218
|
+
:type attrs: dict
|
|
219
|
+
:return: Atom-map number if available.
|
|
220
|
+
:rtype: Optional[int]
|
|
221
|
+
"""
|
|
222
|
+
value = _extract_scalar(attrs.get("atom_map"))
|
|
223
|
+
if value is None:
|
|
224
|
+
return None
|
|
225
|
+
try:
|
|
226
|
+
return int(value)
|
|
227
|
+
except (TypeError, ValueError):
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _is_hydrogen_node(attrs: dict) -> bool:
|
|
232
|
+
"""
|
|
233
|
+
Determine whether a node represents hydrogen.
|
|
234
|
+
|
|
235
|
+
Supports both scalar and paired ``element`` attributes.
|
|
236
|
+
|
|
237
|
+
:param attrs: Node attribute dictionary.
|
|
238
|
+
:type attrs: dict
|
|
239
|
+
:return: ``True`` if the node corresponds to hydrogen.
|
|
240
|
+
:rtype: bool
|
|
241
|
+
"""
|
|
242
|
+
value = attrs.get("element")
|
|
243
|
+
if _is_pair(value):
|
|
244
|
+
return any(item == "H" for item in value)
|
|
245
|
+
return value == "H"
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _build_its_for_rsmi(
|
|
249
|
+
reactant_graph: nx.Graph,
|
|
250
|
+
product_graph: nx.Graph,
|
|
251
|
+
format: ITSFormat,
|
|
252
|
+
) -> nx.Graph:
|
|
253
|
+
"""
|
|
254
|
+
Build an ITS graph for downstream RSMI generation.
|
|
255
|
+
|
|
256
|
+
:param reactant_graph: Reactant graph.
|
|
257
|
+
:type reactant_graph: nx.Graph
|
|
258
|
+
:param product_graph: Product graph.
|
|
259
|
+
:type product_graph: nx.Graph
|
|
260
|
+
:param format: ITS format.
|
|
261
|
+
:type format: ITSFormat
|
|
262
|
+
:return: Constructed ITS graph.
|
|
263
|
+
:rtype: nx.Graph
|
|
264
|
+
"""
|
|
265
|
+
if format == "typesGH":
|
|
266
|
+
return ITSConstruction().ITSGraph(reactant_graph, product_graph)
|
|
267
|
+
|
|
268
|
+
return ITSConstruction().construct(
|
|
269
|
+
reactant_graph,
|
|
270
|
+
product_graph,
|
|
271
|
+
node_attrs=list(DEFAULT_NODE_ATTRS),
|
|
272
|
+
edge_attrs=list(DEFAULT_EDGE_ATTRS),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _extract_rc_graph(its: nx.Graph, format: ITSFormat) -> nx.Graph:
|
|
277
|
+
"""
|
|
278
|
+
Extract the reaction-center graph from an ITS graph.
|
|
279
|
+
|
|
280
|
+
:param its: ITS graph.
|
|
281
|
+
:type its: nx.Graph
|
|
282
|
+
:param format: ITS format.
|
|
283
|
+
:type format: ITSFormat
|
|
284
|
+
:return: Reaction-center graph.
|
|
285
|
+
:rtype: nx.Graph
|
|
286
|
+
"""
|
|
287
|
+
if format == "typesGH":
|
|
288
|
+
return get_rc(its)
|
|
289
|
+
|
|
290
|
+
return RCExtractor(preserve_full_attrs=True).extract(its)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _get_preserved_hydrogen_maps(
|
|
294
|
+
its: nx.Graph,
|
|
295
|
+
format: ITSFormat,
|
|
296
|
+
) -> list[int]:
|
|
297
|
+
"""
|
|
298
|
+
Collect atom maps of hydrogens that cannot be implicitized.
|
|
299
|
+
|
|
300
|
+
Reaction-centre hydrogens retain explicit identity. Hydrogen-only
|
|
301
|
+
components also remain explicit because there is no heavy-atom neighbour
|
|
302
|
+
onto which their multiplicity can be projected.
|
|
303
|
+
|
|
304
|
+
:param its: ITS graph.
|
|
305
|
+
:type its: nx.Graph
|
|
306
|
+
:param format: ITS format.
|
|
307
|
+
:type format: ITSFormat
|
|
308
|
+
:return: Sorted unique hydrogen atom-map numbers.
|
|
309
|
+
:rtype: list[int]
|
|
310
|
+
"""
|
|
311
|
+
rc_graph = _extract_rc_graph(its, format)
|
|
312
|
+
atom_maps: set[int] = set()
|
|
313
|
+
|
|
314
|
+
for _, attrs in rc_graph.nodes(data=True):
|
|
315
|
+
if not _is_hydrogen_node(attrs):
|
|
316
|
+
continue
|
|
317
|
+
atom_map = _extract_atom_map(attrs)
|
|
318
|
+
if atom_map is not None:
|
|
319
|
+
atom_maps.add(atom_map)
|
|
320
|
+
|
|
321
|
+
for node, attrs in its.nodes(data=True):
|
|
322
|
+
if not _is_hydrogen_node(attrs) or any(
|
|
323
|
+
not _is_hydrogen_node(its.nodes[neighbor])
|
|
324
|
+
for neighbor in its.neighbors(node)
|
|
325
|
+
):
|
|
326
|
+
continue
|
|
327
|
+
atom_map = _extract_atom_map(attrs)
|
|
328
|
+
if atom_map is not None:
|
|
329
|
+
atom_maps.add(atom_map)
|
|
330
|
+
|
|
331
|
+
return sorted(atom_maps)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _decompose_its(
|
|
335
|
+
its: nx.Graph,
|
|
336
|
+
format: ITSFormat,
|
|
337
|
+
) -> tuple[nx.Graph, nx.Graph]:
|
|
338
|
+
"""
|
|
339
|
+
Convert an ITS graph back to reactant and product graphs.
|
|
340
|
+
|
|
341
|
+
:param its: ITS graph.
|
|
342
|
+
:type its: nx.Graph
|
|
343
|
+
:param format: ITS format.
|
|
344
|
+
:type format: ITSFormat
|
|
345
|
+
:return: Reactant and product graphs.
|
|
346
|
+
:rtype: tuple[nx.Graph, nx.Graph]
|
|
347
|
+
"""
|
|
348
|
+
if format == "typesGH":
|
|
349
|
+
return its_decompose(its)
|
|
350
|
+
|
|
351
|
+
reverter = ITSReverter(its)
|
|
352
|
+
return reverter.to_reactant_graph(), reverter.to_product_graph()
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def smiles_to_graph(
|
|
356
|
+
smiles: str,
|
|
357
|
+
drop_non_aam: bool = False,
|
|
358
|
+
sanitize: bool = True,
|
|
359
|
+
use_index_as_atom_map: bool = False,
|
|
360
|
+
node_attrs: Optional[Sequence[str]] = None,
|
|
361
|
+
edge_attrs: Optional[Sequence[str]] = None,
|
|
362
|
+
include_stereo_descriptors: bool = True,
|
|
363
|
+
) -> Optional[nx.Graph]:
|
|
364
|
+
"""
|
|
365
|
+
Convert a SMILES string to a molecular graph.
|
|
366
|
+
|
|
367
|
+
:param smiles: SMILES representation of the molecule.
|
|
368
|
+
:type smiles: str
|
|
369
|
+
:param drop_non_aam: If ``True``, drop atoms without atom-map labels.
|
|
370
|
+
:type drop_non_aam: bool
|
|
371
|
+
:param sanitize: If ``True``, sanitize the RDKit molecule.
|
|
372
|
+
:type sanitize: bool
|
|
373
|
+
:param use_index_as_atom_map: If ``True``, overwrite atom-map labels
|
|
374
|
+
using atom indices.
|
|
375
|
+
:type use_index_as_atom_map: bool
|
|
376
|
+
:param node_attrs: Node attributes to export into the graph.
|
|
377
|
+
:type node_attrs: Optional[Sequence[str]]
|
|
378
|
+
:param edge_attrs: Edge attributes to export into the graph.
|
|
379
|
+
:type edge_attrs: Optional[Sequence[str]]
|
|
380
|
+
:param include_stereo_descriptors: Whether to construct the graph-level
|
|
381
|
+
relative-stereochemistry registry.
|
|
382
|
+
:type include_stereo_descriptors: bool
|
|
383
|
+
:return: Molecular graph or ``None`` on failure.
|
|
384
|
+
:rtype: Optional[nx.Graph]
|
|
385
|
+
"""
|
|
386
|
+
resolved_node_attrs, resolved_edge_attrs = _resolve_graph_attrs(
|
|
387
|
+
node_attrs=node_attrs,
|
|
388
|
+
edge_attrs=edge_attrs,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
mol = Chem.MolFromSmiles(smiles, sanitize=False)
|
|
393
|
+
if mol is None:
|
|
394
|
+
logger.warning("Failed to parse SMILES: %s", smiles)
|
|
395
|
+
return None
|
|
396
|
+
|
|
397
|
+
if sanitize:
|
|
398
|
+
try:
|
|
399
|
+
Chem.SanitizeMol(mol)
|
|
400
|
+
except Exception as exc:
|
|
401
|
+
logger.error(
|
|
402
|
+
"Sanitization failed for SMILES %s: %s",
|
|
403
|
+
smiles,
|
|
404
|
+
exc,
|
|
405
|
+
)
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
graph = MolToGraph(
|
|
409
|
+
node_attrs=resolved_node_attrs,
|
|
410
|
+
edge_attrs=resolved_edge_attrs,
|
|
411
|
+
include_stereo_descriptors=include_stereo_descriptors,
|
|
412
|
+
).transform(
|
|
413
|
+
mol,
|
|
414
|
+
drop_non_aam=drop_non_aam,
|
|
415
|
+
use_index_as_atom_map=use_index_as_atom_map,
|
|
416
|
+
)
|
|
417
|
+
if graph is None:
|
|
418
|
+
logger.warning(
|
|
419
|
+
"Failed to convert molecule to graph for SMILES: %s",
|
|
420
|
+
smiles,
|
|
421
|
+
)
|
|
422
|
+
return graph
|
|
423
|
+
except Exception as exc:
|
|
424
|
+
logger.error(
|
|
425
|
+
"Unhandled exception while converting SMILES to graph: %s; %s",
|
|
426
|
+
smiles,
|
|
427
|
+
exc,
|
|
428
|
+
)
|
|
429
|
+
return None
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def rsmi_to_graph(
|
|
433
|
+
rsmi: str,
|
|
434
|
+
drop_non_aam: bool = True,
|
|
435
|
+
sanitize: bool = True,
|
|
436
|
+
use_index_as_atom_map: bool = True,
|
|
437
|
+
node_attrs: Optional[Sequence[str]] = None,
|
|
438
|
+
edge_attrs: Optional[Sequence[str]] = None,
|
|
439
|
+
include_stereo_descriptors: bool = True,
|
|
440
|
+
) -> tuple[Optional[nx.Graph], Optional[nx.Graph]]:
|
|
441
|
+
"""
|
|
442
|
+
Convert a reaction SMILES into reactant and product graphs.
|
|
443
|
+
|
|
444
|
+
:param rsmi: Reaction SMILES string in ``reactants>>products`` format.
|
|
445
|
+
:type rsmi: str
|
|
446
|
+
:param drop_non_aam: If ``True``, drop atoms lacking atom maps.
|
|
447
|
+
:type drop_non_aam: bool
|
|
448
|
+
:param sanitize: If ``True``, sanitize molecules during conversion.
|
|
449
|
+
:type sanitize: bool
|
|
450
|
+
:param use_index_as_atom_map: If ``True``, overwrite atom-map labels
|
|
451
|
+
using atom indices.
|
|
452
|
+
:type use_index_as_atom_map: bool
|
|
453
|
+
:param node_attrs: Node attributes to export into the graphs.
|
|
454
|
+
:type node_attrs: Optional[Sequence[str]]
|
|
455
|
+
:param edge_attrs: Edge attributes to export into the graphs.
|
|
456
|
+
:type edge_attrs: Optional[Sequence[str]]
|
|
457
|
+
:param include_stereo_descriptors: Whether to construct graph-level
|
|
458
|
+
relative-stereochemistry registries.
|
|
459
|
+
:type include_stereo_descriptors: bool
|
|
460
|
+
:return: Tuple of reactant and product graphs.
|
|
461
|
+
:rtype: tuple[Optional[nx.Graph], Optional[nx.Graph]]
|
|
462
|
+
"""
|
|
463
|
+
resolved_node_attrs, resolved_edge_attrs = _resolve_graph_attrs(
|
|
464
|
+
node_attrs=node_attrs,
|
|
465
|
+
edge_attrs=edge_attrs,
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
try:
|
|
469
|
+
reactants_smiles, products_smiles = _split_rsmi(rsmi)
|
|
470
|
+
except ValueError:
|
|
471
|
+
logger.error("Invalid RSMI format: %s", rsmi)
|
|
472
|
+
return None, None
|
|
473
|
+
|
|
474
|
+
reactant_graph = smiles_to_graph(
|
|
475
|
+
smiles=reactants_smiles,
|
|
476
|
+
drop_non_aam=drop_non_aam,
|
|
477
|
+
sanitize=sanitize,
|
|
478
|
+
use_index_as_atom_map=use_index_as_atom_map,
|
|
479
|
+
node_attrs=resolved_node_attrs,
|
|
480
|
+
edge_attrs=resolved_edge_attrs,
|
|
481
|
+
include_stereo_descriptors=include_stereo_descriptors,
|
|
482
|
+
)
|
|
483
|
+
product_graph = smiles_to_graph(
|
|
484
|
+
smiles=products_smiles,
|
|
485
|
+
drop_non_aam=drop_non_aam,
|
|
486
|
+
sanitize=sanitize,
|
|
487
|
+
use_index_as_atom_map=use_index_as_atom_map,
|
|
488
|
+
node_attrs=resolved_node_attrs,
|
|
489
|
+
edge_attrs=resolved_edge_attrs,
|
|
490
|
+
include_stereo_descriptors=include_stereo_descriptors,
|
|
491
|
+
)
|
|
492
|
+
return reactant_graph, product_graph
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def graph_to_smi(
|
|
496
|
+
graph: nx.Graph,
|
|
497
|
+
sanitize: bool = True,
|
|
498
|
+
preserve_atom_maps: Optional[Sequence[int]] = None,
|
|
499
|
+
prefer_kekule_order: bool = True,
|
|
500
|
+
) -> Optional[str]:
|
|
501
|
+
"""
|
|
502
|
+
Convert a molecular graph to a SMILES string.
|
|
503
|
+
|
|
504
|
+
:param graph: Molecular graph.
|
|
505
|
+
:type graph: nx.Graph
|
|
506
|
+
:param sanitize: If ``True``, sanitize the generated molecule.
|
|
507
|
+
:type sanitize: bool
|
|
508
|
+
:param preserve_atom_maps: Atom-map numbers whose hydrogens should
|
|
509
|
+
remain explicit.
|
|
510
|
+
:type preserve_atom_maps: Optional[Sequence[int]]
|
|
511
|
+
:param prefer_kekule_order: Whether retained Kekule/sigma-pi edge fields
|
|
512
|
+
should take precedence over aromatic presentation order.
|
|
513
|
+
:type prefer_kekule_order: bool
|
|
514
|
+
:return: SMILES string or ``None`` on failure.
|
|
515
|
+
:rtype: Optional[str]
|
|
516
|
+
"""
|
|
517
|
+
try:
|
|
518
|
+
preserved = list(preserve_atom_maps or [])
|
|
519
|
+
if not preserved:
|
|
520
|
+
mol = GraphToMol().graph_to_mol(
|
|
521
|
+
graph,
|
|
522
|
+
sanitize=sanitize,
|
|
523
|
+
use_h_count=True,
|
|
524
|
+
prefer_kekule_order=prefer_kekule_order,
|
|
525
|
+
)
|
|
526
|
+
else:
|
|
527
|
+
from synkit.Graph.Hyrogen._misc import implicit_hydrogen
|
|
528
|
+
|
|
529
|
+
graph_imp = implicit_hydrogen(graph, set(preserved))
|
|
530
|
+
mol = GraphToMol().graph_to_mol(
|
|
531
|
+
graph_imp,
|
|
532
|
+
sanitize=sanitize,
|
|
533
|
+
use_h_count=True,
|
|
534
|
+
prefer_kekule_order=prefer_kekule_order,
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
if mol is None:
|
|
538
|
+
return None
|
|
539
|
+
return Chem.MolToSmiles(mol)
|
|
540
|
+
except Exception as exc:
|
|
541
|
+
logger.debug("Error generating SMILES: %s", exc)
|
|
542
|
+
return None
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def graph_to_rsmi(
|
|
546
|
+
r: nx.Graph,
|
|
547
|
+
p: nx.Graph,
|
|
548
|
+
its: Optional[nx.Graph] = None,
|
|
549
|
+
sanitize: bool = True,
|
|
550
|
+
explicit_hydrogen: bool = False,
|
|
551
|
+
preserve_hydrogen_maps: Optional[Sequence[int]] = None,
|
|
552
|
+
) -> Optional[str]:
|
|
553
|
+
"""Convert reactant and product graphs into a reaction SMILES string.
|
|
554
|
+
|
|
555
|
+
:param r: Graph representing the reactants.
|
|
556
|
+
:type r: networkx.Graph
|
|
557
|
+
:param p: Graph representing the products.
|
|
558
|
+
:type p: networkx.Graph
|
|
559
|
+
:param its: Imaginary transition state graph. If None, it will be
|
|
560
|
+
constructed.
|
|
561
|
+
:type its: networkx.Graph or None
|
|
562
|
+
:param sanitize: Whether to sanitize molecules during conversion.
|
|
563
|
+
:type sanitize: bool
|
|
564
|
+
:param explicit_hydrogen: Whether to preserve explicit hydrogens in
|
|
565
|
+
the SMILES.
|
|
566
|
+
:type explicit_hydrogen: bool
|
|
567
|
+
:param preserve_hydrogen_maps: Precomputed atom maps of reaction-centre
|
|
568
|
+
hydrogens that must remain explicit. When omitted, derive them from
|
|
569
|
+
``its`` for backward compatibility.
|
|
570
|
+
:type preserve_hydrogen_maps: Optional[Sequence[int]]
|
|
571
|
+
:return: Reaction SMILES string in 'reactants>>products' format or
|
|
572
|
+
None on failure.
|
|
573
|
+
:rtype: str or None
|
|
574
|
+
"""
|
|
575
|
+
try:
|
|
576
|
+
if explicit_hydrogen:
|
|
577
|
+
r_smiles = graph_to_smi(r, sanitize=sanitize)
|
|
578
|
+
p_smiles = graph_to_smi(p, sanitize=sanitize)
|
|
579
|
+
else:
|
|
580
|
+
if preserve_hydrogen_maps is None:
|
|
581
|
+
if its is None:
|
|
582
|
+
its = ITSConstruction().ITSGraph(r, p)
|
|
583
|
+
rc = get_rc(its)
|
|
584
|
+
list_hydrogen = [
|
|
585
|
+
d["atom_map"]
|
|
586
|
+
for _, d in rc.nodes(data=True)
|
|
587
|
+
if d.get("element") == "H"
|
|
588
|
+
]
|
|
589
|
+
else:
|
|
590
|
+
list_hydrogen = list(preserve_hydrogen_maps)
|
|
591
|
+
r_smiles = graph_to_smi(
|
|
592
|
+
r,
|
|
593
|
+
sanitize=sanitize,
|
|
594
|
+
preserve_atom_maps=list_hydrogen,
|
|
595
|
+
)
|
|
596
|
+
p_smiles = graph_to_smi(
|
|
597
|
+
p,
|
|
598
|
+
sanitize=sanitize,
|
|
599
|
+
preserve_atom_maps=list_hydrogen,
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
if r_smiles is None or p_smiles is None:
|
|
603
|
+
return None
|
|
604
|
+
|
|
605
|
+
return f"{r_smiles}>>{p_smiles}"
|
|
606
|
+
except Exception as e:
|
|
607
|
+
logger.debug(f"Error in generating reaction SMILES: {str(e)}")
|
|
608
|
+
return None
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def rsmi_to_its(
|
|
612
|
+
rsmi: str,
|
|
613
|
+
drop_non_aam: bool = True,
|
|
614
|
+
sanitize: bool = True,
|
|
615
|
+
use_index_as_atom_map: bool = True,
|
|
616
|
+
core: bool = False,
|
|
617
|
+
node_attrs: Optional[Sequence[str]] = None,
|
|
618
|
+
edge_attrs: Optional[Sequence[str]] = None,
|
|
619
|
+
explicit_hydrogen: bool = False,
|
|
620
|
+
format: ITSFormat = "typesGH",
|
|
621
|
+
include_context_edges: bool = True,
|
|
622
|
+
) -> nx.Graph:
|
|
623
|
+
"""
|
|
624
|
+
Convert a reaction SMILES into an ITS graph.
|
|
625
|
+
|
|
626
|
+
Supported formats:
|
|
627
|
+
|
|
628
|
+
- ``"typesGH"``: legacy ITS representation
|
|
629
|
+
- ``"tuple"``: paired-attribute ITS representation
|
|
630
|
+
|
|
631
|
+
:param rsmi: Reaction SMILES string.
|
|
632
|
+
:type rsmi: str
|
|
633
|
+
:param drop_non_aam: If ``True``, discard fragments lacking atom maps.
|
|
634
|
+
:type drop_non_aam: bool
|
|
635
|
+
:param sanitize: If ``True``, sanitize molecules during conversion.
|
|
636
|
+
:type sanitize: bool
|
|
637
|
+
:param use_index_as_atom_map: If ``True``, overwrite atom maps using
|
|
638
|
+
atom indices.
|
|
639
|
+
:type use_index_as_atom_map: bool
|
|
640
|
+
:param core: If ``True``, return only the reaction-center graph.
|
|
641
|
+
:type core: bool
|
|
642
|
+
:param node_attrs: Node attributes to include in graph construction.
|
|
643
|
+
:type node_attrs: Optional[Sequence[str]]
|
|
644
|
+
:param edge_attrs: Edge attributes to include in graph construction.
|
|
645
|
+
:type edge_attrs: Optional[Sequence[str]]
|
|
646
|
+
:param explicit_hydrogen: If ``True``, convert implicit hydrogens to
|
|
647
|
+
explicit nodes for the selected ITS format.
|
|
648
|
+
:type explicit_hydrogen: bool
|
|
649
|
+
:param format: ITS format.
|
|
650
|
+
:type format: ITSFormat
|
|
651
|
+
:param include_context_edges: For tuple-format reaction centers, include
|
|
652
|
+
unchanged edges between reaction-center nodes when ``True``. Set to
|
|
653
|
+
``False`` for a minimal changed-edge reaction center.
|
|
654
|
+
:type include_context_edges: bool
|
|
655
|
+
:return: ITS graph or RC graph.
|
|
656
|
+
:rtype: nx.Graph
|
|
657
|
+
:raises ValueError: If graph construction fails.
|
|
658
|
+
"""
|
|
659
|
+
validated_format = _validate_its_format(format)
|
|
660
|
+
resolved_node_attrs, resolved_edge_attrs = _resolve_graph_attrs(
|
|
661
|
+
node_attrs=node_attrs,
|
|
662
|
+
edge_attrs=edge_attrs,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
reactant_graph, product_graph = rsmi_to_graph(
|
|
666
|
+
rsmi=rsmi,
|
|
667
|
+
drop_non_aam=drop_non_aam,
|
|
668
|
+
sanitize=sanitize,
|
|
669
|
+
use_index_as_atom_map=use_index_as_atom_map,
|
|
670
|
+
node_attrs=resolved_node_attrs,
|
|
671
|
+
edge_attrs=resolved_edge_attrs,
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
reactant_graph = _require_graph(reactant_graph, "reactant")
|
|
675
|
+
product_graph = _require_graph(product_graph, "product")
|
|
676
|
+
|
|
677
|
+
if validated_format == "typesGH":
|
|
678
|
+
its_graph = ITSConstruction().ITSGraph(reactant_graph, product_graph)
|
|
679
|
+
if explicit_hydrogen:
|
|
680
|
+
from synkit.Graph.Hyrogen._misc import h_to_explicit
|
|
681
|
+
|
|
682
|
+
its_graph = h_to_explicit(its_graph, None, True)
|
|
683
|
+
if core:
|
|
684
|
+
its_graph = get_rc(its_graph)
|
|
685
|
+
return its_graph
|
|
686
|
+
|
|
687
|
+
its_graph = ITSConstruction().construct(
|
|
688
|
+
reactant_graph,
|
|
689
|
+
product_graph,
|
|
690
|
+
node_attrs=resolved_node_attrs,
|
|
691
|
+
edge_attrs=resolved_edge_attrs,
|
|
692
|
+
)
|
|
693
|
+
for node in its_graph:
|
|
694
|
+
its_graph.nodes[node]["charge_model_consistent"] = (
|
|
695
|
+
_local_charge_model_consistent(reactant_graph, node),
|
|
696
|
+
_local_charge_model_consistent(product_graph, node),
|
|
697
|
+
)
|
|
698
|
+
if explicit_hydrogen:
|
|
699
|
+
from synkit.Graph.Hyrogen._misc import h_to_explicit
|
|
700
|
+
|
|
701
|
+
its_graph = h_to_explicit(its_graph, None, True)
|
|
702
|
+
if core:
|
|
703
|
+
its_graph = RCExtractor(
|
|
704
|
+
node_attrs=resolved_node_attrs,
|
|
705
|
+
edge_attrs=resolved_edge_attrs,
|
|
706
|
+
preserve_full_attrs=False,
|
|
707
|
+
).extract(
|
|
708
|
+
its_graph,
|
|
709
|
+
include_context_edges=include_context_edges,
|
|
710
|
+
)
|
|
711
|
+
return its_graph
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def its_to_rsmi(
|
|
715
|
+
its: nx.Graph,
|
|
716
|
+
sanitize: bool = True,
|
|
717
|
+
explicit_hydrogen: bool = False,
|
|
718
|
+
clean_wildcards: bool = False,
|
|
719
|
+
format: ITSFormat = "typesGH",
|
|
720
|
+
) -> str:
|
|
721
|
+
"""
|
|
722
|
+
Convert an ITS graph into a reaction SMILES (rSMI) string.
|
|
723
|
+
|
|
724
|
+
This function decomposes or reverts the ITS graph into reactant and
|
|
725
|
+
product graphs depending on the selected ITS format, then serializes
|
|
726
|
+
them into a reaction SMILES string.
|
|
727
|
+
|
|
728
|
+
:param its: ITS graph to convert back into reaction SMILES.
|
|
729
|
+
:type its: nx.Graph
|
|
730
|
+
:param sanitize: If ``True``, sanitize graphs before SMILES generation.
|
|
731
|
+
:type sanitize: bool
|
|
732
|
+
:param explicit_hydrogen: If ``True``, include explicit hydrogens in
|
|
733
|
+
the generated SMILES.
|
|
734
|
+
:type explicit_hydrogen: bool
|
|
735
|
+
:param clean_wildcards: If ``True``, clean wildcard radicals in the
|
|
736
|
+
generated reaction SMILES.
|
|
737
|
+
:type clean_wildcards: bool
|
|
738
|
+
:param format: ITS format. Supported values are ``"typesGH"`` and
|
|
739
|
+
``"tuple"``.
|
|
740
|
+
:type format: ITSFormat
|
|
741
|
+
:return: Reaction SMILES string.
|
|
742
|
+
:rtype: str
|
|
743
|
+
:raises ValueError: If the ITS format is unsupported.
|
|
744
|
+
"""
|
|
745
|
+
validated_format = _validate_its_format(format)
|
|
746
|
+
reactant_graph, product_graph = _decompose_its(its, validated_format)
|
|
747
|
+
|
|
748
|
+
if validated_format == "tuple":
|
|
749
|
+
preserved_hydrogens = (
|
|
750
|
+
[]
|
|
751
|
+
if explicit_hydrogen
|
|
752
|
+
else _get_preserved_hydrogen_maps(its, validated_format)
|
|
753
|
+
)
|
|
754
|
+
reactant_smiles = graph_to_smi(
|
|
755
|
+
reactant_graph,
|
|
756
|
+
sanitize=sanitize,
|
|
757
|
+
preserve_atom_maps=preserved_hydrogens,
|
|
758
|
+
)
|
|
759
|
+
try:
|
|
760
|
+
if explicit_hydrogen:
|
|
761
|
+
product = product_graph
|
|
762
|
+
else:
|
|
763
|
+
from synkit.Graph.Hyrogen._misc import implicit_hydrogen
|
|
764
|
+
|
|
765
|
+
product = implicit_hydrogen(
|
|
766
|
+
product_graph,
|
|
767
|
+
set(preserved_hydrogens),
|
|
768
|
+
)
|
|
769
|
+
product_smiles = graph_to_smi(
|
|
770
|
+
product,
|
|
771
|
+
sanitize=sanitize,
|
|
772
|
+
preserve_atom_maps=preserved_hydrogens,
|
|
773
|
+
)
|
|
774
|
+
except Exception as exc:
|
|
775
|
+
logger.debug("Error generating tuple product SMILES: %s", exc)
|
|
776
|
+
product_smiles = None
|
|
777
|
+
rsmi = (
|
|
778
|
+
f"{reactant_smiles}>>{product_smiles}"
|
|
779
|
+
if reactant_smiles is not None and product_smiles is not None
|
|
780
|
+
else None
|
|
781
|
+
)
|
|
782
|
+
else:
|
|
783
|
+
rsmi = graph_to_rsmi(
|
|
784
|
+
reactant_graph,
|
|
785
|
+
product_graph,
|
|
786
|
+
its,
|
|
787
|
+
sanitize,
|
|
788
|
+
explicit_hydrogen,
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
if rsmi is None:
|
|
792
|
+
raise ValueError("Failed to convert ITS graph to reaction SMILES.")
|
|
793
|
+
|
|
794
|
+
if clean_wildcards:
|
|
795
|
+
from synkit.Chem.Reaction.radical_wildcard import clean_wc
|
|
796
|
+
|
|
797
|
+
rsmi = clean_wc(rsmi)
|
|
798
|
+
|
|
799
|
+
return rsmi
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
def smart_to_gml(
|
|
803
|
+
smart: str,
|
|
804
|
+
core: bool = True,
|
|
805
|
+
sanitize: bool = True,
|
|
806
|
+
rule_name: str = "rule",
|
|
807
|
+
reindex: bool = False,
|
|
808
|
+
explicit_hydrogen: bool = False,
|
|
809
|
+
useSmiles: bool = True,
|
|
810
|
+
) -> str:
|
|
811
|
+
"""
|
|
812
|
+
Convert a reaction SMARTS or SMILES string into GML.
|
|
813
|
+
|
|
814
|
+
This function uses the legacy ITS/GML pipeline.
|
|
815
|
+
|
|
816
|
+
:param smart: Reaction SMARTS or SMILES string.
|
|
817
|
+
:type smart: str
|
|
818
|
+
:param core: If ``True``, export only the reaction core.
|
|
819
|
+
:type core: bool
|
|
820
|
+
:param sanitize: If ``True``, sanitize molecules during conversion.
|
|
821
|
+
:type sanitize: bool
|
|
822
|
+
:param rule_name: Rule name stored in the GML output.
|
|
823
|
+
:type rule_name: str
|
|
824
|
+
:param reindex: If ``True``, reindex graph nodes before export.
|
|
825
|
+
:type reindex: bool
|
|
826
|
+
:param explicit_hydrogen: If ``True``, include explicit hydrogens.
|
|
827
|
+
:type explicit_hydrogen: bool
|
|
828
|
+
:param useSmiles: If ``True``, treat input as reaction SMILES.
|
|
829
|
+
Otherwise, treat it as reaction SMARTS.
|
|
830
|
+
:type useSmiles: bool
|
|
831
|
+
:return: GML representation of the reaction rule.
|
|
832
|
+
:rtype: str
|
|
833
|
+
:raises ValueError: If graph construction fails.
|
|
834
|
+
"""
|
|
835
|
+
rsmi = smart if useSmiles else rsmarts_to_rsmi(smart)
|
|
836
|
+
|
|
837
|
+
reactant_graph, product_graph = rsmi_to_graph(
|
|
838
|
+
rsmi=rsmi,
|
|
839
|
+
sanitize=sanitize,
|
|
840
|
+
)
|
|
841
|
+
reactant_graph = _require_graph(reactant_graph, "reactant")
|
|
842
|
+
product_graph = _require_graph(product_graph, "product")
|
|
843
|
+
|
|
844
|
+
its_graph = ITSConstruction().ITSGraph(reactant_graph, product_graph)
|
|
845
|
+
if core:
|
|
846
|
+
its_graph = get_rc(its_graph)
|
|
847
|
+
reactant_graph, product_graph = its_decompose(its_graph)
|
|
848
|
+
|
|
849
|
+
return NXToGML().transform(
|
|
850
|
+
(reactant_graph, product_graph, its_graph),
|
|
851
|
+
reindex=reindex,
|
|
852
|
+
rule_name=rule_name,
|
|
853
|
+
explicit_hydrogen=explicit_hydrogen,
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def gml_to_smart(
|
|
858
|
+
gml: str,
|
|
859
|
+
sanitize: bool = True,
|
|
860
|
+
explicit_hydrogen: bool = False,
|
|
861
|
+
useSmiles: bool = True,
|
|
862
|
+
) -> str:
|
|
863
|
+
"""
|
|
864
|
+
Convert a GML reaction rule back to reaction SMILES or SMARTS.
|
|
865
|
+
|
|
866
|
+
:param gml: GML string.
|
|
867
|
+
:type gml: str
|
|
868
|
+
:param sanitize: If ``True``, sanitize during SMILES generation.
|
|
869
|
+
:type sanitize: bool
|
|
870
|
+
:param explicit_hydrogen: If ``True``, keep explicit hydrogens.
|
|
871
|
+
:type explicit_hydrogen: bool
|
|
872
|
+
:param useSmiles: If ``True``, return reaction SMILES. Otherwise,
|
|
873
|
+
return reaction SMARTS.
|
|
874
|
+
:type useSmiles: bool
|
|
875
|
+
:return: Reaction SMILES or SMARTS string.
|
|
876
|
+
:rtype: str
|
|
877
|
+
:raises ValueError: If conversion fails.
|
|
878
|
+
"""
|
|
879
|
+
reactant_graph, product_graph, rc_graph = GMLToNX(gml).transform()
|
|
880
|
+
rsmi = graph_to_rsmi(
|
|
881
|
+
r=reactant_graph,
|
|
882
|
+
p=product_graph,
|
|
883
|
+
its=rc_graph,
|
|
884
|
+
sanitize=sanitize,
|
|
885
|
+
explicit_hydrogen=explicit_hydrogen,
|
|
886
|
+
)
|
|
887
|
+
if rsmi is None:
|
|
888
|
+
raise ValueError("Failed to convert GML to reaction SMILES.")
|
|
889
|
+
|
|
890
|
+
if useSmiles:
|
|
891
|
+
return rsmi
|
|
892
|
+
return rsmi_to_rsmarts(rsmi)
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
def its_to_gml(
|
|
896
|
+
its: nx.Graph,
|
|
897
|
+
core: bool = True,
|
|
898
|
+
rule_name: str = "rule",
|
|
899
|
+
reindex: bool = True,
|
|
900
|
+
explicit_hydrogen: bool = False,
|
|
901
|
+
format: ITSFormat = "typesGH",
|
|
902
|
+
) -> str:
|
|
903
|
+
"""
|
|
904
|
+
Convert an ITS graph to GML format.
|
|
905
|
+
|
|
906
|
+
:param its: ITS graph.
|
|
907
|
+
:type its: nx.Graph
|
|
908
|
+
:param core: If ``True``, export the reaction-center graph.
|
|
909
|
+
:type core: bool
|
|
910
|
+
:param rule_name: Rule name stored in the GML output.
|
|
911
|
+
:type rule_name: str
|
|
912
|
+
:param reindex: If ``True``, reindex graph nodes before export.
|
|
913
|
+
:type reindex: bool
|
|
914
|
+
:param explicit_hydrogen: If ``True``, include explicit hydrogens.
|
|
915
|
+
:type explicit_hydrogen: bool
|
|
916
|
+
:param format: ITS format.
|
|
917
|
+
:type format: ITSFormat
|
|
918
|
+
:return: GML representation of the reaction.
|
|
919
|
+
:rtype: str
|
|
920
|
+
"""
|
|
921
|
+
validated_format = _validate_its_format(format)
|
|
922
|
+
exported_its = _extract_rc_graph(its, validated_format) if core else its
|
|
923
|
+
reactant_graph, product_graph = _decompose_its(exported_its, validated_format)
|
|
924
|
+
|
|
925
|
+
return NXToGML().transform(
|
|
926
|
+
(reactant_graph, product_graph, exported_its),
|
|
927
|
+
reindex=reindex,
|
|
928
|
+
rule_name=rule_name,
|
|
929
|
+
explicit_hydrogen=explicit_hydrogen,
|
|
930
|
+
)
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
def gml_to_its(gml: str) -> nx.Graph:
|
|
934
|
+
"""
|
|
935
|
+
Convert a GML reaction rule back into an ITS graph.
|
|
936
|
+
|
|
937
|
+
:param gml: GML string.
|
|
938
|
+
:type gml: str
|
|
939
|
+
:return: ITS graph.
|
|
940
|
+
:rtype: nx.Graph
|
|
941
|
+
"""
|
|
942
|
+
_, _, its_graph = GMLToNX(gml).transform()
|
|
943
|
+
return its_graph
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
def rsmi_to_rsmarts(rsmi: str) -> str:
|
|
947
|
+
"""
|
|
948
|
+
Convert mapped reaction SMILES to reaction SMARTS.
|
|
949
|
+
|
|
950
|
+
:param rsmi: Reaction SMILES input.
|
|
951
|
+
:type rsmi: str
|
|
952
|
+
:return: Reaction SMARTS string.
|
|
953
|
+
:rtype: str
|
|
954
|
+
:raises ValueError: If conversion fails.
|
|
955
|
+
"""
|
|
956
|
+
try:
|
|
957
|
+
rxn = rdChemReactions.ReactionFromSmarts(rsmi, useSmiles=True)
|
|
958
|
+
return rdChemReactions.ReactionToSmarts(rxn)
|
|
959
|
+
except Exception as exc:
|
|
960
|
+
raise ValueError(f"Failed to convert RSMI to RSMARTS: {exc}") from exc
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
def rsmarts_to_rsmi(rsmarts: str) -> str:
|
|
964
|
+
"""
|
|
965
|
+
Convert reaction SMARTS to reaction SMILES.
|
|
966
|
+
|
|
967
|
+
:param rsmarts: Reaction SMARTS input.
|
|
968
|
+
:type rsmarts: str
|
|
969
|
+
:return: Reaction SMILES string.
|
|
970
|
+
:rtype: str
|
|
971
|
+
:raises ValueError: If conversion fails.
|
|
972
|
+
"""
|
|
973
|
+
try:
|
|
974
|
+
rxn = rdChemReactions.ReactionFromSmarts(rsmarts, useSmiles=False)
|
|
975
|
+
return rdChemReactions.ReactionToSmiles(rxn)
|
|
976
|
+
except Exception as exc:
|
|
977
|
+
raise ValueError(f"Failed to convert RSMARTS to RSMI: {exc}") from exc
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
def dfs_to_smiles(dfs: str, keep_map: bool = True) -> str:
|
|
981
|
+
"""Convert DFS-style annotated SMILES to normal SMILES form.
|
|
982
|
+
|
|
983
|
+
The conversion replaces ``[]`` with ``[*]`` and converts bracketed tokens
|
|
984
|
+
followed by digits, such as ``[H]12``, into atom-mapped tokens
|
|
985
|
+
``[H:12]`` when ``keep_map=True``.
|
|
986
|
+
When ``keep_map=False``, it removes the trailing digits instead. Tokens
|
|
987
|
+
already containing ``:`` inside brackets are preserved.
|
|
988
|
+
|
|
989
|
+
:param dfs: DFS-style SMILES or reaction SMILES.
|
|
990
|
+
:type dfs: str
|
|
991
|
+
:param keep_map: Whether to keep atom maps.
|
|
992
|
+
:type keep_map: bool
|
|
993
|
+
:return: Converted SMILES string.
|
|
994
|
+
:rtype: str
|
|
995
|
+
"""
|
|
996
|
+
if not isinstance(dfs, str):
|
|
997
|
+
raise ValueError("dfs must be a string")
|
|
998
|
+
|
|
999
|
+
normalized = dfs.replace("[]", "[*]")
|
|
1000
|
+
|
|
1001
|
+
def _replace(match: re.Match[str]) -> str:
|
|
1002
|
+
inner = match.group(1)
|
|
1003
|
+
digits = match.group(2)
|
|
1004
|
+
|
|
1005
|
+
if ":" in inner:
|
|
1006
|
+
return match.group(0)
|
|
1007
|
+
if inner == "":
|
|
1008
|
+
inner = "*"
|
|
1009
|
+
|
|
1010
|
+
if keep_map:
|
|
1011
|
+
return f"[{inner}:{digits}]"
|
|
1012
|
+
return f"[{inner}]"
|
|
1013
|
+
|
|
1014
|
+
return _BRACKET_DIGIT_PATTERN.sub(_replace, normalized)
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
def smiles_to_dfs(smiles: str) -> str:
|
|
1018
|
+
"""
|
|
1019
|
+
Convert SMILES with atom maps into DFS-style notation.
|
|
1020
|
+
|
|
1021
|
+
Rules:
|
|
1022
|
+
- ``[X:123]`` becomes ``[X]123``
|
|
1023
|
+
- ``[*:3]`` becomes ``[]3``
|
|
1024
|
+
- unmapped tokens remain unchanged
|
|
1025
|
+
- remaining ``[*]`` is normalized back to ``[]``
|
|
1026
|
+
|
|
1027
|
+
:param smiles: SMILES or reaction SMILES.
|
|
1028
|
+
:type smiles: str
|
|
1029
|
+
:return: DFS-style string.
|
|
1030
|
+
:rtype: str
|
|
1031
|
+
"""
|
|
1032
|
+
if not isinstance(smiles, str):
|
|
1033
|
+
raise ValueError("smiles must be a string")
|
|
1034
|
+
|
|
1035
|
+
def _replace(match: re.Match[str]) -> str:
|
|
1036
|
+
inner = match.group(1)
|
|
1037
|
+
number = match.group(2)
|
|
1038
|
+
if inner == "*":
|
|
1039
|
+
return f"[]{number}"
|
|
1040
|
+
return f"[{inner}]{number}"
|
|
1041
|
+
|
|
1042
|
+
dfs = _BRACKET_MAP_PATTERN.sub(_replace, smiles)
|
|
1043
|
+
return dfs.replace("[*]", "[]")
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
def normalize_dfs_for_compare(dfs: str) -> str:
|
|
1047
|
+
"""
|
|
1048
|
+
Normalize DFS-style strings for comparison.
|
|
1049
|
+
|
|
1050
|
+
:param dfs: DFS-style string.
|
|
1051
|
+
:type dfs: str
|
|
1052
|
+
:return: Normalized comparison string.
|
|
1053
|
+
:rtype: str
|
|
1054
|
+
"""
|
|
1055
|
+
if not isinstance(dfs, str):
|
|
1056
|
+
raise ValueError("dfs must be a string")
|
|
1057
|
+
|
|
1058
|
+
normalized = dfs.replace("[*]", "[]")
|
|
1059
|
+
return re.sub(r"\s+", "", normalized)
|