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,395 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Automorphism orbits of a labeled graph (thin wrapper over synkit).
|
|
3
|
+
|
|
4
|
+
Two atoms are *symmetry-equivalent* when some automorphism of the molecular graph
|
|
5
|
+
maps one to the other; the set of atoms reachable from a given atom under the
|
|
6
|
+
automorphism group is its *orbit*. Branching the exact search over one
|
|
7
|
+
representative per orbit (orbital branching, :mod:`mapper.exact.branching`) avoids
|
|
8
|
+
exploring assignments that are equivalent by symmetry, and orbits also drive
|
|
9
|
+
symmetry-distinct optimum enumeration (:mod:`mapper.exact.enumerate`).
|
|
10
|
+
|
|
11
|
+
This module converts a :class:`~mapper.graph.labeled_graph.LabeledGraph` into a
|
|
12
|
+
``networkx`` graph (node attribute ``element`` = atomic number, edge attribute
|
|
13
|
+
``order`` = bond order) and asks
|
|
14
|
+
:class:`synkit.Graph.Matcher.automorphism.Automorphism` for the orbits over the
|
|
15
|
+
*whole* graph (all connected components). If automorphism analysis fails,
|
|
16
|
+
:func:`node_orbits` falls back to the discrete partition, which simply disables
|
|
17
|
+
symmetry pruning without affecting correctness.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import hashlib
|
|
23
|
+
import time
|
|
24
|
+
|
|
25
|
+
import networkx as nx
|
|
26
|
+
import numpy as np
|
|
27
|
+
from typing import Dict, List, FrozenSet
|
|
28
|
+
|
|
29
|
+
from synkit.Graph.Canon import ExactCanonicalResult, ExactColoredGraphCanonicalizer
|
|
30
|
+
from synkit.Graph.Matcher.automorphism import Automorphism
|
|
31
|
+
|
|
32
|
+
from .synkit_adapter import graph_to_nx
|
|
33
|
+
|
|
34
|
+
_MAX_ORBIT_NODES = 48
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _atomic_numbers(lg):
|
|
38
|
+
nums = lg.props.get("atomic numbers")
|
|
39
|
+
if nums is None:
|
|
40
|
+
nums = list(lg.labels)
|
|
41
|
+
return list(nums)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _objective_graph_data(lg, binary=False, node_properties=()):
|
|
45
|
+
"""Return the exact matrix/element data used by chemical distance."""
|
|
46
|
+
n = len(lg.labels)
|
|
47
|
+
matrix = []
|
|
48
|
+
for source in range(n):
|
|
49
|
+
neighbours = lg.graph.get(source, {})
|
|
50
|
+
row = []
|
|
51
|
+
for target in range(n):
|
|
52
|
+
if source == target or target not in neighbours:
|
|
53
|
+
row.append(0.0)
|
|
54
|
+
else:
|
|
55
|
+
row.append(1.0 if binary else float(neighbours[target]))
|
|
56
|
+
matrix.append(tuple(row))
|
|
57
|
+
colors = []
|
|
58
|
+
atomic_numbers = _atomic_numbers(lg)
|
|
59
|
+
for index, atomic_number in enumerate(atomic_numbers):
|
|
60
|
+
extras = []
|
|
61
|
+
for name in node_properties:
|
|
62
|
+
values = lg.props.get(name)
|
|
63
|
+
if values is None or len(values) != n:
|
|
64
|
+
raise ValueError(
|
|
65
|
+
f"symmetry node property {name!r} requires one value per atom"
|
|
66
|
+
)
|
|
67
|
+
extras.append(values[index])
|
|
68
|
+
colors.append((atomic_number, *extras) if extras else atomic_number)
|
|
69
|
+
return tuple(matrix), tuple(colors)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _objective_graph_digest(lg, binary=False, node_properties=()) -> str:
|
|
73
|
+
matrix, elements = _objective_graph_data(lg, binary, node_properties)
|
|
74
|
+
payload = repr(
|
|
75
|
+
(
|
|
76
|
+
tuple((type(value).__module__, type(value).__qualname__, repr(value)) for value in elements),
|
|
77
|
+
matrix,
|
|
78
|
+
)
|
|
79
|
+
).encode("utf-8", "surrogatepass")
|
|
80
|
+
return hashlib.sha256(payload).hexdigest()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _is_exact_automorphism(permutation, matrix, elements) -> bool:
|
|
84
|
+
"""Check a permutation against the exact chemical-distance input."""
|
|
85
|
+
n = len(elements)
|
|
86
|
+
if len(permutation) != n or sorted(permutation) != list(range(n)):
|
|
87
|
+
return False
|
|
88
|
+
if any(elements[index] != elements[permutation[index]] for index in range(n)):
|
|
89
|
+
return False
|
|
90
|
+
array = np.asarray(matrix)
|
|
91
|
+
return bool(np.array_equal(array, array[np.ix_(permutation, permutation)]))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def to_nx(lg, binary=False, node_properties=()):
|
|
95
|
+
"""Build a ``networkx.Graph`` from a labeled graph for automorphism analysis.
|
|
96
|
+
|
|
97
|
+
Nodes carry ``element`` (atomic number); edges carry ``order`` (bond order,
|
|
98
|
+
or ``1`` when ``binary``).
|
|
99
|
+
"""
|
|
100
|
+
g = graph_to_nx(lg, binary=binary, include_label=False)
|
|
101
|
+
_, elements = _objective_graph_data(lg, binary, node_properties)
|
|
102
|
+
for i in range(len(lg.labels)):
|
|
103
|
+
g.nodes[i]["element"] = elements[i] if i < len(elements) else 0
|
|
104
|
+
return g
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _discrete_orbits(n: int) -> List[FrozenSet[int]]:
|
|
108
|
+
return [frozenset({i}) for i in range(n)]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def node_orbits(lg, binary=False) -> List[FrozenSet[int]]:
|
|
112
|
+
"""Automorphism orbits of a labeled graph's atoms.
|
|
113
|
+
|
|
114
|
+
:param lg:
|
|
115
|
+
:type lg: LabeledGraph
|
|
116
|
+
:param binary: Whether bond orders are binarised.
|
|
117
|
+
:type binary: bool, optional
|
|
118
|
+
|
|
119
|
+
:return: The orbits (a partition of the atom indices). Without synkit, the
|
|
120
|
+
discrete partition (one atom per orbit) is returned.
|
|
121
|
+
:rtype: list[frozenset[int]]
|
|
122
|
+
"""
|
|
123
|
+
n = len(lg.labels)
|
|
124
|
+
if n > _MAX_ORBIT_NODES:
|
|
125
|
+
return _discrete_orbits(n)
|
|
126
|
+
try:
|
|
127
|
+
g = to_nx(lg, binary=binary)
|
|
128
|
+
auto = Automorphism(
|
|
129
|
+
g,
|
|
130
|
+
node_attr_keys=["element"],
|
|
131
|
+
edge_attr_keys=["order"],
|
|
132
|
+
anchor_largest_component=False,
|
|
133
|
+
)
|
|
134
|
+
orbits = [frozenset(o) for o in auto.orbits]
|
|
135
|
+
except Exception:
|
|
136
|
+
return _discrete_orbits(n)
|
|
137
|
+
seen = set().union(*orbits) if orbits else set()
|
|
138
|
+
# Isolated atoms may be omitted by the backend; add them as singletons.
|
|
139
|
+
for i in range(n):
|
|
140
|
+
if i not in seen:
|
|
141
|
+
orbits.append(frozenset({i}))
|
|
142
|
+
return orbits
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def orbit_id_map(orbits) -> Dict[int, int]:
|
|
146
|
+
"""Map each atom index to an integer orbit id."""
|
|
147
|
+
out = {}
|
|
148
|
+
for oid, orb in enumerate(orbits):
|
|
149
|
+
for i in orb:
|
|
150
|
+
out[i] = oid
|
|
151
|
+
return out
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def n_automorphisms(lg, binary=False) -> int:
|
|
155
|
+
"""Size of the automorphism group."""
|
|
156
|
+
try:
|
|
157
|
+
g = to_nx(lg, binary=binary)
|
|
158
|
+
auto = Automorphism(
|
|
159
|
+
g,
|
|
160
|
+
node_attr_keys=["element"],
|
|
161
|
+
edge_attr_keys=["order"],
|
|
162
|
+
anchor_largest_component=False,
|
|
163
|
+
)
|
|
164
|
+
return int(auto.n_automorphisms)
|
|
165
|
+
except Exception:
|
|
166
|
+
return 1
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _verified_twin_permutations(matrix, elements, limit):
|
|
170
|
+
"""Recover a bounded subgroup from equal incoming and outgoing rows.
|
|
171
|
+
|
|
172
|
+
Adjacent transpositions within each nonadjacent-twin class generate its
|
|
173
|
+
full symmetric group. Check each witness against the complete objective.
|
|
174
|
+
"""
|
|
175
|
+
n = len(elements)
|
|
176
|
+
identity = tuple(range(n))
|
|
177
|
+
classes = {}
|
|
178
|
+
for atom in range(n):
|
|
179
|
+
signature = (matrix[atom], tuple(row[atom] for row in matrix))
|
|
180
|
+
classes.setdefault(signature, []).append(atom)
|
|
181
|
+
permutations = [identity]
|
|
182
|
+
for atoms in classes.values():
|
|
183
|
+
# Colors may be comparable but not hashable.
|
|
184
|
+
color_classes = []
|
|
185
|
+
for atom in atoms:
|
|
186
|
+
for group in color_classes:
|
|
187
|
+
if elements[atom] == elements[group[0]]:
|
|
188
|
+
group.append(atom)
|
|
189
|
+
break
|
|
190
|
+
else:
|
|
191
|
+
color_classes.append([atom])
|
|
192
|
+
for group in color_classes:
|
|
193
|
+
for first, second in zip(group, group[1:]):
|
|
194
|
+
permutation = list(identity)
|
|
195
|
+
permutation[first], permutation[second] = second, first
|
|
196
|
+
permutation = tuple(permutation)
|
|
197
|
+
if _is_exact_automorphism(permutation, matrix, elements):
|
|
198
|
+
permutations.append(permutation)
|
|
199
|
+
if len(permutations) >= limit:
|
|
200
|
+
return tuple(sorted(permutations))
|
|
201
|
+
return tuple(sorted(permutations))
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _component_automorphism_permutations( # noqa: C901
|
|
205
|
+
graph, matrix, elements, limit, timeout_seconds, max_search_nodes
|
|
206
|
+
):
|
|
207
|
+
"""Find component generators and equal-component swaps under shared budgets.
|
|
208
|
+
|
|
209
|
+
Equal canonical codes propose transports; full matrix verification is the
|
|
210
|
+
authority for every lifted witness. Retain compact generators instead of
|
|
211
|
+
expanding their often enormous direct-product group.
|
|
212
|
+
"""
|
|
213
|
+
components = sorted(
|
|
214
|
+
(tuple(sorted(c)) for c in nx.connected_components(graph)),
|
|
215
|
+
key=lambda c: (len(c), c),
|
|
216
|
+
)
|
|
217
|
+
if len(components) <= 1:
|
|
218
|
+
return None
|
|
219
|
+
started = time.perf_counter()
|
|
220
|
+
identity = tuple(range(len(elements)))
|
|
221
|
+
permutations = {identity}
|
|
222
|
+
verification_matrix = np.asarray(matrix)
|
|
223
|
+
matches = {}
|
|
224
|
+
remaining_nodes = max_search_nodes
|
|
225
|
+
complete = True
|
|
226
|
+
|
|
227
|
+
def accept(permutation):
|
|
228
|
+
nonlocal complete
|
|
229
|
+
permutation = tuple(permutation)
|
|
230
|
+
if permutation in permutations:
|
|
231
|
+
return
|
|
232
|
+
if len(permutations) >= limit:
|
|
233
|
+
complete = False
|
|
234
|
+
return
|
|
235
|
+
if _is_exact_automorphism(permutation, verification_matrix, elements):
|
|
236
|
+
permutations.add(permutation)
|
|
237
|
+
else:
|
|
238
|
+
complete = False
|
|
239
|
+
|
|
240
|
+
for atoms in components:
|
|
241
|
+
remaining = timeout_seconds - (time.perf_counter() - started)
|
|
242
|
+
if remaining <= 0 or remaining_nodes <= 0 or len(permutations) >= limit:
|
|
243
|
+
complete = False
|
|
244
|
+
break
|
|
245
|
+
try:
|
|
246
|
+
result = ExactColoredGraphCanonicalizer(
|
|
247
|
+
graph.subgraph(atoms).copy(),
|
|
248
|
+
node_color="element",
|
|
249
|
+
edge_color="order",
|
|
250
|
+
prune_automorphisms=True,
|
|
251
|
+
enumerate_automorphism_group=False,
|
|
252
|
+
).search(timeout_seconds=remaining, max_search_nodes=remaining_nodes)
|
|
253
|
+
except Exception:
|
|
254
|
+
complete = False
|
|
255
|
+
break
|
|
256
|
+
remaining_nodes -= result.statistics.visited_nodes
|
|
257
|
+
for witness in result.automorphisms:
|
|
258
|
+
permutation = list(identity)
|
|
259
|
+
for source, target in witness.as_dict().items():
|
|
260
|
+
permutation[source] = target
|
|
261
|
+
accept(permutation)
|
|
262
|
+
if not isinstance(result, ExactCanonicalResult):
|
|
263
|
+
complete = False
|
|
264
|
+
continue
|
|
265
|
+
previous = matches.get(result.canonical_code)
|
|
266
|
+
if previous is not None:
|
|
267
|
+
permutation = list(identity)
|
|
268
|
+
for first, second in zip(previous, result.canonical_order):
|
|
269
|
+
permutation[first], permutation[second] = second, first
|
|
270
|
+
accept(permutation)
|
|
271
|
+
# Adjacent component swaps generate all permutations of equal components.
|
|
272
|
+
matches[result.canonical_code] = result.canonical_order
|
|
273
|
+
if not complete:
|
|
274
|
+
for permutation in _verified_twin_permutations(matrix, elements, limit):
|
|
275
|
+
accept(permutation)
|
|
276
|
+
return tuple(sorted(permutations)), complete
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def bounded_automorphism_permutations( # noqa: C901
|
|
280
|
+
lg,
|
|
281
|
+
binary=False,
|
|
282
|
+
*,
|
|
283
|
+
limit=256,
|
|
284
|
+
timeout_seconds=0.25,
|
|
285
|
+
max_search_nodes=10000,
|
|
286
|
+
node_properties=(),
|
|
287
|
+
):
|
|
288
|
+
"""Return bounded, exactly verified product-graph symmetries.
|
|
289
|
+
|
|
290
|
+
Disconnected graphs use component canonicalization under shared time/node
|
|
291
|
+
budgets, retaining compact internal generators and equal-component swaps.
|
|
292
|
+
Connected graphs use SynKit's bounded exact canonicalizer and a bounded
|
|
293
|
+
expansion of its generators. If a canonical search does not complete,
|
|
294
|
+
verified nonadjacent-twin transpositions recover a safe subgroup; absent
|
|
295
|
+
such witnesses the identity disables pruning. Every returned permutation
|
|
296
|
+
is a concrete automorphism; group completeness is not required for safety.
|
|
297
|
+
|
|
298
|
+
:param lg: Mapper labeled graph.
|
|
299
|
+
:param binary: Whether edge orders are binarized.
|
|
300
|
+
:param limit: Maximum number of returned permutations.
|
|
301
|
+
:param timeout_seconds: Canonical-search wall-time budget.
|
|
302
|
+
:param max_search_nodes: Canonical-search node budget.
|
|
303
|
+
:return: ``(permutations, canonical_search_complete)``.
|
|
304
|
+
"""
|
|
305
|
+
if isinstance(limit, bool) or not isinstance(limit, int) or limit < 1:
|
|
306
|
+
raise ValueError("limit must be a positive integer")
|
|
307
|
+
node_properties = tuple(str(name) for name in node_properties)
|
|
308
|
+
n = len(lg.labels)
|
|
309
|
+
identity = tuple(range(n))
|
|
310
|
+
if n <= 1 or limit == 1:
|
|
311
|
+
return (identity,), n <= 1
|
|
312
|
+
|
|
313
|
+
cache_key = (
|
|
314
|
+
_objective_graph_digest(lg, binary, node_properties),
|
|
315
|
+
bool(binary),
|
|
316
|
+
node_properties,
|
|
317
|
+
int(limit),
|
|
318
|
+
float(timeout_seconds),
|
|
319
|
+
int(max_search_nodes),
|
|
320
|
+
)
|
|
321
|
+
cache = lg.props.setdefault("_bounded_automorphism_permutations", {})
|
|
322
|
+
if cache_key in cache:
|
|
323
|
+
return cache[cache_key]
|
|
324
|
+
|
|
325
|
+
graph = to_nx(lg, binary=binary, node_properties=node_properties)
|
|
326
|
+
matrix, elements = _objective_graph_data(lg, binary, node_properties)
|
|
327
|
+
|
|
328
|
+
def fallback():
|
|
329
|
+
outcome = (_verified_twin_permutations(matrix, elements, limit), False)
|
|
330
|
+
cache[cache_key] = outcome
|
|
331
|
+
return outcome
|
|
332
|
+
|
|
333
|
+
component_outcome = _component_automorphism_permutations(
|
|
334
|
+
graph, matrix, elements, limit, timeout_seconds, max_search_nodes
|
|
335
|
+
)
|
|
336
|
+
if component_outcome is not None:
|
|
337
|
+
cache[cache_key] = component_outcome
|
|
338
|
+
return component_outcome
|
|
339
|
+
|
|
340
|
+
try:
|
|
341
|
+
canonicalizer = ExactColoredGraphCanonicalizer(
|
|
342
|
+
graph,
|
|
343
|
+
node_color="element",
|
|
344
|
+
edge_color="order",
|
|
345
|
+
prune_automorphisms=True,
|
|
346
|
+
enumerate_automorphism_group=False,
|
|
347
|
+
)
|
|
348
|
+
result = canonicalizer.search(
|
|
349
|
+
timeout_seconds=timeout_seconds,
|
|
350
|
+
max_search_nodes=max_search_nodes,
|
|
351
|
+
)
|
|
352
|
+
except Exception:
|
|
353
|
+
return fallback()
|
|
354
|
+
if not isinstance(result, ExactCanonicalResult):
|
|
355
|
+
partial = set(_verified_twin_permutations(matrix, elements, limit))
|
|
356
|
+
for witness in result.automorphisms:
|
|
357
|
+
permutation = tuple(witness.as_dict()[index] for index in range(n))
|
|
358
|
+
if len(partial) >= limit:
|
|
359
|
+
break
|
|
360
|
+
if _is_exact_automorphism(permutation, matrix, elements):
|
|
361
|
+
partial.add(permutation)
|
|
362
|
+
outcome = (tuple(sorted(partial)), False)
|
|
363
|
+
cache[cache_key] = outcome
|
|
364
|
+
return outcome
|
|
365
|
+
|
|
366
|
+
generators = {
|
|
367
|
+
tuple(witness.as_dict()[index] for index in range(n))
|
|
368
|
+
for witness in result.automorphisms
|
|
369
|
+
}
|
|
370
|
+
generators.add(identity)
|
|
371
|
+
generators = {
|
|
372
|
+
permutation
|
|
373
|
+
for permutation in generators
|
|
374
|
+
if _is_exact_automorphism(permutation, matrix, elements)
|
|
375
|
+
}
|
|
376
|
+
permutations = set(generators)
|
|
377
|
+
frontier = list(sorted(generators))
|
|
378
|
+
while frontier and len(permutations) < limit:
|
|
379
|
+
right = frontier.pop(0)
|
|
380
|
+
for left in tuple(sorted(permutations)):
|
|
381
|
+
for composed in (
|
|
382
|
+
tuple(left[right[index]] for index in range(n)),
|
|
383
|
+
tuple(right[left[index]] for index in range(n)),
|
|
384
|
+
):
|
|
385
|
+
if composed in permutations:
|
|
386
|
+
continue
|
|
387
|
+
permutations.add(composed)
|
|
388
|
+
frontier.append(composed)
|
|
389
|
+
if len(permutations) >= limit:
|
|
390
|
+
break
|
|
391
|
+
if len(permutations) >= limit:
|
|
392
|
+
break
|
|
393
|
+
outcome = (tuple(sorted(permutations)[:limit]), True)
|
|
394
|
+
cache[cache_key] = outcome
|
|
395
|
+
return outcome
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Block-cut-tree decomposition of a labeled graph.
|
|
3
|
+
|
|
4
|
+
A connected graph decomposes into *biconnected components* (blocks) joined at
|
|
5
|
+
*articulation points* (cut vertices). Contracting each block to a node and
|
|
6
|
+
linking it to its cut vertices yields the *block-cut tree*. Removing an
|
|
7
|
+
articulation point disconnects the graph, so any matching/automorphism question
|
|
8
|
+
factorises over this tree: the pieces interact only through the shared cut
|
|
9
|
+
vertices.
|
|
10
|
+
|
|
11
|
+
This module exposes the decomposition (:func:`block_cut_tree`) and a helper to
|
|
12
|
+
locate which block(s) each atom belongs to (:func:`node_block_map`). The exact
|
|
13
|
+
kernel solver uses them to split an uncertainty region that spans several
|
|
14
|
+
independent blocks into per-block sub-problems, turning one ``O(k!)`` search into
|
|
15
|
+
a sum of much smaller ones (see ``solve_kernel_blockwise`` in
|
|
16
|
+
:mod:`mapper.exact.branching`).
|
|
17
|
+
|
|
18
|
+
Built on :func:`networkx.biconnected_components` /
|
|
19
|
+
:func:`networkx.articulation_points`.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from typing import Dict, FrozenSet, List, Set
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import networkx as nx
|
|
29
|
+
|
|
30
|
+
HAS_NX = True
|
|
31
|
+
except Exception: # pragma: no cover
|
|
32
|
+
HAS_NX = False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _to_nx(graph, n):
|
|
36
|
+
g = nx.Graph()
|
|
37
|
+
g.add_nodes_from(range(n))
|
|
38
|
+
for i, nbrs in graph.items():
|
|
39
|
+
for j in nbrs:
|
|
40
|
+
if i != j:
|
|
41
|
+
g.add_edge(i, j)
|
|
42
|
+
return g
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class BlockCutTree:
|
|
47
|
+
"""Block-cut-tree decomposition.
|
|
48
|
+
|
|
49
|
+
:ivar blocks: Biconnected components (node sets). A bridge edge is a block of size 2.
|
|
50
|
+
:vartype blocks: list[frozenset[int]]
|
|
51
|
+
:ivar articulation_points: Cut vertices shared between blocks.
|
|
52
|
+
:vartype articulation_points: set[int]
|
|
53
|
+
:ivar tree: Bipartite block-cut tree with nodes ``("B", block_index)`` and
|
|
54
|
+
``("C", cut_vertex)``.
|
|
55
|
+
:vartype tree: networkx.Graph
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
blocks: List[FrozenSet[int]]
|
|
59
|
+
articulation_points: Set[int]
|
|
60
|
+
tree: "nx.Graph"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def block_cut_tree(graph, n):
|
|
64
|
+
"""Compute the block-cut-tree decomposition of a labeled-graph adjacency dict.
|
|
65
|
+
|
|
66
|
+
:param graph: Adjacency (edge weights ignored).
|
|
67
|
+
:type graph: dict[int, dict[int, number]]
|
|
68
|
+
:param n: Number of nodes.
|
|
69
|
+
:type n: int
|
|
70
|
+
|
|
71
|
+
:rtype: BlockCutTree
|
|
72
|
+
"""
|
|
73
|
+
if not HAS_NX:
|
|
74
|
+
raise ImportError("networkx is required for block-cut-tree decomposition")
|
|
75
|
+
g = _to_nx(graph, n)
|
|
76
|
+
blocks = [frozenset(b) for b in nx.biconnected_components(g)]
|
|
77
|
+
arts = set(nx.articulation_points(g))
|
|
78
|
+
|
|
79
|
+
tree = nx.Graph()
|
|
80
|
+
for bi, block in enumerate(blocks):
|
|
81
|
+
tree.add_node(("B", bi))
|
|
82
|
+
for v in block:
|
|
83
|
+
if v in arts:
|
|
84
|
+
tree.add_node(("C", v))
|
|
85
|
+
tree.add_edge(("B", bi), ("C", v))
|
|
86
|
+
return BlockCutTree(blocks=blocks, articulation_points=arts, tree=tree)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def node_block_map(bct) -> Dict[int, List[int]]:
|
|
90
|
+
"""Map each atom index to the list of block indices containing it.
|
|
91
|
+
|
|
92
|
+
Articulation points belong to several blocks; ordinary atoms to exactly one.
|
|
93
|
+
"""
|
|
94
|
+
out: Dict[int, List[int]] = {}
|
|
95
|
+
for bi, block in enumerate(bct.blocks):
|
|
96
|
+
for v in block:
|
|
97
|
+
out.setdefault(v, []).append(bi)
|
|
98
|
+
return out
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
from .synkit_adapter import stable_int_token, synkit_wl_node_labels
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LabeledGraph:
|
|
9
|
+
"""Indexed graph with labels and metadata."""
|
|
10
|
+
|
|
11
|
+
SYNKIT_WL_NODE_LIMIT = 256
|
|
12
|
+
|
|
13
|
+
def __init__(self, graph, labels):
|
|
14
|
+
self.graph = self._normalise_graph(graph, len(labels))
|
|
15
|
+
self.labels = list(labels)
|
|
16
|
+
self.label2idxs = defaultdict(list)
|
|
17
|
+
self.props = {}
|
|
18
|
+
self.reindex_labels()
|
|
19
|
+
|
|
20
|
+
self._ini_labels = list(self.labels)
|
|
21
|
+
self._ini_wl_labels = None
|
|
22
|
+
self._irred_labels = {}
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def _normalise_graph(graph, n_nodes):
|
|
26
|
+
normalised = {idx: dict(graph.get(idx, {})) for idx in range(n_nodes)}
|
|
27
|
+
for src, nbrs in graph.items():
|
|
28
|
+
normalised.setdefault(src, {}).update(nbrs)
|
|
29
|
+
return normalised
|
|
30
|
+
|
|
31
|
+
def reindex_labels(self):
|
|
32
|
+
"""Rebuild label -> indices."""
|
|
33
|
+
label_index = defaultdict(list)
|
|
34
|
+
for idx, label in enumerate(self.labels):
|
|
35
|
+
label_index[label].append(idx)
|
|
36
|
+
self.label2idxs = label_index
|
|
37
|
+
|
|
38
|
+
def build_label2idxs(self):
|
|
39
|
+
"""Alias for reindex_labels."""
|
|
40
|
+
self.reindex_labels()
|
|
41
|
+
|
|
42
|
+
def copy(self):
|
|
43
|
+
"""Copy graph state."""
|
|
44
|
+
clone = type(self).__new__(type(self))
|
|
45
|
+
clone.graph = {idx: dict(nbrs) for idx, nbrs in self.graph.items()}
|
|
46
|
+
clone.labels = list(self.labels)
|
|
47
|
+
clone.label2idxs = defaultdict(
|
|
48
|
+
list,
|
|
49
|
+
{label: list(idxs) for label, idxs in self.label2idxs.items()},
|
|
50
|
+
)
|
|
51
|
+
clone.props = self.props
|
|
52
|
+
clone._ini_labels = self._ini_labels
|
|
53
|
+
clone._ini_wl_labels = self._ini_wl_labels
|
|
54
|
+
clone._irred_labels = dict(self._irred_labels)
|
|
55
|
+
return clone
|
|
56
|
+
|
|
57
|
+
def set_prop(self, name, prop):
|
|
58
|
+
"""Set metadata."""
|
|
59
|
+
self.props[name] = prop
|
|
60
|
+
|
|
61
|
+
def binarize_graph(self):
|
|
62
|
+
"""Set all edge weights to 1."""
|
|
63
|
+
for nbrs in self.graph.values():
|
|
64
|
+
for nbr in list(nbrs):
|
|
65
|
+
nbrs[nbr] = 1
|
|
66
|
+
|
|
67
|
+
def neighborhood_signature(self, idx, labels=None, binary=False):
|
|
68
|
+
"""WL neighborhood signature."""
|
|
69
|
+
labels = self.labels if labels is None else labels
|
|
70
|
+
grouped = defaultdict(list)
|
|
71
|
+
for nbr, weight in self.graph.get(idx, {}).items():
|
|
72
|
+
grouped[labels[nbr]].append(1 if binary else weight)
|
|
73
|
+
return (
|
|
74
|
+
labels[idx],
|
|
75
|
+
tuple(
|
|
76
|
+
sorted(
|
|
77
|
+
(label, tuple(sorted(weights, reverse=True)))
|
|
78
|
+
for label, weights in grouped.items()
|
|
79
|
+
)
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def one_wl_round(self, labels=None, binary=False):
|
|
84
|
+
"""One deterministic WL round."""
|
|
85
|
+
labels = self.labels if labels is None else labels
|
|
86
|
+
return [
|
|
87
|
+
stable_int_token(self.neighborhood_signature(idx, labels, binary))
|
|
88
|
+
for idx in range(len(labels))
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
def get_WL_labels(self):
|
|
92
|
+
"""Stabilized WL colors."""
|
|
93
|
+
if len(self.labels) <= self.SYNKIT_WL_NODE_LIMIT:
|
|
94
|
+
synkit_labels = synkit_wl_node_labels(self)
|
|
95
|
+
if synkit_labels is not None:
|
|
96
|
+
return synkit_labels
|
|
97
|
+
|
|
98
|
+
colors = list(self.labels)
|
|
99
|
+
for _ in range(max(1, 2 * len(colors))):
|
|
100
|
+
refined = self.one_wl_round(colors)
|
|
101
|
+
if len(set(refined)) == len(set(colors)):
|
|
102
|
+
return colors
|
|
103
|
+
colors = refined
|
|
104
|
+
return colors
|