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,207 @@
|
|
|
1
|
+
from rdkit import Chem
|
|
2
|
+
from joblib import Parallel, delayed
|
|
3
|
+
from typing import Dict, Any, List, Union, Tuple, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Neutralize:
|
|
7
|
+
"""Neutralize unbalanced charges in chemical reactions by adding
|
|
8
|
+
counter‑ions.
|
|
9
|
+
|
|
10
|
+
Provides utilities to calculate formal charges, parse reaction
|
|
11
|
+
SMILES, and adjust reactants/products with [Na+] or [Cl‑] to restore
|
|
12
|
+
neutrality.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def calculate_charge(smiles: str) -> int:
|
|
17
|
+
"""Calculate the formal charge of a molecule.
|
|
18
|
+
|
|
19
|
+
:param smiles: SMILES string of the molecule.
|
|
20
|
+
:type smiles: str
|
|
21
|
+
:return: Formal charge of the molecule (0 if invalid SMILES).
|
|
22
|
+
:rtype: int
|
|
23
|
+
"""
|
|
24
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
25
|
+
if mol is None:
|
|
26
|
+
return 0
|
|
27
|
+
return Chem.rdmolops.GetFormalCharge(mol)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def parse_reaction(reaction_smiles: str) -> Tuple[Optional[str], Optional[str]]:
|
|
31
|
+
"""Split a reaction SMILES into reactants and products.
|
|
32
|
+
|
|
33
|
+
:param reaction_smiles: Reaction SMILES in 'reactants>>products'
|
|
34
|
+
format.
|
|
35
|
+
:type reaction_smiles: str
|
|
36
|
+
:return: Tuple of (reactants, products) SMILES, or (None, None)
|
|
37
|
+
if parse fails.
|
|
38
|
+
:rtype: Tuple[Optional[str], Optional[str]]
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
reactants, products = reaction_smiles.split(">>")
|
|
42
|
+
return reactants, products
|
|
43
|
+
except ValueError:
|
|
44
|
+
return None, None
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def calculate_charge_dict(
|
|
48
|
+
reaction: Dict[str, Any], reaction_column: str
|
|
49
|
+
) -> Dict[str, Union[str, int]]:
|
|
50
|
+
"""Compute and store the total formal charge of the products in a
|
|
51
|
+
reaction dict.
|
|
52
|
+
|
|
53
|
+
:param reaction: Dictionary containing at least `reaction_column` with a reaction SMILES.
|
|
54
|
+
:type reaction: Dict[str, Any]
|
|
55
|
+
:param reaction_column: Key under which the reaction SMILES is stored.
|
|
56
|
+
:type reaction_column: str
|
|
57
|
+
:return: The same dictionary updated with:
|
|
58
|
+
- 'reactants': reactant SMILES or None
|
|
59
|
+
- 'products': product SMILES or None
|
|
60
|
+
- 'total_charge_in_products': integer sum of product charges or None
|
|
61
|
+
:rtype: Dict[str, Union[str, int]]
|
|
62
|
+
"""
|
|
63
|
+
reactants, products = Neutralize.parse_reaction(
|
|
64
|
+
reaction.get(reaction_column, "")
|
|
65
|
+
)
|
|
66
|
+
if reactants is None or products is None:
|
|
67
|
+
reaction.update(
|
|
68
|
+
{"reactants": None, "products": None, "total_charge_in_products": None}
|
|
69
|
+
)
|
|
70
|
+
else:
|
|
71
|
+
reaction["reactants"] = reactants
|
|
72
|
+
reaction["products"] = products
|
|
73
|
+
total = sum(Neutralize.calculate_charge(p) for p in products.split("."))
|
|
74
|
+
reaction["total_charge_in_products"] = total
|
|
75
|
+
return reaction
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def fix_negative_charge(
|
|
79
|
+
reaction_dict: Dict[str, Any],
|
|
80
|
+
charges_column: str = "total_charge_in_products",
|
|
81
|
+
id_column: str = "R-id",
|
|
82
|
+
reaction_column: str = "reactions",
|
|
83
|
+
) -> Dict[str, Any]:
|
|
84
|
+
"""Add [Na+] ions to neutralize negative product charge.
|
|
85
|
+
|
|
86
|
+
:param reaction_dict: Dictionary with 'reactants', 'products', and charge info.
|
|
87
|
+
:type reaction_dict: Dict[str, Any]
|
|
88
|
+
:param charges_column: Key for product total charge. Defaults to 'total_charge_in_products'.
|
|
89
|
+
:type charges_column: str
|
|
90
|
+
:param id_column: Key for reaction identifier. Defaults to 'R-id'.
|
|
91
|
+
:type id_column: str
|
|
92
|
+
:param reaction_column: Key for reaction SMILES to update. Defaults to 'reactions'.
|
|
93
|
+
:type reaction_column: str
|
|
94
|
+
:return: New dictionary with:
|
|
95
|
+
- updated `reaction_column` including added [Na+] ions
|
|
96
|
+
- 'reactants' and 'products' with ions appended
|
|
97
|
+
- charge column set to 0
|
|
98
|
+
:rtype: Dict[str, Any]
|
|
99
|
+
"""
|
|
100
|
+
num_to_add = abs(reaction_dict.get(charges_column, 0))
|
|
101
|
+
sodium = "[Na+]"
|
|
102
|
+
addition = ("." + ".".join([sodium] * num_to_add)) if num_to_add else ""
|
|
103
|
+
new_react = reaction_dict["reactants"] + addition
|
|
104
|
+
new_prod = reaction_dict["products"] + addition
|
|
105
|
+
new_reaction = f"{new_react}>>{new_prod}"
|
|
106
|
+
|
|
107
|
+
reaction_dict.update(
|
|
108
|
+
{
|
|
109
|
+
id_column: reaction_dict.get(id_column),
|
|
110
|
+
reaction_column: new_reaction,
|
|
111
|
+
"reactants": new_react,
|
|
112
|
+
"products": new_prod,
|
|
113
|
+
charges_column: 0,
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
return reaction_dict
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def fix_positive_charge(
|
|
120
|
+
reaction_dict: Dict[str, Any],
|
|
121
|
+
charges_column: str = "total_charge_in_products",
|
|
122
|
+
id_column: str = "R-id",
|
|
123
|
+
reaction_column: str = "reactions",
|
|
124
|
+
) -> Dict[str, Any]:
|
|
125
|
+
"""Add [Cl‑] ions to neutralize positive product charge.
|
|
126
|
+
|
|
127
|
+
:param reaction_dict: Dictionary with 'reactants', 'products', and charge info.
|
|
128
|
+
:type reaction_dict: Dict[str, Any]
|
|
129
|
+
:param charges_column: Key for product total charge. Defaults to 'total_charge_in_products'.
|
|
130
|
+
:type charges_column: str
|
|
131
|
+
:param id_column: Key for reaction identifier. Defaults to 'R-id'.
|
|
132
|
+
:type id_column: str
|
|
133
|
+
:param reaction_column: Key for reaction SMILES to update. Defaults to 'reactions'.
|
|
134
|
+
:type reaction_column: str
|
|
135
|
+
:return: New dictionary with:
|
|
136
|
+
- updated `reaction_column` including added [Cl‑] ions
|
|
137
|
+
- 'reactants' and 'products' with ions appended
|
|
138
|
+
- charge column set to 0
|
|
139
|
+
:rtype: Dict[str, Any]
|
|
140
|
+
"""
|
|
141
|
+
num_to_add = abs(reaction_dict.get(charges_column, 0))
|
|
142
|
+
chloride = "[Cl-]"
|
|
143
|
+
addition = ("." + ".".join([chloride] * num_to_add)) if num_to_add else ""
|
|
144
|
+
new_react = reaction_dict["reactants"] + addition
|
|
145
|
+
new_prod = reaction_dict["products"] + addition
|
|
146
|
+
new_reaction = f"{new_react}>>{new_prod}"
|
|
147
|
+
reaction_dict.update(
|
|
148
|
+
{
|
|
149
|
+
id_column: reaction_dict.get(id_column),
|
|
150
|
+
reaction_column: new_reaction,
|
|
151
|
+
"reactants": new_react,
|
|
152
|
+
"products": new_prod,
|
|
153
|
+
charges_column: 0,
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
return reaction_dict
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def fix_unbalanced_charged(
|
|
160
|
+
reaction_dict: Dict[str, Any],
|
|
161
|
+
reaction_column: str,
|
|
162
|
+
) -> Dict[str, Any]:
|
|
163
|
+
"""Detect and neutralize unbalanced product charge by adding
|
|
164
|
+
counter‑ions.
|
|
165
|
+
|
|
166
|
+
:param reaction_dict: Dictionary with raw reaction SMILES under `reaction_column`.
|
|
167
|
+
:type reaction_dict: Dict[str, Any]
|
|
168
|
+
:param reaction_column: Key for reaction SMILES in the input dict.
|
|
169
|
+
:type reaction_column: str
|
|
170
|
+
:return: Dictionary with balanced charges and updated SMILES.
|
|
171
|
+
:rtype: Dict[str, Any]
|
|
172
|
+
"""
|
|
173
|
+
rd = Neutralize.calculate_charge_dict(reaction_dict, reaction_column)
|
|
174
|
+
total = rd.get("total_charge_in_products", 0)
|
|
175
|
+
if total > 0:
|
|
176
|
+
return Neutralize.fix_positive_charge(rd)
|
|
177
|
+
if total < 0:
|
|
178
|
+
return Neutralize.fix_negative_charge(rd)
|
|
179
|
+
return rd
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def parallel_fix_unbalanced_charge(
|
|
183
|
+
cls,
|
|
184
|
+
reaction_dicts: List[Dict[str, Any]],
|
|
185
|
+
reaction_column: str,
|
|
186
|
+
n_jobs: int = 4,
|
|
187
|
+
) -> List[Dict[str, Any]]:
|
|
188
|
+
"""Neutralize charges in multiple reaction dictionaries in parallel.
|
|
189
|
+
|
|
190
|
+
:param reaction_dicts: List of reaction dictionaries to process.
|
|
191
|
+
:type reaction_dicts: List[Dict[str, Any]]
|
|
192
|
+
:param reaction_column: Key for reaction SMILES in each dict.
|
|
193
|
+
:type reaction_column: str
|
|
194
|
+
:param n_jobs: Number of parallel jobs (use -1 for all cores).
|
|
195
|
+
Defaults to 4.
|
|
196
|
+
:type n_jobs: int
|
|
197
|
+
:return: List of dictionaries with balanced charges and updated
|
|
198
|
+
SMILES.
|
|
199
|
+
:rtype: List[Dict[str, Any]]
|
|
200
|
+
"""
|
|
201
|
+
return Parallel(n_jobs=n_jobs)(
|
|
202
|
+
delayed(cls.fix_unbalanced_charged)(
|
|
203
|
+
d,
|
|
204
|
+
reaction_column,
|
|
205
|
+
)
|
|
206
|
+
for d in reaction_dicts
|
|
207
|
+
)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from rdkit import Chem
|
|
2
|
+
from rdkit.Chem import SanitizeFlags
|
|
3
|
+
import re
|
|
4
|
+
from typing import Tuple, List, Optional, Dict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def clean_wc(
|
|
8
|
+
rsmi: str, invert: bool = False, max_frag: bool = False, wild_card: bool = True
|
|
9
|
+
) -> str:
|
|
10
|
+
"""Clean wildcard-containing fragments from one side of a reaction SMILES,
|
|
11
|
+
optionally selecting the largest remaining fragment.
|
|
12
|
+
|
|
13
|
+
:param rsmi: Reaction SMILES string in the form 'R>>P'.
|
|
14
|
+
:type rsmi: str
|
|
15
|
+
:param invert: If True, process the reactant side; otherwise the product side.
|
|
16
|
+
:type invert: bool
|
|
17
|
+
:param max_frag: If True, force fragment selection (implies wild_card=True).
|
|
18
|
+
:type max_frag: bool
|
|
19
|
+
:param wild_card: If True, remove fragments containing '*' before selection.
|
|
20
|
+
:type wild_card: bool
|
|
21
|
+
:return: The processed reaction SMILES.
|
|
22
|
+
:rtype: str
|
|
23
|
+
:raises ValueError: If input does not split into reactant and product.
|
|
24
|
+
|
|
25
|
+
.. rubric:: Example
|
|
26
|
+
|
|
27
|
+
>>> clean_wc('A.B>>C.*', invert=False, wild_card=True)
|
|
28
|
+
'A.B>>C'
|
|
29
|
+
>>> clean_wc('A.B>>C.D', invert=False, max_frag=True)
|
|
30
|
+
'A.B>>C'
|
|
31
|
+
"""
|
|
32
|
+
# Ensure max_frag implies wild_card
|
|
33
|
+
if max_frag:
|
|
34
|
+
wild_card = True
|
|
35
|
+
|
|
36
|
+
# Split into reactant and product
|
|
37
|
+
parts = rsmi.split(">>")
|
|
38
|
+
if len(parts) != 2:
|
|
39
|
+
raise ValueError("Reaction SMILES must contain exactly one '>>'.")
|
|
40
|
+
react, prod = parts
|
|
41
|
+
|
|
42
|
+
# Select side to process
|
|
43
|
+
side = react if invert else prod
|
|
44
|
+
|
|
45
|
+
processed = side
|
|
46
|
+
if wild_card:
|
|
47
|
+
frags = side.split(".")
|
|
48
|
+
# Filter out fragments containing wildcards
|
|
49
|
+
filtered = [frag for frag in frags if "*" not in frag]
|
|
50
|
+
if len(filtered) > 1:
|
|
51
|
+
# select the longest fragment
|
|
52
|
+
processed = max(filtered, key=len)
|
|
53
|
+
elif len(filtered) == 1:
|
|
54
|
+
processed = filtered[0]
|
|
55
|
+
# if no filtered fragments or single fragment, keep original side
|
|
56
|
+
|
|
57
|
+
# Reconstruct and return
|
|
58
|
+
if invert:
|
|
59
|
+
return f"{processed}>>{prod}"
|
|
60
|
+
return f"{react}>>{processed}"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class RadicalWildcardAdder:
|
|
64
|
+
"""A utility for adding wildcard dummy atoms ([*]) to radical centers in
|
|
65
|
+
reaction SMILES, with unique incremental atom-map indices and correct
|
|
66
|
+
propagation into products.
|
|
67
|
+
|
|
68
|
+
Each reactive radical atom in the reactant block is identified by its unpaired electron count,
|
|
69
|
+
assigned one or more wildcard map indices, and recorded. The same wildcard(s) are then appended
|
|
70
|
+
to the corresponding atom(s) in the product block, ensuring consistent mapping.
|
|
71
|
+
|
|
72
|
+
:param start_map: If provided, this integer will be the first atom-map index
|
|
73
|
+
used for wildcard dummy atoms; subsequent radicals get incremented indices.
|
|
74
|
+
If None, the next unused index is auto-determined from the input SMILES.
|
|
75
|
+
:type start_map: Optional[int]
|
|
76
|
+
|
|
77
|
+
.. rubric:: Example
|
|
78
|
+
|
|
79
|
+
>>> adder = RadicalWildcardAdder(start_map=8)
|
|
80
|
+
>>> rxn = "[C:2][OH:4].[O:6][H:7]>>[C:2][O:6].[OH:4][H:7]"
|
|
81
|
+
>>> print(adder.transform(rxn))
|
|
82
|
+
[C:2]([OH:4])[*:8].[O:6]([H:7])[*:9]>>[C:2]([O:6][*:9])[*:8].[OH:4][H:7]
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self, start_map: Optional[int] = None) -> None:
|
|
86
|
+
"""Initialize the adder with an optional starting map index.
|
|
87
|
+
|
|
88
|
+
:param start_map: Starting atom-map index for wildcards or None
|
|
89
|
+
to auto-pick.
|
|
90
|
+
:type start_map: Optional[int]
|
|
91
|
+
"""
|
|
92
|
+
self.start_map = start_map
|
|
93
|
+
|
|
94
|
+
def __repr__(self) -> str:
|
|
95
|
+
"""Official representation."""
|
|
96
|
+
return f"<RadicalWildcardAdder(start_map={self.start_map})>"
|
|
97
|
+
|
|
98
|
+
def __str__(self) -> str:
|
|
99
|
+
"""User-friendly description."""
|
|
100
|
+
m = self.start_map if self.start_map is not None else "auto"
|
|
101
|
+
return f"RadicalWildcardAdder(start_map={m})"
|
|
102
|
+
|
|
103
|
+
def transform(self, rxn_smiles: str) -> str:
|
|
104
|
+
"""Append wildcard dummy atoms to each radical center in the reactant
|
|
105
|
+
block and propagate the same wildcards to the matching atoms in the
|
|
106
|
+
product block.
|
|
107
|
+
|
|
108
|
+
:param rxn_smiles: Reaction SMILES string, two-component or
|
|
109
|
+
three-component.
|
|
110
|
+
:type rxn_smiles: str
|
|
111
|
+
:return: Modified reaction SMILES with consistent wildcard
|
|
112
|
+
attachments.
|
|
113
|
+
:rtype: str
|
|
114
|
+
:raises ValueError: If the SMILES is not valid or fragments fail
|
|
115
|
+
to parse.
|
|
116
|
+
"""
|
|
117
|
+
# Split into reactants > agents? > products
|
|
118
|
+
react_blk, agents_blk, prod_blk = self._split_reaction(rxn_smiles)
|
|
119
|
+
|
|
120
|
+
# Determine first wildcard map index
|
|
121
|
+
existing = [int(n) for n in re.findall(r":(\d+)", rxn_smiles)]
|
|
122
|
+
next_map = (
|
|
123
|
+
self.start_map
|
|
124
|
+
if self.start_map is not None
|
|
125
|
+
else max(existing, default=0) + 1
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Record mapping: original atom-map -> list of wildcard_maps
|
|
129
|
+
wildcard_map_for: Dict[int, List[int]] = {}
|
|
130
|
+
|
|
131
|
+
# Build sanitizeOps mask (skip H-adjustment)
|
|
132
|
+
keep_ops = SanitizeFlags.SANITIZE_ADJUSTHS
|
|
133
|
+
|
|
134
|
+
# Process one block (helper)
|
|
135
|
+
def _process(frags: List[str], propagate: bool) -> List[str]:
|
|
136
|
+
nonlocal next_map
|
|
137
|
+
out = []
|
|
138
|
+
for smi in frags:
|
|
139
|
+
if not smi:
|
|
140
|
+
continue
|
|
141
|
+
# Load unsanitized then re-sanitize to preserve explicit H
|
|
142
|
+
# mol = Chem.MolFromSmiles(smi, sanitize=False)
|
|
143
|
+
# if mol is None:
|
|
144
|
+
# raise ValueError(f"Cannot parse SMILES fragment: {smi}")
|
|
145
|
+
# Chem.SanitizeMol(mol, sanitizeOps=keep_ops)
|
|
146
|
+
mol = Chem.MolFromSmiles(smi, sanitize=False)
|
|
147
|
+
Chem.SanitizeMol(mol)
|
|
148
|
+
rw = Chem.RWMol(mol)
|
|
149
|
+
|
|
150
|
+
atoms = list(rw.GetAtoms())
|
|
151
|
+
changed = False
|
|
152
|
+
|
|
153
|
+
for atom in atoms:
|
|
154
|
+
rad = atom.GetNumRadicalElectrons()
|
|
155
|
+
orig_map = atom.GetAtomMapNum()
|
|
156
|
+
if rad > 0:
|
|
157
|
+
# Initialize list for this orig_map
|
|
158
|
+
if propagate and orig_map not in wildcard_map_for:
|
|
159
|
+
wildcard_map_for[orig_map] = []
|
|
160
|
+
# For each unpaired electron, attach a wildcard
|
|
161
|
+
for _ in range(rad):
|
|
162
|
+
if propagate:
|
|
163
|
+
wm = next_map
|
|
164
|
+
wildcard_map_for[orig_map].append(wm)
|
|
165
|
+
next_map += 1
|
|
166
|
+
else:
|
|
167
|
+
# in products, use already-recorded wm sequentially
|
|
168
|
+
wm_list = wildcard_map_for.get(orig_map, [])
|
|
169
|
+
if not wm_list:
|
|
170
|
+
continue
|
|
171
|
+
wm = wm_list.pop(0)
|
|
172
|
+
# add dummy wildcard
|
|
173
|
+
dummy = Chem.Atom(0)
|
|
174
|
+
dummy.SetAtomMapNum(wm)
|
|
175
|
+
dummy.SetNoImplicit(True)
|
|
176
|
+
rw.AddAtom(dummy)
|
|
177
|
+
rw.AddBond(
|
|
178
|
+
atom.GetIdx(),
|
|
179
|
+
rw.GetNumAtoms() - 1,
|
|
180
|
+
Chem.BondType.SINGLE,
|
|
181
|
+
)
|
|
182
|
+
changed = True
|
|
183
|
+
|
|
184
|
+
if changed:
|
|
185
|
+
Chem.SanitizeMol(rw.GetMol(), sanitizeOps=keep_ops)
|
|
186
|
+
|
|
187
|
+
out.append(
|
|
188
|
+
Chem.MolToSmiles(
|
|
189
|
+
rw.GetMol(), isomericSmiles=True, allHsExplicit=True
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
return out
|
|
193
|
+
|
|
194
|
+
react_frags = react_blk.split(".") if react_blk else []
|
|
195
|
+
new_reacts = _process(react_frags, propagate=True)
|
|
196
|
+
|
|
197
|
+
prod_frags = prod_blk.split(".") if prod_blk else []
|
|
198
|
+
new_prods = _process(prod_frags, propagate=False)
|
|
199
|
+
|
|
200
|
+
react_str = ".".join(new_reacts)
|
|
201
|
+
prod_str = ".".join(new_prods)
|
|
202
|
+
if agents_blk is None:
|
|
203
|
+
return f"{react_str}>>{prod_str}"
|
|
204
|
+
return f"{react_str}>{agents_blk}>{prod_str}"
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def _split_reaction(rxn: str) -> Tuple[str, Optional[str], str]:
|
|
208
|
+
"""Split a reaction SMILES into reactants, agents (optional), and
|
|
209
|
+
products.
|
|
210
|
+
|
|
211
|
+
:param rxn: The reaction SMILES string.
|
|
212
|
+
:type rxn: str
|
|
213
|
+
:return: Tuple of (reactants_block, agents_block or None,
|
|
214
|
+
products_block).
|
|
215
|
+
:rtype: Tuple[str, Optional[str], str]
|
|
216
|
+
:raises ValueError: If the SMILES does not contain 2 or 3 '>'
|
|
217
|
+
symbols.
|
|
218
|
+
"""
|
|
219
|
+
parts = rxn.split(">")
|
|
220
|
+
if len(parts) == 2:
|
|
221
|
+
return parts[0], None, parts[1]
|
|
222
|
+
if len(parts) == 3:
|
|
223
|
+
return parts[0], parts[1], parts[2]
|
|
224
|
+
raise ValueError("Reaction SMILES must contain 2 or 3 '>' symbols")
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from typing import List, Optional, Tuple
|
|
2
|
+
from rdkit import Chem
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Standardize:
|
|
6
|
+
"""Utilities to normalize and filter reaction and molecule SMILES.
|
|
7
|
+
|
|
8
|
+
This class provides methods to remove atom‑mapping, filter invalid molecules,
|
|
9
|
+
canonicalize reaction SMILES, and a full pipeline via `fit`.
|
|
10
|
+
|
|
11
|
+
:ivar None: Stateless helper class.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
"""Initialize the Standardize helper.
|
|
16
|
+
|
|
17
|
+
No instance attributes are set.
|
|
18
|
+
"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def remove_atom_mapping(reaction_smiles: str, symbol: str = ">>") -> str:
|
|
23
|
+
"""Remove atom‑map numbers from a reaction SMILES string.
|
|
24
|
+
|
|
25
|
+
:param reaction_smiles: Reaction SMILES with atom maps, e.g.
|
|
26
|
+
'C[CH3:1]>>C'.
|
|
27
|
+
:type reaction_smiles: str
|
|
28
|
+
:param symbol: Separator between reactants and products.
|
|
29
|
+
Defaults to '>>'.
|
|
30
|
+
:type symbol: str
|
|
31
|
+
:return: Reaction SMILES without atom‑mapping annotations.
|
|
32
|
+
:rtype: str
|
|
33
|
+
:raises ValueError: If the input format is invalid or contains
|
|
34
|
+
invalid SMILES.
|
|
35
|
+
"""
|
|
36
|
+
parts = reaction_smiles.split(symbol)
|
|
37
|
+
if len(parts) != 2:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
"Invalid reaction SMILES format. Expected 'reactants>>products'."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def clean_smiles(smi: str) -> str:
|
|
43
|
+
mol = Chem.MolFromSmiles(smi)
|
|
44
|
+
if mol is None:
|
|
45
|
+
raise ValueError(f"Invalid SMILES string: {smi}")
|
|
46
|
+
for atom in mol.GetAtoms():
|
|
47
|
+
atom.SetAtomMapNum(0)
|
|
48
|
+
return Chem.MolToSmiles(mol, canonical=True)
|
|
49
|
+
|
|
50
|
+
react, prod = map(clean_smiles, parts)
|
|
51
|
+
return f"{react}{symbol}{prod}"
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def filter_valid_molecules(smiles_list: List[str]) -> List[Chem.Mol]:
|
|
55
|
+
"""Filter and sanitize a list of SMILES, returning only valid Mol
|
|
56
|
+
objects.
|
|
57
|
+
|
|
58
|
+
:param smiles_list: List of SMILES strings to validate.
|
|
59
|
+
:type smiles_list: List[str]
|
|
60
|
+
:return: List of sanitized RDKit Mol objects.
|
|
61
|
+
:rtype: List[rdkit.Chem.Mol]
|
|
62
|
+
"""
|
|
63
|
+
valid: List[Chem.Mol] = []
|
|
64
|
+
for smi in smiles_list:
|
|
65
|
+
mol = Chem.MolFromSmiles(smi, sanitize=False)
|
|
66
|
+
if mol:
|
|
67
|
+
try:
|
|
68
|
+
Chem.SanitizeMol(mol)
|
|
69
|
+
valid.append(mol)
|
|
70
|
+
except Exception:
|
|
71
|
+
continue
|
|
72
|
+
return valid
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def _parse_molecule_fragments(
|
|
76
|
+
smiles_list: List[str],
|
|
77
|
+
) -> Tuple[List[Chem.Mol], bool]:
|
|
78
|
+
"""Parse and sanitize SMILES fragments.
|
|
79
|
+
|
|
80
|
+
:param smiles_list: List of SMILES strings to validate.
|
|
81
|
+
:type smiles_list: List[str]
|
|
82
|
+
:return: Tuple of valid molecules and whether any fragment was invalid.
|
|
83
|
+
:rtype: Tuple[List[rdkit.Chem.Mol], bool]
|
|
84
|
+
"""
|
|
85
|
+
valid: List[Chem.Mol] = []
|
|
86
|
+
had_invalid = False
|
|
87
|
+
for smi in smiles_list:
|
|
88
|
+
mol = Chem.MolFromSmiles(smi, sanitize=False)
|
|
89
|
+
if mol:
|
|
90
|
+
try:
|
|
91
|
+
Chem.SanitizeMol(mol)
|
|
92
|
+
valid.append(mol)
|
|
93
|
+
except Exception:
|
|
94
|
+
had_invalid = True
|
|
95
|
+
else:
|
|
96
|
+
had_invalid = True
|
|
97
|
+
return valid, had_invalid
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def standardize_rsmi(
|
|
101
|
+
rsmi: str, stereo: bool = False, remove_invalid: bool = True
|
|
102
|
+
) -> Optional[str]:
|
|
103
|
+
"""Normalize a reaction SMILES: validate molecules, sort fragments, optionally keep stereo.
|
|
104
|
+
|
|
105
|
+
:param rsmi: Reaction SMILES in 'reactants>>products' format.
|
|
106
|
+
:type rsmi: str
|
|
107
|
+
:param stereo: If True, include stereochemistry in the output. Defaults to False.
|
|
108
|
+
:type stereo: bool
|
|
109
|
+
:param remove_invalid: If True, drop invalid fragments and standardize
|
|
110
|
+
remaining molecules. If False, return None when any invalid fragment
|
|
111
|
+
exists. Defaults to True.
|
|
112
|
+
:type remove_invalid: bool
|
|
113
|
+
:return: Standardized reaction SMILES or None if no valid molecules remain.
|
|
114
|
+
:rtype: Optional[str]
|
|
115
|
+
:raises ValueError: If the input format is invalid.
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
react_str, prod_str = rsmi.split(">>")
|
|
119
|
+
except ValueError:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
"Invalid reaction SMILES format. Expected 'reactants>>products'."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
react_mols, react_invalid = Standardize._parse_molecule_fragments(
|
|
125
|
+
react_str.split(".")
|
|
126
|
+
)
|
|
127
|
+
prod_mols, prod_invalid = Standardize._parse_molecule_fragments(
|
|
128
|
+
prod_str.split(".")
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if not remove_invalid and (react_invalid or prod_invalid):
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
if not react_mols or not prod_mols:
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
sorted_react = ".".join(
|
|
138
|
+
sorted(Chem.MolToSmiles(m, isomericSmiles=stereo) for m in react_mols)
|
|
139
|
+
)
|
|
140
|
+
sorted_prod = ".".join(
|
|
141
|
+
sorted(Chem.MolToSmiles(m, isomericSmiles=stereo) for m in prod_mols)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return f"{sorted_react}>>{sorted_prod}"
|
|
145
|
+
|
|
146
|
+
def fit(
|
|
147
|
+
self,
|
|
148
|
+
rsmi: str,
|
|
149
|
+
remove_aam: bool = True,
|
|
150
|
+
ignore_stereo: bool = True,
|
|
151
|
+
remove_invalid: bool = True,
|
|
152
|
+
) -> Optional[str]:
|
|
153
|
+
"""Full standardization pipeline: strip atom‑mapping, normalize SMILES, fix hydrogen notation.
|
|
154
|
+
|
|
155
|
+
:param rsmi: Reaction SMILES to process.
|
|
156
|
+
:type rsmi: str
|
|
157
|
+
:param remove_aam: If True, remove atom‑mapping annotations. Defaults to True.
|
|
158
|
+
:type remove_aam: bool
|
|
159
|
+
:param ignore_stereo: If True, drop stereochemistry. Defaults to True.
|
|
160
|
+
:type ignore_stereo: bool
|
|
161
|
+
:param remove_invalid: If True, drop invalid fragments and standardize
|
|
162
|
+
remaining molecules. If False, return None when any invalid fragment
|
|
163
|
+
exists. Defaults to True.
|
|
164
|
+
:type remove_invalid: bool
|
|
165
|
+
:return: The standardized reaction SMILES, or None if standardization fails.
|
|
166
|
+
:rtype: Optional[str]
|
|
167
|
+
"""
|
|
168
|
+
std = self.standardize_rsmi(
|
|
169
|
+
rsmi, stereo=not ignore_stereo, remove_invalid=remove_invalid
|
|
170
|
+
)
|
|
171
|
+
if std is None:
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
if remove_aam:
|
|
175
|
+
std = self.remove_atom_mapping(std)
|
|
176
|
+
|
|
177
|
+
# Format any double‑hydrogen notation
|
|
178
|
+
return std.replace("[HH]", "[H][H]")
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def categorize_reactions(
|
|
182
|
+
reactions: List[str], target_reaction: str
|
|
183
|
+
) -> Tuple[List[str], List[str]]:
|
|
184
|
+
"""Partition reactions into those matching a target and those not.
|
|
185
|
+
|
|
186
|
+
:param reactions: List of reaction SMILES to categorize.
|
|
187
|
+
:type reactions: List[str]
|
|
188
|
+
:param target_reaction: Benchmark reaction SMILES for comparison.
|
|
189
|
+
:type target_reaction: str
|
|
190
|
+
:return: Tuple of (matches, non_matches):
|
|
191
|
+
- matches: reactions equal to standardized target
|
|
192
|
+
- non_matches: all others
|
|
193
|
+
:rtype: Tuple[List[str], List[str]]
|
|
194
|
+
"""
|
|
195
|
+
tgt = Standardize.standardize_rsmi(target_reaction, stereo=False)
|
|
196
|
+
matches: List[str] = []
|
|
197
|
+
non_matches: List[str] = []
|
|
198
|
+
for rxn in reactions:
|
|
199
|
+
if rxn == tgt:
|
|
200
|
+
matches.append(rxn)
|
|
201
|
+
else:
|
|
202
|
+
non_matches.append(rxn)
|
|
203
|
+
return matches, non_matches
|