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,319 @@
|
|
|
1
|
+
"""Polynomial admissible bounds for exact chemical-distance search."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy.optimize import linear_sum_assignment
|
|
9
|
+
|
|
10
|
+
from ..assignment.assignment_certificate import certified_lattice_assignment, scaled_lattice_costs
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _assignment_with_lattice_check(costs):
|
|
14
|
+
"""Certify LAP optimality on the supported lattice; preserve generic API.
|
|
15
|
+
|
|
16
|
+
A certificate failure is never converted to the generic floating-point
|
|
17
|
+
route. Only a matrix outside the declared lattice domain uses that route,
|
|
18
|
+
whose historical numerical qualifications remain unchanged.
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
scaled_lattice_costs(costs)
|
|
22
|
+
except ValueError:
|
|
23
|
+
return linear_sum_assignment(costs)
|
|
24
|
+
certificate = certified_lattice_assignment(costs)
|
|
25
|
+
return np.arange(len(costs)), np.asarray(certificate.permutation, dtype=int)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ResidualBondMass:
|
|
29
|
+
"""Maintain exact residual matrix sum/absolute-sum bounds under DFS."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, reactant, product, reactant_order, product_atoms=None):
|
|
32
|
+
atom_count = len(reactant_order)
|
|
33
|
+
self.reactant_sums = [0.0] * (atom_count + 1)
|
|
34
|
+
self.reactant_abs_sums = [0.0] * (atom_count + 1)
|
|
35
|
+
for depth in range(atom_count - 1, -1, -1):
|
|
36
|
+
atom = reactant_order[depth]
|
|
37
|
+
later = reactant_order[depth + 1 :]
|
|
38
|
+
self.reactant_sums[depth] = self.reactant_sums[depth + 1] + float(
|
|
39
|
+
reactant[atom, atom]
|
|
40
|
+
+ reactant[atom, later].sum()
|
|
41
|
+
+ reactant[later, atom].sum()
|
|
42
|
+
)
|
|
43
|
+
self.reactant_abs_sums[depth] = (
|
|
44
|
+
self.reactant_abs_sums[depth + 1]
|
|
45
|
+
+ float(abs(reactant[atom, atom]))
|
|
46
|
+
+ float(np.abs(reactant[atom, later]).sum())
|
|
47
|
+
+ float(np.abs(reactant[later, atom]).sum())
|
|
48
|
+
)
|
|
49
|
+
self.product = product
|
|
50
|
+
if product_atoms is None:
|
|
51
|
+
product_atoms = range(product.shape[0])
|
|
52
|
+
product_atoms = tuple(product_atoms)
|
|
53
|
+
self.active = np.zeros(product.shape[0], dtype=bool)
|
|
54
|
+
self.active[list(product_atoms)] = True
|
|
55
|
+
product_block = product[np.ix_(product_atoms, product_atoms)]
|
|
56
|
+
self.product_sum = float(product_block.sum())
|
|
57
|
+
self.product_abs_sum = float(np.abs(product_block).sum())
|
|
58
|
+
|
|
59
|
+
def interval(self, depth):
|
|
60
|
+
"""Return residual bond-mass lower and upper bounds."""
|
|
61
|
+
lower = 0.5 * abs(self.reactant_sums[depth] - self.product_sum)
|
|
62
|
+
upper = 0.5 * (self.reactant_abs_sums[depth] + self.product_abs_sum)
|
|
63
|
+
return lower, upper
|
|
64
|
+
|
|
65
|
+
def remove(self, image):
|
|
66
|
+
"""Remove one product image and return exact restoration deltas."""
|
|
67
|
+
diagonal = float(self.product[image, image])
|
|
68
|
+
removed = float(
|
|
69
|
+
self.product[image, self.active].sum()
|
|
70
|
+
+ self.product[self.active, image].sum()
|
|
71
|
+
- diagonal
|
|
72
|
+
)
|
|
73
|
+
removed_abs = float(
|
|
74
|
+
np.abs(self.product[image, self.active]).sum()
|
|
75
|
+
+ np.abs(self.product[self.active, image]).sum()
|
|
76
|
+
- abs(diagonal)
|
|
77
|
+
)
|
|
78
|
+
self.active[image] = False
|
|
79
|
+
self.product_sum -= removed
|
|
80
|
+
self.product_abs_sum -= removed_abs
|
|
81
|
+
return removed, removed_abs
|
|
82
|
+
|
|
83
|
+
def restore(self, image, deltas):
|
|
84
|
+
"""Undo :meth:`remove` during DFS backtracking."""
|
|
85
|
+
removed, removed_abs = deltas
|
|
86
|
+
self.product_sum += removed
|
|
87
|
+
self.product_abs_sum += removed_abs
|
|
88
|
+
self.active[image] = True
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def element_blocks(indices, elements):
|
|
92
|
+
"""Group atom indices by their exact element label."""
|
|
93
|
+
blocks: dict[object, list[int]] = {}
|
|
94
|
+
for index in indices:
|
|
95
|
+
blocks.setdefault(elements[index], []).append(index)
|
|
96
|
+
return blocks
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def blocked_assignment_extreme(
|
|
100
|
+
costs,
|
|
101
|
+
reactant_atoms,
|
|
102
|
+
product_atoms,
|
|
103
|
+
reactant_elements,
|
|
104
|
+
product_elements,
|
|
105
|
+
*,
|
|
106
|
+
maximize=False,
|
|
107
|
+
) -> float:
|
|
108
|
+
"""Solve independent element-compatible LAP blocks."""
|
|
109
|
+
reactant_blocks = element_blocks(reactant_atoms, reactant_elements)
|
|
110
|
+
product_blocks = element_blocks(product_atoms, product_elements)
|
|
111
|
+
if set(reactant_blocks) != set(product_blocks):
|
|
112
|
+
return -math.inf if maximize else math.inf
|
|
113
|
+
|
|
114
|
+
total = 0.0
|
|
115
|
+
for element, row_atoms in reactant_blocks.items():
|
|
116
|
+
column_atoms = product_blocks[element]
|
|
117
|
+
if len(row_atoms) != len(column_atoms):
|
|
118
|
+
return -math.inf if maximize else math.inf
|
|
119
|
+
block = costs[np.ix_(row_atoms, column_atoms)]
|
|
120
|
+
if not np.isfinite(block).all():
|
|
121
|
+
return -math.inf if maximize else math.inf
|
|
122
|
+
rows, columns = _assignment_with_lattice_check(-block if maximize else block)
|
|
123
|
+
total += float(block[rows, columns].sum())
|
|
124
|
+
return total
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def internal_cost_interval(reactant, product, reactant_atoms, product_atoms):
|
|
128
|
+
"""Admissible min/max bounds for still-unassigned internal pairs."""
|
|
129
|
+
if not reactant_atoms:
|
|
130
|
+
return 0.0, 0.0
|
|
131
|
+
reactant_block = reactant[np.ix_(reactant_atoms, reactant_atoms)]
|
|
132
|
+
product_block = product[np.ix_(product_atoms, product_atoms)]
|
|
133
|
+
lower = 0.5 * abs(float(reactant_block.sum() - product_block.sum()))
|
|
134
|
+
upper = 0.5 * float(np.abs(reactant_block).sum() + np.abs(product_block).sum())
|
|
135
|
+
return lower, upper
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def atom_profile_costs(reactant, product, reactant_elements, product_elements):
|
|
139
|
+
"""Lower-bound total CD forced by each individual atom assignment.
|
|
140
|
+
|
|
141
|
+
For a fixed ``i -> p``, every element-preserving completion pairs the
|
|
142
|
+
entries in reactant row ``i`` with entries in product row ``p`` inside
|
|
143
|
+
each neighbor-element block. Sorted one-dimensional matching minimizes
|
|
144
|
+
their L1 difference. Half of that row mismatch cannot exceed the complete
|
|
145
|
+
chemical distance, making values above a numeric target safe to remove
|
|
146
|
+
from that atom's domain. Summing the half-row costs over a compatible
|
|
147
|
+
bijection also bounds its total CD. A fixed-prefix sum plus a LAP over
|
|
148
|
+
the remaining profile matrix is therefore an admissible global bound;
|
|
149
|
+
combine it with other global bounds using max to avoid double-counting.
|
|
150
|
+
"""
|
|
151
|
+
atom_count = len(reactant_elements)
|
|
152
|
+
costs = np.zeros((atom_count, atom_count), dtype=float)
|
|
153
|
+
reactant_blocks = element_blocks(range(atom_count), reactant_elements)
|
|
154
|
+
product_blocks = element_blocks(range(atom_count), product_elements)
|
|
155
|
+
levels = np.unique(np.concatenate((reactant.ravel(), product.ravel())))
|
|
156
|
+
histogram_profiles = (
|
|
157
|
+
len(levels) <= 16
|
|
158
|
+
and np.isfinite(levels).all()
|
|
159
|
+
and np.max(np.abs(levels), initial=0) * max(1, atom_count) < 2**40
|
|
160
|
+
and np.equal(levels * 2, np.rint(levels * 2)).all()
|
|
161
|
+
)
|
|
162
|
+
for element, reactant_atoms in reactant_blocks.items():
|
|
163
|
+
product_atoms = product_blocks[element]
|
|
164
|
+
if histogram_profiles:
|
|
165
|
+
# Sorted L1 distance equals the integral of the difference of
|
|
166
|
+
# cumulative counts. Small half-integer alphabets stay exact and
|
|
167
|
+
# avoid allocating a rows-by-rows-by-neighbors tensor.
|
|
168
|
+
left = reactant[:, reactant_atoms]
|
|
169
|
+
right = product[:, product_atoms]
|
|
170
|
+
for low, high in zip(levels, levels[1:]):
|
|
171
|
+
left_counts = np.count_nonzero(left >= high, axis=1)
|
|
172
|
+
right_counts = np.count_nonzero(right >= high, axis=1)
|
|
173
|
+
costs += (high - low) * np.abs(
|
|
174
|
+
left_counts[:, None] - right_counts[None, :]
|
|
175
|
+
)
|
|
176
|
+
continue
|
|
177
|
+
sorted_reactant = np.sort(reactant[:, reactant_atoms], axis=1)
|
|
178
|
+
sorted_product = np.sort(product[:, product_atoms], axis=1)
|
|
179
|
+
costs += np.abs(sorted_reactant[:, None, :] - sorted_product[None, :, :]).sum(
|
|
180
|
+
axis=2
|
|
181
|
+
)
|
|
182
|
+
costs *= 0.5
|
|
183
|
+
for reactant_atom, element in enumerate(reactant_elements):
|
|
184
|
+
for product_atom, product_element in enumerate(product_elements):
|
|
185
|
+
if element != product_element:
|
|
186
|
+
costs[reactant_atom, product_atom] = math.inf
|
|
187
|
+
return costs
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def assignment_edge_lower_bounds(costs, reactant_elements, product_elements):
|
|
191
|
+
"""Minimum typed LAP cost forced by every individual assignment edge.
|
|
192
|
+
|
|
193
|
+
Relative to an optimal matching, forcing an edge closes an alternating
|
|
194
|
+
cycle. All-pairs shortest paths give its cheapest completion, without
|
|
195
|
+
solving a separate LAP for every candidate edge. This is a bound on the
|
|
196
|
+
complete profile assignment, not an additional edge contribution.
|
|
197
|
+
"""
|
|
198
|
+
n = len(reactant_elements)
|
|
199
|
+
result = np.full((n, n), math.inf)
|
|
200
|
+
row_blocks = element_blocks(range(n), reactant_elements)
|
|
201
|
+
column_blocks = element_blocks(range(n), product_elements)
|
|
202
|
+
total = 0.0
|
|
203
|
+
for element, rows in row_blocks.items():
|
|
204
|
+
columns = column_blocks[element]
|
|
205
|
+
block = costs[np.ix_(rows, columns)]
|
|
206
|
+
row_indices, permutation = _assignment_with_lattice_check(block)
|
|
207
|
+
matched_columns = [columns[index] for index in permutation]
|
|
208
|
+
matched_costs = block[row_indices, permutation]
|
|
209
|
+
total += float(matched_costs.sum())
|
|
210
|
+
delta = block[:, permutation] - matched_costs[:, None]
|
|
211
|
+
distances = delta.copy()
|
|
212
|
+
for pivot in range(len(rows)):
|
|
213
|
+
distances = np.minimum(
|
|
214
|
+
distances, distances[:, pivot, None] + distances[pivot, None, :]
|
|
215
|
+
)
|
|
216
|
+
result[np.ix_(rows, matched_columns)] = delta + distances.T
|
|
217
|
+
result += total
|
|
218
|
+
return result
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def conditioned_profile_assignment_lower_bound(
|
|
222
|
+
reactant,
|
|
223
|
+
product,
|
|
224
|
+
cross_costs,
|
|
225
|
+
reactant_atoms,
|
|
226
|
+
product_atoms,
|
|
227
|
+
reactant_elements,
|
|
228
|
+
product_elements,
|
|
229
|
+
*,
|
|
230
|
+
return_costs=False,
|
|
231
|
+
):
|
|
232
|
+
"""Bound remaining CD using profiles conditioned on the assigned prefix.
|
|
233
|
+
|
|
234
|
+
Cross costs account for edges to assigned atoms exactly. Sorted incident
|
|
235
|
+
profiles within the remaining subgraphs bound internal outgoing-row costs.
|
|
236
|
+
One LAP on their sum enforces a shared remaining atom assignment for both
|
|
237
|
+
contributions. Add committed prefix CD to obtain a full-objective bound.
|
|
238
|
+
"""
|
|
239
|
+
if not reactant_atoms:
|
|
240
|
+
return (0.0, np.empty((0, 0))) if return_costs else 0.0
|
|
241
|
+
rows = [reactant_elements[atom] for atom in reactant_atoms]
|
|
242
|
+
columns = [product_elements[atom] for atom in product_atoms]
|
|
243
|
+
internal = atom_profile_costs(
|
|
244
|
+
reactant[np.ix_(reactant_atoms, reactant_atoms)],
|
|
245
|
+
product[np.ix_(product_atoms, product_atoms)],
|
|
246
|
+
rows,
|
|
247
|
+
columns,
|
|
248
|
+
)
|
|
249
|
+
costs = internal + cross_costs[np.ix_(reactant_atoms, product_atoms)]
|
|
250
|
+
lower = blocked_assignment_extreme(
|
|
251
|
+
costs,
|
|
252
|
+
range(len(rows)),
|
|
253
|
+
range(len(columns)),
|
|
254
|
+
rows,
|
|
255
|
+
columns,
|
|
256
|
+
)
|
|
257
|
+
return (lower, costs) if return_costs else lower
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def reaction_center_order(
|
|
261
|
+
reactant,
|
|
262
|
+
product,
|
|
263
|
+
reactant_elements,
|
|
264
|
+
element_counts,
|
|
265
|
+
seed_mapping,
|
|
266
|
+
):
|
|
267
|
+
"""Order a seeded exact search from changed atoms out through the graph."""
|
|
268
|
+
atom_count = len(reactant_elements)
|
|
269
|
+
default = sorted(
|
|
270
|
+
range(atom_count),
|
|
271
|
+
key=lambda atom: (
|
|
272
|
+
element_counts[reactant_elements[atom]],
|
|
273
|
+
-int(np.count_nonzero(reactant[atom])),
|
|
274
|
+
atom,
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
if seed_mapping is None:
|
|
278
|
+
return default
|
|
279
|
+
images = np.asarray(seed_mapping, dtype=int)
|
|
280
|
+
mapped_product = product[images[:, None], images[None, :]]
|
|
281
|
+
local_change = 0.5 * np.abs(reactant - mapped_product).sum(axis=1)
|
|
282
|
+
seeds = [atom for atom in range(atom_count) if local_change[atom] > 0]
|
|
283
|
+
if not seeds:
|
|
284
|
+
return default
|
|
285
|
+
|
|
286
|
+
distances = [atom_count + 1] * atom_count
|
|
287
|
+
pending = list(seeds)
|
|
288
|
+
for atom in seeds:
|
|
289
|
+
distances[atom] = 0
|
|
290
|
+
for atom in pending:
|
|
291
|
+
neighbors = np.flatnonzero(
|
|
292
|
+
np.logical_or(reactant[atom] != 0, reactant[:, atom] != 0)
|
|
293
|
+
)
|
|
294
|
+
for neighbor in neighbors:
|
|
295
|
+
neighbor = int(neighbor)
|
|
296
|
+
if distances[neighbor] <= distances[atom] + 1:
|
|
297
|
+
continue
|
|
298
|
+
distances[neighbor] = distances[atom] + 1
|
|
299
|
+
pending.append(neighbor)
|
|
300
|
+
return sorted(
|
|
301
|
+
range(atom_count),
|
|
302
|
+
key=lambda atom: (
|
|
303
|
+
distances[atom],
|
|
304
|
+
-float(local_change[atom]),
|
|
305
|
+
element_counts[reactant_elements[atom]],
|
|
306
|
+
-int(np.count_nonzero(reactant[atom])),
|
|
307
|
+
atom,
|
|
308
|
+
),
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
__all__ = [
|
|
313
|
+
"ResidualBondMass",
|
|
314
|
+
"atom_profile_costs",
|
|
315
|
+
"blocked_assignment_extreme",
|
|
316
|
+
"element_blocks",
|
|
317
|
+
"internal_cost_interval",
|
|
318
|
+
"reaction_center_order",
|
|
319
|
+
]
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exhaustive DFS/branch-and-bound exact mapper for small labeled graphs.
|
|
3
|
+
|
|
4
|
+
This module provides a standalone solver (:class:`ExactMapper`) that is
|
|
5
|
+
independent of the SLAP heuristic. It is most useful for very small graphs
|
|
6
|
+
(up to ~10 atoms) or for unit-testing the kernel-based solvers, where the
|
|
7
|
+
solution space is small enough to enumerate directly.
|
|
8
|
+
|
|
9
|
+
For larger reactions, extract the uncertainty-region kernel first
|
|
10
|
+
(:func:`mapper.exact.kernel.extract_kernel`) and solve it with the faster
|
|
11
|
+
orbital-branching solver (:func:`mapper.exact.branching.solve_kernel`).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import List, Optional
|
|
18
|
+
|
|
19
|
+
from scipy.optimize import linear_sum_assignment
|
|
20
|
+
|
|
21
|
+
from ....graph.labeled_graph import LabeledGraph
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ExactResult:
|
|
26
|
+
"""Container for an exact mapping result.
|
|
27
|
+
|
|
28
|
+
:ivar mapping: ``mapping[i]`` gives the product atom index for reactant atom ``i``.
|
|
29
|
+
:vartype mapping: list[int]
|
|
30
|
+
:ivar cost: Total absolute difference between adjacency matrices and loop counts.
|
|
31
|
+
:vartype cost: float
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
mapping: List[int]
|
|
35
|
+
cost: float
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ExactMapper:
|
|
39
|
+
"""Exhaustive exact mapper for pairs of labelled graphs.
|
|
40
|
+
|
|
41
|
+
Performs a depth-first search over all bijective atom assignments.
|
|
42
|
+
Three enhancements over naive enumeration keep it practical on kernels:
|
|
43
|
+
|
|
44
|
+
1. **Branch-and-bound** — prunes branches whose current cost already
|
|
45
|
+
exceeds the best known solution.
|
|
46
|
+
2. **Admissible lower bound** — at each node, solves a small LAP on the
|
|
47
|
+
loop (diagonal) differences of unassigned atoms to get a tighter
|
|
48
|
+
pruning threshold.
|
|
49
|
+
Search intentionally does not infer symmetry from local fingerprints.
|
|
50
|
+
Equal fingerprints do not prove that two atoms can be transposed by an
|
|
51
|
+
automorphism, so such a rule can discard the true optimum.
|
|
52
|
+
|
|
53
|
+
Intended for small graphs (reaction-center kernels up to ~10 atoms).
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self):
|
|
57
|
+
self.best_result: Optional[ExactResult] = None
|
|
58
|
+
|
|
59
|
+
def _compute_partial_cost(
|
|
60
|
+
self,
|
|
61
|
+
react: LabeledGraph,
|
|
62
|
+
prod: LabeledGraph,
|
|
63
|
+
assignment: List[Optional[int]],
|
|
64
|
+
depth: int,
|
|
65
|
+
) -> float:
|
|
66
|
+
"""Incremental cost added by assigning reactant atom ``depth``."""
|
|
67
|
+
i = depth
|
|
68
|
+
p = assignment[i]
|
|
69
|
+
cost = 0.0
|
|
70
|
+
loop_react = react.graph.get(i, {}).get(i, 0)
|
|
71
|
+
loop_prod = prod.graph.get(p, {}).get(p, 0)
|
|
72
|
+
cost += abs(loop_react - loop_prod)
|
|
73
|
+
for j in range(i):
|
|
74
|
+
q = assignment[j]
|
|
75
|
+
if q is None:
|
|
76
|
+
continue
|
|
77
|
+
w_react = react.graph.get(i, {}).get(j, 0)
|
|
78
|
+
w_prod = prod.graph.get(p, {}).get(q, 0)
|
|
79
|
+
cost += abs(w_react - w_prod)
|
|
80
|
+
return cost
|
|
81
|
+
|
|
82
|
+
def _search( # noqa: C901
|
|
83
|
+
self,
|
|
84
|
+
react: LabeledGraph,
|
|
85
|
+
prod: LabeledGraph,
|
|
86
|
+
assignment: List[Optional[int]],
|
|
87
|
+
used_prod: List[bool],
|
|
88
|
+
depth: int,
|
|
89
|
+
current_cost: float,
|
|
90
|
+
) -> None:
|
|
91
|
+
n = len(react.labels)
|
|
92
|
+
if self.best_result is not None and current_cost >= self.best_result.cost:
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
if self.best_result is not None:
|
|
96
|
+
lb = 0.0
|
|
97
|
+
label_to_unreact: dict = {}
|
|
98
|
+
label_to_unprod: dict = {}
|
|
99
|
+
for i_un in range(depth, n):
|
|
100
|
+
lbl = react.labels[i_un]
|
|
101
|
+
label_to_unreact.setdefault(lbl, []).append(i_un)
|
|
102
|
+
for p_un in range(n):
|
|
103
|
+
if not used_prod[p_un]:
|
|
104
|
+
lbl = prod.labels[p_un]
|
|
105
|
+
label_to_unprod.setdefault(lbl, []).append(p_un)
|
|
106
|
+
|
|
107
|
+
infeasible = False
|
|
108
|
+
for lbl, rs in label_to_unreact.items():
|
|
109
|
+
ps = label_to_unprod.get(lbl, [])
|
|
110
|
+
if len(rs) != len(ps):
|
|
111
|
+
infeasible = True
|
|
112
|
+
break
|
|
113
|
+
if not rs:
|
|
114
|
+
continue
|
|
115
|
+
m = len(rs)
|
|
116
|
+
cost_mat = [[0.0] * m for _ in range(m)]
|
|
117
|
+
for ii, i_idx in enumerate(rs):
|
|
118
|
+
loop_i = react.graph.get(i_idx, {}).get(i_idx, 0)
|
|
119
|
+
for jj, p_idx in enumerate(ps):
|
|
120
|
+
loop_p = prod.graph.get(p_idx, {}).get(p_idx, 0)
|
|
121
|
+
cost_mat[ii][jj] = abs(loop_i - loop_p)
|
|
122
|
+
row_ind, col_ind = linear_sum_assignment(cost_mat)
|
|
123
|
+
lb += sum(cost_mat[r][c] for r, c in zip(row_ind, col_ind))
|
|
124
|
+
|
|
125
|
+
if infeasible or current_cost + lb >= self.best_result.cost:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
if depth == n:
|
|
129
|
+
if self.best_result is None or current_cost < self.best_result.cost:
|
|
130
|
+
self.best_result = ExactResult(
|
|
131
|
+
mapping=list(assignment), cost=current_cost
|
|
132
|
+
)
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
i = depth
|
|
136
|
+
for p in range(n):
|
|
137
|
+
if used_prod[p]:
|
|
138
|
+
continue
|
|
139
|
+
if react.labels[i] != prod.labels[p]:
|
|
140
|
+
continue
|
|
141
|
+
assignment[i] = p
|
|
142
|
+
used_prod[p] = True
|
|
143
|
+
incr = self._compute_partial_cost(react, prod, assignment, depth)
|
|
144
|
+
self._search(
|
|
145
|
+
react, prod, assignment, used_prod, depth + 1, current_cost + incr
|
|
146
|
+
)
|
|
147
|
+
assignment[i] = None
|
|
148
|
+
used_prod[p] = False
|
|
149
|
+
|
|
150
|
+
def solve(self, react: LabeledGraph, prod: LabeledGraph) -> ExactResult:
|
|
151
|
+
"""Compute the optimal bijective mapping between two labelled graphs.
|
|
152
|
+
|
|
153
|
+
:param react: Must have the same number of nodes and the same multiset of labels.
|
|
154
|
+
:type react: LabeledGraph
|
|
155
|
+
:param prod: Must have the same number of nodes and the same multiset of labels.
|
|
156
|
+
:type prod: LabeledGraph
|
|
157
|
+
|
|
158
|
+
:return: Optimal mapping and its cost.
|
|
159
|
+
:rtype: ExactResult
|
|
160
|
+
|
|
161
|
+
:raises ValueError: If the graphs have incompatible sizes or label multisets.
|
|
162
|
+
:raises RuntimeError: If no valid mapping is found (should never happen for compatible graphs).
|
|
163
|
+
"""
|
|
164
|
+
n = len(react.labels)
|
|
165
|
+
if len(prod.labels) != n:
|
|
166
|
+
raise ValueError("Reactant and product must have the same number of atoms")
|
|
167
|
+
if sorted(react.labels) != sorted(prod.labels):
|
|
168
|
+
raise ValueError(
|
|
169
|
+
"Reactant and product must have the same multiset of labels"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
self.best_result = None
|
|
173
|
+
assignment: List[Optional[int]] = [None] * n
|
|
174
|
+
used_prod: List[bool] = [False] * n
|
|
175
|
+
self._search(react, prod, assignment, used_prod, 0, 0.0)
|
|
176
|
+
|
|
177
|
+
if self.best_result is None:
|
|
178
|
+
raise RuntimeError("No valid mapping found")
|
|
179
|
+
return self.best_result
|