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,554 @@
|
|
|
1
|
+
"""Reaction-SMILES interface for full-space exact mapping and symmetry classes.
|
|
2
|
+
|
|
3
|
+
``map_reaction`` defaults to Python propagation and assignment-bounded search
|
|
4
|
+
(PABS). It does not run SLAP or extract a reduced uncertainty kernel. The
|
|
5
|
+
optional C++ backend performs a separate full-space integer-shell search.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import math
|
|
12
|
+
import time
|
|
13
|
+
from collections import Counter
|
|
14
|
+
from dataclasses import asdict, dataclass
|
|
15
|
+
from numbers import Real
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from rdkit import Chem
|
|
19
|
+
|
|
20
|
+
from .solvers.native.reaction_text import split_reaction_fields
|
|
21
|
+
from .solvers.exact.distance_search.records import normalize_distance_target
|
|
22
|
+
from .solvers.exact.hydrogen import enumerate_minimal_hydrogen_transfers
|
|
23
|
+
from .solvers.exact.pabs.propagation import PropagationConfig
|
|
24
|
+
from .solvers.exact.search import enumerate_pabs_mappings
|
|
25
|
+
from .graph.labeled_graph import LabeledGraph
|
|
26
|
+
from .reporting.spectrum import _attributed_its_graph, _exact_code
|
|
27
|
+
from .solvers.slap.lap import _adjacency_and_elements
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class ReactionMapping:
|
|
32
|
+
"""One exact symmetry class representative, including any missing atoms."""
|
|
33
|
+
|
|
34
|
+
mapped_reaction: str
|
|
35
|
+
mapping: tuple[int, ...]
|
|
36
|
+
class_id: str
|
|
37
|
+
distance: float
|
|
38
|
+
hydrogen_distance: int | None
|
|
39
|
+
reactant_only_maps: tuple[int, ...]
|
|
40
|
+
product_only_maps: tuple[int, ...]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class ReactionMappingResult:
|
|
45
|
+
"""User-facing mappings with independent search/classification completion."""
|
|
46
|
+
|
|
47
|
+
reactions: tuple[ReactionMapping, ...]
|
|
48
|
+
complete: bool
|
|
49
|
+
search_complete: bool
|
|
50
|
+
classification_complete: bool
|
|
51
|
+
hydrogen_complete: bool
|
|
52
|
+
minimum_cost: float | None
|
|
53
|
+
incomplete_reason: str | None
|
|
54
|
+
backend: str
|
|
55
|
+
hydrogen: str
|
|
56
|
+
objective: str
|
|
57
|
+
balance: str
|
|
58
|
+
agents: str
|
|
59
|
+
labeled_mapping_count: int | None
|
|
60
|
+
labeled_mapping_count_scope: str
|
|
61
|
+
minimum_cost_scope: str
|
|
62
|
+
elapsed_seconds: float
|
|
63
|
+
search_result: object
|
|
64
|
+
|
|
65
|
+
def as_dict(self):
|
|
66
|
+
"""Export public metadata and mapped reactions without solver internals."""
|
|
67
|
+
from dataclasses import fields
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
field.name: (
|
|
71
|
+
[asdict(item) for item in self.reactions] if field.name == "reactions" else getattr(self, field.name)
|
|
72
|
+
)
|
|
73
|
+
for field in fields(self)
|
|
74
|
+
if field.name != "search_result"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def mapped_reactions(self):
|
|
79
|
+
return tuple(item.mapped_reaction for item in self.reactions)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _parse(reaction, hydrogen):
|
|
83
|
+
if not isinstance(reaction, str):
|
|
84
|
+
raise TypeError("reaction must be reaction SMILES")
|
|
85
|
+
parts = split_reaction_fields(
|
|
86
|
+
reaction,
|
|
87
|
+
error_message="Expected reactants>agents>products or reactants>>products",
|
|
88
|
+
)
|
|
89
|
+
if not parts[0] or not parts[2]:
|
|
90
|
+
raise ValueError("Expected reactants>agents>products or reactants>>products")
|
|
91
|
+
params = Chem.SmilesParserParams()
|
|
92
|
+
params.removeHs = False
|
|
93
|
+
endpoints = []
|
|
94
|
+
for side in (parts[0], parts[2]):
|
|
95
|
+
mol = Chem.MolFromSmiles(side, params)
|
|
96
|
+
if mol is None:
|
|
97
|
+
raise ValueError("Could not parse reaction endpoint")
|
|
98
|
+
for atom in mol.GetAtoms():
|
|
99
|
+
if atom.GetAtomicNum() == 0:
|
|
100
|
+
raise ValueError("Wildcard atoms require a concrete element")
|
|
101
|
+
atom.SetAtomMapNum(0)
|
|
102
|
+
if hydrogen == "explicit":
|
|
103
|
+
mol = Chem.AddHs(mol)
|
|
104
|
+
else:
|
|
105
|
+
mol = Chem.RemoveHs(mol)
|
|
106
|
+
if any(atom.GetAtomicNum() == 1 for atom in mol.GetAtoms()):
|
|
107
|
+
raise ValueError("Free or isotopic H requires hydrogen='explicit'")
|
|
108
|
+
Chem.AssignStereochemistry(mol, cleanIt=True, force=True)
|
|
109
|
+
endpoints.append(mol)
|
|
110
|
+
return endpoints, parts[1]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _identity(atom):
|
|
114
|
+
return atom.GetAtomicNum(), atom.GetIsotope()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _graphs(mols, balance, objective):
|
|
118
|
+
inventories = [Counter(_identity(atom) for atom in mol.GetAtoms()) for mol in mols]
|
|
119
|
+
if balance == "strict" and inventories[0] != inventories[1]:
|
|
120
|
+
raise ValueError("Unbalanced atom inventories; select balance='dummy' explicitly")
|
|
121
|
+
universe = inventories[0] | inventories[1]
|
|
122
|
+
graphs = []
|
|
123
|
+
for mol, inventory in zip(mols, inventories):
|
|
124
|
+
atoms = list(mol.GetAtoms())
|
|
125
|
+
labels = [_identity(atom) for atom in atoms]
|
|
126
|
+
dummy_labels = sorted((universe - inventory).elements())
|
|
127
|
+
labels.extend(dummy_labels)
|
|
128
|
+
adjacency = {i: {} for i in range(len(labels))}
|
|
129
|
+
for bond in mol.GetBonds():
|
|
130
|
+
i, j = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
|
|
131
|
+
adjacency[i][j] = adjacency[j][i] = bond.GetBondTypeAsDouble()
|
|
132
|
+
properties = {
|
|
133
|
+
"charge": [atom.GetFormalCharge() for atom in atoms],
|
|
134
|
+
"radical": [atom.GetNumRadicalElectrons() for atom in atoms],
|
|
135
|
+
"hcount": [atom.GetTotalNumHs() for atom in atoms],
|
|
136
|
+
"stereo": [atom.GetProp("_CIPCode") if atom.HasProp("_CIPCode") else "" for atom in atoms],
|
|
137
|
+
"dummy": [False] * len(atoms),
|
|
138
|
+
# Fix stereobond endpoints individually in the pruning subgroup.
|
|
139
|
+
# The output equivalence also retains E/Z bond annotations; the
|
|
140
|
+
# matrix-only automorphism engine cannot otherwise see them.
|
|
141
|
+
"_stereo_anchor": [0] * len(atoms),
|
|
142
|
+
}
|
|
143
|
+
for bond in mol.GetBonds():
|
|
144
|
+
if bond.GetStereo() != Chem.BondStereo.STEREONONE:
|
|
145
|
+
for index in (bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()):
|
|
146
|
+
properties["_stereo_anchor"][index] = index + 1
|
|
147
|
+
for name, values in properties.items():
|
|
148
|
+
values.extend([True if name == "dummy" else "" if name == "stereo" else 0] * len(dummy_labels))
|
|
149
|
+
if objective == "lewis":
|
|
150
|
+
from synkit.IO.mol_to_graph import MolToGraph
|
|
151
|
+
|
|
152
|
+
# A uniquely colored anchor converts unary electron costs into
|
|
153
|
+
# ordinary edges. Its bijection is forced by atom compatibility.
|
|
154
|
+
electrons = [2 * MolToGraph.estimate_lone_pairs(atom) + atom.GetNumRadicalElectrons() for atom in atoms]
|
|
155
|
+
electrons.extend([0] * len(dummy_labels))
|
|
156
|
+
anchor = len(labels)
|
|
157
|
+
labels.append((0, -1))
|
|
158
|
+
adjacency[anchor] = {}
|
|
159
|
+
for i, count in enumerate(electrons):
|
|
160
|
+
if count:
|
|
161
|
+
adjacency[i][anchor] = adjacency[anchor][i] = count / 2
|
|
162
|
+
for name, values in properties.items():
|
|
163
|
+
values.append(True if name == "dummy" else "" if name == "stereo" else 0)
|
|
164
|
+
graph = LabeledGraph(adjacency, labels)
|
|
165
|
+
graph.props.update(properties)
|
|
166
|
+
graphs.append(graph)
|
|
167
|
+
return graphs
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _render(mols, mapping):
|
|
171
|
+
mapped = [Chem.Mol(mol) for mol in mols]
|
|
172
|
+
product_numbers = {image: atom + 1 for atom, image in enumerate(mapping)}
|
|
173
|
+
for atom in mapped[0].GetAtoms():
|
|
174
|
+
atom.SetAtomMapNum(atom.GetIdx() + 1)
|
|
175
|
+
for atom in mapped[1].GetAtoms():
|
|
176
|
+
atom.SetAtomMapNum(product_numbers[atom.GetIdx()])
|
|
177
|
+
nr, np = (mol.GetNumAtoms() for mol in mols)
|
|
178
|
+
reactant_only = tuple(i + 1 for i, j in enumerate(mapping[:nr]) if j >= np)
|
|
179
|
+
product_only = tuple(i + 1 for i, j in enumerate(mapping) if i >= nr and j < np)
|
|
180
|
+
return (
|
|
181
|
+
">>".join(Chem.MolToSmiles(mol, canonical=True) for mol in mapped),
|
|
182
|
+
reactant_only,
|
|
183
|
+
product_only,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _lift_mapping(mols, heavy_mapping, plan):
|
|
188
|
+
explicit = [Chem.AddHs(mol) for mol in mols]
|
|
189
|
+
children = []
|
|
190
|
+
for mol in explicit:
|
|
191
|
+
children.append(
|
|
192
|
+
{
|
|
193
|
+
atom.GetIdx(): [neighbor.GetIdx() for neighbor in atom.GetNeighbors() if neighbor.GetAtomicNum() == 1]
|
|
194
|
+
for atom in mol.GetAtoms()
|
|
195
|
+
if atom.GetAtomicNum() != 1
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
mapping = [-1] * explicit[0].GetNumAtoms()
|
|
199
|
+
mapping[: len(heavy_mapping)] = heavy_mapping
|
|
200
|
+
|
|
201
|
+
def transfer(source, target, count):
|
|
202
|
+
r = children[0][source]
|
|
203
|
+
p = children[1][heavy_mapping[target]]
|
|
204
|
+
for i, j in zip(r[:count], p[:count]):
|
|
205
|
+
mapping[i] = j
|
|
206
|
+
del r[:count]
|
|
207
|
+
del p[:count]
|
|
208
|
+
|
|
209
|
+
for i, count in enumerate(plan.preserved):
|
|
210
|
+
transfer(i, i, count)
|
|
211
|
+
for source, target, count in plan.transfers:
|
|
212
|
+
transfer(source, target, count)
|
|
213
|
+
if sorted(mapping) != list(range(len(mapping))):
|
|
214
|
+
raise RuntimeError("Hydrogen flow did not produce a full bijection")
|
|
215
|
+
return explicit, tuple(mapping)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def map_reaction(
|
|
219
|
+
reaction,
|
|
220
|
+
*,
|
|
221
|
+
backend="python",
|
|
222
|
+
library_path=None,
|
|
223
|
+
CD="minimal",
|
|
224
|
+
CD_mode="exact",
|
|
225
|
+
hydrogen=None,
|
|
226
|
+
objective="lewis",
|
|
227
|
+
balance="strict",
|
|
228
|
+
binary=False,
|
|
229
|
+
time_limit_seconds=10.0,
|
|
230
|
+
max_mappings=100_000,
|
|
231
|
+
max_bijections=None,
|
|
232
|
+
max_hydrogen_plans=10_000,
|
|
233
|
+
classification_max_search_nodes=100_000,
|
|
234
|
+
search_config=None,
|
|
235
|
+
):
|
|
236
|
+
"""Return symmetry-distinct mapped reactions from unmapped reaction SMILES.
|
|
237
|
+
|
|
238
|
+
``backend='python'`` uses exact propagation and linear-assignment bounds,
|
|
239
|
+
called PABS (Propagation and Assignment-Bounded Search) by this package.
|
|
240
|
+
``backend='cpp'`` uses the optional compiled full-space search. This entry
|
|
241
|
+
point does not run the SLAP heuristic or extract its uncertainty kernel.
|
|
242
|
+
It searches compatible assignments and then classifies the resulting ITSs.
|
|
243
|
+
Inspect ``complete`` because deadlines or output limits can interrupt it.
|
|
244
|
+
|
|
245
|
+
With CD omitted, prove the minimum and enumerate every minimum-distance
|
|
246
|
+
class. Numeric CD selects exactly that distance; the native backend also
|
|
247
|
+
accepts ``CD_mode='at_most'`` to enumerate every class at distance <= CD.
|
|
248
|
+
|
|
249
|
+
The default ``objective='lewis'`` uses explicit H and weighted bonds. Its
|
|
250
|
+
normalized distance is bond-order distance plus half the L1 difference of
|
|
251
|
+
estimated nonbonding electron counts (twice lone pairs plus radicals).
|
|
252
|
+
Select ``objective='bond'`` for bond-only distance and implicit heavy H.
|
|
253
|
+
``hydrogen=None`` selects explicit H for Lewis and heavy atoms for bond CD.
|
|
254
|
+
|
|
255
|
+
Hydrogen modes: ``heavy`` searches heavy bonds; ``explicit`` searches all
|
|
256
|
+
H bonds jointly; ``compressed`` returns minimum-H lifts conditional on each
|
|
257
|
+
selected heavy mapping (not a global full-H optimum).
|
|
258
|
+
|
|
259
|
+
``balance='dummy'`` pads missing element/isotope inventories with isolated
|
|
260
|
+
zero-state placeholders. Output contains only real endpoint atoms; map IDs
|
|
261
|
+
present on one side are reported explicitly. Agents are excluded from search
|
|
262
|
+
and returned separately. Existing atom-map numbers are discarded, not fixed.
|
|
263
|
+
``search_config`` optionally supplies ``PropagationConfig`` controls for the
|
|
264
|
+
Python PABS backend, including experimental separator and factor-spectrum
|
|
265
|
+
bounds.
|
|
266
|
+
|
|
267
|
+
Classes use exact colored ITS canonicalization, equivalent to the two-sided
|
|
268
|
+
automorphism action beta*f*alpha^-1. Incomplete canonicalization never merges
|
|
269
|
+
unverified classes. The deadline is shared by search, H lifting and dedup.
|
|
270
|
+
"""
|
|
271
|
+
return _map_reaction_with_search(
|
|
272
|
+
reaction,
|
|
273
|
+
search_function=enumerate_pabs_mappings,
|
|
274
|
+
backend=backend,
|
|
275
|
+
library_path=library_path,
|
|
276
|
+
CD=CD,
|
|
277
|
+
CD_mode=CD_mode,
|
|
278
|
+
hydrogen=hydrogen,
|
|
279
|
+
objective=objective,
|
|
280
|
+
balance=balance,
|
|
281
|
+
binary=binary,
|
|
282
|
+
time_limit_seconds=time_limit_seconds,
|
|
283
|
+
max_mappings=max_mappings,
|
|
284
|
+
max_bijections=max_bijections,
|
|
285
|
+
max_hydrogen_plans=max_hydrogen_plans,
|
|
286
|
+
classification_max_search_nodes=classification_max_search_nodes,
|
|
287
|
+
search_config=search_config,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# Parsing, callbacks and rendering share one search budget and result contract.
|
|
292
|
+
def _map_reaction_with_search( # noqa: C901
|
|
293
|
+
reaction,
|
|
294
|
+
*,
|
|
295
|
+
search_function,
|
|
296
|
+
backend="python",
|
|
297
|
+
library_path=None,
|
|
298
|
+
CD="minimal",
|
|
299
|
+
CD_mode="exact",
|
|
300
|
+
hydrogen=None,
|
|
301
|
+
objective="lewis",
|
|
302
|
+
balance="strict",
|
|
303
|
+
binary=False,
|
|
304
|
+
time_limit_seconds=10.0,
|
|
305
|
+
max_mappings=100_000,
|
|
306
|
+
max_bijections=None,
|
|
307
|
+
max_hydrogen_plans=10_000,
|
|
308
|
+
classification_max_search_nodes=100_000,
|
|
309
|
+
search_config=None,
|
|
310
|
+
native_classification_library=None,
|
|
311
|
+
):
|
|
312
|
+
"""Share parsing, hydrogen lifting, rendering and classification across solvers."""
|
|
313
|
+
started = time.perf_counter()
|
|
314
|
+
CD = normalize_distance_target(CD)
|
|
315
|
+
if CD_mode not in {"exact", "at_most"}:
|
|
316
|
+
raise ValueError("CD_mode must be 'exact' or 'at_most'")
|
|
317
|
+
if CD_mode == "at_most" and (backend != "cpp" or CD == "minimal"):
|
|
318
|
+
raise ValueError("CD_mode='at_most' requires native search and numeric CD")
|
|
319
|
+
if hydrogen is None:
|
|
320
|
+
hydrogen = "explicit" if objective == "lewis" else "heavy"
|
|
321
|
+
if CD_mode == "at_most" and hydrogen == "compressed":
|
|
322
|
+
raise ValueError(
|
|
323
|
+
"CD_mode='at_most' requires explicit or heavy hydrogens; "
|
|
324
|
+
"compressed lifts have a separate conditional distance"
|
|
325
|
+
)
|
|
326
|
+
if hydrogen not in {"heavy", "explicit", "compressed"}:
|
|
327
|
+
raise ValueError("hydrogen must be 'heavy', 'explicit', or 'compressed'")
|
|
328
|
+
if objective not in {"bond", "lewis"} or balance not in {"strict", "dummy"}:
|
|
329
|
+
raise ValueError("objective must be 'bond' or 'lewis'; balance must be 'strict' or 'dummy'")
|
|
330
|
+
if objective == "lewis" and (hydrogen != "explicit" or binary):
|
|
331
|
+
raise ValueError("Lewis objective requires hydrogen='explicit' and binary=False")
|
|
332
|
+
if search_config is not None and not isinstance(search_config, PropagationConfig):
|
|
333
|
+
raise TypeError("search_config must be a PropagationConfig or None")
|
|
334
|
+
if search_config is not None and backend != "python":
|
|
335
|
+
raise ValueError("search_config is currently supported by backend='python'")
|
|
336
|
+
if hydrogen == "compressed" and balance != "strict":
|
|
337
|
+
raise ValueError("Compressed H requires strict balance; use explicit H with dummy balance")
|
|
338
|
+
if time_limit_seconds is not None and (
|
|
339
|
+
isinstance(time_limit_seconds, bool)
|
|
340
|
+
or not isinstance(time_limit_seconds, Real)
|
|
341
|
+
or not math.isfinite(time_limit_seconds)
|
|
342
|
+
or time_limit_seconds < 0
|
|
343
|
+
):
|
|
344
|
+
raise ValueError("time_limit_seconds must be finite and non-negative")
|
|
345
|
+
for name, value in (
|
|
346
|
+
("max_hydrogen_plans", max_hydrogen_plans),
|
|
347
|
+
("classification_max_search_nodes", classification_max_search_nodes),
|
|
348
|
+
):
|
|
349
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
|
|
350
|
+
raise ValueError(f"{name} must be a positive integer")
|
|
351
|
+
deadline = None if time_limit_seconds is None else started + time_limit_seconds
|
|
352
|
+
|
|
353
|
+
def remaining():
|
|
354
|
+
return None if deadline is None else max(0, deadline - time.perf_counter())
|
|
355
|
+
|
|
356
|
+
mols, agents = _parse(reaction, hydrogen)
|
|
357
|
+
graphs = _graphs(mols, balance, objective)
|
|
358
|
+
native_classifier = None
|
|
359
|
+
native_class_registry = {}
|
|
360
|
+
if native_classification_library is not None:
|
|
361
|
+
if hydrogen == "compressed":
|
|
362
|
+
raise ValueError("Native class pipeline currently requires explicit or heavy H")
|
|
363
|
+
from .solvers.native.classification import NativeITSBatchClassifier
|
|
364
|
+
|
|
365
|
+
native_classifier = NativeITSBatchClassifier.from_endpoint_graphs(
|
|
366
|
+
mols, graphs, library_path=native_classification_library, binary=binary
|
|
367
|
+
)
|
|
368
|
+
native_classifier.frontend_max_canonical_nodes = classification_max_search_nodes
|
|
369
|
+
hcounts = [[atom.GetTotalNumHs() for atom in mol.GetAtoms()] for mol in mols]
|
|
370
|
+
if hydrogen == "compressed" and sum(hcounts[0]) != sum(hcounts[1]):
|
|
371
|
+
raise ValueError("Compressed H requires equal H inventories; use explicit H with dummy balance")
|
|
372
|
+
records = {}
|
|
373
|
+
canonical_cache = {}
|
|
374
|
+
classification_complete = hydrogen_complete = True
|
|
375
|
+
failure = None
|
|
376
|
+
|
|
377
|
+
def classify(endpoint_mols, endpoint_graphs, mapping, cost, hcost=None):
|
|
378
|
+
nonlocal classification_complete, failure
|
|
379
|
+
if not classification_complete:
|
|
380
|
+
return
|
|
381
|
+
if deadline is not None and time.perf_counter() >= deadline:
|
|
382
|
+
classification_complete = False
|
|
383
|
+
failure = "classification:time_limit"
|
|
384
|
+
return
|
|
385
|
+
if native_classifier is not None:
|
|
386
|
+
native_record = native_class_registry.pop(tuple(mapping), None)
|
|
387
|
+
if native_record is None:
|
|
388
|
+
raise RuntimeError("Native class callback lacks an exact class record")
|
|
389
|
+
code = native_record.key
|
|
390
|
+
if code not in records:
|
|
391
|
+
rendered, left_only, right_only = _render(endpoint_mols, mapping)
|
|
392
|
+
records[code] = ReactionMapping(
|
|
393
|
+
rendered,
|
|
394
|
+
tuple(mapping),
|
|
395
|
+
native_record.identifier,
|
|
396
|
+
float(cost),
|
|
397
|
+
hcost,
|
|
398
|
+
left_only,
|
|
399
|
+
right_only,
|
|
400
|
+
)
|
|
401
|
+
return
|
|
402
|
+
a, labels = _adjacency_and_elements(endpoint_graphs[0], binary)
|
|
403
|
+
b, _ = _adjacency_and_elements(endpoint_graphs[1], binary)
|
|
404
|
+
properties = {
|
|
405
|
+
name: (endpoint_graphs[0].props[name], endpoint_graphs[1].props[name])
|
|
406
|
+
for name in ("charge", "radical", "hcount", "stereo", "dummy")
|
|
407
|
+
}
|
|
408
|
+
# _attributed_its_graph expects product already in reactant coordinates.
|
|
409
|
+
images = np.asarray(mapping, dtype=int)
|
|
410
|
+
its = _attributed_its_graph(a, b[images[:, None], images[None, :]], labels, properties, mapping)
|
|
411
|
+
# Retain endpoint E/Z annotations in the exact equivalence relation.
|
|
412
|
+
for i, j, attrs in its.edges(data=True):
|
|
413
|
+
stereo = []
|
|
414
|
+
for mol, left, right in (
|
|
415
|
+
(endpoint_mols[0], i, j),
|
|
416
|
+
(endpoint_mols[1], mapping[i], mapping[j]),
|
|
417
|
+
):
|
|
418
|
+
bond = mol.GetBondBetweenAtoms(left, right) if max(left, right) < mol.GetNumAtoms() else None
|
|
419
|
+
stereo.append(str(bond.GetStereo()) if bond is not None else "")
|
|
420
|
+
attrs["color"] += tuple(stereo)
|
|
421
|
+
# Identical fully colored indexed graphs need no second proof. This
|
|
422
|
+
# cache never treats a hash or WL fingerprint as class equality.
|
|
423
|
+
signature = (
|
|
424
|
+
tuple((i, attrs["color"]) for i, attrs in its.nodes(data=True)),
|
|
425
|
+
tuple((i, j, attrs["color"]) for i, j, attrs in its.edges(data=True)),
|
|
426
|
+
)
|
|
427
|
+
code = canonical_cache.get(signature)
|
|
428
|
+
reason = None
|
|
429
|
+
if code is None:
|
|
430
|
+
code, reason = _exact_code(
|
|
431
|
+
its,
|
|
432
|
+
timeout_seconds=remaining(),
|
|
433
|
+
max_search_nodes=classification_max_search_nodes,
|
|
434
|
+
)
|
|
435
|
+
if code is None:
|
|
436
|
+
classification_complete = False
|
|
437
|
+
failure = f"classification:{reason}"
|
|
438
|
+
return
|
|
439
|
+
if len(canonical_cache) < 256:
|
|
440
|
+
canonical_cache[signature] = code
|
|
441
|
+
if code not in records:
|
|
442
|
+
rendered, left_only, right_only = _render(endpoint_mols, mapping)
|
|
443
|
+
records[code] = ReactionMapping(
|
|
444
|
+
rendered,
|
|
445
|
+
tuple(mapping),
|
|
446
|
+
hashlib.sha256(repr(code).encode()).hexdigest(),
|
|
447
|
+
float(cost),
|
|
448
|
+
hcost,
|
|
449
|
+
left_only,
|
|
450
|
+
right_only,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
def receive(mapping, cost):
|
|
454
|
+
nonlocal hydrogen_complete, failure
|
|
455
|
+
if hydrogen == "compressed":
|
|
456
|
+
plans = enumerate_minimal_hydrogen_transfers(
|
|
457
|
+
*hcounts,
|
|
458
|
+
mapping,
|
|
459
|
+
max_plans=max_hydrogen_plans,
|
|
460
|
+
time_limit_seconds=remaining(),
|
|
461
|
+
)
|
|
462
|
+
if not plans.complete:
|
|
463
|
+
hydrogen_complete = False
|
|
464
|
+
failure = f"hydrogen:{plans.truncation_reason}"
|
|
465
|
+
for plan in plans.plans:
|
|
466
|
+
explicit, lifted = _lift_mapping(mols, mapping, plan)
|
|
467
|
+
classify(
|
|
468
|
+
explicit,
|
|
469
|
+
_graphs(explicit, "strict", "bond"),
|
|
470
|
+
lifted,
|
|
471
|
+
cost + plan.distance,
|
|
472
|
+
plan.distance,
|
|
473
|
+
)
|
|
474
|
+
else:
|
|
475
|
+
classify(mols, graphs, mapping, cost)
|
|
476
|
+
|
|
477
|
+
search_options = {} if search_config is None else {"config": search_config}
|
|
478
|
+
if native_classifier is not None:
|
|
479
|
+
search_options.update(
|
|
480
|
+
_native_classifier=native_classifier,
|
|
481
|
+
_native_class_registry=native_class_registry,
|
|
482
|
+
)
|
|
483
|
+
if backend == "cpp":
|
|
484
|
+
search_options.update(
|
|
485
|
+
CD_mode=CD_mode,
|
|
486
|
+
symmetry=True,
|
|
487
|
+
node_properties=(
|
|
488
|
+
"charge",
|
|
489
|
+
"radical",
|
|
490
|
+
"hcount",
|
|
491
|
+
"stereo",
|
|
492
|
+
"dummy",
|
|
493
|
+
"_stereo_anchor",
|
|
494
|
+
),
|
|
495
|
+
)
|
|
496
|
+
search = search_function(
|
|
497
|
+
graphs,
|
|
498
|
+
backend=backend,
|
|
499
|
+
library_path=library_path,
|
|
500
|
+
CD=CD,
|
|
501
|
+
binary=binary,
|
|
502
|
+
max_bijections=max_bijections,
|
|
503
|
+
max_mappings=max_mappings,
|
|
504
|
+
time_limit_seconds=remaining(),
|
|
505
|
+
collect_mappings=False,
|
|
506
|
+
mapping_callback=receive,
|
|
507
|
+
compute_minimum_cost=(CD == "minimal"),
|
|
508
|
+
**search_options,
|
|
509
|
+
)
|
|
510
|
+
native_details = search.backend_statistics or {}
|
|
511
|
+
hydrogen_complete = hydrogen_complete and bool(native_details.get("hydrogen_complete", True))
|
|
512
|
+
if native_classifier is not None:
|
|
513
|
+
classification_complete = classification_complete and bool(native_details.get("classification_complete", False))
|
|
514
|
+
if not classification_complete:
|
|
515
|
+
reason = native_details.get("classification_reason") or search.truncation_reason or "incomplete"
|
|
516
|
+
failure = f"classification:{reason}"
|
|
517
|
+
if not hydrogen_complete and failure is None:
|
|
518
|
+
failure = f"hydrogen:{search.truncation_reason or 'incomplete'}"
|
|
519
|
+
objective_name = (
|
|
520
|
+
"heavy_cd_with_conditional_minimum_h_lifts"
|
|
521
|
+
if hydrogen == "compressed"
|
|
522
|
+
else (
|
|
523
|
+
"lewis_bond_plus_nonbonding_electron_distance"
|
|
524
|
+
if objective == "lewis"
|
|
525
|
+
else ("explicit_h_bond_distance" if hydrogen == "explicit" else "heavy_bond_distance")
|
|
526
|
+
)
|
|
527
|
+
)
|
|
528
|
+
labeled_count = getattr(search, "selected_labeled_mapping_count", search.selected_mapping_count)
|
|
529
|
+
count_scope = (
|
|
530
|
+
"heavy_mapping" if hydrogen == "compressed" else "augmented_mapping" if balance == "dummy" else "mapping"
|
|
531
|
+
)
|
|
532
|
+
count_scope += "_ball" if CD_mode == "at_most" else "_shell"
|
|
533
|
+
return ReactionMappingResult(
|
|
534
|
+
reactions=tuple(sorted(records.values(), key=lambda item: item.class_id)),
|
|
535
|
+
complete=search.complete and classification_complete and hydrogen_complete,
|
|
536
|
+
search_complete=search.complete,
|
|
537
|
+
classification_complete=classification_complete,
|
|
538
|
+
hydrogen_complete=hydrogen_complete,
|
|
539
|
+
minimum_cost=search.minimum_cost,
|
|
540
|
+
incomplete_reason=failure or search.truncation_reason,
|
|
541
|
+
backend=search.backend,
|
|
542
|
+
hydrogen=hydrogen,
|
|
543
|
+
objective=objective_name,
|
|
544
|
+
balance=balance,
|
|
545
|
+
agents=agents,
|
|
546
|
+
labeled_mapping_count=labeled_count,
|
|
547
|
+
labeled_mapping_count_scope=("not_computed_symmetry_pruned_search" if labeled_count is None else count_scope),
|
|
548
|
+
minimum_cost_scope=("heavy_bond_distance" if hydrogen == "compressed" else objective_name),
|
|
549
|
+
elapsed_seconds=time.perf_counter() - started,
|
|
550
|
+
search_result=search,
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
__all__ = ["ReactionMapping", "ReactionMappingResult", "map_reaction"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Exact-shell analysis, ITS spectra, and structural alternatives."""
|