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,247 @@
|
|
|
1
|
+
"""Exact integer assignments with inherited, repaired primal--dual state."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy.sparse import csr_matrix
|
|
9
|
+
from scipy.sparse.csgraph import connected_components
|
|
10
|
+
|
|
11
|
+
from .assignment_certificate import AssignmentCertificate
|
|
12
|
+
from .assignment_verification import verify_supported_assignment
|
|
13
|
+
from ..pabs.propagation_limits import check_deadline
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class AssignmentState:
|
|
18
|
+
"""Certified residual matching; costs and potentials are scaled by four."""
|
|
19
|
+
|
|
20
|
+
rows: tuple[int, ...]
|
|
21
|
+
columns: tuple[int, ...]
|
|
22
|
+
certificate: AssignmentCertificate
|
|
23
|
+
augmentations: int
|
|
24
|
+
inherited_edges: int
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def lower_bound(self):
|
|
28
|
+
"""Return the exact integer objective of this residual assignment."""
|
|
29
|
+
return self.certificate.scaled_objective
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _initial_state(costs, rows, columns, parent):
|
|
33
|
+
n = len(costs)
|
|
34
|
+
v = np.zeros(n, dtype=np.int64)
|
|
35
|
+
old_matches = {}
|
|
36
|
+
if parent is not None:
|
|
37
|
+
prior = parent.certificate
|
|
38
|
+
old_columns = dict(zip(parent.columns, prior.column_potentials))
|
|
39
|
+
v[:] = [old_columns.get(image, 0) for image in columns]
|
|
40
|
+
old_matches = {
|
|
41
|
+
row: parent.columns[prior.permutation[i]]
|
|
42
|
+
for i, row in enumerate(parent.rows)
|
|
43
|
+
}
|
|
44
|
+
# Repair feasibility after arbitrary residual cost changes. Old potentials
|
|
45
|
+
# are suggestions; each new row minimum restores u[i]+v[j] <= C[i,j].
|
|
46
|
+
u = np.min(costs - v[None, :], axis=1)
|
|
47
|
+
p = np.full(n, -1, dtype=int)
|
|
48
|
+
positions = {image: j for j, image in enumerate(columns)}
|
|
49
|
+
inherited = 0
|
|
50
|
+
for i, row in enumerate(rows):
|
|
51
|
+
j = positions.get(old_matches.get(row))
|
|
52
|
+
if j is not None and p[j] < 0 and costs[i, j] == u[i] + v[j]:
|
|
53
|
+
p[j] = i
|
|
54
|
+
inherited += 1
|
|
55
|
+
return u, v, p, inherited
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _augment(costs, u, v, p, root, deadline=None):
|
|
59
|
+
"""Hungarian shortest augmenting path from one unmatched row."""
|
|
60
|
+
n = len(costs)
|
|
61
|
+
infinity = np.int64(2**60)
|
|
62
|
+
slack = np.full(n, infinity, dtype=np.int64)
|
|
63
|
+
predecessor = np.full(n, -1, dtype=int)
|
|
64
|
+
used = np.zeros(n, dtype=bool)
|
|
65
|
+
row, previous = root, -1
|
|
66
|
+
while True:
|
|
67
|
+
check_deadline(deadline)
|
|
68
|
+
reduced = costs[row] - u[row] - v
|
|
69
|
+
improve = ~used & (reduced < slack)
|
|
70
|
+
slack[improve] = reduced[improve]
|
|
71
|
+
predecessor[improve] = previous
|
|
72
|
+
image = int(np.argmin(np.where(used, infinity, slack)))
|
|
73
|
+
delta = int(slack[image])
|
|
74
|
+
u[root] += delta
|
|
75
|
+
if used.any():
|
|
76
|
+
u[p[used]] += delta
|
|
77
|
+
v[used] -= delta
|
|
78
|
+
slack[~used] -= delta
|
|
79
|
+
used[image] = True
|
|
80
|
+
if p[image] < 0:
|
|
81
|
+
break
|
|
82
|
+
row, previous = int(p[image]), image
|
|
83
|
+
while image >= 0:
|
|
84
|
+
previous = int(predecessor[image])
|
|
85
|
+
p[image] = root if previous < 0 else p[previous]
|
|
86
|
+
image = previous
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def solve_assignment(costs, allowed, rows, columns, parent=None, *, deadline=None):
|
|
90
|
+
"""Solve a nonnegative integer LAP and independently check its certificate.
|
|
91
|
+
|
|
92
|
+
Unsupported edges get a finite penalty above every all-supported matching.
|
|
93
|
+
A returned None means no supported perfect matching exists. The bounded
|
|
94
|
+
matrix and potential range keeps all working integer operations exact.
|
|
95
|
+
"""
|
|
96
|
+
check_deadline(deadline)
|
|
97
|
+
original = np.asarray(costs)
|
|
98
|
+
if original.dtype.kind not in "iu":
|
|
99
|
+
raise ValueError("Assignment costs must be exact integers")
|
|
100
|
+
costs = original.astype(np.int64)
|
|
101
|
+
allowed = np.asarray(allowed, dtype=bool)
|
|
102
|
+
n = len(rows)
|
|
103
|
+
if costs.shape != (n, n) or allowed.shape != costs.shape or len(columns) != n:
|
|
104
|
+
raise ValueError("Residual assignment must be square")
|
|
105
|
+
if np.any(costs < 0) or np.max(costs, initial=0) > 2**30 or n > 512:
|
|
106
|
+
raise ValueError("Synister-CP assignment exceeds its exact integer domain")
|
|
107
|
+
if not n:
|
|
108
|
+
return AssignmentState((), (), AssignmentCertificate((), (), (), 0), 0, 0)
|
|
109
|
+
penalty = (n + 1) * int(costs.max(initial=0)) + 1
|
|
110
|
+
matrix = np.where(allowed, costs, penalty)
|
|
111
|
+
u, v, p, inherited = _initial_state(matrix, rows, columns, parent)
|
|
112
|
+
augmentations = 0
|
|
113
|
+
for row in range(n):
|
|
114
|
+
check_deadline(deadline)
|
|
115
|
+
if row not in p:
|
|
116
|
+
_augment(matrix, u, v, p, row, deadline)
|
|
117
|
+
augmentations += 1
|
|
118
|
+
permutation = np.empty(n, dtype=int)
|
|
119
|
+
permutation[p] = np.arange(n)
|
|
120
|
+
if not allowed[np.arange(n), permutation].all():
|
|
121
|
+
return None
|
|
122
|
+
certificate = AssignmentCertificate(
|
|
123
|
+
tuple(map(int, permutation)),
|
|
124
|
+
tuple(map(int, u)),
|
|
125
|
+
tuple(map(int, v)),
|
|
126
|
+
sum(int(matrix[i, j]) for i, j in enumerate(permutation)),
|
|
127
|
+
)
|
|
128
|
+
if not verify_supported_assignment(costs, allowed, certificate):
|
|
129
|
+
raise ArithmeticError(
|
|
130
|
+
"Synister-CP assignment failed independent integer verification"
|
|
131
|
+
)
|
|
132
|
+
return AssignmentState(
|
|
133
|
+
tuple(rows), tuple(columns), certificate, augmentations, inherited
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def reduced_cost_filter(costs, allowed, state, budget):
|
|
138
|
+
"""Keep every edge whose certified forced-cost lower bound fits the budget."""
|
|
139
|
+
certificate = state.certificate
|
|
140
|
+
reduced = (
|
|
141
|
+
costs
|
|
142
|
+
- np.asarray(certificate.row_potentials)[:, None]
|
|
143
|
+
- np.asarray(certificate.column_potentials)[None, :]
|
|
144
|
+
)
|
|
145
|
+
return allowed & (state.lower_bound + reduced <= budget)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def cycle_edge_lower_bounds(costs, allowed, state, *, deadline=None):
|
|
149
|
+
"""Certify forced-edge LAP costs by minimum alternating completion cycles.
|
|
150
|
+
|
|
151
|
+
Relative to the certified matching, reduced-cost arcs are nonnegative.
|
|
152
|
+
Forcing i to the column owned by j needs a path from j back to i. A
|
|
153
|
+
completed all-pairs shortest-path calculation gives the exact forced LAP
|
|
154
|
+
optimum. Interrupted calculations are never used to prune.
|
|
155
|
+
"""
|
|
156
|
+
certificate = state.certificate
|
|
157
|
+
permutation = np.asarray(certificate.permutation, dtype=int)
|
|
158
|
+
reduced = (
|
|
159
|
+
costs[:, permutation]
|
|
160
|
+
- np.asarray(certificate.row_potentials)[:, None]
|
|
161
|
+
- np.asarray(certificate.column_potentials)[permutation][None, :]
|
|
162
|
+
)
|
|
163
|
+
domain = allowed[:, permutation]
|
|
164
|
+
if np.any(reduced[domain] < 0):
|
|
165
|
+
raise ArithmeticError(
|
|
166
|
+
"Forced-edge calculation requires a feasible assignment dual"
|
|
167
|
+
)
|
|
168
|
+
# <=512 vertices, |potentials|<=2**50: every simple finite path <2**61.
|
|
169
|
+
infinity = np.int64(2**61)
|
|
170
|
+
arcs = np.where(domain, reduced, infinity)
|
|
171
|
+
_, component = connected_components(
|
|
172
|
+
csr_matrix(domain), directed=True, connection="strong"
|
|
173
|
+
)
|
|
174
|
+
distances = np.full_like(arcs, infinity)
|
|
175
|
+
for label in np.unique(component):
|
|
176
|
+
vertices = np.flatnonzero(component == label)
|
|
177
|
+
local = arcs[np.ix_(vertices, vertices)].copy()
|
|
178
|
+
for pivot in range(len(vertices)):
|
|
179
|
+
check_deadline(deadline)
|
|
180
|
+
local = np.minimum(
|
|
181
|
+
local, local[:, pivot, None] + local[pivot, None, :]
|
|
182
|
+
)
|
|
183
|
+
distances[np.ix_(vertices, vertices)] = local
|
|
184
|
+
result = np.empty_like(costs)
|
|
185
|
+
result[:, permutation] = state.lower_bound + arcs + distances.T
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def solve_partitioned_assignment(
|
|
190
|
+
costs,
|
|
191
|
+
allowed,
|
|
192
|
+
rows,
|
|
193
|
+
columns,
|
|
194
|
+
row_labels,
|
|
195
|
+
column_labels,
|
|
196
|
+
parent=None,
|
|
197
|
+
*,
|
|
198
|
+
deadline=None,
|
|
199
|
+
):
|
|
200
|
+
"""Solve independent atom-type blocks and verify their combined certificate."""
|
|
201
|
+
groups = {}
|
|
202
|
+
for i, label in enumerate(row_labels):
|
|
203
|
+
groups.setdefault(label, ([], []))[0].append(i)
|
|
204
|
+
for j, label in enumerate(column_labels):
|
|
205
|
+
groups.setdefault(label, ([], []))[1].append(j)
|
|
206
|
+
if len(groups) <= 1:
|
|
207
|
+
return solve_assignment(
|
|
208
|
+
costs, allowed, rows, columns, parent, deadline=deadline
|
|
209
|
+
)
|
|
210
|
+
n = len(rows)
|
|
211
|
+
permutation, u, v = [0] * n, [0] * n, [0] * n
|
|
212
|
+
augmentations = inherited = 0
|
|
213
|
+
for left, right in groups.values():
|
|
214
|
+
check_deadline(deadline)
|
|
215
|
+
if len(left) != len(right):
|
|
216
|
+
return None
|
|
217
|
+
state = solve_assignment(
|
|
218
|
+
costs[np.ix_(left, right)],
|
|
219
|
+
allowed[np.ix_(left, right)],
|
|
220
|
+
tuple(rows[i] for i in left),
|
|
221
|
+
tuple(columns[j] for j in right),
|
|
222
|
+
parent,
|
|
223
|
+
deadline=deadline,
|
|
224
|
+
)
|
|
225
|
+
if state is None:
|
|
226
|
+
return None
|
|
227
|
+
certificate = state.certificate
|
|
228
|
+
for i, position in enumerate(left):
|
|
229
|
+
permutation[position] = right[certificate.permutation[i]]
|
|
230
|
+
u[position] = certificate.row_potentials[i]
|
|
231
|
+
for j, position in enumerate(right):
|
|
232
|
+
v[position] = certificate.column_potentials[j]
|
|
233
|
+
augmentations += state.augmentations
|
|
234
|
+
inherited += state.inherited_edges
|
|
235
|
+
certificate = AssignmentCertificate(
|
|
236
|
+
tuple(permutation),
|
|
237
|
+
tuple(u),
|
|
238
|
+
tuple(v),
|
|
239
|
+
sum(int(costs[i, j]) for i, j in enumerate(permutation)),
|
|
240
|
+
)
|
|
241
|
+
if not verify_supported_assignment(costs, allowed, certificate):
|
|
242
|
+
raise ArithmeticError(
|
|
243
|
+
"Combined assignment blocks failed independent verification"
|
|
244
|
+
)
|
|
245
|
+
return AssignmentState(
|
|
246
|
+
tuple(rows), tuple(columns), certificate, augmentations, inherited
|
|
247
|
+
)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Optimality certificates and gap reporting.
|
|
3
|
+
|
|
4
|
+
A :class:`Certificate` pairs the cost of a concrete mapping (an *upper bound* on
|
|
5
|
+
the optimal chemical distance) with an admissible *lower bound*, and reports the
|
|
6
|
+
gap between them. When the gap is zero the mapping is *proven optimal*:
|
|
7
|
+
|
|
8
|
+
* ``method="dual-lap"`` -- the lower bound is the Gilmore-Lawler / dual-LAP
|
|
9
|
+
relaxation (:func:`mapper.slap.lap.dual_lap_lower_bound`). A zero gap means the
|
|
10
|
+
cheap relaxation already certifies optimality; a positive gap is honest about
|
|
11
|
+
the relaxation not being tight, *not* about the mapping being suboptimal.
|
|
12
|
+
* ``method="milp-exact"`` -- the lower bound equals the optimum returned by the
|
|
13
|
+
exact MILP/QAP solver (:mod:`mapper.exact.milp`), which CBC proves optimal, so
|
|
14
|
+
the gap is zero by construction.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
|
|
21
|
+
from ..slap.lap import dual_lap_lower_bound, chemical_distance, recover_mapping
|
|
22
|
+
|
|
23
|
+
# Costs are integer/half-integer in cd units; this tolerance absorbs float noise.
|
|
24
|
+
_TOL = 1e-6
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class Certificate:
|
|
29
|
+
"""Optimality certificate for a single mapping.
|
|
30
|
+
|
|
31
|
+
:ivar upper_bound: Cost of the concrete mapping (chemical distance, cd units).
|
|
32
|
+
:vartype upper_bound: float
|
|
33
|
+
:ivar lower_bound: Admissible lower bound on the optimal chemical distance.
|
|
34
|
+
:vartype lower_bound: float
|
|
35
|
+
:ivar method: How ``lower_bound`` was obtained (``"dual-lap"`` or ``"milp-exact"``).
|
|
36
|
+
:vartype method: str
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
upper_bound: float
|
|
40
|
+
lower_bound: float
|
|
41
|
+
method: str = "dual-lap"
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def gap(self) -> float:
|
|
45
|
+
"""Optimality gap ``upper_bound - lower_bound`` (>= 0)."""
|
|
46
|
+
return self.upper_bound - self.lower_bound
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def proven_optimal(self) -> bool:
|
|
50
|
+
"""True iff the lower bound certifies the mapping is optimal."""
|
|
51
|
+
import math
|
|
52
|
+
|
|
53
|
+
return math.isfinite(self.lower_bound) and self.gap <= _TOL
|
|
54
|
+
|
|
55
|
+
def __str__(self) -> str:
|
|
56
|
+
status = "PROVEN OPTIMAL" if self.proven_optimal else "not certified"
|
|
57
|
+
return (
|
|
58
|
+
f"Certificate(cost={self.upper_bound:g}, "
|
|
59
|
+
f"lower_bound={self.lower_bound:g}, gap={self.gap:g}, "
|
|
60
|
+
f"method={self.method!r}) -> {status}"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def certify_result(result, binary, method="dual-lap"):
|
|
65
|
+
"""Build and attach a :class:`Certificate` to a single result dict.
|
|
66
|
+
|
|
67
|
+
Uses the result's ``"cd"`` as the incumbent upper bound (falling back to a
|
|
68
|
+
recomputed chemical distance) and the dual-LAP lower bound. The certificate
|
|
69
|
+
is stored under ``result["certificate"]`` and returned.
|
|
70
|
+
|
|
71
|
+
:param result: A result entry with ``"lgp"`` and (optionally) ``"cd"``.
|
|
72
|
+
:type result: dict
|
|
73
|
+
:param binary: Whether bond orders are binarised (matches the matcher's ``binary``).
|
|
74
|
+
:type binary: bool
|
|
75
|
+
:param method: Lower-bound provenance label.
|
|
76
|
+
:type method: str, optional
|
|
77
|
+
|
|
78
|
+
:rtype: Certificate
|
|
79
|
+
"""
|
|
80
|
+
lgp = result["lgp"]
|
|
81
|
+
if "cd" in result:
|
|
82
|
+
ub = float(result["cd"])
|
|
83
|
+
else:
|
|
84
|
+
ub = chemical_distance(lgp, recover_mapping(lgp), binary)
|
|
85
|
+
lb = dual_lap_lower_bound(lgp, binary)
|
|
86
|
+
cert = Certificate(upper_bound=ub, lower_bound=lb, method=method)
|
|
87
|
+
result["certificate"] = cert
|
|
88
|
+
return cert
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def certify_results_exact(results, binary):
|
|
92
|
+
"""Attach exact (MILP/QAP) certificates to a set of optimal results.
|
|
93
|
+
|
|
94
|
+
The uncertainty-region kernel is extracted from the results and solved to
|
|
95
|
+
proven optimality with the MILP/QAP solver. The proven kernel optimum becomes
|
|
96
|
+
the lower bound for every result, so results whose cost equals it are
|
|
97
|
+
certified optimal over the uncertainty region (``method="milp-exact"``).
|
|
98
|
+
|
|
99
|
+
Falls back to the dual-LAP certificate when PuLP is unavailable or the kernel
|
|
100
|
+
is degenerate.
|
|
101
|
+
|
|
102
|
+
:param results: Optimal results sharing a common reaction (each with ``"lgp"``/``"cd"``).
|
|
103
|
+
:type results: list[dict]
|
|
104
|
+
:param binary: Whether bond orders are binarised.
|
|
105
|
+
:type binary: bool
|
|
106
|
+
"""
|
|
107
|
+
if not results:
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
from .kernel_solvers.kernel import extract_kernel
|
|
111
|
+
from .kernel_solvers.milp import solve_kernel_milp, HAS_PULP
|
|
112
|
+
|
|
113
|
+
if not HAS_PULP:
|
|
114
|
+
for r in results:
|
|
115
|
+
certify_result(r, binary)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
lgp = results[0]["lgp"]
|
|
119
|
+
kernel = extract_kernel(results, lgp, binary=binary)
|
|
120
|
+
sol = solve_kernel_milp(kernel)
|
|
121
|
+
lb = sol.cost
|
|
122
|
+
method = "milp-exact" if sol.proven_optimal else "milp"
|
|
123
|
+
for r in results:
|
|
124
|
+
if "cd" in r:
|
|
125
|
+
ub = float(r["cd"])
|
|
126
|
+
else:
|
|
127
|
+
ub = chemical_distance(r["lgp"], recover_mapping(r["lgp"]), binary)
|
|
128
|
+
r["certificate"] = Certificate(upper_bound=ub, lower_bound=lb, method=method)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Reference-ITS component reduction for staged exact AAM enumeration.
|
|
2
|
+
|
|
3
|
+
The reduction is intentionally explicit about scope. Given a *known* mapped
|
|
4
|
+
reaction (or any trusted seed mapping), it keeps every connected component of
|
|
5
|
+
the union ITS that contains a changed heavy-atom bond, H-count change, or charge
|
|
6
|
+
change. Completely unchanged disconnected components are fixed to the seed
|
|
7
|
+
mapping and can subsequently be represented by endpoint automorphism orbits.
|
|
8
|
+
|
|
9
|
+
This is exact for the seed-conditioned active-component space and is useful for
|
|
10
|
+
reference-AAM reproduction. It is not an unrestricted proof over mappings that
|
|
11
|
+
exchange atoms between active and inactive components; callers must not label it
|
|
12
|
+
as the complete atom-compatible CD shell.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
import math
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from ...graph.labeled_graph import LabeledGraph
|
|
23
|
+
from ..slap.lap import _adjacency_and_elements
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _node_property(lg, name, default=0):
|
|
27
|
+
values = lg.props.get(name)
|
|
28
|
+
if values is None:
|
|
29
|
+
return [default] * len(lg.labels)
|
|
30
|
+
if len(values) != len(lg.labels):
|
|
31
|
+
raise ValueError(f"{name} must contain one value per graph atom")
|
|
32
|
+
return list(values)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _induced_labeled_graph(lg, nodes):
|
|
36
|
+
nodes = tuple(nodes)
|
|
37
|
+
position = {node: index for index, node in enumerate(nodes)}
|
|
38
|
+
graph = {index: {} for index in range(len(nodes))}
|
|
39
|
+
for source in nodes:
|
|
40
|
+
for target, weight in lg.graph.get(source, {}).items():
|
|
41
|
+
if target in position:
|
|
42
|
+
graph[position[source]][position[target]] = weight
|
|
43
|
+
reduced = LabeledGraph(graph, [lg.labels[node] for node in nodes])
|
|
44
|
+
for name in ("atomic numbers", "hcounts", "charges"):
|
|
45
|
+
values = lg.props.get(name)
|
|
46
|
+
if values is not None:
|
|
47
|
+
reduced.set_prop(name, [values[node] for node in nodes])
|
|
48
|
+
return reduced
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _union_components(reactant, mapped_product):
|
|
52
|
+
size = reactant.shape[0]
|
|
53
|
+
neighbours = [set() for _ in range(size)]
|
|
54
|
+
for left in range(size):
|
|
55
|
+
for right in range(left + 1, size):
|
|
56
|
+
if reactant[left, right] != 0 or mapped_product[left, right] != 0:
|
|
57
|
+
neighbours[left].add(right)
|
|
58
|
+
neighbours[right].add(left)
|
|
59
|
+
components = []
|
|
60
|
+
unseen = set(range(size))
|
|
61
|
+
while unseen:
|
|
62
|
+
root = min(unseen)
|
|
63
|
+
component = set()
|
|
64
|
+
pending = [root]
|
|
65
|
+
while pending:
|
|
66
|
+
node = pending.pop()
|
|
67
|
+
if node in component:
|
|
68
|
+
continue
|
|
69
|
+
component.add(node)
|
|
70
|
+
pending.extend(neighbours[node] - component)
|
|
71
|
+
unseen.difference_update(component)
|
|
72
|
+
components.append(frozenset(component))
|
|
73
|
+
return tuple(components)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass(frozen=True)
|
|
77
|
+
class ITSComponentReduction:
|
|
78
|
+
"""A seed-conditioned active ITS problem and its exact lifting map."""
|
|
79
|
+
|
|
80
|
+
lgp: tuple[LabeledGraph, LabeledGraph]
|
|
81
|
+
reference_mapping: tuple[int, ...]
|
|
82
|
+
reactant_atoms: tuple[int, ...]
|
|
83
|
+
product_atoms: tuple[int, ...]
|
|
84
|
+
inactive_atoms: tuple[int, ...]
|
|
85
|
+
fixed_mapping: tuple[int, ...]
|
|
86
|
+
changed_atoms: tuple[int, ...]
|
|
87
|
+
heavy_distance: float
|
|
88
|
+
hydrogen_distance: int
|
|
89
|
+
scope: str = "reference_active_its_component_space"
|
|
90
|
+
|
|
91
|
+
def lift(self, core_mapping) -> list[int]:
|
|
92
|
+
"""Lift one reduced mapping while retaining the seed outside the ITS core."""
|
|
93
|
+
core_mapping = tuple(int(image) for image in core_mapping)
|
|
94
|
+
size = len(self.reactant_atoms)
|
|
95
|
+
if sorted(core_mapping) != list(range(size)):
|
|
96
|
+
raise ValueError("core_mapping must be a complete reduced permutation")
|
|
97
|
+
lifted = list(self.fixed_mapping)
|
|
98
|
+
for position, reactant_atom in enumerate(self.reactant_atoms):
|
|
99
|
+
lifted[reactant_atom] = self.product_atoms[core_mapping[position]]
|
|
100
|
+
return lifted
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def reduce_to_reference_its_components(
|
|
104
|
+
lgp,
|
|
105
|
+
reference_mapping,
|
|
106
|
+
*,
|
|
107
|
+
binary=False,
|
|
108
|
+
tolerance=1e-9,
|
|
109
|
+
) -> ITSComponentReduction:
|
|
110
|
+
"""Keep union-ITS components changed by a trusted reference mapping.
|
|
111
|
+
|
|
112
|
+
Hydrogens remain implicit. Per-heavy-atom H counts are carried in the
|
|
113
|
+
reduced graph metadata and H-count changes participate in reaction-center
|
|
114
|
+
selection. ``hydrogen_distance`` is the L1 H-count discrepancy induced by
|
|
115
|
+
the seed heavy-atom mapping; it is the minimum explicit H--parent bond-edit
|
|
116
|
+
contribution when the two hydrogen inventories are balanced.
|
|
117
|
+
"""
|
|
118
|
+
reactant, reactant_elements = _adjacency_and_elements(lgp[0], binary)
|
|
119
|
+
product, product_elements = _adjacency_and_elements(lgp[1], binary)
|
|
120
|
+
if reactant.shape != product.shape:
|
|
121
|
+
raise ValueError("reactant and product must contain the same atom count")
|
|
122
|
+
size = reactant.shape[0]
|
|
123
|
+
mapping = tuple(int(image) for image in reference_mapping)
|
|
124
|
+
if sorted(mapping) != list(range(size)):
|
|
125
|
+
raise ValueError("reference_mapping must be a complete permutation")
|
|
126
|
+
if any(
|
|
127
|
+
reactant_elements[atom] != product_elements[image]
|
|
128
|
+
for atom, image in enumerate(mapping)
|
|
129
|
+
):
|
|
130
|
+
raise ValueError("reference_mapping must preserve atom types")
|
|
131
|
+
|
|
132
|
+
images = np.asarray(mapping, dtype=int)
|
|
133
|
+
mapped_product = product[images[:, None], images[None, :]]
|
|
134
|
+
changed = set()
|
|
135
|
+
for left in range(size):
|
|
136
|
+
for right in range(left + 1, size):
|
|
137
|
+
if not math.isclose(
|
|
138
|
+
float(reactant[left, right]),
|
|
139
|
+
float(mapped_product[left, right]),
|
|
140
|
+
abs_tol=tolerance,
|
|
141
|
+
rel_tol=0.0,
|
|
142
|
+
):
|
|
143
|
+
changed.update((left, right))
|
|
144
|
+
|
|
145
|
+
h_r = _node_property(lgp[0], "hcounts")
|
|
146
|
+
h_p = _node_property(lgp[1], "hcounts")
|
|
147
|
+
charge_r = _node_property(lgp[0], "charges")
|
|
148
|
+
charge_p = _node_property(lgp[1], "charges")
|
|
149
|
+
hydrogen_distance = 0
|
|
150
|
+
for atom, image in enumerate(mapping):
|
|
151
|
+
h_delta = abs(int(h_r[atom]) - int(h_p[image]))
|
|
152
|
+
hydrogen_distance += h_delta
|
|
153
|
+
if h_delta or charge_r[atom] != charge_p[image]:
|
|
154
|
+
changed.add(atom)
|
|
155
|
+
|
|
156
|
+
components = _union_components(reactant, mapped_product)
|
|
157
|
+
active = (
|
|
158
|
+
set().union(*(component for component in components if component & changed))
|
|
159
|
+
if changed
|
|
160
|
+
else set()
|
|
161
|
+
)
|
|
162
|
+
reactant_atoms = tuple(sorted(active))
|
|
163
|
+
product_atoms = tuple(mapping[atom] for atom in reactant_atoms)
|
|
164
|
+
inactive_atoms = tuple(atom for atom in range(size) if atom not in active)
|
|
165
|
+
reduced_pair = (
|
|
166
|
+
_induced_labeled_graph(lgp[0], reactant_atoms),
|
|
167
|
+
_induced_labeled_graph(lgp[1], product_atoms),
|
|
168
|
+
)
|
|
169
|
+
heavy_distance = 0.5 * float(np.abs(reactant - mapped_product).sum())
|
|
170
|
+
return ITSComponentReduction(
|
|
171
|
+
lgp=reduced_pair,
|
|
172
|
+
reference_mapping=tuple(range(len(reactant_atoms))),
|
|
173
|
+
reactant_atoms=reactant_atoms,
|
|
174
|
+
product_atoms=product_atoms,
|
|
175
|
+
inactive_atoms=inactive_atoms,
|
|
176
|
+
fixed_mapping=mapping,
|
|
177
|
+
changed_atoms=tuple(sorted(changed)),
|
|
178
|
+
heavy_distance=heavy_distance,
|
|
179
|
+
hydrogen_distance=hydrogen_distance,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
__all__ = ["ITSComponentReduction", "reduce_to_reference_its_components"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Full-space distance enumeration, replay certificates, and small-graph exhaustive search."""
|