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,1025 @@
|
|
|
1
|
+
"""Complete atom-compatible enumeration at an exact chemical distance."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import time
|
|
7
|
+
from numbers import Real
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from ....graph.automorphism import bounded_automorphism_permutations
|
|
12
|
+
from .bounds import (
|
|
13
|
+
ResidualBondMass as _ResidualBondMass,
|
|
14
|
+
atom_profile_costs as _atom_profile_costs,
|
|
15
|
+
assignment_edge_lower_bounds as _assignment_edge_lower_bounds,
|
|
16
|
+
blocked_assignment_extreme as _blocked_assignment_extreme,
|
|
17
|
+
conditioned_profile_assignment_lower_bound as _conditioned_profile_bound,
|
|
18
|
+
element_blocks as _element_blocks,
|
|
19
|
+
reaction_center_order as _reaction_center_order,
|
|
20
|
+
)
|
|
21
|
+
from .inputs import (
|
|
22
|
+
prepare_distance_problem, validate_distance_options, normalize_initial_mapping,
|
|
23
|
+
initial_distance_domains, supports_strict_improvement,
|
|
24
|
+
)
|
|
25
|
+
from .records import (
|
|
26
|
+
CertificateVerificationError,
|
|
27
|
+
DistanceEnumerationCertificate,
|
|
28
|
+
DistanceEnumerationResult,
|
|
29
|
+
DistanceTarget,
|
|
30
|
+
EnumerationStatus,
|
|
31
|
+
ExactEnumerationLimitError,
|
|
32
|
+
_make_certificate,
|
|
33
|
+
_mappings_sha256,
|
|
34
|
+
normalize_distance_target,
|
|
35
|
+
)
|
|
36
|
+
from .records import _certificate_sha256 as _certificate_sha256 # noqa: F401
|
|
37
|
+
from .verify import verify_distance_enumeration_certificate
|
|
38
|
+
from ..symmetry import (
|
|
39
|
+
_MAX_STABILIZER_GENERATORS,
|
|
40
|
+
_MAX_SCHREIER_WORK,
|
|
41
|
+
largest_cyclic_subgroup,
|
|
42
|
+
orbital_candidate_witnesses,
|
|
43
|
+
permutation_group_order,
|
|
44
|
+
point_stabilizer_generators_checked,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_MAX_CROSS_COST_CACHE_BYTES = 32 * 1024**2
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _mapping_cost(reactant, product, mapping) -> float:
|
|
52
|
+
images = np.asarray(mapping, dtype=int)
|
|
53
|
+
mapped_product = product[images[:, None], images[None, :]]
|
|
54
|
+
return 0.5 * float(np.abs(reactant - mapped_product).sum())
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def enumerate_distance_mappings( # noqa: C901
|
|
58
|
+
lgp,
|
|
59
|
+
*,
|
|
60
|
+
CD: str | Real = "minimal",
|
|
61
|
+
binary: bool = True,
|
|
62
|
+
max_bijections: int | None = 1_000_000,
|
|
63
|
+
tolerance: float = 1e-9,
|
|
64
|
+
time_limit_seconds: float | None = None,
|
|
65
|
+
certify: bool = False,
|
|
66
|
+
symmetry_pruning: bool = False,
|
|
67
|
+
max_symmetry_automorphisms: int = 256,
|
|
68
|
+
symmetry_timeout_seconds: float = 0.25,
|
|
69
|
+
symmetry_max_search_nodes: int = 10_000,
|
|
70
|
+
expand_symmetry: bool = False,
|
|
71
|
+
initial_mapping=None,
|
|
72
|
+
max_mappings: int | None = None,
|
|
73
|
+
assignment_lower_bound: bool = True,
|
|
74
|
+
assignment_upper_bound: bool = True,
|
|
75
|
+
atom_profile_pruning: bool = True,
|
|
76
|
+
seed_center_order: bool = True,
|
|
77
|
+
symmetry_node_properties=(),
|
|
78
|
+
fixed_mapping=None,
|
|
79
|
+
collect_mappings: bool = True,
|
|
80
|
+
mapping_callback=None,
|
|
81
|
+
compute_minimum_cost: bool = True,
|
|
82
|
+
_optimization_only: bool = False,
|
|
83
|
+
) -> DistanceEnumerationResult:
|
|
84
|
+
"""Enumerate the complete atom-compatible space at an exact CD.
|
|
85
|
+
|
|
86
|
+
``CD='minimal'`` returns every globally minimal labeled mapping. A numeric
|
|
87
|
+
``CD`` returns every labeled mapping whose chemical distance equals that
|
|
88
|
+
value within ``tolerance``. The pre-search factorial cap makes failure
|
|
89
|
+
explicit instead of returning a silently incomplete enumeration. With
|
|
90
|
+
``symmetry_pruning=True``, verified product-graph automorphisms remove
|
|
91
|
+
non-lex-leading mappings and their witnesses become part of the replayable
|
|
92
|
+
tree cover. Automorphism discovery is bounded, so multiple representatives
|
|
93
|
+
of a full product-automorphism orbit may remain. ``collect_mappings=False``
|
|
94
|
+
keeps only the exact selected count and optionally streams each final
|
|
95
|
+
mapping to ``mapping_callback``. Streaming a minimal shell uses a separate
|
|
96
|
+
optimization pass so provisional incumbents are never emitted.
|
|
97
|
+
``compute_minimum_cost=False`` skips the otherwise separate minimization
|
|
98
|
+
pass for a numeric shell; this is useful when only that exact shell is
|
|
99
|
+
required and leaves ``result.minimum_cost`` unset.
|
|
100
|
+
Numeric uncertified searches also remove atom pairs whose element-blocked
|
|
101
|
+
incident-bond profiles alone exceed the target.
|
|
102
|
+
``fixed_mapping`` may pin an injective, atom-compatible subset of reactant
|
|
103
|
+
atoms to product atoms. The returned shell is complete within that
|
|
104
|
+
conditioned subspace, and product symmetry is restricted to the pointwise
|
|
105
|
+
stabilizer of the fixed product images.
|
|
106
|
+
``expand_symmetry=True`` restricts pruning to one fully enumerated cyclic
|
|
107
|
+
verified subgroup and streams every group image of each representative;
|
|
108
|
+
this recovers the complete labeled shell exactly once.
|
|
109
|
+
"""
|
|
110
|
+
target = normalize_distance_target(CD)
|
|
111
|
+
started = time.perf_counter()
|
|
112
|
+
validate_distance_options(
|
|
113
|
+
max_bijections=max_bijections,
|
|
114
|
+
tolerance=tolerance,
|
|
115
|
+
time_limit_seconds=time_limit_seconds,
|
|
116
|
+
symmetry_pruning=symmetry_pruning,
|
|
117
|
+
expand_symmetry=expand_symmetry,
|
|
118
|
+
certify=certify,
|
|
119
|
+
max_symmetry_automorphisms=max_symmetry_automorphisms,
|
|
120
|
+
symmetry_timeout_seconds=symmetry_timeout_seconds,
|
|
121
|
+
symmetry_max_search_nodes=symmetry_max_search_nodes,
|
|
122
|
+
max_mappings=max_mappings,
|
|
123
|
+
assignment_lower_bound=assignment_lower_bound,
|
|
124
|
+
assignment_upper_bound=assignment_upper_bound,
|
|
125
|
+
atom_profile_pruning=atom_profile_pruning,
|
|
126
|
+
seed_center_order=seed_center_order,
|
|
127
|
+
symmetry_node_properties=symmetry_node_properties,
|
|
128
|
+
collect_mappings=collect_mappings,
|
|
129
|
+
mapping_callback=mapping_callback,
|
|
130
|
+
compute_minimum_cost=compute_minimum_cost,
|
|
131
|
+
_optimization_only=_optimization_only,
|
|
132
|
+
)
|
|
133
|
+
symmetry_node_properties = tuple(str(name) for name in symmetry_node_properties)
|
|
134
|
+
(
|
|
135
|
+
reactant,
|
|
136
|
+
product,
|
|
137
|
+
reactant_elements,
|
|
138
|
+
product_elements,
|
|
139
|
+
reactant_counts,
|
|
140
|
+
product_by_element,
|
|
141
|
+
atom_count,
|
|
142
|
+
fixed,
|
|
143
|
+
fixed_images,
|
|
144
|
+
total_bijections,
|
|
145
|
+
maximum_cost_upper_bound,
|
|
146
|
+
input_sha256,
|
|
147
|
+
) = prepare_distance_problem(lgp, binary, fixed_mapping, certify, expand_symmetry)
|
|
148
|
+
|
|
149
|
+
if target != "minimal" and target > maximum_cost_upper_bound + tolerance:
|
|
150
|
+
certificate = None
|
|
151
|
+
if certify:
|
|
152
|
+
certificate = _make_certificate(
|
|
153
|
+
schema_version=1,
|
|
154
|
+
kind="maximum_cost_upper_bound",
|
|
155
|
+
input_sha256=input_sha256,
|
|
156
|
+
target=target,
|
|
157
|
+
binary=bool(binary),
|
|
158
|
+
tolerance=float(tolerance),
|
|
159
|
+
reactant_order=(),
|
|
160
|
+
terminal_prefixes=(),
|
|
161
|
+
frontier_prefixes=(),
|
|
162
|
+
total_bijections=total_bijections,
|
|
163
|
+
maximum_cost_upper_bound=maximum_cost_upper_bound,
|
|
164
|
+
selected_mapping_count=0,
|
|
165
|
+
selected_mappings_sha256=_mappings_sha256([]),
|
|
166
|
+
cost=None,
|
|
167
|
+
pruning_limit=None,
|
|
168
|
+
status="no_solutions",
|
|
169
|
+
)
|
|
170
|
+
return DistanceEnumerationResult(
|
|
171
|
+
target=target,
|
|
172
|
+
cost=None,
|
|
173
|
+
minimum_cost=None,
|
|
174
|
+
mappings=[],
|
|
175
|
+
distances=[],
|
|
176
|
+
total_bijections=total_bijections,
|
|
177
|
+
maximum_cost_upper_bound=maximum_cost_upper_bound,
|
|
178
|
+
visited_leaves=0,
|
|
179
|
+
pruned_branches=1,
|
|
180
|
+
elapsed_seconds=time.perf_counter() - started,
|
|
181
|
+
status="no_solutions",
|
|
182
|
+
complete=True,
|
|
183
|
+
certificate=certificate,
|
|
184
|
+
)
|
|
185
|
+
if max_bijections is not None and total_bijections > max_bijections:
|
|
186
|
+
raise ExactEnumerationLimitError(
|
|
187
|
+
f"{total_bijections:,} atom-compatible bijections exceed "
|
|
188
|
+
f"max_bijections={max_bijections:,}"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
proven_minimum_cost = None
|
|
192
|
+
fixed_minimum_cost = None
|
|
193
|
+
optimization = None
|
|
194
|
+
optimization_seconds = 0.0
|
|
195
|
+
needs_optimization_pass = (target != "minimal" and compute_minimum_cost) or (
|
|
196
|
+
target == "minimal" and (not collect_mappings or mapping_callback is not None)
|
|
197
|
+
)
|
|
198
|
+
if needs_optimization_pass and not _optimization_only:
|
|
199
|
+
optimization_started = time.perf_counter()
|
|
200
|
+
optimization = enumerate_distance_mappings(
|
|
201
|
+
lgp,
|
|
202
|
+
CD="minimal",
|
|
203
|
+
binary=binary,
|
|
204
|
+
max_bijections=max_bijections,
|
|
205
|
+
tolerance=tolerance,
|
|
206
|
+
time_limit_seconds=time_limit_seconds,
|
|
207
|
+
certify=False,
|
|
208
|
+
symmetry_pruning=symmetry_pruning,
|
|
209
|
+
max_symmetry_automorphisms=max_symmetry_automorphisms,
|
|
210
|
+
symmetry_timeout_seconds=symmetry_timeout_seconds,
|
|
211
|
+
symmetry_max_search_nodes=symmetry_max_search_nodes,
|
|
212
|
+
expand_symmetry=expand_symmetry,
|
|
213
|
+
initial_mapping=initial_mapping,
|
|
214
|
+
max_mappings=None,
|
|
215
|
+
assignment_lower_bound=assignment_lower_bound,
|
|
216
|
+
assignment_upper_bound=assignment_upper_bound,
|
|
217
|
+
atom_profile_pruning=atom_profile_pruning,
|
|
218
|
+
seed_center_order=seed_center_order,
|
|
219
|
+
symmetry_node_properties=symmetry_node_properties,
|
|
220
|
+
fixed_mapping=fixed,
|
|
221
|
+
collect_mappings=True,
|
|
222
|
+
mapping_callback=None,
|
|
223
|
+
compute_minimum_cost=True,
|
|
224
|
+
_optimization_only=True,
|
|
225
|
+
)
|
|
226
|
+
optimization_seconds = time.perf_counter() - optimization_started
|
|
227
|
+
if optimization.complete:
|
|
228
|
+
proven_minimum_cost = optimization.minimum_cost
|
|
229
|
+
if target == "minimal":
|
|
230
|
+
fixed_minimum_cost = proven_minimum_cost
|
|
231
|
+
if optimization.mappings:
|
|
232
|
+
initial_mapping = optimization.mappings[0]
|
|
233
|
+
elif target == "minimal":
|
|
234
|
+
# Streaming must never expose provisional incumbents. If the
|
|
235
|
+
# prerequisite optimization did not prove the minimum, return its
|
|
236
|
+
# explicit incomplete result without starting the callback pass.
|
|
237
|
+
return optimization
|
|
238
|
+
|
|
239
|
+
preferred_mapping = normalize_initial_mapping(
|
|
240
|
+
initial_mapping, atom_count, reactant_elements, product_elements, fixed
|
|
241
|
+
)
|
|
242
|
+
reactant_order = _reaction_center_order(
|
|
243
|
+
reactant,
|
|
244
|
+
product,
|
|
245
|
+
reactant_elements,
|
|
246
|
+
reactant_counts,
|
|
247
|
+
preferred_mapping if seed_center_order else None,
|
|
248
|
+
)
|
|
249
|
+
reactant_order = [atom for atom in reactant_order if atom not in fixed]
|
|
250
|
+
profile_pair_costs = None
|
|
251
|
+
if atom_profile_pruning and not certify:
|
|
252
|
+
profile_pair_costs = _atom_profile_costs(
|
|
253
|
+
reactant, product, reactant_elements, product_elements
|
|
254
|
+
)
|
|
255
|
+
profile_edge_bounds = profile_pair_costs
|
|
256
|
+
profile_domain_limit = float(target) if target != "minimal" else fixed_minimum_cost
|
|
257
|
+
if profile_domain_limit is None and preferred_mapping is not None:
|
|
258
|
+
profile_domain_limit = _mapping_cost(reactant, product, preferred_mapping)
|
|
259
|
+
if profile_pair_costs is not None and profile_domain_limit is not None:
|
|
260
|
+
finite_profiles = profile_pair_costs[np.isfinite(profile_pair_costs)]
|
|
261
|
+
# Quarter-unit weights keep alternating-path arithmetic exact here.
|
|
262
|
+
# General floats retain the existing tolerance-aware individual bound.
|
|
263
|
+
if (
|
|
264
|
+
maximum_cost_upper_bound < 2**40
|
|
265
|
+
and np.equal(finite_profiles * 4, np.rint(finite_profiles * 4)).all()
|
|
266
|
+
):
|
|
267
|
+
profile_edge_bounds = _assignment_edge_lower_bounds(
|
|
268
|
+
profile_pair_costs, reactant_elements, product_elements
|
|
269
|
+
)
|
|
270
|
+
domains, profile_domain_pruned = initial_distance_domains(
|
|
271
|
+
reactant_order, reactant_elements, product_by_element, fixed_images,
|
|
272
|
+
profile_edge_bounds, profile_domain_limit, tolerance,
|
|
273
|
+
)
|
|
274
|
+
# At an attained global row-profile bound every row attains its own bound.
|
|
275
|
+
# Zero-cost rows must then match exactly. Restrict this propagation to exact
|
|
276
|
+
# quarter-unit costs with tolerance smaller than the objective quantum.
|
|
277
|
+
tight_profile_rows = bool(
|
|
278
|
+
profile_pair_costs is not None
|
|
279
|
+
and profile_domain_limit is not None
|
|
280
|
+
and maximum_cost_upper_bound < 2**40
|
|
281
|
+
and tolerance < 0.25
|
|
282
|
+
and np.equal(reactant * 2, np.rint(reactant * 2)).all()
|
|
283
|
+
and np.equal(product * 2, np.rint(product * 2)).all()
|
|
284
|
+
and _blocked_assignment_extreme(
|
|
285
|
+
profile_pair_costs,
|
|
286
|
+
range(atom_count),
|
|
287
|
+
range(atom_count),
|
|
288
|
+
reactant_elements,
|
|
289
|
+
product_elements,
|
|
290
|
+
)
|
|
291
|
+
== profile_domain_limit
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
dynamic_row_order = profile_pair_costs is not None and not certify
|
|
295
|
+
|
|
296
|
+
mapping_scope = (
|
|
297
|
+
"fixed_assignment_subspace"
|
|
298
|
+
if fixed
|
|
299
|
+
else "complete_atom_compatible_assignment_space"
|
|
300
|
+
)
|
|
301
|
+
symmetry_permutations = (tuple(range(atom_count)),)
|
|
302
|
+
symmetry_search_complete = True
|
|
303
|
+
symmetry_group_order = 1
|
|
304
|
+
symmetry_quotient_complete = True
|
|
305
|
+
if symmetry_pruning:
|
|
306
|
+
symmetry_permutations, symmetry_search_complete = (
|
|
307
|
+
bounded_automorphism_permutations(
|
|
308
|
+
lgp[1],
|
|
309
|
+
binary,
|
|
310
|
+
limit=int(max_symmetry_automorphisms),
|
|
311
|
+
timeout_seconds=float(symmetry_timeout_seconds),
|
|
312
|
+
max_search_nodes=int(symmetry_max_search_nodes),
|
|
313
|
+
node_properties=symmetry_node_properties,
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
root_generators = tuple(symmetry_permutations[1:])
|
|
317
|
+
for image in sorted(fixed_images):
|
|
318
|
+
root_generators, stabilizer_complete = point_stabilizer_generators_checked(
|
|
319
|
+
root_generators, image
|
|
320
|
+
)
|
|
321
|
+
symmetry_quotient_complete &= stabilizer_complete
|
|
322
|
+
if expand_symmetry:
|
|
323
|
+
symmetry_permutations = largest_cyclic_subgroup(
|
|
324
|
+
symmetry_permutations,
|
|
325
|
+
max_order=max_symmetry_automorphisms,
|
|
326
|
+
)
|
|
327
|
+
root_generators = tuple(symmetry_permutations[1:])
|
|
328
|
+
symmetry_group_order = len(symmetry_permutations)
|
|
329
|
+
else:
|
|
330
|
+
mapping_scope = (
|
|
331
|
+
"verified_product_automorphism_lex_leaders_within_"
|
|
332
|
+
"fixed_assignment_subspace"
|
|
333
|
+
if fixed
|
|
334
|
+
else "verified_product_automorphism_lex_leaders"
|
|
335
|
+
)
|
|
336
|
+
symmetry_group_order = permutation_group_order(root_generators)
|
|
337
|
+
if symmetry_group_order is None:
|
|
338
|
+
symmetry_quotient_complete = False
|
|
339
|
+
else:
|
|
340
|
+
root_generators = ()
|
|
341
|
+
mapping = [-1] * atom_count
|
|
342
|
+
used_products = [False] * atom_count
|
|
343
|
+
for atom, image in fixed.items():
|
|
344
|
+
mapping[atom] = image
|
|
345
|
+
used_products[image] = True
|
|
346
|
+
mappings: list[list[int]] = []
|
|
347
|
+
distances: list[float] = []
|
|
348
|
+
selected_mapping_count = 0
|
|
349
|
+
if preferred_mapping is None:
|
|
350
|
+
preferred_mapping = [-1] * atom_count
|
|
351
|
+
for atom, image in fixed.items():
|
|
352
|
+
preferred_mapping[atom] = image
|
|
353
|
+
reactant_blocks = _element_blocks(reactant_order, reactant_elements)
|
|
354
|
+
for element, atoms in reactant_blocks.items():
|
|
355
|
+
available_images = [
|
|
356
|
+
image
|
|
357
|
+
for image in product_by_element[element]
|
|
358
|
+
if image not in fixed_images
|
|
359
|
+
]
|
|
360
|
+
for atom, image in zip(atoms, available_images):
|
|
361
|
+
preferred_mapping[atom] = image
|
|
362
|
+
best_cost = (
|
|
363
|
+
fixed_minimum_cost
|
|
364
|
+
if fixed_minimum_cost is not None
|
|
365
|
+
else (
|
|
366
|
+
_mapping_cost(reactant, product, preferred_mapping)
|
|
367
|
+
if target == "minimal"
|
|
368
|
+
else math.inf
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
if _optimization_only and target == "minimal":
|
|
372
|
+
mappings = [list(preferred_mapping)]
|
|
373
|
+
distances = [float(best_cost)]
|
|
374
|
+
selected_mapping_count = 1
|
|
375
|
+
profile_compatible = None
|
|
376
|
+
tight_profile_pruned = 0
|
|
377
|
+
if tight_profile_rows:
|
|
378
|
+
profile_compatible = np.zeros((atom_count, atom_count), dtype=bool)
|
|
379
|
+
for atom, images in domains.items():
|
|
380
|
+
profile_compatible[atom, list(images)] = True
|
|
381
|
+
|
|
382
|
+
def tighten_profiles(atom, image):
|
|
383
|
+
allowed = (profile_pair_costs != 0) | (
|
|
384
|
+
reactant[:, atom, None] == product[:, image][None, :]
|
|
385
|
+
)
|
|
386
|
+
if profile_pair_costs[atom, image] == 0:
|
|
387
|
+
allowed &= reactant[atom, :, None] == product[image, :][None, :]
|
|
388
|
+
# Each entry is removed at most once on the live DFS path, so retained
|
|
389
|
+
# flat indices use O(n^2) space rather than one dense mask per level.
|
|
390
|
+
removed = np.flatnonzero(profile_compatible & ~allowed)
|
|
391
|
+
profile_compatible.flat[removed] = False
|
|
392
|
+
return removed
|
|
393
|
+
|
|
394
|
+
if tight_profile_rows:
|
|
395
|
+
for atom, image in fixed.items():
|
|
396
|
+
tighten_profiles(atom, image)
|
|
397
|
+
cross_costs = np.zeros((atom_count, atom_count), dtype=float)
|
|
398
|
+
for atom, image in fixed.items():
|
|
399
|
+
delta = 0.5 * np.abs(reactant[:, atom, None] - product[:, image][None, :])
|
|
400
|
+
delta += 0.5 * np.abs(reactant[atom, :, None] - product[image, :][None, :])
|
|
401
|
+
cross_costs += delta
|
|
402
|
+
remaining_product_atoms = [
|
|
403
|
+
image for image in range(atom_count) if image not in fixed_images
|
|
404
|
+
]
|
|
405
|
+
residual_bond_mass = _ResidualBondMass(
|
|
406
|
+
reactant,
|
|
407
|
+
product,
|
|
408
|
+
reactant_order,
|
|
409
|
+
remaining_product_atoms,
|
|
410
|
+
)
|
|
411
|
+
# A dynamic row order needs reversible reactant mass, rather than suffix
|
|
412
|
+
# sums tied to the original order. Reuse the matrix-side removal tracker.
|
|
413
|
+
reactant_bond_mass = (
|
|
414
|
+
_ResidualBondMass(reactant, reactant, reactant_order, reactant_order)
|
|
415
|
+
if dynamic_row_order
|
|
416
|
+
else None
|
|
417
|
+
)
|
|
418
|
+
initial_order_rank = {
|
|
419
|
+
atom: position for position, atom in enumerate(reactant_order)
|
|
420
|
+
}
|
|
421
|
+
reactant_neighbors = (reactant != 0) | (reactant.T != 0)
|
|
422
|
+
assigned_neighbors = np.zeros(atom_count, dtype=int)
|
|
423
|
+
for atom in fixed:
|
|
424
|
+
assigned_neighbors += reactant_neighbors[:, atom]
|
|
425
|
+
fixed_atoms = sorted(fixed)
|
|
426
|
+
fixed_product_atoms = [fixed[atom] for atom in fixed_atoms]
|
|
427
|
+
initial_committed_cost = (
|
|
428
|
+
0.5
|
|
429
|
+
* float(
|
|
430
|
+
np.abs(
|
|
431
|
+
reactant[np.ix_(fixed_atoms, fixed_atoms)]
|
|
432
|
+
- product[np.ix_(fixed_product_atoms, fixed_product_atoms)]
|
|
433
|
+
).sum()
|
|
434
|
+
)
|
|
435
|
+
if fixed_atoms
|
|
436
|
+
else 0.0
|
|
437
|
+
)
|
|
438
|
+
search_count = len(reactant_order)
|
|
439
|
+
visited_leaves = 0
|
|
440
|
+
pruned_branches = 0
|
|
441
|
+
deadline = (
|
|
442
|
+
None if time_limit_seconds is None else started + float(time_limit_seconds)
|
|
443
|
+
)
|
|
444
|
+
timed_out = False
|
|
445
|
+
truncation_reason = None
|
|
446
|
+
terminal_prefixes: list[tuple[int, ...]] = []
|
|
447
|
+
frontier_prefixes: list[tuple[int, ...]] = []
|
|
448
|
+
symmetry_prefixes: list[tuple[tuple[int, ...], tuple[int, ...]]] = []
|
|
449
|
+
lower_bound_prefixes: list[tuple[int, ...]] = []
|
|
450
|
+
upper_bound_prefixes: list[tuple[int, ...]] = []
|
|
451
|
+
visited_nodes = 0
|
|
452
|
+
symmetry_pruned_branches = 0
|
|
453
|
+
lower_bound_pruned_branches = 0
|
|
454
|
+
upper_bound_pruned_branches = 0
|
|
455
|
+
stopped_with_unexplored_work = False
|
|
456
|
+
# Half-integer bond weights give exact quarter-unit DFS costs and bounds
|
|
457
|
+
# in this conservative range. Only the proof pass can discard ties.
|
|
458
|
+
# Arbitrary floats retain tolerance-aware pruning; the shell stays inclusive.
|
|
459
|
+
strict_improvement = supports_strict_improvement(
|
|
460
|
+
_optimization_only, target, certify, maximum_cost_upper_bound, reactant, product
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
def current_prefix(depth: int) -> tuple[int, ...]:
|
|
464
|
+
return tuple(mapping[atom] for atom in reactant_order[:depth])
|
|
465
|
+
|
|
466
|
+
profile_bound_calls = 0
|
|
467
|
+
profile_bound_pruned = 0
|
|
468
|
+
conditioned_bound_calls = 0
|
|
469
|
+
conditioned_bound_pruned = 0
|
|
470
|
+
|
|
471
|
+
def remaining_assignment_interval(
|
|
472
|
+
depth: int, committed_cost: float, profile_cost: float
|
|
473
|
+
):
|
|
474
|
+
nonlocal profile_bound_calls, profile_bound_pruned
|
|
475
|
+
nonlocal conditioned_bound_calls, conditioned_bound_pruned
|
|
476
|
+
remaining_atoms = reactant_order[depth:]
|
|
477
|
+
branch_costs = None
|
|
478
|
+
if not remaining_atoms:
|
|
479
|
+
return 0.0, 0.0, None
|
|
480
|
+
remaining_products = [
|
|
481
|
+
product_atom
|
|
482
|
+
for product_atom in range(atom_count)
|
|
483
|
+
if not used_products[product_atom]
|
|
484
|
+
]
|
|
485
|
+
cross_lower = _blocked_assignment_extreme(
|
|
486
|
+
cross_costs,
|
|
487
|
+
remaining_atoms,
|
|
488
|
+
remaining_products,
|
|
489
|
+
reactant_elements,
|
|
490
|
+
product_elements,
|
|
491
|
+
)
|
|
492
|
+
if reactant_bond_mass is None:
|
|
493
|
+
internal_lower, internal_upper = residual_bond_mass.interval(depth)
|
|
494
|
+
else:
|
|
495
|
+
internal_lower = 0.5 * abs(
|
|
496
|
+
reactant_bond_mass.product_sum - residual_bond_mass.product_sum
|
|
497
|
+
)
|
|
498
|
+
internal_upper = 0.5 * (
|
|
499
|
+
reactant_bond_mass.product_abs_sum + residual_bond_mass.product_abs_sum
|
|
500
|
+
)
|
|
501
|
+
lower = cross_lower + internal_lower
|
|
502
|
+
if tight_profile_rows:
|
|
503
|
+
# The attained root bound remains valid for every prefix. Edge
|
|
504
|
+
# filtering and zero-row propagation already constrain this face;
|
|
505
|
+
# avoid repeatedly recomputing its uninformative profile relaxations.
|
|
506
|
+
lower = max(lower, profile_domain_limit - committed_cost)
|
|
507
|
+
limit = float(best_cost if target == "minimal" else target)
|
|
508
|
+
base_pruned = committed_cost + lower > limit + tolerance or (
|
|
509
|
+
strict_improvement and committed_cost + lower >= limit
|
|
510
|
+
)
|
|
511
|
+
if (
|
|
512
|
+
profile_pair_costs is not None
|
|
513
|
+
and not base_pruned
|
|
514
|
+
and not tight_profile_rows
|
|
515
|
+
):
|
|
516
|
+
# The prefix's half-row profile sum plus a remaining LAP bounds
|
|
517
|
+
# total CD. Combine by max, avoiding overlapping edge costs.
|
|
518
|
+
profile_bound_calls += 1
|
|
519
|
+
profile_lower = profile_cost + _blocked_assignment_extreme(
|
|
520
|
+
profile_pair_costs,
|
|
521
|
+
remaining_atoms,
|
|
522
|
+
remaining_products,
|
|
523
|
+
reactant_elements,
|
|
524
|
+
product_elements,
|
|
525
|
+
)
|
|
526
|
+
lower = max(lower, profile_lower - committed_cost)
|
|
527
|
+
if profile_lower > limit + tolerance or (
|
|
528
|
+
strict_improvement and profile_lower >= limit
|
|
529
|
+
):
|
|
530
|
+
profile_bound_pruned += 1
|
|
531
|
+
# Condition the residual profiles only after cheaper bounds survive.
|
|
532
|
+
# Limit the cubic profile work to modest remaining subproblems.
|
|
533
|
+
already_pruned = committed_cost + lower > limit + tolerance or (
|
|
534
|
+
strict_improvement and committed_cost + lower >= limit
|
|
535
|
+
)
|
|
536
|
+
if (
|
|
537
|
+
profile_pair_costs is not None
|
|
538
|
+
and not tight_profile_rows
|
|
539
|
+
and not already_pruned
|
|
540
|
+
and depth > 0
|
|
541
|
+
and len(remaining_atoms) <= 256
|
|
542
|
+
):
|
|
543
|
+
conditioned_bound_calls += 1
|
|
544
|
+
conditioned_lower, branch_costs = _conditioned_profile_bound(
|
|
545
|
+
reactant,
|
|
546
|
+
product,
|
|
547
|
+
cross_costs,
|
|
548
|
+
remaining_atoms,
|
|
549
|
+
remaining_products,
|
|
550
|
+
reactant_elements,
|
|
551
|
+
product_elements,
|
|
552
|
+
return_costs=True,
|
|
553
|
+
)
|
|
554
|
+
lower = max(lower, conditioned_lower)
|
|
555
|
+
if committed_cost + lower > limit + tolerance or (
|
|
556
|
+
strict_improvement and committed_cost + lower >= limit
|
|
557
|
+
):
|
|
558
|
+
conditioned_bound_pruned += 1
|
|
559
|
+
if target == "minimal" or not assignment_upper_bound:
|
|
560
|
+
return lower, None, branch_costs
|
|
561
|
+
cross_upper = _blocked_assignment_extreme(
|
|
562
|
+
cross_costs,
|
|
563
|
+
remaining_atoms,
|
|
564
|
+
remaining_products,
|
|
565
|
+
reactant_elements,
|
|
566
|
+
product_elements,
|
|
567
|
+
maximize=True,
|
|
568
|
+
)
|
|
569
|
+
return lower, cross_upper + internal_upper, branch_costs
|
|
570
|
+
|
|
571
|
+
cached_delta_bytes = 0
|
|
572
|
+
peak_cached_delta_bytes = 0
|
|
573
|
+
stabilizer_calls = 0
|
|
574
|
+
|
|
575
|
+
def cross_cost_delta(atom: int, image: int):
|
|
576
|
+
"""Build a prefix-cost delta; retain it only within the cache budget."""
|
|
577
|
+
delta = 0.5 * np.abs(reactant[:, atom, None] - product[:, image][None, :])
|
|
578
|
+
delta += 0.5 * np.abs(reactant[atom, :, None] - product[image, :][None, :])
|
|
579
|
+
return delta
|
|
580
|
+
|
|
581
|
+
def visit(
|
|
582
|
+
depth: int,
|
|
583
|
+
committed_cost: float,
|
|
584
|
+
stabilizer_generators=(),
|
|
585
|
+
pending_point=None,
|
|
586
|
+
profile_cost=0.0,
|
|
587
|
+
) -> None:
|
|
588
|
+
nonlocal best_cost, visited_leaves, pruned_branches, mappings, distances
|
|
589
|
+
nonlocal selected_mapping_count
|
|
590
|
+
nonlocal timed_out, truncation_reason, visited_nodes
|
|
591
|
+
nonlocal symmetry_pruned_branches, lower_bound_pruned_branches
|
|
592
|
+
nonlocal upper_bound_pruned_branches, stopped_with_unexplored_work
|
|
593
|
+
nonlocal symmetry_quotient_complete, stabilizer_calls
|
|
594
|
+
nonlocal cached_delta_bytes, peak_cached_delta_bytes
|
|
595
|
+
nonlocal tight_profile_pruned
|
|
596
|
+
|
|
597
|
+
visited_nodes += 1
|
|
598
|
+
|
|
599
|
+
if timed_out:
|
|
600
|
+
stopped_with_unexplored_work = True
|
|
601
|
+
if certify:
|
|
602
|
+
frontier_prefixes.append(current_prefix(depth))
|
|
603
|
+
return
|
|
604
|
+
if deadline is not None and time.perf_counter() >= deadline:
|
|
605
|
+
timed_out = True
|
|
606
|
+
truncation_reason = "time_limit"
|
|
607
|
+
if certify:
|
|
608
|
+
frontier_prefixes.append(current_prefix(depth))
|
|
609
|
+
return
|
|
610
|
+
|
|
611
|
+
limit = best_cost if target == "minimal" else target
|
|
612
|
+
if committed_cost > float(limit) + tolerance or (
|
|
613
|
+
strict_improvement and committed_cost >= float(limit)
|
|
614
|
+
):
|
|
615
|
+
pruned_branches += 1
|
|
616
|
+
if certify:
|
|
617
|
+
terminal_prefixes.append(current_prefix(depth))
|
|
618
|
+
return
|
|
619
|
+
branch_costs = None
|
|
620
|
+
if assignment_lower_bound and depth < search_count:
|
|
621
|
+
lower_bound, upper_bound, branch_costs = remaining_assignment_interval(
|
|
622
|
+
depth, committed_cost, profile_cost
|
|
623
|
+
)
|
|
624
|
+
if committed_cost + lower_bound > float(limit) + tolerance or (
|
|
625
|
+
strict_improvement and committed_cost + lower_bound >= float(limit)
|
|
626
|
+
):
|
|
627
|
+
lower_bound_pruned_branches += 1
|
|
628
|
+
if certify:
|
|
629
|
+
lower_bound_prefixes.append(current_prefix(depth))
|
|
630
|
+
return
|
|
631
|
+
if (
|
|
632
|
+
upper_bound is not None
|
|
633
|
+
and target != "minimal"
|
|
634
|
+
and committed_cost + upper_bound < float(target) - tolerance
|
|
635
|
+
):
|
|
636
|
+
upper_bound_pruned_branches += 1
|
|
637
|
+
if certify:
|
|
638
|
+
upper_bound_prefixes.append(current_prefix(depth))
|
|
639
|
+
return
|
|
640
|
+
if depth == search_count:
|
|
641
|
+
visited_leaves += 1
|
|
642
|
+
if certify:
|
|
643
|
+
terminal_prefixes.append(current_prefix(depth))
|
|
644
|
+
cost = float(committed_cost)
|
|
645
|
+
if cost < best_cost:
|
|
646
|
+
best_cost = cost
|
|
647
|
+
if target == "minimal":
|
|
648
|
+
if _optimization_only:
|
|
649
|
+
mappings = [list(mapping)]
|
|
650
|
+
distances = [cost]
|
|
651
|
+
else:
|
|
652
|
+
retained = [
|
|
653
|
+
(candidate, distance)
|
|
654
|
+
for candidate, distance in zip(mappings, distances)
|
|
655
|
+
if distance <= best_cost + tolerance
|
|
656
|
+
]
|
|
657
|
+
mappings = [candidate for candidate, _ in retained]
|
|
658
|
+
distances = [distance for _, distance in retained]
|
|
659
|
+
selected_mapping_count = len(mappings)
|
|
660
|
+
selected = (
|
|
661
|
+
cost <= best_cost + tolerance
|
|
662
|
+
if target == "minimal"
|
|
663
|
+
else math.isclose(cost, target, abs_tol=tolerance, rel_tol=0.0)
|
|
664
|
+
)
|
|
665
|
+
if selected and not _optimization_only:
|
|
666
|
+
expanded = (
|
|
667
|
+
(
|
|
668
|
+
[permutation[image] for image in mapping]
|
|
669
|
+
for permutation in symmetry_permutations
|
|
670
|
+
)
|
|
671
|
+
if expand_symmetry
|
|
672
|
+
else (list(mapping) for _ in range(1))
|
|
673
|
+
)
|
|
674
|
+
for expansion_index, selected_mapping in enumerate(expanded):
|
|
675
|
+
selected_mapping_count += 1
|
|
676
|
+
if collect_mappings:
|
|
677
|
+
mappings.append(selected_mapping)
|
|
678
|
+
distances.append(cost)
|
|
679
|
+
if mapping_callback is not None:
|
|
680
|
+
mapping_callback(selected_mapping, cost)
|
|
681
|
+
if (
|
|
682
|
+
max_mappings is not None
|
|
683
|
+
and selected_mapping_count >= max_mappings
|
|
684
|
+
):
|
|
685
|
+
timed_out = True
|
|
686
|
+
truncation_reason = "mapping_limit"
|
|
687
|
+
if (
|
|
688
|
+
expand_symmetry
|
|
689
|
+
and expansion_index + 1 < len(symmetry_permutations)
|
|
690
|
+
):
|
|
691
|
+
stopped_with_unexplored_work = True
|
|
692
|
+
break
|
|
693
|
+
return
|
|
694
|
+
|
|
695
|
+
# Bounds and leaves need no child stabilizer. Construct it only for
|
|
696
|
+
# a surviving internal node, where candidate orbit pruning uses it.
|
|
697
|
+
if symmetry_pruning and pending_point is not None:
|
|
698
|
+
# These generators come from the verified, deduplicated root set
|
|
699
|
+
# or a completed stabilizer construction. Preserve the same tuple
|
|
700
|
+
# when the subgroup already fixes this point, within helper budgets.
|
|
701
|
+
if not (
|
|
702
|
+
len(stabilizer_generators)
|
|
703
|
+
<= min(_MAX_STABILIZER_GENERATORS, _MAX_SCHREIER_WORK)
|
|
704
|
+
and all(
|
|
705
|
+
g[pending_point] == pending_point for g in stabilizer_generators
|
|
706
|
+
)
|
|
707
|
+
):
|
|
708
|
+
stabilizer_calls += 1
|
|
709
|
+
stabilizer_generators, stabilizer_complete = (
|
|
710
|
+
point_stabilizer_generators_checked(
|
|
711
|
+
stabilizer_generators, pending_point
|
|
712
|
+
)
|
|
713
|
+
)
|
|
714
|
+
symmetry_quotient_complete &= stabilizer_complete
|
|
715
|
+
|
|
716
|
+
selected_position = depth
|
|
717
|
+
branch_allowed = None
|
|
718
|
+
if dynamic_row_order:
|
|
719
|
+
remaining_rows = reactant_order[depth:]
|
|
720
|
+
remaining_images = np.flatnonzero(~np.asarray(used_products))
|
|
721
|
+
if tight_profile_rows:
|
|
722
|
+
counts = (
|
|
723
|
+
profile_compatible[remaining_rows] & ~np.asarray(used_products)
|
|
724
|
+
).sum(axis=1)
|
|
725
|
+
elif branch_costs is not None:
|
|
726
|
+
finite_costs = branch_costs[np.isfinite(branch_costs)]
|
|
727
|
+
if (
|
|
728
|
+
maximum_cost_upper_bound < 2**40
|
|
729
|
+
and np.equal(finite_costs * 4, np.rint(finite_costs * 4)).all()
|
|
730
|
+
):
|
|
731
|
+
forced_bounds = committed_cost + _assignment_edge_lower_bounds(
|
|
732
|
+
branch_costs,
|
|
733
|
+
[reactant_elements[atom] for atom in remaining_rows],
|
|
734
|
+
[product_elements[image] for image in remaining_images],
|
|
735
|
+
)
|
|
736
|
+
else:
|
|
737
|
+
row_minima = branch_costs.min(axis=1)
|
|
738
|
+
forced_bounds = (
|
|
739
|
+
committed_cost + branch_costs
|
|
740
|
+
+ (row_minima.sum() - row_minima)[:, None]
|
|
741
|
+
)
|
|
742
|
+
branch_allowed = forced_bounds <= float(limit) + tolerance
|
|
743
|
+
counts = branch_allowed.sum(axis=1)
|
|
744
|
+
else:
|
|
745
|
+
counts = np.asarray([
|
|
746
|
+
sum(
|
|
747
|
+
not used_products[image]
|
|
748
|
+
and committed_cost + cross_costs[atom, image]
|
|
749
|
+
<= float(limit) + tolerance
|
|
750
|
+
for image in domains[atom]
|
|
751
|
+
)
|
|
752
|
+
for atom in remaining_rows
|
|
753
|
+
])
|
|
754
|
+
selected_position = depth + min(
|
|
755
|
+
range(len(remaining_rows)),
|
|
756
|
+
key=lambda position: (
|
|
757
|
+
int(counts[position]),
|
|
758
|
+
-int(assigned_neighbors[remaining_rows[position]]),
|
|
759
|
+
initial_order_rank[remaining_rows[position]],
|
|
760
|
+
),
|
|
761
|
+
)
|
|
762
|
+
reactant_order[depth], reactant_order[selected_position] = (
|
|
763
|
+
reactant_order[selected_position],
|
|
764
|
+
reactant_order[depth],
|
|
765
|
+
)
|
|
766
|
+
reactant_atom = reactant_order[depth]
|
|
767
|
+
allowed_images = (
|
|
768
|
+
set(remaining_images[branch_allowed[selected_position - depth]])
|
|
769
|
+
if branch_allowed is not None else None
|
|
770
|
+
)
|
|
771
|
+
candidate_images = sorted(
|
|
772
|
+
(
|
|
773
|
+
image
|
|
774
|
+
for image in domains[reactant_atom]
|
|
775
|
+
if not used_products[image]
|
|
776
|
+
and (allowed_images is None or image in allowed_images)
|
|
777
|
+
and (
|
|
778
|
+
profile_compatible is None
|
|
779
|
+
or profile_compatible[reactant_atom, image]
|
|
780
|
+
)
|
|
781
|
+
),
|
|
782
|
+
key=lambda product_atom: (
|
|
783
|
+
product_atom != preferred_mapping[reactant_atom],
|
|
784
|
+
cross_costs[reactant_atom, product_atom],
|
|
785
|
+
product_atom,
|
|
786
|
+
),
|
|
787
|
+
)
|
|
788
|
+
symmetry_witnesses = (
|
|
789
|
+
orbital_candidate_witnesses(candidate_images, stabilizer_generators)
|
|
790
|
+
if symmetry_pruning
|
|
791
|
+
else {image: None for image in candidate_images}
|
|
792
|
+
)
|
|
793
|
+
for product_atom in candidate_images:
|
|
794
|
+
# Certificate searches still need to record frontier prefixes.
|
|
795
|
+
if timed_out and truncation_reason == "time_limit" and not certify:
|
|
796
|
+
stopped_with_unexplored_work = True
|
|
797
|
+
break
|
|
798
|
+
symmetry_witness = symmetry_witnesses[product_atom]
|
|
799
|
+
if symmetry_witness is not None:
|
|
800
|
+
symmetry_pruned_branches += 1
|
|
801
|
+
if certify:
|
|
802
|
+
prefix = current_prefix(depth) + (product_atom,)
|
|
803
|
+
symmetry_prefixes.append((prefix, symmetry_witness))
|
|
804
|
+
continue
|
|
805
|
+
incremental = float(cross_costs[reactant_atom, product_atom])
|
|
806
|
+
mapping[reactant_atom] = product_atom
|
|
807
|
+
used_products[product_atom] = True
|
|
808
|
+
removed_profiles = None
|
|
809
|
+
if tight_profile_rows:
|
|
810
|
+
removed_profiles = tighten_profiles(reactant_atom, product_atom)
|
|
811
|
+
remaining_rows = reactant_order[depth + 1 :]
|
|
812
|
+
available = profile_compatible[remaining_rows] & ~np.asarray(
|
|
813
|
+
used_products
|
|
814
|
+
)
|
|
815
|
+
if not available.any(axis=1).all():
|
|
816
|
+
tight_profile_pruned += 1
|
|
817
|
+
profile_compatible.flat[removed_profiles] = True
|
|
818
|
+
used_products[product_atom] = False
|
|
819
|
+
mapping[reactant_atom] = -1
|
|
820
|
+
continue
|
|
821
|
+
removed_bond_mass = residual_bond_mass.remove(product_atom)
|
|
822
|
+
removed_reactant_mass = None
|
|
823
|
+
if dynamic_row_order:
|
|
824
|
+
removed_reactant_mass = reactant_bond_mass.remove(reactant_atom)
|
|
825
|
+
assigned_neighbors[:] += reactant_neighbors[:, reactant_atom]
|
|
826
|
+
delta = cross_cost_delta(reactant_atom, product_atom)
|
|
827
|
+
cross_costs[:] += delta
|
|
828
|
+
retained = cached_delta_bytes + delta.nbytes <= _MAX_CROSS_COST_CACHE_BYTES
|
|
829
|
+
if retained:
|
|
830
|
+
cached_delta_bytes += delta.nbytes
|
|
831
|
+
peak_cached_delta_bytes = max(
|
|
832
|
+
peak_cached_delta_bytes, cached_delta_bytes
|
|
833
|
+
)
|
|
834
|
+
else:
|
|
835
|
+
delta = None
|
|
836
|
+
visit(
|
|
837
|
+
depth + 1,
|
|
838
|
+
committed_cost + incremental,
|
|
839
|
+
stabilizer_generators,
|
|
840
|
+
product_atom,
|
|
841
|
+
profile_cost
|
|
842
|
+
+ (
|
|
843
|
+
float(profile_pair_costs[reactant_atom, product_atom])
|
|
844
|
+
if profile_pair_costs is not None
|
|
845
|
+
else 0.0
|
|
846
|
+
),
|
|
847
|
+
)
|
|
848
|
+
if retained:
|
|
849
|
+
cached_delta_bytes -= delta.nbytes
|
|
850
|
+
else:
|
|
851
|
+
delta = cross_cost_delta(reactant_atom, product_atom)
|
|
852
|
+
cross_costs[:] -= delta
|
|
853
|
+
del delta
|
|
854
|
+
residual_bond_mass.restore(product_atom, removed_bond_mass)
|
|
855
|
+
if removed_reactant_mass is not None:
|
|
856
|
+
reactant_bond_mass.restore(reactant_atom, removed_reactant_mass)
|
|
857
|
+
assigned_neighbors[:] -= reactant_neighbors[:, reactant_atom]
|
|
858
|
+
if removed_profiles is not None:
|
|
859
|
+
profile_compatible.flat[removed_profiles] = True
|
|
860
|
+
used_products[product_atom] = False
|
|
861
|
+
mapping[reactant_atom] = -1
|
|
862
|
+
if dynamic_row_order:
|
|
863
|
+
reactant_order[depth], reactant_order[selected_position] = (
|
|
864
|
+
reactant_order[selected_position],
|
|
865
|
+
reactant_order[depth],
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
traversal_started = time.perf_counter()
|
|
869
|
+
initial_profile_cost = (
|
|
870
|
+
sum(float(profile_pair_costs[atom, image]) for atom, image in fixed.items())
|
|
871
|
+
if profile_pair_costs is not None
|
|
872
|
+
else 0.0
|
|
873
|
+
)
|
|
874
|
+
visit(0, initial_committed_cost, root_generators, profile_cost=initial_profile_cost)
|
|
875
|
+
traversal_seconds = time.perf_counter() - traversal_started
|
|
876
|
+
if (
|
|
877
|
+
timed_out
|
|
878
|
+
and truncation_reason == "mapping_limit"
|
|
879
|
+
and not stopped_with_unexplored_work
|
|
880
|
+
):
|
|
881
|
+
timed_out = False
|
|
882
|
+
truncation_reason = None
|
|
883
|
+
elapsed_seconds = time.perf_counter() - started
|
|
884
|
+
if timed_out and target == "minimal" and fixed_minimum_cost is None:
|
|
885
|
+
# Never expose a best-so-far mapping as a proven exact minimum.
|
|
886
|
+
mappings = []
|
|
887
|
+
distances = []
|
|
888
|
+
selected_mapping_count = 0
|
|
889
|
+
minimum_cost = None
|
|
890
|
+
selected_cost = None
|
|
891
|
+
else:
|
|
892
|
+
minimum_cost = (
|
|
893
|
+
proven_minimum_cost
|
|
894
|
+
if target != "minimal"
|
|
895
|
+
else None
|
|
896
|
+
if math.isinf(best_cost)
|
|
897
|
+
else float(best_cost)
|
|
898
|
+
)
|
|
899
|
+
selected_cost = (
|
|
900
|
+
minimum_cost
|
|
901
|
+
if target == "minimal"
|
|
902
|
+
else (float(target) if selected_mapping_count else None)
|
|
903
|
+
)
|
|
904
|
+
complete = not timed_out
|
|
905
|
+
selected_labeled_mapping_count = selected_mapping_count
|
|
906
|
+
if symmetry_pruning and not expand_symmetry:
|
|
907
|
+
selected_labeled_mapping_count = (
|
|
908
|
+
selected_mapping_count * symmetry_group_order
|
|
909
|
+
if symmetry_group_order is not None and symmetry_quotient_complete
|
|
910
|
+
else None
|
|
911
|
+
)
|
|
912
|
+
status: EnumerationStatus = (
|
|
913
|
+
"timeout"
|
|
914
|
+
if timed_out
|
|
915
|
+
else "complete"
|
|
916
|
+
if selected_mapping_count
|
|
917
|
+
else "no_solutions"
|
|
918
|
+
)
|
|
919
|
+
certificate = None
|
|
920
|
+
if certify:
|
|
921
|
+
certificate = _make_certificate(
|
|
922
|
+
schema_version=(
|
|
923
|
+
4
|
|
924
|
+
if upper_bound_prefixes
|
|
925
|
+
else 3
|
|
926
|
+
if assignment_lower_bound
|
|
927
|
+
else 2
|
|
928
|
+
if symmetry_pruning
|
|
929
|
+
else 1
|
|
930
|
+
),
|
|
931
|
+
kind="prefix_cover",
|
|
932
|
+
input_sha256=input_sha256,
|
|
933
|
+
target=target,
|
|
934
|
+
binary=bool(binary),
|
|
935
|
+
tolerance=float(tolerance),
|
|
936
|
+
reactant_order=tuple(reactant_order),
|
|
937
|
+
terminal_prefixes=tuple(terminal_prefixes),
|
|
938
|
+
frontier_prefixes=tuple(frontier_prefixes),
|
|
939
|
+
total_bijections=total_bijections,
|
|
940
|
+
maximum_cost_upper_bound=maximum_cost_upper_bound,
|
|
941
|
+
selected_mapping_count=selected_mapping_count,
|
|
942
|
+
selected_mappings_sha256=_mappings_sha256(mappings),
|
|
943
|
+
cost=selected_cost,
|
|
944
|
+
pruning_limit=(
|
|
945
|
+
None
|
|
946
|
+
if target == "minimal" and math.isinf(best_cost)
|
|
947
|
+
else float(best_cost)
|
|
948
|
+
if target == "minimal"
|
|
949
|
+
else float(target)
|
|
950
|
+
),
|
|
951
|
+
status=status,
|
|
952
|
+
mapping_scope=mapping_scope,
|
|
953
|
+
symmetry_prefixes=tuple(symmetry_prefixes),
|
|
954
|
+
lower_bound_prefixes=tuple(lower_bound_prefixes),
|
|
955
|
+
upper_bound_prefixes=tuple(upper_bound_prefixes),
|
|
956
|
+
)
|
|
957
|
+
return DistanceEnumerationResult(
|
|
958
|
+
target=target,
|
|
959
|
+
cost=selected_cost,
|
|
960
|
+
minimum_cost=minimum_cost,
|
|
961
|
+
mappings=mappings,
|
|
962
|
+
distances=distances,
|
|
963
|
+
total_bijections=total_bijections,
|
|
964
|
+
maximum_cost_upper_bound=maximum_cost_upper_bound,
|
|
965
|
+
visited_leaves=visited_leaves,
|
|
966
|
+
pruned_branches=pruned_branches,
|
|
967
|
+
elapsed_seconds=elapsed_seconds,
|
|
968
|
+
status=status,
|
|
969
|
+
complete=complete,
|
|
970
|
+
truncation_reason=truncation_reason,
|
|
971
|
+
scope=mapping_scope,
|
|
972
|
+
certificate=certificate,
|
|
973
|
+
visited_nodes=visited_nodes,
|
|
974
|
+
symmetry_pruned_branches=symmetry_pruned_branches,
|
|
975
|
+
symmetry_automorphism_count=len(symmetry_permutations),
|
|
976
|
+
symmetry_search_complete=symmetry_search_complete,
|
|
977
|
+
lower_bound_pruned_branches=lower_bound_pruned_branches,
|
|
978
|
+
upper_bound_pruned_branches=upper_bound_pruned_branches,
|
|
979
|
+
selected_mapping_count=selected_mapping_count,
|
|
980
|
+
symmetry_group_order=symmetry_group_order,
|
|
981
|
+
symmetry_quotient_complete=symmetry_quotient_complete,
|
|
982
|
+
selected_labeled_mapping_count=selected_labeled_mapping_count,
|
|
983
|
+
backend_statistics={
|
|
984
|
+
"search": {
|
|
985
|
+
"phase": "minimum_proof" if _optimization_only else "shell_enumeration",
|
|
986
|
+
"strict_improvement": strict_improvement,
|
|
987
|
+
"stabilizer_calls": stabilizer_calls,
|
|
988
|
+
"profile_bound_calls": profile_bound_calls,
|
|
989
|
+
"profile_domain_pruned": profile_domain_pruned,
|
|
990
|
+
"tight_profile_rows": tight_profile_rows,
|
|
991
|
+
"dynamic_profile_order": dynamic_row_order,
|
|
992
|
+
"tight_profile_pruned": tight_profile_pruned,
|
|
993
|
+
"profile_bound_pruned": profile_bound_pruned,
|
|
994
|
+
"conditioned_bound_calls": conditioned_bound_calls,
|
|
995
|
+
"conditioned_bound_pruned": conditioned_bound_pruned,
|
|
996
|
+
"peak_cached_delta_bytes": peak_cached_delta_bytes,
|
|
997
|
+
"incumbent_cost": (
|
|
998
|
+
float(best_cost)
|
|
999
|
+
if target == "minimal" and math.isfinite(best_cost)
|
|
1000
|
+
else None
|
|
1001
|
+
),
|
|
1002
|
+
"preprocessing_seconds": traversal_started
|
|
1003
|
+
- started
|
|
1004
|
+
- optimization_seconds,
|
|
1005
|
+
"traversal_seconds": traversal_seconds,
|
|
1006
|
+
"minimum_proof_seconds": optimization_seconds,
|
|
1007
|
+
"minimum_proof_nodes": optimization.visited_nodes
|
|
1008
|
+
if optimization is not None
|
|
1009
|
+
else None,
|
|
1010
|
+
}
|
|
1011
|
+
},
|
|
1012
|
+
)
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
__all__ = [
|
|
1016
|
+
"CertificateVerificationError",
|
|
1017
|
+
"DistanceEnumerationCertificate",
|
|
1018
|
+
"DistanceEnumerationResult",
|
|
1019
|
+
"DistanceTarget",
|
|
1020
|
+
"EnumerationStatus",
|
|
1021
|
+
"ExactEnumerationLimitError",
|
|
1022
|
+
"enumerate_distance_mappings",
|
|
1023
|
+
"normalize_distance_target",
|
|
1024
|
+
"verify_distance_enumeration_certificate",
|
|
1025
|
+
]
|