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,940 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from time import perf_counter
|
|
6
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
7
|
+
|
|
8
|
+
import networkx as nx
|
|
9
|
+
|
|
10
|
+
from synkit.Graph.Canon.exact import (
|
|
11
|
+
CanonicalSearchIncomplete,
|
|
12
|
+
ExactCanonicalResult,
|
|
13
|
+
ExactColoredGraphCanonicalizer,
|
|
14
|
+
IncompleteCanonicalResult,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from ._common import (
|
|
18
|
+
AutomorphismResult,
|
|
19
|
+
CanonicalResult,
|
|
20
|
+
SymmetryConfig,
|
|
21
|
+
edge_token,
|
|
22
|
+
graph_key_from_order,
|
|
23
|
+
node_token,
|
|
24
|
+
orbits_from_mappings,
|
|
25
|
+
should_stop,
|
|
26
|
+
)
|
|
27
|
+
from .wl_canon import WLCanonicalizer
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class IRInternalResult:
|
|
32
|
+
"""
|
|
33
|
+
Internal exact-search result produced by :class:`IRCanonicalEngine`.
|
|
34
|
+
|
|
35
|
+
:param canonical_order:
|
|
36
|
+
Canonical node ordering selected by the exact individualize-refine search.
|
|
37
|
+
:type canonical_order: List[Any]
|
|
38
|
+
|
|
39
|
+
:param canonical_key:
|
|
40
|
+
Canonical graph key associated with ``canonical_order``.
|
|
41
|
+
:type canonical_key: Tuple[Any, ...]
|
|
42
|
+
|
|
43
|
+
:param automorphism_count:
|
|
44
|
+
Number of automorphisms discovered for the best canonical form. When the
|
|
45
|
+
search is truncated, this may be a lower bound or capped count depending
|
|
46
|
+
on the caller configuration.
|
|
47
|
+
:type automorphism_count: int
|
|
48
|
+
|
|
49
|
+
:param sample_permutations:
|
|
50
|
+
Sample node orders equivalent to the canonical form.
|
|
51
|
+
:type sample_permutations: List[List[Any]]
|
|
52
|
+
|
|
53
|
+
:param sample_mappings:
|
|
54
|
+
Sample automorphism mappings represented as ``source_node -> image_node``.
|
|
55
|
+
:type sample_mappings: List[Dict[Any, Any]]
|
|
56
|
+
|
|
57
|
+
:param orbits:
|
|
58
|
+
Orbit partition induced by the sampled automorphism mappings.
|
|
59
|
+
:type orbits: List[set[Any]]
|
|
60
|
+
|
|
61
|
+
:param elapsed_seconds:
|
|
62
|
+
Wall-clock runtime in seconds for the executed search.
|
|
63
|
+
:type elapsed_seconds: float
|
|
64
|
+
|
|
65
|
+
:param stopped_early:
|
|
66
|
+
Whether the search stopped early because of timeout, count limit, or
|
|
67
|
+
``stop_after_two``.
|
|
68
|
+
:type stopped_early: bool
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
canonical_order: List[Any]
|
|
72
|
+
canonical_key: Tuple[Any, ...]
|
|
73
|
+
automorphism_count: int
|
|
74
|
+
sample_permutations: List[List[Any]]
|
|
75
|
+
sample_mappings: List[Dict[Any, Any]]
|
|
76
|
+
orbits: List[set[Any]]
|
|
77
|
+
elapsed_seconds: float
|
|
78
|
+
stopped_early: bool
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class IRCanonicalEngine:
|
|
82
|
+
"""Exact individualize-refine engine for canonical labeling and automorphism search.
|
|
83
|
+
|
|
84
|
+
This engine starts from a Weisfeiler-Lehman (WL) coloring, then applies an
|
|
85
|
+
exact individualize-refine search on ambiguous cells. It is shared by both
|
|
86
|
+
canonicalization and automorphism queries so that an exact completed result
|
|
87
|
+
can be cached and reused.
|
|
88
|
+
|
|
89
|
+
:param source:
|
|
90
|
+
Input CRN-like object or graph accepted by :class:`WLCanonicalizer`.
|
|
91
|
+
:type source: Any
|
|
92
|
+
|
|
93
|
+
:param include_rule:
|
|
94
|
+
Whether rule/reaction node semantics should be encoded into node tokens.
|
|
95
|
+
:type include_rule: bool
|
|
96
|
+
|
|
97
|
+
:param include_stoich:
|
|
98
|
+
Whether stoichiometric edge/node information should be included in the
|
|
99
|
+
canonicalization tokens.
|
|
100
|
+
:type include_stoich: bool
|
|
101
|
+
|
|
102
|
+
:param wl_iters:
|
|
103
|
+
Maximum number of WL refinement iterations used to initialize the exact
|
|
104
|
+
search.
|
|
105
|
+
:type wl_iters: int
|
|
106
|
+
|
|
107
|
+
:param wl_digest_size:
|
|
108
|
+
Digest size used by the WL canonicalizer for hashed color refinement.
|
|
109
|
+
:type wl_digest_size: int
|
|
110
|
+
|
|
111
|
+
:param config:
|
|
112
|
+
Symmetry configuration controlling which semantic attributes are included
|
|
113
|
+
in node and edge tokens.
|
|
114
|
+
:type config: Optional[SymmetryConfig]
|
|
115
|
+
|
|
116
|
+
.. rubric:: Notes
|
|
117
|
+
|
|
118
|
+
Exact automorphism counting is generally harder than obtaining a single
|
|
119
|
+
canonical order. Therefore, this class caches completed exact runs so later
|
|
120
|
+
canonicalization and automorphism queries can reuse the same result instead
|
|
121
|
+
of rerunning the search.
|
|
122
|
+
|
|
123
|
+
.. rubric:: Examples
|
|
124
|
+
|
|
125
|
+
.. code-block:: python
|
|
126
|
+
|
|
127
|
+
from synkit.CRN.Sym._ir import IRCanonicalEngine
|
|
128
|
+
|
|
129
|
+
engine = IRCanonicalEngine(
|
|
130
|
+
source=crn,
|
|
131
|
+
include_rule=True,
|
|
132
|
+
include_stoich=True,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
canon = engine.canonical_result()
|
|
136
|
+
print(canon.canonical_order)
|
|
137
|
+
print(canon.canonical_key)
|
|
138
|
+
|
|
139
|
+
auto = engine.automorphism_result(max_count=10)
|
|
140
|
+
print(auto.automorphism_count)
|
|
141
|
+
print(auto.orbits)
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
source: Any,
|
|
147
|
+
*,
|
|
148
|
+
include_rule: bool = True,
|
|
149
|
+
include_stoich: bool = True,
|
|
150
|
+
wl_iters: int = 20,
|
|
151
|
+
wl_digest_size: int = 16,
|
|
152
|
+
config: Optional[SymmetryConfig] = None,
|
|
153
|
+
) -> None:
|
|
154
|
+
"""
|
|
155
|
+
Initialize the exact IR engine.
|
|
156
|
+
|
|
157
|
+
:param source:
|
|
158
|
+
Input CRN-like object or graph.
|
|
159
|
+
:type source: Any
|
|
160
|
+
|
|
161
|
+
:param include_rule:
|
|
162
|
+
Whether rule/reaction semantics are included.
|
|
163
|
+
:type include_rule: bool
|
|
164
|
+
|
|
165
|
+
:param include_stoich:
|
|
166
|
+
Whether stoichiometric information is encoded into the graph tokens.
|
|
167
|
+
:type include_stoich: bool
|
|
168
|
+
|
|
169
|
+
:param wl_iters:
|
|
170
|
+
Number of WL refinement iterations.
|
|
171
|
+
:type wl_iters: int
|
|
172
|
+
|
|
173
|
+
:param wl_digest_size:
|
|
174
|
+
Digest size used internally by WL hashing.
|
|
175
|
+
:type wl_digest_size: int
|
|
176
|
+
|
|
177
|
+
:param config:
|
|
178
|
+
Optional semantic symmetry configuration. If ``None``, semantic mode
|
|
179
|
+
is used.
|
|
180
|
+
:type config: Optional[SymmetryConfig]
|
|
181
|
+
"""
|
|
182
|
+
self.config = config or SymmetryConfig.semantic()
|
|
183
|
+
self.wl = WLCanonicalizer(
|
|
184
|
+
source,
|
|
185
|
+
include_rule=include_rule,
|
|
186
|
+
include_stoich=include_stoich,
|
|
187
|
+
n_iter=wl_iters,
|
|
188
|
+
digest_size=wl_digest_size,
|
|
189
|
+
config=self.config,
|
|
190
|
+
)
|
|
191
|
+
self._refine_cache: Dict[Tuple[Tuple[Any, ...], ...], List[List[Any]]] = {}
|
|
192
|
+
self._sig_cache: Dict[
|
|
193
|
+
Tuple[Tuple[Tuple[Any, ...], ...], Any], Tuple[Any, ...]
|
|
194
|
+
] = {}
|
|
195
|
+
self._node_tok_cache: Dict[Any, Tuple[Any, ...]] = {}
|
|
196
|
+
self._deg_cache: Dict[Any, Tuple[int, int]] = {}
|
|
197
|
+
self._pred_cache: Dict[Any, List[Tuple[Any, Tuple[Any, ...]]]] = {}
|
|
198
|
+
self._succ_cache: Dict[Any, List[Tuple[Any, Tuple[Any, ...]]]] = {}
|
|
199
|
+
self._act_cache: Dict[Any, int] = {}
|
|
200
|
+
self._exact_full_cache: Optional[IRInternalResult] = None
|
|
201
|
+
self._prepare_static_caches()
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def G(self) -> nx.DiGraph:
|
|
205
|
+
"""Return the internal directed graph used by the engine.
|
|
206
|
+
|
|
207
|
+
:return:
|
|
208
|
+
Directed graph produced by :class:`WLCanonicalizer`.
|
|
209
|
+
:rtype: nx.DiGraph
|
|
210
|
+
"""
|
|
211
|
+
return self.wl.G
|
|
212
|
+
|
|
213
|
+
@property
|
|
214
|
+
def graph_type(self) -> str:
|
|
215
|
+
"""Return the graph type label reported by the WL canonicalizer.
|
|
216
|
+
|
|
217
|
+
:return:
|
|
218
|
+
Graph type identifier.
|
|
219
|
+
:rtype: str
|
|
220
|
+
"""
|
|
221
|
+
return self.wl.graph_type
|
|
222
|
+
|
|
223
|
+
def _prepare_static_caches(self) -> None:
|
|
224
|
+
"""Precompute static per-node caches used during refinement.
|
|
225
|
+
|
|
226
|
+
This stores node tokens, degree pairs, predecessor signatures, successor
|
|
227
|
+
signatures, and a simple activity score for branch ordering.
|
|
228
|
+
|
|
229
|
+
:return:
|
|
230
|
+
``None``.
|
|
231
|
+
:rtype: None
|
|
232
|
+
"""
|
|
233
|
+
G = self.G
|
|
234
|
+
for v in G.nodes():
|
|
235
|
+
self._node_tok_cache[v] = node_token(G.nodes[v], self.config)
|
|
236
|
+
self._deg_cache[v] = (G.in_degree(v), G.out_degree(v))
|
|
237
|
+
self._pred_cache[v] = [
|
|
238
|
+
(u, edge_token(G[u][v], self.config)) for u in G.predecessors(v)
|
|
239
|
+
]
|
|
240
|
+
self._succ_cache[v] = [
|
|
241
|
+
(u, edge_token(G[v][u], self.config)) for u in G.successors(v)
|
|
242
|
+
]
|
|
243
|
+
self._act_cache[v] = len(self._pred_cache[v]) + len(self._succ_cache[v])
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def _part_key(part: Sequence[Sequence[Any]]) -> Tuple[Tuple[Any, ...], ...]:
|
|
247
|
+
"""Convert a partition into a hashable cache key.
|
|
248
|
+
|
|
249
|
+
:param part:
|
|
250
|
+
Partition represented as a sequence of cells.
|
|
251
|
+
:type part: Sequence[Sequence[Any]]
|
|
252
|
+
|
|
253
|
+
:return:
|
|
254
|
+
Immutable tuple-of-tuples representation of the partition.
|
|
255
|
+
:rtype: Tuple[Tuple[Any, ...], ...]
|
|
256
|
+
"""
|
|
257
|
+
return tuple(tuple(cell) for cell in part)
|
|
258
|
+
|
|
259
|
+
def _cell_signature(self, v: Any, part: Sequence[Sequence[Any]]) -> Tuple[Any, ...]:
|
|
260
|
+
"""Compute the refinement signature of a node with respect to a partition.
|
|
261
|
+
|
|
262
|
+
The signature combines node token, degree, and per-cell incident edge
|
|
263
|
+
token multisets. This is the core feature used to split cells during
|
|
264
|
+
refinement.
|
|
265
|
+
|
|
266
|
+
:param v:
|
|
267
|
+
Node whose signature is requested.
|
|
268
|
+
:type v: Any
|
|
269
|
+
|
|
270
|
+
:param part:
|
|
271
|
+
Current partition.
|
|
272
|
+
:type part: Sequence[Sequence[Any]]
|
|
273
|
+
|
|
274
|
+
:return:
|
|
275
|
+
Signature tuple used for exact refinement.
|
|
276
|
+
:rtype: Tuple[Any, ...]
|
|
277
|
+
"""
|
|
278
|
+
pkey = self._part_key(part)
|
|
279
|
+
ck = (pkey, v)
|
|
280
|
+
if ck in self._sig_cache:
|
|
281
|
+
return self._sig_cache[ck]
|
|
282
|
+
|
|
283
|
+
tok = self._node_tok_cache[v]
|
|
284
|
+
deg = self._deg_cache[v]
|
|
285
|
+
|
|
286
|
+
pred_map = defaultdict(list)
|
|
287
|
+
succ_map = defaultdict(list)
|
|
288
|
+
for u, etok in self._pred_cache[v]:
|
|
289
|
+
pred_map[u].append(etok)
|
|
290
|
+
for u, etok in self._succ_cache[v]:
|
|
291
|
+
succ_map[u].append(etok)
|
|
292
|
+
|
|
293
|
+
per_cell: List[Any] = []
|
|
294
|
+
for cell in part:
|
|
295
|
+
cell_set = set(cell)
|
|
296
|
+
in_items: List[Tuple[Any, ...]] = []
|
|
297
|
+
out_items: List[Tuple[Any, ...]] = []
|
|
298
|
+
for u in cell_set:
|
|
299
|
+
if u in pred_map:
|
|
300
|
+
in_items.extend(pred_map[u])
|
|
301
|
+
if u in succ_map:
|
|
302
|
+
out_items.extend(succ_map[u])
|
|
303
|
+
in_items.sort(key=str)
|
|
304
|
+
out_items.sort(key=str)
|
|
305
|
+
if self.config.use_edge_direction:
|
|
306
|
+
per_cell.append((tuple(in_items), tuple(out_items)))
|
|
307
|
+
else:
|
|
308
|
+
mixed = in_items + out_items
|
|
309
|
+
mixed.sort(key=str)
|
|
310
|
+
per_cell.append(tuple(mixed))
|
|
311
|
+
|
|
312
|
+
sig = (tok, deg, tuple(per_cell))
|
|
313
|
+
self._sig_cache[ck] = sig
|
|
314
|
+
return sig
|
|
315
|
+
|
|
316
|
+
def _refine(self, part: Sequence[Sequence[Any]]) -> List[List[Any]]:
|
|
317
|
+
"""Refine a partition until it becomes stable.
|
|
318
|
+
|
|
319
|
+
Cells are repeatedly split using :meth:`_cell_signature` until no further
|
|
320
|
+
refinement is possible.
|
|
321
|
+
|
|
322
|
+
:param part:
|
|
323
|
+
Input partition.
|
|
324
|
+
:type part: Sequence[Sequence[Any]]
|
|
325
|
+
|
|
326
|
+
:return:
|
|
327
|
+
Stable refined partition.
|
|
328
|
+
:rtype: List[List[Any]]
|
|
329
|
+
"""
|
|
330
|
+
pkey = self._part_key(part)
|
|
331
|
+
if pkey in self._refine_cache:
|
|
332
|
+
return [list(cell) for cell in self._refine_cache[pkey]]
|
|
333
|
+
|
|
334
|
+
cur = [sorted(cell, key=str) for cell in part]
|
|
335
|
+
while True:
|
|
336
|
+
changed = False
|
|
337
|
+
new_part: List[List[Any]] = []
|
|
338
|
+
cur_key = self._part_key(cur)
|
|
339
|
+
for cell in cur:
|
|
340
|
+
if len(cell) <= 1:
|
|
341
|
+
new_part.append(list(cell))
|
|
342
|
+
continue
|
|
343
|
+
buckets: Dict[Tuple[Any, ...], List[Any]] = defaultdict(list)
|
|
344
|
+
for v in cell:
|
|
345
|
+
sig = self._cell_signature(v, cur)
|
|
346
|
+
buckets[sig].append(v)
|
|
347
|
+
if len(buckets) == 1:
|
|
348
|
+
new_part.append(list(cell))
|
|
349
|
+
else:
|
|
350
|
+
changed = True
|
|
351
|
+
for _, block in sorted(
|
|
352
|
+
buckets.items(),
|
|
353
|
+
key=lambda kv: (str(kv[0]), tuple(map(str, kv[1]))),
|
|
354
|
+
):
|
|
355
|
+
new_part.append(sorted(block, key=str))
|
|
356
|
+
cur = new_part
|
|
357
|
+
if not changed:
|
|
358
|
+
result = [list(cell) for cell in cur]
|
|
359
|
+
self._refine_cache[cur_key] = [list(cell) for cell in result]
|
|
360
|
+
self._refine_cache[pkey] = [list(cell) for cell in result]
|
|
361
|
+
return result
|
|
362
|
+
|
|
363
|
+
def _initial_partition(self) -> List[List[Any]]:
|
|
364
|
+
"""Build the initial partition from WL colors and node tokens.
|
|
365
|
+
|
|
366
|
+
:return:
|
|
367
|
+
Refined initial partition.
|
|
368
|
+
:rtype: List[List[Any]]
|
|
369
|
+
"""
|
|
370
|
+
buckets: Dict[Tuple[Any, ...], List[Any]] = defaultdict(list)
|
|
371
|
+
for v in self.G.nodes():
|
|
372
|
+
buckets[(self.wl.color_of(v), self._node_tok_cache[v])].append(v)
|
|
373
|
+
part = [
|
|
374
|
+
sorted(vs, key=str)
|
|
375
|
+
for _, vs in sorted(
|
|
376
|
+
buckets.items(), key=lambda kv: (str(kv[0]), tuple(map(str, kv[1])))
|
|
377
|
+
)
|
|
378
|
+
]
|
|
379
|
+
return self._refine(part)
|
|
380
|
+
|
|
381
|
+
def _target_cell(self, part: Sequence[Sequence[Any]]) -> List[Any]:
|
|
382
|
+
"""Choose the next ambiguous cell to individualize.
|
|
383
|
+
|
|
384
|
+
Preference is given to smaller cells, then cells with higher total
|
|
385
|
+
activity, then token and string-based tie breaking.
|
|
386
|
+
|
|
387
|
+
:param part:
|
|
388
|
+
Current partition.
|
|
389
|
+
:type part: Sequence[Sequence[Any]]
|
|
390
|
+
|
|
391
|
+
:return:
|
|
392
|
+
Selected ambiguous cell.
|
|
393
|
+
:rtype: List[Any]
|
|
394
|
+
"""
|
|
395
|
+
ambiguous = [list(cell) for cell in part if len(cell) > 1]
|
|
396
|
+
return min(
|
|
397
|
+
ambiguous,
|
|
398
|
+
key=lambda cell: (
|
|
399
|
+
len(cell),
|
|
400
|
+
-sum(self._act_cache[v] for v in cell),
|
|
401
|
+
tuple(self._node_tok_cache[v] for v in cell),
|
|
402
|
+
tuple(map(str, cell)),
|
|
403
|
+
),
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
def _candidate_order_key(
|
|
407
|
+
self, v: Any, part: Sequence[Sequence[Any]]
|
|
408
|
+
) -> Tuple[Any, ...]:
|
|
409
|
+
"""Build a sorting key for candidates within the chosen target cell.
|
|
410
|
+
|
|
411
|
+
:param v:
|
|
412
|
+
Candidate node.
|
|
413
|
+
:type v: Any
|
|
414
|
+
|
|
415
|
+
:param part:
|
|
416
|
+
Current partition.
|
|
417
|
+
:type part: Sequence[Sequence[Any]]
|
|
418
|
+
|
|
419
|
+
:return:
|
|
420
|
+
Candidate ordering key.
|
|
421
|
+
:rtype: Tuple[Any, ...]
|
|
422
|
+
"""
|
|
423
|
+
return (
|
|
424
|
+
self._cell_signature(v, part),
|
|
425
|
+
-self._act_cache[v],
|
|
426
|
+
str(v),
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
@staticmethod
|
|
430
|
+
def _slice_result(
|
|
431
|
+
res: IRInternalResult, max_count: Optional[int], *, force_stopped: bool = False
|
|
432
|
+
) -> IRInternalResult:
|
|
433
|
+
"""Return a truncated copy of an exact result.
|
|
434
|
+
|
|
435
|
+
:param res:
|
|
436
|
+
Full internal result.
|
|
437
|
+
:type res: IRInternalResult
|
|
438
|
+
|
|
439
|
+
:param max_count:
|
|
440
|
+
Maximum number of permutations/mappings to retain. If ``None``, the
|
|
441
|
+
original result is returned unchanged.
|
|
442
|
+
:type max_count: Optional[int]
|
|
443
|
+
|
|
444
|
+
:param force_stopped:
|
|
445
|
+
Whether the sliced result should be marked as stopped early even if
|
|
446
|
+
the original full result completed.
|
|
447
|
+
:type force_stopped: bool
|
|
448
|
+
|
|
449
|
+
:return:
|
|
450
|
+
Possibly truncated internal result.
|
|
451
|
+
:rtype: IRInternalResult
|
|
452
|
+
"""
|
|
453
|
+
if max_count is None:
|
|
454
|
+
return res
|
|
455
|
+
perms = [list(p) for p in res.sample_permutations[:max_count]]
|
|
456
|
+
maps = [dict(m) for m in res.sample_mappings[:max_count]]
|
|
457
|
+
stopped = (
|
|
458
|
+
force_stopped or (res.automorphism_count > max_count) or res.stopped_early
|
|
459
|
+
)
|
|
460
|
+
orbits = (
|
|
461
|
+
res.orbits
|
|
462
|
+
if not stopped
|
|
463
|
+
else orbits_from_mappings(
|
|
464
|
+
res.canonical_order,
|
|
465
|
+
maps if maps else [{v: v for v in res.canonical_order}],
|
|
466
|
+
)
|
|
467
|
+
)
|
|
468
|
+
return IRInternalResult(
|
|
469
|
+
canonical_order=list(res.canonical_order),
|
|
470
|
+
canonical_key=res.canonical_key,
|
|
471
|
+
automorphism_count=min(res.automorphism_count, max_count),
|
|
472
|
+
sample_permutations=perms,
|
|
473
|
+
sample_mappings=maps,
|
|
474
|
+
orbits=orbits,
|
|
475
|
+
elapsed_seconds=res.elapsed_seconds,
|
|
476
|
+
stopped_early=stopped,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
def _reuse_cached_run(
|
|
480
|
+
self,
|
|
481
|
+
*,
|
|
482
|
+
max_count: Optional[int],
|
|
483
|
+
timeout_sec: Optional[float],
|
|
484
|
+
stop_after_two: bool,
|
|
485
|
+
) -> Optional[IRInternalResult]:
|
|
486
|
+
"""Reuse a previously completed exact result when valid.
|
|
487
|
+
|
|
488
|
+
:param max_count:
|
|
489
|
+
Requested maximum number of stored automorphisms.
|
|
490
|
+
:type max_count: Optional[int]
|
|
491
|
+
|
|
492
|
+
:param timeout_sec:
|
|
493
|
+
Optional timeout for the current request.
|
|
494
|
+
:type timeout_sec: Optional[float]
|
|
495
|
+
|
|
496
|
+
:param stop_after_two:
|
|
497
|
+
Whether the caller only needs to distinguish uniqueness from
|
|
498
|
+
non-uniqueness.
|
|
499
|
+
:type stop_after_two: bool
|
|
500
|
+
|
|
501
|
+
:return:
|
|
502
|
+
Cached result if reusable, otherwise ``None``.
|
|
503
|
+
:rtype: Optional[IRInternalResult]
|
|
504
|
+
"""
|
|
505
|
+
if timeout_sec is not None or self._exact_full_cache is None:
|
|
506
|
+
return None
|
|
507
|
+
if stop_after_two:
|
|
508
|
+
return self._slice_result(self._exact_full_cache, 2)
|
|
509
|
+
return self._slice_result(self._exact_full_cache, max_count)
|
|
510
|
+
|
|
511
|
+
def _initialize_run_state(
|
|
512
|
+
self,
|
|
513
|
+
) -> Tuple[float, List[List[Any]], List[Any], Tuple[Any, ...]]:
|
|
514
|
+
"""Prepare the initial state for an exact run.
|
|
515
|
+
|
|
516
|
+
:return:
|
|
517
|
+
Tuple containing start time, initial refined partition, WL order, and
|
|
518
|
+
the canonical key of the WL order.
|
|
519
|
+
:rtype: Tuple[float, List[List[Any]], List[Any], Tuple[Any, ...]]
|
|
520
|
+
"""
|
|
521
|
+
start = perf_counter()
|
|
522
|
+
init = self._initial_partition()
|
|
523
|
+
wl_order = self.wl.canonical_order()
|
|
524
|
+
wl_key = graph_key_from_order(self.G, wl_order, self.config)
|
|
525
|
+
return start, init, list(wl_order), wl_key
|
|
526
|
+
|
|
527
|
+
def _build_child_partition(
|
|
528
|
+
self, part: Sequence[Sequence[Any]], target: Sequence[Any], chosen: Any
|
|
529
|
+
) -> List[List[Any]]:
|
|
530
|
+
"""Create a child partition by individualizing one node from a target cell.
|
|
531
|
+
|
|
532
|
+
:param part:
|
|
533
|
+
Current partition.
|
|
534
|
+
:type part: Sequence[Sequence[Any]]
|
|
535
|
+
|
|
536
|
+
:param target:
|
|
537
|
+
Target ambiguous cell to split.
|
|
538
|
+
:type target: Sequence[Any]
|
|
539
|
+
|
|
540
|
+
:param chosen:
|
|
541
|
+
Node to isolate into its own singleton cell.
|
|
542
|
+
:type chosen: Any
|
|
543
|
+
|
|
544
|
+
:return:
|
|
545
|
+
Child partition after individualization.
|
|
546
|
+
:rtype: List[List[Any]]
|
|
547
|
+
"""
|
|
548
|
+
target_set = set(target)
|
|
549
|
+
rem = [u for u in target if u != chosen]
|
|
550
|
+
child: List[List[Any]] = []
|
|
551
|
+
for cell in part:
|
|
552
|
+
if set(cell) == target_set and len(cell) == len(target):
|
|
553
|
+
child.append([chosen])
|
|
554
|
+
if rem:
|
|
555
|
+
child.append(rem)
|
|
556
|
+
else:
|
|
557
|
+
child.append(list(cell))
|
|
558
|
+
return child
|
|
559
|
+
|
|
560
|
+
def _finalize_leaf(
|
|
561
|
+
self,
|
|
562
|
+
part: Sequence[Sequence[Any]],
|
|
563
|
+
*,
|
|
564
|
+
best_key: Optional[Tuple[Any, ...]],
|
|
565
|
+
best_order: Optional[List[Any]],
|
|
566
|
+
sample_perms: List[List[Any]],
|
|
567
|
+
count: int,
|
|
568
|
+
max_count: Optional[int],
|
|
569
|
+
stop_after_two: bool,
|
|
570
|
+
) -> Tuple[
|
|
571
|
+
Optional[Tuple[Any, ...]], Optional[List[Any]], List[List[Any]], int, bool
|
|
572
|
+
]:
|
|
573
|
+
"""Process a fully individualized partition.
|
|
574
|
+
|
|
575
|
+
:param part:
|
|
576
|
+
Fully discrete partition.
|
|
577
|
+
:type part: Sequence[Sequence[Any]]
|
|
578
|
+
|
|
579
|
+
:param best_key:
|
|
580
|
+
Current best canonical key.
|
|
581
|
+
:type best_key: Optional[Tuple[Any, ...]]
|
|
582
|
+
|
|
583
|
+
:param best_order:
|
|
584
|
+
Current best canonical order.
|
|
585
|
+
:type best_order: Optional[List[Any]]
|
|
586
|
+
|
|
587
|
+
:param sample_perms:
|
|
588
|
+
Collected sample permutations for the current best key.
|
|
589
|
+
:type sample_perms: List[List[Any]]
|
|
590
|
+
|
|
591
|
+
:param count:
|
|
592
|
+
Current automorphism count for the best key.
|
|
593
|
+
:type count: int
|
|
594
|
+
|
|
595
|
+
:param max_count:
|
|
596
|
+
Maximum number of stored permutations.
|
|
597
|
+
:type max_count: Optional[int]
|
|
598
|
+
|
|
599
|
+
:param stop_after_two:
|
|
600
|
+
Whether to stop once two equivalent canonical orders are found.
|
|
601
|
+
:type stop_after_two: bool
|
|
602
|
+
|
|
603
|
+
:return:
|
|
604
|
+
Updated ``(best_key, best_order, sample_perms, count, should_stop_now)``.
|
|
605
|
+
:rtype: Tuple[Optional[Tuple[Any, ...]], Optional[List[Any]], List[List[Any]], int, bool]
|
|
606
|
+
"""
|
|
607
|
+
order = [cell[0] for cell in part]
|
|
608
|
+
key = graph_key_from_order(self.G, order, self.config)
|
|
609
|
+
|
|
610
|
+
if best_key is None or key < best_key:
|
|
611
|
+
return key, order, [list(order)], 1, False
|
|
612
|
+
|
|
613
|
+
if key == best_key:
|
|
614
|
+
count += 1
|
|
615
|
+
if max_count is None or len(sample_perms) < max_count:
|
|
616
|
+
sample_perms.append(list(order))
|
|
617
|
+
return (
|
|
618
|
+
best_key,
|
|
619
|
+
best_order,
|
|
620
|
+
sample_perms,
|
|
621
|
+
count,
|
|
622
|
+
(stop_after_two and count >= 2),
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
return best_key, best_order, sample_perms, count, False
|
|
626
|
+
|
|
627
|
+
def _assemble_result(
|
|
628
|
+
self,
|
|
629
|
+
*,
|
|
630
|
+
start: float,
|
|
631
|
+
best_key: Tuple[Any, ...],
|
|
632
|
+
best_order: List[Any],
|
|
633
|
+
sample_perms: List[List[Any]],
|
|
634
|
+
count: int,
|
|
635
|
+
stopped: bool,
|
|
636
|
+
) -> IRInternalResult:
|
|
637
|
+
"""Assemble the final :class:`IRInternalResult`.
|
|
638
|
+
|
|
639
|
+
:param start:
|
|
640
|
+
Start time returned by :func:`perf_counter`.
|
|
641
|
+
:type start: float
|
|
642
|
+
|
|
643
|
+
:param best_key:
|
|
644
|
+
Best canonical key found.
|
|
645
|
+
:type best_key: Tuple[Any, ...]
|
|
646
|
+
|
|
647
|
+
:param best_order:
|
|
648
|
+
Best canonical order found.
|
|
649
|
+
:type best_order: List[Any]
|
|
650
|
+
|
|
651
|
+
:param sample_perms:
|
|
652
|
+
Sample canonical-equivalent permutations.
|
|
653
|
+
:type sample_perms: List[List[Any]]
|
|
654
|
+
|
|
655
|
+
:param count:
|
|
656
|
+
Automorphism count accumulated for the best key.
|
|
657
|
+
:type count: int
|
|
658
|
+
|
|
659
|
+
:param stopped:
|
|
660
|
+
Whether the search stopped early.
|
|
661
|
+
:type stopped: bool
|
|
662
|
+
|
|
663
|
+
:return:
|
|
664
|
+
Internal result object.
|
|
665
|
+
:rtype: IRInternalResult
|
|
666
|
+
"""
|
|
667
|
+
sample_mappings = [
|
|
668
|
+
{best_order[i]: p[i] for i in range(len(best_order))} for p in sample_perms
|
|
669
|
+
]
|
|
670
|
+
orbits = orbits_from_mappings(
|
|
671
|
+
best_order,
|
|
672
|
+
sample_mappings if sample_mappings else [{v: v for v in best_order}],
|
|
673
|
+
)
|
|
674
|
+
return IRInternalResult(
|
|
675
|
+
canonical_order=best_order,
|
|
676
|
+
canonical_key=best_key,
|
|
677
|
+
automorphism_count=count,
|
|
678
|
+
sample_permutations=sample_perms,
|
|
679
|
+
sample_mappings=sample_mappings,
|
|
680
|
+
orbits=orbits,
|
|
681
|
+
elapsed_seconds=perf_counter() - start,
|
|
682
|
+
stopped_early=stopped,
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
def run(
|
|
686
|
+
self,
|
|
687
|
+
*,
|
|
688
|
+
max_count: Optional[int] = None,
|
|
689
|
+
timeout_sec: Optional[float] = None,
|
|
690
|
+
stop_after_two: bool = False,
|
|
691
|
+
) -> IRInternalResult:
|
|
692
|
+
"""Run exact individualize-refine search.
|
|
693
|
+
|
|
694
|
+
This method computes a canonical order exactly and, when requested,
|
|
695
|
+
counts automorphisms of the best canonical form.
|
|
696
|
+
|
|
697
|
+
:param max_count:
|
|
698
|
+
Optional cap on the number of stored sample permutations and mappings.
|
|
699
|
+
This does not necessarily limit the search unless enforced by
|
|
700
|
+
:func:`should_stop`.
|
|
701
|
+
:type max_count: Optional[int]
|
|
702
|
+
|
|
703
|
+
:param timeout_sec:
|
|
704
|
+
Optional wall-clock timeout in seconds.
|
|
705
|
+
:type timeout_sec: Optional[float]
|
|
706
|
+
|
|
707
|
+
:param stop_after_two:
|
|
708
|
+
If ``True``, stop as soon as two canonical-equivalent leaves are found.
|
|
709
|
+
This is useful for fast uniqueness testing.
|
|
710
|
+
:type stop_after_two: bool
|
|
711
|
+
|
|
712
|
+
:return:
|
|
713
|
+
Exact-search internal result.
|
|
714
|
+
:rtype: IRInternalResult
|
|
715
|
+
"""
|
|
716
|
+
if max_count is not None or timeout_sec is not None or stop_after_two:
|
|
717
|
+
return self._run_bounded_compatibility(
|
|
718
|
+
max_count=max_count,
|
|
719
|
+
timeout_sec=timeout_sec,
|
|
720
|
+
stop_after_two=stop_after_two,
|
|
721
|
+
)
|
|
722
|
+
cached = self._reuse_cached_run(
|
|
723
|
+
max_count=max_count,
|
|
724
|
+
timeout_sec=timeout_sec,
|
|
725
|
+
stop_after_two=stop_after_two,
|
|
726
|
+
)
|
|
727
|
+
if cached is not None:
|
|
728
|
+
return cached
|
|
729
|
+
|
|
730
|
+
native = ExactColoredGraphCanonicalizer(
|
|
731
|
+
self.G,
|
|
732
|
+
node_color=lambda attrs: node_token(attrs, self.config),
|
|
733
|
+
edge_color=lambda attrs: edge_token(attrs, self.config),
|
|
734
|
+
).search(timeout_seconds=timeout_sec)
|
|
735
|
+
if isinstance(native, IncompleteCanonicalResult):
|
|
736
|
+
raise CanonicalSearchIncomplete(
|
|
737
|
+
"CRN canonicalization did not complete: " f"{native.reason}."
|
|
738
|
+
)
|
|
739
|
+
if not isinstance(native, ExactCanonicalResult):
|
|
740
|
+
raise RuntimeError("Native exact canonicalizer returned no result.")
|
|
741
|
+
|
|
742
|
+
full_mappings = [witness.as_dict() for witness in native.automorphisms]
|
|
743
|
+
cap = 2 if stop_after_two else max_count
|
|
744
|
+
stopped = cap is not None and len(full_mappings) > cap
|
|
745
|
+
sample_mappings = full_mappings if cap is None else full_mappings[:cap]
|
|
746
|
+
best_order = list(native.canonical_order)
|
|
747
|
+
sample_perms = [
|
|
748
|
+
[mapping[node] for node in best_order] for mapping in sample_mappings
|
|
749
|
+
]
|
|
750
|
+
orbits = orbits_from_mappings(
|
|
751
|
+
best_order,
|
|
752
|
+
(
|
|
753
|
+
sample_mappings
|
|
754
|
+
if sample_mappings
|
|
755
|
+
else [{node: node for node in best_order}]
|
|
756
|
+
),
|
|
757
|
+
)
|
|
758
|
+
result = IRInternalResult(
|
|
759
|
+
canonical_order=best_order,
|
|
760
|
+
canonical_key=graph_key_from_order(
|
|
761
|
+
self.G,
|
|
762
|
+
best_order,
|
|
763
|
+
self.config,
|
|
764
|
+
),
|
|
765
|
+
automorphism_count=len(sample_mappings),
|
|
766
|
+
sample_permutations=sample_perms,
|
|
767
|
+
sample_mappings=sample_mappings,
|
|
768
|
+
orbits=orbits,
|
|
769
|
+
elapsed_seconds=native.statistics.elapsed_seconds,
|
|
770
|
+
stopped_early=stopped,
|
|
771
|
+
)
|
|
772
|
+
if timeout_sec is None:
|
|
773
|
+
full_result = IRInternalResult(
|
|
774
|
+
canonical_order=best_order,
|
|
775
|
+
canonical_key=result.canonical_key,
|
|
776
|
+
automorphism_count=len(full_mappings),
|
|
777
|
+
sample_permutations=[
|
|
778
|
+
[mapping[node] for node in best_order] for mapping in full_mappings
|
|
779
|
+
],
|
|
780
|
+
sample_mappings=full_mappings,
|
|
781
|
+
orbits=[set(cell) for cell in native.orbits],
|
|
782
|
+
elapsed_seconds=native.statistics.elapsed_seconds,
|
|
783
|
+
stopped_early=False,
|
|
784
|
+
)
|
|
785
|
+
self._exact_full_cache = full_result
|
|
786
|
+
return result
|
|
787
|
+
|
|
788
|
+
def _run_bounded_compatibility(
|
|
789
|
+
self,
|
|
790
|
+
*,
|
|
791
|
+
max_count: Optional[int],
|
|
792
|
+
timeout_sec: Optional[float],
|
|
793
|
+
stop_after_two: bool,
|
|
794
|
+
) -> IRInternalResult:
|
|
795
|
+
"""Run the historical bounded diagnostic IR traversal.
|
|
796
|
+
|
|
797
|
+
This path preserves CRN automorphism-count and timeout diagnostics.
|
|
798
|
+
``stopped_early`` is authoritative: callers may inspect the partial
|
|
799
|
+
evidence, but :meth:`canonical_result` refuses to promote it.
|
|
800
|
+
"""
|
|
801
|
+
cached = self._reuse_cached_run(
|
|
802
|
+
max_count=max_count,
|
|
803
|
+
timeout_sec=timeout_sec,
|
|
804
|
+
stop_after_two=stop_after_two,
|
|
805
|
+
)
|
|
806
|
+
if cached is not None:
|
|
807
|
+
return cached
|
|
808
|
+
|
|
809
|
+
start, init, wl_order, wl_key = self._initialize_run_state()
|
|
810
|
+
best_key: Optional[Tuple[Any, ...]] = wl_key
|
|
811
|
+
best_order: Optional[List[Any]] = list(wl_order)
|
|
812
|
+
sample_perms: List[List[Any]] = []
|
|
813
|
+
count = 0
|
|
814
|
+
stopped = False
|
|
815
|
+
|
|
816
|
+
def dfs(part: List[List[Any]]) -> None:
|
|
817
|
+
nonlocal best_key, best_order, sample_perms, count, stopped
|
|
818
|
+
if stopped:
|
|
819
|
+
return
|
|
820
|
+
if should_stop(
|
|
821
|
+
start,
|
|
822
|
+
timeout_sec,
|
|
823
|
+
count=count,
|
|
824
|
+
max_count=max_count,
|
|
825
|
+
):
|
|
826
|
+
stopped = True
|
|
827
|
+
return
|
|
828
|
+
|
|
829
|
+
refined = self._refine(part)
|
|
830
|
+
ambiguous = [cell for cell in refined if len(cell) > 1]
|
|
831
|
+
if not ambiguous:
|
|
832
|
+
(
|
|
833
|
+
best_key,
|
|
834
|
+
best_order,
|
|
835
|
+
sample_perms,
|
|
836
|
+
count,
|
|
837
|
+
stop_now,
|
|
838
|
+
) = self._finalize_leaf(
|
|
839
|
+
refined,
|
|
840
|
+
best_key=best_key,
|
|
841
|
+
best_order=best_order,
|
|
842
|
+
sample_perms=sample_perms,
|
|
843
|
+
count=count,
|
|
844
|
+
max_count=max_count,
|
|
845
|
+
stop_after_two=stop_after_two,
|
|
846
|
+
)
|
|
847
|
+
if stop_now:
|
|
848
|
+
stopped = True
|
|
849
|
+
return
|
|
850
|
+
|
|
851
|
+
target = self._target_cell(refined)
|
|
852
|
+
ordered_candidates = sorted(
|
|
853
|
+
target,
|
|
854
|
+
key=lambda value: self._candidate_order_key(
|
|
855
|
+
value,
|
|
856
|
+
refined,
|
|
857
|
+
),
|
|
858
|
+
)
|
|
859
|
+
for value in ordered_candidates:
|
|
860
|
+
child = self._build_child_partition(
|
|
861
|
+
refined,
|
|
862
|
+
target,
|
|
863
|
+
value,
|
|
864
|
+
)
|
|
865
|
+
dfs(child)
|
|
866
|
+
if stopped:
|
|
867
|
+
return
|
|
868
|
+
|
|
869
|
+
dfs(init)
|
|
870
|
+
assert best_key is not None and best_order is not None
|
|
871
|
+
return self._assemble_result(
|
|
872
|
+
start=start,
|
|
873
|
+
best_key=best_key,
|
|
874
|
+
best_order=best_order,
|
|
875
|
+
sample_perms=sample_perms,
|
|
876
|
+
count=count,
|
|
877
|
+
stopped=stopped,
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
def canonical_result(
|
|
881
|
+
self, *, timeout_sec: Optional[float] = None
|
|
882
|
+
) -> CanonicalResult:
|
|
883
|
+
"""Compute the exact canonicalization result.
|
|
884
|
+
|
|
885
|
+
:param timeout_sec:
|
|
886
|
+
Optional timeout in seconds.
|
|
887
|
+
:type timeout_sec: Optional[float]
|
|
888
|
+
|
|
889
|
+
:return:
|
|
890
|
+
Public canonicalization result.
|
|
891
|
+
:rtype: CanonicalResult
|
|
892
|
+
"""
|
|
893
|
+
res = self.run(timeout_sec=timeout_sec)
|
|
894
|
+
if res.stopped_early:
|
|
895
|
+
raise CanonicalSearchIncomplete(
|
|
896
|
+
"CRN canonical identity requires a complete search."
|
|
897
|
+
)
|
|
898
|
+
return CanonicalResult(
|
|
899
|
+
canonical_order=res.canonical_order,
|
|
900
|
+
canonical_key=res.canonical_key,
|
|
901
|
+
exact=True,
|
|
902
|
+
elapsed_seconds=res.elapsed_seconds,
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
def automorphism_result(
|
|
906
|
+
self,
|
|
907
|
+
*,
|
|
908
|
+
max_count: Optional[int] = None,
|
|
909
|
+
timeout_sec: Optional[float] = None,
|
|
910
|
+
stop_after_two: bool = False,
|
|
911
|
+
) -> AutomorphismResult:
|
|
912
|
+
"""Compute the automorphism result.
|
|
913
|
+
|
|
914
|
+
:param max_count:
|
|
915
|
+
Optional cap on the number of stored sample mappings.
|
|
916
|
+
:type max_count: Optional[int]
|
|
917
|
+
|
|
918
|
+
:param timeout_sec:
|
|
919
|
+
Optional timeout in seconds.
|
|
920
|
+
:type timeout_sec: Optional[float]
|
|
921
|
+
|
|
922
|
+
:param stop_after_two:
|
|
923
|
+
Whether to stop once two automorphisms are found.
|
|
924
|
+
:type stop_after_two: bool
|
|
925
|
+
|
|
926
|
+
:return:
|
|
927
|
+
Public automorphism result.
|
|
928
|
+
:rtype: AutomorphismResult
|
|
929
|
+
"""
|
|
930
|
+
res = self.run(
|
|
931
|
+
max_count=max_count, timeout_sec=timeout_sec, stop_after_two=stop_after_two
|
|
932
|
+
)
|
|
933
|
+
return AutomorphismResult(
|
|
934
|
+
graph_type=self.graph_type,
|
|
935
|
+
automorphism_count=res.automorphism_count,
|
|
936
|
+
sample_mappings=res.sample_mappings,
|
|
937
|
+
orbits=res.orbits,
|
|
938
|
+
elapsed_seconds=res.elapsed_seconds,
|
|
939
|
+
stopped_early=res.stopped_early,
|
|
940
|
+
)
|