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,160 @@
|
|
|
1
|
+
"""Reference-blinded conversion of mapped reaction SMILES.
|
|
2
|
+
|
|
3
|
+
Mapped atom identifiers are needed to recover the held-out reference mapping,
|
|
4
|
+
but using the same identifier order on both endpoints leaks that mapping into
|
|
5
|
+
the search traversal. This module applies independent deterministic endpoint
|
|
6
|
+
permutations and returns the reference correspondence separately.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from rdkit import Chem
|
|
16
|
+
except ImportError: # pragma: no cover - exercised only without RDKit
|
|
17
|
+
Chem = None
|
|
18
|
+
|
|
19
|
+
from synkit.IO import rsmi_to_graph
|
|
20
|
+
|
|
21
|
+
from ..graph.labeled_graph import LabeledGraph
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class BlindedReactionProblem:
|
|
26
|
+
"""Graph pair and held-out mapping in blinded coordinates."""
|
|
27
|
+
|
|
28
|
+
lgp: tuple[LabeledGraph, LabeledGraph]
|
|
29
|
+
reference_mapping: tuple[int, ...]
|
|
30
|
+
reactant_atom_maps: tuple[int, ...]
|
|
31
|
+
product_atom_maps: tuple[int, ...]
|
|
32
|
+
reaction_sha256: str
|
|
33
|
+
heavy_only: bool
|
|
34
|
+
blind_seed: str
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _blind_order(nodes, *, blind_seed: str, side: str) -> tuple[int, ...]:
|
|
38
|
+
"""Return an endpoint order independent of reference alignment."""
|
|
39
|
+
|
|
40
|
+
def key(node):
|
|
41
|
+
payload = f"{blind_seed}\0{side}\0{int(node)}".encode("utf-8")
|
|
42
|
+
return hashlib.sha256(payload).digest()
|
|
43
|
+
|
|
44
|
+
return tuple(sorted((int(node) for node in nodes), key=key))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _fold_explicit_hydrogens(graph) -> None:
|
|
48
|
+
for node, attributes in graph.nodes(data=True):
|
|
49
|
+
if attributes.get("element") == "H":
|
|
50
|
+
continue
|
|
51
|
+
explicit = sum(
|
|
52
|
+
graph.nodes[neighbour].get("element") == "H"
|
|
53
|
+
for neighbour in graph.neighbors(node)
|
|
54
|
+
)
|
|
55
|
+
attributes["hcount"] = int(attributes.get("hcount", 0) or 0) + int(explicit)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _selected_graph(graph, *, heavy_only: bool):
|
|
59
|
+
if not heavy_only:
|
|
60
|
+
return graph.copy()
|
|
61
|
+
_fold_explicit_hydrogens(graph)
|
|
62
|
+
return graph.subgraph(
|
|
63
|
+
node
|
|
64
|
+
for node, attributes in graph.nodes(data=True)
|
|
65
|
+
if attributes.get("element") != "H"
|
|
66
|
+
).copy()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _as_labeled_graph(graph, ordered_nodes) -> LabeledGraph:
|
|
70
|
+
positions = {node: index for index, node in enumerate(ordered_nodes)}
|
|
71
|
+
adjacency = {index: {} for index in range(len(ordered_nodes))}
|
|
72
|
+
for begin, end, attributes in graph.edges(data=True):
|
|
73
|
+
weight = float(attributes.get("order", 1.0))
|
|
74
|
+
adjacency[positions[begin]][positions[end]] = weight
|
|
75
|
+
adjacency[positions[end]][positions[begin]] = weight
|
|
76
|
+
periodic_table = Chem.GetPeriodicTable()
|
|
77
|
+
elements = [
|
|
78
|
+
int(periodic_table.GetAtomicNumber(graph.nodes[node]["element"]))
|
|
79
|
+
for node in ordered_nodes
|
|
80
|
+
]
|
|
81
|
+
labeled = LabeledGraph(adjacency, elements)
|
|
82
|
+
labeled.set_prop("atomic numbers", elements)
|
|
83
|
+
labeled.set_prop(
|
|
84
|
+
"hcounts",
|
|
85
|
+
[int(graph.nodes[node].get("hcount", 0) or 0) for node in ordered_nodes],
|
|
86
|
+
)
|
|
87
|
+
labeled.set_prop(
|
|
88
|
+
"charges",
|
|
89
|
+
[int(graph.nodes[node].get("charge", 0) or 0) for node in ordered_nodes],
|
|
90
|
+
)
|
|
91
|
+
return labeled
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def blinded_mapped_reaction_problem(
|
|
95
|
+
reaction: str,
|
|
96
|
+
*,
|
|
97
|
+
heavy_only: bool = True,
|
|
98
|
+
blind_seed: str = "synister-global-v1",
|
|
99
|
+
) -> BlindedReactionProblem:
|
|
100
|
+
"""Convert mapped reaction SMILES without exposing reference alignment.
|
|
101
|
+
|
|
102
|
+
The two endpoint orders are deterministic but use different salts. The
|
|
103
|
+
held-out mapping is returned only as a separate tuple, suitable for
|
|
104
|
+
evaluation after a global search.
|
|
105
|
+
"""
|
|
106
|
+
if not isinstance(reaction, str) or not reaction.strip():
|
|
107
|
+
raise ValueError("reaction must be a non-empty mapped reaction SMILES")
|
|
108
|
+
if Chem is None:
|
|
109
|
+
raise ImportError("RDKit is required for blinded reaction conversion")
|
|
110
|
+
if not isinstance(heavy_only, bool):
|
|
111
|
+
raise TypeError("heavy_only must be boolean")
|
|
112
|
+
if not isinstance(blind_seed, str) or not blind_seed:
|
|
113
|
+
raise ValueError("blind_seed must be a non-empty string")
|
|
114
|
+
|
|
115
|
+
reactant, product = rsmi_to_graph(
|
|
116
|
+
reaction,
|
|
117
|
+
drop_non_aam=True,
|
|
118
|
+
use_index_as_atom_map=True,
|
|
119
|
+
include_stereo_descriptors=False,
|
|
120
|
+
)
|
|
121
|
+
if reactant is None or product is None:
|
|
122
|
+
raise ValueError("SynKit could not convert reaction SMILES")
|
|
123
|
+
endpoints = tuple(
|
|
124
|
+
_selected_graph(graph, heavy_only=heavy_only) for graph in (reactant, product)
|
|
125
|
+
)
|
|
126
|
+
for side, graph in zip(("reactant", "product"), endpoints):
|
|
127
|
+
for node, attributes in graph.nodes(data=True):
|
|
128
|
+
if int(attributes.get("atom_map", 0) or 0) != int(node):
|
|
129
|
+
raise ValueError(f"{side} graph is not atom-map indexed")
|
|
130
|
+
|
|
131
|
+
reactant_maps = set(endpoints[0])
|
|
132
|
+
product_maps = set(endpoints[1])
|
|
133
|
+
if reactant_maps != product_maps:
|
|
134
|
+
raise ValueError("mapped endpoint atom inventories differ")
|
|
135
|
+
if sorted(endpoints[0].nodes[node]["element"] for node in reactant_maps) != sorted(
|
|
136
|
+
endpoints[1].nodes[node]["element"] for node in product_maps
|
|
137
|
+
):
|
|
138
|
+
raise ValueError("mapped endpoint element inventories differ")
|
|
139
|
+
|
|
140
|
+
reactant_order = _blind_order(reactant_maps, blind_seed=blind_seed, side="reactant")
|
|
141
|
+
product_order = _blind_order(product_maps, blind_seed=blind_seed, side="product")
|
|
142
|
+
product_positions = {
|
|
143
|
+
atom_map: position for position, atom_map in enumerate(product_order)
|
|
144
|
+
}
|
|
145
|
+
reference = tuple(product_positions[atom_map] for atom_map in reactant_order)
|
|
146
|
+
return BlindedReactionProblem(
|
|
147
|
+
lgp=(
|
|
148
|
+
_as_labeled_graph(endpoints[0], reactant_order),
|
|
149
|
+
_as_labeled_graph(endpoints[1], product_order),
|
|
150
|
+
),
|
|
151
|
+
reference_mapping=reference,
|
|
152
|
+
reactant_atom_maps=reactant_order,
|
|
153
|
+
product_atom_maps=product_order,
|
|
154
|
+
reaction_sha256=hashlib.sha256(reaction.encode("utf-8")).hexdigest(),
|
|
155
|
+
heavy_only=heavy_only,
|
|
156
|
+
blind_seed=blind_seed,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
__all__ = ["BlindedReactionProblem", "blinded_mapped_reaction_problem"]
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Symmetry-distinct deduplication of atom-to-atom mappings via canonical ITS hashing.
|
|
3
|
+
|
|
4
|
+
The SLAP search can return several minimum-cost mappings that describe the *same*
|
|
5
|
+
reaction once symmetry-equivalent atoms are relabeled (for example, two mappings
|
|
6
|
+
that differ only by swapping two equivalent hydrogens or two equivalent ring
|
|
7
|
+
positions). Such mappings are isomorphic copies of a single optimum and should be
|
|
8
|
+
collapsed to one representative.
|
|
9
|
+
|
|
10
|
+
The canonical way to test "same reaction up to atom relabeling" is to build the
|
|
11
|
+
imaginary transition state (ITS) graph -- the union of the reactant and product
|
|
12
|
+
graphs with each atom identified through the mapping and each edge labeled by its
|
|
13
|
+
(reactant order, product order) pair -- and compare ITS graphs up to isomorphism.
|
|
14
|
+
Two mappings are isomorphic copies if and only if their ITS graphs are isomorphic
|
|
15
|
+
as node- and edge-labeled graphs. We obtain a canonical hash of each ITS graph and
|
|
16
|
+
deduplicate by that hash.
|
|
17
|
+
|
|
18
|
+
This module uses SynKit ITS construction and its Weisfeiler-Lehman graph hash
|
|
19
|
+
(:class:`synkit.Graph.Feature.wl_hash.WLHash`, a thin wrapper over
|
|
20
|
+
``networkx.weisfeiler_lehman_graph_hash``) for canonical hashing. The WL hash is
|
|
21
|
+
isomorphism-invariant by construction -- it depends only on iterative aggregation
|
|
22
|
+
of node/edge attributes over graph topology, not on input atom ordering -- so two
|
|
23
|
+
mappings that are isomorphic copies always receive the same hash.
|
|
24
|
+
|
|
25
|
+
Note that mappings which are genuinely *different* reaction centres but happen to
|
|
26
|
+
tie on chemical distance (e.g. an ambiguity over which of two equivalent leaving
|
|
27
|
+
groups departs) have *distinct* ITS graphs and are therefore preserved -- they are
|
|
28
|
+
separate optima, not isomorphic duplicates.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from synkit.Graph.Feature.wl_hash import WLHash
|
|
32
|
+
from synkit.IO import rsmi_to_its
|
|
33
|
+
|
|
34
|
+
_hasher = None
|
|
35
|
+
|
|
36
|
+
# Node attributes seeding the WL colour refinement. ``atom_map`` is deliberately
|
|
37
|
+
# excluded so that two mappings differing only by a relabeling of equivalent
|
|
38
|
+
# atoms collapse to the same hash. The bond change is carried by the edge
|
|
39
|
+
# ``order`` attribute, an ``(reactant_order, product_order)`` tuple, which is fed
|
|
40
|
+
# into the refinement so that distinct reaction centres receive distinct hashes.
|
|
41
|
+
_WL_NODE_ATTRS = ["element", "aromatic", "charge", "hcount", "lone_pairs"]
|
|
42
|
+
_WL_EDGE_ATTRS = ["order"]
|
|
43
|
+
_WL_ITERATIONS = 5
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_hasher():
|
|
47
|
+
global _hasher
|
|
48
|
+
if _hasher is None:
|
|
49
|
+
_hasher = WLHash(
|
|
50
|
+
node=_WL_NODE_ATTRS,
|
|
51
|
+
edge=_WL_EDGE_ATTRS,
|
|
52
|
+
iterations=_WL_ITERATIONS,
|
|
53
|
+
)
|
|
54
|
+
return _hasher
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def its_canonical_hash(mapped_rxn_smiles):
|
|
58
|
+
"""Return a canonical hash of the ITS graph of a mapped reaction SMILES.
|
|
59
|
+
|
|
60
|
+
:param mapped_rxn_smiles: Reaction SMILES annotated with atom map numbers (``"A>>B"``).
|
|
61
|
+
:type mapped_rxn_smiles: str
|
|
62
|
+
|
|
63
|
+
:return: A hash that is identical for mappings describing the same reaction up to
|
|
64
|
+
relabeling of symmetry-equivalent atoms, or ``None`` if the ITS graph
|
|
65
|
+
could not be constructed.
|
|
66
|
+
:rtype: str or None
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
its = rsmi_to_its(mapped_rxn_smiles)
|
|
70
|
+
return _get_hasher().weisfeiler_lehman_graph_hash(its)
|
|
71
|
+
except Exception:
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _standard_order(edge_attrs):
|
|
76
|
+
if "standard_order" in edge_attrs:
|
|
77
|
+
return float(edge_attrs["standard_order"])
|
|
78
|
+
order = edge_attrs.get("order")
|
|
79
|
+
if isinstance(order, (tuple, list)) and len(order) == 2:
|
|
80
|
+
return float(order[0]) - float(order[1])
|
|
81
|
+
return 0.0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def electron_balance_imbalances(its):
|
|
85
|
+
"""
|
|
86
|
+
Return per-node signed bond-order imbalance for an ITS graph.
|
|
87
|
+
|
|
88
|
+
The ITS edge ``standard_order`` is reactant bond order minus product bond
|
|
89
|
+
order. Summing it over incident changed edges gives a compact local
|
|
90
|
+
bookkeeping signal: positive values indicate net bond order lost at that
|
|
91
|
+
atom, negative values indicate net bond order gained.
|
|
92
|
+
"""
|
|
93
|
+
imbalances = {node: 0.0 for node in its.nodes}
|
|
94
|
+
for u, v, attrs in its.edges(data=True):
|
|
95
|
+
delta = _standard_order(attrs)
|
|
96
|
+
imbalances[u] += delta
|
|
97
|
+
imbalances[v] += delta
|
|
98
|
+
return imbalances
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def reaction_center_atom_maps(mapped_rxn_smiles, tol=1e-9):
|
|
102
|
+
"""
|
|
103
|
+
Return atom-map numbers incident to changed ITS edges.
|
|
104
|
+
|
|
105
|
+
The result is suitable for lightweight hydrogen expansion: map heavy atoms
|
|
106
|
+
first, then display hydrogens only on atoms whose bonds changed.
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
its = rsmi_to_its(mapped_rxn_smiles)
|
|
110
|
+
except Exception:
|
|
111
|
+
return set()
|
|
112
|
+
|
|
113
|
+
maps = set()
|
|
114
|
+
for u, v, attrs in its.edges(data=True):
|
|
115
|
+
if abs(_standard_order(attrs)) <= tol:
|
|
116
|
+
continue
|
|
117
|
+
for node in (u, v):
|
|
118
|
+
data = its.nodes[node]
|
|
119
|
+
atom_map = data.get("atom_map")
|
|
120
|
+
if atom_map and data.get("element") != "H":
|
|
121
|
+
maps.add(int(atom_map))
|
|
122
|
+
return maps
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def is_electron_balanced(its, tol=1e-9): # noqa: C901
|
|
126
|
+
"""
|
|
127
|
+
Return whether an ITS passes the bond-order electron-balance heuristic.
|
|
128
|
+
|
|
129
|
+
This check deliberately uses explicit-H ITS graphs. Hydrogens are therefore
|
|
130
|
+
present when synkit can expose them, and SynKit 1.4+ node attributes such as
|
|
131
|
+
``lone_pairs`` are available for hashing/bookkeeping. The rule is still a
|
|
132
|
+
conservative bond-order heuristic rather than a full arrow-pushing proof:
|
|
133
|
+
|
|
134
|
+
- each connected reaction-center component must have zero net changed bond
|
|
135
|
+
order;
|
|
136
|
+
- no atom may carry more than two units of local bond-order imbalance, and
|
|
137
|
+
magnitude-2 centers must appear in opposite-sign pairs.
|
|
138
|
+
|
|
139
|
+
The second condition allows normal terminal heteroatom source/sink behavior
|
|
140
|
+
in substitutions. Magnitude-2 centers are allowed only when the same
|
|
141
|
+
reaction-center component contains a matching opposite magnitude-2 center,
|
|
142
|
+
which covers imine/condensation-style lone-pair bookkeeping while rejecting
|
|
143
|
+
mappings that delete a C=O double bond and distribute the compensation over
|
|
144
|
+
unrelated single-bond changes.
|
|
145
|
+
"""
|
|
146
|
+
changed_edges = []
|
|
147
|
+
adjacency = {}
|
|
148
|
+
for u, v, attrs in its.edges(data=True):
|
|
149
|
+
delta = _standard_order(attrs)
|
|
150
|
+
if abs(delta) <= tol:
|
|
151
|
+
continue
|
|
152
|
+
changed_edges.append((u, v, delta))
|
|
153
|
+
adjacency.setdefault(u, []).append(v)
|
|
154
|
+
adjacency.setdefault(v, []).append(u)
|
|
155
|
+
|
|
156
|
+
if not changed_edges:
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
imbalances = electron_balance_imbalances(its)
|
|
160
|
+
if any(abs(value) > 2.0 + tol for value in imbalances.values()):
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
edge_by_component = {}
|
|
164
|
+
for u, v, delta in changed_edges:
|
|
165
|
+
edge_by_component.setdefault(u, []).append((u, v, delta))
|
|
166
|
+
edge_by_component.setdefault(v, []).append((u, v, delta))
|
|
167
|
+
|
|
168
|
+
seen = set()
|
|
169
|
+
for start in adjacency:
|
|
170
|
+
if start in seen:
|
|
171
|
+
continue
|
|
172
|
+
stack = [start]
|
|
173
|
+
component = set()
|
|
174
|
+
while stack:
|
|
175
|
+
node = stack.pop()
|
|
176
|
+
if node in component:
|
|
177
|
+
continue
|
|
178
|
+
component.add(node)
|
|
179
|
+
stack.extend(adjacency.get(node, ()))
|
|
180
|
+
seen.update(component)
|
|
181
|
+
|
|
182
|
+
total = 0.0
|
|
183
|
+
used_edges = set()
|
|
184
|
+
for node in component:
|
|
185
|
+
for edge in edge_by_component.get(node, ()):
|
|
186
|
+
key = frozenset(edge[:2])
|
|
187
|
+
if key in used_edges:
|
|
188
|
+
continue
|
|
189
|
+
used_edges.add(key)
|
|
190
|
+
total += edge[2]
|
|
191
|
+
if abs(total) > tol:
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
big_pos = 0
|
|
195
|
+
big_neg = 0
|
|
196
|
+
for node in component:
|
|
197
|
+
value = imbalances[node]
|
|
198
|
+
if value > 1.0 + tol:
|
|
199
|
+
big_pos += 1
|
|
200
|
+
elif value < -1.0 - tol:
|
|
201
|
+
big_neg += 1
|
|
202
|
+
if big_pos != big_neg:
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _node_missing_on_one_side(data):
|
|
209
|
+
present = data.get("present")
|
|
210
|
+
return (
|
|
211
|
+
isinstance(present, (tuple, list))
|
|
212
|
+
and len(present) == 2
|
|
213
|
+
and present[0] != present[1]
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def electron_balance_status(its, tol=1e-9):
|
|
218
|
+
"""
|
|
219
|
+
Return ``True``/``False``/``None`` for electron-balance status.
|
|
220
|
+
|
|
221
|
+
``None`` means the mapped reaction omits a coproduct/byproduct in a changed
|
|
222
|
+
component, so the displayed ITS is incomplete and a strict electron-balance
|
|
223
|
+
verdict would be overconfident.
|
|
224
|
+
"""
|
|
225
|
+
adjacency = {}
|
|
226
|
+
edge_delta = {}
|
|
227
|
+
for u, v, attrs in its.edges(data=True):
|
|
228
|
+
delta = _standard_order(attrs)
|
|
229
|
+
if abs(delta) <= tol:
|
|
230
|
+
continue
|
|
231
|
+
adjacency.setdefault(u, []).append(v)
|
|
232
|
+
adjacency.setdefault(v, []).append(u)
|
|
233
|
+
edge_delta[frozenset((u, v))] = delta
|
|
234
|
+
|
|
235
|
+
seen = set()
|
|
236
|
+
for start in adjacency:
|
|
237
|
+
if start in seen:
|
|
238
|
+
continue
|
|
239
|
+
stack = [start]
|
|
240
|
+
component = set()
|
|
241
|
+
while stack:
|
|
242
|
+
node = stack.pop()
|
|
243
|
+
if node in component:
|
|
244
|
+
continue
|
|
245
|
+
component.add(node)
|
|
246
|
+
stack.extend(adjacency.get(node, ()))
|
|
247
|
+
seen.update(component)
|
|
248
|
+
|
|
249
|
+
total = 0.0
|
|
250
|
+
used_edges = set()
|
|
251
|
+
for node in component:
|
|
252
|
+
for nbr in adjacency.get(node, ()):
|
|
253
|
+
key = frozenset((node, nbr))
|
|
254
|
+
if key in used_edges:
|
|
255
|
+
continue
|
|
256
|
+
used_edges.add(key)
|
|
257
|
+
total += edge_delta[key]
|
|
258
|
+
|
|
259
|
+
if abs(total) > tol and any(
|
|
260
|
+
_node_missing_on_one_side(its.nodes[node]) for node in component
|
|
261
|
+
):
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
return is_electron_balanced(its, tol=tol)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def mapped_rxn_is_electron_balanced(mapped_rxn_smiles):
|
|
268
|
+
"""
|
|
269
|
+
Return electron-balance status for a mapped reaction SMILES.
|
|
270
|
+
|
|
271
|
+
Returns ``None`` if synkit cannot construct the explicit-H ITS graph, so
|
|
272
|
+
callers can avoid treating parser failures as proven invalid chemistry.
|
|
273
|
+
"""
|
|
274
|
+
try:
|
|
275
|
+
its = rsmi_to_its(mapped_rxn_smiles, explicit_hydrogen=True)
|
|
276
|
+
except Exception:
|
|
277
|
+
return None
|
|
278
|
+
return electron_balance_status(its)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def dedup_mapped_rxns(results, smiles_key="smiles"):
|
|
282
|
+
"""Remove isomorphic duplicate mappings from a list of result dictionaries.
|
|
283
|
+
|
|
284
|
+
Each result is expected to carry a ``"smiles"`` key with the mapped reaction
|
|
285
|
+
SMILES. The canonical ITS hash is computed for every result and stored under
|
|
286
|
+
``"its_hash"``; only the first result seen for each distinct hash is kept, so
|
|
287
|
+
the original ordering of symmetry-distinct optima is preserved.
|
|
288
|
+
|
|
289
|
+
Results whose ITS graph cannot be built (hash ``None``) are never collapsed
|
|
290
|
+
together -- they are all kept, keyed by their raw mapped SMILES instead -- so
|
|
291
|
+
deduplication can only ever remove provable isomorphic copies.
|
|
292
|
+
|
|
293
|
+
:param results: Mapping results, each containing a ``"smiles"`` entry.
|
|
294
|
+
:type results: list[dict]
|
|
295
|
+
|
|
296
|
+
:return: The symmetry-distinct subset of ``results``.
|
|
297
|
+
:rtype: list[dict]
|
|
298
|
+
"""
|
|
299
|
+
seen = set()
|
|
300
|
+
deduped = []
|
|
301
|
+
for r in results:
|
|
302
|
+
h = its_canonical_hash(r.get(smiles_key))
|
|
303
|
+
r["its_hash"] = h
|
|
304
|
+
key = h if h is not None else ("raw", r.get(smiles_key))
|
|
305
|
+
if key in seen:
|
|
306
|
+
continue
|
|
307
|
+
seen.add(key)
|
|
308
|
+
deduped.append(r)
|
|
309
|
+
return deduped
|